11
11
12
12
import com .oracle .truffle .api .CompilerDirectives ;
13
13
import org .truffleruby .RubyLanguage ;
14
+ import org .truffleruby .core .exception .RubyException ;
14
15
import org .truffleruby .core .hash .RubyHash ;
15
16
import org .truffleruby .core .hash .library .HashStoreLibrary ;
16
17
import org .truffleruby .core .hash .library .HashStoreLibrary .EachEntryCallback ;
20
21
import org .truffleruby .language .methods .Arity ;
21
22
22
23
import com .oracle .truffle .api .CompilerDirectives .CompilationFinal ;
24
+ import com .oracle .truffle .api .CompilerDirectives .TruffleBoundary ;
23
25
import com .oracle .truffle .api .frame .VirtualFrame ;
24
26
import com .oracle .truffle .api .nodes .ExplodeLoop ;
25
27
import com .oracle .truffle .api .profiles .BranchProfile ;
26
28
29
+ import java .util .ArrayList ;
30
+ import java .util .Arrays ;
31
+ import java .util .List ;
32
+ import java .util .stream .Collectors ;
33
+
27
34
/** Check that no extra keyword arguments are given, when there is no **kwrest */
28
35
public class CheckKeywordArityNode extends RubyBaseNode {
29
36
30
37
public final Arity arity ;
31
38
@ Child private ReadUserKeywordsHashNode readUserKeywordsHashNode ;
32
- @ Child private CheckKeywordArgumentsNode checkKeywordArgumentsNode ;
39
+ @ Child private CheckExtraKeywordArgumentsNode checkExtraKeywordArgumentsNode ;
33
40
34
41
public CheckKeywordArityNode (Arity arity ) {
35
42
assert !arity .hasKeywordsRest () : "no need to create this node" ;
@@ -45,37 +52,37 @@ public void checkArity(VirtualFrame frame) {
45
52
}
46
53
47
54
private void checkKeywordArguments (RubyHash keywordArguments ) {
48
- if (checkKeywordArgumentsNode == null ) {
55
+ if (checkExtraKeywordArgumentsNode == null ) {
49
56
CompilerDirectives .transferToInterpreterAndInvalidate ();
50
- checkKeywordArgumentsNode = insert (new CheckKeywordArgumentsNode (getLanguage (), arity ));
57
+ checkExtraKeywordArgumentsNode = insert (new CheckExtraKeywordArgumentsNode (getLanguage (), arity ));
51
58
}
52
- checkKeywordArgumentsNode .check (keywordArguments );
59
+
60
+ checkExtraKeywordArgumentsNode .check (keywordArguments );
53
61
}
54
62
55
- private static class CheckKeywordArgumentsNode extends RubyBaseNode implements EachEntryCallback {
63
+ private static class CheckExtraKeywordArgumentsNode extends RubyBaseNode implements EachEntryCallback {
56
64
57
65
@ CompilationFinal (dimensions = 1 ) private final RubySymbol [] allowedKeywords ;
58
66
59
67
private final BranchProfile unknownKeywordProfile = BranchProfile .create ();
60
68
@ Child private HashStoreLibrary hashes ;
61
69
62
- public CheckKeywordArgumentsNode (RubyLanguage language , Arity arity ) {
70
+ public CheckExtraKeywordArgumentsNode (RubyLanguage language , Arity arity ) {
63
71
assert !arity .hasKeywordsRest ();
64
72
hashes = HashStoreLibrary .createDispatched ();
65
73
allowedKeywords = keywordsAsSymbols (language , arity );
66
74
}
67
75
68
76
public void check (RubyHash keywordArguments ) {
69
- hashes .eachEntry (keywordArguments .store , keywordArguments , this , null );
77
+ hashes .eachEntry (keywordArguments .store , keywordArguments , this , keywordArguments );
70
78
}
71
79
72
80
@ Override
73
81
public void accept (int index , Object key , Object value , Object state ) {
74
82
if (!keywordAllowed (key )) {
75
83
unknownKeywordProfile .enter ();
76
- throw new RaiseException (
77
- getContext (),
78
- coreExceptions ().argumentErrorUnknownKeyword (key , this ));
84
+ RubyHash keywordArguments = (RubyHash ) state ;
85
+ throw new RaiseException (getContext (), unknownKeywordsError (keywordArguments ));
79
86
}
80
87
}
81
88
@@ -89,6 +96,30 @@ private boolean keywordAllowed(Object keyword) {
89
96
90
97
return false ;
91
98
}
99
+
100
+ @ TruffleBoundary
101
+ private RubyException unknownKeywordsError (RubyHash keywordArguments ) {
102
+ Object [] keys = findExtraKeywordArguments (keywordArguments );
103
+ return coreExceptions ().argumentErrorUnknownKeywords (keys , this );
104
+ }
105
+
106
+ @ TruffleBoundary
107
+ @ SuppressWarnings ("unchecked" )
108
+ private Object [] findExtraKeywordArguments (RubyHash keywordArguments ) {
109
+ final ArrayList <Object > actualKeywordsAsList = new ArrayList <>();
110
+ hashes .eachEntry (
111
+ keywordArguments .store ,
112
+ keywordArguments ,
113
+ (index , key , value , state ) -> ((ArrayList <Object >) state ).add (key ),
114
+ actualKeywordsAsList );
115
+
116
+ final List <RubySymbol > allowedKeywordsAsList = Arrays .asList (allowedKeywords );
117
+ final List <Object > extraKeywords = actualKeywordsAsList .stream ()
118
+ .filter (k -> !allowedKeywordsAsList .contains (k ))
119
+ .collect (Collectors .toList ());
120
+
121
+ return extraKeywords .toArray ();
122
+ }
92
123
}
93
124
94
125
static RubySymbol [] keywordsAsSymbols (RubyLanguage language , Arity arity ) {
0 commit comments