@@ -67,19 +67,22 @@ if (polyglot_as_boolean(polyglot_invoke(RUBY_CEXT, "warning?"))) { \
67
67
} \
68
68
})
69
69
70
- #define rb_tr_scan_args_1 (ARGC , ARGV , FORMAT , V1 ) rb_tr_scan_args(ARGC, ARGV, FORMAT, V1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
71
- #define rb_tr_scan_args_2 (ARGC , ARGV , FORMAT , V1 , V2 ) rb_tr_scan_args(ARGC, ARGV, FORMAT, V1, V2, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
72
- #define rb_tr_scan_args_3 (ARGC , ARGV , FORMAT , V1 , V2 , V3 ) rb_tr_scan_args(ARGC, ARGV, FORMAT, V1, V2, V3, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
73
- #define rb_tr_scan_args_4 (ARGC , ARGV , FORMAT , V1 , V2 , V3 , V4 ) rb_tr_scan_args(ARGC, ARGV, FORMAT, V1, V2, V3, V4, NULL, NULL, NULL, NULL, NULL, NULL)
74
- #define rb_tr_scan_args_5 (ARGC , ARGV , FORMAT , V1 , V2 , V3 , V4 , V5 ) rb_tr_scan_args(ARGC, ARGV, FORMAT, V1, V2, V3, V4, V5, NULL, NULL, NULL, NULL, NULL)
75
- #define rb_tr_scan_args_6 (ARGC , ARGV , FORMAT , V1 , V2 , V3 , V4 , V5 , V6 ) rb_tr_scan_args(ARGC, ARGV, FORMAT, V1, V2, V3, V4, V5, V6, NULL, NULL, NULL, NULL)
76
- #define rb_tr_scan_args_7 (ARGC , ARGV , FORMAT , V1 , V2 , V3 , V4 , V5 , V6 , V7 ) rb_tr_scan_args(ARGC, ARGV, FORMAT, V1, V2, V3, V4, V5, V6, V7, NULL, NULL, NULL)
77
- #define rb_tr_scan_args_8 (ARGC , ARGV , FORMAT , V1 , V2 , V3 , V4 , V5 , V6 , V7 , V8 ) rb_tr_scan_args(ARGC, ARGV, FORMAT, V1, V2, V3, V4, V5, V6, V7, V8, NULL, NULL)
78
- #define rb_tr_scan_args_9 (ARGC , ARGV , FORMAT , V1 , V2 , V3 , V4 , V5 , V6 , V7 , V8 , V9 ) rb_tr_scan_args(ARGC, ARGV, FORMAT, V1, V2, V3, V4, V5, V6, V7, V8, V9, NULL)
79
- #define rb_tr_scan_args_10 (ARGC , ARGV , FORMAT , V1 , V2 , V3 , V4 , V5 , V6 , V7 , V8 , V9 , V10 ) rb_tr_scan_args(ARGC, ARGV, FORMAT, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10)
80
-
81
- #define SCAN_ARGS_IMPL (_1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , NAME , ...) NAME
82
- #define rb_scan_args (ARGC , ARGV , FORMAT , ...) SCAN_ARGS_IMPL(__VA_ARGS__, rb_tr_scan_args_10, rb_tr_scan_args_9, rb_tr_scan_args_8, rb_tr_scan_args_7, rb_tr_scan_args_6, rb_tr_scan_args_5, rb_tr_scan_args_4, rb_tr_scan_args_3, rb_tr_scan_args_2, rb_tr_scan_args_1)(ARGC, ARGV, FORMAT, __VA_ARGS__)
70
+ #define rb_tr_scan_args_kw_1 (KW_FLAG , ARGC , ARGV , FORMAT , V1 ) rb_tr_scan_args_kw(KW_FLAG, ARGC, ARGV, FORMAT, V1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
71
+ #define rb_tr_scan_args_kw_2 (KW_FLAG , ARGC , ARGV , FORMAT , V1 , V2 ) rb_tr_scan_args_kw(KW_FLAG, ARGC, ARGV, FORMAT, V1, V2, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
72
+ #define rb_tr_scan_args_kw_3 (KW_FLAG , ARGC , ARGV , FORMAT , V1 , V2 , V3 ) rb_tr_scan_args_kw(KW_FLAG, ARGC, ARGV, FORMAT, V1, V2, V3, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
73
+ #define rb_tr_scan_args_kw_4 (KW_FLAG , ARGC , ARGV , FORMAT , V1 , V2 , V3 , V4 ) rb_tr_scan_args_kw(KW_FLAG, ARGC, ARGV, FORMAT, V1, V2, V3, V4, NULL, NULL, NULL, NULL, NULL, NULL)
74
+ #define rb_tr_scan_args_kw_5 (KW_FLAG , ARGC , ARGV , FORMAT , V1 , V2 , V3 , V4 , V5 ) rb_tr_scan_args_kw(KW_FLAG, ARGC, ARGV, FORMAT, V1, V2, V3, V4, V5, NULL, NULL, NULL, NULL, NULL)
75
+ #define rb_tr_scan_args_kw_6 (KW_FLAG , ARGC , ARGV , FORMAT , V1 , V2 , V3 , V4 , V5 , V6 ) rb_tr_scan_args_kw(KW_FLAG, ARGC, ARGV, FORMAT, V1, V2, V3, V4, V5, V6, NULL, NULL, NULL, NULL)
76
+ #define rb_tr_scan_args_kw_7 (KW_FLAG , ARGC , ARGV , FORMAT , V1 , V2 , V3 , V4 , V5 , V6 , V7 ) rb_tr_scan_args_kw(KW_FLAG, ARGC, ARGV, FORMAT, V1, V2, V3, V4, V5, V6, V7, NULL, NULL, NULL)
77
+ #define rb_tr_scan_args_kw_8 (KW_FLAG , ARGC , ARGV , FORMAT , V1 , V2 , V3 , V4 , V5 , V6 , V7 , V8 ) rb_tr_scan_args_kw(KW_FLAG, ARGC, ARGV, FORMAT, V1, V2, V3, V4, V5, V6, V7, V8, NULL, NULL)
78
+ #define rb_tr_scan_args_kw_9 (KW_FLAG , ARGC , ARGV , FORMAT , V1 , V2 , V3 , V4 , V5 , V6 , V7 , V8 , V9 ) rb_tr_scan_args_kw(KW_FLAG, ARGC, ARGV, FORMAT, V1, V2, V3, V4, V5, V6, V7, V8, V9, NULL)
79
+ #define rb_tr_scan_args_kw_10 (KW_FLAG , ARGC , ARGV , FORMAT , V1 , V2 , V3 , V4 , V5 , V6 , V7 , V8 , V9 , V10 ) rb_tr_scan_args_kw(KW_FLAG, ARGC, ARGV, FORMAT, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10)
80
+
81
+ #define SCAN_ARGS_KW_IMPL (_1 , _2 , _3 , _4 , _5 , _6 , _7 , _8 , _9 , _10 , NAME , ...) NAME
82
+ #define rb_scan_args_kw (KW_FLAG , ARGC , ARGV , FORMAT , ...) SCAN_ARGS_KW_IMPL(__VA_ARGS__, rb_tr_scan_args_kw_10, rb_tr_scan_args_kw_9, rb_tr_scan_args_kw_8, rb_tr_scan_args_kw_7, rb_tr_scan_args_kw_6, rb_tr_scan_args_kw_5, rb_tr_scan_args_kw_4, rb_tr_scan_args_kw_3, rb_tr_scan_args_kw_2, rb_tr_scan_args_kw_1)(KW_FLAG, ARGC, ARGV, FORMAT, __VA_ARGS__)
83
+
84
+ #define rb_scan_args (ARGC , ARGV , FORMAT , ...) SCAN_ARGS_KW_IMPL(__VA_ARGS__, rb_tr_scan_args_kw_10, rb_tr_scan_args_kw_9, rb_tr_scan_args_kw_8, rb_tr_scan_args_kw_7, rb_tr_scan_args_kw_6, rb_tr_scan_args_kw_5, rb_tr_scan_args_kw_4, rb_tr_scan_args_kw_3, rb_tr_scan_args_kw_2, rb_tr_scan_args_kw_1)(RB_SCAN_ARGS_PASS_CALLED_KEYWORDS, ARGC, ARGV, FORMAT, __VA_ARGS__)
85
+
83
86
84
87
// Invoking ruby methods.
85
88
@@ -199,9 +202,8 @@ static inline char *rb_tr_string_value_cstr(VALUE *value_pointer) {
199
202
return RSTRING_PTR (string );
200
203
}
201
204
202
- ALWAYS_INLINE (static int rb_tr_scan_args (int argc , VALUE * argv , const char * format , VALUE * v1 , VALUE * v2 , VALUE * v3 , VALUE * v4 , VALUE * v5 , VALUE * v6 , VALUE * v7 , VALUE * v8 , VALUE * v9 , VALUE * v10 ));
203
- static inline int rb_tr_scan_args (int argc , VALUE * argv , const char * format , VALUE * v1 , VALUE * v2 , VALUE * v3 , VALUE * v4 , VALUE * v5 , VALUE * v6 , VALUE * v7 , VALUE * v8 , VALUE * v9 , VALUE * v10 ) {
204
- // Parse the format string
205
+ ALWAYS_INLINE (static int rb_tr_scan_args_kw (int kw_flag , int argc , VALUE * argv , const char * format , VALUE * v1 , VALUE * v2 , VALUE * v3 , VALUE * v4 , VALUE * v5 , VALUE * v6 , VALUE * v7 , VALUE * v8 , VALUE * v9 , VALUE * v10 ));
206
+ static inline int rb_tr_scan_args_kw (int kw_flag , int argc , VALUE * argv , const char * format , VALUE * v1 , VALUE * v2 , VALUE * v3 , VALUE * v4 , VALUE * v5 , VALUE * v6 , VALUE * v7 , VALUE * v8 , VALUE * v9 , VALUE * v10 ) {
205
207
206
208
// TODO CS 7-Feb-17 maybe we could inline cache this part?
207
209
@@ -213,6 +215,19 @@ static inline int rb_tr_scan_args(int argc, VALUE *argv, const char *format, VAL
213
215
bool kwargs ;
214
216
bool block ;
215
217
218
+ // Interpret kw_flag
219
+
220
+ int keyword_given = 0 ;
221
+ int empty_keyword_given = 0 ;
222
+ int last_hash_keyword = 0 ;
223
+
224
+ switch (kw_flag ) {
225
+ case RB_SCAN_ARGS_PASS_CALLED_KEYWORDS : break ;
226
+ case RB_SCAN_ARGS_KEYWORDS : keyword_given = 1 ; break ;
227
+ case RB_SCAN_ARGS_EMPTY_KEYWORDS : empty_keyword_given = 1 ; break ;
228
+ case RB_SCAN_ARGS_LAST_HASH_KEYWORDS : last_hash_keyword = 1 ; break ;
229
+ }
230
+
216
231
// TODO CS 27-Feb-17 can LLVM constant-fold through isdigit?
217
232
218
233
if (isdigit (* formatp )) {
@@ -276,26 +291,60 @@ static inline int rb_tr_scan_args(int argc, VALUE *argv, const char *format, VAL
276
291
// accepting them will still need to be set to Qnil in such cases.
277
292
bool erased_kwargs = false;
278
293
bool found_kwargs = false;
294
+ VALUE hash = Qnil ;
279
295
280
- if (kwargs && (n_mand < argc )) {
296
+ /* capture an option hash - phase 1: pop */
297
+ /* Ignore final positional hash if empty keywords given */
298
+ if (argc > 0 && !(kwargs && empty_keyword_given )) {
281
299
VALUE last = argv [argc - 1 ];
282
300
283
- if (NIL_P (last )) {
284
- /* nil is taken as an empty option hash only if it is not
285
- ambiguous; i.e. '*' is not specified and arguments are
286
- given more than sufficient */
287
- if (rest || argc <= n_mand + n_opt ) {
288
- kwargs = false;
289
- erased_kwargs = true;
301
+ if (kwargs && n_mand < argc ) {
302
+ if (keyword_given ) {
303
+ if (!RB_TYPE_P (last , T_HASH )) {
304
+ rb_warn ("Keyword flag set when calling rb_scan_args, but last entry is not a hash" );
305
+ }
306
+ else {
307
+ hash = last ;
308
+ }
290
309
}
291
- } else {
292
- if (!polyglot_as_boolean (RUBY_CEXT_INVOKE_NO_WRAP ("test_kwargs" , argv [argc - 1 ], Qfalse ))) {
293
- kwargs = false;
294
- erased_kwargs = true;
310
+
311
+ else if (NIL_P (last )) {
312
+ /* For backwards compatibility, nil is taken as an empty
313
+ option hash only if it is not ambiguous; i.e. '*' is
314
+ not specified and arguments are given more than sufficient.
315
+ This will be removed in Ruby 3. */
316
+ if (rest || argc <= n_mand + n_opt ) {
317
+ kwargs = false;
318
+ erased_kwargs = true;
319
+ rb_warn ("The last argument is nil, treating as empty keywords" );
320
+ }
295
321
}
322
+ else {
323
+ hash = rb_check_hash_type (last );
324
+ if (NIL_P (hash )) {
325
+ kwargs = false;
326
+ erased_kwargs = true;
327
+ }
328
+ }
329
+
330
+ /* Ruby 3: Remove if branch, as it will not attempt to split hashes */
331
+ if (!NIL_P (hash )) {
332
+ if (!polyglot_as_boolean (RUBY_CEXT_INVOKE_NO_WRAP ("test_kwargs" , argv [argc - 1 ], Qfalse ))) {
333
+ // Does not handle the case where "The last argument is split into positional and keyword parameters"
334
+ // Instead assumes that it is all one hash
335
+ kwargs = false;
336
+ erased_kwargs = true;
337
+ }
338
+ }
339
+ }
340
+ else if (kwargs && keyword_given && n_mand == argc ) {
341
+ /* Warn if treating keywords as positional, as in Ruby 3, this will be an error */
342
+ rb_warn ("Passing the keyword argument as the last hash parameter is deprecated" );
296
343
}
297
344
}
298
345
346
+ // Skipped the part of MRI where empty_keyword_given with rb_warn("Passing the keyword argument as the last hash parameter is deprecated");
347
+
299
348
int trailing = post ;
300
349
301
350
if (kwargs ) {
0 commit comments