Ruby 4.1.0dev (2026-10-29 revision 952e77c511a0d183f43464cba30c55834eac643e)
prism_compile.c (952e77c511a0d183f43464cba30c55834eac643e)
1#include "prism.h"
2#include "prism_compile.h"
3#include "ruby/version.h"
4
5#include <fcntl.h>
6
12typedef struct {
14 int32_t line;
15
17 uint32_t node_id;
19
20/******************************************************************************/
21/* These macros operate on pm_node_location_t structs as opposed to NODE*s. */
22/******************************************************************************/
23
24#define PUSH_ADJUST(seq, location, label) \
25 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), (int) (location).line))
26
27#define PUSH_ADJUST_RESTORE(seq, label) \
28 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), -1))
29
30#define PUSH_INSN(seq, location, insn) \
31 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 0))
32
33#define PUSH_INSN1(seq, location, insn, op1) \
34 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 1, (VALUE)(op1)))
35
36#define PUSH_INSN2(seq, location, insn, op1, op2) \
37 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 2, (VALUE)(op1), (VALUE)(op2)))
38
39#define PUSH_INSN3(seq, location, insn, op1, op2, op3) \
40 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3)))
41
42#define PUSH_INSNL(seq, location, insn, label) \
43 (PUSH_INSN1(seq, location, insn, label), LABEL_REF(label))
44
45#define PUSH_LABEL(seq, label) \
46 ADD_ELEM((seq), (LINK_ELEMENT *) (label))
47
48#define PUSH_SEND_R(seq, location, id, argc, block, flag, keywords) \
49 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (int) (location).line, (int) (location).node_id, (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords)))
50
51#define PUSH_SEND(seq, location, id, argc) \
52 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)INT2FIX(0), NULL)
53
54#define PUSH_SEND_WITH_FLAG(seq, location, id, argc, flag) \
55 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)(flag), NULL)
56
57#define PUSH_SEND_WITH_BLOCK(seq, location, id, argc, block) \
58 PUSH_SEND_R((seq), location, (id), (argc), (block), (VALUE)INT2FIX(0), NULL)
59
60#define PUSH_CALL(seq, location, id, argc) \
61 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
62
63#define PUSH_CALL_WITH_BLOCK(seq, location, id, argc, block) \
64 PUSH_SEND_R((seq), location, (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
65
66#define PUSH_TRACE(seq, event) \
67 ADD_ELEM((seq), (LINK_ELEMENT *) new_trace_body(iseq, (event), 0))
68
69#define PUSH_CATCH_ENTRY(type, ls, le, iseqv, lc) \
70 ADD_CATCH_ENTRY((type), (ls), (le), (iseqv), (lc))
71
72#define PUSH_SEQ(seq1, seq2) \
73 APPEND_LIST((seq1), (seq2))
74
75#define PUSH_SYNTHETIC_PUTNIL(seq, iseq) \
76 do { \
77 int lineno = ISEQ_COMPILE_DATA(iseq)->last_line; \
78 if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq)); \
79 ADD_SYNTHETIC_INSN(seq, lineno, -1, putnil); \
80 } while (0)
81
82/******************************************************************************/
83/* These functions compile getlocal/setlocal instructions but operate on */
84/* prism locations instead of NODEs. */
85/******************************************************************************/
86
87static void
88pm_iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int node_id, int idx, int level)
89{
90 if (iseq_local_block_param_p(iseq, idx, level)) {
91 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(getblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
92 }
93 else {
94 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(getlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
95 }
96 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qfalse);
97}
98
99static void
100pm_iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int node_id, int idx, int level)
101{
102 if (iseq_local_block_param_p(iseq, idx, level)) {
103 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(setblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
104 }
105 else {
106 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(setlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
107 }
108 update_lvar_state(iseq, level, idx);
109 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue);
110}
111
112#define PUSH_GETLOCAL(seq, location, idx, level) \
113 pm_iseq_add_getlocal(iseq, (seq), (int) (location).line, (int) (location).node_id, (idx), (level))
114
115#define PUSH_SETLOCAL(seq, location, idx, level) \
116 pm_iseq_add_setlocal(iseq, (seq), (int) (location).line, (int) (location).node_id, (idx), (level))
117
118/******************************************************************************/
119/* These are helper macros for the compiler. */
120/******************************************************************************/
121
122#define OLD_ISEQ NEW_ISEQ
123#undef NEW_ISEQ
124
125#define NEW_ISEQ(node, name, type, line_no) \
126 pm_new_child_iseq(iseq, (node), rb_fstring(name), 0, (type), (line_no))
127
128#define OLD_CHILD_ISEQ NEW_CHILD_ISEQ
129#undef NEW_CHILD_ISEQ
130
131#define NEW_CHILD_ISEQ(node, name, type, line_no) \
132 pm_new_child_iseq(iseq, (node), rb_fstring(name), iseq, (type), (line_no))
133
134#define PM_COMPILE(node) \
135 pm_compile_node(iseq, (node), ret, popped, scope_node)
136
137#define PM_COMPILE_INTO_ANCHOR(_ret, node) \
138 pm_compile_node(iseq, (node), _ret, popped, scope_node)
139
140#define PM_COMPILE_POPPED(node) \
141 pm_compile_node(iseq, (node), ret, true, scope_node)
142
143#define PM_COMPILE_NOT_POPPED(node) \
144 pm_compile_node(iseq, (node), ret, false, scope_node)
145
146// Direct-indexed lookup table. -1 means "not present".
147#define PM_INDEX_LOOKUP_TABLE_INIT { .values = NULL, .capacity = 0, .owned = false }
148
149static inline void
150pm_index_lookup_table_init(pm_index_lookup_table_t *table, int constants_size, rb_iseq_t *iseq)
151{
152 int capacity = constants_size + PM_INDEX_LOOKUP_SPECIALS;
153 table->values = compile_data_alloc2_type(iseq, int, capacity);
154 memset(table->values, -1, capacity * sizeof(int));
155 table->capacity = capacity;
156 table->owned = false;
157}
158
165static inline pm_line_column_t
166pm_line_offset_list_line_column_cached(const pm_line_offset_list_t *list, uint32_t cursor, int32_t start_line, size_t *last_line)
167{
168 size_t hint = *last_line;
169 size_t size = list->size;
170 const uint32_t *offsets = list->offsets;
171
172 RUBY_ASSERT(hint < size);
173
174 /* Check if the cursor is on the same line as the hint. */
175 if (offsets[hint] <= cursor) {
176 if (hint + 1 >= size || offsets[hint + 1] > cursor) {
177 *last_line = hint;
178 return ((pm_line_column_t) {
179 .line = ((int32_t) hint) + start_line,
180 .column = cursor - offsets[hint]
181 });
182 }
183
184 /* Linear scan forward (up to 8 lines before giving up). */
185 size_t limit = hint + 9;
186 if (limit > size) limit = size;
187 for (size_t idx = hint + 1; idx < limit; idx++) {
188 if (offsets[idx] > cursor) {
189 *last_line = idx - 1;
190 return ((pm_line_column_t) {
191 .line = ((int32_t) (idx - 1)) + start_line,
192 .column = cursor - offsets[idx - 1]
193 });
194 }
195 if (offsets[idx] == cursor) {
196 *last_line = idx;
197 return ((pm_line_column_t) { ((int32_t) idx) + start_line, 0 });
198 }
199 }
200 }
201 else {
202 /* Linear scan backward (up to 8 lines before giving up). */
203 size_t limit = hint > 8 ? hint - 8 : 0;
204 for (size_t idx = hint; idx > limit; idx--) {
205 if (offsets[idx - 1] <= cursor) {
206 *last_line = idx - 1;
207 return ((pm_line_column_t) {
208 .line = ((int32_t) (idx - 1)) + start_line,
209 .column = cursor - offsets[idx - 1]
210 });
211 }
212 }
213 }
214
215 /* Fall back to binary search. */
216 pm_line_column_t result = pm_line_offset_list_line_column(list, cursor, start_line);
217 *last_line = (size_t) (result.line - start_line);
218 return result;
219}
220
225static inline int32_t
226pm_line_offset_list_line_cached(const pm_line_offset_list_t *list, uint32_t cursor, int32_t start_line, size_t *last_line)
227{
228 return pm_line_offset_list_line_column_cached(list, cursor, start_line, last_line).line;
229}
230
231#define PM_NODE_START_LOCATION(node) \
232 ((pm_node_location_t) { .line = pm_line_offset_list_line_cached(scope_node->line_offsets, ((const pm_node_t *) (node))->location.start, scope_node->start_line, &scope_node->last_line), .node_id = ((const pm_node_t *) (node))->node_id })
233
234#define PM_NODE_END_LOCATION(node) \
235 ((pm_node_location_t) { .line = pm_line_offset_list_line_cached(scope_node->line_offsets, ((const pm_node_t *) (node))->location.start + ((const pm_node_t *) (node))->location.length, scope_node->start_line, &scope_node->last_line), .node_id = ((const pm_node_t *) (node))->node_id })
236
237#define PM_LOCATION_START_LOCATION(location, id) \
238 ((pm_node_location_t) { .line = pm_line_offset_list_line_cached(scope_node->line_offsets, (location)->start, scope_node->start_line, &scope_node->last_line), .node_id = id })
239
240#define PM_NODE_START_LINE_COLUMN(node) \
241 pm_line_offset_list_line_column_cached(scope_node->line_offsets, ((const pm_node_t *) (node))->location.start, scope_node->start_line, &scope_node->last_line)
242
243#define PM_NODE_END_LINE_COLUMN(node) \
244 pm_line_offset_list_line_column_cached(scope_node->line_offsets, ((const pm_node_t *) (node))->location.start + ((const pm_node_t *) (node))->location.length, scope_node->start_line, &scope_node->last_line)
245
246#define PM_LOCATION_START_LINE_COLUMN(location) \
247 pm_line_offset_list_line_column_cached(scope_node->line_offsets, (location)->start, scope_node->start_line, &scope_node->last_line)
248
249static int
250pm_location_line_number(const pm_parser_t *parser, const pm_location_t *location) {
251 return (int) pm_line_offset_list_line_column(pm_parser_line_offsets(parser), location->start, pm_parser_start_line(parser)).line;
252}
253
258static inline int
259pm_node_line_number_cached(const pm_node_t *node, pm_scope_node_t *scope_node)
260{
261 return (int) pm_line_offset_list_line_cached(scope_node->line_offsets, node->location.start, scope_node->start_line, &scope_node->last_line);
262}
263
264static inline int
265pm_location_line_number_cached(const pm_location_t *location, pm_scope_node_t *scope_node) {
266 return (int) pm_line_offset_list_line_cached(scope_node->line_offsets, location->start, scope_node->start_line, &scope_node->last_line);
267}
268
272static VALUE
273parse_integer_value(const pm_integer_t *integer)
274{
275 VALUE result;
276
277 if (integer->values == NULL) {
278 result = UINT2NUM(integer->value);
279 }
280 else {
281 // The pm_integer_t stores values as an array of uint32_t in
282 // least-significant-word-first order (base 2^32). We can convert
283 // directly to a Ruby Integer using rb_integer_unpack, avoiding the
284 // overhead of constructing a hex string and calling rb_funcall.
285 result = rb_integer_unpack(
286 integer->values,
287 integer->length,
288 sizeof(uint32_t),
289 0,
291 );
292 }
293
294 if (integer->negative) {
295 result = rb_int_uminus(result);
296 }
297
298 if (!SPECIAL_CONST_P(result)) {
299 RB_OBJ_SET_SHAREABLE(result); // bignum
300 }
301
302 return result;
303}
304
308static inline VALUE
309parse_integer(const pm_integer_node_t *node)
310{
311 return parse_integer_value(&node->value);
312}
313
317static VALUE
318parse_float(const pm_float_node_t *node)
319{
320 VALUE val = DBL2NUM(node->value);
321 if (!FLONUM_P(val)) {
323 }
324 return val;
325}
326
333static VALUE
334parse_rational(const pm_rational_node_t *node)
335{
336 VALUE numerator = parse_integer_value(&node->numerator);
337 VALUE denominator = parse_integer_value(&node->denominator);
338
339 return rb_ractor_make_shareable(rb_rational_new(numerator, denominator));
340}
341
348static VALUE
349parse_imaginary(const pm_imaginary_node_t *node)
350{
351 VALUE imaginary_part;
352 switch (PM_NODE_TYPE(node->numeric)) {
353 case PM_FLOAT_NODE: {
354 imaginary_part = parse_float((const pm_float_node_t *) node->numeric);
355 break;
356 }
357 case PM_INTEGER_NODE: {
358 imaginary_part = parse_integer((const pm_integer_node_t *) node->numeric);
359 break;
360 }
361 case PM_RATIONAL_NODE: {
362 imaginary_part = parse_rational((const pm_rational_node_t *) node->numeric);
363 break;
364 }
365 default:
366 rb_bug("Unexpected numeric type on imaginary number %s\n", pm_node_type(PM_NODE_TYPE(node->numeric)));
367 }
368
369 return RB_OBJ_SET_SHAREABLE(rb_complex_raw(INT2FIX(0), imaginary_part));
370}
371
372static inline VALUE
373parse_string(const pm_scope_node_t *scope_node, const pm_string_t *string)
374{
375 return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), scope_node->encoding);
376}
377
383static inline VALUE
384parse_string_encoded(const pm_node_t *node, const pm_string_t *string, rb_encoding *default_encoding)
385{
386 rb_encoding *encoding;
387
388 if (node->flags & PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING) {
389 encoding = rb_ascii8bit_encoding();
390 }
391 else if (node->flags & PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING) {
392 encoding = rb_utf8_encoding();
393 }
394 else {
395 encoding = default_encoding;
396 }
397
398 return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), encoding);
399}
400
401static inline VALUE
402parse_static_literal_string(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *string)
403{
404 rb_encoding *encoding;
405
406 if (node->flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) {
407 encoding = rb_ascii8bit_encoding();
408 }
409 else if (node->flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) {
410 encoding = rb_utf8_encoding();
411 }
412 else {
413 encoding = scope_node->encoding;
414 }
415
416 VALUE value = rb_enc_literal_str((const char *) pm_string_source(string), pm_string_length(string), encoding);
417 rb_enc_str_coderange(value);
418
419 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
420 int line_number = pm_node_line_number_cached(node, scope_node);
421 value = rb_ractor_make_shareable(rb_str_with_debug_created_info(value, rb_iseq_path(iseq), line_number));
422 }
423
424 return value;
425}
426
427static inline ID
428parse_string_symbol(const pm_scope_node_t *scope_node, const pm_symbol_node_t *symbol)
429{
430 rb_encoding *encoding;
431 if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING) {
432 encoding = rb_utf8_encoding();
433 }
434 else if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING) {
435 encoding = rb_ascii8bit_encoding();
436 }
437 else if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING) {
438 encoding = rb_usascii_encoding();
439 }
440 else {
441 encoding = scope_node->encoding;
442 }
443
444 return rb_intern3((const char *) pm_string_source(&symbol->unescaped), pm_string_length(&symbol->unescaped), encoding);
445}
446
447static int
448pm_optimizable_range_item_p(const pm_node_t *node)
449{
450 return (!node || PM_NODE_TYPE_P(node, PM_INTEGER_NODE) || PM_NODE_TYPE_P(node, PM_NIL_NODE));
451}
452
454static VALUE
455parse_regexp_error(rb_iseq_t *iseq, int32_t line_number, const char *fmt, ...)
456{
457 va_list args;
458 va_start(args, fmt);
459 VALUE error = rb_syntax_error_append(Qnil, rb_iseq_path(iseq), line_number, -1, NULL, "%" PRIsVALUE, args);
460 va_end(args);
461 rb_exc_raise(error);
462}
463
464static VALUE
465parse_regexp_string_part(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *unescaped, rb_encoding *implicit_regexp_encoding, rb_encoding *explicit_regexp_encoding)
466{
467 // If we were passed an explicit regexp encoding, then we need to double
468 // check that it's okay here for this fragment of the string.
469 rb_encoding *encoding;
470
471 if (explicit_regexp_encoding != NULL) {
472 encoding = explicit_regexp_encoding;
473 }
474 else if (node->flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) {
475 encoding = rb_ascii8bit_encoding();
476 }
477 else if (node->flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) {
478 encoding = rb_utf8_encoding();
479 }
480 else {
481 encoding = implicit_regexp_encoding;
482 }
483
484 VALUE string = rb_enc_str_new((const char *) pm_string_source(unescaped), pm_string_length(unescaped), encoding);
485 VALUE error = rb_reg_check_preprocess(string);
486
487 if (error != Qnil) parse_regexp_error(iseq, pm_node_line_number_cached(node, scope_node), "%" PRIsVALUE, rb_obj_as_string(error));
488 return string;
489}
490
491static VALUE
492pm_static_literal_concat(rb_iseq_t *iseq, const pm_node_list_t *nodes, pm_scope_node_t *scope_node, rb_encoding *implicit_regexp_encoding, rb_encoding *explicit_regexp_encoding, bool top)
493{
494 VALUE current = Qnil;
495
496 for (size_t index = 0; index < nodes->size; index++) {
497 const pm_node_t *part = nodes->nodes[index];
498 VALUE string;
499
500 switch (PM_NODE_TYPE(part)) {
501 case PM_STRING_NODE:
502 if (implicit_regexp_encoding != NULL) {
503 if (top) {
504 string = parse_regexp_string_part(iseq, scope_node, part, &((const pm_string_node_t *) part)->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
505 }
506 else {
507 string = parse_string_encoded(part, &((const pm_string_node_t *) part)->unescaped, scope_node->encoding);
508 VALUE error = rb_reg_check_preprocess(string);
509 if (error != Qnil) parse_regexp_error(iseq, pm_node_line_number_cached(part, scope_node), "%" PRIsVALUE, rb_obj_as_string(error));
510 }
511 }
512 else {
513 string = parse_string_encoded(part, &((const pm_string_node_t *) part)->unescaped, scope_node->encoding);
514 }
515 break;
516 case PM_INTERPOLATED_STRING_NODE:
517 string = pm_static_literal_concat(iseq, &((const pm_interpolated_string_node_t *) part)->parts, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
518 break;
519 case PM_EMBEDDED_STATEMENTS_NODE: {
521 string = pm_static_literal_concat(iseq, &cast->statements->body, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
522 break;
523 }
524 default:
525 RUBY_ASSERT(false && "unexpected node type in pm_static_literal_concat");
526 return Qnil;
527 }
528
529 if (current != Qnil) {
530 current = rb_str_concat(current, string);
531 }
532 else {
533 current = string;
534 }
535 }
536
537 return top ? rb_fstring(current) : current;
538}
539
540#define RE_OPTION_ENCODING_SHIFT 8
541#define RE_OPTION_ENCODING(encoding) (((encoding) & 0xFF) << RE_OPTION_ENCODING_SHIFT)
542#define ARG_ENCODING_NONE 32
543#define ARG_ENCODING_FIXED 16
544#define ENC_ASCII8BIT 1
545#define ENC_EUC_JP 2
546#define ENC_Windows_31J 3
547#define ENC_UTF8 4
548
553static int
554parse_regexp_flags(const pm_node_t *node)
555{
556 int flags = 0;
557
558 // Check "no encoding" first so that flags don't get clobbered
559 // We're calling `rb_char_to_option_kcode` in this case so that
560 // we don't need to have access to `ARG_ENCODING_NONE`
561 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) {
562 flags |= ARG_ENCODING_NONE;
563 }
564
565 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) {
566 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_EUC_JP));
567 }
568
569 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) {
570 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_Windows_31J));
571 }
572
573 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) {
574 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_UTF8));
575 }
576
577 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) {
578 flags |= ONIG_OPTION_IGNORECASE;
579 }
580
581 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) {
582 flags |= ONIG_OPTION_MULTILINE;
583 }
584
585 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) {
586 flags |= ONIG_OPTION_EXTEND;
587 }
588
589 return flags;
590}
591
592#undef RE_OPTION_ENCODING_SHIFT
593#undef RE_OPTION_ENCODING
594#undef ARG_ENCODING_FIXED
595#undef ARG_ENCODING_NONE
596#undef ENC_ASCII8BIT
597#undef ENC_EUC_JP
598#undef ENC_Windows_31J
599#undef ENC_UTF8
600
601static rb_encoding *
602parse_regexp_encoding(const pm_scope_node_t *scope_node, const pm_node_t *node)
603{
604 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING) || PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) {
605 return rb_ascii8bit_encoding();
606 }
607 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) {
608 return rb_utf8_encoding();
609 }
610 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) {
611 return rb_enc_get_from_index(ENCINDEX_EUC_JP);
612 }
613 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) {
614 return rb_enc_get_from_index(ENCINDEX_Windows_31J);
615 }
616 else {
617 return NULL;
618 }
619}
620
621static VALUE
622parse_regexp(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, VALUE string)
623{
624 VALUE errinfo = rb_errinfo();
625
626 int32_t line_number = pm_node_line_number_cached(node, scope_node);
627 VALUE regexp = rb_reg_compile(string, parse_regexp_flags(node), (const char *) pm_string_source(pm_parser_filepath(scope_node->parser)), line_number);
628
629 if (NIL_P(regexp)) {
630 VALUE message = rb_attr_get(rb_errinfo(), idMesg);
631 rb_set_errinfo(errinfo);
632
633 parse_regexp_error(iseq, line_number, "%" PRIsVALUE, message);
634 return Qnil;
635 }
636
637 return RB_OBJ_SET_SHAREABLE(rb_obj_freeze(regexp));
638}
639
640static inline VALUE
641parse_regexp_literal(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *unescaped)
642{
643 rb_encoding *regexp_encoding = parse_regexp_encoding(scope_node, node);
644 if (regexp_encoding == NULL) regexp_encoding = scope_node->encoding;
645
646 VALUE string = rb_enc_str_new((const char *) pm_string_source(unescaped), pm_string_length(unescaped), regexp_encoding);
647 RB_OBJ_SET_SHAREABLE(string);
648 return parse_regexp(iseq, scope_node, node, string);
649}
650
651static inline VALUE
652parse_regexp_concat(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, const pm_node_list_t *parts)
653{
654 rb_encoding *explicit_regexp_encoding = parse_regexp_encoding(scope_node, node);
655 rb_encoding *implicit_regexp_encoding = explicit_regexp_encoding != NULL ? explicit_regexp_encoding : scope_node->encoding;
656
657 VALUE string = pm_static_literal_concat(iseq, parts, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
658 return parse_regexp(iseq, scope_node, node, string);
659}
660
661static void pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node);
662
663static int
664pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, rb_encoding *implicit_regexp_encoding, rb_encoding *explicit_regexp_encoding, bool mutable_result, bool frozen_result)
665{
666 int stack_size = 0;
667 size_t parts_size = parts->size;
668 bool interpolated = false;
669
670 if (parts_size > 0) {
671 VALUE current_string = Qnil;
672 pm_node_location_t current_location = *node_location;
673
674 for (size_t index = 0; index < parts_size; index++) {
675 const pm_node_t *part = parts->nodes[index];
676
677 if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
678 const pm_string_node_t *string_node = (const pm_string_node_t *) part;
679 VALUE string_value;
680
681 if (implicit_regexp_encoding == NULL) {
682 string_value = parse_string_encoded(part, &string_node->unescaped, scope_node->encoding);
683 }
684 else {
685 string_value = parse_regexp_string_part(iseq, scope_node, (const pm_node_t *) string_node, &string_node->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
686 }
687
688 if (RTEST(current_string)) {
689 current_string = rb_str_concat(current_string, string_value);
690 }
691 else {
692 current_string = string_value;
693 if (index != 0) current_location = PM_NODE_END_LOCATION(part);
694 }
695 }
696 else {
697 interpolated = true;
698
699 if (
700 PM_NODE_TYPE_P(part, PM_EMBEDDED_STATEMENTS_NODE) &&
701 ((const pm_embedded_statements_node_t *) part)->statements != NULL &&
702 ((const pm_embedded_statements_node_t *) part)->statements->body.size == 1 &&
703 PM_NODE_TYPE_P(((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0], PM_STRING_NODE)
704 ) {
705 const pm_string_node_t *string_node = (const pm_string_node_t *) ((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0];
706 VALUE string_value;
707
708 if (implicit_regexp_encoding == NULL) {
709 string_value = parse_string_encoded(part, &string_node->unescaped, scope_node->encoding);
710 }
711 else {
712 string_value = parse_regexp_string_part(iseq, scope_node, (const pm_node_t *) string_node, &string_node->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
713 }
714
715 if (RTEST(current_string)) {
716 current_string = rb_str_concat(current_string, string_value);
717 }
718 else {
719 current_string = string_value;
720 current_location = PM_NODE_START_LOCATION(part);
721 }
722 }
723 else {
724 if (!RTEST(current_string)) {
725 rb_encoding *encoding;
726
727 if (implicit_regexp_encoding != NULL) {
728 if (explicit_regexp_encoding != NULL) {
729 encoding = explicit_regexp_encoding;
730 }
731 else if (pm_parser_encoding_us_ascii(scope_node->parser)) {
732 encoding = rb_ascii8bit_encoding();
733 }
734 else {
735 encoding = implicit_regexp_encoding;
736 }
737 }
738 else {
739 encoding = scope_node->encoding;
740 }
741
742 if (parts_size == 1) {
743 current_string = rb_enc_str_new(NULL, 0, encoding);
744 }
745 }
746
747 if (RTEST(current_string)) {
748 VALUE operand = rb_fstring(current_string);
749 PUSH_INSN1(ret, current_location, putobject, operand);
750 stack_size++;
751 }
752
753 PM_COMPILE_NOT_POPPED(part);
754
755 const pm_node_location_t current_location = PM_NODE_START_LOCATION(part);
756 PUSH_INSN(ret, current_location, dup);
757
758 {
759 const struct rb_callinfo *callinfo = new_callinfo(iseq, idTo_s, 0, VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE, NULL, FALSE);
760 PUSH_INSN1(ret, current_location, objtostring, callinfo);
761 }
762
763 PUSH_INSN(ret, current_location, anytostring);
764
765 current_string = Qnil;
766 stack_size++;
767 }
768 }
769 }
770
771 if (RTEST(current_string)) {
772 current_string = rb_fstring(current_string);
773
774 if (stack_size == 0) {
775 if (frozen_result) {
776 PUSH_INSN1(ret, current_location, putobject, current_string);
777 } else if (mutable_result || interpolated) {
778 PUSH_INSN1(ret, current_location, dupstring, current_string);
779 } else {
780 PUSH_INSN1(ret, current_location, dupchilledstring, current_string);
781 }
782 } else {
783 PUSH_INSN1(ret, current_location, putobject, current_string);
784 }
785
786 current_string = Qnil;
787 stack_size++;
788 }
789 }
790 else {
791 PUSH_INSN(ret, *node_location, putnil);
792 }
793
794 return stack_size;
795}
796
797static void
798pm_compile_regexp_dynamic(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *parts, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
799{
800 rb_encoding *explicit_regexp_encoding = parse_regexp_encoding(scope_node, node);
801 rb_encoding *implicit_regexp_encoding = explicit_regexp_encoding != NULL ? explicit_regexp_encoding : scope_node->encoding;
802
803 int length = pm_interpolated_node_compile(iseq, parts, node_location, ret, popped, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false, false);
804 PUSH_INSN2(ret, *node_location, toregexp, INT2FIX(parse_regexp_flags(node) & 0xFF), INT2FIX(length));
805}
806
811static VALUE
812pm_static_literal_string(rb_iseq_t *iseq, VALUE string, int line_number)
813{
814 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
815 VALUE str = rb_str_with_debug_created_info(string, rb_iseq_path(iseq), line_number);
817 return str;
818 }
819 else {
820 return rb_fstring(string);
821 }
822}
823
829static VALUE
830pm_static_literal_value(rb_iseq_t *iseq, const pm_node_t *node, pm_scope_node_t *scope_node)
831{
832 // Every node that comes into this function should already be marked as
833 // static literal. If it's not, then we have a bug somewhere.
834 RUBY_ASSERT(PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL));
835
836 switch (PM_NODE_TYPE(node)) {
837 case PM_ARRAY_NODE: {
838 const pm_array_node_t *cast = (const pm_array_node_t *) node;
839 const pm_node_list_t *elements = &cast->elements;
840
841 VALUE value = rb_ary_hidden_new(elements->size);
842 for (size_t index = 0; index < elements->size; index++) {
843 rb_ary_push(value, pm_static_literal_value(iseq, elements->nodes[index], scope_node));
844 }
845
847 return value;
848 }
849 case PM_FALSE_NODE:
850 return Qfalse;
851 case PM_FLOAT_NODE:
852 return parse_float((const pm_float_node_t *) node);
853 case PM_HASH_NODE: {
854 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
855 const pm_node_list_t *elements = &cast->elements;
856
857 VALUE array = rb_ary_hidden_new(elements->size * 2);
858 for (size_t index = 0; index < elements->size; index++) {
859 RUBY_ASSERT(PM_NODE_TYPE_P(elements->nodes[index], PM_ASSOC_NODE));
860 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) elements->nodes[index];
861 VALUE pair[2] = { pm_static_literal_value(iseq, cast->key, scope_node), pm_static_literal_value(iseq, cast->value, scope_node) };
862 rb_ary_cat(array, pair, 2);
863 }
864
865 VALUE value = rb_hash_alloc_fixed_size(Qfalse, elements->size);
866 rb_hash_bulk_insert(RARRAY_LEN(array), RARRAY_CONST_PTR(array), value);
867 RB_GC_GUARD(array);
868
870 return value;
871 }
872 case PM_IMAGINARY_NODE:
873 return parse_imaginary((const pm_imaginary_node_t *) node);
874 case PM_INTEGER_NODE:
875 return parse_integer((const pm_integer_node_t *) node);
876 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: {
878 return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts);
879 }
880 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
882 return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts);
883 }
884 case PM_INTERPOLATED_STRING_NODE: {
885 VALUE string = pm_static_literal_concat(iseq, &((const pm_interpolated_string_node_t *) node)->parts, scope_node, NULL, NULL, false);
886 int line_number = pm_node_line_number_cached(node, scope_node);
887 return pm_static_literal_string(iseq, string, line_number);
888 }
889 case PM_INTERPOLATED_SYMBOL_NODE: {
891 VALUE string = pm_static_literal_concat(iseq, &cast->parts, scope_node, NULL, NULL, true);
892
893 return ID2SYM(rb_intern_str(string));
894 }
895 case PM_MATCH_LAST_LINE_NODE: {
897 return parse_regexp_literal(iseq, scope_node, (const pm_node_t *) cast, &cast->unescaped);
898 }
899 case PM_NIL_NODE:
900 return Qnil;
901 case PM_RATIONAL_NODE:
902 return parse_rational((const pm_rational_node_t *) node);
903 case PM_REGULAR_EXPRESSION_NODE: {
905 return parse_regexp_literal(iseq, scope_node, (const pm_node_t *) cast, &cast->unescaped);
906 }
907 case PM_SOURCE_ENCODING_NODE:
908 return rb_enc_from_encoding(scope_node->encoding);
909 case PM_SOURCE_LINE_NODE:
910 return INT2FIX(pm_node_line_number_cached(node, scope_node));
911 case PM_STRING_NODE: {
912 const pm_string_node_t *cast = (const pm_string_node_t *) node;
913 return parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
914 }
915 case PM_SYMBOL_NODE:
916 return ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) node));
917 case PM_TRUE_NODE:
918 return Qtrue;
919 default:
920 rb_bug("Don't have a literal value for node type %s", pm_node_type(PM_NODE_TYPE(node)));
921 return Qfalse;
922 }
923}
924
929pm_code_location(pm_scope_node_t *scope_node, const pm_node_t *node)
930{
931 const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(node);
932 const pm_line_column_t end_location = PM_NODE_END_LINE_COLUMN(node);
933
934 return (rb_code_location_t) {
935 .beg_pos = { .lineno = start_location.line, .column = start_location.column },
936 .end_pos = { .lineno = end_location.line, .column = end_location.column }
937 };
938}
939
945#define PM_BRANCH_COVERAGE_P(iseq) (ISEQ_COVERAGE(iseq) && ISEQ_BRANCH_COVERAGE(iseq))
946
947static void
948pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond,
949 LABEL *then_label, LABEL *else_label, pm_scope_node_t *scope_node);
950
951static void
952pm_compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_node_t *cond, LABEL *then_label, LABEL *else_label, pm_scope_node_t *scope_node)
953{
954 const pm_node_location_t location = PM_NODE_START_LOCATION(cond);
955
956 DECL_ANCHOR(seq);
957
958 LABEL *label = NEW_LABEL(location.line);
959 if (!then_label) then_label = label;
960 else if (!else_label) else_label = label;
961
962 pm_compile_branch_condition(iseq, seq, cond, then_label, else_label, scope_node);
963
964 if (LIST_INSN_SIZE_ONE(seq)) {
965 INSN *insn = (INSN *) ELEM_FIRST_INSN(FIRST_ELEMENT(seq));
966 if (insn->insn_id == BIN(jump) && (LABEL *)(insn->operands[0]) == label) return;
967 }
968
969 if (label->refcnt) {
970 PUSH_LABEL(seq, label);
971 }
972
973 PUSH_SEQ(ret, seq);
974 return;
975}
976
977static void
978pm_compile_flip_flop_bound(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
979{
980 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
981
982 if (PM_NODE_TYPE_P(node, PM_INTEGER_NODE)) {
983 PM_COMPILE_NOT_POPPED(node);
984
985 VALUE operand = ID2SYM(rb_intern("$."));
986 PUSH_INSN1(ret, location, getglobal, operand);
987
988 PUSH_SEND(ret, location, idEq, INT2FIX(1));
989 if (popped) PUSH_INSN(ret, location, pop);
990 }
991 else {
992 PM_COMPILE(node);
993 }
994}
995
996static void
997pm_compile_flip_flop(const pm_flip_flop_node_t *flip_flop_node, LABEL *else_label, LABEL *then_label, rb_iseq_t *iseq, const int lineno, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
998{
999 const pm_node_location_t location = { .line = lineno, .node_id = -1 };
1000 LABEL *lend = NEW_LABEL(location.line);
1001
1002 int again = !(flip_flop_node->base.flags & PM_RANGE_FLAGS_EXCLUDE_END);
1003
1004 rb_num_t count = ISEQ_FLIP_CNT_INCREMENT(ISEQ_BODY(iseq)->local_iseq) + VM_SVAR_FLIPFLOP_START;
1005 VALUE key = INT2FIX(count);
1006
1007 PUSH_INSN2(ret, location, getspecial, key, INT2FIX(0));
1008 PUSH_INSNL(ret, location, branchif, lend);
1009
1010 if (flip_flop_node->left) {
1011 pm_compile_flip_flop_bound(iseq, flip_flop_node->left, ret, popped, scope_node);
1012 }
1013 else {
1014 PUSH_INSN(ret, location, putnil);
1015 }
1016
1017 PUSH_INSNL(ret, location, branchunless, else_label);
1018 PUSH_INSN1(ret, location, putobject, Qtrue);
1019 PUSH_INSN1(ret, location, setspecial, key);
1020 if (!again) {
1021 PUSH_INSNL(ret, location, jump, then_label);
1022 }
1023
1024 PUSH_LABEL(ret, lend);
1025 if (flip_flop_node->right) {
1026 pm_compile_flip_flop_bound(iseq, flip_flop_node->right, ret, popped, scope_node);
1027 }
1028 else {
1029 PUSH_INSN(ret, location, putnil);
1030 }
1031
1032 PUSH_INSNL(ret, location, branchunless, then_label);
1033 PUSH_INSN1(ret, location, putobject, Qfalse);
1034 PUSH_INSN1(ret, location, setspecial, key);
1035 PUSH_INSNL(ret, location, jump, then_label);
1036}
1037
1038static void pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition);
1039
1040static void
1041pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond, LABEL *then_label, LABEL *else_label, pm_scope_node_t *scope_node)
1042{
1043 const pm_node_location_t location = PM_NODE_START_LOCATION(cond);
1044
1045again:
1046 switch (PM_NODE_TYPE(cond)) {
1047 case PM_AND_NODE: {
1048 const pm_and_node_t *cast = (const pm_and_node_t *) cond;
1049 pm_compile_logical(iseq, ret, cast->left, NULL, else_label, scope_node);
1050
1051 cond = cast->right;
1052 goto again;
1053 }
1054 case PM_OR_NODE: {
1055 const pm_or_node_t *cast = (const pm_or_node_t *) cond;
1056 pm_compile_logical(iseq, ret, cast->left, then_label, NULL, scope_node);
1057
1058 cond = cast->right;
1059 goto again;
1060 }
1061 case PM_FALSE_NODE:
1062 case PM_NIL_NODE:
1063 PUSH_INSNL(ret, location, jump, else_label);
1064 return;
1065 case PM_FLOAT_NODE:
1066 case PM_IMAGINARY_NODE:
1067 case PM_INTEGER_NODE:
1068 case PM_LAMBDA_NODE:
1069 case PM_RATIONAL_NODE:
1070 case PM_REGULAR_EXPRESSION_NODE:
1071 case PM_STRING_NODE:
1072 case PM_SYMBOL_NODE:
1073 case PM_TRUE_NODE:
1074 PUSH_INSNL(ret, location, jump, then_label);
1075 return;
1076 case PM_FLIP_FLOP_NODE:
1077 pm_compile_flip_flop((const pm_flip_flop_node_t *) cond, else_label, then_label, iseq, location.line, ret, false, scope_node);
1078 return;
1079 case PM_DEFINED_NODE: {
1080 const pm_defined_node_t *cast = (const pm_defined_node_t *) cond;
1081 pm_compile_defined_expr(iseq, cast->value, &location, ret, false, scope_node, true);
1082 break;
1083 }
1084 default: {
1085 DECL_ANCHOR(cond_seq);
1086 pm_compile_node(iseq, cond, cond_seq, false, scope_node);
1087
1088 if (LIST_INSN_SIZE_ONE(cond_seq)) {
1089 INSN *insn = (INSN *) ELEM_FIRST_INSN(FIRST_ELEMENT(cond_seq));
1090
1091 if (insn->insn_id == BIN(putobject)) {
1092 if (RTEST(insn->operands[0])) {
1093 PUSH_INSNL(ret, location, jump, then_label);
1094 // maybe unreachable
1095 return;
1096 }
1097 else {
1098 PUSH_INSNL(ret, location, jump, else_label);
1099 return;
1100 }
1101 }
1102 }
1103
1104 PUSH_SEQ(ret, cond_seq);
1105 break;
1106 }
1107 }
1108
1109 PUSH_INSNL(ret, location, branchunless, else_label);
1110 PUSH_INSNL(ret, location, jump, then_label);
1111}
1112
1116static void
1117pm_compile_conditional(rb_iseq_t *iseq, const pm_node_location_t *node_location, pm_node_type_t type, const pm_node_t *node, const pm_statements_node_t *statements, const pm_node_t *subsequent, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1118{
1119 const pm_node_location_t location = *node_location;
1120 LABEL *then_label = NEW_LABEL(location.line);
1121 LABEL *else_label = NEW_LABEL(location.line);
1122 LABEL *end_label = NULL;
1123
1124 DECL_ANCHOR(cond_seq);
1125 pm_compile_branch_condition(iseq, cond_seq, predicate, then_label, else_label, scope_node);
1126 PUSH_SEQ(ret, cond_seq);
1127
1128 rb_code_location_t conditional_location = { 0 };
1129 VALUE branches = Qfalse;
1130
1131 if (then_label->refcnt && else_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) {
1132 conditional_location = pm_code_location(scope_node, node);
1133 branches = decl_branch_base(iseq, (int) node->node_id, &conditional_location, type == PM_IF_NODE ? "if" : "unless");
1134 }
1135
1136 if (then_label->refcnt) {
1137 PUSH_LABEL(ret, then_label);
1138
1139 DECL_ANCHOR(then_seq);
1140
1141 if (statements != NULL) {
1142 pm_compile_node(iseq, (const pm_node_t *) statements, then_seq, popped, scope_node);
1143 }
1144 else if (!popped) {
1145 PUSH_SYNTHETIC_PUTNIL(then_seq, iseq);
1146 }
1147
1148 if (else_label->refcnt) {
1149 // Establish branch coverage for the then block.
1150 if (PM_BRANCH_COVERAGE_P(iseq)) {
1151 rb_code_location_t branch_location;
1152
1153 if (statements != NULL) {
1154 branch_location = pm_code_location(scope_node, (const pm_node_t *) statements);
1155 } else if (type == PM_IF_NODE) {
1156 pm_line_column_t predicate_end = PM_NODE_END_LINE_COLUMN(predicate);
1157 branch_location = (rb_code_location_t) {
1158 .beg_pos = { .lineno = predicate_end.line, .column = predicate_end.column },
1159 .end_pos = { .lineno = predicate_end.line, .column = predicate_end.column }
1160 };
1161 } else {
1162 branch_location = conditional_location;
1163 }
1164
1165 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, type == PM_IF_NODE ? "then" : "else", branches);
1166 }
1167
1168 end_label = NEW_LABEL(location.line);
1169 PUSH_INSNL(then_seq, location, jump, end_label);
1170 if (!popped) PUSH_INSN(then_seq, location, pop);
1171 }
1172
1173 PUSH_SEQ(ret, then_seq);
1174 }
1175
1176 if (else_label->refcnt) {
1177 PUSH_LABEL(ret, else_label);
1178
1179 DECL_ANCHOR(else_seq);
1180
1181 if (subsequent != NULL) {
1182 pm_compile_node(iseq, subsequent, else_seq, popped, scope_node);
1183 }
1184 else if (!popped) {
1185 PUSH_SYNTHETIC_PUTNIL(else_seq, iseq);
1186 }
1187
1188 // Establish branch coverage for the else block.
1189 if (then_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) {
1190 rb_code_location_t branch_location;
1191
1192 if (subsequent == NULL) {
1193 branch_location = conditional_location;
1194 } else if (PM_NODE_TYPE_P(subsequent, PM_ELSE_NODE)) {
1195 const pm_else_node_t *else_node = (const pm_else_node_t *) subsequent;
1196 branch_location = pm_code_location(scope_node, else_node->statements != NULL ? ((const pm_node_t *) else_node->statements) : (const pm_node_t *) else_node);
1197 } else {
1198 branch_location = pm_code_location(scope_node, (const pm_node_t *) subsequent);
1199 }
1200
1201 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 1, type == PM_IF_NODE ? "else" : "then", branches);
1202 }
1203
1204 PUSH_SEQ(ret, else_seq);
1205 }
1206
1207 if (end_label) {
1208 PUSH_LABEL(ret, end_label);
1209 }
1210
1211 return;
1212}
1213
1217static void
1218pm_compile_loop(rb_iseq_t *iseq, const pm_node_location_t *node_location, pm_node_flags_t flags, enum pm_node_type type, const pm_node_t *node, const pm_statements_node_t *statements, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1219{
1220 const pm_node_location_t location = *node_location;
1221
1222 LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
1223 LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
1224 LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
1225
1226 LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(location.line); /* next */
1227 LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(location.line); /* redo */
1228 LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(location.line); /* break */
1229 LABEL *end_label = NEW_LABEL(location.line);
1230 LABEL *adjust_label = NEW_LABEL(location.line);
1231
1232 LABEL *next_catch_label = NEW_LABEL(location.line);
1233 LABEL *tmp_label = NULL;
1234
1235 // We're pushing onto the ensure stack because breaks need to break out of
1236 // this loop and not break into the ensure statements within the same
1237 // lexical scope.
1239 push_ensure_entry(iseq, &enl, NULL, NULL);
1240
1241 // begin; end while true
1242 if (flags & PM_LOOP_FLAGS_BEGIN_MODIFIER) {
1243 tmp_label = NEW_LABEL(location.line);
1244 PUSH_INSNL(ret, location, jump, tmp_label);
1245 }
1246 else {
1247 // while true; end
1248 PUSH_INSNL(ret, location, jump, next_label);
1249 }
1250
1251 PUSH_LABEL(ret, adjust_label);
1252 PUSH_INSN(ret, location, putnil);
1253 PUSH_LABEL(ret, next_catch_label);
1254 PUSH_INSN(ret, location, pop);
1255 PUSH_INSNL(ret, location, jump, next_label);
1256 if (tmp_label) PUSH_LABEL(ret, tmp_label);
1257
1258 PUSH_LABEL(ret, redo_label);
1259
1260 // Establish branch coverage for the loop.
1261 if (PM_BRANCH_COVERAGE_P(iseq)) {
1262 rb_code_location_t loop_location = pm_code_location(scope_node, node);
1263 VALUE branches = decl_branch_base(iseq, (int) node->node_id, &loop_location, type == PM_WHILE_NODE ? "while" : "until");
1264
1265 rb_code_location_t branch_location = statements != NULL ? pm_code_location(scope_node, (const pm_node_t *) statements) : loop_location;
1266 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, "body", branches);
1267 }
1268
1269 if (statements != NULL) PM_COMPILE_POPPED((const pm_node_t *) statements);
1270 PUSH_LABEL(ret, next_label);
1271
1272 if (type == PM_WHILE_NODE) {
1273 pm_compile_branch_condition(iseq, ret, predicate, redo_label, end_label, scope_node);
1274 }
1275 else if (type == PM_UNTIL_NODE) {
1276 pm_compile_branch_condition(iseq, ret, predicate, end_label, redo_label, scope_node);
1277 }
1278
1279 PUSH_LABEL(ret, end_label);
1280 PUSH_ADJUST_RESTORE(ret, adjust_label);
1281 PUSH_INSN(ret, location, putnil);
1282
1283 PUSH_LABEL(ret, break_label);
1284 if (popped) PUSH_INSN(ret, location, pop);
1285
1286 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL, break_label);
1287 PUSH_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL, next_catch_label);
1288 PUSH_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL, ISEQ_COMPILE_DATA(iseq)->redo_label);
1289
1290 ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
1291 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
1292 ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
1293 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->prev;
1294
1295 return;
1296}
1297
1298// This recurses through scopes and finds the local index at any scope level
1299// It also takes a pointer to depth, and increments depth appropriately
1300// according to the depth of the local.
1301static pm_local_index_t
1302pm_lookup_local_index(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, pm_constant_id_t constant_id, int start_depth)
1303{
1304 pm_local_index_t lindex = { 0 };
1305 int local_index;
1306
1307 int level;
1308 for (level = 0; level < start_depth; level++) {
1309 scope_node = scope_node->previous;
1310 }
1311
1312 while (!pm_index_lookup_table_lookup(&scope_node->index_lookup_table, constant_id, &local_index))
1313 {
1314 level++;
1315
1316 if (scope_node->previous) {
1317 scope_node = scope_node->previous;
1318 }
1319 else {
1320 // We have recursed up all scope nodes
1321 // and have not found the local yet
1322 rb_bug("Local with constant_id %u does not exist", (unsigned int) constant_id);
1323 }
1324 }
1325
1326 lindex.level = level;
1327 lindex.index = scope_node->local_table_for_iseq_size - (int) local_index;
1328 return lindex;
1329}
1330
1331// This returns the CRuby ID which maps to the pm_constant_id_t
1332//
1333// We add a constants mapping on the scope_node which holds one CRuby ID per
1334// constant in prism's constant pool, filled in pool order. pm_constant_id_index
1335// maps a constant_id to its position in that mapping.
1336// This helper method allows easy access to those IDs
1337static inline ID
1338pm_constant_id_lookup(const pm_scope_node_t *scope_node, pm_constant_id_t constant_id)
1339{
1340 RUBY_ASSERT(constant_id != 0);
1341 size_t index = pm_constant_id_index(constant_id);
1342 RUBY_ASSERT(index < pm_parser_constants_size(scope_node->parser));
1343 return scope_node->constants[index];
1344}
1345
1346static rb_iseq_t *
1347pm_new_child_iseq(rb_iseq_t *iseq, pm_scope_node_t *node, VALUE name, const rb_iseq_t *parent, enum rb_iseq_type type, int line_no)
1348{
1349 debugs("[new_child_iseq]> ---------------------------------------\n");
1350 int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
1351 rb_iseq_t *ret_iseq = pm_iseq_build(node, name,
1352 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
1353 line_no, parent,
1354 isolated_depth ? isolated_depth + 1 : 0,
1355 type, ISEQ_COMPILE_DATA(iseq)->option);
1356 debugs("[new_child_iseq]< ---------------------------------------\n");
1357 return ret_iseq;
1358}
1359
1360static int
1361pm_cpath_const_p(const pm_node_t *node)
1362{
1363 switch (PM_NODE_TYPE(node)) {
1364 case PM_CONSTANT_READ_NODE:
1365 return TRUE;
1366 case PM_CONSTANT_PATH_NODE:
1367 {
1368 const pm_node_t *parent = ((const pm_constant_path_node_t *) node)->parent;
1369 if (!parent) return TRUE; /* ::Foo */
1370 return pm_cpath_const_p(parent);
1371 }
1372 default:
1373 return FALSE;
1374 }
1375}
1376
1377static int
1378pm_compile_class_path(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1379{
1380 if (PM_NODE_TYPE_P(node, PM_CONSTANT_PATH_NODE)) {
1381 const pm_node_t *parent = ((const pm_constant_path_node_t *) node)->parent;
1382
1383 if (parent) {
1384 /* Bar::Foo or expr::Foo */
1385 PM_COMPILE(parent);
1386 int flags = VM_DEFINECLASS_FLAG_SCOPED;
1387 if (!pm_cpath_const_p(parent)) {
1388 flags |= VM_DEFINECLASS_FLAG_DYNAMIC_CREF;
1389 }
1390 return flags;
1391 }
1392 else {
1393 /* toplevel class ::Foo */
1394 PUSH_INSN1(ret, *node_location, putobject, rb_cObject);
1395 return VM_DEFINECLASS_FLAG_SCOPED;
1396 }
1397 }
1398 else {
1399 /* class at cbase Foo */
1400 PUSH_INSN1(ret, *node_location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
1401 return 0;
1402 }
1403}
1404
1409static void
1410pm_compile_call_and_or_write_node(rb_iseq_t *iseq, bool and_node, const pm_node_t *receiver, const pm_node_t *value, pm_constant_id_t write_name, pm_constant_id_t read_name, bool safe_nav, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1411{
1412 const pm_node_location_t location = *node_location;
1413 LABEL *lfin = NEW_LABEL(location.line);
1414 LABEL *lcfin = NEW_LABEL(location.line);
1415 LABEL *lskip = NULL;
1416
1417 int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
1418 ID id_read_name = pm_constant_id_lookup(scope_node, read_name);
1419
1420 PM_COMPILE_NOT_POPPED(receiver);
1421 if (safe_nav) {
1422 lskip = NEW_LABEL(location.line);
1423 PUSH_INSN(ret, location, dup);
1424 PUSH_INSNL(ret, location, branchnil, lskip);
1425 }
1426
1427 PUSH_INSN(ret, location, dup);
1428 PUSH_SEND_WITH_FLAG(ret, location, id_read_name, INT2FIX(0), INT2FIX(flag));
1429 if (!popped) PUSH_INSN(ret, location, dup);
1430
1431 if (and_node) {
1432 PUSH_INSNL(ret, location, branchunless, lcfin);
1433 }
1434 else {
1435 PUSH_INSNL(ret, location, branchif, lcfin);
1436 }
1437
1438 if (!popped) PUSH_INSN(ret, location, pop);
1439 PM_COMPILE_NOT_POPPED(value);
1440
1441 if (!popped) {
1442 PUSH_INSN(ret, location, swap);
1443 PUSH_INSN1(ret, location, topn, INT2FIX(1));
1444 }
1445
1446 ID id_write_name = pm_constant_id_lookup(scope_node, write_name);
1447 PUSH_SEND_WITH_FLAG(ret, location, id_write_name, INT2FIX(1), INT2FIX(flag));
1448 PUSH_INSNL(ret, location, jump, lfin);
1449
1450 PUSH_LABEL(ret, lcfin);
1451 if (!popped) PUSH_INSN(ret, location, swap);
1452
1453 PUSH_LABEL(ret, lfin);
1454
1455 if (lskip && popped) PUSH_LABEL(ret, lskip);
1456 PUSH_INSN(ret, location, pop);
1457 if (lskip && !popped) PUSH_LABEL(ret, lskip);
1458}
1459
1460static void pm_compile_shareable_constant_value(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_flags_t shareability, VALUE path, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, bool top);
1461
1467static void
1468pm_compile_hash_elements(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *elements, const pm_node_flags_t shareability, VALUE path, bool argument, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
1469{
1470 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
1471
1472 // If this element is not popped, then we need to create the hash on the
1473 // stack. Neighboring plain assoc nodes should be grouped together (either
1474 // by newhash or hash merge). Double splat nodes should be merged using the
1475 // merge_kwd method call.
1476 const int max_stack_length = 0x100;
1477 const unsigned int min_tmp_hash_length = 0x800;
1478
1479 int stack_length = 0;
1480 bool first_chunk = true;
1481 bool owned_hash = false;
1482
1483 // This is an optimization wherein we keep track of whether or not the
1484 // previous element was a static literal. If it was, then we do not attempt
1485 // to check if we have a subhash that can be optimized. If it was not, then
1486 // we do check.
1487 bool static_literal = false;
1488
1489 DECL_ANCHOR(anchor);
1490
1491 // Convert pushed elements to a hash, and merge if needed.
1492#define FLUSH_CHUNK \
1493 if (stack_length) { \
1494 if (first_chunk) { \
1495 PUSH_SEQ(ret, anchor); \
1496 PUSH_INSN1(ret, location, newhash, INT2FIX(stack_length)); \
1497 first_chunk = false; \
1498 } \
1499 else { \
1500 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); \
1501 PUSH_INSN(ret, location, swap); \
1502 PUSH_SEQ(ret, anchor); \
1503 if (owned_hash) { \
1504 PUSH_SEND(ret, location, id_core_hash_merge_bang_ptr, INT2FIX(stack_length + 1)); \
1505 } \
1506 else { \
1507 PUSH_SEND(ret, location, id_core_hash_merge_ptr, INT2FIX(stack_length + 1)); \
1508 owned_hash = true; \
1509 } \
1510 } \
1511 INIT_ANCHOR(anchor); \
1512 stack_length = 0; \
1513 }
1514
1515 for (size_t index = 0; index < elements->size; index++) {
1516 const pm_node_t *element = elements->nodes[index];
1517
1518 switch (PM_NODE_TYPE(element)) {
1519 case PM_ASSOC_NODE: {
1520 // Pre-allocation check (this branch can be omitted).
1521 if (
1522 (shareability == 0) &&
1523 PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL) && (
1524 (!static_literal && ((index + min_tmp_hash_length) < elements->size)) ||
1525 (first_chunk && stack_length == 0)
1526 )
1527 ) {
1528 // Count the elements that are statically-known.
1529 size_t count = 1;
1530 while (index + count < elements->size && PM_NODE_FLAG_P(elements->nodes[index + count], PM_NODE_FLAG_STATIC_LITERAL)) count++;
1531
1532 bool first_element = first_chunk && stack_length == 0;
1533
1534 if (first_element || count >= min_tmp_hash_length) {
1535 // The subsequence of elements in this hash is long enough
1536 // to merit its own hash.
1537 VALUE ary = rb_ary_hidden_new(count);
1538
1539 // Create a hidden hash.
1540 for (size_t tmp_end = index + count; index < tmp_end; index++) {
1541 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[index];
1542
1543 VALUE elem[2] = {
1544 pm_static_literal_value(iseq, assoc->key, scope_node),
1545 pm_static_literal_value(iseq, assoc->value, scope_node)
1546 };
1547
1548 rb_ary_cat(ary, elem, 2);
1549 }
1550 index --;
1551
1552 VALUE hash = rb_hash_alloc_fixed_size(Qfalse, RARRAY_LEN(ary) / 2);
1553 rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary), hash);
1554 RB_GC_GUARD(ary);
1556
1557 // Emit optimized code.
1558 FLUSH_CHUNK;
1559 if (first_chunk) {
1560 if (count == elements->size) {
1561 // A fully literal hash.
1562 PUSH_INSN1(ret, location, duphash, hash);
1563 }
1564 else {
1565 // Partial hash that will be merged with a newly built one, no need to dup
1566 PUSH_INSN1(ret, location, putobject, rb_obj_reveal(hash, rb_cHash));
1567 }
1568 first_chunk = false;
1569 }
1570 else {
1571 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1572 PUSH_INSN(ret, location, swap);
1573 PUSH_INSN1(ret, location, putobject, hash);
1574 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1575 }
1576
1577 break;
1578 }
1579 else {
1580 static_literal = true;
1581 }
1582 }
1583 else {
1584 static_literal = false;
1585 }
1586
1587 // If this is a plain assoc node, then we can compile it directly
1588 // and then add the total number of values on the stack.
1589 if (shareability == 0) {
1590 pm_compile_node(iseq, element, anchor, false, scope_node);
1591 }
1592 else {
1593 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
1594 pm_compile_shareable_constant_value(iseq, assoc->key, shareability, path, ret, scope_node, false);
1595 pm_compile_shareable_constant_value(iseq, assoc->value, shareability, path, ret, scope_node, false);
1596 }
1597
1598 if ((stack_length += 2) >= max_stack_length) FLUSH_CHUNK;
1599 break;
1600 }
1601 case PM_ASSOC_SPLAT_NODE: {
1602 FLUSH_CHUNK;
1603
1604 const pm_assoc_splat_node_t *assoc_splat = (const pm_assoc_splat_node_t *) element;
1605 bool empty_hash = assoc_splat->value != NULL && (
1606 (PM_NODE_TYPE_P(assoc_splat->value, PM_HASH_NODE) && ((const pm_hash_node_t *) assoc_splat->value)->elements.size == 0) ||
1607 PM_NODE_TYPE_P(assoc_splat->value, PM_NIL_NODE)
1608 );
1609
1610 bool first_element = first_chunk && stack_length == 0;
1611 bool last_element = index == elements->size - 1;
1612 bool only_element = first_element && last_element;
1613
1614 if (only_element) {
1615 if (argument) {
1616 if (empty_hash) {
1617 // **{} appears at the only keyword argument in method call
1618 // We can substitute it for `**nil`.
1619 PUSH_INSN(ret, location, putnil);
1620 }
1621 else {
1622 // ** is the only element.
1623 // Since we're in a method call, we can use it directly.
1624 // This will be not be flagged as mutable.
1625 if (shareability == 0) {
1626 PM_COMPILE_NOT_POPPED(element);
1627 }
1628 else {
1629 pm_compile_shareable_constant_value(iseq, element, shareability, path, ret, scope_node, false);
1630 }
1631 }
1632 }
1633 else {
1634 // {**something}
1635 // We can simply call `core_hash_coerce(something)` to ensure it is coerced
1636 // into a mutable Hash.
1637 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1638 if (shareability == 0) {
1639 PM_COMPILE_NOT_POPPED(element);
1640 }
1641 else {
1642 pm_compile_shareable_constant_value(iseq, element, shareability, path, ret, scope_node, false);
1643 }
1644 PUSH_SEND(ret, location, id_core_hash_coerce, INT2FIX(1));
1645 }
1646 }
1647 else {
1648 if (!first_element) {
1649 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1650 PUSH_INSN(ret, location, swap);
1651 }
1652
1653 if (shareability == 0) {
1654 PM_COMPILE_NOT_POPPED(element);
1655 }
1656 else {
1657 pm_compile_shareable_constant_value(iseq, element, shareability, path, ret, scope_node, false);
1658 }
1659
1660 if (!first_element) {
1661 if (owned_hash) {
1662 PUSH_SEND(ret, location, id_core_hash_merge_bang_kwd, INT2FIX(2));
1663 }
1664 else {
1665 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1666 owned_hash = true;
1667 }
1668 }
1669 }
1670
1671 first_chunk = false;
1672 static_literal = false;
1673 break;
1674 }
1675 default:
1676 RUBY_ASSERT("Invalid node type for hash" && false);
1677 break;
1678 }
1679 }
1680
1681 FLUSH_CHUNK;
1682#undef FLUSH_CHUNK
1683}
1684
1685#define SPLATARRAY_FALSE 0
1686#define SPLATARRAY_TRUE 1
1687#define DUP_SINGLE_KW_SPLAT 2
1688
1689// This is details. Users should call pm_setup_args() instead.
1690static int
1691pm_setup_args_core(const pm_arguments_node_t *arguments_node, const pm_node_t *block, int *flags, const bool has_regular_blockarg, struct rb_callinfo_kwarg **kw_arg, int *dup_rest, rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_node_location_t *node_location)
1692{
1693 const pm_node_location_t location = *node_location;
1694
1695 int orig_argc = 0;
1696 bool has_splat = false;
1697 bool has_keyword_splat = false;
1698
1699 if (arguments_node == NULL) {
1700 if (*flags & VM_CALL_FCALL) {
1701 *flags |= VM_CALL_VCALL;
1702 }
1703 }
1704 else {
1705 const pm_node_list_t *arguments = &arguments_node->arguments;
1706 has_keyword_splat = PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT);
1707
1708 // We count the number of elements post the splat node that are not keyword elements to
1709 // eventually pass as an argument to newarray
1710 int post_splat_counter = 0;
1711 const pm_node_t *argument;
1712
1713 PM_NODE_LIST_FOREACH(arguments, index, argument) {
1714 switch (PM_NODE_TYPE(argument)) {
1715 // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
1716 case PM_KEYWORD_HASH_NODE: {
1717 const pm_keyword_hash_node_t *keyword_arg = (const pm_keyword_hash_node_t *) argument;
1718 const pm_node_list_t *elements = &keyword_arg->elements;
1719
1720 if (has_keyword_splat || has_splat) {
1721 *flags |= VM_CALL_KW_SPLAT;
1722 has_keyword_splat = true;
1723
1724 if (elements->size > 1 || !(elements->size == 1 && PM_NODE_TYPE_P(elements->nodes[0], PM_ASSOC_SPLAT_NODE))) {
1725 // A new hash will be created for the keyword arguments
1726 // in this case, so mark the method as passing mutable
1727 // keyword splat.
1728 *flags |= VM_CALL_KW_SPLAT_MUT;
1729 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1730 }
1731 else if (*dup_rest & DUP_SINGLE_KW_SPLAT) {
1732 *flags |= VM_CALL_KW_SPLAT_MUT;
1733 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1734 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
1735 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1736 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1737 }
1738 else {
1739 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1740 }
1741 }
1742 else {
1743 // We need to first figure out if all elements of the
1744 // KeywordHashNode are AssocNodes with symbol keys.
1745 if (PM_NODE_FLAG_P(keyword_arg, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS)) {
1746 // If they are all symbol keys then we can pass them as
1747 // keyword arguments. The first thing we need to do is
1748 // deduplicate. We'll do this using the combination of a
1749 // Ruby hash and a Ruby array.
1750 VALUE stored_indices = rb_hash_new();
1751 VALUE keyword_indices = rb_ary_new_capa(elements->size);
1752
1753 size_t size = 0;
1754 for (size_t element_index = 0; element_index < elements->size; element_index++) {
1755 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1756
1757 // Retrieve the stored index from the hash for this
1758 // keyword.
1759 VALUE keyword = pm_static_literal_value(iseq, assoc->key, scope_node);
1760 VALUE stored_index = rb_hash_aref(stored_indices, keyword);
1761
1762 // If this keyword was already seen in the hash,
1763 // then mark the array at that index as false and
1764 // decrement the keyword size.
1765 if (!NIL_P(stored_index)) {
1766 rb_ary_store(keyword_indices, NUM2LEN(stored_index), Qfalse);
1767 size--;
1768 }
1769
1770 // Store (and possibly overwrite) the index for this
1771 // keyword in the hash, mark the array at that index
1772 // as true, and increment the keyword size.
1773 rb_hash_aset(stored_indices, keyword, ULONG2NUM(element_index));
1774 rb_ary_store(keyword_indices, (rb_len_t) element_index, Qtrue);
1775 size++;
1776 }
1777
1778 if (size > VM_CALL_KW_LEN_MAX) {
1779 COMPILE_ERROR(iseq, node_location->line, "too many keyword arguments (%d, maximum is %d)",
1780 (int) size, (int) VM_CALL_KW_LEN_MAX);
1781 }
1782
1783 *kw_arg = rb_xmalloc_mul_add(size, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
1784 *flags |= VM_CALL_KWARG;
1785
1786 VALUE *keywords = (*kw_arg)->keywords;
1787 (*kw_arg)->references = 0;
1788 (*kw_arg)->keyword_len = (int) size;
1789
1790 size_t keyword_index = 0;
1791 for (size_t element_index = 0; element_index < elements->size; element_index++) {
1792 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1793 bool popped = true;
1794
1795 if (rb_ary_entry(keyword_indices, (rb_len_t) element_index) == Qtrue) {
1796 keywords[keyword_index++] = pm_static_literal_value(iseq, assoc->key, scope_node);
1797 popped = false;
1798 }
1799
1800 PM_COMPILE(assoc->value);
1801 }
1802
1803 RUBY_ASSERT(keyword_index == size);
1804 }
1805 else {
1806 // If they aren't all symbol keys then we need to
1807 // construct a new hash and pass that as an argument.
1808 orig_argc++;
1809 *flags |= VM_CALL_KW_SPLAT;
1810
1811 size_t size = elements->size;
1812 if (size > 1) {
1813 // A new hash will be created for the keyword
1814 // arguments in this case, so mark the method as
1815 // passing mutable keyword splat.
1816 *flags |= VM_CALL_KW_SPLAT_MUT;
1817 }
1818
1819 for (size_t element_index = 0; element_index < size; element_index++) {
1820 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1821 PM_COMPILE_NOT_POPPED(assoc->key);
1822 PM_COMPILE_NOT_POPPED(assoc->value);
1823 }
1824
1825 PUSH_INSN1(ret, location, newhash, INT2FIX(size * 2));
1826 }
1827 }
1828 break;
1829 }
1830 case PM_SPLAT_NODE: {
1831 *flags |= VM_CALL_ARGS_SPLAT;
1832 const pm_splat_node_t *splat_node = (const pm_splat_node_t *) argument;
1833
1834 if (splat_node->expression) {
1835 PM_COMPILE_NOT_POPPED(splat_node->expression);
1836 }
1837 else {
1838 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
1839 PUSH_GETLOCAL(ret, location, index.index, index.level);
1840 }
1841
1842 bool first_splat = !has_splat;
1843
1844 if (first_splat) {
1845 // If this is the first splat array seen and it's not the
1846 // last parameter, we want splatarray to dup it.
1847 //
1848 // foo(a, *b, c)
1849 // ^^
1850 if (index + 1 < arguments->size || has_regular_blockarg) {
1851 PUSH_INSN1(ret, location, splatarray, (*dup_rest & SPLATARRAY_TRUE) ? Qtrue : Qfalse);
1852 if (*dup_rest & SPLATARRAY_TRUE) *dup_rest &= ~SPLATARRAY_TRUE;
1853 }
1854 // If this is the first spalt array seen and it's the last
1855 // parameter, we don't want splatarray to dup it.
1856 //
1857 // foo(a, *b)
1858 // ^^
1859 else {
1860 PUSH_INSN1(ret, location, splatarray, Qfalse);
1861 }
1862 }
1863 else {
1864 // If this is not the first splat array seen and it is also
1865 // the last parameter, we don't want splatarray to dup it
1866 // and we need to concat the array.
1867 //
1868 // foo(a, *b, *c)
1869 // ^^
1870 PUSH_INSN(ret, location, concattoarray);
1871 }
1872
1873 has_splat = true;
1874 post_splat_counter = 0;
1875
1876 break;
1877 }
1878 case PM_FORWARDING_ARGUMENTS_NODE: { // not counted in argc return value
1879 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
1880
1881 if (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
1882 *flags |= VM_CALL_FORWARDING;
1883
1884 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_DOT3, 0);
1885 PUSH_GETLOCAL(ret, location, mult_local.index, mult_local.level);
1886
1887 break;
1888 }
1889
1890 if (has_splat) {
1891 // If we already have a splat, we're concatenating to existing array
1892 orig_argc += 1;
1893 } else {
1894 orig_argc += 2;
1895 }
1896
1897 *flags |= VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KW_SPLAT;
1898
1899 // Forwarding arguments nodes are treated as foo(*, **, &)
1900 // So foo(...) equals foo(*, **, &) and as such the local
1901 // table for this method is known in advance
1902 //
1903 // Push the *
1904 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
1905 PUSH_GETLOCAL(ret, location, mult_local.index, mult_local.level);
1906
1907 if (has_splat) {
1908 // If we already have a splat, we need to concatenate arrays
1909 PUSH_INSN(ret, location, concattoarray);
1910 } else {
1911 PUSH_INSN1(ret, location, splatarray, Qfalse);
1912 }
1913
1914 // Push the **
1915 pm_local_index_t pow_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_POW, 0);
1916 PUSH_GETLOCAL(ret, location, pow_local.index, pow_local.level);
1917
1918 // Push the &
1919 pm_local_index_t and_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_AND, 0);
1920 PUSH_INSN2(ret, location, getblockparamproxy, INT2FIX(and_local.index + VM_ENV_DATA_SIZE - 1), INT2FIX(and_local.level));
1921
1922 break;
1923 }
1924 default: {
1925 post_splat_counter++;
1926 PM_COMPILE_NOT_POPPED(argument);
1927
1928 // If we have a splat and we've seen a splat, we need to process
1929 // everything after the splat.
1930 if (has_splat) {
1931 // Stack items are turned into an array and concatenated in
1932 // the following cases:
1933 //
1934 // If the next node is a splat:
1935 //
1936 // foo(*a, b, *c)
1937 //
1938 // If the next node is a kwarg or kwarg splat:
1939 //
1940 // foo(*a, b, c: :d)
1941 // foo(*a, b, **c)
1942 //
1943 // If the next node is a forwarding argument:
1944 //
1945 // foo(*a, b, ...)
1946 //
1947 // If the next node is NULL (we have hit the end):
1948 //
1949 // foo(*a, b)
1950 if (index == arguments->size - 1) {
1951 RUBY_ASSERT(post_splat_counter > 0);
1952 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(post_splat_counter));
1953 }
1954 else {
1955 pm_node_t *next_arg = arguments->nodes[index + 1];
1956
1957 switch (PM_NODE_TYPE(next_arg)) {
1958 // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
1959 case PM_KEYWORD_HASH_NODE: {
1960 PUSH_INSN1(ret, location, newarray, INT2FIX(post_splat_counter));
1961 PUSH_INSN(ret, location, concatarray);
1962 break;
1963 }
1964 case PM_SPLAT_NODE: {
1965 PUSH_INSN1(ret, location, newarray, INT2FIX(post_splat_counter));
1966 PUSH_INSN(ret, location, concatarray);
1967 break;
1968 }
1969 case PM_FORWARDING_ARGUMENTS_NODE: {
1970 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(post_splat_counter));
1971 break;
1972 }
1973 default:
1974 break;
1975 }
1976 }
1977 }
1978 else {
1979 orig_argc++;
1980 }
1981 }
1982 }
1983 }
1984 }
1985
1986 if (has_splat) orig_argc++;
1987 if (has_keyword_splat) orig_argc++;
1988 return orig_argc;
1989}
1990
1995static inline bool
1996pm_setup_args_dup_rest_p(const pm_node_t *node)
1997{
1998 switch (PM_NODE_TYPE(node)) {
1999 case PM_BACK_REFERENCE_READ_NODE:
2000 case PM_CLASS_VARIABLE_READ_NODE:
2001 case PM_CONSTANT_READ_NODE:
2002 case PM_FALSE_NODE:
2003 case PM_FLOAT_NODE:
2004 case PM_GLOBAL_VARIABLE_READ_NODE:
2005 case PM_IMAGINARY_NODE:
2006 case PM_INSTANCE_VARIABLE_READ_NODE:
2007 case PM_INTEGER_NODE:
2008 case PM_LAMBDA_NODE:
2009 case PM_LOCAL_VARIABLE_READ_NODE:
2010 case PM_NIL_NODE:
2011 case PM_NUMBERED_REFERENCE_READ_NODE:
2012 case PM_RATIONAL_NODE:
2013 case PM_REGULAR_EXPRESSION_NODE:
2014 case PM_SELF_NODE:
2015 case PM_STRING_NODE:
2016 case PM_SYMBOL_NODE:
2017 case PM_TRUE_NODE:
2018 return false;
2019 case PM_CONSTANT_PATH_NODE: {
2021 if (cast->parent != NULL) {
2022 return pm_setup_args_dup_rest_p(cast->parent);
2023 }
2024 return false;
2025 }
2026 case PM_IMPLICIT_NODE:
2027 return pm_setup_args_dup_rest_p(((const pm_implicit_node_t *) node)->value);
2028 case PM_ARRAY_NODE: {
2029 const pm_array_node_t *cast = (const pm_array_node_t *) node;
2030 for (size_t index = 0; index < cast->elements.size; index++) {
2031 if (pm_setup_args_dup_rest_p(cast->elements.nodes[index])) {
2032 return true;
2033 }
2034 }
2035 return false;
2036 }
2037 default:
2038 return true;
2039 }
2040}
2041
2045static int
2046pm_setup_args(const pm_arguments_node_t *arguments_node, const pm_node_t *block, int *flags, struct rb_callinfo_kwarg **kw_arg, rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_node_location_t *node_location)
2047{
2048 int dup_rest = SPLATARRAY_TRUE;
2049
2050 const pm_node_list_t *arguments;
2051 size_t arguments_size;
2052
2053 // Calls like foo(1, *f, **hash) that use splat and kwsplat could be
2054 // eligible for eliding duping the rest array (dup_reset=false).
2055 if (
2056 arguments_node != NULL &&
2057 (arguments = &arguments_node->arguments, arguments_size = arguments->size) >= 2 &&
2058 PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT) &&
2059 !PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS) &&
2060 PM_NODE_TYPE_P(arguments->nodes[arguments_size - 1], PM_KEYWORD_HASH_NODE)
2061 ) {
2062 // Start by assuming that dup_rest=false, then check each element of the
2063 // hash to ensure we don't need to flip it back to true (in case one of
2064 // the elements could potentially mutate the array).
2065 dup_rest = SPLATARRAY_FALSE;
2066
2067 const pm_keyword_hash_node_t *keyword_hash = (const pm_keyword_hash_node_t *) arguments->nodes[arguments_size - 1];
2068 const pm_node_list_t *elements = &keyword_hash->elements;
2069
2070 for (size_t index = 0; dup_rest == SPLATARRAY_FALSE && index < elements->size; index++) {
2071 const pm_node_t *element = elements->nodes[index];
2072
2073 switch (PM_NODE_TYPE(element)) {
2074 case PM_ASSOC_NODE: {
2075 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
2076 if (pm_setup_args_dup_rest_p(assoc->key) || pm_setup_args_dup_rest_p(assoc->value)) dup_rest = SPLATARRAY_TRUE;
2077 break;
2078 }
2079 case PM_ASSOC_SPLAT_NODE: {
2080 const pm_assoc_splat_node_t *assoc = (const pm_assoc_splat_node_t *) element;
2081 if (assoc->value != NULL && pm_setup_args_dup_rest_p(assoc->value)) dup_rest = SPLATARRAY_TRUE;
2082 break;
2083 }
2084 default:
2085 break;
2086 }
2087 }
2088 }
2089
2090 int initial_dup_rest = dup_rest;
2091 int argc;
2092
2093 if (block && PM_NODE_TYPE_P(block, PM_BLOCK_ARGUMENT_NODE)) {
2094 // We compile the `&block_arg` expression first and stitch it later
2095 // since the nature of the expression influences whether splat should
2096 // duplicate the array.
2097 bool regular_block_arg = true;
2098 const pm_node_t *block_expr = ((const pm_block_argument_node_t *)block)->expression;
2099
2100 if (block_expr && pm_setup_args_dup_rest_p(block_expr)) {
2101 dup_rest = SPLATARRAY_TRUE | DUP_SINGLE_KW_SPLAT;
2102 initial_dup_rest = dup_rest;
2103 }
2104
2105 DECL_ANCHOR(block_arg);
2106 pm_compile_node(iseq, block, block_arg, false, scope_node);
2107
2108 *flags |= VM_CALL_ARGS_BLOCKARG;
2109
2110 if (LIST_INSN_SIZE_ONE(block_arg)) {
2111 LINK_ELEMENT *elem = FIRST_ELEMENT(block_arg);
2112 if (IS_INSN(elem)) {
2113 INSN *iobj = (INSN *) elem;
2114 if (iobj->insn_id == BIN(getblockparam)) {
2115 iobj->insn_id = BIN(getblockparamproxy);
2116 }
2117
2118 // Allow splat without duplication for simple one-instruction
2119 // block arguments like `&arg`. It is known that this
2120 // optimization can be too aggressive in some cases. See
2121 // [Bug #16504].
2122 regular_block_arg = false;
2123 }
2124 }
2125
2126 argc = pm_setup_args_core(arguments_node, block, flags, regular_block_arg, kw_arg, &dup_rest, iseq, ret, scope_node, node_location);
2127 PUSH_SEQ(ret, block_arg);
2128 }
2129 else {
2130 argc = pm_setup_args_core(arguments_node, block, flags, false, kw_arg, &dup_rest, iseq, ret, scope_node, node_location);
2131 }
2132
2133 // If the dup_rest flag was consumed while compiling the arguments (which
2134 // effectively means we found the splat node), then it would have changed
2135 // during the call to pm_setup_args_core. In this case, we want to add the
2136 // VM_CALL_ARGS_SPLAT_MUT flag.
2137 if (*flags & VM_CALL_ARGS_SPLAT && dup_rest != initial_dup_rest) {
2138 *flags |= VM_CALL_ARGS_SPLAT_MUT;
2139 }
2140
2141 return argc;
2142}
2143
2154static void
2155pm_compile_index_operator_write_node(rb_iseq_t *iseq, const pm_index_operator_write_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
2156{
2157 const pm_node_location_t location = *node_location;
2158 if (!popped) PUSH_INSN(ret, location, putnil);
2159
2160 PM_COMPILE_NOT_POPPED(node->receiver);
2161
2162 int boff = (node->block == NULL ? 0 : 1);
2163 int flag = PM_NODE_TYPE_P(node->receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
2164 struct rb_callinfo_kwarg *keywords = NULL;
2165 int argc = pm_setup_args(node->arguments, (const pm_node_t *) node->block, &flag, &keywords, iseq, ret, scope_node, node_location);
2166
2167 if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
2168 if (boff) {
2169 PUSH_INSN(ret, location, splatkw);
2170 }
2171 else {
2172 PUSH_INSN(ret, location, dup);
2173 PUSH_INSN(ret, location, splatkw);
2174 PUSH_INSN(ret, location, pop);
2175 }
2176 }
2177
2178 int dup_argn = argc + 1 + boff;
2179 int keyword_len = 0;
2180
2181 if (keywords) {
2182 keyword_len = keywords->keyword_len;
2183 dup_argn += keyword_len;
2184 }
2185
2186 PUSH_INSN1(ret, location, dupn, INT2FIX(dup_argn));
2187 PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords);
2188 PM_COMPILE_NOT_POPPED(node->value);
2189
2190 ID id_operator = pm_constant_id_lookup(scope_node, node->binary_operator);
2191 PUSH_SEND(ret, location, id_operator, INT2FIX(1));
2192
2193 if (!popped) {
2194 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2195 }
2196 if (flag & VM_CALL_ARGS_SPLAT) {
2197 if (flag & VM_CALL_KW_SPLAT) {
2198 PUSH_INSN1(ret, location, topn, INT2FIX(2 + boff));
2199
2200 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2201 PUSH_INSN1(ret, location, splatarray, Qtrue);
2202 flag |= VM_CALL_ARGS_SPLAT_MUT;
2203 }
2204
2205 PUSH_INSN(ret, location, swap);
2206 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2207 PUSH_INSN1(ret, location, setn, INT2FIX(2 + boff));
2208 PUSH_INSN(ret, location, pop);
2209 }
2210 else {
2211 if (boff > 0) {
2212 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2213 PUSH_INSN(ret, location, swap);
2214 PUSH_INSN(ret, location, pop);
2215 }
2216 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2217 PUSH_INSN(ret, location, swap);
2218 PUSH_INSN1(ret, location, splatarray, Qtrue);
2219 PUSH_INSN(ret, location, swap);
2220 flag |= VM_CALL_ARGS_SPLAT_MUT;
2221 }
2222 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2223 if (boff > 0) {
2224 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2225 PUSH_INSN(ret, location, pop);
2226 PUSH_INSN(ret, location, pop);
2227 }
2228 }
2229
2230 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc), NULL, INT2FIX(flag), keywords);
2231 }
2232 else if (flag & VM_CALL_KW_SPLAT) {
2233 if (boff > 0) {
2234 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2235 PUSH_INSN(ret, location, swap);
2236 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2237 PUSH_INSN(ret, location, pop);
2238 }
2239 PUSH_INSN(ret, location, swap);
2240 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2241 }
2242 else if (keyword_len) {
2243 PUSH_INSN(ret, location, dup);
2244 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 2));
2245 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 1));
2246 PUSH_INSN(ret, location, pop);
2247 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2248 }
2249 else {
2250 if (boff > 0) {
2251 PUSH_INSN(ret, location, swap);
2252 }
2253 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2254 }
2255
2256 PUSH_INSN(ret, location, pop);
2257}
2258
2271static void
2272pm_compile_index_control_flow_write_node(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_t *receiver, const pm_arguments_node_t *arguments, const pm_block_argument_node_t *block, const pm_node_t *value, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
2273{
2274 const pm_node_location_t location = *node_location;
2275 if (!popped) PUSH_INSN(ret, location, putnil);
2276 PM_COMPILE_NOT_POPPED(receiver);
2277
2278 int boff = (block == NULL ? 0 : 1);
2279 int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
2280 struct rb_callinfo_kwarg *keywords = NULL;
2281 int argc = pm_setup_args(arguments, (const pm_node_t *) block, &flag, &keywords, iseq, ret, scope_node, node_location);
2282
2283 if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
2284 if (boff) {
2285 PUSH_INSN(ret, location, splatkw);
2286 }
2287 else {
2288 PUSH_INSN(ret, location, dup);
2289 PUSH_INSN(ret, location, splatkw);
2290 PUSH_INSN(ret, location, pop);
2291 }
2292 }
2293
2294 int dup_argn = argc + 1 + boff;
2295 int keyword_len = 0;
2296
2297 if (keywords) {
2298 keyword_len = keywords->keyword_len;
2299 dup_argn += keyword_len;
2300 }
2301
2302 PUSH_INSN1(ret, location, dupn, INT2FIX(dup_argn));
2303 PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords);
2304
2305 LABEL *label = NEW_LABEL(location.line);
2306 LABEL *lfin = NEW_LABEL(location.line);
2307
2308 PUSH_INSN(ret, location, dup);
2309 if (PM_NODE_TYPE_P(node, PM_INDEX_AND_WRITE_NODE)) {
2310 PUSH_INSNL(ret, location, branchunless, label);
2311 }
2312 else {
2313 PUSH_INSNL(ret, location, branchif, label);
2314 }
2315
2316 PUSH_INSN(ret, location, pop);
2317 PM_COMPILE_NOT_POPPED(value);
2318
2319 if (!popped) {
2320 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2321 }
2322
2323 if (flag & VM_CALL_ARGS_SPLAT) {
2324 if (flag & VM_CALL_KW_SPLAT) {
2325 PUSH_INSN1(ret, location, topn, INT2FIX(2 + boff));
2326 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2327 PUSH_INSN1(ret, location, splatarray, Qtrue);
2328 flag |= VM_CALL_ARGS_SPLAT_MUT;
2329 }
2330
2331 PUSH_INSN(ret, location, swap);
2332 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2333 PUSH_INSN1(ret, location, setn, INT2FIX(2 + boff));
2334 PUSH_INSN(ret, location, pop);
2335 }
2336 else {
2337 if (boff > 0) {
2338 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2339 PUSH_INSN(ret, location, swap);
2340 PUSH_INSN(ret, location, pop);
2341 }
2342 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2343 PUSH_INSN(ret, location, swap);
2344 PUSH_INSN1(ret, location, splatarray, Qtrue);
2345 PUSH_INSN(ret, location, swap);
2346 flag |= VM_CALL_ARGS_SPLAT_MUT;
2347 }
2348 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2349 if (boff > 0) {
2350 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2351 PUSH_INSN(ret, location, pop);
2352 PUSH_INSN(ret, location, pop);
2353 }
2354 }
2355
2356 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc), NULL, INT2FIX(flag), keywords);
2357 }
2358 else if (flag & VM_CALL_KW_SPLAT) {
2359 if (boff > 0) {
2360 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2361 PUSH_INSN(ret, location, swap);
2362 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2363 PUSH_INSN(ret, location, pop);
2364 }
2365
2366 PUSH_INSN(ret, location, swap);
2367 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2368 }
2369 else if (keyword_len) {
2370 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 1));
2371 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 0));
2372 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2373 }
2374 else {
2375 if (boff > 0) {
2376 PUSH_INSN(ret, location, swap);
2377 }
2378 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2379 }
2380
2381 PUSH_INSN(ret, location, pop);
2382 PUSH_INSNL(ret, location, jump, lfin);
2383 PUSH_LABEL(ret, label);
2384 if (!popped) {
2385 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2386 }
2387 PUSH_INSN1(ret, location, adjuststack, INT2FIX(dup_argn + 1));
2388 PUSH_LABEL(ret, lfin);
2389}
2390
2391// When we compile a pattern matching expression, we use the stack as a scratch
2392// space to store lots of different values (consider it like we have a pattern
2393// matching function and we need space for a bunch of different local
2394// variables). The "base index" refers to the index on the stack where we
2395// started compiling the pattern matching expression. These offsets from that
2396// base index indicate the location of the various locals we need.
2397#define PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE 0
2398#define PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING 1
2399#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P 2
2400#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE 3
2401#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY 4
2402
2403// A forward declaration because this is the recursive function that handles
2404// compiling a pattern. It can be reentered by nesting patterns, as in the case
2405// of arrays or hashes.
2406static int pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *matched_label, LABEL *unmatched_label, bool in_single_pattern, bool use_deconstructed_cache, unsigned int base_index);
2407
2412static int
2413pm_compile_pattern_generic_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, VALUE message, unsigned int base_index)
2414{
2415 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2416 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2417
2418 PUSH_INSN(ret, location, dup);
2419 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2420
2421 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2422 PUSH_INSN1(ret, location, putobject, message);
2423 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2424 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(2));
2425 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2426
2427 PUSH_INSN1(ret, location, putobject, Qfalse);
2428 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2429
2430 PUSH_INSN(ret, location, pop);
2431 PUSH_INSN(ret, location, pop);
2432 PUSH_LABEL(ret, match_succeeded_label);
2433
2434 return COMPILE_OK;
2435}
2436
2442static int
2443pm_compile_pattern_length_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, VALUE message, VALUE length, unsigned int base_index)
2444{
2445 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2446 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2447
2448 PUSH_INSN(ret, location, dup);
2449 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2450
2451 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2452 PUSH_INSN1(ret, location, putobject, message);
2453 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2454 PUSH_INSN(ret, location, dup);
2455 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2456 PUSH_INSN1(ret, location, putobject, length);
2457 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(4));
2458 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2459
2460 PUSH_INSN1(ret, location, putobject, Qfalse);
2461 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2462
2463 PUSH_INSN(ret, location, pop);
2464 PUSH_INSN(ret, location, pop);
2465 PUSH_LABEL(ret, match_succeeded_label);
2466
2467 return COMPILE_OK;
2468}
2469
2475static int
2476pm_compile_pattern_eqq_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, unsigned int base_index)
2477{
2478 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2479 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2480
2481 PUSH_INSN(ret, location, dup);
2482 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2483 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2484
2485 VALUE operand = rb_fstring_lit("%p === %p does not return true");
2486 PUSH_INSN1(ret, location, putobject, operand);
2487
2488 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2489 PUSH_INSN1(ret, location, topn, INT2FIX(5));
2490 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2491 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2492 PUSH_INSN1(ret, location, putobject, Qfalse);
2493 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2494 PUSH_INSN(ret, location, pop);
2495 PUSH_INSN(ret, location, pop);
2496
2497 PUSH_LABEL(ret, match_succeeded_label);
2498 PUSH_INSN1(ret, location, setn, INT2FIX(2));
2499 PUSH_INSN(ret, location, pop);
2500 PUSH_INSN(ret, location, pop);
2501
2502 return COMPILE_OK;
2503}
2504
2511static int
2512pm_compile_pattern_match(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *unmatched_label, bool in_single_pattern, bool use_deconstructed_cache, unsigned int base_index)
2513{
2514 LABEL *matched_label = NEW_LABEL(pm_node_line_number_cached(node, scope_node));
2515 CHECK(pm_compile_pattern(iseq, scope_node, node, ret, matched_label, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index));
2516 PUSH_LABEL(ret, matched_label);
2517 return COMPILE_OK;
2518}
2519
2525static int
2526pm_compile_pattern_deconstruct(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *deconstruct_label, LABEL *match_failed_label, LABEL *deconstructed_label, LABEL *type_error_label, bool in_single_pattern, bool use_deconstructed_cache, unsigned int base_index)
2527{
2528 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2529
2530 if (use_deconstructed_cache) {
2531 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2532 PUSH_INSNL(ret, location, branchnil, deconstruct_label);
2533
2534 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2535 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2536
2537 PUSH_INSN(ret, location, pop);
2538 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE - 1));
2539 PUSH_INSNL(ret, location, jump, deconstructed_label);
2540 }
2541 else {
2542 PUSH_INSNL(ret, location, jump, deconstruct_label);
2543 }
2544
2545 PUSH_LABEL(ret, deconstruct_label);
2546 PUSH_INSN(ret, location, dup);
2547
2548 VALUE operand = ID2SYM(rb_intern("deconstruct"));
2549 PUSH_INSN1(ret, location, putobject, operand);
2550 PUSH_SEND(ret, location, idRespond_to, INT2FIX(1));
2551
2552 if (use_deconstructed_cache) {
2553 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE + 1));
2554 }
2555
2556 if (in_single_pattern) {
2557 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p does not respond to #deconstruct"), base_index + 1));
2558 }
2559
2560 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2561 PUSH_SEND(ret, location, rb_intern("deconstruct"), INT2FIX(0));
2562
2563 if (use_deconstructed_cache) {
2564 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2565 }
2566
2567 PUSH_INSN(ret, location, dup);
2568 PUSH_INSN1(ret, location, checktype, INT2FIX(T_ARRAY));
2569 PUSH_INSNL(ret, location, branchunless, type_error_label);
2570 PUSH_LABEL(ret, deconstructed_label);
2571
2572 return COMPILE_OK;
2573}
2574
2579static int
2580pm_compile_pattern_constant(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *match_failed_label, bool in_single_pattern, unsigned int base_index)
2581{
2582 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2583
2584 PUSH_INSN(ret, location, dup);
2585 PM_COMPILE_NOT_POPPED(node);
2586
2587 if (in_single_pattern) {
2588 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
2589 }
2590 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
2591 if (in_single_pattern) {
2592 CHECK(pm_compile_pattern_eqq_error(iseq, scope_node, node, ret, base_index + 3));
2593 }
2594 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2595 return COMPILE_OK;
2596}
2597
2602static void
2603pm_compile_pattern_error_handler(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *done_label, bool popped)
2604{
2605 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2606 LABEL *key_error_label = NEW_LABEL(location.line);
2607 LABEL *cleanup_label = NEW_LABEL(location.line);
2608
2609 struct rb_callinfo_kwarg *kw_arg = rb_xmalloc_mul_add(2, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
2610 kw_arg->references = 0;
2611 kw_arg->keyword_len = 2;
2612 kw_arg->keywords[0] = ID2SYM(rb_intern("matchee"));
2613 kw_arg->keywords[1] = ID2SYM(rb_intern("key"));
2614
2615 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2616 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2617 PUSH_INSNL(ret, location, branchif, key_error_label);
2618
2619 PUSH_INSN1(ret, location, putobject, rb_eNoMatchingPatternError);
2620 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2621
2622 {
2623 VALUE operand = rb_fstring_lit("%p: %s");
2624 PUSH_INSN1(ret, location, putobject, operand);
2625 }
2626
2627 PUSH_INSN1(ret, location, topn, INT2FIX(4));
2628 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 6));
2629 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2630 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2631 PUSH_INSNL(ret, location, jump, cleanup_label);
2632
2633 PUSH_LABEL(ret, key_error_label);
2634 PUSH_INSN1(ret, location, putobject, rb_eNoMatchingPatternKeyError);
2635 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2636
2637 {
2638 VALUE operand = rb_fstring_lit("%p: %s");
2639 PUSH_INSN1(ret, location, putobject, operand);
2640 }
2641
2642 PUSH_INSN1(ret, location, topn, INT2FIX(4));
2643 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 6));
2644 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2645 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE + 4));
2646 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY + 5));
2647 PUSH_SEND_R(ret, location, rb_intern("new"), INT2FIX(1), NULL, INT2FIX(VM_CALL_KWARG), kw_arg);
2648 PUSH_SEND(ret, location, id_core_raise, INT2FIX(1));
2649 PUSH_LABEL(ret, cleanup_label);
2650
2651 PUSH_INSN1(ret, location, adjuststack, INT2FIX(7));
2652 if (!popped) PUSH_INSN(ret, location, putnil);
2653 PUSH_INSNL(ret, location, jump, done_label);
2654 PUSH_INSN1(ret, location, dupn, INT2FIX(5));
2655 if (popped) PUSH_INSN(ret, location, putnil);
2656}
2657
2661static int
2662pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *matched_label, LABEL *unmatched_label, bool in_single_pattern, bool use_deconstructed_cache, unsigned int base_index)
2663{
2664 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2665
2666 switch (PM_NODE_TYPE(node)) {
2667 case PM_ARRAY_PATTERN_NODE: {
2668 // Array patterns in pattern matching are triggered by using commas in
2669 // a pattern or wrapping it in braces. They are represented by a
2670 // ArrayPatternNode. This looks like:
2671 //
2672 // foo => [1, 2, 3]
2673 //
2674 // It can optionally have a splat in the middle of it, which can
2675 // optionally have a name attached.
2677
2678 const size_t requireds_size = cast->requireds.size;
2679 const size_t posts_size = cast->posts.size;
2680 const size_t minimum_size = requireds_size + posts_size;
2681
2682 bool rest_named = false;
2683 bool use_rest_size = false;
2684
2685 if (cast->rest != NULL) {
2686 rest_named = (PM_NODE_TYPE_P(cast->rest, PM_SPLAT_NODE) && ((const pm_splat_node_t *) cast->rest)->expression != NULL);
2687 use_rest_size = (rest_named || (!rest_named && posts_size > 0));
2688 }
2689
2690 LABEL *match_failed_label = NEW_LABEL(location.line);
2691 LABEL *type_error_label = NEW_LABEL(location.line);
2692 LABEL *deconstruct_label = NEW_LABEL(location.line);
2693 LABEL *deconstructed_label = NEW_LABEL(location.line);
2694
2695 if (use_rest_size) {
2696 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2697 PUSH_INSN(ret, location, swap);
2698 base_index++;
2699 }
2700
2701 if (cast->constant != NULL) {
2702 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2703 }
2704
2705 CHECK(pm_compile_pattern_deconstruct(iseq, scope_node, node, ret, deconstruct_label, match_failed_label, deconstructed_label, type_error_label, in_single_pattern, use_deconstructed_cache, base_index));
2706
2707 PUSH_INSN(ret, location, dup);
2708 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2709 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2710 PUSH_SEND(ret, location, cast->rest == NULL ? idEq : idGE, INT2FIX(1));
2711 if (in_single_pattern) {
2712 VALUE message = cast->rest == NULL ? rb_fstring_lit("%p length mismatch (given %p, expected %p)") : rb_fstring_lit("%p length mismatch (given %p, expected %p+)");
2713 CHECK(pm_compile_pattern_length_error(iseq, scope_node, node, ret, message, INT2FIX(minimum_size), base_index + 1));
2714 }
2715 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2716
2717 for (size_t index = 0; index < requireds_size; index++) {
2718 const pm_node_t *required = cast->requireds.nodes[index];
2719 PUSH_INSN(ret, location, dup);
2720 PUSH_INSN1(ret, location, putobject, INT2FIX(index));
2721 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2722 CHECK(pm_compile_pattern_match(iseq, scope_node, required, ret, match_failed_label, in_single_pattern, false, base_index + 1));
2723 }
2724
2725 if (cast->rest != NULL) {
2726 if (rest_named) {
2727 PUSH_INSN(ret, location, dup);
2728 PUSH_INSN1(ret, location, putobject, INT2FIX(requireds_size));
2729 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2730 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2731 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2732 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2733 PUSH_INSN1(ret, location, setn, INT2FIX(4));
2734 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2735 CHECK(pm_compile_pattern_match(iseq, scope_node, ((const pm_splat_node_t *) cast->rest)->expression, ret, match_failed_label, in_single_pattern, false, base_index + 1));
2736 }
2737 else if (posts_size > 0) {
2738 PUSH_INSN(ret, location, dup);
2739 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2740 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2741 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2742 PUSH_INSN1(ret, location, setn, INT2FIX(2));
2743 PUSH_INSN(ret, location, pop);
2744 }
2745 }
2746
2747 for (size_t index = 0; index < posts_size; index++) {
2748 const pm_node_t *post = cast->posts.nodes[index];
2749 PUSH_INSN(ret, location, dup);
2750
2751 PUSH_INSN1(ret, location, putobject, INT2FIX(requireds_size + index));
2752 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2753 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2754 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2755 CHECK(pm_compile_pattern_match(iseq, scope_node, post, ret, match_failed_label, in_single_pattern, false, base_index + 1));
2756 }
2757
2758 PUSH_INSN(ret, location, pop);
2759 if (use_rest_size) {
2760 PUSH_INSN(ret, location, pop);
2761 }
2762
2763 PUSH_INSNL(ret, location, jump, matched_label);
2764 PUSH_INSN(ret, location, putnil);
2765 if (use_rest_size) {
2766 PUSH_INSN(ret, location, putnil);
2767 }
2768
2769 PUSH_LABEL(ret, type_error_label);
2770 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2771 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2772
2773 {
2774 VALUE operand = rb_fstring_lit("deconstruct must return Array");
2775 PUSH_INSN1(ret, location, putobject, operand);
2776 }
2777
2778 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2779 PUSH_INSN(ret, location, pop);
2780
2781 PUSH_LABEL(ret, match_failed_label);
2782 PUSH_INSN(ret, location, pop);
2783 if (use_rest_size) {
2784 PUSH_INSN(ret, location, pop);
2785 }
2786
2787 PUSH_INSNL(ret, location, jump, unmatched_label);
2788 break;
2789 }
2790 case PM_FIND_PATTERN_NODE: {
2791 // Find patterns in pattern matching are triggered by using commas in
2792 // a pattern or wrapping it in braces and using a splat on both the left
2793 // and right side of the pattern. This looks like:
2794 //
2795 // foo => [*, 1, 2, 3, *]
2796 //
2797 // There can be any number of requireds in the middle. The splats on
2798 // both sides can optionally have names attached.
2799 const pm_find_pattern_node_t *cast = (const pm_find_pattern_node_t *) node;
2800 const size_t size = cast->requireds.size;
2801
2802 LABEL *match_failed_label = NEW_LABEL(location.line);
2803 LABEL *type_error_label = NEW_LABEL(location.line);
2804 LABEL *deconstruct_label = NEW_LABEL(location.line);
2805 LABEL *deconstructed_label = NEW_LABEL(location.line);
2806
2807 if (cast->constant) {
2808 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2809 }
2810
2811 CHECK(pm_compile_pattern_deconstruct(iseq, scope_node, node, ret, deconstruct_label, match_failed_label, deconstructed_label, type_error_label, in_single_pattern, use_deconstructed_cache, base_index));
2812
2813 PUSH_INSN(ret, location, dup);
2814 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2815 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2816 PUSH_SEND(ret, location, idGE, INT2FIX(1));
2817 if (in_single_pattern) {
2818 CHECK(pm_compile_pattern_length_error(iseq, scope_node, node, ret, rb_fstring_lit("%p length mismatch (given %p, expected %p+)"), INT2FIX(size), base_index + 1));
2819 }
2820 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2821
2822 {
2823 LABEL *while_begin_label = NEW_LABEL(location.line);
2824 LABEL *next_loop_label = NEW_LABEL(location.line);
2825 LABEL *find_succeeded_label = NEW_LABEL(location.line);
2826 LABEL *find_failed_label = NEW_LABEL(location.line);
2827
2828 PUSH_INSN(ret, location, dup);
2829 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2830
2831 PUSH_INSN(ret, location, dup);
2832 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2833 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2834 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2835 PUSH_LABEL(ret, while_begin_label);
2836
2837 PUSH_INSN(ret, location, dup);
2838 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2839 PUSH_SEND(ret, location, idLE, INT2FIX(1));
2840 PUSH_INSNL(ret, location, branchunless, find_failed_label);
2841
2842 for (size_t index = 0; index < size; index++) {
2843 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2844 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2845
2846 if (index != 0) {
2847 PUSH_INSN1(ret, location, putobject, INT2FIX(index));
2848 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2849 }
2850
2851 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2852 CHECK(pm_compile_pattern_match(iseq, scope_node, cast->requireds.nodes[index], ret, next_loop_label, in_single_pattern, false, base_index + 4));
2853 }
2854
2855 const pm_splat_node_t *left = cast->left;
2856
2857 if (left->expression != NULL) {
2858 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2859 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2860 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2861 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2862 CHECK(pm_compile_pattern_match(iseq, scope_node, left->expression, ret, find_failed_label, in_single_pattern, false, base_index + 4));
2863 }
2864
2865 const pm_splat_node_t *right = cast->right;
2866
2867 if (right->expression != NULL) {
2868 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2869 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2870 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2871 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2872 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2873 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2874 pm_compile_pattern_match(iseq, scope_node, right->expression, ret, find_failed_label, in_single_pattern, false, base_index + 4);
2875 }
2876
2877 PUSH_INSNL(ret, location, jump, find_succeeded_label);
2878
2879 PUSH_LABEL(ret, next_loop_label);
2880 PUSH_INSN1(ret, location, putobject, INT2FIX(1));
2881 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2882 PUSH_INSNL(ret, location, jump, while_begin_label);
2883
2884 PUSH_LABEL(ret, find_failed_label);
2885 PUSH_INSN1(ret, location, adjuststack, INT2FIX(3));
2886 if (in_single_pattern) {
2887 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2888
2889 {
2890 VALUE operand = rb_fstring_lit("%p does not match to find pattern");
2891 PUSH_INSN1(ret, location, putobject, operand);
2892 }
2893
2894 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2895 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(2));
2896 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2897
2898 PUSH_INSN1(ret, location, putobject, Qfalse);
2899 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2900
2901 PUSH_INSN(ret, location, pop);
2902 PUSH_INSN(ret, location, pop);
2903 }
2904 PUSH_INSNL(ret, location, jump, match_failed_label);
2905 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2906
2907 PUSH_LABEL(ret, find_succeeded_label);
2908 PUSH_INSN1(ret, location, adjuststack, INT2FIX(3));
2909 }
2910
2911 PUSH_INSN(ret, location, pop);
2912 PUSH_INSNL(ret, location, jump, matched_label);
2913 PUSH_INSN(ret, location, putnil);
2914
2915 PUSH_LABEL(ret, type_error_label);
2916 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2917 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2918
2919 {
2920 VALUE operand = rb_fstring_lit("deconstruct must return Array");
2921 PUSH_INSN1(ret, location, putobject, operand);
2922 }
2923
2924 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2925 PUSH_INSN(ret, location, pop);
2926
2927 PUSH_LABEL(ret, match_failed_label);
2928 PUSH_INSN(ret, location, pop);
2929 PUSH_INSNL(ret, location, jump, unmatched_label);
2930
2931 break;
2932 }
2933 case PM_HASH_PATTERN_NODE: {
2934 // Hash patterns in pattern matching are triggered by using labels and
2935 // values in a pattern or by using the ** operator. They are represented
2936 // by the HashPatternNode. This looks like:
2937 //
2938 // foo => { a: 1, b: 2, **bar }
2939 //
2940 // It can optionally have an assoc splat in the middle of it, which can
2941 // optionally have a name.
2942 const pm_hash_pattern_node_t *cast = (const pm_hash_pattern_node_t *) node;
2943
2944 // We don't consider it a "rest" parameter if it's a ** that is unnamed.
2945 bool has_rest = cast->rest != NULL && !(PM_NODE_TYPE_P(cast->rest, PM_ASSOC_SPLAT_NODE) && ((const pm_assoc_splat_node_t *) cast->rest)->value == NULL);
2946 bool has_keys = cast->elements.size > 0 || cast->rest != NULL;
2947
2948 LABEL *match_failed_label = NEW_LABEL(location.line);
2949 LABEL *type_error_label = NEW_LABEL(location.line);
2950 VALUE keys = Qnil;
2951
2952 if (has_keys && !has_rest) {
2953 keys = rb_ary_new_capa(cast->elements.size);
2954
2955 for (size_t index = 0; index < cast->elements.size; index++) {
2956 const pm_node_t *element = cast->elements.nodes[index];
2957 RUBY_ASSERT(PM_NODE_TYPE_P(element, PM_ASSOC_NODE));
2958
2959 const pm_node_t *key = ((const pm_assoc_node_t *) element)->key;
2960 RUBY_ASSERT(PM_NODE_TYPE_P(key, PM_SYMBOL_NODE));
2961
2962 VALUE symbol = ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) key));
2963 rb_ary_push(keys, symbol);
2964 }
2965 }
2966
2967 if (cast->constant) {
2968 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2969 }
2970
2971 PUSH_INSN(ret, location, dup);
2972
2973 {
2974 VALUE operand = ID2SYM(rb_intern("deconstruct_keys"));
2975 PUSH_INSN1(ret, location, putobject, operand);
2976 }
2977
2978 PUSH_SEND(ret, location, idRespond_to, INT2FIX(1));
2979 if (in_single_pattern) {
2980 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p does not respond to #deconstruct_keys"), base_index + 1));
2981 }
2982 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2983
2984 if (NIL_P(keys)) {
2985 PUSH_INSN(ret, location, putnil);
2986 }
2987 else {
2988 rb_obj_hide(keys);
2990 PUSH_INSN1(ret, location, duparray, keys);
2991 RB_OBJ_WRITTEN(iseq, Qundef, keys);
2992 }
2993 PUSH_SEND(ret, location, rb_intern("deconstruct_keys"), INT2FIX(1));
2994
2995 PUSH_INSN(ret, location, dup);
2996 PUSH_INSN1(ret, location, checktype, INT2FIX(T_HASH));
2997 PUSH_INSNL(ret, location, branchunless, type_error_label);
2998
2999 if (has_rest) {
3000 PUSH_SEND(ret, location, rb_intern("dup"), INT2FIX(0));
3001 }
3002
3003 if (has_keys) {
3004 DECL_ANCHOR(match_values);
3005
3006 for (size_t index = 0; index < cast->elements.size; index++) {
3007 const pm_node_t *element = cast->elements.nodes[index];
3008 RUBY_ASSERT(PM_NODE_TYPE_P(element, PM_ASSOC_NODE));
3009
3010 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
3011 const pm_node_t *key = assoc->key;
3012 RUBY_ASSERT(PM_NODE_TYPE_P(key, PM_SYMBOL_NODE));
3013
3014 VALUE symbol = ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) key));
3015 PUSH_INSN(ret, location, dup);
3016 PUSH_INSN1(ret, location, putobject, symbol);
3017 PUSH_SEND(ret, location, rb_intern("key?"), INT2FIX(1));
3018
3019 if (in_single_pattern) {
3020 LABEL *match_succeeded_label = NEW_LABEL(location.line);
3021
3022 PUSH_INSN(ret, location, dup);
3023 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
3024
3025 {
3026 VALUE operand = rb_str_freeze(rb_sprintf("key not found: %+"PRIsVALUE, symbol));
3027 RB_OBJ_SET_SHAREABLE(operand);
3028 PUSH_INSN1(ret, location, putobject, operand);
3029 }
3030
3031 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 2));
3032 PUSH_INSN1(ret, location, putobject, Qtrue);
3033 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 3));
3034 PUSH_INSN1(ret, location, topn, INT2FIX(3));
3035 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE + 4));
3036 PUSH_INSN1(ret, location, putobject, symbol);
3037 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY + 5));
3038
3039 PUSH_INSN1(ret, location, adjuststack, INT2FIX(4));
3040 PUSH_LABEL(ret, match_succeeded_label);
3041 }
3042
3043 PUSH_INSNL(ret, location, branchunless, match_failed_label);
3044 PUSH_INSN(match_values, location, dup);
3045 PUSH_INSN1(match_values, location, putobject, symbol);
3046 PUSH_SEND(match_values, location, has_rest ? rb_intern("delete") : idAREF, INT2FIX(1));
3047
3048 const pm_node_t *value = assoc->value;
3049 if (PM_NODE_TYPE_P(value, PM_IMPLICIT_NODE)) {
3050 value = ((const pm_implicit_node_t *) value)->value;
3051 }
3052
3053 CHECK(pm_compile_pattern_match(iseq, scope_node, value, match_values, match_failed_label, in_single_pattern, false, base_index + 1));
3054 }
3055
3056 PUSH_SEQ(ret, match_values);
3057 }
3058 else {
3059 PUSH_INSN(ret, location, dup);
3060 PUSH_SEND(ret, location, idEmptyP, INT2FIX(0));
3061 if (in_single_pattern) {
3062 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p is not empty"), base_index + 1));
3063 }
3064 PUSH_INSNL(ret, location, branchunless, match_failed_label);
3065 }
3066
3067 if (has_rest) {
3068 switch (PM_NODE_TYPE(cast->rest)) {
3069 case PM_NO_KEYWORDS_PARAMETER_NODE: {
3070 PUSH_INSN(ret, location, dup);
3071 PUSH_SEND(ret, location, idEmptyP, INT2FIX(0));
3072 if (in_single_pattern) {
3073 pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("rest of %p is not empty"), base_index + 1);
3074 }
3075 PUSH_INSNL(ret, location, branchunless, match_failed_label);
3076 break;
3077 }
3078 case PM_ASSOC_SPLAT_NODE: {
3079 const pm_assoc_splat_node_t *splat = (const pm_assoc_splat_node_t *) cast->rest;
3080 PUSH_INSN(ret, location, dup);
3081 pm_compile_pattern_match(iseq, scope_node, splat->value, ret, match_failed_label, in_single_pattern, false, base_index + 1);
3082 break;
3083 }
3084 default:
3085 rb_bug("unreachable");
3086 break;
3087 }
3088 }
3089
3090 PUSH_INSN(ret, location, pop);
3091 PUSH_INSNL(ret, location, jump, matched_label);
3092 PUSH_INSN(ret, location, putnil);
3093
3094 PUSH_LABEL(ret, type_error_label);
3095 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
3096 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
3097
3098 {
3099 VALUE operand = rb_fstring_lit("deconstruct_keys must return Hash");
3100 PUSH_INSN1(ret, location, putobject, operand);
3101 }
3102
3103 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
3104 PUSH_INSN(ret, location, pop);
3105
3106 PUSH_LABEL(ret, match_failed_label);
3107 PUSH_INSN(ret, location, pop);
3108 PUSH_INSNL(ret, location, jump, unmatched_label);
3109 break;
3110 }
3111 case PM_CAPTURE_PATTERN_NODE: {
3112 // Capture patterns allow you to pattern match against an element in a
3113 // pattern and also capture the value into a local variable. This looks
3114 // like:
3115 //
3116 // [1] => [Integer => foo]
3117 //
3118 // In this case the `Integer => foo` will be represented by a
3119 // CapturePatternNode, which has both a value (the pattern to match
3120 // against) and a target (the place to write the variable into).
3122
3123 LABEL *match_failed_label = NEW_LABEL(location.line);
3124
3125 PUSH_INSN(ret, location, dup);
3126 CHECK(pm_compile_pattern_match(iseq, scope_node, cast->value, ret, match_failed_label, in_single_pattern, use_deconstructed_cache, base_index + 1));
3127 CHECK(pm_compile_pattern(iseq, scope_node, (const pm_node_t *) cast->target, ret, matched_label, match_failed_label, in_single_pattern, false, base_index));
3128 PUSH_INSN(ret, location, putnil);
3129
3130 PUSH_LABEL(ret, match_failed_label);
3131 PUSH_INSN(ret, location, pop);
3132 PUSH_INSNL(ret, location, jump, unmatched_label);
3133
3134 break;
3135 }
3136 case PM_LOCAL_VARIABLE_TARGET_NODE: {
3137 // Local variables can be targeted by placing identifiers in the place
3138 // of a pattern. For example, foo in bar. This results in the value
3139 // being matched being written to that local variable.
3141 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
3142
3143 PUSH_SETLOCAL(ret, location, index.index, index.level);
3144 PUSH_INSNL(ret, location, jump, matched_label);
3145 break;
3146 }
3147 case PM_ALTERNATION_PATTERN_NODE: {
3148 // Alternation patterns allow you to specify multiple patterns in a
3149 // single expression using the | operator.
3151
3152 LABEL *matched_left_label = NEW_LABEL(location.line);
3153 LABEL *unmatched_left_label = NEW_LABEL(location.line);
3154
3155 // First, we're going to attempt to match against the left pattern. If
3156 // that pattern matches, then we'll skip matching the right pattern.
3157 PUSH_INSN(ret, location, dup);
3158 CHECK(pm_compile_pattern(iseq, scope_node, cast->left, ret, matched_left_label, unmatched_left_label, in_single_pattern, use_deconstructed_cache, base_index + 1));
3159
3160 // If we get here, then we matched on the left pattern. In this case we
3161 // should pop out the duplicate value that we preemptively added to
3162 // match against the right pattern and then jump to the match label.
3163 PUSH_LABEL(ret, matched_left_label);
3164 PUSH_INSN(ret, location, pop);
3165 PUSH_INSNL(ret, location, jump, matched_label);
3166 PUSH_INSN(ret, location, putnil);
3167
3168 // If we get here, then we didn't match on the left pattern. In this
3169 // case we attempt to match against the right pattern.
3170 PUSH_LABEL(ret, unmatched_left_label);
3171 CHECK(pm_compile_pattern(iseq, scope_node, cast->right, ret, matched_label, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index));
3172 break;
3173 }
3174 case PM_PARENTHESES_NODE:
3175 // Parentheses are allowed to wrap expressions in pattern matching and
3176 // they do nothing since they can only wrap individual expressions and
3177 // not groups. In this case we'll recurse back into this same function
3178 // with the body of the parentheses.
3179 return pm_compile_pattern(iseq, scope_node, ((const pm_parentheses_node_t *) node)->body, ret, matched_label, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index);
3180 case PM_PINNED_EXPRESSION_NODE:
3181 // Pinned expressions are a way to match against the value of an
3182 // expression that should be evaluated at runtime. This looks like:
3183 // foo in ^(bar). To compile these, we compile the expression as if it
3184 // were a literal value by falling through to the literal case.
3185 node = ((const pm_pinned_expression_node_t *) node)->expression;
3186 /* fallthrough */
3187 case PM_ARRAY_NODE:
3188 case PM_CLASS_VARIABLE_READ_NODE:
3189 case PM_CONSTANT_PATH_NODE:
3190 case PM_CONSTANT_READ_NODE:
3191 case PM_FALSE_NODE:
3192 case PM_FLOAT_NODE:
3193 case PM_GLOBAL_VARIABLE_READ_NODE:
3194 case PM_IMAGINARY_NODE:
3195 case PM_INSTANCE_VARIABLE_READ_NODE:
3196 case PM_IT_LOCAL_VARIABLE_READ_NODE:
3197 case PM_INTEGER_NODE:
3198 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
3199 case PM_INTERPOLATED_STRING_NODE:
3200 case PM_INTERPOLATED_SYMBOL_NODE:
3201 case PM_INTERPOLATED_X_STRING_NODE:
3202 case PM_LAMBDA_NODE:
3203 case PM_LOCAL_VARIABLE_READ_NODE:
3204 case PM_NIL_NODE:
3205 case PM_SOURCE_ENCODING_NODE:
3206 case PM_SOURCE_FILE_NODE:
3207 case PM_SOURCE_LINE_NODE:
3208 case PM_RANGE_NODE:
3209 case PM_RATIONAL_NODE:
3210 case PM_REGULAR_EXPRESSION_NODE:
3211 case PM_SELF_NODE:
3212 case PM_STRING_NODE:
3213 case PM_SYMBOL_NODE:
3214 case PM_TRUE_NODE:
3215 case PM_X_STRING_NODE: {
3216 // These nodes are all simple patterns, which means we'll use the
3217 // checkmatch instruction to match against them, which is effectively a
3218 // VM-level === operator.
3219 PM_COMPILE_NOT_POPPED(node);
3220 if (in_single_pattern) {
3221 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
3222 }
3223
3224 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
3225
3226 if (in_single_pattern) {
3227 pm_compile_pattern_eqq_error(iseq, scope_node, node, ret, base_index + 2);
3228 }
3229
3230 PUSH_INSNL(ret, location, branchif, matched_label);
3231 PUSH_INSNL(ret, location, jump, unmatched_label);
3232 break;
3233 }
3234 case PM_PINNED_VARIABLE_NODE: {
3235 // Pinned variables are a way to match against the value of a variable
3236 // without it looking like you're trying to write to the variable. This
3237 // looks like: foo in ^@bar. To compile these, we compile the variable
3238 // that they hold.
3240 CHECK(pm_compile_pattern(iseq, scope_node, cast->variable, ret, matched_label, unmatched_label, in_single_pattern, true, base_index));
3241 break;
3242 }
3243 case PM_IF_NODE:
3244 case PM_UNLESS_NODE: {
3245 // If and unless nodes can show up here as guards on `in` clauses. This
3246 // looks like:
3247 //
3248 // case foo
3249 // in bar if baz?
3250 // qux
3251 // end
3252 //
3253 // Because we know they're in the modifier form and they can't have any
3254 // variation on this pattern, we compile them differently (more simply)
3255 // here than we would in the normal compilation path.
3256 const pm_node_t *predicate;
3257 const pm_node_t *statement;
3258
3259 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3260 const pm_if_node_t *cast = (const pm_if_node_t *) node;
3261 predicate = cast->predicate;
3262
3263 RUBY_ASSERT(cast->statements != NULL && cast->statements->body.size == 1);
3264 statement = cast->statements->body.nodes[0];
3265 }
3266 else {
3267 const pm_unless_node_t *cast = (const pm_unless_node_t *) node;
3268 predicate = cast->predicate;
3269
3270 RUBY_ASSERT(cast->statements != NULL && cast->statements->body.size == 1);
3271 statement = cast->statements->body.nodes[0];
3272 }
3273
3274 CHECK(pm_compile_pattern_match(iseq, scope_node, statement, ret, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index));
3275 PM_COMPILE_NOT_POPPED(predicate);
3276
3277 if (in_single_pattern) {
3278 LABEL *match_succeeded_label = NEW_LABEL(location.line);
3279
3280 PUSH_INSN(ret, location, dup);
3281 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3282 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
3283 }
3284 else {
3285 PUSH_INSNL(ret, location, branchunless, match_succeeded_label);
3286 }
3287
3288 {
3289 VALUE operand = rb_fstring_lit("guard clause does not return true");
3290 PUSH_INSN1(ret, location, putobject, operand);
3291 }
3292
3293 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
3294 PUSH_INSN1(ret, location, putobject, Qfalse);
3295 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
3296
3297 PUSH_INSN(ret, location, pop);
3298 PUSH_INSN(ret, location, pop);
3299
3300 PUSH_LABEL(ret, match_succeeded_label);
3301 }
3302
3303 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3304 PUSH_INSNL(ret, location, branchunless, unmatched_label);
3305 }
3306 else {
3307 PUSH_INSNL(ret, location, branchif, unmatched_label);
3308 }
3309
3310 PUSH_INSNL(ret, location, jump, matched_label);
3311 break;
3312 }
3313 default:
3314 // If we get here, then we have a node type that should not be in this
3315 // position. This would be a bug in the parser, because a different node
3316 // type should never have been created in this position in the tree.
3317 rb_bug("Unexpected node type in pattern matching expression: %s", pm_node_type(PM_NODE_TYPE(node)));
3318 break;
3319 }
3320
3321 return COMPILE_OK;
3322}
3323
3324#undef PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE
3325#undef PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING
3326#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P
3327#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE
3328#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY
3329
3330// Generate a scope node from the given node.
3331void
3332pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_t *previous)
3333{
3334 if (previous) {
3335 // Copy inherited fields from the parent scope in one shot, then
3336 // zero out the fields that are scope-specific.
3337 *scope = *previous;
3338 scope->locals = (pm_constant_id_list_t) { 0 };
3339 scope->parameters = NULL;
3340 scope->body = NULL;
3341 scope->local_table_for_iseq_size = 0;
3342 scope->index_lookup_table = (pm_index_lookup_table_t) PM_INDEX_LOOKUP_TABLE_INIT;
3343 scope->pre_execution_anchor = NULL;
3344 }
3345 else {
3346 memset(scope, 0, sizeof(pm_scope_node_t));
3347 }
3348
3349 scope->base.type = PM_SCOPE_NODE;
3350 scope->base.location.start = node->location.start;
3351 scope->base.location.length = node->location.length;
3352 scope->previous = previous;
3353 scope->ast_node = (pm_node_t *) node;
3354
3355 switch (PM_NODE_TYPE(node)) {
3356 case PM_BLOCK_NODE: {
3357 const pm_block_node_t *cast = (const pm_block_node_t *) node;
3358 scope->body = cast->body;
3359 scope->locals = cast->locals;
3360 scope->parameters = cast->parameters;
3361 break;
3362 }
3363 case PM_CLASS_NODE: {
3364 const pm_class_node_t *cast = (const pm_class_node_t *) node;
3365 scope->body = cast->body;
3366 scope->locals = cast->locals;
3367 break;
3368 }
3369 case PM_DEF_NODE: {
3370 const pm_def_node_t *cast = (const pm_def_node_t *) node;
3371 scope->parameters = (pm_node_t *) cast->parameters;
3372 scope->body = cast->body;
3373 scope->locals = cast->locals;
3374 break;
3375 }
3376 case PM_ENSURE_NODE: {
3377 const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node;
3378 scope->body = (pm_node_t *) node;
3379
3380 if (cast->statements != NULL) {
3381 scope->base.location.start = cast->statements->base.location.start;
3382 scope->base.location.length = cast->statements->base.location.length;
3383 }
3384
3385 break;
3386 }
3387 case PM_FOR_NODE: {
3388 const pm_for_node_t *cast = (const pm_for_node_t *) node;
3389 scope->body = (pm_node_t *) cast->statements;
3390 break;
3391 }
3392 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
3393 RUBY_ASSERT(node->flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE);
3394 scope->body = (pm_node_t *) node;
3395 break;
3396 }
3397 case PM_LAMBDA_NODE: {
3398 const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node;
3399 scope->parameters = cast->parameters;
3400 scope->body = cast->body;
3401 scope->locals = cast->locals;
3402 break;
3403 }
3404 case PM_MODULE_NODE: {
3405 const pm_module_node_t *cast = (const pm_module_node_t *) node;
3406 scope->body = cast->body;
3407 scope->locals = cast->locals;
3408 break;
3409 }
3410 case PM_POST_EXECUTION_NODE: {
3412 scope->body = (pm_node_t *) cast->statements;
3413 break;
3414 }
3415 case PM_PROGRAM_NODE: {
3416 const pm_program_node_t *cast = (const pm_program_node_t *) node;
3417 scope->body = (pm_node_t *) cast->statements;
3418 scope->locals = cast->locals;
3419 break;
3420 }
3421 case PM_RESCUE_NODE: {
3422 const pm_rescue_node_t *cast = (const pm_rescue_node_t *) node;
3423 scope->body = (pm_node_t *) cast->statements;
3424 break;
3425 }
3426 case PM_RESCUE_MODIFIER_NODE: {
3428 scope->body = (pm_node_t *) cast->rescue_expression;
3429 break;
3430 }
3431 case PM_SINGLETON_CLASS_NODE: {
3433 scope->body = cast->body;
3434 scope->locals = cast->locals;
3435 break;
3436 }
3437 case PM_STATEMENTS_NODE: {
3438 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
3439 scope->body = (pm_node_t *) cast;
3440 break;
3441 }
3442 default:
3443 rb_bug("unreachable");
3444 break;
3445 }
3446}
3447
3448void
3449pm_scope_node_destroy(pm_scope_node_t *scope_node)
3450{
3451 if (scope_node->index_lookup_table.owned) {
3452 xfree(scope_node->index_lookup_table.values);
3453 }
3454}
3455
3467static void
3468pm_compile_retry_end_label(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *retry_end_l)
3469{
3470 INSN *iobj;
3471 LINK_ELEMENT *last_elem = LAST_ELEMENT(ret);
3472 iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem);
3473 while (!IS_INSN_ID(iobj, send) && !IS_INSN_ID(iobj, invokesuper) && !IS_INSN_ID(iobj, sendforward) && !IS_INSN_ID(iobj, invokesuperforward)) {
3474 iobj = (INSN*) get_prev_insn(iobj);
3475 }
3476 ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l);
3477
3478 // LINK_ANCHOR has a pointer to the last element, but
3479 // ELEM_INSERT_NEXT does not update it even if we add an insn to the
3480 // last of LINK_ANCHOR. So this updates it manually.
3481 if (&iobj->link == LAST_ELEMENT(ret)) {
3482 ret->last = (LINK_ELEMENT*) retry_end_l;
3483 }
3484}
3485
3486static const char *
3487pm_iseq_builtin_function_name(const pm_scope_node_t *scope_node, const pm_node_t *receiver, ID method_id)
3488{
3489 const char *name = rb_id2name(method_id);
3490 static const char prefix[] = "__builtin_";
3491 const size_t prefix_len = sizeof(prefix) - 1;
3492
3493 if (receiver == NULL) {
3494 if (UNLIKELY(strncmp(prefix, name, prefix_len) == 0)) {
3495 // __builtin_foo
3496 return &name[prefix_len];
3497 }
3498 }
3499 else if (PM_NODE_TYPE_P(receiver, PM_CALL_NODE)) {
3500 if (PM_NODE_FLAG_P(receiver, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
3501 const pm_call_node_t *cast = (const pm_call_node_t *) receiver;
3502 if (pm_constant_id_lookup(scope_node, cast->name) == rb_intern_const("__builtin")) {
3503 // __builtin.foo
3504 return name;
3505 }
3506 }
3507 }
3508 else if (PM_NODE_TYPE_P(receiver, PM_CONSTANT_READ_NODE)) {
3509 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) receiver;
3510 if (pm_constant_id_lookup(scope_node, cast->name) == rb_intern_const("Primitive")) {
3511 // Primitive.foo
3512 return name;
3513 }
3514 }
3515
3516 return NULL;
3517}
3518
3519// Compile Primitive.attr! :leaf, ...
3520static int
3521pm_compile_builtin_attr(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_arguments_node_t *arguments, const pm_node_location_t *node_location)
3522{
3523 if (arguments == NULL) {
3524 COMPILE_ERROR(iseq, node_location->line, "attr!: no argument");
3525 return COMPILE_NG;
3526 }
3527
3528 const pm_node_t *argument;
3529 PM_NODE_LIST_FOREACH(&arguments->arguments, index, argument) {
3530 if (!PM_NODE_TYPE_P(argument, PM_SYMBOL_NODE)) {
3531 COMPILE_ERROR(iseq, node_location->line, "non symbol argument to attr!: %s", pm_node_type(PM_NODE_TYPE(argument)));
3532 return COMPILE_NG;
3533 }
3534
3535 VALUE symbol = pm_static_literal_value(iseq, argument, scope_node);
3536 if (compile_builtin_attr_symbol(iseq, symbol) != COMPILE_OK) {
3537 COMPILE_ERROR(iseq, node_location->line, "unknown argument to attr!: %" PRIsVALUE, symbol);
3538 return COMPILE_NG;
3539 }
3540 }
3541
3542 return COMPILE_OK;
3543}
3544
3545static int
3546pm_compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_scope_node_t *scope_node, const pm_arguments_node_t *arguments, const pm_node_location_t *node_location, int popped)
3547{
3548 if (arguments == NULL) {
3549 COMPILE_ERROR(iseq, node_location->line, "arg!: no argument");
3550 return COMPILE_NG;
3551 }
3552
3553 if (arguments->arguments.size != 1) {
3554 COMPILE_ERROR(iseq, node_location->line, "arg!: too many argument");
3555 return COMPILE_NG;
3556 }
3557
3558 const pm_node_t *argument = arguments->arguments.nodes[0];
3559 if (!PM_NODE_TYPE_P(argument, PM_SYMBOL_NODE)) {
3560 COMPILE_ERROR(iseq, node_location->line, "non symbol argument to arg!: %s", pm_node_type(PM_NODE_TYPE(argument)));
3561 return COMPILE_NG;
3562 }
3563
3564 if (!popped) {
3565 ID name = parse_string_symbol(scope_node, ((const pm_symbol_node_t *) argument));
3566 int index = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size - get_local_var_idx(iseq, name);
3567
3568 debugs("id: %s idx: %d\n", rb_id2name(name), index);
3569 PUSH_GETLOCAL(ret, *node_location, index, get_lvar_level(iseq));
3570 }
3571
3572 return COMPILE_OK;
3573}
3574
3575static int
3576pm_compile_builtin_mandatory_only_method(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_call_node_t *call_node, const pm_node_location_t *node_location)
3577{
3578 const pm_node_t *ast_node = scope_node->ast_node;
3579 if (!PM_NODE_TYPE_P(ast_node, PM_DEF_NODE)) {
3580 rb_bug("mandatory_only?: not in method definition");
3581 return COMPILE_NG;
3582 }
3583
3584 const pm_def_node_t *def_node = (const pm_def_node_t *) ast_node;
3585 const pm_parameters_node_t *parameters_node = def_node->parameters;
3586 if (parameters_node == NULL) {
3587 rb_bug("mandatory_only?: in method definition with no parameters");
3588 return COMPILE_NG;
3589 }
3590
3591 const pm_node_t *body_node = def_node->body;
3592 if (body_node == NULL || !PM_NODE_TYPE_P(body_node, PM_STATEMENTS_NODE) || (((const pm_statements_node_t *) body_node)->body.size != 1) || !PM_NODE_TYPE_P(((const pm_statements_node_t *) body_node)->body.nodes[0], PM_IF_NODE)) {
3593 rb_bug("mandatory_only?: not in method definition with plain statements");
3594 return COMPILE_NG;
3595 }
3596
3597 const pm_if_node_t *if_node = (const pm_if_node_t *) ((const pm_statements_node_t *) body_node)->body.nodes[0];
3598 if (if_node->predicate != ((const pm_node_t *) call_node)) {
3599 rb_bug("mandatory_only?: can't find mandatory node");
3600 return COMPILE_NG;
3601 }
3602
3603 pm_parameters_node_t parameters = {
3604 .base = parameters_node->base,
3605 .requireds = parameters_node->requireds
3606 };
3607
3608 const pm_def_node_t def = {
3609 .base = def_node->base,
3610 .name = def_node->name,
3611 .receiver = def_node->receiver,
3612 .parameters = &parameters,
3613 .body = (pm_node_t *) if_node->statements,
3614 .locals = {
3615 .ids = def_node->locals.ids,
3616 .size = parameters_node->requireds.size,
3617 .capacity = def_node->locals.capacity
3618 }
3619 };
3620
3621 pm_scope_node_t next_scope_node;
3622 pm_scope_node_init(&def.base, &next_scope_node, scope_node);
3623
3624 const rb_iseq_t *mandatory_only_iseq = pm_iseq_build(
3625 &next_scope_node,
3626 rb_iseq_base_label(iseq),
3627 rb_iseq_path(iseq),
3628 rb_iseq_realpath(iseq),
3629 node_location->line,
3630 NULL,
3631 0,
3632 ISEQ_TYPE_METHOD,
3633 ISEQ_COMPILE_DATA(iseq)->option
3634 );
3635 RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->mandatory_only_iseq, (VALUE)mandatory_only_iseq);
3636
3637 pm_scope_node_destroy(&next_scope_node);
3638 return COMPILE_OK;
3639}
3640
3641static int
3642pm_compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_call_node_t *call_node, const pm_node_location_t *node_location, int popped, const rb_iseq_t *parent_block, const char *builtin_func)
3643{
3644 const pm_arguments_node_t *arguments = call_node->arguments;
3645
3646 if (parent_block != NULL) {
3647 COMPILE_ERROR(iseq, node_location->line, "should not call builtins here.");
3648 return COMPILE_NG;
3649 }
3650
3651#define BUILTIN_INLINE_PREFIX "_bi"
3652 char inline_func[sizeof(BUILTIN_INLINE_PREFIX) + DECIMAL_SIZE_OF(int)];
3653 bool cconst = false;
3654retry:;
3655 const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
3656
3657 if (bf == NULL) {
3658 if (strcmp("cstmt!", builtin_func) == 0 || strcmp("cexpr!", builtin_func) == 0) {
3659 // ok
3660 }
3661 else if (strcmp("cconst!", builtin_func) == 0) {
3662 cconst = true;
3663 }
3664 else if (strcmp("cinit!", builtin_func) == 0) {
3665 // ignore
3666 return COMPILE_OK;
3667 }
3668 else if (strcmp("attr!", builtin_func) == 0) {
3669 return pm_compile_builtin_attr(iseq, scope_node, arguments, node_location);
3670 }
3671 else if (strcmp("arg!", builtin_func) == 0) {
3672 return pm_compile_builtin_arg(iseq, ret, scope_node, arguments, node_location, popped);
3673 }
3674 else if (strcmp("mandatory_only?", builtin_func) == 0) {
3675 if (popped) {
3676 rb_bug("mandatory_only? should be in if condition");
3677 }
3678 else if (!LIST_INSN_SIZE_ZERO(ret)) {
3679 rb_bug("mandatory_only? should be put on top");
3680 }
3681
3682 PUSH_INSN1(ret, *node_location, putobject, Qfalse);
3683 return pm_compile_builtin_mandatory_only_method(iseq, scope_node, call_node, node_location);
3684 }
3685 else if (strcmp("local_self!", builtin_func) == 0) {
3686 // Push the local named "self" (e.g. the `self:` keyword parameter
3687 // of Ractor.shareable_proc) onto the stack.
3688 pm_constant_id_t self_id = pm_parser_constant_find(scope_node->parser, (const uint8_t *) "self", 4);
3689 if (self_id == 0) {
3690 COMPILE_ERROR(iseq, node_location->line, "local_self! called but 'self' not found in local table");
3691 return COMPILE_NG;
3692 }
3693 pm_local_index_t self_index = pm_lookup_local_index(iseq, scope_node, self_id, /*start_depth=*/0);
3694 PUSH_GETLOCAL(ret, *node_location, self_index.index, self_index.level);
3695 return COMPILE_OK;
3696 }
3697 else if (1) {
3698 rb_bug("can't find builtin function:%s", builtin_func);
3699 }
3700 else {
3701 COMPILE_ERROR(iseq, node_location->line, "can't find builtin function:%s", builtin_func);
3702 return COMPILE_NG;
3703 }
3704
3705 int inline_index = node_location->line;
3706 snprintf(inline_func, sizeof(inline_func), BUILTIN_INLINE_PREFIX "%d", inline_index);
3707 builtin_func = inline_func;
3708 arguments = NULL;
3709 goto retry;
3710 }
3711
3712 if (cconst) {
3713 typedef VALUE(*builtin_func0)(void *, VALUE);
3714 VALUE const_val = (*(builtin_func0)(uintptr_t)bf->func_ptr)(NULL, Qnil);
3715 PUSH_INSN1(ret, *node_location, putobject, const_val);
3716 return COMPILE_OK;
3717 }
3718
3719 // fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
3720
3721 DECL_ANCHOR(args_seq);
3722
3723 int flags = 0;
3724 struct rb_callinfo_kwarg *keywords = NULL;
3725 int argc = pm_setup_args(arguments, call_node->block, &flags, &keywords, iseq, args_seq, scope_node, node_location);
3726
3727 if (argc != bf->argc) {
3728 COMPILE_ERROR(iseq, node_location->line, "argc is not match for builtin function:%s (expect %d but %d)", builtin_func, bf->argc, argc);
3729 return COMPILE_NG;
3730 }
3731
3732 unsigned int start_index;
3733 if (delegate_call_p(iseq, argc, args_seq, &start_index)) {
3734 PUSH_INSN2(ret, *node_location, opt_invokebuiltin_delegate, bf, INT2FIX(start_index));
3735 }
3736 else {
3737 PUSH_SEQ(ret, args_seq);
3738 PUSH_INSN1(ret, *node_location, invokebuiltin, bf);
3739 }
3740
3741 if (popped) PUSH_INSN(ret, *node_location, pop);
3742 return COMPILE_OK;
3743}
3744
3748static void
3749pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, ID method_id, LABEL *start)
3750{
3751 const pm_location_t *message_loc = &call_node->message_loc;
3752 if (message_loc->length == 0) message_loc = &call_node->base.location;
3753
3754 const pm_node_location_t location = PM_LOCATION_START_LOCATION(message_loc, call_node->base.node_id);
3755
3756 LABEL *else_label = NEW_LABEL(location.line);
3757 LABEL *end_label = NEW_LABEL(location.line);
3758 LABEL *retry_end_l = NEW_LABEL(location.line);
3759
3760 VALUE branches = Qfalse;
3761 rb_code_location_t code_location = { 0 };
3762 int node_id = location.node_id;
3763
3764 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
3765 if (PM_BRANCH_COVERAGE_P(iseq)) {
3766 uint32_t end_cursor = 0;
3767 bool end_found = false;
3768
3769 if (call_node->closing_loc.length > 0) {
3770 uint32_t cursor = call_node->closing_loc.start + call_node->closing_loc.length;
3771 end_cursor = cursor;
3772 end_found = true;
3773 }
3774
3775 if (call_node->arguments != NULL) {
3776 uint32_t cursor = call_node->arguments->base.location.start + call_node->arguments->base.location.length;
3777 if (!end_found || cursor > end_cursor) {
3778 end_cursor = cursor;
3779 end_found = true;
3780 }
3781 }
3782
3783 if (call_node->message_loc.length > 0) {
3784 uint32_t cursor = call_node->message_loc.start + call_node->message_loc.length;
3785 if (!end_found || cursor > end_cursor) {
3786 end_cursor = cursor;
3787 end_found = true;
3788 }
3789 }
3790
3791 if (!end_found) {
3792 end_cursor = call_node->closing_loc.start + call_node->closing_loc.length;
3793 }
3794
3795 const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(call_node);
3796 const pm_line_column_t end_location = pm_line_offset_list_line_column_cached(scope_node->line_offsets, end_cursor, scope_node->start_line, &scope_node->last_line);
3797
3798 code_location = (rb_code_location_t) {
3799 .beg_pos = { .lineno = start_location.line, .column = start_location.column },
3800 .end_pos = { .lineno = end_location.line, .column = end_location.column }
3801 };
3802
3803 branches = decl_branch_base(iseq, (int) call_node->base.node_id, &code_location, "&.");
3804 }
3805
3806 PUSH_INSN(ret, location, dup);
3807 PUSH_INSNL(ret, location, branchnil, else_label);
3808
3809 add_trace_branch_coverage(iseq, ret, &code_location, node_id, 0, "then", branches);
3810 }
3811
3812 LINK_ELEMENT *opt_new_prelude = LAST_ELEMENT(ret);
3813
3814 int flags = 0;
3815 struct rb_callinfo_kwarg *kw_arg = NULL;
3816
3817 int orig_argc = pm_setup_args(call_node->arguments, call_node->block, &flags, &kw_arg, iseq, ret, scope_node, &location);
3818 const rb_iseq_t *previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
3819 const rb_iseq_t *block_iseq = NULL;
3820
3821 if (call_node->block != NULL && PM_NODE_TYPE_P(call_node->block, PM_BLOCK_NODE)) {
3822 // Scope associated with the block
3823 pm_scope_node_t next_scope_node;
3824 pm_scope_node_init(call_node->block, &next_scope_node, scope_node);
3825
3826 block_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, pm_node_line_number_cached(call_node->block, scope_node));
3827 pm_scope_node_destroy(&next_scope_node);
3828 ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
3829 }
3830 else {
3831 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
3832 flags |= VM_CALL_VCALL;
3833 }
3834
3835 if (!flags) {
3836 flags |= VM_CALL_ARGS_SIMPLE;
3837 }
3838 }
3839
3840 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) {
3841 flags |= VM_CALL_FCALL;
3842 }
3843
3844 if (!popped && PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) {
3845 if (flags & VM_CALL_ARGS_BLOCKARG) {
3846 PUSH_INSN1(ret, location, topn, INT2FIX(1));
3847 if (flags & VM_CALL_ARGS_SPLAT) {
3848 PUSH_INSN1(ret, location, putobject, INT2FIX(-1));
3849 PUSH_SEND_WITH_FLAG(ret, location, idAREF, INT2FIX(1), INT2FIX(0));
3850 }
3851 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 3));
3852 PUSH_INSN(ret, location, pop);
3853 }
3854 else if (flags & VM_CALL_ARGS_SPLAT) {
3855 PUSH_INSN(ret, location, dup);
3856 PUSH_INSN1(ret, location, putobject, INT2FIX(-1));
3857 PUSH_SEND_WITH_FLAG(ret, location, idAREF, INT2FIX(1), INT2FIX(0));
3858 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 2));
3859 PUSH_INSN(ret, location, pop);
3860 }
3861 else {
3862 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 1));
3863 }
3864 }
3865
3866 if ((flags & VM_CALL_KW_SPLAT) && (flags & VM_CALL_ARGS_BLOCKARG) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
3867 PUSH_INSN(ret, location, splatkw);
3868 }
3869
3870 LABEL *not_basic_new = NEW_LABEL(location.line);
3871 LABEL *not_basic_new_finish = NEW_LABEL(location.line);
3872
3873 bool inline_new = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction &&
3874 method_id == rb_intern("new") &&
3875 call_node->block == NULL &&
3876 (flags & VM_CALL_ARGS_BLOCKARG) == 0;
3877
3878 if (inline_new) {
3879 if (LAST_ELEMENT(ret) == opt_new_prelude) {
3880 PUSH_INSN(ret, location, putnil);
3881 PUSH_INSN(ret, location, swap);
3882 }
3883 else {
3884 ELEM_INSERT_NEXT(opt_new_prelude, &new_insn_body(iseq, location.line, location.node_id, BIN(swap), 0)->link);
3885 ELEM_INSERT_NEXT(opt_new_prelude, &new_insn_body(iseq, location.line, location.node_id, BIN(putnil), 0)->link);
3886 }
3887
3888 rb_callinfo_kwarg_retain(kw_arg);
3889
3890 // Jump unless the receiver uses the "basic" implementation of "new"
3891 VALUE ci;
3892 if (flags & VM_CALL_FORWARDING) {
3893 ci = (VALUE)new_callinfo(iseq, method_id, orig_argc + 1, flags, kw_arg, 0);
3894 }
3895 else {
3896 ci = (VALUE)new_callinfo(iseq, method_id, orig_argc, flags, kw_arg, 0);
3897 }
3898
3899 PUSH_INSN2(ret, location, opt_new, ci, not_basic_new);
3900 LABEL_REF(not_basic_new);
3901 // optimized path
3902 PUSH_SEND_R(ret, location, rb_intern("initialize"), INT2FIX(orig_argc), block_iseq, INT2FIX(flags | VM_CALL_FCALL), kw_arg);
3903 PUSH_INSNL(ret, location, jump, not_basic_new_finish);
3904
3905 PUSH_LABEL(ret, not_basic_new);
3906 // Fall back to normal send
3907 PUSH_SEND_R(ret, location, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg);
3908 PUSH_INSN(ret, location, swap);
3909
3910 PUSH_LABEL(ret, not_basic_new_finish);
3911 PUSH_INSN(ret, location, pop);
3912
3913 rb_callinfo_kwarg_release(kw_arg);
3914 }
3915 else {
3916 PUSH_SEND_R(ret, location, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg);
3917 }
3918
3919 if (block_iseq && ISEQ_BODY(block_iseq)->catch_table) {
3920 pm_compile_retry_end_label(iseq, ret, retry_end_l);
3921 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, start, retry_end_l, block_iseq, retry_end_l);
3922 }
3923
3924 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
3925 PUSH_INSNL(ret, location, jump, end_label);
3926 PUSH_LABEL(ret, else_label);
3927 add_trace_branch_coverage(iseq, ret, &code_location, node_id, 1, "else", branches);
3928 PUSH_LABEL(ret, end_label);
3929 }
3930
3931 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && !popped) {
3932 PUSH_INSN(ret, location, pop);
3933 }
3934
3935 if (popped) PUSH_INSN(ret, location, pop);
3936 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
3937}
3938
3943static inline VALUE
3944pm_compile_back_reference_ref(const pm_scope_node_t *scope_node, const pm_back_reference_read_node_t *node)
3945{
3946 const char *type = (const char *) (pm_parser_start(scope_node->parser) + node->base.location.start + 1);
3947
3948 // Since a back reference is `$<char>`, Ruby represents the ID as an
3949 // rb_intern on the value after the `$`.
3950 return INT2FIX(rb_intern2(type, 1)) << 1 | 1;
3951}
3952
3957static inline VALUE
3958pm_compile_numbered_reference_ref(const pm_numbered_reference_read_node_t *node)
3959{
3960 return INT2FIX(node->number << 1);
3961}
3962
3963static void
3964pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition, LABEL **lfinish, bool explicit_receiver)
3965{
3966#define PUSH_VAL(type) (in_condition ? Qtrue : rb_iseq_defined_string(type))
3967
3968 // in_condition is the same as compile.c's needstr
3969 enum defined_type dtype = DEFINED_NOT_DEFINED;
3970 const pm_node_location_t location = *node_location;
3971
3972 switch (PM_NODE_TYPE(node)) {
3973/* DEFINED_NIL ****************************************************************/
3974 case PM_NIL_NODE:
3975 // defined?(nil)
3976 // ^^^
3977 dtype = DEFINED_NIL;
3978 break;
3979/* DEFINED_IVAR ***************************************************************/
3980 case PM_INSTANCE_VARIABLE_READ_NODE: {
3981 // defined?(@a)
3982 // ^^
3984 ID name = pm_constant_id_lookup(scope_node, cast->name);
3985
3986 PUSH_INSN3(ret, location, definedivar, ID2SYM(name), get_ivar_ic_value(iseq, name), PUSH_VAL(DEFINED_IVAR));
3987
3988 return;
3989 }
3990/* DEFINED_LVAR ***************************************************************/
3991 case PM_LOCAL_VARIABLE_READ_NODE:
3992 // a = 1; defined?(a)
3993 // ^
3994 case PM_IT_LOCAL_VARIABLE_READ_NODE:
3995 // 1.then { defined?(it) }
3996 // ^^
3997 dtype = DEFINED_LVAR;
3998 break;
3999/* DEFINED_GVAR ***************************************************************/
4000 case PM_GLOBAL_VARIABLE_READ_NODE: {
4001 // defined?($a)
4002 // ^^
4004 ID name = pm_constant_id_lookup(scope_node, cast->name);
4005
4006 PUSH_INSN(ret, location, putnil);
4007 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_GVAR), ID2SYM(name), PUSH_VAL(DEFINED_GVAR));
4008
4009 return;
4010 }
4011/* DEFINED_CVAR ***************************************************************/
4012 case PM_CLASS_VARIABLE_READ_NODE: {
4013 // defined?(@@a)
4014 // ^^^
4016 ID name = pm_constant_id_lookup(scope_node, cast->name);
4017
4018 PUSH_INSN(ret, location, putnil);
4019 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CVAR), ID2SYM(name), PUSH_VAL(DEFINED_CVAR));
4020
4021 return;
4022 }
4023/* DEFINED_CONST **************************************************************/
4024 case PM_CONSTANT_READ_NODE: {
4025 // defined?(A)
4026 // ^
4028 ID name = pm_constant_id_lookup(scope_node, cast->name);
4029
4030 PUSH_INSN(ret, location, putnil);
4031 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), ID2SYM(name), PUSH_VAL(DEFINED_CONST));
4032
4033 return;
4034 }
4035/* DEFINED_YIELD **************************************************************/
4036 case PM_YIELD_NODE:
4037 // defined?(yield)
4038 // ^^^^^
4039 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
4040
4041 PUSH_INSN(ret, location, putnil);
4042 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_YIELD), 0, PUSH_VAL(DEFINED_YIELD));
4043
4044 return;
4045/* DEFINED_ZSUPER *************************************************************/
4046 case PM_SUPER_NODE: {
4047 // defined?(super 1, 2)
4048 // ^^^^^^^^^^
4049 const pm_super_node_t *cast = (const pm_super_node_t *) node;
4050
4051 if (cast->block != NULL && !PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE)) {
4052 dtype = DEFINED_EXPR;
4053 break;
4054 }
4055
4056 PUSH_INSN(ret, location, putnil);
4057 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_ZSUPER), 0, PUSH_VAL(DEFINED_ZSUPER));
4058 return;
4059 }
4060 case PM_FORWARDING_SUPER_NODE: {
4061 // defined?(super)
4062 // ^^^^^
4064
4065 if (cast->block != NULL) {
4066 dtype = DEFINED_EXPR;
4067 break;
4068 }
4069
4070 PUSH_INSN(ret, location, putnil);
4071 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_ZSUPER), 0, PUSH_VAL(DEFINED_ZSUPER));
4072 return;
4073 }
4074/* DEFINED_SELF ***************************************************************/
4075 case PM_SELF_NODE:
4076 // defined?(self)
4077 // ^^^^
4078 dtype = DEFINED_SELF;
4079 break;
4080/* DEFINED_TRUE ***************************************************************/
4081 case PM_TRUE_NODE:
4082 // defined?(true)
4083 // ^^^^
4084 dtype = DEFINED_TRUE;
4085 break;
4086/* DEFINED_FALSE **************************************************************/
4087 case PM_FALSE_NODE:
4088 // defined?(false)
4089 // ^^^^^
4090 dtype = DEFINED_FALSE;
4091 break;
4092/* DEFINED_ASGN ***************************************************************/
4093 case PM_CALL_AND_WRITE_NODE:
4094 // defined?(a.a &&= 1)
4095 // ^^^^^^^^^
4096 case PM_CALL_OPERATOR_WRITE_NODE:
4097 // defined?(a.a += 1)
4098 // ^^^^^^^^
4099 case PM_CALL_OR_WRITE_NODE:
4100 // defined?(a.a ||= 1)
4101 // ^^^^^^^^^
4102 case PM_CLASS_VARIABLE_AND_WRITE_NODE:
4103 // defined?(@@a &&= 1)
4104 // ^^^^^^^^^
4105 case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE:
4106 // defined?(@@a += 1)
4107 // ^^^^^^^^
4108 case PM_CLASS_VARIABLE_OR_WRITE_NODE:
4109 // defined?(@@a ||= 1)
4110 // ^^^^^^^^^
4111 case PM_CLASS_VARIABLE_WRITE_NODE:
4112 // defined?(@@a = 1)
4113 // ^^^^^^^
4114 case PM_CONSTANT_AND_WRITE_NODE:
4115 // defined?(A &&= 1)
4116 // ^^^^^^^
4117 case PM_CONSTANT_OPERATOR_WRITE_NODE:
4118 // defined?(A += 1)
4119 // ^^^^^^
4120 case PM_CONSTANT_OR_WRITE_NODE:
4121 // defined?(A ||= 1)
4122 // ^^^^^^^
4123 case PM_CONSTANT_PATH_AND_WRITE_NODE:
4124 // defined?(A::A &&= 1)
4125 // ^^^^^^^^^^
4126 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE:
4127 // defined?(A::A += 1)
4128 // ^^^^^^^^^
4129 case PM_CONSTANT_PATH_OR_WRITE_NODE:
4130 // defined?(A::A ||= 1)
4131 // ^^^^^^^^^^
4132 case PM_CONSTANT_PATH_WRITE_NODE:
4133 // defined?(A::A = 1)
4134 // ^^^^^^^^
4135 case PM_CONSTANT_WRITE_NODE:
4136 // defined?(A = 1)
4137 // ^^^^^
4138 case PM_GLOBAL_VARIABLE_AND_WRITE_NODE:
4139 // defined?($a &&= 1)
4140 // ^^^^^^^^
4141 case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE:
4142 // defined?($a += 1)
4143 // ^^^^^^^
4144 case PM_GLOBAL_VARIABLE_OR_WRITE_NODE:
4145 // defined?($a ||= 1)
4146 // ^^^^^^^^
4147 case PM_GLOBAL_VARIABLE_WRITE_NODE:
4148 // defined?($a = 1)
4149 // ^^^^^^
4150 case PM_INDEX_AND_WRITE_NODE:
4151 // defined?(a[1] &&= 1)
4152 // ^^^^^^^^^^
4153 case PM_INDEX_OPERATOR_WRITE_NODE:
4154 // defined?(a[1] += 1)
4155 // ^^^^^^^^^
4156 case PM_INDEX_OR_WRITE_NODE:
4157 // defined?(a[1] ||= 1)
4158 // ^^^^^^^^^^
4159 case PM_INSTANCE_VARIABLE_AND_WRITE_NODE:
4160 // defined?(@a &&= 1)
4161 // ^^^^^^^^
4162 case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE:
4163 // defined?(@a += 1)
4164 // ^^^^^^^
4165 case PM_INSTANCE_VARIABLE_OR_WRITE_NODE:
4166 // defined?(@a ||= 1)
4167 // ^^^^^^^^
4168 case PM_INSTANCE_VARIABLE_WRITE_NODE:
4169 // defined?(@a = 1)
4170 // ^^^^^^
4171 case PM_LOCAL_VARIABLE_AND_WRITE_NODE:
4172 // defined?(a &&= 1)
4173 // ^^^^^^^
4174 case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE:
4175 // defined?(a += 1)
4176 // ^^^^^^
4177 case PM_LOCAL_VARIABLE_OR_WRITE_NODE:
4178 // defined?(a ||= 1)
4179 // ^^^^^^^
4180 case PM_LOCAL_VARIABLE_WRITE_NODE:
4181 // defined?(a = 1)
4182 // ^^^^^
4183 case PM_MULTI_WRITE_NODE:
4184 // defined?((a, = 1))
4185 // ^^^^^^
4186 dtype = DEFINED_ASGN;
4187 break;
4188/* DEFINED_EXPR ***************************************************************/
4189 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
4190 // defined?((alias $a $b))
4191 // ^^^^^^^^^^^
4192 case PM_ALIAS_METHOD_NODE:
4193 // defined?((alias a b))
4194 // ^^^^^^^^^
4195 case PM_AND_NODE:
4196 // defined?(a and b)
4197 // ^^^^^^^
4198 case PM_BREAK_NODE:
4199 // defined?(break 1)
4200 // ^^^^^^^
4201 case PM_CASE_MATCH_NODE:
4202 // defined?(case 1; in 1; end)
4203 // ^^^^^^^^^^^^^^^^^
4204 case PM_CASE_NODE:
4205 // defined?(case 1; when 1; end)
4206 // ^^^^^^^^^^^^^^^^^^^
4207 case PM_CLASS_NODE:
4208 // defined?(class Foo; end)
4209 // ^^^^^^^^^^^^^^
4210 case PM_DEF_NODE:
4211 // defined?(def a() end)
4212 // ^^^^^^^^^^^
4213 case PM_DEFINED_NODE:
4214 // defined?(defined?(a))
4215 // ^^^^^^^^^^^
4216 case PM_FLIP_FLOP_NODE:
4217 // defined?(not (a .. b))
4218 // ^^^^^^
4219 case PM_FLOAT_NODE:
4220 // defined?(1.0)
4221 // ^^^
4222 case PM_FOR_NODE:
4223 // defined?(for a in 1 do end)
4224 // ^^^^^^^^^^^^^^^^^
4225 case PM_IF_NODE:
4226 // defined?(if a then end)
4227 // ^^^^^^^^^^^^^
4228 case PM_IMAGINARY_NODE:
4229 // defined?(1i)
4230 // ^^
4231 case PM_INTEGER_NODE:
4232 // defined?(1)
4233 // ^
4234 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE:
4235 // defined?(not /#{1}/)
4236 // ^^^^^^
4237 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
4238 // defined?(/#{1}/)
4239 // ^^^^^^
4240 case PM_INTERPOLATED_STRING_NODE:
4241 // defined?("#{1}")
4242 // ^^^^^^
4243 case PM_INTERPOLATED_SYMBOL_NODE:
4244 // defined?(:"#{1}")
4245 // ^^^^^^^
4246 case PM_INTERPOLATED_X_STRING_NODE:
4247 // defined?(`#{1}`)
4248 // ^^^^^^
4249 case PM_LAMBDA_NODE:
4250 // defined?(-> {})
4251 // ^^^^^
4252 case PM_MATCH_LAST_LINE_NODE:
4253 // defined?(not //)
4254 // ^^^^^^
4255 case PM_MATCH_PREDICATE_NODE:
4256 // defined?(1 in 1)
4257 // ^^^^^^
4258 case PM_MATCH_REQUIRED_NODE:
4259 // defined?(1 => 1)
4260 // ^^^^^^
4261 case PM_MATCH_WRITE_NODE:
4262 // defined?(/(?<a>)/ =~ "")
4263 // ^^^^^^^^^^^^^^
4264 case PM_MODULE_NODE:
4265 // defined?(module A end)
4266 // ^^^^^^^^^^^^
4267 case PM_NEXT_NODE:
4268 // defined?(next 1)
4269 // ^^^^^^
4270 case PM_OR_NODE:
4271 // defined?(a or b)
4272 // ^^^^^^
4273 case PM_POST_EXECUTION_NODE:
4274 // defined?((END {}))
4275 // ^^^^^^^^
4276 case PM_RANGE_NODE:
4277 // defined?(1..1)
4278 // ^^^^
4279 case PM_RATIONAL_NODE:
4280 // defined?(1r)
4281 // ^^
4282 case PM_REDO_NODE:
4283 // defined?(redo)
4284 // ^^^^
4285 case PM_REGULAR_EXPRESSION_NODE:
4286 // defined?(//)
4287 // ^^
4288 case PM_RESCUE_MODIFIER_NODE:
4289 // defined?(a rescue b)
4290 // ^^^^^^^^^^
4291 case PM_RETRY_NODE:
4292 // defined?(retry)
4293 // ^^^^^
4294 case PM_RETURN_NODE:
4295 // defined?(return)
4296 // ^^^^^^
4297 case PM_SINGLETON_CLASS_NODE:
4298 // defined?(class << self; end)
4299 // ^^^^^^^^^^^^^^^^^^
4300 case PM_SOURCE_ENCODING_NODE:
4301 // defined?(__ENCODING__)
4302 // ^^^^^^^^^^^^
4303 case PM_SOURCE_FILE_NODE:
4304 // defined?(__FILE__)
4305 // ^^^^^^^^
4306 case PM_SOURCE_LINE_NODE:
4307 // defined?(__LINE__)
4308 // ^^^^^^^^
4309 case PM_STRING_NODE:
4310 // defined?("")
4311 // ^^
4312 case PM_SYMBOL_NODE:
4313 // defined?(:a)
4314 // ^^
4315 case PM_UNDEF_NODE:
4316 // defined?((undef a))
4317 // ^^^^^^^
4318 case PM_UNLESS_NODE:
4319 // defined?(unless a then end)
4320 // ^^^^^^^^^^^^^^^^^
4321 case PM_UNTIL_NODE:
4322 // defined?(until a do end)
4323 // ^^^^^^^^^^^^^^
4324 case PM_WHILE_NODE:
4325 // defined?(while a do end)
4326 // ^^^^^^^^^^^^^^
4327 case PM_X_STRING_NODE:
4328 // defined?(``)
4329 // ^^
4330 dtype = DEFINED_EXPR;
4331 break;
4332/* DEFINED_REF ****************************************************************/
4333 case PM_BACK_REFERENCE_READ_NODE: {
4334 // defined?($+)
4335 // ^^
4337 VALUE ref = pm_compile_back_reference_ref(scope_node, cast);
4338
4339 PUSH_INSN(ret, location, putnil);
4340 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_REF), ref, PUSH_VAL(DEFINED_GVAR));
4341
4342 return;
4343 }
4344 case PM_NUMBERED_REFERENCE_READ_NODE: {
4345 // defined?($1)
4346 // ^^
4348 VALUE ref = pm_compile_numbered_reference_ref(cast);
4349
4350 PUSH_INSN(ret, location, putnil);
4351 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_REF), ref, PUSH_VAL(DEFINED_GVAR));
4352
4353 return;
4354 }
4355/* DEFINED_CONST_FROM *********************************************************/
4356 case PM_CONSTANT_PATH_NODE: {
4357 // defined?(A::A)
4358 // ^^^^
4360 ID name = pm_constant_id_lookup(scope_node, cast->name);
4361
4362 if (cast->parent != NULL) {
4363 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4364 pm_compile_defined_expr0(iseq, cast->parent, node_location, ret, popped, scope_node, true, lfinish, false);
4365
4366 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4367 PM_COMPILE(cast->parent);
4368 }
4369 else {
4370 PUSH_INSN1(ret, location, putobject, rb_cObject);
4371 }
4372
4373 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), ID2SYM(name), PUSH_VAL(DEFINED_CONST));
4374 return;
4375 }
4376/* Containers *****************************************************************/
4377 case PM_BEGIN_NODE: {
4378 // defined?(begin end)
4379 // ^^^^^^^^^
4380 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
4381
4382 if (cast->rescue_clause == NULL && cast->ensure_clause == NULL && cast->else_clause == NULL) {
4383 if (cast->statements == NULL) {
4384 // If we have empty statements, then we want to return "nil".
4385 dtype = DEFINED_NIL;
4386 }
4387 else if (cast->statements->body.size == 1) {
4388 // If we have a begin node that is wrapping a single statement
4389 // then we want to recurse down to that statement and compile
4390 // it.
4391 pm_compile_defined_expr0(iseq, cast->statements->body.nodes[0], node_location, ret, popped, scope_node, in_condition, lfinish, false);
4392 return;
4393 }
4394 else {
4395 // Otherwise, we have a begin wrapping multiple statements, in
4396 // which case this is defined as "expression".
4397 dtype = DEFINED_EXPR;
4398 }
4399 } else {
4400 // If we have any of the other clauses besides the main begin/end,
4401 // this is defined as "expression".
4402 dtype = DEFINED_EXPR;
4403 }
4404
4405 break;
4406 }
4407 case PM_PARENTHESES_NODE: {
4408 // defined?(())
4409 // ^^
4410 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
4411
4412 if (cast->body == NULL) {
4413 // If we have empty parentheses, then we want to return "nil".
4414 dtype = DEFINED_NIL;
4415 }
4416 else if (PM_NODE_TYPE_P(cast->body, PM_STATEMENTS_NODE) && !PM_NODE_FLAG_P(cast, PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS)) {
4417 // If we have a parentheses node that is wrapping a single statement
4418 // then we want to recurse down to that statement and compile it.
4419 pm_compile_defined_expr0(iseq, ((const pm_statements_node_t *) cast->body)->body.nodes[0], node_location, ret, popped, scope_node, in_condition, lfinish, false);
4420 return;
4421 }
4422 else {
4423 // Otherwise, we have parentheses wrapping multiple statements, in
4424 // which case this is defined as "expression".
4425 dtype = DEFINED_EXPR;
4426 }
4427
4428 break;
4429 }
4430 case PM_ARRAY_NODE: {
4431 // defined?([])
4432 // ^^
4433 const pm_array_node_t *cast = (const pm_array_node_t *) node;
4434
4435 if (cast->elements.size > 0 && !lfinish[1]) {
4436 lfinish[1] = NEW_LABEL(location.line);
4437 }
4438
4439 for (size_t index = 0; index < cast->elements.size; index++) {
4440 pm_compile_defined_expr0(iseq, cast->elements.nodes[index], node_location, ret, popped, scope_node, true, lfinish, false);
4441 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4442 }
4443
4444 dtype = DEFINED_EXPR;
4445 break;
4446 }
4447 case PM_HASH_NODE:
4448 // defined?({ a: 1 })
4449 // ^^^^^^^^
4450 case PM_KEYWORD_HASH_NODE: {
4451 // defined?(a(a: 1))
4452 // ^^^^
4453 const pm_node_list_t *elements;
4454
4455 if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) {
4456 elements = &((const pm_hash_node_t *) node)->elements;
4457 }
4458 else {
4459 elements = &((const pm_keyword_hash_node_t *) node)->elements;
4460 }
4461
4462 if (elements->size > 0 && !lfinish[1]) {
4463 lfinish[1] = NEW_LABEL(location.line);
4464 }
4465
4466 for (size_t index = 0; index < elements->size; index++) {
4467 pm_compile_defined_expr0(iseq, elements->nodes[index], node_location, ret, popped, scope_node, true, lfinish, false);
4468 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4469 }
4470
4471 dtype = DEFINED_EXPR;
4472 break;
4473 }
4474 case PM_ASSOC_NODE: {
4475 // defined?({ a: 1 })
4476 // ^^^^
4477 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node;
4478
4479 pm_compile_defined_expr0(iseq, cast->key, node_location, ret, popped, scope_node, true, lfinish, false);
4480 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4481 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, true, lfinish, false);
4482
4483 return;
4484 }
4485 case PM_ASSOC_SPLAT_NODE: {
4486 // defined?({ **a })
4487 // ^^^^
4488 const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node;
4489
4490 if (cast->value == NULL) {
4491 dtype = DEFINED_EXPR;
4492 break;
4493 }
4494
4495 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, true, lfinish, false);
4496 return;
4497 }
4498 case PM_IMPLICIT_NODE: {
4499 // defined?({ a: })
4500 // ^^
4501 const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node;
4502 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4503 return;
4504 }
4505 case PM_CALL_NODE: {
4506#define BLOCK_P(cast) ((cast)->block != NULL && PM_NODE_TYPE_P((cast)->block, PM_BLOCK_NODE))
4507
4508 // defined?(a(1, 2, 3))
4509 // ^^^^^^^^^^
4510 const pm_call_node_t *cast = ((const pm_call_node_t *) node);
4511
4512 if (BLOCK_P(cast)) {
4513 dtype = DEFINED_EXPR;
4514 break;
4515 }
4516
4517 if (cast->receiver || cast->arguments || (cast->block && PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE))) {
4518 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4519 if (!lfinish[2]) lfinish[2] = NEW_LABEL(location.line);
4520 }
4521
4522 if (cast->arguments) {
4523 pm_compile_defined_expr0(iseq, (const pm_node_t *) cast->arguments, node_location, ret, popped, scope_node, true, lfinish, false);
4524 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4525 }
4526
4527 if (cast->block && PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE)) {
4528 pm_compile_defined_expr0(iseq, cast->block, node_location, ret, popped, scope_node, true, lfinish, false);
4529 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4530 }
4531
4532 if (cast->receiver) {
4533 if (PM_NODE_TYPE_P(cast->receiver, PM_CALL_NODE) && !BLOCK_P((const pm_call_node_t *) cast->receiver)) {
4534 // Special behavior here where we chain calls together. This is
4535 // the only path that sets explicit_receiver to true.
4536 pm_compile_defined_expr0(iseq, cast->receiver, node_location, ret, popped, scope_node, true, lfinish, true);
4537 PUSH_INSNL(ret, location, branchunless, lfinish[2]);
4538
4539 const pm_call_node_t *receiver = (const pm_call_node_t *) cast->receiver;
4540 ID method_id = pm_constant_id_lookup(scope_node, receiver->name);
4541
4542 pm_compile_call(iseq, receiver, ret, popped, scope_node, method_id, NULL);
4543 }
4544 else {
4545 pm_compile_defined_expr0(iseq, cast->receiver, node_location, ret, popped, scope_node, true, lfinish, false);
4546 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4547 PM_COMPILE(cast->receiver);
4548 }
4549
4550 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
4551
4552 if (explicit_receiver) PUSH_INSN(ret, location, dup);
4553 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_METHOD), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
4554 }
4555 else {
4556 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
4557
4558 PUSH_INSN(ret, location, putself);
4559 if (explicit_receiver) PUSH_INSN(ret, location, dup);
4560
4561 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_FUNC), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
4562 }
4563
4564 return;
4565
4566#undef BLOCK_P
4567 }
4568 case PM_ARGUMENTS_NODE: {
4569 // defined?(a(1, 2, 3))
4570 // ^^^^^^^
4571 const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node;
4572
4573 for (size_t index = 0; index < cast->arguments.size; index++) {
4574 pm_compile_defined_expr0(iseq, cast->arguments.nodes[index], node_location, ret, popped, scope_node, in_condition, lfinish, false);
4575 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4576 }
4577
4578 dtype = DEFINED_EXPR;
4579 break;
4580 }
4581 case PM_BLOCK_ARGUMENT_NODE:
4582 // defined?(a(&b))
4583 // ^^
4584 dtype = DEFINED_EXPR;
4585 break;
4586 case PM_FORWARDING_ARGUMENTS_NODE:
4587 // def a(...) = defined?(a(...))
4588 // ^^^
4589 dtype = DEFINED_EXPR;
4590 break;
4591 case PM_SPLAT_NODE: {
4592 // def a(*) = defined?(a(*))
4593 // ^
4594 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
4595
4596 if (cast->expression == NULL) {
4597 dtype = DEFINED_EXPR;
4598 break;
4599 }
4600
4601 pm_compile_defined_expr0(iseq, cast->expression, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4602
4603 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4604 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4605
4606 dtype = DEFINED_EXPR;
4607 break;
4608 }
4609 case PM_SHAREABLE_CONSTANT_NODE:
4610 // # shareable_constant_value: literal
4611 // defined?(A = 1)
4612 // ^^^^^
4613 pm_compile_defined_expr0(iseq, ((const pm_shareable_constant_node_t *) node)->write, node_location, ret, popped, scope_node, in_condition, lfinish, explicit_receiver);
4614 return;
4615/* Unreachable (parameters) ***************************************************/
4616 case PM_BLOCK_LOCAL_VARIABLE_NODE:
4617 case PM_BLOCK_PARAMETER_NODE:
4618 case PM_BLOCK_PARAMETERS_NODE:
4619 case PM_FORWARDING_PARAMETER_NODE:
4620 case PM_IMPLICIT_REST_NODE:
4621 case PM_IT_PARAMETERS_NODE:
4622 case PM_PARAMETERS_NODE:
4623 case PM_KEYWORD_REST_PARAMETER_NODE:
4624 case PM_NO_KEYWORDS_PARAMETER_NODE:
4625 case PM_NO_BLOCK_PARAMETER_NODE:
4626 case PM_NUMBERED_PARAMETERS_NODE:
4627 case PM_OPTIONAL_KEYWORD_PARAMETER_NODE:
4628 case PM_OPTIONAL_PARAMETER_NODE:
4629 case PM_REQUIRED_KEYWORD_PARAMETER_NODE:
4630 case PM_REQUIRED_PARAMETER_NODE:
4631 case PM_REST_PARAMETER_NODE:
4632/* Unreachable (pattern matching) *********************************************/
4633 case PM_ALTERNATION_PATTERN_NODE:
4634 case PM_ARRAY_PATTERN_NODE:
4635 case PM_CAPTURE_PATTERN_NODE:
4636 case PM_FIND_PATTERN_NODE:
4637 case PM_HASH_PATTERN_NODE:
4638 case PM_PINNED_EXPRESSION_NODE:
4639 case PM_PINNED_VARIABLE_NODE:
4640/* Unreachable (indirect writes) **********************************************/
4641 case PM_CALL_TARGET_NODE:
4642 case PM_CLASS_VARIABLE_TARGET_NODE:
4643 case PM_CONSTANT_PATH_TARGET_NODE:
4644 case PM_CONSTANT_TARGET_NODE:
4645 case PM_GLOBAL_VARIABLE_TARGET_NODE:
4646 case PM_INDEX_TARGET_NODE:
4647 case PM_INSTANCE_VARIABLE_TARGET_NODE:
4648 case PM_LOCAL_VARIABLE_TARGET_NODE:
4649 case PM_MULTI_TARGET_NODE:
4650/* Unreachable (clauses) ******************************************************/
4651 case PM_ELSE_NODE:
4652 case PM_ENSURE_NODE:
4653 case PM_IN_NODE:
4654 case PM_RESCUE_NODE:
4655 case PM_WHEN_NODE:
4656/* Unreachable (miscellaneous) ************************************************/
4657 case PM_BLOCK_NODE:
4658 case PM_EMBEDDED_STATEMENTS_NODE:
4659 case PM_EMBEDDED_VARIABLE_NODE:
4660 case PM_ERROR_RECOVERY_NODE:
4661 case PM_PRE_EXECUTION_NODE:
4662 case PM_PROGRAM_NODE:
4663 case PM_SCOPE_NODE:
4664 case PM_STATEMENTS_NODE:
4665 rb_bug("Unreachable node in defined?: %s", pm_node_type(PM_NODE_TYPE(node)));
4666 }
4667
4668 RUBY_ASSERT(dtype != DEFINED_NOT_DEFINED);
4669 PUSH_INSN1(ret, location, putobject, PUSH_VAL(dtype));
4670
4671#undef PUSH_VAL
4672}
4673
4674static void
4675pm_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition, LABEL **lfinish)
4676{
4677 LINK_ELEMENT *lcur = ret->last;
4678 pm_compile_defined_expr0(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4679
4680 if (lfinish[1]) {
4681 LABEL *lstart = NEW_LABEL(node_location->line);
4682 LABEL *lend = NEW_LABEL(node_location->line);
4683
4685 rb_iseq_new_with_callback_new_callback(build_defined_rescue_iseq, NULL);
4686
4687 const rb_iseq_t *rescue = new_child_iseq_with_callback(
4688 iseq,
4689 ifunc,
4690 rb_str_concat(rb_str_new2("defined guard in "), ISEQ_BODY(iseq)->location.label),
4691 iseq,
4692 ISEQ_TYPE_RESCUE,
4693 0
4694 );
4695
4696 lstart->rescued = LABEL_RESCUE_BEG;
4697 lend->rescued = LABEL_RESCUE_END;
4698
4699 APPEND_LABEL(ret, lcur, lstart);
4700 PUSH_LABEL(ret, lend);
4701 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
4702 }
4703}
4704
4705static void
4706pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition)
4707{
4708 LABEL *lfinish[3];
4709 LINK_ELEMENT *last = ret->last;
4710
4711 lfinish[0] = NEW_LABEL(node_location->line);
4712 lfinish[1] = 0;
4713 lfinish[2] = 0;
4714
4715 if (!popped) {
4716 pm_defined_expr(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish);
4717 }
4718
4719 if (lfinish[1]) {
4720 ELEM_INSERT_NEXT(last, &new_insn_body(iseq, node_location->line, node_location->node_id, BIN(putnil), 0)->link);
4721 PUSH_INSN(ret, *node_location, swap);
4722
4723 if (lfinish[2]) PUSH_LABEL(ret, lfinish[2]);
4724 PUSH_INSN(ret, *node_location, pop);
4725 PUSH_LABEL(ret, lfinish[1]);
4726
4727 }
4728
4729 PUSH_LABEL(ret, lfinish[0]);
4730}
4731
4732// This is exactly the same as add_ensure_iseq, except it compiled
4733// the node as a Prism node, and not a CRuby node
4734static void
4735pm_add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return, pm_scope_node_t *scope_node)
4736{
4737 RUBY_ASSERT(can_add_ensure_iseq(iseq));
4738
4740 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
4741 struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
4742 DECL_ANCHOR(ensure);
4743
4744 while (enlp) {
4745 if (enlp->erange != NULL) {
4746 DECL_ANCHOR(ensure_part);
4747 LABEL *lstart = NEW_LABEL(0);
4748 LABEL *lend = NEW_LABEL(0);
4749
4750 add_ensure_range(iseq, enlp->erange, lstart, lend);
4751
4752 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
4753 PUSH_LABEL(ensure_part, lstart);
4754 bool popped = true;
4755 PM_COMPILE_INTO_ANCHOR(ensure_part, (const pm_node_t *) enlp->ensure_node);
4756 PUSH_LABEL(ensure_part, lend);
4757 PUSH_SEQ(ensure, ensure_part);
4758 }
4759 else {
4760 if (!is_return) {
4761 break;
4762 }
4763 }
4764 enlp = enlp->prev;
4765 }
4766 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = prev_enlp;
4767 PUSH_SEQ(ret, ensure);
4768}
4769
4770
4771
4777static void
4778pm_insert_local_index(pm_constant_id_t constant_id, int local_index, pm_index_lookup_table_t *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node)
4779{
4780 RUBY_ASSERT((constant_id & PM_SPECIAL_CONSTANT_FLAG) == 0);
4781
4782 ID local = pm_constant_id_lookup(scope_node, constant_id);
4783 local_table_for_iseq->ids[local_index] = local;
4784 pm_index_lookup_table_insert(index_lookup_table, constant_id, local_index);
4785}
4786
4790static void
4791pm_insert_local_special(pm_constant_id_t special_id, ID local_name, int local_index, pm_index_lookup_table_t *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq)
4792{
4793 local_table_for_iseq->ids[local_index] = local_name;
4794 pm_index_lookup_table_insert(index_lookup_table, special_id, local_index);
4795}
4796
4803static int
4804pm_compile_destructured_param_locals(const pm_multi_target_node_t *node, pm_index_lookup_table_t *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node, int local_index)
4805{
4806 for (size_t index = 0; index < node->lefts.size; index++) {
4807 const pm_node_t *left = node->lefts.nodes[index];
4808
4809 if (PM_NODE_TYPE_P(left, PM_REQUIRED_PARAMETER_NODE)) {
4810 if (!PM_NODE_FLAG_P(left, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4811 pm_insert_local_index(((const pm_required_parameter_node_t *) left)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4812 local_index++;
4813 }
4814 }
4815 else {
4816 RUBY_ASSERT(PM_NODE_TYPE_P(left, PM_MULTI_TARGET_NODE));
4817 local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) left, index_lookup_table, local_table_for_iseq, scope_node, local_index);
4818 }
4819 }
4820
4821 if (node->rest != NULL && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE)) {
4822 const pm_splat_node_t *rest = (const pm_splat_node_t *) node->rest;
4823
4824 if (rest->expression != NULL) {
4825 RUBY_ASSERT(PM_NODE_TYPE_P(rest->expression, PM_REQUIRED_PARAMETER_NODE));
4826
4827 if (!PM_NODE_FLAG_P(rest->expression, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4828 pm_insert_local_index(((const pm_required_parameter_node_t *) rest->expression)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4829 local_index++;
4830 }
4831 }
4832 }
4833
4834 for (size_t index = 0; index < node->rights.size; index++) {
4835 const pm_node_t *right = node->rights.nodes[index];
4836
4837 if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) {
4838 if (!PM_NODE_FLAG_P(right, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4839 pm_insert_local_index(((const pm_required_parameter_node_t *) right)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4840 local_index++;
4841 }
4842 }
4843 else {
4844 RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE));
4845 local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) right, index_lookup_table, local_table_for_iseq, scope_node, local_index);
4846 }
4847 }
4848
4849 return local_index;
4850}
4851
4856static inline void
4857pm_compile_destructured_param_write(rb_iseq_t *iseq, const pm_required_parameter_node_t *node, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
4858{
4859 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
4860 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, node->name, 0);
4861 PUSH_SETLOCAL(ret, location, index.index, index.level);
4862}
4863
4872static void
4873pm_compile_destructured_param_writes(rb_iseq_t *iseq, const pm_multi_target_node_t *node, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
4874{
4875 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
4876 bool has_rest = (node->rest && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE) && (((const pm_splat_node_t *) node->rest)->expression) != NULL);
4877 bool has_rights = node->rights.size > 0;
4878
4879 int flag = (has_rest || has_rights) ? 1 : 0;
4880 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->lefts.size), INT2FIX(flag));
4881
4882 for (size_t index = 0; index < node->lefts.size; index++) {
4883 const pm_node_t *left = node->lefts.nodes[index];
4884
4885 if (PM_NODE_TYPE_P(left, PM_REQUIRED_PARAMETER_NODE)) {
4886 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) left, ret, scope_node);
4887 }
4888 else {
4889 RUBY_ASSERT(PM_NODE_TYPE_P(left, PM_MULTI_TARGET_NODE));
4890 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) left, ret, scope_node);
4891 }
4892 }
4893
4894 if (has_rest) {
4895 if (has_rights) {
4896 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->rights.size), INT2FIX(3));
4897 }
4898
4899 const pm_node_t *rest = ((const pm_splat_node_t *) node->rest)->expression;
4900 RUBY_ASSERT(PM_NODE_TYPE_P(rest, PM_REQUIRED_PARAMETER_NODE));
4901
4902 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) rest, ret, scope_node);
4903 }
4904
4905 if (has_rights) {
4906 if (!has_rest) {
4907 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->rights.size), INT2FIX(2));
4908 }
4909
4910 for (size_t index = 0; index < node->rights.size; index++) {
4911 const pm_node_t *right = node->rights.nodes[index];
4912
4913 if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) {
4914 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) right, ret, scope_node);
4915 }
4916 else {
4917 RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE));
4918 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) right, ret, scope_node);
4919 }
4920 }
4921 }
4922}
4923
4929 // The pointer to the topn instruction that will need to be modified after
4930 // we know the total stack size of all of the targets.
4931 INSN *topn;
4932
4933 // The index of the stack from the base of the entire multi target at which
4934 // the parent expression is located.
4935 size_t stack_index;
4936
4937 // The number of slots in the stack that this node occupies.
4938 size_t stack_size;
4939
4940 // The position of the node in the list of targets.
4941 size_t position;
4942
4943 // A pointer to the next node in this linked list.
4944 struct pm_multi_target_state_node *next;
4946
4954typedef struct {
4955 // The total number of slots in the stack that this multi target occupies.
4956 size_t stack_size;
4957
4958 // The position of the current node being compiled. This is forwarded to
4959 // nodes when they are allocated.
4960 size_t position;
4961
4962 // A pointer to the head of this linked list.
4964
4965 // A pointer to the tail of this linked list.
4968
4972static void
4973pm_multi_target_state_push(pm_multi_target_state_t *state, INSN *topn, size_t stack_size)
4974{
4976 node->topn = topn;
4977 node->stack_index = state->stack_size + 1;
4978 node->stack_size = stack_size;
4979 node->position = state->position;
4980 node->next = NULL;
4981
4982 if (state->head == NULL) {
4983 state->head = node;
4984 state->tail = node;
4985 }
4986 else {
4987 state->tail->next = node;
4988 state->tail = node;
4989 }
4990
4991 state->stack_size += stack_size;
4992}
4993
4999static void
5000pm_multi_target_state_update(pm_multi_target_state_t *state)
5001{
5002 // If nothing was ever pushed onto the stack, then we don't need to do any
5003 // kind of updates.
5004 if (state->stack_size == 0) return;
5005
5006 pm_multi_target_state_node_t *current = state->head;
5008
5009 while (current != NULL) {
5010 VALUE offset = INT2FIX(state->stack_size - current->stack_index + current->position);
5011 current->topn->operands[0] = offset;
5012
5013 // stack_size will be > 1 in the case that we compiled an index target
5014 // and it had arguments. In this case, we use multiple topn instructions
5015 // to grab up all of the arguments as well, so those offsets need to be
5016 // updated as well.
5017 if (current->stack_size > 1) {
5018 INSN *insn = current->topn;
5019
5020 for (size_t index = 1; index < current->stack_size; index += 1) {
5021 LINK_ELEMENT *element = get_next_insn(insn);
5022 RUBY_ASSERT(IS_INSN(element));
5023
5024 insn = (INSN *) element;
5025 RUBY_ASSERT(insn->insn_id == BIN(topn));
5026
5027 insn->operands[0] = offset;
5028 }
5029 }
5030
5031 previous = current;
5032 current = current->next;
5033
5034 SIZED_FREE(previous);
5035 }
5036}
5037
5038static void
5039pm_compile_multi_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const parents, LINK_ANCHOR *const writes, LINK_ANCHOR *const cleanup, pm_scope_node_t *scope_node, pm_multi_target_state_t *state);
5040
5069static void
5070pm_compile_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const parents, LINK_ANCHOR *const writes, LINK_ANCHOR *const cleanup, pm_scope_node_t *scope_node, pm_multi_target_state_t *state)
5071{
5072 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5073
5074 switch (PM_NODE_TYPE(node)) {
5075 case PM_LOCAL_VARIABLE_TARGET_NODE: {
5076 // Local variable targets have no parent expression, so they only need
5077 // to compile the write.
5078 //
5079 // for i in []; end
5080 //
5082 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
5083
5084 PUSH_SETLOCAL(writes, location, index.index, index.level);
5085 break;
5086 }
5087 case PM_CLASS_VARIABLE_TARGET_NODE: {
5088 // Class variable targets have no parent expression, so they only need
5089 // to compile the write.
5090 //
5091 // for @@i in []; end
5092 //
5094 ID name = pm_constant_id_lookup(scope_node, cast->name);
5095
5096 VALUE operand = ID2SYM(name);
5097 PUSH_INSN2(writes, location, setclassvariable, operand, get_cvar_ic_value(iseq, name));
5098 break;
5099 }
5100 case PM_CONSTANT_TARGET_NODE: {
5101 // Constant targets have no parent expression, so they only need to
5102 // compile the write.
5103 //
5104 // for I in []; end
5105 //
5107 ID name = pm_constant_id_lookup(scope_node, cast->name);
5108
5109 VALUE operand = ID2SYM(name);
5110 PUSH_INSN1(writes, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5111 PUSH_INSN1(writes, location, setconstant, operand);
5112 break;
5113 }
5114 case PM_GLOBAL_VARIABLE_TARGET_NODE: {
5115 // Global variable targets have no parent expression, so they only need
5116 // to compile the write.
5117 //
5118 // for $i in []; end
5119 //
5121 ID name = pm_constant_id_lookup(scope_node, cast->name);
5122
5123 VALUE operand = ID2SYM(name);
5124 PUSH_INSN1(writes, location, setglobal, operand);
5125 break;
5126 }
5127 case PM_INSTANCE_VARIABLE_TARGET_NODE: {
5128 // Instance variable targets have no parent expression, so they only
5129 // need to compile the write.
5130 //
5131 // for @i in []; end
5132 //
5134 ID name = pm_constant_id_lookup(scope_node, cast->name);
5135
5136 VALUE operand = ID2SYM(name);
5137 PUSH_INSN2(writes, location, setinstancevariable, operand, get_ivar_ic_value(iseq, name));
5138 break;
5139 }
5140 case PM_CONSTANT_PATH_TARGET_NODE: {
5141 // Constant path targets have a parent expression that is the object
5142 // that owns the constant. This needs to be compiled first into the
5143 // parents sequence. If no parent is found, then it represents using the
5144 // unary :: operator to indicate a top-level constant. In that case we
5145 // need to push Object onto the stack.
5146 //
5147 // for I::J in []; end
5148 //
5150 ID name = pm_constant_id_lookup(scope_node, cast->name);
5151
5152 if (cast->parent != NULL) {
5153 pm_compile_node(iseq, cast->parent, parents, false, scope_node);
5154 }
5155 else {
5156 PUSH_INSN1(parents, location, putobject, rb_cObject);
5157 }
5158
5159 if (state == NULL) {
5160 PUSH_INSN(writes, location, swap);
5161 }
5162 else {
5163 PUSH_INSN1(writes, location, topn, INT2FIX(1));
5164 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), 1);
5165 }
5166
5167 VALUE operand = ID2SYM(name);
5168 PUSH_INSN1(writes, location, setconstant, operand);
5169
5170 if (state != NULL) {
5171 PUSH_INSN(cleanup, location, pop);
5172 }
5173
5174 break;
5175 }
5176 case PM_CALL_TARGET_NODE: {
5177 // Call targets have a parent expression that is the receiver of the
5178 // method being called. This needs to be compiled first into the parents
5179 // sequence. These nodes cannot have arguments, so the method call is
5180 // compiled with a single argument which represents the value being
5181 // written.
5182 //
5183 // for i.j in []; end
5184 //
5185 const pm_call_target_node_t *cast = (const pm_call_target_node_t *) node;
5186 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
5187
5188 pm_compile_node(iseq, cast->receiver, parents, false, scope_node);
5189
5190 LABEL *safe_label = NULL;
5191 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
5192 safe_label = NEW_LABEL(location.line);
5193 PUSH_INSN(parents, location, dup);
5194 PUSH_INSNL(parents, location, branchnil, safe_label);
5195 }
5196
5197 if (state != NULL) {
5198 PUSH_INSN1(writes, location, topn, INT2FIX(1));
5199 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), 1);
5200 PUSH_INSN(writes, location, swap);
5201 }
5202
5203 int flags = VM_CALL_ARGS_SIMPLE;
5204 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) flags |= VM_CALL_FCALL;
5205
5206 PUSH_SEND_WITH_FLAG(writes, location, method_id, INT2FIX(1), INT2FIX(flags));
5207 if (safe_label != NULL && state == NULL) PUSH_LABEL(writes, safe_label);
5208 PUSH_INSN(writes, location, pop);
5209 if (safe_label != NULL && state != NULL) PUSH_LABEL(writes, safe_label);
5210
5211 if (state != NULL) {
5212 PUSH_INSN(cleanup, location, pop);
5213 }
5214
5215 break;
5216 }
5217 case PM_INDEX_TARGET_NODE: {
5218 // Index targets have a parent expression that is the receiver of the
5219 // method being called and any additional arguments that are being
5220 // passed along with the value being written. The receiver and arguments
5221 // both need to be on the stack. Note that this is even more complicated
5222 // by the fact that these nodes can hold a block using the unary &
5223 // operator.
5224 //
5225 // for i[:j] in []; end
5226 //
5227 const pm_index_target_node_t *cast = (const pm_index_target_node_t *) node;
5228
5229 pm_compile_node(iseq, cast->receiver, parents, false, scope_node);
5230
5231 int flags = 0;
5232 struct rb_callinfo_kwarg *kwargs = NULL;
5233 int argc = pm_setup_args(cast->arguments, (const pm_node_t *) cast->block, &flags, &kwargs, iseq, parents, scope_node, &location);
5234
5235 if (state != NULL) {
5236 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5237 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), argc + 1);
5238
5239 if (argc == 0) {
5240 PUSH_INSN(writes, location, swap);
5241 }
5242 else {
5243 for (int index = 0; index < argc; index++) {
5244 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5245 }
5246 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5247 }
5248 }
5249
5250 // The argc that we're going to pass to the send instruction is the
5251 // number of arguments + 1 for the value being written. If there's a
5252 // splat, then we need to insert newarray and concatarray instructions
5253 // after the arguments have been written.
5254 int ci_argc = argc + 1;
5255 if (flags & VM_CALL_ARGS_SPLAT) {
5256 ci_argc--;
5257 PUSH_INSN1(writes, location, newarray, INT2FIX(1));
5258 PUSH_INSN(writes, location, concatarray);
5259 }
5260
5261 PUSH_SEND_R(writes, location, idASET, INT2NUM(ci_argc), NULL, INT2FIX(flags), kwargs);
5262 PUSH_INSN(writes, location, pop);
5263
5264 if (state != NULL) {
5265 if (argc != 0) {
5266 PUSH_INSN(writes, location, pop);
5267 }
5268
5269 for (int index = 0; index < argc + 1; index++) {
5270 PUSH_INSN(cleanup, location, pop);
5271 }
5272 }
5273
5274 break;
5275 }
5276 case PM_MULTI_TARGET_NODE: {
5277 // Multi target nodes represent a set of writes to multiple variables.
5278 // The parent expressions are the combined set of the parent expressions
5279 // of its inner target nodes.
5280 //
5281 // for i, j in []; end
5282 //
5283 size_t before_position;
5284 if (state != NULL) {
5285 before_position = state->position;
5286 state->position--;
5287 }
5288
5289 pm_compile_multi_target_node(iseq, node, parents, writes, cleanup, scope_node, state);
5290 if (state != NULL) state->position = before_position;
5291
5292 break;
5293 }
5294 case PM_SPLAT_NODE: {
5295 // Splat nodes capture all values into an array. They can be used
5296 // as targets in assignments or for loops.
5297 //
5298 // for *x in []; end
5299 //
5300 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
5301
5302 if (cast->expression != NULL) {
5303 pm_compile_target_node(iseq, cast->expression, parents, writes, cleanup, scope_node, state);
5304 }
5305
5306 break;
5307 }
5308 default:
5309 rb_bug("Unexpected node type: %s", pm_node_type(PM_NODE_TYPE(node)));
5310 break;
5311 }
5312}
5313
5319static void
5320pm_compile_multi_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const parents, LINK_ANCHOR *const writes, LINK_ANCHOR *const cleanup, pm_scope_node_t *scope_node, pm_multi_target_state_t *state)
5321{
5322 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5323 const pm_node_list_t *lefts;
5324 const pm_node_t *rest;
5325 const pm_node_list_t *rights;
5326
5327 switch (PM_NODE_TYPE(node)) {
5328 case PM_MULTI_TARGET_NODE: {
5329 const pm_multi_target_node_t *cast = (const pm_multi_target_node_t *) node;
5330 lefts = &cast->lefts;
5331 rest = cast->rest;
5332 rights = &cast->rights;
5333 break;
5334 }
5335 case PM_MULTI_WRITE_NODE: {
5336 const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node;
5337 lefts = &cast->lefts;
5338 rest = cast->rest;
5339 rights = &cast->rights;
5340 break;
5341 }
5342 default:
5343 rb_bug("Unsupported node %s", pm_node_type(PM_NODE_TYPE(node)));
5344 break;
5345 }
5346
5347 bool has_rest = (rest != NULL) && PM_NODE_TYPE_P(rest, PM_SPLAT_NODE) && ((const pm_splat_node_t *) rest)->expression != NULL;
5348 bool has_posts = rights->size > 0;
5349
5350 // The first instruction in the writes sequence is going to spread the
5351 // top value of the stack onto the number of values that we're going to
5352 // write.
5353 PUSH_INSN2(writes, location, expandarray, INT2FIX(lefts->size), INT2FIX((has_rest || has_posts) ? 1 : 0));
5354
5355 // We need to keep track of some additional state information as we're
5356 // going through the targets because we will need to revisit them once
5357 // we know how many values are being pushed onto the stack.
5358 pm_multi_target_state_t target_state = { 0 };
5359 if (state == NULL) state = &target_state;
5360
5361 size_t base_position = state->position;
5362 size_t splat_position = (has_rest || has_posts) ? 1 : 0;
5363
5364 // Next, we'll iterate through all of the leading targets.
5365 for (size_t index = 0; index < lefts->size; index++) {
5366 const pm_node_t *target = lefts->nodes[index];
5367 state->position = lefts->size - index + splat_position + base_position;
5368 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5369 }
5370
5371 // Next, we'll compile the rest target if there is one.
5372 if (has_rest) {
5373 const pm_node_t *target = ((const pm_splat_node_t *) rest)->expression;
5374 state->position = 1 + rights->size + base_position;
5375
5376 if (has_posts) {
5377 PUSH_INSN2(writes, location, expandarray, INT2FIX(rights->size), INT2FIX(3));
5378 }
5379
5380 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5381 }
5382
5383 // Finally, we'll compile the trailing targets.
5384 if (has_posts) {
5385 if (!has_rest && rest != NULL) {
5386 PUSH_INSN2(writes, location, expandarray, INT2FIX(rights->size), INT2FIX(2));
5387 }
5388
5389 for (size_t index = 0; index < rights->size; index++) {
5390 const pm_node_t *target = rights->nodes[index];
5391 state->position = rights->size - index + base_position;
5392 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5393 }
5394 }
5395}
5396
5402static void
5403pm_compile_for_node_index(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
5404{
5405 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5406
5407 switch (PM_NODE_TYPE(node)) {
5408 case PM_LOCAL_VARIABLE_TARGET_NODE: {
5409 // For local variables, all we have to do is retrieve the value and then
5410 // compile the index node.
5411 PUSH_GETLOCAL(ret, location, 1, 0);
5412 pm_compile_target_node(iseq, node, ret, ret, ret, scope_node, NULL);
5413 break;
5414 }
5415 case PM_CLASS_VARIABLE_TARGET_NODE:
5416 case PM_CONSTANT_TARGET_NODE:
5417 case PM_GLOBAL_VARIABLE_TARGET_NODE:
5418 case PM_INSTANCE_VARIABLE_TARGET_NODE:
5419 case PM_CONSTANT_PATH_TARGET_NODE:
5420 case PM_CALL_TARGET_NODE:
5421 case PM_INDEX_TARGET_NODE: {
5422 // For other targets, we need to potentially compile the parent or
5423 // owning expression of this target, then retrieve the value, expand it,
5424 // and then compile the necessary writes.
5425 DECL_ANCHOR(writes);
5426 DECL_ANCHOR(cleanup);
5427
5428 pm_multi_target_state_t state = { 0 };
5429 state.position = 1;
5430 pm_compile_target_node(iseq, node, ret, writes, cleanup, scope_node, &state);
5431
5432 PUSH_GETLOCAL(ret, location, 1, 0);
5433 PUSH_INSN2(ret, location, expandarray, INT2FIX(1), INT2FIX(0));
5434
5435 PUSH_SEQ(ret, writes);
5436 PUSH_SEQ(ret, cleanup);
5437
5438 pm_multi_target_state_update(&state);
5439 break;
5440 }
5441 case PM_SPLAT_NODE:
5442 case PM_MULTI_TARGET_NODE: {
5443 DECL_ANCHOR(writes);
5444 DECL_ANCHOR(cleanup);
5445
5446 pm_compile_target_node(iseq, node, ret, writes, cleanup, scope_node, NULL);
5447
5448 LABEL *not_single = NEW_LABEL(location.line);
5449 LABEL *not_ary = NEW_LABEL(location.line);
5450
5451 // When there are multiple targets, we'll do a bunch of work to convert
5452 // the value into an array before we expand it. Effectively we're trying
5453 // to accomplish:
5454 //
5455 // (args.length == 1 && Array.try_convert(args[0])) || args
5456 //
5457 PUSH_GETLOCAL(ret, location, 1, 0);
5458 PUSH_INSN(ret, location, dup);
5459 PUSH_CALL(ret, location, idLength, INT2FIX(0));
5460 PUSH_INSN1(ret, location, putobject, INT2FIX(1));
5461 PUSH_CALL(ret, location, idEq, INT2FIX(1));
5462 PUSH_INSNL(ret, location, branchunless, not_single);
5463 PUSH_INSN(ret, location, dup);
5464 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
5465 PUSH_CALL(ret, location, idAREF, INT2FIX(1));
5466 PUSH_INSN1(ret, location, putobject, rb_cArray);
5467 PUSH_INSN(ret, location, swap);
5468 PUSH_CALL(ret, location, rb_intern("try_convert"), INT2FIX(1));
5469 PUSH_INSN(ret, location, dup);
5470 PUSH_INSNL(ret, location, branchunless, not_ary);
5471 PUSH_INSN(ret, location, swap);
5472
5473 PUSH_LABEL(ret, not_ary);
5474 PUSH_INSN(ret, location, pop);
5475
5476 PUSH_LABEL(ret, not_single);
5477
5478 if (PM_NODE_TYPE_P(node, PM_SPLAT_NODE)) {
5479 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
5480 PUSH_INSN2(ret, location, expandarray, INT2FIX(0), INT2FIX(cast->expression == NULL ? 0 : 1));
5481 }
5482
5483 PUSH_SEQ(ret, writes);
5484 PUSH_SEQ(ret, cleanup);
5485 break;
5486 }
5487 default:
5488 rb_bug("Unexpected node type for index in for node: %s", pm_node_type(PM_NODE_TYPE(node)));
5489 break;
5490 }
5491}
5492
5493static void
5494pm_compile_rescue(rb_iseq_t *iseq, const pm_begin_node_t *cast, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5495{
5496 LABEL *lstart = NEW_LABEL(node_location->line);
5497 LABEL *lend = NEW_LABEL(node_location->line);
5498 LABEL *lcont = NEW_LABEL(node_location->line);
5499
5500 pm_scope_node_t rescue_scope_node;
5501 pm_scope_node_init((const pm_node_t *) cast->rescue_clause, &rescue_scope_node, scope_node);
5502
5503 rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(
5504 &rescue_scope_node,
5505 rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label),
5506 ISEQ_TYPE_RESCUE,
5507 pm_node_line_number_cached((const pm_node_t *) cast->rescue_clause, scope_node)
5508 );
5509
5510 pm_scope_node_destroy(&rescue_scope_node);
5511
5512 lstart->rescued = LABEL_RESCUE_BEG;
5513 lend->rescued = LABEL_RESCUE_END;
5514 PUSH_LABEL(ret, lstart);
5515
5516 bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
5517 ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
5518
5519 if (cast->statements != NULL) {
5520 PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->statements);
5521 }
5522 else {
5523 const pm_node_location_t location = PM_NODE_START_LOCATION(cast->rescue_clause);
5524 PUSH_INSN(ret, location, putnil);
5525 }
5526
5527 ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
5528 PUSH_LABEL(ret, lend);
5529
5530 if (cast->else_clause != NULL) {
5531 if (!popped) PUSH_INSN(ret, *node_location, pop);
5532 PM_COMPILE((const pm_node_t *) cast->else_clause);
5533 }
5534
5535 PUSH_INSN(ret, *node_location, nop);
5536 PUSH_LABEL(ret, lcont);
5537
5538 if (popped) PUSH_INSN(ret, *node_location, pop);
5539 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
5540 PUSH_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
5541}
5542
5543static void
5544pm_compile_ensure(rb_iseq_t *iseq, const pm_begin_node_t *cast, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5545{
5546 const pm_statements_node_t *statements = cast->ensure_clause->statements;
5547
5548 pm_node_location_t location;
5549 if (statements != NULL) {
5550 location = PM_NODE_START_LOCATION(statements);
5551 }
5552 else {
5553 location = *node_location;
5554 }
5555
5556 LABEL *lstart = NEW_LABEL(location.line);
5557 LABEL *lend = NEW_LABEL(location.line);
5558 LABEL *lcont = NEW_LABEL(location.line);
5559
5560 struct ensure_range er;
5562 struct ensure_range *erange;
5563
5564 DECL_ANCHOR(ensr);
5565 if (statements != NULL) {
5566 pm_compile_node(iseq, (const pm_node_t *) statements, ensr, true, scope_node);
5567 }
5568
5569 LINK_ELEMENT *last = ensr->last;
5570 bool last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave);
5571
5572 er.begin = lstart;
5573 er.end = lend;
5574 er.next = 0;
5575 push_ensure_entry(iseq, &enl, &er, (void *) cast->ensure_clause);
5576
5577 PUSH_LABEL(ret, lstart);
5578 if (cast->rescue_clause != NULL) {
5579 pm_compile_rescue(iseq, cast, node_location, ret, popped | last_leave, scope_node);
5580 }
5581 else if (cast->statements != NULL) {
5582 pm_compile_node(iseq, (const pm_node_t *) cast->statements, ret, popped | last_leave, scope_node);
5583 }
5584 else if (!(popped | last_leave)) {
5585 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
5586 }
5587
5588 PUSH_LABEL(ret, lend);
5589 PUSH_SEQ(ret, ensr);
5590 if (!popped && last_leave) PUSH_INSN(ret, *node_location, putnil);
5591 PUSH_LABEL(ret, lcont);
5592 if (last_leave) PUSH_INSN(ret, *node_location, pop);
5593
5594 pm_scope_node_t next_scope_node;
5595 pm_scope_node_init((const pm_node_t *) cast->ensure_clause, &next_scope_node, scope_node);
5596
5597 rb_iseq_t *child_iseq = NEW_CHILD_ISEQ(
5598 &next_scope_node,
5599 rb_str_concat(rb_str_new2("ensure in "), ISEQ_BODY(iseq)->location.label),
5600 ISEQ_TYPE_ENSURE,
5601 location.line
5602 );
5603
5604 pm_scope_node_destroy(&next_scope_node);
5605
5606 erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
5607 if (lstart->link.next != &lend->link) {
5608 while (erange) {
5609 PUSH_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end, child_iseq, lcont);
5610 erange = erange->next;
5611 }
5612 }
5613 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
5614}
5615
5620static inline bool
5621pm_opt_str_freeze_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
5622{
5623 return (
5624 !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) &&
5625 node->receiver != NULL &&
5626 PM_NODE_TYPE_P(node->receiver, PM_STRING_NODE) &&
5627 node->arguments == NULL &&
5628 node->block == NULL &&
5629 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
5630 );
5631}
5632
5637static void
5638pm_compile_constant_read(rb_iseq_t *iseq, VALUE name, const pm_location_t *name_loc, uint32_t node_id, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
5639{
5640 const pm_node_location_t location = PM_LOCATION_START_LOCATION(name_loc, node_id);
5641
5642 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
5643 ISEQ_BODY(iseq)->ic_size++;
5644 VALUE segments = rb_ary_new_from_args(1, name);
5645 RB_OBJ_SET_SHAREABLE(segments);
5646 PUSH_INSN1(ret, location, opt_getconstant_path, segments);
5647 }
5648 else {
5649 PUSH_INSN(ret, location, putnil);
5650 PUSH_INSN1(ret, location, putobject, Qtrue);
5651 PUSH_INSN1(ret, location, getconstant, name);
5652 }
5653}
5654
5659static VALUE
5660pm_constant_path_parts(const pm_node_t *node, const pm_scope_node_t *scope_node)
5661{
5662 VALUE parts = rb_ary_new();
5663
5664 while (true) {
5665 switch (PM_NODE_TYPE(node)) {
5666 case PM_CONSTANT_READ_NODE: {
5668 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5669
5670 rb_ary_unshift(parts, name);
5671 return parts;
5672 }
5673 case PM_CONSTANT_PATH_NODE: {
5675 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5676
5677 rb_ary_unshift(parts, name);
5678 if (cast->parent == NULL) {
5679 rb_ary_unshift(parts, ID2SYM(idNULL));
5680 return parts;
5681 }
5682
5683 node = cast->parent;
5684 break;
5685 }
5686 default:
5687 return Qnil;
5688 }
5689 }
5690}
5691
5697static void
5698pm_compile_constant_path(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const prefix, LINK_ANCHOR *const body, bool popped, pm_scope_node_t *scope_node)
5699{
5700 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5701
5702 switch (PM_NODE_TYPE(node)) {
5703 case PM_CONSTANT_READ_NODE: {
5705 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5706
5707 PUSH_INSN1(body, location, putobject, Qtrue);
5708 PUSH_INSN1(body, location, getconstant, name);
5709 break;
5710 }
5711 case PM_CONSTANT_PATH_NODE: {
5713 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5714
5715 if (cast->parent == NULL) {
5716 PUSH_INSN(body, location, pop);
5717 PUSH_INSN1(body, location, putobject, rb_cObject);
5718 PUSH_INSN1(body, location, putobject, Qtrue);
5719 PUSH_INSN1(body, location, getconstant, name);
5720 }
5721 else {
5722 pm_compile_constant_path(iseq, cast->parent, prefix, body, false, scope_node);
5723 PUSH_INSN1(body, location, putobject, Qfalse);
5724 PUSH_INSN1(body, location, getconstant, name);
5725 }
5726 break;
5727 }
5728 default:
5729 PM_COMPILE_INTO_ANCHOR(prefix, node);
5730 break;
5731 }
5732}
5733
5737static VALUE
5738pm_compile_shareable_constant_literal(rb_iseq_t *iseq, const pm_node_t *node, pm_scope_node_t *scope_node)
5739{
5740 switch (PM_NODE_TYPE(node)) {
5741 case PM_TRUE_NODE:
5742 case PM_FALSE_NODE:
5743 case PM_NIL_NODE:
5744 case PM_SYMBOL_NODE:
5745 case PM_REGULAR_EXPRESSION_NODE:
5746 case PM_SOURCE_LINE_NODE:
5747 case PM_INTEGER_NODE:
5748 case PM_FLOAT_NODE:
5749 case PM_RATIONAL_NODE:
5750 case PM_IMAGINARY_NODE:
5751 case PM_SOURCE_ENCODING_NODE:
5752 return pm_static_literal_value(iseq, node, scope_node);
5753 case PM_STRING_NODE:
5754 return parse_static_literal_string(iseq, scope_node, node, &((const pm_string_node_t *) node)->unescaped);
5755 case PM_ARRAY_NODE: {
5756 const pm_array_node_t *cast = (const pm_array_node_t *) node;
5757 VALUE result = rb_ary_new_capa(cast->elements.size);
5758
5759 for (size_t index = 0; index < cast->elements.size; index++) {
5760 VALUE element = pm_compile_shareable_constant_literal(iseq, cast->elements.nodes[index], scope_node);
5761 if (element == Qundef) return Qundef;
5762
5763 rb_ary_push(result, element);
5764 }
5765
5766 return rb_ractor_make_shareable(result);
5767 }
5768 case PM_HASH_NODE: {
5769 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
5770 VALUE result = rb_hash_alloc_fixed_size(rb_cHash, cast->elements.size);
5771
5772 for (size_t index = 0; index < cast->elements.size; index++) {
5773 const pm_node_t *element = cast->elements.nodes[index];
5774 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) return Qundef;
5775
5776 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
5777
5778 VALUE key = pm_compile_shareable_constant_literal(iseq, assoc->key, scope_node);
5779 if (key == Qundef) return Qundef;
5780
5781 VALUE value = pm_compile_shareable_constant_literal(iseq, assoc->value, scope_node);
5782 if (value == Qundef) return Qundef;
5783
5784 rb_hash_aset(result, key, value);
5785 }
5786
5787 return rb_ractor_make_shareable(result);
5788 }
5789 default:
5790 return Qundef;
5791 }
5792}
5793
5798static void
5799pm_compile_shareable_constant_value(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_flags_t shareability, VALUE path, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, bool top)
5800{
5801 VALUE literal = pm_compile_shareable_constant_literal(iseq, node, scope_node);
5802 if (literal != Qundef) {
5803 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5804 PUSH_INSN1(ret, location, putobject, literal);
5805 return;
5806 }
5807
5808 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5809 switch (PM_NODE_TYPE(node)) {
5810 case PM_ARRAY_NODE: {
5811 const pm_array_node_t *cast = (const pm_array_node_t *) node;
5812
5813 if (top) {
5814 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5815 }
5816
5817 for (size_t index = 0; index < cast->elements.size; index++) {
5818 pm_compile_shareable_constant_value(iseq, cast->elements.nodes[index], shareability, path, ret, scope_node, false);
5819 }
5820
5821 PUSH_INSN1(ret, location, newarray, INT2FIX(cast->elements.size));
5822
5823 if (top) {
5824 ID method_id = (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) ? rb_intern("make_shareable_copy") : rb_intern("make_shareable");
5825 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5826 }
5827
5828 return;
5829 }
5830 case PM_HASH_NODE: {
5831 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
5832
5833 if (top) {
5834 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5835 }
5836
5837 pm_compile_hash_elements(iseq, (const pm_node_t *) cast, &cast->elements, shareability, path, false, ret, scope_node);
5838
5839 if (top) {
5840 ID method_id = (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) ? rb_intern("make_shareable_copy") : rb_intern("make_shareable");
5841 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5842 }
5843
5844 return;
5845 }
5846 default: {
5847 DECL_ANCHOR(value_seq);
5848
5849 pm_compile_node(iseq, node, value_seq, false, scope_node);
5850 if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) {
5851 PUSH_SEND_WITH_FLAG(value_seq, location, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE));
5852 }
5853
5854 if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL) {
5855 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5856 PUSH_SEQ(ret, value_seq);
5857 if (!RB_OBJ_SHAREABLE_P(path)) {
5859 }
5860 PUSH_INSN1(ret, location, putobject, path);
5861 PUSH_SEND_WITH_FLAG(ret, location, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE));
5862 }
5863 else if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) {
5864 if (top) PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5865 PUSH_SEQ(ret, value_seq);
5866 if (top) PUSH_SEND_WITH_FLAG(ret, location, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5867 }
5868 else if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING) {
5869 if (top) PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5870 PUSH_SEQ(ret, value_seq);
5871 if (top) PUSH_SEND_WITH_FLAG(ret, location, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5872 }
5873
5874 break;
5875 }
5876 }
5877}
5878
5883static void
5884pm_compile_constant_write_node(rb_iseq_t *iseq, const pm_constant_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5885{
5886 const pm_node_location_t location = *node_location;
5887 ID name_id = pm_constant_id_lookup(scope_node, node->name);
5888
5889 if (shareability != 0) {
5890 pm_compile_shareable_constant_value(iseq, node->value, shareability, rb_id2str(name_id), ret, scope_node, true);
5891 }
5892 else {
5893 PM_COMPILE_NOT_POPPED(node->value);
5894 }
5895
5896 if (!popped) PUSH_INSN(ret, location, dup);
5897 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5898
5899 VALUE operand = ID2SYM(name_id);
5900 PUSH_INSN1(ret, location, setconstant, operand);
5901}
5902
5907static void
5908pm_compile_constant_and_write_node(rb_iseq_t *iseq, const pm_constant_and_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5909{
5910 const pm_node_location_t location = *node_location;
5911
5912 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5913 LABEL *end_label = NEW_LABEL(location.line);
5914
5915 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5916 if (!popped) PUSH_INSN(ret, location, dup);
5917
5918 PUSH_INSNL(ret, location, branchunless, end_label);
5919 if (!popped) PUSH_INSN(ret, location, pop);
5920
5921 if (shareability != 0) {
5922 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5923 }
5924 else {
5925 PM_COMPILE_NOT_POPPED(node->value);
5926 }
5927
5928 if (!popped) PUSH_INSN(ret, location, dup);
5929 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5930 PUSH_INSN1(ret, location, setconstant, name);
5931 PUSH_LABEL(ret, end_label);
5932}
5933
5938static void
5939pm_compile_constant_or_write_node(rb_iseq_t *iseq, const pm_constant_or_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5940{
5941 const pm_node_location_t location = *node_location;
5942 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5943
5944 LABEL *set_label = NEW_LABEL(location.line);
5945 LABEL *end_label = NEW_LABEL(location.line);
5946
5947 PUSH_INSN(ret, location, putnil);
5948 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), name, Qtrue);
5949 PUSH_INSNL(ret, location, branchunless, set_label);
5950
5951 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5952 if (!popped) PUSH_INSN(ret, location, dup);
5953
5954 PUSH_INSNL(ret, location, branchif, end_label);
5955 if (!popped) PUSH_INSN(ret, location, pop);
5956 PUSH_LABEL(ret, set_label);
5957
5958 if (shareability != 0) {
5959 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5960 }
5961 else {
5962 PM_COMPILE_NOT_POPPED(node->value);
5963 }
5964
5965 if (!popped) PUSH_INSN(ret, location, dup);
5966 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5967 PUSH_INSN1(ret, location, setconstant, name);
5968 PUSH_LABEL(ret, end_label);
5969}
5970
5975static void
5976pm_compile_constant_operator_write_node(rb_iseq_t *iseq, const pm_constant_operator_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5977{
5978 const pm_node_location_t location = *node_location;
5979
5980 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5981 ID method_id = pm_constant_id_lookup(scope_node, node->binary_operator);
5982
5983 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5984
5985 if (shareability != 0) {
5986 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5987 }
5988 else {
5989 PM_COMPILE_NOT_POPPED(node->value);
5990 }
5991
5992 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5993 if (!popped) PUSH_INSN(ret, location, dup);
5994
5995 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5996 PUSH_INSN1(ret, location, setconstant, name);
5997}
5998
6003static VALUE
6004pm_constant_path_path(const pm_constant_path_node_t *node, const pm_scope_node_t *scope_node)
6005{
6006 VALUE parts = rb_ary_new();
6007 rb_ary_push(parts, rb_id2str(pm_constant_id_lookup(scope_node, node->name)));
6008
6009 const pm_node_t *current = node->parent;
6010 while (current != NULL && PM_NODE_TYPE_P(current, PM_CONSTANT_PATH_NODE)) {
6011 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) current;
6012 rb_ary_unshift(parts, rb_id2str(pm_constant_id_lookup(scope_node, cast->name)));
6013 current = cast->parent;
6014 }
6015
6016 if (current == NULL) {
6017 rb_ary_unshift(parts, rb_id2str(idNULL));
6018 }
6019 else if (PM_NODE_TYPE_P(current, PM_CONSTANT_READ_NODE)) {
6020 rb_ary_unshift(parts, rb_id2str(pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) current)->name)));
6021 }
6022 else {
6023 rb_ary_unshift(parts, rb_str_new_cstr("..."));
6024 }
6025
6026 return rb_ary_join(parts, rb_str_new_cstr("::"));
6027}
6028
6033static void
6034pm_compile_constant_path_write_node(rb_iseq_t *iseq, const pm_constant_path_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
6035{
6036 const pm_node_location_t location = *node_location;
6037 const pm_constant_path_node_t *target = node->target;
6038 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6039
6040 if (target->parent) {
6041 PM_COMPILE_NOT_POPPED((const pm_node_t *) target->parent);
6042 }
6043 else {
6044 PUSH_INSN1(ret, location, putobject, rb_cObject);
6045 }
6046
6047 if (shareability != 0) {
6048 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6049 }
6050 else {
6051 PM_COMPILE_NOT_POPPED(node->value);
6052 }
6053
6054 if (!popped) {
6055 PUSH_INSN(ret, location, swap);
6056 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6057 }
6058
6059 PUSH_INSN(ret, location, swap);
6060 PUSH_INSN1(ret, location, setconstant, name);
6061}
6062
6067static void
6068pm_compile_constant_path_and_write_node(rb_iseq_t *iseq, const pm_constant_path_and_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
6069{
6070 const pm_node_location_t location = *node_location;
6071 const pm_constant_path_node_t *target = node->target;
6072
6073 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6074 LABEL *lfin = NEW_LABEL(location.line);
6075
6076 if (target->parent) {
6077 PM_COMPILE_NOT_POPPED(target->parent);
6078 }
6079 else {
6080 PUSH_INSN1(ret, location, putobject, rb_cObject);
6081 }
6082
6083 PUSH_INSN(ret, location, dup);
6084 PUSH_INSN1(ret, location, putobject, Qtrue);
6085 PUSH_INSN1(ret, location, getconstant, name);
6086
6087 if (!popped) PUSH_INSN(ret, location, dup);
6088 PUSH_INSNL(ret, location, branchunless, lfin);
6089
6090 if (!popped) PUSH_INSN(ret, location, pop);
6091
6092 if (shareability != 0) {
6093 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6094 }
6095 else {
6096 PM_COMPILE_NOT_POPPED(node->value);
6097 }
6098
6099 if (popped) {
6100 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6101 }
6102 else {
6103 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
6104 PUSH_INSN(ret, location, swap);
6105 }
6106
6107 PUSH_INSN1(ret, location, setconstant, name);
6108 PUSH_LABEL(ret, lfin);
6109
6110 if (!popped) PUSH_INSN(ret, location, swap);
6111 PUSH_INSN(ret, location, pop);
6112}
6113
6118static void
6119pm_compile_constant_path_or_write_node(rb_iseq_t *iseq, const pm_constant_path_or_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
6120{
6121 const pm_node_location_t location = *node_location;
6122 const pm_constant_path_node_t *target = node->target;
6123
6124 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6125 LABEL *lassign = NEW_LABEL(location.line);
6126 LABEL *lfin = NEW_LABEL(location.line);
6127
6128 if (target->parent) {
6129 PM_COMPILE_NOT_POPPED(target->parent);
6130 }
6131 else {
6132 PUSH_INSN1(ret, location, putobject, rb_cObject);
6133 }
6134
6135 PUSH_INSN(ret, location, dup);
6136 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), name, Qtrue);
6137 PUSH_INSNL(ret, location, branchunless, lassign);
6138
6139 PUSH_INSN(ret, location, dup);
6140 PUSH_INSN1(ret, location, putobject, Qtrue);
6141 PUSH_INSN1(ret, location, getconstant, name);
6142
6143 if (!popped) PUSH_INSN(ret, location, dup);
6144 PUSH_INSNL(ret, location, branchif, lfin);
6145
6146 if (!popped) PUSH_INSN(ret, location, pop);
6147 PUSH_LABEL(ret, lassign);
6148
6149 if (shareability != 0) {
6150 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6151 }
6152 else {
6153 PM_COMPILE_NOT_POPPED(node->value);
6154 }
6155
6156 if (popped) {
6157 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6158 }
6159 else {
6160 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
6161 PUSH_INSN(ret, location, swap);
6162 }
6163
6164 PUSH_INSN1(ret, location, setconstant, name);
6165 PUSH_LABEL(ret, lfin);
6166
6167 if (!popped) PUSH_INSN(ret, location, swap);
6168 PUSH_INSN(ret, location, pop);
6169}
6170
6175static void
6176pm_compile_constant_path_operator_write_node(rb_iseq_t *iseq, const pm_constant_path_operator_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
6177{
6178 const pm_node_location_t location = *node_location;
6179 const pm_constant_path_node_t *target = node->target;
6180
6181 ID method_id = pm_constant_id_lookup(scope_node, node->binary_operator);
6182 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6183
6184 if (target->parent) {
6185 PM_COMPILE_NOT_POPPED(target->parent);
6186 }
6187 else {
6188 PUSH_INSN1(ret, location, putobject, rb_cObject);
6189 }
6190
6191 PUSH_INSN(ret, location, dup);
6192 PUSH_INSN1(ret, location, putobject, Qtrue);
6193 PUSH_INSN1(ret, location, getconstant, name);
6194
6195 if (shareability != 0) {
6196 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6197 }
6198 else {
6199 PM_COMPILE_NOT_POPPED(node->value);
6200 }
6201
6202 PUSH_CALL(ret, location, method_id, INT2FIX(1));
6203 PUSH_INSN(ret, location, swap);
6204
6205 if (!popped) {
6206 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6207 PUSH_INSN(ret, location, swap);
6208 }
6209
6210 PUSH_INSN1(ret, location, setconstant, name);
6211}
6212
6219#define PM_CONTAINER_P(node) (PM_NODE_TYPE_P(node, PM_ARRAY_NODE) || PM_NODE_TYPE_P(node, PM_HASH_NODE) || PM_NODE_TYPE_P(node, PM_RANGE_NODE))
6220
6225static inline void
6226pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped)
6227{
6228 const pm_node_location_t location = *node_location;
6229 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
6230
6231 pm_constant_id_list_t *locals = &scope_node->locals;
6232 pm_parameters_node_t *parameters_node = NULL;
6233 pm_node_list_t *keywords_list = NULL;
6234 pm_node_list_t *optionals_list = NULL;
6235 pm_node_list_t *posts_list = NULL;
6236 pm_node_list_t *requireds_list = NULL;
6237 pm_node_list_t *block_locals = NULL;
6238 bool trailing_comma = false;
6239
6240 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE) || PM_NODE_TYPE_P(scope_node->ast_node, PM_MODULE_NODE)) {
6241 PUSH_TRACE(ret, RUBY_EVENT_CLASS);
6242 }
6243
6244 if (scope_node->parameters != NULL) {
6245 switch (PM_NODE_TYPE(scope_node->parameters)) {
6246 case PM_BLOCK_PARAMETERS_NODE: {
6247 pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) scope_node->parameters;
6248 parameters_node = cast->parameters;
6249 block_locals = &cast->locals;
6250
6251 if (parameters_node) {
6252 if (parameters_node->rest && PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE)) {
6253 trailing_comma = true;
6254 }
6255 }
6256 break;
6257 }
6258 case PM_PARAMETERS_NODE: {
6259 parameters_node = (pm_parameters_node_t *) scope_node->parameters;
6260 break;
6261 }
6262 case PM_NUMBERED_PARAMETERS_NODE: {
6263 uint32_t maximum = ((const pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
6264 body->param.lead_num = maximum;
6265 body->param.flags.ambiguous_param0 = maximum == 1;
6266 break;
6267 }
6268 case PM_IT_PARAMETERS_NODE:
6269 body->param.lead_num = 1;
6270 body->param.flags.ambiguous_param0 = true;
6271 break;
6272 default:
6273 rb_bug("Unexpected node type for parameters: %s", pm_node_type(PM_NODE_TYPE(scope_node->parameters)));
6274 }
6275 }
6276
6277 struct rb_iseq_param_keyword *keyword = NULL;
6278
6279 if (parameters_node) {
6280 optionals_list = &parameters_node->optionals;
6281 requireds_list = &parameters_node->requireds;
6282 keywords_list = &parameters_node->keywords;
6283 posts_list = &parameters_node->posts;
6284 }
6285 else if (scope_node->parameters && (PM_NODE_TYPE_P(scope_node->parameters, PM_NUMBERED_PARAMETERS_NODE) || PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE))) {
6286 body->param.opt_num = 0;
6287 }
6288 else {
6289 body->param.lead_num = 0;
6290 body->param.opt_num = 0;
6291 }
6292
6293 //********STEP 1**********
6294 // Goal: calculate the table size for the locals, accounting for
6295 // hidden variables and multi target nodes
6296 size_t locals_size = locals->size;
6297
6298 // Index lookup table buffer size is only the number of the locals.
6299 // We'll initialize it after computing table_size below.
6300 pm_index_lookup_table_t index_lookup_table = PM_INDEX_LOOKUP_TABLE_INIT;
6301
6302 int table_size = (int) locals_size;
6303
6304 // For nodes have a hidden iteration variable. We add that to the local
6305 // table size here.
6306 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) table_size++;
6307
6308 if (keywords_list && keywords_list->size) {
6309 table_size++;
6310 }
6311
6312 if (requireds_list) {
6313 for (size_t i = 0; i < requireds_list->size; i++) {
6314 // For each MultiTargetNode, we're going to have one
6315 // additional anonymous local not represented in the locals table
6316 // We want to account for this in our table size
6317 pm_node_t *required = requireds_list->nodes[i];
6318 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
6319 table_size++;
6320 }
6321 else if (PM_NODE_TYPE_P(required, PM_REQUIRED_PARAMETER_NODE)) {
6322 if (PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6323 table_size++;
6324 }
6325 }
6326 }
6327 }
6328
6329 // If we have the `it` implicit local variable, we need to account for
6330 // it in the local table size.
6331 if (scope_node->parameters != NULL && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
6332 table_size++;
6333 }
6334
6335 // Ensure there is enough room in the local table for any
6336 // parameters that have been repeated
6337 // ex: def underscore_parameters(_, _ = 1, _ = 2); _; end
6338 // ^^^^^^^^^^^^
6339 if (optionals_list && optionals_list->size) {
6340 for (size_t i = 0; i < optionals_list->size; i++) {
6341 pm_node_t * node = optionals_list->nodes[i];
6342 if (PM_NODE_FLAG_P(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6343 table_size++;
6344 }
6345 }
6346 }
6347
6348 // If we have an anonymous "rest" node, we'll need to increase the local
6349 // table size to take it in to account.
6350 // def m(foo, *, bar)
6351 // ^
6352 if (parameters_node) {
6353 if (parameters_node->rest) {
6354 if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
6355 if (!((const pm_rest_parameter_node_t *) parameters_node->rest)->name || PM_NODE_FLAG_P(parameters_node->rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6356 table_size++;
6357 }
6358 }
6359 }
6360
6361 // def foo(_, **_); _; end
6362 // ^^^
6363 if (parameters_node->keyword_rest) {
6364 // def foo(...); end
6365 // ^^^
6366 // When we have a `...` as the keyword_rest, it's a forwarding_parameter_node and
6367 // we need to leave space for 4 locals: *, **, &, ...
6368 if (PM_NODE_TYPE_P(parameters_node->keyword_rest, PM_FORWARDING_PARAMETER_NODE)) {
6369 // Only optimize specifically methods like this: `foo(...)`
6370 if (requireds_list->size == 0 && optionals_list->size == 0 && keywords_list->size == 0) {
6371 ISEQ_BODY(iseq)->param.flags.use_block = TRUE;
6372 ISEQ_BODY(iseq)->param.flags.forwardable = TRUE;
6373 table_size += 1;
6374 }
6375 else {
6376 table_size += 4;
6377 }
6378 }
6379 else {
6380 const pm_keyword_rest_parameter_node_t *kw_rest = (const pm_keyword_rest_parameter_node_t *) parameters_node->keyword_rest;
6381
6382 // If it's anonymous or repeated, then we need to allocate stack space
6383 if (!kw_rest->name || PM_NODE_FLAG_P(kw_rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6384 table_size++;
6385 }
6386 }
6387 }
6388 }
6389
6390 if (posts_list) {
6391 for (size_t i = 0; i < posts_list->size; i++) {
6392 // For each MultiTargetNode, we're going to have one
6393 // additional anonymous local not represented in the locals table
6394 // We want to account for this in our table size
6395 pm_node_t *required = posts_list->nodes[i];
6396 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE) || PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6397 table_size++;
6398 }
6399 }
6400 }
6401
6402 if (keywords_list && keywords_list->size) {
6403 for (size_t i = 0; i < keywords_list->size; i++) {
6404 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6405 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6406 table_size++;
6407 }
6408 }
6409 }
6410
6411 if (parameters_node && parameters_node->block && PM_NODE_TYPE_P(parameters_node->block, PM_BLOCK_PARAMETER_NODE)) {
6412 const pm_block_parameter_node_t *block_node = (const pm_block_parameter_node_t *) parameters_node->block;
6413
6414 if (PM_NODE_FLAG_P(block_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER) || !block_node->name) {
6415 table_size++;
6416 }
6417 }
6418
6419 // We can create local_table_for_iseq with the correct size
6420 VALUE idtmp = 0;
6421 rb_ast_id_table_t *local_table_for_iseq = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
6422 local_table_for_iseq->size = table_size;
6423
6424 // Init the direct-indexed lookup table. The capacity is based on the
6425 // parser's constant pool size (for regular locals) plus special slots.
6426 pm_index_lookup_table_init(&index_lookup_table, (int) pm_parser_constants_size(scope_node->parser), iseq);
6427
6428 //********END OF STEP 1**********
6429
6430 //********STEP 2**********
6431 // Goal: populate iv index table as well as local table, keeping the
6432 // layout of the local table consistent with the layout of the
6433 // stack when calling the method
6434 //
6435 // Do a first pass on all of the parameters, setting their values in
6436 // the local_table_for_iseq, _except_ for Multis who get a hidden
6437 // variable in this step, and will get their names inserted in step 3
6438
6439 // local_index is a cursor that keeps track of the current
6440 // index into local_table_for_iseq. The local table is actually a list,
6441 // and the order of that list must match the order of the items pushed
6442 // on the stack. We need to take in to account things pushed on the
6443 // stack that _might not have a name_ (for example array destructuring).
6444 // This index helps us know which item we're dealing with and also give
6445 // those anonymous items temporary names (as below)
6446 int local_index = 0;
6447
6448 // Here we figure out local table indices and insert them in to the
6449 // index lookup table and local tables.
6450 //
6451 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6452 // ^^^^^^^^^^^^^
6453 if (requireds_list && requireds_list->size) {
6454 for (size_t i = 0; i < requireds_list->size; i++, local_index++) {
6455 ID local;
6456
6457 // For each MultiTargetNode, we're going to have one additional
6458 // anonymous local not represented in the locals table. We want
6459 // to account for this in our table size.
6460 pm_node_t *required = requireds_list->nodes[i];
6461
6462 switch (PM_NODE_TYPE(required)) {
6463 case PM_MULTI_TARGET_NODE: {
6464 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6465 // ^^^^^^^^^^
6466 local = rb_make_temporary_id(local_index);
6467 local_table_for_iseq->ids[local_index] = local;
6468 break;
6469 }
6470 case PM_REQUIRED_PARAMETER_NODE: {
6471 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6472 // ^
6473 const pm_required_parameter_node_t *param = (const pm_required_parameter_node_t *) required;
6474
6475 if (PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6476 ID local = pm_constant_id_lookup(scope_node, param->name);
6477 local_table_for_iseq->ids[local_index] = local;
6478 }
6479 else {
6480 pm_insert_local_index(param->name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6481 }
6482
6483 break;
6484 }
6485 default:
6486 rb_bug("Unsupported node in requireds in parameters %s", pm_node_type(PM_NODE_TYPE(required)));
6487 }
6488 }
6489
6490 body->param.lead_num = (int) requireds_list->size;
6491 body->param.flags.has_lead = true;
6492 }
6493
6494 if (scope_node->parameters != NULL && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
6495 local_table_for_iseq->ids[local_index++] = idItImplicit;
6496 }
6497
6498 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6499 // ^^^^^
6500 if (optionals_list && optionals_list->size) {
6501 body->param.opt_num = (int) optionals_list->size;
6502 body->param.flags.has_opt = true;
6503
6504 for (size_t i = 0; i < optionals_list->size; i++, local_index++) {
6505 pm_node_t * node = optionals_list->nodes[i];
6506 pm_constant_id_t name = ((const pm_optional_parameter_node_t *) node)->name;
6507
6508 if (PM_NODE_FLAG_P(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6509 ID local = pm_constant_id_lookup(scope_node, name);
6510 local_table_for_iseq->ids[local_index] = local;
6511 }
6512 else {
6513 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6514 }
6515 }
6516 }
6517
6518 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6519 // ^^
6520 if (parameters_node && parameters_node->rest) {
6521 body->param.rest_start = local_index;
6522
6523 // If there's a trailing comma, we'll have an implicit rest node,
6524 // and we don't want it to impact the rest variables on param
6525 if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
6526 body->param.flags.has_rest = true;
6527 RUBY_ASSERT(body->param.rest_start != -1);
6528
6529 pm_constant_id_t name = ((const pm_rest_parameter_node_t *) parameters_node->rest)->name;
6530
6531 if (name) {
6532 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6533 // ^^
6534 if (PM_NODE_FLAG_P(parameters_node->rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6535 ID local = pm_constant_id_lookup(scope_node, name);
6536 local_table_for_iseq->ids[local_index] = local;
6537 }
6538 else {
6539 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6540 }
6541 }
6542 else {
6543 // def foo(a, (b, *c, d), e = 1, *, g, (h, *i, j), k:, l: 1, **m, &n)
6544 // ^
6545 body->param.flags.anon_rest = true;
6546 pm_insert_local_special(PM_CONSTANT_MULT, idMULT, local_index, &index_lookup_table, local_table_for_iseq);
6547 }
6548
6549 local_index++;
6550 }
6551 }
6552
6553 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6554 // ^^^^^^^^^^^^^
6555 if (posts_list && posts_list->size) {
6556 body->param.post_num = (int) posts_list->size;
6557 body->param.post_start = local_index;
6558 body->param.flags.has_post = true;
6559
6560 for (size_t i = 0; i < posts_list->size; i++, local_index++) {
6561 ID local;
6562
6563 // For each MultiTargetNode, we're going to have one additional
6564 // anonymous local not represented in the locals table. We want
6565 // to account for this in our table size.
6566 const pm_node_t *post_node = posts_list->nodes[i];
6567
6568 switch (PM_NODE_TYPE(post_node)) {
6569 case PM_MULTI_TARGET_NODE: {
6570 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6571 // ^^^^^^^^^^
6572 local = rb_make_temporary_id(local_index);
6573 local_table_for_iseq->ids[local_index] = local;
6574 break;
6575 }
6576 case PM_REQUIRED_PARAMETER_NODE: {
6577 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6578 // ^
6579 const pm_required_parameter_node_t *param = (const pm_required_parameter_node_t *) post_node;
6580
6581 if (PM_NODE_FLAG_P(param, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6582 ID local = pm_constant_id_lookup(scope_node, param->name);
6583 local_table_for_iseq->ids[local_index] = local;
6584 }
6585 else {
6586 pm_insert_local_index(param->name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6587 }
6588 break;
6589 }
6590 default:
6591 rb_bug("Unsupported node in posts in parameters %s", pm_node_type(PM_NODE_TYPE(post_node)));
6592 }
6593 }
6594 }
6595
6596 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6597 // ^^^^^^^^
6598 // Keywords create an internal variable on the parse tree
6599 if (keywords_list && keywords_list->size) {
6600 if (keywords_list->size > VM_CALL_KW_LEN_MAX) {
6601 COMPILE_ERROR(iseq, node_location->line, "too many keyword parameters (%d, maximum is %d)",
6602 (int) keywords_list->size, (int) VM_CALL_KW_LEN_MAX);
6603 }
6604
6605 keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6606 keyword->num = (int) keywords_list->size;
6607
6608 const VALUE default_values = rb_ary_hidden_new(1);
6609 const VALUE complex_mark = rb_str_tmp_new(0);
6610
6611 for (size_t i = 0; i < keywords_list->size; i++) {
6612 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6613 pm_constant_id_t name;
6614
6615 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6616 // ^^
6617 if (PM_NODE_TYPE_P(keyword_parameter_node, PM_REQUIRED_KEYWORD_PARAMETER_NODE)) {
6618 name = ((const pm_required_keyword_parameter_node_t *) keyword_parameter_node)->name;
6619 keyword->required_num++;
6620 ID local = pm_constant_id_lookup(scope_node, name);
6621
6622 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6623 local_table_for_iseq->ids[local_index] = local;
6624 }
6625 else {
6626 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6627 }
6628 local_index++;
6629 }
6630 }
6631
6632 for (size_t i = 0; i < keywords_list->size; i++) {
6633 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6634 pm_constant_id_t name;
6635
6636 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6637 // ^^^^
6638 if (PM_NODE_TYPE_P(keyword_parameter_node, PM_OPTIONAL_KEYWORD_PARAMETER_NODE)) {
6639 const pm_optional_keyword_parameter_node_t *cast = ((const pm_optional_keyword_parameter_node_t *) keyword_parameter_node);
6640
6641 pm_node_t *value = cast->value;
6642 name = cast->name;
6643
6644 if (PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL) && !PM_CONTAINER_P(value)) {
6645 rb_ary_push(default_values, pm_static_literal_value(iseq, value, scope_node));
6646 }
6647 else {
6648 rb_ary_push(default_values, complex_mark);
6649 }
6650
6651 ID local = pm_constant_id_lookup(scope_node, name);
6652 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6653 local_table_for_iseq->ids[local_index] = local;
6654 }
6655 else {
6656 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6657 }
6658 local_index++;
6659 }
6660
6661 }
6662
6663 if (RARRAY_LEN(default_values)) {
6664 VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
6665
6666 for (int i = 0; i < RARRAY_LEN(default_values); i++) {
6667 VALUE dv = RARRAY_AREF(default_values, i);
6668 if (dv == complex_mark) dv = Qundef;
6669 RB_OBJ_WRITE(iseq, &dvs[i], dv);
6670 }
6671
6672 keyword->default_values = dvs;
6673 }
6674
6675 // Hidden local for keyword arguments
6676 keyword->bits_start = local_index;
6677 ID local = rb_make_temporary_id(local_index);
6678 local_table_for_iseq->ids[local_index] = local;
6679 local_index++;
6680
6681 body->param.keyword = keyword;
6682 body->param.flags.has_kw = true;
6683 }
6684
6685 if (body->type == ISEQ_TYPE_BLOCK && local_index == 1 && requireds_list && requireds_list->size == 1 && !trailing_comma) {
6686 body->param.flags.ambiguous_param0 = true;
6687 }
6688
6689 if (parameters_node) {
6690 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6691 // ^^^
6692 if (parameters_node->keyword_rest) {
6693 switch (PM_NODE_TYPE(parameters_node->keyword_rest)) {
6694 case PM_NO_KEYWORDS_PARAMETER_NODE: {
6695 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **nil, &n)
6696 // ^^^^^
6697 body->param.flags.accepts_no_kwarg = true;
6698 break;
6699 }
6700 case PM_KEYWORD_REST_PARAMETER_NODE: {
6701 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6702 // ^^^
6703 const pm_keyword_rest_parameter_node_t *kw_rest_node = (const pm_keyword_rest_parameter_node_t *) parameters_node->keyword_rest;
6704 if (!body->param.flags.has_kw) {
6705 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6706 }
6707
6708 keyword->rest_start = local_index;
6709 body->param.flags.has_kwrest = true;
6710
6711 pm_constant_id_t constant_id = kw_rest_node->name;
6712 if (constant_id) {
6713 if (PM_NODE_FLAG_P(kw_rest_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6714 ID local = pm_constant_id_lookup(scope_node, constant_id);
6715 local_table_for_iseq->ids[local_index] = local;
6716 }
6717 else {
6718 pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6719 }
6720 }
6721 else {
6722 body->param.flags.anon_kwrest = true;
6723 pm_insert_local_special(PM_CONSTANT_POW, idPow, local_index, &index_lookup_table, local_table_for_iseq);
6724 }
6725
6726 local_index++;
6727 break;
6728 }
6729 case PM_FORWARDING_PARAMETER_NODE: {
6730 // def foo(...)
6731 // ^^^
6732 if (!ISEQ_BODY(iseq)->param.flags.forwardable) {
6733 // Add the anonymous *
6734 body->param.rest_start = local_index;
6735 body->param.flags.has_rest = true;
6736 body->param.flags.anon_rest = true;
6737 pm_insert_local_special(PM_CONSTANT_MULT, idMULT, local_index++, &index_lookup_table, local_table_for_iseq);
6738
6739 // Add the anonymous **
6740 RUBY_ASSERT(!body->param.flags.has_kw);
6741 body->param.flags.has_kw = false;
6742 body->param.flags.has_kwrest = true;
6743 body->param.flags.anon_kwrest = true;
6744 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6745 keyword->rest_start = local_index;
6746 pm_insert_local_special(PM_CONSTANT_POW, idPow, local_index++, &index_lookup_table, local_table_for_iseq);
6747
6748 // Add the anonymous &
6749 body->param.block_start = local_index;
6750 body->param.flags.has_block = true;
6751 pm_insert_local_special(PM_CONSTANT_AND, idAnd, local_index++, &index_lookup_table, local_table_for_iseq);
6752 }
6753
6754 // Add the ...
6755 pm_insert_local_special(PM_CONSTANT_DOT3, idDot3, local_index++, &index_lookup_table, local_table_for_iseq);
6756 break;
6757 }
6758 default:
6759 rb_bug("node type %s not expected as keyword_rest", pm_node_type(PM_NODE_TYPE(parameters_node->keyword_rest)));
6760 }
6761 }
6762
6763 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6764 // ^^
6765 if (parameters_node->block) {
6766 switch (PM_NODE_TYPE(parameters_node->block)) {
6767 case PM_BLOCK_PARAMETER_NODE: {
6768 body->param.block_start = local_index;
6769 body->param.flags.has_block = true;
6770
6771 iseq_set_use_block(iseq);
6772
6773 pm_constant_id_t name = ((const pm_block_parameter_node_t *) parameters_node->block)->name;
6774
6775 if (name) {
6776 if (PM_NODE_FLAG_P(parameters_node->block, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6777 ID local = pm_constant_id_lookup(scope_node, name);
6778 local_table_for_iseq->ids[local_index] = local;
6779 }
6780 else {
6781 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6782 }
6783 }
6784 else {
6785 pm_insert_local_special(PM_CONSTANT_AND, idAnd, local_index, &index_lookup_table, local_table_for_iseq);
6786 }
6787
6788 local_index++;
6789 break;
6790 }
6791 case PM_NO_BLOCK_PARAMETER_NODE: {
6792 body->param.flags.accepts_no_block = true;
6793 break;
6794 }
6795 default:
6796 rb_bug("node type %s not expected as block parameter", pm_node_type(PM_NODE_TYPE(parameters_node->block)));
6797 }
6798 }
6799 }
6800
6801 //********END OF STEP 2**********
6802 // The local table is now consistent with expected
6803 // stack layout
6804
6805 // If there's only one required element in the parameters
6806 // CRuby needs to recognize it as an ambiguous parameter
6807
6808 //********STEP 3**********
6809 // Goal: fill in the names of the parameters in MultiTargetNodes
6810 //
6811 // Go through requireds again to set the multis
6812
6813 if (requireds_list && requireds_list->size) {
6814 for (size_t i = 0; i < requireds_list->size; i++) {
6815 // For each MultiTargetNode, we're going to have one
6816 // additional anonymous local not represented in the locals table
6817 // We want to account for this in our table size
6818 const pm_node_t *required = requireds_list->nodes[i];
6819
6820 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
6821 local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) required, &index_lookup_table, local_table_for_iseq, scope_node, local_index);
6822 }
6823 }
6824 }
6825
6826 // Go through posts again to set the multis
6827 if (posts_list && posts_list->size) {
6828 for (size_t i = 0; i < posts_list->size; i++) {
6829 // For each MultiTargetNode, we're going to have one
6830 // additional anonymous local not represented in the locals table
6831 // We want to account for this in our table size
6832 const pm_node_t *post = posts_list->nodes[i];
6833
6834 if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
6835 local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) post, &index_lookup_table, local_table_for_iseq, scope_node, local_index);
6836 }
6837 }
6838 }
6839
6840 // Set any anonymous locals for the for node
6841 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
6842 if (PM_NODE_TYPE_P(((const pm_for_node_t *) scope_node->ast_node)->index, PM_LOCAL_VARIABLE_TARGET_NODE)) {
6843 body->param.lead_num++;
6844 }
6845 else {
6846 body->param.rest_start = local_index;
6847 body->param.flags.has_rest = true;
6848 }
6849
6850 ID local = rb_make_temporary_id(local_index);
6851 local_table_for_iseq->ids[local_index] = local;
6852 local_index++;
6853 }
6854
6855 // Fill in any NumberedParameters, if they exist
6856 if (scope_node->parameters && PM_NODE_TYPE_P(scope_node->parameters, PM_NUMBERED_PARAMETERS_NODE)) {
6857 int maximum = ((const pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
6858 RUBY_ASSERT(0 < maximum && maximum <= 9);
6859 for (int i = 0; i < maximum; i++, local_index++) {
6860 const uint8_t param_name[] = { '_', '1' + i };
6861 pm_constant_id_t constant_id = pm_parser_constant_find(scope_node->parser, param_name, 2);
6862 RUBY_ASSERT(constant_id && "parser should fill in any gaps in numbered parameters");
6863 pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6864 }
6865 body->param.lead_num = maximum;
6866 body->param.flags.has_lead = true;
6867 }
6868
6869 // Fill in the anonymous `it` parameter, if it exists
6870 if (scope_node->parameters && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
6871 body->param.lead_num = 1;
6872 body->param.flags.has_lead = true;
6873 }
6874
6875 //********END OF STEP 3**********
6876
6877 //********STEP 4**********
6878 // Goal: fill in the method body locals
6879 // To be explicit, these are the non-parameter locals
6880 // We fill in the block_locals, if they exist
6881 // lambda { |x; y| y }
6882 // ^
6883 if (block_locals && block_locals->size) {
6884 for (size_t i = 0; i < block_locals->size; i++, local_index++) {
6885 pm_constant_id_t constant_id = ((const pm_block_local_variable_node_t *) block_locals->nodes[i])->name;
6886 pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6887 }
6888 }
6889
6890 // Fill in any locals we missed
6891 if (scope_node->locals.size) {
6892 for (size_t i = 0; i < scope_node->locals.size; i++) {
6893 pm_constant_id_t constant_id = locals->ids[i];
6894 if (constant_id) {
6895 int existing;
6896 if (!pm_index_lookup_table_lookup(&index_lookup_table, constant_id, &existing)) {
6897 ID local = pm_constant_id_lookup(scope_node, constant_id);
6898 local_table_for_iseq->ids[local_index] = local;
6899 pm_index_lookup_table_insert(&index_lookup_table, constant_id, local_index);
6900 local_index++;
6901 }
6902 }
6903 }
6904 }
6905
6906 //********END OF STEP 4**********
6907
6908 // We set the index_lookup_table on the scope node so we can
6909 // refer to the parameters correctly.
6910 scope_node->index_lookup_table = index_lookup_table;
6911 iseq_calc_param_size(iseq);
6912
6913 if (ISEQ_BODY(iseq)->param.flags.forwardable) {
6914 // We're treating `...` as a parameter so that frame
6915 // pushing won't clobber it.
6916 ISEQ_BODY(iseq)->param.size += 1;
6917 }
6918
6919 // FIXME: args?
6920 iseq_set_local_table(iseq, local_table_for_iseq, 0);
6921 iseq_set_parameters_lvar_state(iseq);
6922
6923 scope_node->local_table_for_iseq_size = local_table_for_iseq->size;
6924
6925 if (keyword != NULL) {
6926 size_t keyword_start_index = keyword->bits_start - keyword->num;
6927 keyword->table = (ID *)&ISEQ_BODY(iseq)->local_table[keyword_start_index];
6928 }
6929
6930 local_table_for_iseq = NULL;
6931 if (idtmp) ALLOCV_END(idtmp);
6932
6933 //********STEP 5************
6934 // Goal: compile anything that needed to be compiled
6935 if (optionals_list && optionals_list->size) {
6936 LABEL **opt_table = (LABEL **) ALLOC_N(VALUE, optionals_list->size + 1);
6937 LABEL *label;
6938
6939 // TODO: Should we make an api for NEW_LABEL where you can pass
6940 // a pointer to the label it should fill out? We already
6941 // have a list of labels allocated above so it seems wasteful
6942 // to do the copies.
6943 for (size_t i = 0; i < optionals_list->size; i++) {
6944 label = NEW_LABEL(location.line);
6945 opt_table[i] = label;
6946 PUSH_LABEL(ret, label);
6947 pm_node_t *optional_node = optionals_list->nodes[i];
6948 PM_COMPILE_NOT_POPPED(optional_node);
6949 }
6950
6951 // Set the last label
6952 label = NEW_LABEL(location.line);
6953 opt_table[optionals_list->size] = label;
6954 PUSH_LABEL(ret, label);
6955
6956 body->param.opt_table = (const VALUE *) opt_table;
6957 }
6958
6959 if (keywords_list && keywords_list->size) {
6960 size_t optional_index = 0;
6961 for (size_t i = 0; i < keywords_list->size; i++) {
6962 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6963 pm_constant_id_t name;
6964
6965 switch (PM_NODE_TYPE(keyword_parameter_node)) {
6966 case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: {
6967 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6968 // ^^^^
6969 const pm_optional_keyword_parameter_node_t *cast = ((const pm_optional_keyword_parameter_node_t *) keyword_parameter_node);
6970
6971 pm_node_t *value = cast->value;
6972 name = cast->name;
6973
6974 if (!PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL) || PM_CONTAINER_P(value)) {
6975 LABEL *end_label = NEW_LABEL(location.line);
6976
6977 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, name, 0);
6978 int kw_bits_idx = table_size - body->param.keyword->bits_start;
6979 PUSH_INSN2(ret, location, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(optional_index));
6980 PUSH_INSNL(ret, location, branchif, end_label);
6981 PM_COMPILE(value);
6982 PUSH_SETLOCAL(ret, location, index.index, index.level);
6983 PUSH_LABEL(ret, end_label);
6984 }
6985 optional_index++;
6986 break;
6987 }
6988 case PM_REQUIRED_KEYWORD_PARAMETER_NODE:
6989 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6990 // ^^
6991 break;
6992 default:
6993 rb_bug("Unexpected keyword parameter node type %s", pm_node_type(PM_NODE_TYPE(keyword_parameter_node)));
6994 }
6995 }
6996 }
6997
6998 if (requireds_list && requireds_list->size) {
6999 for (size_t i = 0; i < requireds_list->size; i++) {
7000 // For each MultiTargetNode, we're going to have one additional
7001 // anonymous local not represented in the locals table. We want
7002 // to account for this in our table size.
7003 const pm_node_t *required = requireds_list->nodes[i];
7004
7005 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
7006 PUSH_GETLOCAL(ret, location, table_size - (int)i, 0);
7007 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) required, ret, scope_node);
7008 }
7009 }
7010 }
7011
7012 if (posts_list && posts_list->size) {
7013 for (size_t i = 0; i < posts_list->size; i++) {
7014 // For each MultiTargetNode, we're going to have one additional
7015 // anonymous local not represented in the locals table. We want
7016 // to account for this in our table size.
7017 const pm_node_t *post = posts_list->nodes[i];
7018
7019 if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
7020 PUSH_GETLOCAL(ret, location, table_size - body->param.post_start - (int) i, 0);
7021 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) post, ret, scope_node);
7022 }
7023 }
7024 }
7025
7026 switch (body->type) {
7027 case ISEQ_TYPE_PLAIN: {
7028 RUBY_ASSERT(PM_NODE_TYPE_P(scope_node->ast_node, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE));
7029
7031 pm_compile_regexp_dynamic(iseq, (const pm_node_t *) cast, &cast->parts, &location, ret, popped, scope_node);
7032
7033 break;
7034 }
7035 case ISEQ_TYPE_BLOCK: {
7036 LABEL *start = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(0);
7037 LABEL *end = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(0);
7038 const pm_node_location_t block_location = { .line = body->location.first_lineno, .node_id = scope_node->ast_node->node_id };
7039
7040 start->rescued = LABEL_RESCUE_BEG;
7041 end->rescued = LABEL_RESCUE_END;
7042
7043 // For nodes automatically assign the iteration variable to whatever
7044 // index variable. We need to handle that write here because it has
7045 // to happen in the context of the block. Note that this happens
7046 // before the B_CALL tracepoint event.
7047 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
7048 pm_compile_for_node_index(iseq, ((const pm_for_node_t *) scope_node->ast_node)->index, ret, scope_node);
7049 }
7050
7051 PUSH_TRACE(ret, RUBY_EVENT_B_CALL);
7052 PUSH_INSN(ret, block_location, nop);
7053 PUSH_LABEL(ret, start);
7054
7055 if (scope_node->body != NULL) {
7056 switch (PM_NODE_TYPE(scope_node->ast_node)) {
7057 case PM_POST_EXECUTION_NODE: {
7058 const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) scope_node->ast_node;
7059 PUSH_INSN1(ret, block_location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7060
7061 // We create another ScopeNode from the statements within the PostExecutionNode
7062 pm_scope_node_t next_scope_node;
7063 pm_scope_node_init((const pm_node_t *) cast->statements, &next_scope_node, scope_node);
7064
7065 const rb_iseq_t *block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(body->parent_iseq), ISEQ_TYPE_BLOCK, location.line);
7066 pm_scope_node_destroy(&next_scope_node);
7067
7068 PUSH_CALL_WITH_BLOCK(ret, block_location, id_core_set_postexe, INT2FIX(0), block);
7069 break;
7070 }
7071 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
7073 pm_compile_regexp_dynamic(iseq, (const pm_node_t *) cast, &cast->parts, &location, ret, popped, scope_node);
7074 break;
7075 }
7076 default:
7077 pm_compile_node(iseq, scope_node->body, ret, popped, scope_node);
7078 break;
7079 }
7080 }
7081 else {
7082 PUSH_INSN(ret, block_location, putnil);
7083 }
7084
7085 PUSH_LABEL(ret, end);
7086 PUSH_TRACE(ret, RUBY_EVENT_B_RETURN);
7087 ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
7088
7089 /* wide range catch handler must put at last */
7090 PUSH_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, NULL, start);
7091 PUSH_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, NULL, end);
7092 break;
7093 }
7094 case ISEQ_TYPE_ENSURE: {
7095 const pm_node_location_t statements_location = (scope_node->body != NULL ? PM_NODE_START_LOCATION(scope_node->body) : location);
7096 iseq_set_exception_local_table(iseq);
7097
7098 if (scope_node->body != NULL) {
7099 PM_COMPILE_POPPED((const pm_node_t *) scope_node->body);
7100 }
7101
7102 PUSH_GETLOCAL(ret, statements_location, 1, 0);
7103 PUSH_INSN1(ret, statements_location, throw, INT2FIX(0));
7104 return;
7105 }
7106 case ISEQ_TYPE_METHOD: {
7107 ISEQ_COMPILE_DATA(iseq)->root_node = (const void *) scope_node->body;
7108 PUSH_TRACE(ret, RUBY_EVENT_CALL);
7109
7110 if (scope_node->body) {
7111 PM_COMPILE((const pm_node_t *) scope_node->body);
7112 }
7113 else {
7114 PUSH_INSN(ret, location, putnil);
7115 }
7116
7117 ISEQ_COMPILE_DATA(iseq)->root_node = (const void *) scope_node->body;
7118 PUSH_TRACE(ret, RUBY_EVENT_RETURN);
7119
7120 ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
7121 break;
7122 }
7123 case ISEQ_TYPE_RESCUE: {
7124 iseq_set_exception_local_table(iseq);
7125 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_RESCUE_MODIFIER_NODE)) {
7126 LABEL *lab = NEW_LABEL(location.line);
7127 LABEL *rescue_end = NEW_LABEL(location.line);
7128 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
7129 PUSH_INSN1(ret, location, putobject, rb_eStandardError);
7130 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
7131 PUSH_INSNL(ret, location, branchif, lab);
7132 PUSH_INSNL(ret, location, jump, rescue_end);
7133 PUSH_LABEL(ret, lab);
7134 PUSH_TRACE(ret, RUBY_EVENT_RESCUE);
7135 PM_COMPILE((const pm_node_t *) scope_node->body);
7136 PUSH_INSN(ret, location, leave);
7137 PUSH_LABEL(ret, rescue_end);
7138 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
7139 }
7140 else {
7141 PM_COMPILE((const pm_node_t *) scope_node->ast_node);
7142 }
7143 PUSH_INSN1(ret, location, throw, INT2FIX(0));
7144
7145 return;
7146 }
7147 default:
7148 if (scope_node->body) {
7149 PM_COMPILE((const pm_node_t *) scope_node->body);
7150 }
7151 else {
7152 PUSH_INSN(ret, location, putnil);
7153 }
7154 break;
7155 }
7156
7157 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE) || PM_NODE_TYPE_P(scope_node->ast_node, PM_MODULE_NODE)) {
7158 const pm_node_location_t end_location = PM_NODE_END_LOCATION(scope_node->ast_node);
7159 PUSH_TRACE(ret, RUBY_EVENT_END);
7160 ISEQ_COMPILE_DATA(iseq)->last_line = end_location.line;
7161 }
7162
7163 if (!PM_NODE_TYPE_P(scope_node->ast_node, PM_ENSURE_NODE)) {
7164 const pm_node_location_t location = { .line = ISEQ_COMPILE_DATA(iseq)->last_line, .node_id = scope_node->ast_node->node_id };
7165 PUSH_INSN(ret, location, leave);
7166 }
7167}
7168
7169static inline void
7170pm_compile_alias_global_variable_node(rb_iseq_t *iseq, const pm_alias_global_variable_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
7171{
7172 // alias $foo $bar
7173 // ^^^^^^^^^^^^^^^
7174 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7175
7176 {
7177 const pm_location_t *name_loc = &node->new_name->location;
7178 VALUE operand = ID2SYM(rb_intern3((const char *) (pm_parser_start(scope_node->parser) + name_loc->start), name_loc->length, scope_node->encoding));
7179 PUSH_INSN1(ret, *location, putobject, operand);
7180 }
7181
7182 {
7183 const pm_location_t *name_loc = &node->old_name->location;
7184 VALUE operand = ID2SYM(rb_intern3((const char *) (pm_parser_start(scope_node->parser) + name_loc->start), name_loc->length, scope_node->encoding));
7185 PUSH_INSN1(ret, *location, putobject, operand);
7186 }
7187
7188 PUSH_SEND(ret, *location, id_core_set_variable_alias, INT2FIX(2));
7189 if (popped) PUSH_INSN(ret, *location, pop);
7190}
7191
7192static inline void
7193pm_compile_alias_method_node(rb_iseq_t *iseq, const pm_alias_method_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
7194{
7195 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7196 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
7197 PM_COMPILE_NOT_POPPED(node->new_name);
7198 PM_COMPILE_NOT_POPPED(node->old_name);
7199
7200 PUSH_SEND(ret, *location, id_core_set_method_alias, INT2FIX(3));
7201 if (popped) PUSH_INSN(ret, *location, pop);
7202}
7203
7218static void
7219pm_compile_logical_chain(rb_iseq_t *iseq, size_t size, const pm_node_t **nodes, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
7220{
7221 const pm_node_type_t type = PM_NODE_TYPE(nodes[1]);
7222 LABEL *end_label = NEW_LABEL(location->line);
7223
7224 for (size_t index = 0; index + 1 < size; index += 2) {
7225 PM_COMPILE_NOT_POPPED(nodes[index]);
7226
7227 /* Each operator node starts where its left operand does, so every
7228 * operator node in the chain is on the same line. */
7229 const pm_node_location_t operator_location = {
7230 .line = location->line,
7231 .node_id = nodes[index + 1]->node_id
7232 };
7233
7234 if (!popped) PUSH_INSN(ret, operator_location, dup);
7235 if (type == PM_AND_NODE)
7236 {
7237 PUSH_INSNL(ret, operator_location, branchunless, end_label);
7238 }
7239 else {
7240 PUSH_INSNL(ret, operator_location, branchif, end_label);
7241 }
7242 if (!popped) PUSH_INSN(ret, operator_location, pop);
7243 }
7244
7245 PM_COMPILE(nodes[size - 1]);
7246 PUSH_LABEL(ret, end_label);
7247}
7248
7249static void
7250pm_compile_and_node(rb_iseq_t *iseq, const pm_and_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
7251{
7252 const pm_node_t *cursor = (const pm_node_t *) node;
7253 size_t size = 1;
7254
7255 while (PM_NODE_TYPE_P(cursor, PM_AND_NODE)) {
7256 cursor = ((const pm_and_node_t *) cursor)->left;
7257 size += 2;
7258 }
7259
7260 VALUE handle = 0;
7261 const pm_node_t **nodes = ALLOCV_N(const pm_node_t *, handle, size);
7262
7263 cursor = (const pm_node_t *) node;
7264 size_t index = size;
7265
7266 while (PM_NODE_TYPE_P(cursor, PM_AND_NODE)) {
7267 const pm_and_node_t *cast = (const pm_and_node_t *) cursor;
7268 nodes[--index] = cast->right;
7269 nodes[--index] = cursor;
7270 cursor = cast->left;
7271 }
7272
7273 nodes[0] = cursor;
7274 pm_compile_logical_chain(iseq, size, nodes, location, ret, popped, scope_node);
7275 ALLOCV_END(handle);
7276}
7277
7278static inline void
7279pm_compile_array_node(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *elements, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
7280{
7281 // If every node in the array is static, then we can compile the entire
7282 // array now instead of later.
7283 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
7284 // We're only going to compile this node if it's not popped. If it
7285 // is popped, then we know we don't need to do anything since it's
7286 // statically known.
7287 if (!popped) {
7288 if (elements->size) {
7289 VALUE value = pm_static_literal_value(iseq, node, scope_node);
7291 PUSH_INSN1(ret, *location, duparray, value);
7292 }
7293 else {
7294 PUSH_INSN1(ret, *location, newarray, INT2FIX(0));
7295 }
7296 }
7297 return;
7298 }
7299
7300 // Here since we know there are possible side-effects inside the
7301 // array contents, we're going to build it entirely at runtime.
7302 // We'll do this by pushing all of the elements onto the stack and
7303 // then combining them with newarray.
7304 //
7305 // If this array is popped, then this serves only to ensure we enact
7306 // all side-effects (like method calls) that are contained within
7307 // the array contents.
7308 //
7309 // We treat all sequences of non-splat elements as their
7310 // own arrays, followed by a newarray, and then continually
7311 // concat the arrays with the SplatNode nodes.
7312 const int max_new_array_size = 0x100;
7313 const unsigned int min_tmp_array_size = 0x40;
7314
7315 int new_array_size = 0;
7316 bool first_chunk = true;
7317
7318 // This is an optimization wherein we keep track of whether or not
7319 // the previous element was a static literal. If it was, then we do
7320 // not attempt to check if we have a subarray that can be optimized.
7321 // If it was not, then we do check.
7322 bool static_literal = false;
7323
7324 // Either create a new array, or push to the existing array.
7325#define FLUSH_CHUNK \
7326 if (new_array_size) { \
7327 if (first_chunk) PUSH_INSN1(ret, *location, newarray, INT2FIX(new_array_size)); \
7328 else PUSH_INSN1(ret, *location, pushtoarray, INT2FIX(new_array_size)); \
7329 first_chunk = false; \
7330 new_array_size = 0; \
7331 }
7332
7333 for (size_t index = 0; index < elements->size; index++) {
7334 const pm_node_t *element = elements->nodes[index];
7335
7336 if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) {
7337 FLUSH_CHUNK;
7338
7339 const pm_splat_node_t *splat_element = (const pm_splat_node_t *) element;
7340 if (splat_element->expression) {
7341 PM_COMPILE_NOT_POPPED(splat_element->expression);
7342 }
7343 else {
7344 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
7345 PUSH_GETLOCAL(ret, *location, index.index, index.level);
7346 }
7347
7348 if (first_chunk) {
7349 // If this is the first element of the array then we
7350 // need to splatarray the elements into the list.
7351 PUSH_INSN1(ret, *location, splatarray, Qtrue);
7352 first_chunk = false;
7353 }
7354 else {
7355 PUSH_INSN(ret, *location, concattoarray);
7356 }
7357
7358 static_literal = false;
7359 }
7360 else if (PM_NODE_TYPE_P(element, PM_KEYWORD_HASH_NODE)) {
7361 if (new_array_size == 0 && first_chunk) {
7362 PUSH_INSN1(ret, *location, newarray, INT2FIX(0));
7363 first_chunk = false;
7364 }
7365 else {
7366 FLUSH_CHUNK;
7367 }
7368
7369 // If we get here, then this is the last element of the
7370 // array/arguments, because it cannot be followed by
7371 // anything else without a syntax error. This looks like:
7372 //
7373 // [foo, bar, baz: qux]
7374 // ^^^^^^^^
7375 //
7376 // [foo, bar, **baz]
7377 // ^^^^^
7378 //
7379 const pm_keyword_hash_node_t *keyword_hash = (const pm_keyword_hash_node_t *) element;
7380 pm_compile_hash_elements(iseq, element, &keyword_hash->elements, 0, Qundef, false, ret, scope_node);
7381
7382 // This boolean controls the manner in which we push the
7383 // hash onto the array. If it's all keyword splats, then we
7384 // can use the very specialized pushtoarraykwsplat
7385 // instruction to check if it's empty before we push it.
7386 size_t splats = 0;
7387 while (splats < keyword_hash->elements.size && PM_NODE_TYPE_P(keyword_hash->elements.nodes[splats], PM_ASSOC_SPLAT_NODE)) splats++;
7388
7389 if (keyword_hash->elements.size == splats) {
7390 PUSH_INSN(ret, *location, pushtoarraykwsplat);
7391 }
7392 else {
7393 new_array_size++;
7394 }
7395 }
7396 else if (
7397 PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL) &&
7398 !PM_CONTAINER_P(element) &&
7399 !static_literal &&
7400 ((index + min_tmp_array_size) < elements->size)
7401 ) {
7402 // If we have a static literal, then there's the potential
7403 // to group a bunch of them together with a literal array
7404 // and then concat them together.
7405 size_t right_index = index + 1;
7406 while (
7407 right_index < elements->size &&
7408 PM_NODE_FLAG_P(elements->nodes[right_index], PM_NODE_FLAG_STATIC_LITERAL) &&
7409 !PM_CONTAINER_P(elements->nodes[right_index])
7410 ) right_index++;
7411
7412 size_t tmp_array_size = right_index - index;
7413 if (tmp_array_size >= min_tmp_array_size) {
7414 VALUE tmp_array = rb_ary_hidden_new(tmp_array_size);
7415
7416 // Create the temporary array.
7417 for (; tmp_array_size; tmp_array_size--)
7418 rb_ary_push(tmp_array, pm_static_literal_value(iseq, elements->nodes[index++], scope_node));
7419
7420 index--; // about to be incremented by for loop
7421 RB_OBJ_SET_FROZEN_SHAREABLE(tmp_array);
7422
7423 // Emit the optimized code.
7424 FLUSH_CHUNK;
7425 if (first_chunk) {
7426 PUSH_INSN1(ret, *location, duparray, tmp_array);
7427 first_chunk = false;
7428 }
7429 else {
7430 PUSH_INSN1(ret, *location, putobject, tmp_array);
7431 PUSH_INSN(ret, *location, concattoarray);
7432 }
7433 }
7434 else {
7435 PM_COMPILE_NOT_POPPED(element);
7436 if (++new_array_size >= max_new_array_size) FLUSH_CHUNK;
7437 static_literal = true;
7438 }
7439 } else {
7440 PM_COMPILE_NOT_POPPED(element);
7441 if (++new_array_size >= max_new_array_size) FLUSH_CHUNK;
7442 static_literal = false;
7443 }
7444 }
7445
7446 FLUSH_CHUNK;
7447 if (popped) PUSH_INSN(ret, *location, pop);
7448
7449#undef FLUSH_CHUNK
7450}
7451
7452static inline void
7453pm_compile_break_node(rb_iseq_t *iseq, const pm_break_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
7454{
7455 unsigned long throw_flag = 0;
7456
7457 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
7458 /* while/until */
7459 LABEL *splabel = NEW_LABEL(0);
7460 PUSH_LABEL(ret, splabel);
7461 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
7462
7463 if (node->arguments != NULL) {
7464 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
7465 }
7466 else {
7467 PUSH_INSN(ret, *location, putnil);
7468 }
7469
7470 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
7471 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
7472 PUSH_ADJUST_RESTORE(ret, splabel);
7473 if (!popped) PUSH_INSN(ret, *location, putnil);
7474 }
7475 else {
7476 const rb_iseq_t *ip = iseq;
7477
7478 while (ip) {
7479 if (!ISEQ_COMPILE_DATA(ip)) {
7480 ip = 0;
7481 break;
7482 }
7483
7484 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
7485 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
7486 }
7487 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
7488 throw_flag = 0;
7489 }
7490 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
7491 COMPILE_ERROR(iseq, location->line, "Invalid break");
7492 return;
7493 }
7494 else {
7495 ip = ISEQ_BODY(ip)->parent_iseq;
7496 continue;
7497 }
7498
7499 /* escape from block */
7500 if (node->arguments != NULL) {
7501 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
7502 }
7503 else {
7504 PUSH_INSN(ret, *location, putnil);
7505 }
7506
7507 PUSH_INSN1(ret, *location, throw, INT2FIX(throw_flag | TAG_BREAK));
7508 if (popped) PUSH_INSN(ret, *location, pop);
7509
7510 return;
7511 }
7512
7513 COMPILE_ERROR(iseq, location->line, "Invalid break");
7514 }
7515}
7516
7517static inline void
7518pm_compile_call_node(rb_iseq_t *iseq, const pm_call_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
7519{
7520 ID method_id = pm_constant_id_lookup(scope_node, node->name);
7521
7522 const pm_location_t *message_loc = &node->message_loc;
7523 if (message_loc->length == 0) message_loc = &node->base.location;
7524
7525 const pm_node_location_t location = PM_LOCATION_START_LOCATION(message_loc, node->base.node_id);
7526 const char *builtin_func;
7527
7528 if (UNLIKELY(iseq_has_builtin_function_table(iseq)) && (builtin_func = pm_iseq_builtin_function_name(scope_node, node->receiver, method_id)) != NULL) {
7529 pm_compile_builtin_function_call(iseq, ret, scope_node, node, &location, popped, ISEQ_COMPILE_DATA(iseq)->current_block, builtin_func);
7530 return;
7531 }
7532
7533 LABEL *start = NEW_LABEL(location.line);
7534 if (node->block) PUSH_LABEL(ret, start);
7535
7536 switch (method_id) {
7537 case idUMinus: {
7538 if (pm_opt_str_freeze_p(iseq, node)) {
7539 VALUE value = parse_static_literal_string(iseq, scope_node, node->receiver, &((const pm_string_node_t * ) node->receiver)->unescaped);
7540 const struct rb_callinfo *callinfo = new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE);
7541 PUSH_INSN2(ret, location, opt_str_uminus, value, callinfo);
7542 if (popped) PUSH_INSN(ret, location, pop);
7543 return;
7544 }
7545 break;
7546 }
7547 case idFreeze: {
7548 if (pm_opt_str_freeze_p(iseq, node)) {
7549 VALUE value = parse_static_literal_string(iseq, scope_node, node->receiver, &((const pm_string_node_t * ) node->receiver)->unescaped);
7550 const struct rb_callinfo *callinfo = new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE);
7551 PUSH_INSN2(ret, location, opt_str_freeze, value, callinfo);
7552 if (popped) PUSH_INSN(ret, location, pop);
7553 return;
7554 }
7555 break;
7556 }
7557 }
7558
7559 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && !popped) {
7560 PUSH_INSN(ret, location, putnil);
7561 }
7562
7563 if (node->receiver == NULL) {
7564 PUSH_INSN(ret, location, putself);
7565 }
7566 else {
7567 if ((method_id == idCall || method_id == idAREF || method_id == idYield || method_id == idEqq) && PM_NODE_TYPE_P(node->receiver, PM_LOCAL_VARIABLE_READ_NODE)) {
7568 const pm_local_variable_read_node_t *read_node_cast = (const pm_local_variable_read_node_t *) node->receiver;
7569 uint32_t node_id = node->receiver->node_id;
7570 int idx, level;
7571
7572 if (iseq_block_param_id_p(iseq, pm_constant_id_lookup(scope_node, read_node_cast->name), &idx, &level)) {
7573 ADD_ELEM(ret, (LINK_ELEMENT *) new_insn_body(iseq, location.line, node_id, BIN(getblockparamproxy), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
7574 }
7575 else {
7576 PM_COMPILE_NOT_POPPED(node->receiver);
7577 }
7578 }
7579 else {
7580 PM_COMPILE_NOT_POPPED(node->receiver);
7581 }
7582 }
7583
7584 pm_compile_call(iseq, node, ret, popped, scope_node, method_id, start);
7585 return;
7586}
7587
7588static inline void
7589pm_compile_call_operator_write_node(rb_iseq_t *iseq, const pm_call_operator_write_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
7590{
7591 int flag = 0;
7592
7593 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) {
7594 flag = VM_CALL_FCALL;
7595 }
7596
7597 PM_COMPILE_NOT_POPPED(node->receiver);
7598
7599 LABEL *safe_label = NULL;
7600 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
7601 safe_label = NEW_LABEL(location->line);
7602 PUSH_INSN(ret, *location, dup);
7603 PUSH_INSNL(ret, *location, branchnil, safe_label);
7604 }
7605
7606 PUSH_INSN(ret, *location, dup);
7607
7608 ID id_read_name = pm_constant_id_lookup(scope_node, node->read_name);
7609 PUSH_SEND_WITH_FLAG(ret, *location, id_read_name, INT2FIX(0), INT2FIX(flag));
7610
7611 PM_COMPILE_NOT_POPPED(node->value);
7612 ID id_operator = pm_constant_id_lookup(scope_node, node->binary_operator);
7613 PUSH_SEND(ret, *location, id_operator, INT2FIX(1));
7614
7615 if (!popped) {
7616 PUSH_INSN(ret, *location, swap);
7617 PUSH_INSN1(ret, *location, topn, INT2FIX(1));
7618 }
7619
7620 ID id_write_name = pm_constant_id_lookup(scope_node, node->write_name);
7621 PUSH_SEND_WITH_FLAG(ret, *location, id_write_name, INT2FIX(1), INT2FIX(flag));
7622
7623 if (safe_label != NULL && popped) PUSH_LABEL(ret, safe_label);
7624 PUSH_INSN(ret, *location, pop);
7625 if (safe_label != NULL && !popped) PUSH_LABEL(ret, safe_label);
7626}
7627
7644static VALUE
7645pm_compile_case_node_dispatch(rb_iseq_t *iseq, VALUE dispatch, const pm_node_t *node, LABEL *label, pm_scope_node_t *scope_node)
7646{
7647 VALUE key = Qundef;
7648 switch (PM_NODE_TYPE(node)) {
7649 case PM_FLOAT_NODE: {
7650 key = pm_static_literal_value(iseq, node, scope_node);
7651 double intptr;
7652
7653 if (modf(RFLOAT_VALUE(key), &intptr) == 0.0) {
7654 key = (FIXABLE(intptr) ? LONG2FIX((long) intptr) : rb_dbl2big(intptr));
7655 }
7656
7657 break;
7658 }
7659 case PM_FALSE_NODE:
7660 case PM_INTEGER_NODE:
7661 case PM_NIL_NODE:
7662 case PM_SOURCE_LINE_NODE:
7663 case PM_SYMBOL_NODE:
7664 case PM_TRUE_NODE:
7665 key = pm_static_literal_value(iseq, node, scope_node);
7666 break;
7667 case PM_STRING_NODE: {
7668 const pm_string_node_t *cast = (const pm_string_node_t *) node;
7669 key = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
7670 break;
7671 }
7672 default:
7673 return Qundef;
7674 }
7675
7676 cdhash_aset_if_missing(dispatch, key, (VALUE)label);
7677 return dispatch;
7678}
7679
7683static inline void
7684pm_compile_case_node(rb_iseq_t *iseq, const pm_case_node_t *cast, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
7685{
7686 const pm_node_location_t location = *node_location;
7687 const pm_node_list_t *conditions = &cast->conditions;
7688
7689 // This is the anchor that we will compile the conditions of the various
7690 // `when` nodes into. If a match is found, they will need to jump into
7691 // the body_seq anchor to the correct spot.
7692 DECL_ANCHOR(cond_seq);
7693
7694 // This is the anchor that we will compile the bodies of the various
7695 // `when` nodes into. We'll make sure that the clauses that are compiled
7696 // jump into the correct spots within this anchor.
7697 DECL_ANCHOR(body_seq);
7698
7699 // This is the label where all of the when clauses will jump to if they
7700 // have matched and are done executing their bodies.
7701 LABEL *end_label = NEW_LABEL(location.line);
7702
7703 // If we have a predicate on this case statement, then it's going to
7704 // compare all of the various when clauses to the predicate. If we
7705 // don't, then it's basically an if-elsif-else chain.
7706 if (cast->predicate == NULL) {
7707 // Establish branch coverage for the case node.
7708 VALUE branches = Qfalse;
7709 rb_code_location_t case_location = { 0 };
7710 int branch_id = 0;
7711
7712 if (PM_BRANCH_COVERAGE_P(iseq)) {
7713 case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
7714 branches = decl_branch_base(iseq, (int) cast->base.node_id, &case_location, "case");
7715 }
7716
7717 // Loop through each clauses in the case node and compile each of
7718 // the conditions within them into cond_seq. If they match, they
7719 // should jump into their respective bodies in body_seq.
7720 for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) {
7721 const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index];
7722 const pm_node_list_t *conditions = &clause->conditions;
7723
7724 int clause_lineno = pm_node_line_number_cached((const pm_node_t *) clause, scope_node);
7725 LABEL *label = NEW_LABEL(clause_lineno);
7726 PUSH_LABEL(body_seq, label);
7727
7728 // Establish branch coverage for the when clause.
7729 if (PM_BRANCH_COVERAGE_P(iseq)) {
7730 rb_code_location_t branch_location = pm_code_location(scope_node, clause->statements != NULL ? ((const pm_node_t *) clause->statements) : ((const pm_node_t *) clause));
7731 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches);
7732 }
7733
7734 if (clause->statements != NULL) {
7735 pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node);
7736 }
7737 else if (!popped) {
7738 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
7739 }
7740
7741 PUSH_INSNL(body_seq, location, jump, end_label);
7742
7743 // Compile each of the conditions for the when clause into the
7744 // cond_seq. Each one should have a unique condition and should
7745 // jump to the subsequent one if it doesn't match.
7746 for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) {
7747 const pm_node_t *condition = conditions->nodes[condition_index];
7748
7749 if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) {
7750 pm_node_location_t cond_location = PM_NODE_START_LOCATION(condition);
7751 PUSH_INSN(cond_seq, cond_location, putnil);
7752 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7753 PUSH_INSN1(cond_seq, cond_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
7754 PUSH_INSNL(cond_seq, cond_location, branchif, label);
7755 }
7756 else {
7757 LABEL *next_label = NEW_LABEL(pm_node_line_number_cached(condition, scope_node));
7758 pm_compile_branch_condition(iseq, cond_seq, condition, label, next_label, scope_node);
7759 PUSH_LABEL(cond_seq, next_label);
7760 }
7761 }
7762 }
7763
7764 // Establish branch coverage for the else clause (implicit or
7765 // explicit).
7766 if (PM_BRANCH_COVERAGE_P(iseq)) {
7767 rb_code_location_t branch_location;
7768
7769 if (cast->else_clause == NULL) {
7770 branch_location = case_location;
7771 } else if (cast->else_clause->statements == NULL) {
7772 branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->else_clause);
7773 } else {
7774 branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->else_clause->statements);
7775 }
7776
7777 add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
7778 }
7779
7780 // Compile the else clause if there is one.
7781 if (cast->else_clause != NULL) {
7782 pm_compile_node(iseq, (const pm_node_t *) cast->else_clause, cond_seq, popped, scope_node);
7783 }
7784 else if (!popped) {
7785 PUSH_SYNTHETIC_PUTNIL(cond_seq, iseq);
7786 }
7787
7788 // Finally, jump to the end label if none of the other conditions
7789 // have matched.
7790 PUSH_INSNL(cond_seq, location, jump, end_label);
7791 PUSH_SEQ(ret, cond_seq);
7792 }
7793 else {
7794 // Establish branch coverage for the case node.
7795 VALUE branches = Qfalse;
7796 rb_code_location_t case_location = { 0 };
7797 int branch_id = 0;
7798
7799 if (PM_BRANCH_COVERAGE_P(iseq)) {
7800 case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
7801 branches = decl_branch_base(iseq, (int) cast->base.node_id, &case_location, "case");
7802 }
7803
7804 // This is the label where everything will fall into if none of the
7805 // conditions matched.
7806 LABEL *else_label = NEW_LABEL(location.line);
7807
7808 // It's possible for us to speed up the case node by using a
7809 // dispatch hash. This is a hash that maps the conditions of the
7810 // various when clauses to the labels of their bodies. If we can
7811 // compile the conditions into a hash key, then we can use a hash
7812 // lookup to jump directly to the correct when clause body.
7813 VALUE dispatch = Qundef;
7814 if (ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
7815 dispatch = cdhash_new(0);
7816 }
7817
7818 // We're going to loop through each of the conditions in the case
7819 // node and compile each of their contents into both the cond_seq
7820 // and the body_seq. Each condition will use its own label to jump
7821 // from its conditions into its body.
7822 //
7823 // Note that none of the code in the loop below should be adding
7824 // anything to ret, as we're going to be laying out the entire case
7825 // node instructions later.
7826 for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) {
7827 const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index];
7828 pm_node_location_t clause_location = PM_NODE_START_LOCATION((const pm_node_t *) clause);
7829
7830 const pm_node_list_t *conditions = &clause->conditions;
7831 LABEL *label = NEW_LABEL(clause_location.line);
7832
7833 // Compile each of the conditions for the when clause into the
7834 // cond_seq. Each one should have a unique comparison that then
7835 // jumps into the body if it matches.
7836 for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) {
7837 const pm_node_t *condition = conditions->nodes[condition_index];
7838 const pm_node_location_t condition_location = PM_NODE_START_LOCATION(condition);
7839
7840 // If we haven't already abandoned the optimization, then
7841 // we're going to try to compile the condition into the
7842 // dispatch hash.
7843 if (dispatch != Qundef) {
7844 dispatch = pm_compile_case_node_dispatch(iseq, dispatch, condition, label, scope_node);
7845 }
7846
7847 if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) {
7848 PUSH_INSN(cond_seq, condition_location, dup);
7849 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7850 PUSH_INSN1(cond_seq, condition_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
7851 }
7852 else {
7853 if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
7854 const pm_string_node_t *string = (const pm_string_node_t *) condition;
7855 VALUE value = parse_static_literal_string(iseq, scope_node, condition, &string->unescaped);
7856 PUSH_INSN1(cond_seq, condition_location, putobject, value);
7857 }
7858 else {
7859 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7860 }
7861
7862 PUSH_INSN1(cond_seq, condition_location, topn, INT2FIX(1));
7863 PUSH_SEND_WITH_FLAG(cond_seq, condition_location, idEqq, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
7864 }
7865
7866 PUSH_INSNL(cond_seq, condition_location, branchif, label);
7867 }
7868
7869 // Now, add the label to the body and compile the body of the
7870 // when clause. This involves popping the predicate, compiling
7871 // the statements to be executed, and then compiling a jump to
7872 // the end of the case node.
7873 PUSH_LABEL(body_seq, label);
7874 PUSH_INSN(body_seq, clause_location, pop);
7875
7876 // Establish branch coverage for the when clause.
7877 if (PM_BRANCH_COVERAGE_P(iseq)) {
7878 rb_code_location_t branch_location = pm_code_location(scope_node, clause->statements != NULL ? ((const pm_node_t *) clause->statements) : ((const pm_node_t *) clause));
7879 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches);
7880 }
7881
7882 if (clause->statements != NULL) {
7883 pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node);
7884 }
7885 else if (!popped) {
7886 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
7887 }
7888
7889 PUSH_INSNL(body_seq, clause_location, jump, end_label);
7890 }
7891
7892 // Now that we have compiled the conditions and the bodies of the
7893 // various when clauses, we can compile the predicate, lay out the
7894 // conditions, compile the fallback subsequent if there is one, and
7895 // finally put in the bodies of the when clauses.
7896 PM_COMPILE_NOT_POPPED(cast->predicate);
7897
7898 // If we have a dispatch hash, then we'll use it here to create the
7899 // optimization.
7900 if (dispatch != Qundef) {
7901 PUSH_INSN(ret, location, dup);
7902 RB_OBJ_SET_SHAREABLE(dispatch); // it is special that the hash is shareable but not frozen, because compile.c modify them. This Hahs instance is not accessible so it is safe to leave it.
7903 PUSH_INSN2(ret, location, opt_case_dispatch, dispatch, else_label);
7904 LABEL_REF(else_label);
7905 }
7906
7907 PUSH_SEQ(ret, cond_seq);
7908
7909 // Compile either the explicit else clause or an implicit else
7910 // clause.
7911 PUSH_LABEL(ret, else_label);
7912
7913 if (cast->else_clause != NULL) {
7914 pm_node_location_t else_location = PM_NODE_START_LOCATION(cast->else_clause->statements != NULL ? ((const pm_node_t *) cast->else_clause->statements) : ((const pm_node_t *) cast->else_clause));
7915 PUSH_INSN(ret, else_location, pop);
7916
7917 // Establish branch coverage for the else clause.
7918 if (PM_BRANCH_COVERAGE_P(iseq)) {
7919 rb_code_location_t branch_location = pm_code_location(scope_node, cast->else_clause->statements != NULL ? ((const pm_node_t *) cast->else_clause->statements) : ((const pm_node_t *) cast->else_clause));
7920 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
7921 }
7922
7923 PM_COMPILE((const pm_node_t *) cast->else_clause);
7924 PUSH_INSNL(ret, else_location, jump, end_label);
7925 }
7926 else {
7927 PUSH_INSN(ret, location, pop);
7928
7929 // Establish branch coverage for the implicit else clause.
7930 if (PM_BRANCH_COVERAGE_P(iseq)) {
7931 add_trace_branch_coverage(iseq, ret, &case_location, case_location.beg_pos.column, branch_id, "else", branches);
7932 }
7933
7934 if (!popped) PUSH_INSN(ret, location, putnil);
7935 PUSH_INSNL(ret, location, jump, end_label);
7936 }
7937 }
7938
7939 PUSH_SEQ(ret, body_seq);
7940 PUSH_LABEL(ret, end_label);
7941}
7942
7943static inline void
7944pm_compile_case_match_node(rb_iseq_t *iseq, const pm_case_match_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
7945{
7946 // This is the anchor that we will compile the bodies of the various
7947 // `in` nodes into. We'll make sure that the patterns that are compiled
7948 // jump into the correct spots within this anchor.
7949 DECL_ANCHOR(body_seq);
7950
7951 // This is the anchor that we will compile the patterns of the various
7952 // `in` nodes into. If a match is found, they will need to jump into the
7953 // body_seq anchor to the correct spot.
7954 DECL_ANCHOR(cond_seq);
7955
7956 // This label is used to indicate the end of the entire node. It is
7957 // jumped to after the entire stack is cleaned up.
7958 LABEL *end_label = NEW_LABEL(location->line);
7959
7960 // This label is used as the fallback for the case match. If no match is
7961 // found, then we jump to this label. This is either an `else` clause or
7962 // an error handler.
7963 LABEL *else_label = NEW_LABEL(location->line);
7964
7965 // We're going to use this to uniquely identify each branch so that we
7966 // can track coverage information.
7967 rb_code_location_t case_location = { 0 };
7968 VALUE branches = Qfalse;
7969 int branch_id = 0;
7970
7971 if (PM_BRANCH_COVERAGE_P(iseq)) {
7972 case_location = pm_code_location(scope_node, (const pm_node_t *) node);
7973 branches = decl_branch_base(iseq, (int) node->base.node_id, &case_location, "case");
7974 }
7975
7976 // If there is only one pattern, then the behavior changes a bit. It
7977 // effectively gets treated as a match required node (this is how it is
7978 // represented in the other parser).
7979 bool in_single_pattern = node->else_clause == NULL && node->conditions.size == 1;
7980
7981 // First, we're going to push a bunch of stuff onto the stack that is
7982 // going to serve as our scratch space.
7983 if (in_single_pattern) {
7984 PUSH_INSN(ret, *location, putnil); // key error key
7985 PUSH_INSN(ret, *location, putnil); // key error matchee
7986 PUSH_INSN1(ret, *location, putobject, Qfalse); // key error?
7987 PUSH_INSN(ret, *location, putnil); // error string
7988 }
7989
7990 // Now we're going to compile the value to match against.
7991 PUSH_INSN(ret, *location, putnil); // deconstruct cache
7992 PM_COMPILE_NOT_POPPED(node->predicate);
7993
7994 // Next, we'll loop through every in clause and compile its body into
7995 // the body_seq anchor and its pattern into the cond_seq anchor. We'll
7996 // make sure the pattern knows how to jump correctly into the body if it
7997 // finds a match.
7998 for (size_t index = 0; index < node->conditions.size; index++) {
7999 const pm_node_t *condition = node->conditions.nodes[index];
8000 RUBY_ASSERT(PM_NODE_TYPE_P(condition, PM_IN_NODE));
8001
8002 const pm_in_node_t *in_node = (const pm_in_node_t *) condition;
8003 const pm_node_location_t in_location = PM_NODE_START_LOCATION(in_node);
8004 const pm_node_location_t pattern_location = PM_NODE_START_LOCATION(in_node->pattern);
8005
8006 if (branch_id) {
8007 PUSH_INSN(body_seq, in_location, putnil);
8008 }
8009
8010 LABEL *body_label = NEW_LABEL(in_location.line);
8011 PUSH_LABEL(body_seq, body_label);
8012 PUSH_INSN1(body_seq, in_location, adjuststack, INT2FIX(in_single_pattern ? 6 : 2));
8013
8014 // Establish branch coverage for the in clause.
8015 if (PM_BRANCH_COVERAGE_P(iseq)) {
8016 rb_code_location_t branch_location = pm_code_location(scope_node, in_node->statements != NULL ? ((const pm_node_t *) in_node->statements) : ((const pm_node_t *) in_node));
8017 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "in", branches);
8018 }
8019
8020 if (in_node->statements != NULL) {
8021 PM_COMPILE_INTO_ANCHOR(body_seq, (const pm_node_t *) in_node->statements);
8022 }
8023 else if (!popped) {
8024 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
8025 }
8026
8027 PUSH_INSNL(body_seq, in_location, jump, end_label);
8028 LABEL *next_pattern_label = NEW_LABEL(pattern_location.line);
8029
8030 PUSH_INSN(cond_seq, pattern_location, dup);
8031 pm_compile_pattern(iseq, scope_node, in_node->pattern, cond_seq, body_label, next_pattern_label, in_single_pattern, true, 2);
8032 PUSH_LABEL(cond_seq, next_pattern_label);
8033 LABEL_UNREMOVABLE(next_pattern_label);
8034 }
8035
8036 if (node->else_clause != NULL) {
8037 // If we have an `else` clause, then this becomes our fallback (and
8038 // there is no need to compile in code to potentially raise an
8039 // error).
8040 const pm_else_node_t *else_node = node->else_clause;
8041
8042 PUSH_LABEL(cond_seq, else_label);
8043 PUSH_INSN(cond_seq, *location, pop);
8044 PUSH_INSN(cond_seq, *location, pop);
8045
8046 // Establish branch coverage for the else clause.
8047 if (PM_BRANCH_COVERAGE_P(iseq)) {
8048 rb_code_location_t branch_location = pm_code_location(scope_node, else_node->statements != NULL ? ((const pm_node_t *) else_node->statements) : ((const pm_node_t *) else_node));
8049 add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
8050 }
8051
8052 PM_COMPILE_INTO_ANCHOR(cond_seq, (const pm_node_t *) else_node);
8053 PUSH_INSNL(cond_seq, *location, jump, end_label);
8054 PUSH_INSN(cond_seq, *location, putnil);
8055 if (popped) PUSH_INSN(cond_seq, *location, putnil);
8056 }
8057 else {
8058 // Otherwise, if we do not have an `else` clause, we will compile in
8059 // the code to handle raising an appropriate error.
8060 PUSH_LABEL(cond_seq, else_label);
8061
8062 // Establish branch coverage for the implicit else clause.
8063 add_trace_branch_coverage(iseq, cond_seq, &case_location, case_location.beg_pos.column, branch_id, "else", branches);
8064
8065 if (in_single_pattern) {
8066 pm_compile_pattern_error_handler(iseq, scope_node, (const pm_node_t *) node, cond_seq, end_label, popped);
8067 }
8068 else {
8069 PUSH_INSN1(cond_seq, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8070 PUSH_INSN1(cond_seq, *location, putobject, rb_eNoMatchingPatternError);
8071 PUSH_INSN1(cond_seq, *location, topn, INT2FIX(2));
8072 PUSH_SEND(cond_seq, *location, id_core_raise, INT2FIX(2));
8073
8074 PUSH_INSN1(cond_seq, *location, adjuststack, INT2FIX(3));
8075 if (!popped) PUSH_INSN(cond_seq, *location, putnil);
8076 PUSH_INSNL(cond_seq, *location, jump, end_label);
8077 PUSH_INSN1(cond_seq, *location, dupn, INT2FIX(1));
8078 if (popped) PUSH_INSN(cond_seq, *location, putnil);
8079 }
8080 }
8081
8082 // At the end of all of this compilation, we will add the code for the
8083 // conditions first, then the various bodies, then mark the end of the
8084 // entire sequence with the end label.
8085 PUSH_SEQ(ret, cond_seq);
8086 PUSH_SEQ(ret, body_seq);
8087 PUSH_LABEL(ret, end_label);
8088}
8089
8090static inline void
8091pm_compile_forwarding_super_node(rb_iseq_t *iseq, const pm_forwarding_super_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8092{
8093 const rb_iseq_t *block = NULL;
8094 const rb_iseq_t *previous_block = NULL;
8095 LABEL *retry_label = NULL;
8096 LABEL *retry_end_l = NULL;
8097
8098 if (node->block != NULL) {
8099 previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
8100 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
8101
8102 retry_label = NEW_LABEL(location->line);
8103 retry_end_l = NEW_LABEL(location->line);
8104
8105 PUSH_LABEL(ret, retry_label);
8106 }
8107 else {
8108 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
8109 }
8110
8111 PUSH_INSN(ret, *location, putself);
8112 int flag = VM_CALL_ZSUPER | VM_CALL_SUPER | VM_CALL_FCALL;
8113
8114 if (node->block != NULL) {
8115 pm_scope_node_t next_scope_node;
8116 pm_scope_node_init((const pm_node_t *) node->block, &next_scope_node, scope_node);
8117
8118 ISEQ_COMPILE_DATA(iseq)->current_block = block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location->line);
8119 pm_scope_node_destroy(&next_scope_node);
8120 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) block);
8121 }
8122
8123 DECL_ANCHOR(args);
8124
8125 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
8126 const rb_iseq_t *local_iseq = body->local_iseq;
8127 const struct rb_iseq_constant_body *const local_body = ISEQ_BODY(local_iseq);
8128
8129 int argc = 0;
8130 int depth = get_lvar_level(iseq);
8131
8132 if (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
8133 flag |= VM_CALL_FORWARDING;
8134 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_DOT3, 0);
8135 PUSH_GETLOCAL(ret, *location, mult_local.index, mult_local.level);
8136
8137 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, 0, flag, NULL, block != NULL);
8138 PUSH_INSN2(ret, *location, invokesuperforward, callinfo, block);
8139
8140 if (popped) PUSH_INSN(ret, *location, pop);
8141 if (node->block) {
8142 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
8143 }
8144 return;
8145 }
8146
8147 if (local_body->param.flags.has_lead) {
8148 /* required arguments */
8149 for (int i = 0; i < local_body->param.lead_num; i++) {
8150 int idx = local_body->local_table_size - i;
8151 PUSH_GETLOCAL(args, *location, idx, depth);
8152 }
8153 argc += local_body->param.lead_num;
8154 }
8155
8156 if (local_body->param.flags.has_opt) {
8157 /* optional arguments */
8158 for (int j = 0; j < local_body->param.opt_num; j++) {
8159 int idx = local_body->local_table_size - (argc + j);
8160 PUSH_GETLOCAL(args, *location, idx, depth);
8161 }
8162 argc += local_body->param.opt_num;
8163 }
8164
8165 if (local_body->param.flags.has_rest) {
8166 /* rest argument */
8167 int idx = local_body->local_table_size - local_body->param.rest_start;
8168 PUSH_GETLOCAL(args, *location, idx, depth);
8169 PUSH_INSN1(args, *location, splatarray, Qfalse);
8170
8171 argc = local_body->param.rest_start + 1;
8172 flag |= VM_CALL_ARGS_SPLAT;
8173 }
8174
8175 if (local_body->param.flags.has_post) {
8176 /* post arguments */
8177 int post_len = local_body->param.post_num;
8178 int post_start = local_body->param.post_start;
8179
8180 int j = 0;
8181 for (; j < post_len; j++) {
8182 int idx = local_body->local_table_size - (post_start + j);
8183 PUSH_GETLOCAL(args, *location, idx, depth);
8184 }
8185
8186 if (local_body->param.flags.has_rest) {
8187 // argc remains unchanged from rest branch
8188 PUSH_INSN1(args, *location, newarray, INT2FIX(j));
8189 PUSH_INSN(args, *location, concatarray);
8190 }
8191 else {
8192 argc = post_len + post_start;
8193 }
8194 }
8195
8196 const struct rb_iseq_param_keyword *const local_keyword = local_body->param.keyword;
8197 if (local_body->param.flags.has_kw) {
8198 int local_size = local_body->local_table_size;
8199 argc++;
8200
8201 PUSH_INSN1(args, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8202
8203 if (local_body->param.flags.has_kwrest) {
8204 int idx = local_body->local_table_size - local_keyword->rest_start;
8205 PUSH_GETLOCAL(args, *location, idx, depth);
8206 RUBY_ASSERT(local_keyword->num > 0);
8207 PUSH_SEND(args, *location, rb_intern("dup"), INT2FIX(0));
8208 }
8209 else {
8210 PUSH_INSN1(args, *location, newhash, INT2FIX(0));
8211 }
8212 int i = 0;
8213 for (; i < local_keyword->num; ++i) {
8214 ID id = local_keyword->table[i];
8215 int idx = local_size - get_local_var_idx(local_iseq, id);
8216
8217 {
8218 VALUE operand = ID2SYM(id);
8219 PUSH_INSN1(args, *location, putobject, operand);
8220 }
8221
8222 PUSH_GETLOCAL(args, *location, idx, depth);
8223 }
8224
8225 PUSH_SEND(args, *location, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
8226 flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT;
8227 }
8228 else if (local_body->param.flags.has_kwrest) {
8229 int idx = local_body->local_table_size - local_keyword->rest_start;
8230 PUSH_GETLOCAL(args, *location, idx, depth);
8231 argc++;
8232 flag |= VM_CALL_KW_SPLAT;
8233 }
8234
8235 PUSH_SEQ(ret, args);
8236
8237 {
8238 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flag, NULL, block != NULL);
8239 PUSH_INSN2(ret, *location, invokesuper, callinfo, block);
8240 }
8241
8242 if (node->block != NULL) {
8243 pm_compile_retry_end_label(iseq, ret, retry_end_l);
8244 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, block, retry_end_l);
8245 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
8246 }
8247
8248 if (popped) PUSH_INSN(ret, *location, pop);
8249}
8250
8251static inline void
8252pm_compile_match_required_node(rb_iseq_t *iseq, const pm_match_required_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8253{
8254 LABEL *matched_label = NEW_LABEL(location->line);
8255 LABEL *unmatched_label = NEW_LABEL(location->line);
8256 LABEL *done_label = NEW_LABEL(location->line);
8257
8258 // First, we're going to push a bunch of stuff onto the stack that is
8259 // going to serve as our scratch space.
8260 PUSH_INSN(ret, *location, putnil); // key error key
8261 PUSH_INSN(ret, *location, putnil); // key error matchee
8262 PUSH_INSN1(ret, *location, putobject, Qfalse); // key error?
8263 PUSH_INSN(ret, *location, putnil); // error string
8264 PUSH_INSN(ret, *location, putnil); // deconstruct cache
8265
8266 // Next we're going to compile the value expression such that it's on
8267 // the stack.
8268 PM_COMPILE_NOT_POPPED(node->value);
8269
8270 // Here we'll dup it so that it can be used for comparison, but also be
8271 // used for error handling.
8272 PUSH_INSN(ret, *location, dup);
8273
8274 // Next we'll compile the pattern. We indicate to the pm_compile_pattern
8275 // function that this is the only pattern that will be matched against
8276 // through the in_single_pattern parameter. We also indicate that the
8277 // value to compare against is 2 slots from the top of the stack (the
8278 // base_index parameter).
8279 pm_compile_pattern(iseq, scope_node, node->pattern, ret, matched_label, unmatched_label, true, true, 2);
8280
8281 // If the pattern did not match the value, then we're going to compile
8282 // in our error handler code. This will determine which error to raise
8283 // and raise it.
8284 PUSH_LABEL(ret, unmatched_label);
8285 pm_compile_pattern_error_handler(iseq, scope_node, (const pm_node_t *) node, ret, done_label, popped);
8286
8287 // If the pattern did match, we'll clean up the values we've pushed onto
8288 // the stack and then push nil onto the stack if it's not popped.
8289 PUSH_LABEL(ret, matched_label);
8290 PUSH_INSN1(ret, *location, adjuststack, INT2FIX(6));
8291 if (!popped) PUSH_INSN(ret, *location, putnil);
8292 PUSH_INSNL(ret, *location, jump, done_label);
8293
8294 PUSH_LABEL(ret, done_label);
8295}
8296
8297static inline void
8298pm_compile_match_write_node(rb_iseq_t *iseq, const pm_match_write_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8299{
8300 LABEL *fail_label = NEW_LABEL(location->line);
8301 LABEL *end_label = NEW_LABEL(location->line);
8302
8303 // First, we'll compile the call so that all of its instructions are
8304 // present. Then we'll compile all of the local variable targets.
8305 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->call);
8306
8307 // Now, check if the match was successful. If it was, then we'll
8308 // continue on and assign local variables. Otherwise we'll skip over the
8309 // assignment code.
8310 {
8311 VALUE operand = rb_id2sym(idBACKREF);
8312 PUSH_INSN1(ret, *location, getglobal, operand);
8313 }
8314
8315 PUSH_INSN(ret, *location, dup);
8316 PUSH_INSNL(ret, *location, branchunless, fail_label);
8317
8318 // If there's only a single local variable target, we can skip some of
8319 // the bookkeeping, so we'll put a special branch here.
8320 size_t targets_count = node->targets.size;
8321
8322 if (targets_count == 1) {
8323 const pm_node_t *target = node->targets.nodes[0];
8324 RUBY_ASSERT(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
8325
8326 const pm_local_variable_target_node_t *local_target = (const pm_local_variable_target_node_t *) target;
8327 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, local_target->name, local_target->depth);
8328
8329 {
8330 VALUE operand = rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name));
8331 PUSH_INSN1(ret, *location, putobject, operand);
8332 }
8333
8334 PUSH_SEND(ret, *location, idAREF, INT2FIX(1));
8335 PUSH_LABEL(ret, fail_label);
8336 PUSH_SETLOCAL(ret, *location, index.index, index.level);
8337 if (popped) PUSH_INSN(ret, *location, pop);
8338 return;
8339 }
8340
8341 DECL_ANCHOR(fail_anchor);
8342
8343 // Otherwise there is more than one local variable target, so we'll need
8344 // to do some bookkeeping.
8345 for (size_t targets_index = 0; targets_index < targets_count; targets_index++) {
8346 const pm_node_t *target = node->targets.nodes[targets_index];
8347 RUBY_ASSERT(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
8348
8349 const pm_local_variable_target_node_t *local_target = (const pm_local_variable_target_node_t *) target;
8350 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, local_target->name, local_target->depth);
8351
8352 if (((size_t) targets_index) < (targets_count - 1)) {
8353 PUSH_INSN(ret, *location, dup);
8354 }
8355
8356 {
8357 VALUE operand = rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name));
8358 PUSH_INSN1(ret, *location, putobject, operand);
8359 }
8360
8361 PUSH_SEND(ret, *location, idAREF, INT2FIX(1));
8362 PUSH_SETLOCAL(ret, *location, index.index, index.level);
8363
8364 PUSH_INSN(fail_anchor, *location, putnil);
8365 PUSH_SETLOCAL(fail_anchor, *location, index.index, index.level);
8366 }
8367
8368 // Since we matched successfully, now we'll jump to the end.
8369 PUSH_INSNL(ret, *location, jump, end_label);
8370
8371 // In the case that the match failed, we'll loop through each local
8372 // variable target and set all of them to `nil`.
8373 PUSH_LABEL(ret, fail_label);
8374 PUSH_INSN(ret, *location, pop);
8375 PUSH_SEQ(ret, fail_anchor);
8376
8377 // Finally, we can push the end label for either case.
8378 PUSH_LABEL(ret, end_label);
8379 if (popped) PUSH_INSN(ret, *location, pop);
8380}
8381
8382static inline void
8383pm_compile_next_node(rb_iseq_t *iseq, const pm_next_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8384{
8385 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
8386 LABEL *splabel = NEW_LABEL(0);
8387 PUSH_LABEL(ret, splabel);
8388
8389 if (node->arguments) {
8390 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8391 }
8392 else {
8393 PUSH_INSN(ret, *location, putnil);
8394 }
8395 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8396
8397 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
8398 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8399
8400 PUSH_ADJUST_RESTORE(ret, splabel);
8401 if (!popped) PUSH_INSN(ret, *location, putnil);
8402 }
8403 else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
8404 LABEL *splabel = NEW_LABEL(0);
8405
8406 PUSH_LABEL(ret, splabel);
8407 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->start_label);
8408
8409 if (node->arguments != NULL) {
8410 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8411 }
8412 else {
8413 PUSH_INSN(ret, *location, putnil);
8414 }
8415
8416 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8417 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
8418 PUSH_ADJUST_RESTORE(ret, splabel);
8419 splabel->unremovable = FALSE;
8420
8421 if (!popped) PUSH_INSN(ret, *location, putnil);
8422 }
8423 else {
8424 const rb_iseq_t *ip = iseq;
8425 unsigned long throw_flag = 0;
8426
8427 while (ip) {
8428 if (!ISEQ_COMPILE_DATA(ip)) {
8429 ip = 0;
8430 break;
8431 }
8432
8433 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
8434 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8435 /* while loop */
8436 break;
8437 }
8438 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8439 break;
8440 }
8441 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8442 COMPILE_ERROR(iseq, location->line, "Invalid next");
8443 return;
8444 }
8445
8446 ip = ISEQ_BODY(ip)->parent_iseq;
8447 }
8448
8449 if (ip != 0) {
8450 if (node->arguments) {
8451 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8452 }
8453 else {
8454 PUSH_INSN(ret, *location, putnil);
8455 }
8456
8457 PUSH_INSN1(ret, *location, throw, INT2FIX(throw_flag | TAG_NEXT));
8458 if (popped) PUSH_INSN(ret, *location, pop);
8459 }
8460 else {
8461 COMPILE_ERROR(iseq, location->line, "Invalid next");
8462 }
8463 }
8464}
8465
8466static void
8467pm_compile_or_node(rb_iseq_t *iseq, const pm_or_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8468{
8469 const pm_node_t *cursor = (const pm_node_t *) node;
8470 size_t size = 1;
8471
8472 while (PM_NODE_TYPE_P(cursor, PM_OR_NODE)) {
8473 cursor = ((const pm_or_node_t *) cursor)->left;
8474 size += 2;
8475 }
8476
8477 VALUE handle = 0;
8478 const pm_node_t **nodes = ALLOCV_N(const pm_node_t *, handle, size);
8479
8480 cursor = (const pm_node_t *) node;
8481 size_t index = size;
8482
8483 while (PM_NODE_TYPE_P(cursor, PM_OR_NODE)) {
8484 const pm_or_node_t *cast = (const pm_or_node_t *) cursor;
8485 nodes[--index] = cast->right;
8486 nodes[--index] = cursor;
8487 cursor = cast->left;
8488 }
8489
8490 nodes[0] = cursor;
8491 pm_compile_logical_chain(iseq, size, nodes, location, ret, popped, scope_node);
8492 ALLOCV_END(handle);
8493}
8494
8495static inline void
8496pm_compile_redo_node(rb_iseq_t *iseq, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8497{
8498 if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
8499 LABEL *splabel = NEW_LABEL(0);
8500
8501 PUSH_LABEL(ret, splabel);
8502 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
8503 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8504
8505 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
8506 PUSH_ADJUST_RESTORE(ret, splabel);
8507 if (!popped) PUSH_INSN(ret, *location, putnil);
8508 }
8509 else if (ISEQ_BODY(iseq)->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
8510 LABEL *splabel = NEW_LABEL(0);
8511
8512 PUSH_LABEL(ret, splabel);
8513 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8514 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->start_label);
8515
8516 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8517 PUSH_ADJUST_RESTORE(ret, splabel);
8518 if (!popped) PUSH_INSN(ret, *location, putnil);
8519 }
8520 else {
8521 const rb_iseq_t *ip = iseq;
8522
8523 while (ip) {
8524 if (!ISEQ_COMPILE_DATA(ip)) {
8525 ip = 0;
8526 break;
8527 }
8528
8529 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8530 break;
8531 }
8532 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8533 break;
8534 }
8535 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8536 COMPILE_ERROR(iseq, location->line, "Invalid redo");
8537 return;
8538 }
8539
8540 ip = ISEQ_BODY(ip)->parent_iseq;
8541 }
8542
8543 if (ip != 0) {
8544 PUSH_INSN(ret, *location, putnil);
8545 PUSH_INSN1(ret, *location, throw, INT2FIX(VM_THROW_NO_ESCAPE_FLAG | TAG_REDO));
8546 if (popped) PUSH_INSN(ret, *location, pop);
8547 }
8548 else {
8549 COMPILE_ERROR(iseq, location->line, "Invalid redo");
8550 }
8551 }
8552}
8553
8554static inline void
8555pm_compile_rescue_node(rb_iseq_t *iseq, const pm_rescue_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8556{
8557 iseq_set_exception_local_table(iseq);
8558
8559 // First, establish the labels that we need to be able to jump to within
8560 // this compilation block.
8561 LABEL *exception_match_label = NEW_LABEL(location->line);
8562 LABEL *rescue_end_label = NEW_LABEL(location->line);
8563
8564 // Next, compile each of the exceptions that we're going to be
8565 // handling. For each one, we'll add instructions to check if the
8566 // exception matches the raised one, and if it does then jump to the
8567 // exception_match_label label. Otherwise it will fall through to the
8568 // subsequent check. If there are no exceptions, we'll only check
8569 // StandardError.
8570 const pm_node_list_t *exceptions = &node->exceptions;
8571
8572 if (exceptions->size > 0) {
8573 for (size_t index = 0; index < exceptions->size; index++) {
8574 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8575 PM_COMPILE(exceptions->nodes[index]);
8576 int checkmatch_flags = VM_CHECKMATCH_TYPE_RESCUE;
8577 if (PM_NODE_TYPE_P(exceptions->nodes[index], PM_SPLAT_NODE)) {
8578 checkmatch_flags |= VM_CHECKMATCH_ARRAY;
8579 }
8580 PUSH_INSN1(ret, *location, checkmatch, INT2FIX(checkmatch_flags));
8581 PUSH_INSNL(ret, *location, branchif, exception_match_label);
8582 }
8583 }
8584 else {
8585 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8586 PUSH_INSN1(ret, *location, putobject, rb_eStandardError);
8587 PUSH_INSN1(ret, *location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
8588 PUSH_INSNL(ret, *location, branchif, exception_match_label);
8589 }
8590
8591 // If none of the exceptions that we are matching against matched, then
8592 // we'll jump straight to the rescue_end_label label.
8593 PUSH_INSNL(ret, *location, jump, rescue_end_label);
8594
8595 // Here we have the exception_match_label, which is where the
8596 // control-flow goes in the case that one of the exceptions matched.
8597 // Here we will compile the instructions to handle the exception.
8598 PUSH_LABEL(ret, exception_match_label);
8599 PUSH_TRACE(ret, RUBY_EVENT_RESCUE);
8600
8601 // If we have a reference to the exception, then we'll compile the write
8602 // into the instruction sequence. This can look quite different
8603 // depending on the kind of write being performed.
8604 if (node->reference) {
8605 DECL_ANCHOR(writes);
8606 DECL_ANCHOR(cleanup);
8607
8608 pm_compile_target_node(iseq, node->reference, ret, writes, cleanup, scope_node, NULL);
8609 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8610
8611 PUSH_SEQ(ret, writes);
8612 PUSH_SEQ(ret, cleanup);
8613 }
8614
8615 // If we have statements to execute, we'll compile them here. Otherwise
8616 // we'll push nil onto the stack.
8617 if (node->statements != NULL) {
8618 // We'll temporarily remove the end_label location from the iseq
8619 // when compiling the statements so that next/redo statements
8620 // inside the body will throw to the correct place instead of
8621 // jumping straight to the end of this iseq
8622 LABEL *prev_end = ISEQ_COMPILE_DATA(iseq)->end_label;
8623 ISEQ_COMPILE_DATA(iseq)->end_label = NULL;
8624
8625 PM_COMPILE((const pm_node_t *) node->statements);
8626
8627 // Now restore the end_label
8628 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end;
8629 }
8630 else {
8631 PUSH_INSN(ret, *location, putnil);
8632 }
8633
8634 PUSH_INSN(ret, *location, leave);
8635
8636 // Here we'll insert the rescue_end_label label, which is jumped to if
8637 // none of the exceptions matched. It will cause the control-flow to
8638 // either jump to the next rescue clause or it will fall through to the
8639 // subsequent instruction returning the raised error.
8640 PUSH_LABEL(ret, rescue_end_label);
8641 if (node->subsequent != NULL) {
8642 PM_COMPILE((const pm_node_t *) node->subsequent);
8643 }
8644 else {
8645 PUSH_GETLOCAL(ret, *location, 1, 0);
8646 }
8647}
8648
8649static inline void
8650pm_compile_return_node(rb_iseq_t *iseq, const pm_return_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8651{
8652 const pm_arguments_node_t *arguments = node->arguments;
8653 enum rb_iseq_type type = ISEQ_BODY(iseq)->type;
8654 LABEL *splabel = 0;
8655
8656 const rb_iseq_t *parent_iseq = iseq;
8657 enum rb_iseq_type parent_type = ISEQ_BODY(parent_iseq)->type;
8658 while (parent_type == ISEQ_TYPE_RESCUE || parent_type == ISEQ_TYPE_ENSURE) {
8659 if (!(parent_iseq = ISEQ_BODY(parent_iseq)->parent_iseq)) break;
8660 parent_type = ISEQ_BODY(parent_iseq)->type;
8661 }
8662
8663 switch (parent_type) {
8664 case ISEQ_TYPE_TOP:
8665 case ISEQ_TYPE_MAIN:
8666 if (arguments) {
8667 rb_warn("argument of top-level return is ignored");
8668 }
8669 if (parent_iseq == iseq) {
8670 type = ISEQ_TYPE_METHOD;
8671 }
8672 break;
8673 default:
8674 break;
8675 }
8676
8677 if (type == ISEQ_TYPE_METHOD) {
8678 splabel = NEW_LABEL(0);
8679 PUSH_LABEL(ret, splabel);
8680 PUSH_ADJUST(ret, *location, 0);
8681 }
8682
8683 if (arguments != NULL) {
8684 PM_COMPILE_NOT_POPPED((const pm_node_t *) arguments);
8685 }
8686 else {
8687 PUSH_INSN(ret, *location, putnil);
8688 }
8689
8690 if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
8691 pm_add_ensure_iseq(ret, iseq, 1, scope_node);
8692 PUSH_TRACE(ret, RUBY_EVENT_RETURN);
8693 PUSH_INSN(ret, *location, leave);
8694 PUSH_ADJUST_RESTORE(ret, splabel);
8695 if (!popped) PUSH_INSN(ret, *location, putnil);
8696 }
8697 else {
8698 PUSH_INSN1(ret, *location, throw, INT2FIX(TAG_RETURN));
8699 if (popped) PUSH_INSN(ret, *location, pop);
8700 }
8701}
8702
8703static inline void
8704pm_compile_super_node(rb_iseq_t *iseq, const pm_super_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8705{
8706 DECL_ANCHOR(args);
8707
8708 LABEL *retry_label = NEW_LABEL(location->line);
8709 LABEL *retry_end_l = NEW_LABEL(location->line);
8710
8711 const rb_iseq_t *previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
8712 const rb_iseq_t *current_block;
8713 ISEQ_COMPILE_DATA(iseq)->current_block = current_block = NULL;
8714
8715 PUSH_LABEL(ret, retry_label);
8716 PUSH_INSN(ret, *location, putself);
8717
8718 int flags = 0;
8719 struct rb_callinfo_kwarg *keywords = NULL;
8720 int argc = pm_setup_args(node->arguments, node->block, &flags, &keywords, iseq, ret, scope_node, location);
8721 bool is_forwardable = (node->arguments != NULL) && PM_NODE_FLAG_P(node->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING);
8722 flags |= VM_CALL_SUPER | VM_CALL_FCALL;
8723
8724 if (node->block && PM_NODE_TYPE_P(node->block, PM_BLOCK_NODE)) {
8725 pm_scope_node_t next_scope_node;
8726 pm_scope_node_init(node->block, &next_scope_node, scope_node);
8727
8728 ISEQ_COMPILE_DATA(iseq)->current_block = current_block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location->line);
8729 pm_scope_node_destroy(&next_scope_node);
8730 }
8731
8732 if (!node->block) {
8733 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
8734 }
8735
8736 if ((flags & VM_CALL_ARGS_BLOCKARG) && (flags & VM_CALL_KW_SPLAT) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
8737 PUSH_INSN(args, *location, splatkw);
8738 }
8739
8740 PUSH_SEQ(ret, args);
8741 if (is_forwardable && ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
8742 flags |= VM_CALL_FORWARDING;
8743
8744 {
8745 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, current_block != NULL);
8746 PUSH_INSN2(ret, *location, invokesuperforward, callinfo, current_block);
8747 }
8748 }
8749 else {
8750 {
8751 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, current_block != NULL);
8752 PUSH_INSN2(ret, *location, invokesuper, callinfo, current_block);
8753 }
8754
8755 }
8756
8757 pm_compile_retry_end_label(iseq, ret, retry_end_l);
8758
8759 if (popped) PUSH_INSN(ret, *location, pop);
8760 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
8761 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, current_block, retry_end_l);
8762}
8763
8764static inline void
8765pm_compile_yield_node(rb_iseq_t *iseq, const pm_yield_node_t *node, const pm_node_location_t *location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8766{
8767 switch (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->type) {
8768 case ISEQ_TYPE_TOP:
8769 case ISEQ_TYPE_MAIN:
8770 case ISEQ_TYPE_CLASS:
8771 COMPILE_ERROR(iseq, location->line, "Invalid yield");
8772 return;
8773 default: /* valid */;
8774 }
8775
8776 int argc = 0;
8777 int flags = 0;
8778 struct rb_callinfo_kwarg *keywords = NULL;
8779
8780 if (node->arguments) {
8781 argc = pm_setup_args(node->arguments, NULL, &flags, &keywords, iseq, ret, scope_node, location);
8782 }
8783
8784 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, FALSE);
8785 PUSH_INSN1(ret, *location, invokeblock, callinfo);
8786
8787 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
8788 if (popped) PUSH_INSN(ret, *location, pop);
8789
8790 int level = 0;
8791 for (const rb_iseq_t *tmp_iseq = iseq; tmp_iseq != ISEQ_BODY(iseq)->local_iseq; level++) {
8792 tmp_iseq = ISEQ_BODY(tmp_iseq)->parent_iseq;
8793 }
8794
8795 if (level > 0) access_outer_variables(iseq, level, rb_intern("yield"), true);
8796}
8797
8808static void
8809pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8810{
8811 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
8812 int lineno = (int) location.line;
8813
8814 if (PM_NODE_TYPE_P(node, PM_BEGIN_NODE) && (((const pm_begin_node_t *) node)->statements == NULL) && (((const pm_begin_node_t *) node)->rescue_clause != NULL)) {
8815 // If this node is a begin node and it has empty statements and also
8816 // has a rescue clause, then the other parser considers it as
8817 // starting on the same line as the rescue, as opposed to the
8818 // location of the begin keyword. We replicate that behavior here.
8819 lineno = (int) PM_NODE_START_LINE_COLUMN(((const pm_begin_node_t *) node)->rescue_clause).line;
8820 }
8821
8822 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_NEWLINE) && ISEQ_COMPILE_DATA(iseq)->last_line != lineno) {
8823 // If this node has the newline flag set and it is on a new line
8824 // from the previous nodes that have been compiled for this ISEQ,
8825 // then we need to emit a newline event.
8826 int event = RUBY_EVENT_LINE;
8827
8828 ISEQ_COMPILE_DATA(iseq)->last_line = lineno;
8829 if (lineno > 0 && ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
8830 event |= RUBY_EVENT_COVERAGE_LINE;
8831 }
8832 PUSH_TRACE(ret, event);
8833 }
8834
8835 switch (PM_NODE_TYPE(node)) {
8836 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
8837 // alias $foo $bar
8838 // ^^^^^^^^^^^^^^^
8839 pm_compile_alias_global_variable_node(iseq, (const pm_alias_global_variable_node_t *) node, &location, ret, popped, scope_node);
8840 return;
8841 case PM_ALIAS_METHOD_NODE:
8842 // alias foo bar
8843 // ^^^^^^^^^^^^^
8844 pm_compile_alias_method_node(iseq, (const pm_alias_method_node_t *) node, &location, ret, popped, scope_node);
8845 return;
8846 case PM_AND_NODE:
8847 // a and b
8848 // ^^^^^^^
8849 pm_compile_and_node(iseq, (const pm_and_node_t *) node, &location, ret, popped, scope_node);
8850 return;
8851 case PM_ARGUMENTS_NODE: {
8852 // break foo
8853 // ^^^
8854 //
8855 // These are ArgumentsNodes that are not compiled directly by their
8856 // parent call nodes, used in the cases of NextNodes, ReturnNodes, and
8857 // BreakNodes. They can create an array like ArrayNode.
8858 const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node;
8859 const pm_node_list_t *elements = &cast->arguments;
8860
8861 if (elements->size == 1) {
8862 // If we are only returning a single element through one of the jump
8863 // nodes, then we will only compile that node directly.
8864 PM_COMPILE(elements->nodes[0]);
8865 }
8866 else {
8867 pm_compile_array_node(iseq, (const pm_node_t *) cast, elements, &location, ret, popped, scope_node);
8868 }
8869 return;
8870 }
8871 case PM_ARRAY_NODE: {
8872 // [foo, bar, baz]
8873 // ^^^^^^^^^^^^^^^
8874 const pm_array_node_t *cast = (const pm_array_node_t *) node;
8875 pm_compile_array_node(iseq, (const pm_node_t *) cast, &cast->elements, &location, ret, popped, scope_node);
8876 return;
8877 }
8878 case PM_ASSOC_NODE: {
8879 // { foo: 1 }
8880 // ^^^^^^
8881 //
8882 // foo(bar: 1)
8883 // ^^^^^^
8884 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node;
8885
8886 PM_COMPILE(cast->key);
8887 PM_COMPILE(cast->value);
8888
8889 return;
8890 }
8891 case PM_ASSOC_SPLAT_NODE: {
8892 // { **foo }
8893 // ^^^^^
8894 //
8895 // def foo(**); bar(**); end
8896 // ^^
8897 const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node;
8898
8899 if (cast->value != NULL) {
8900 PM_COMPILE(cast->value);
8901 }
8902 else if (!popped) {
8903 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_POW, 0);
8904 PUSH_GETLOCAL(ret, location, index.index, index.level);
8905 }
8906
8907 return;
8908 }
8909 case PM_BACK_REFERENCE_READ_NODE: {
8910 // $+
8911 // ^^
8912 if (!popped) {
8914 VALUE backref = pm_compile_back_reference_ref(scope_node, cast);
8915
8916 PUSH_INSN2(ret, location, getspecial, INT2FIX(1), backref);
8917 }
8918 return;
8919 }
8920 case PM_BEGIN_NODE: {
8921 // begin end
8922 // ^^^^^^^^^
8923 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
8924
8925 if (cast->ensure_clause) {
8926 // Compiling the ensure clause will compile the rescue clause (if
8927 // there is one), which will compile the begin statements.
8928 pm_compile_ensure(iseq, cast, &location, ret, popped, scope_node);
8929 }
8930 else if (cast->rescue_clause) {
8931 // Compiling rescue will compile begin statements (if applicable).
8932 pm_compile_rescue(iseq, cast, &location, ret, popped, scope_node);
8933 }
8934 else {
8935 // If there is neither ensure or rescue, the just compile the
8936 // statements.
8937 if (cast->statements != NULL) {
8938 PM_COMPILE((const pm_node_t *) cast->statements);
8939 }
8940 else if (!popped) {
8941 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
8942 }
8943 }
8944 return;
8945 }
8946 case PM_BLOCK_ARGUMENT_NODE: {
8947 // foo(&bar)
8948 // ^^^^
8950
8951 if (cast->expression != NULL) {
8952 PM_COMPILE(cast->expression);
8953 }
8954 else {
8955 // If there's no expression, this must be block forwarding.
8956 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_AND, 0);
8957 PUSH_INSN2(ret, location, getblockparamproxy, INT2FIX(local_index.index + VM_ENV_DATA_SIZE - 1), INT2FIX(local_index.level));
8958 }
8959 return;
8960 }
8961 case PM_BREAK_NODE:
8962 // break
8963 // ^^^^^
8964 //
8965 // break foo
8966 // ^^^^^^^^^
8967 pm_compile_break_node(iseq, (const pm_break_node_t *) node, &location, ret, popped, scope_node);
8968 return;
8969 case PM_CALL_NODE:
8970 // foo
8971 // ^^^
8972 //
8973 // foo.bar
8974 // ^^^^^^^
8975 //
8976 // foo.bar() {}
8977 // ^^^^^^^^^^^^
8978 pm_compile_call_node(iseq, (const pm_call_node_t *) node, ret, popped, scope_node);
8979 return;
8980 case PM_CALL_AND_WRITE_NODE: {
8981 // foo.bar &&= baz
8982 // ^^^^^^^^^^^^^^^
8984 pm_compile_call_and_or_write_node(iseq, true, cast->receiver, cast->value, cast->write_name, cast->read_name, PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION), &location, ret, popped, scope_node);
8985 return;
8986 }
8987 case PM_CALL_OR_WRITE_NODE: {
8988 // foo.bar ||= baz
8989 // ^^^^^^^^^^^^^^^
8991 pm_compile_call_and_or_write_node(iseq, false, cast->receiver, cast->value, cast->write_name, cast->read_name, PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION), &location, ret, popped, scope_node);
8992 return;
8993 }
8994 case PM_CALL_OPERATOR_WRITE_NODE:
8995 // foo.bar += baz
8996 // ^^^^^^^^^^^^^^^
8997 //
8998 // Call operator writes occur when you have a call node on the left-hand
8999 // side of a write operator that is not `=`. As an example,
9000 // `foo.bar *= 1`. This breaks down to caching the receiver on the
9001 // stack and then performing three method calls, one to read the value,
9002 // one to compute the result, and one to write the result back to the
9003 // receiver.
9004 pm_compile_call_operator_write_node(iseq, (const pm_call_operator_write_node_t *) node, &location, ret, popped, scope_node);
9005 return;
9006 case PM_CASE_NODE:
9007 // case foo; when bar; end
9008 // ^^^^^^^^^^^^^^^^^^^^^^^
9009 pm_compile_case_node(iseq, (const pm_case_node_t *) node, &location, ret, popped, scope_node);
9010 return;
9011 case PM_CASE_MATCH_NODE:
9012 // case foo; in bar; end
9013 // ^^^^^^^^^^^^^^^^^^^^^
9014 //
9015 // If you use the `case` keyword to create a case match node, it will
9016 // match against all of the `in` clauses until it finds one that
9017 // matches. If it doesn't find one, it can optionally fall back to an
9018 // `else` clause. If none is present and a match wasn't found, it will
9019 // raise an appropriate error.
9020 pm_compile_case_match_node(iseq, (const pm_case_match_node_t *) node, &location, ret, popped, scope_node);
9021 return;
9022 case PM_CLASS_NODE: {
9023 // class Foo; end
9024 // ^^^^^^^^^^^^^^
9025 const pm_class_node_t *cast = (const pm_class_node_t *) node;
9026
9027 ID class_id = pm_constant_id_lookup(scope_node, cast->name);
9028 VALUE class_name = rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(class_id)));
9029
9030 pm_scope_node_t next_scope_node;
9031 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9032
9033 const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(&next_scope_node, class_name, ISEQ_TYPE_CLASS, location.line);
9034 pm_scope_node_destroy(&next_scope_node);
9035
9036 // TODO: Once we merge constant path nodes correctly, fix this flag
9037 const int flags = VM_DEFINECLASS_TYPE_CLASS |
9038 (cast->superclass ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
9039 pm_compile_class_path(iseq, cast->constant_path, &location, ret, false, scope_node);
9040
9041 if (cast->superclass) {
9042 PM_COMPILE_NOT_POPPED(cast->superclass);
9043 }
9044 else {
9045 PUSH_INSN(ret, location, putnil);
9046 }
9047
9048 {
9049 VALUE operand = ID2SYM(class_id);
9050 PUSH_INSN3(ret, location, defineclass, operand, class_iseq, INT2FIX(flags));
9051 }
9052 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq);
9053
9054 if (popped) PUSH_INSN(ret, location, pop);
9055 return;
9056 }
9057 case PM_CLASS_VARIABLE_AND_WRITE_NODE: {
9058 // @@foo &&= bar
9059 // ^^^^^^^^^^^^^
9061 LABEL *end_label = NEW_LABEL(location.line);
9062
9063 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9064 VALUE name = ID2SYM(name_id);
9065
9066 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
9067 if (!popped) PUSH_INSN(ret, location, dup);
9068
9069 PUSH_INSNL(ret, location, branchunless, end_label);
9070 if (!popped) PUSH_INSN(ret, location, pop);
9071
9072 PM_COMPILE_NOT_POPPED(cast->value);
9073 if (!popped) PUSH_INSN(ret, location, dup);
9074
9075 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
9076 PUSH_LABEL(ret, end_label);
9077
9078 return;
9079 }
9080 case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: {
9081 // @@foo += bar
9082 // ^^^^^^^^^^^^
9084
9085 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9086 VALUE name = ID2SYM(name_id);
9087
9088 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
9089 PM_COMPILE_NOT_POPPED(cast->value);
9090
9091 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9092 int flags = VM_CALL_ARGS_SIMPLE;
9093 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
9094
9095 if (!popped) PUSH_INSN(ret, location, dup);
9096 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
9097
9098 return;
9099 }
9100 case PM_CLASS_VARIABLE_OR_WRITE_NODE: {
9101 // @@foo ||= bar
9102 // ^^^^^^^^^^^^^
9104 LABEL *end_label = NEW_LABEL(location.line);
9105 LABEL *start_label = NEW_LABEL(location.line);
9106
9107 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9108 VALUE name = ID2SYM(name_id);
9109
9110 PUSH_INSN(ret, location, putnil);
9111 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CVAR), name, Qtrue);
9112 PUSH_INSNL(ret, location, branchunless, start_label);
9113
9114 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
9115 if (!popped) PUSH_INSN(ret, location, dup);
9116
9117 PUSH_INSNL(ret, location, branchif, end_label);
9118 if (!popped) PUSH_INSN(ret, location, pop);
9119
9120 PUSH_LABEL(ret, start_label);
9121 PM_COMPILE_NOT_POPPED(cast->value);
9122 if (!popped) PUSH_INSN(ret, location, dup);
9123
9124 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
9125 PUSH_LABEL(ret, end_label);
9126
9127 return;
9128 }
9129 case PM_CLASS_VARIABLE_READ_NODE: {
9130 // @@foo
9131 // ^^^^^
9132 if (!popped) {
9134 ID name = pm_constant_id_lookup(scope_node, cast->name);
9135 PUSH_INSN2(ret, location, getclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
9136 }
9137 return;
9138 }
9139 case PM_CLASS_VARIABLE_WRITE_NODE: {
9140 // @@foo = 1
9141 // ^^^^^^^^^
9143 PM_COMPILE_NOT_POPPED(cast->value);
9144 if (!popped) PUSH_INSN(ret, location, dup);
9145
9146 ID name = pm_constant_id_lookup(scope_node, cast->name);
9147 PUSH_INSN2(ret, location, setclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
9148
9149 return;
9150 }
9151 case PM_CONSTANT_PATH_NODE: {
9152 // Foo::Bar
9153 // ^^^^^^^^
9154 VALUE parts;
9155
9156 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache && ((parts = pm_constant_path_parts(node, scope_node)) != Qnil)) {
9157 ISEQ_BODY(iseq)->ic_size++;
9158 RB_OBJ_SET_SHAREABLE(parts);
9159 PUSH_INSN1(ret, location, opt_getconstant_path, parts);
9160 }
9161 else {
9162 DECL_ANCHOR(prefix);
9163 DECL_ANCHOR(body);
9164
9165 pm_compile_constant_path(iseq, node, prefix, body, popped, scope_node);
9166 if (LIST_INSN_SIZE_ZERO(prefix)) {
9167 PUSH_INSN(ret, location, putnil);
9168 }
9169 else {
9170 PUSH_SEQ(ret, prefix);
9171 }
9172
9173 PUSH_SEQ(ret, body);
9174 }
9175
9176 if (popped) PUSH_INSN(ret, location, pop);
9177 return;
9178 }
9179 case PM_CONSTANT_PATH_AND_WRITE_NODE: {
9180 // Foo::Bar &&= baz
9181 // ^^^^^^^^^^^^^^^^
9183 pm_compile_constant_path_and_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9184 return;
9185 }
9186 case PM_CONSTANT_PATH_OR_WRITE_NODE: {
9187 // Foo::Bar ||= baz
9188 // ^^^^^^^^^^^^^^^^
9190 pm_compile_constant_path_or_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9191 return;
9192 }
9193 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: {
9194 // Foo::Bar += baz
9195 // ^^^^^^^^^^^^^^^
9197 pm_compile_constant_path_operator_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9198 return;
9199 }
9200 case PM_CONSTANT_PATH_WRITE_NODE: {
9201 // Foo::Bar = 1
9202 // ^^^^^^^^^^^^
9204 pm_compile_constant_path_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9205 return;
9206 }
9207 case PM_CONSTANT_READ_NODE: {
9208 // Foo
9209 // ^^^
9211 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9212
9213 pm_compile_constant_read(iseq, name, &cast->base.location, location.node_id, ret, scope_node);
9214 if (popped) PUSH_INSN(ret, location, pop);
9215
9216 return;
9217 }
9218 case PM_CONSTANT_AND_WRITE_NODE: {
9219 // Foo &&= bar
9220 // ^^^^^^^^^^^
9222 pm_compile_constant_and_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9223 return;
9224 }
9225 case PM_CONSTANT_OR_WRITE_NODE: {
9226 // Foo ||= bar
9227 // ^^^^^^^^^^^
9229 pm_compile_constant_or_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9230 return;
9231 }
9232 case PM_CONSTANT_OPERATOR_WRITE_NODE: {
9233 // Foo += bar
9234 // ^^^^^^^^^^
9236 pm_compile_constant_operator_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9237 return;
9238 }
9239 case PM_CONSTANT_WRITE_NODE: {
9240 // Foo = 1
9241 // ^^^^^^^
9243 pm_compile_constant_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9244 return;
9245 }
9246 case PM_DEF_NODE: {
9247 // def foo; end
9248 // ^^^^^^^^^^^^
9249 //
9250 // def self.foo; end
9251 // ^^^^^^^^^^^^^^^^^
9252 const pm_def_node_t *cast = (const pm_def_node_t *) node;
9253 ID method_name = pm_constant_id_lookup(scope_node, cast->name);
9254
9255 pm_scope_node_t next_scope_node;
9256 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9257
9258 rb_iseq_t *method_iseq = NEW_ISEQ(&next_scope_node, rb_id2str(method_name), ISEQ_TYPE_METHOD, location.line);
9259 pm_scope_node_destroy(&next_scope_node);
9260
9261 if (cast->receiver) {
9262 PM_COMPILE_NOT_POPPED(cast->receiver);
9263 PUSH_INSN2(ret, location, definesmethod, ID2SYM(method_name), method_iseq);
9264 }
9265 else {
9266 PUSH_INSN2(ret, location, definemethod, ID2SYM(method_name), method_iseq);
9267 }
9268 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) method_iseq);
9269
9270 if (!popped) {
9271 PUSH_INSN1(ret, location, putobject, ID2SYM(method_name));
9272 }
9273
9274 return;
9275 }
9276 case PM_DEFINED_NODE: {
9277 // defined?(a)
9278 // ^^^^^^^^^^^
9279 const pm_defined_node_t *cast = (const pm_defined_node_t *) node;
9280 pm_compile_defined_expr(iseq, cast->value, &location, ret, popped, scope_node, false);
9281 return;
9282 }
9283 case PM_EMBEDDED_STATEMENTS_NODE: {
9284 // "foo #{bar}"
9285 // ^^^^^^
9287
9288 if (cast->statements != NULL) {
9289 PM_COMPILE((const pm_node_t *) (cast->statements));
9290 }
9291 else {
9292 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
9293 }
9294
9295 if (popped) PUSH_INSN(ret, location, pop);
9296 return;
9297 }
9298 case PM_EMBEDDED_VARIABLE_NODE: {
9299 // "foo #@bar"
9300 // ^^^^^
9302 PM_COMPILE(cast->variable);
9303 return;
9304 }
9305 case PM_FALSE_NODE: {
9306 // false
9307 // ^^^^^
9308 if (!popped) {
9309 PUSH_INSN1(ret, location, putobject, Qfalse);
9310 }
9311 return;
9312 }
9313 case PM_ENSURE_NODE: {
9314 const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node;
9315
9316 if (cast->statements != NULL) {
9317 PM_COMPILE((const pm_node_t *) cast->statements);
9318 }
9319
9320 return;
9321 }
9322 case PM_ELSE_NODE: {
9323 // if foo then bar else baz end
9324 // ^^^^^^^^^^^^
9325 const pm_else_node_t *cast = (const pm_else_node_t *) node;
9326
9327 if (cast->statements != NULL) {
9328 PM_COMPILE((const pm_node_t *) cast->statements);
9329 }
9330 else if (!popped) {
9331 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
9332 }
9333
9334 return;
9335 }
9336 case PM_FLIP_FLOP_NODE: {
9337 // if foo .. bar; end
9338 // ^^^^^^^^^^
9339 const pm_flip_flop_node_t *cast = (const pm_flip_flop_node_t *) node;
9340
9341 LABEL *final_label = NEW_LABEL(location.line);
9342 LABEL *then_label = NEW_LABEL(location.line);
9343 LABEL *else_label = NEW_LABEL(location.line);
9344
9345 pm_compile_flip_flop(cast, else_label, then_label, iseq, location.line, ret, popped, scope_node);
9346
9347 PUSH_LABEL(ret, then_label);
9348 PUSH_INSN1(ret, location, putobject, Qtrue);
9349 PUSH_INSNL(ret, location, jump, final_label);
9350 PUSH_LABEL(ret, else_label);
9351 PUSH_INSN1(ret, location, putobject, Qfalse);
9352 PUSH_LABEL(ret, final_label);
9353
9354 return;
9355 }
9356 case PM_FLOAT_NODE: {
9357 // 1.0
9358 // ^^^
9359 if (!popped) {
9360 VALUE operand = parse_float((const pm_float_node_t *) node);
9361 PUSH_INSN1(ret, location, putobject, operand);
9362 }
9363 return;
9364 }
9365 case PM_FOR_NODE: {
9366 // for foo in bar do end
9367 // ^^^^^^^^^^^^^^^^^^^^^
9368 const pm_for_node_t *cast = (const pm_for_node_t *) node;
9369
9370 LABEL *retry_label = NEW_LABEL(location.line);
9371 LABEL *retry_end_l = NEW_LABEL(location.line);
9372
9373 // First, compile the collection that we're going to be iterating over.
9374 PUSH_LABEL(ret, retry_label);
9375 PM_COMPILE_NOT_POPPED(cast->collection);
9376
9377 // Next, create the new scope that is going to contain the block that
9378 // will be passed to the each method.
9379 pm_scope_node_t next_scope_node;
9380 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9381
9382 const rb_iseq_t *child_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location.line);
9383 pm_scope_node_destroy(&next_scope_node);
9384
9385 const rb_iseq_t *prev_block = ISEQ_COMPILE_DATA(iseq)->current_block;
9386 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
9387
9388 // Now, create the method call to each that will be used to iterate over
9389 // the collection, and pass the newly created iseq as the block.
9390 PUSH_SEND_WITH_BLOCK(ret, location, idEach, INT2FIX(0), child_iseq);
9391 pm_compile_retry_end_label(iseq, ret, retry_end_l);
9392
9393 if (popped) PUSH_INSN(ret, location, pop);
9394 ISEQ_COMPILE_DATA(iseq)->current_block = prev_block;
9395 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
9396 return;
9397 }
9398 case PM_FORWARDING_ARGUMENTS_NODE:
9399 rb_bug("Cannot compile a ForwardingArgumentsNode directly\n");
9400 return;
9401 case PM_FORWARDING_SUPER_NODE:
9402 // super
9403 // ^^^^^
9404 //
9405 // super {}
9406 // ^^^^^^^^
9407 pm_compile_forwarding_super_node(iseq, (const pm_forwarding_super_node_t *) node, &location, ret, popped, scope_node);
9408 return;
9409 case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: {
9410 // $foo &&= bar
9411 // ^^^^^^^^^^^^
9413 LABEL *end_label = NEW_LABEL(location.line);
9414
9415 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9416 PUSH_INSN1(ret, location, getglobal, name);
9417 if (!popped) PUSH_INSN(ret, location, dup);
9418
9419 PUSH_INSNL(ret, location, branchunless, end_label);
9420 if (!popped) PUSH_INSN(ret, location, pop);
9421
9422 PM_COMPILE_NOT_POPPED(cast->value);
9423 if (!popped) PUSH_INSN(ret, location, dup);
9424
9425 PUSH_INSN1(ret, location, setglobal, name);
9426 PUSH_LABEL(ret, end_label);
9427
9428 return;
9429 }
9430 case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: {
9431 // $foo += bar
9432 // ^^^^^^^^^^^
9434
9435 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9436 PUSH_INSN1(ret, location, getglobal, name);
9437 PM_COMPILE_NOT_POPPED(cast->value);
9438
9439 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9440 int flags = VM_CALL_ARGS_SIMPLE;
9441 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
9442
9443 if (!popped) PUSH_INSN(ret, location, dup);
9444 PUSH_INSN1(ret, location, setglobal, name);
9445
9446 return;
9447 }
9448 case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: {
9449 // $foo ||= bar
9450 // ^^^^^^^^^^^^
9452 LABEL *set_label = NEW_LABEL(location.line);
9453 LABEL *end_label = NEW_LABEL(location.line);
9454
9455 PUSH_INSN(ret, location, putnil);
9456 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9457
9458 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_GVAR), name, Qtrue);
9459 PUSH_INSNL(ret, location, branchunless, set_label);
9460
9461 PUSH_INSN1(ret, location, getglobal, name);
9462 if (!popped) PUSH_INSN(ret, location, dup);
9463
9464 PUSH_INSNL(ret, location, branchif, end_label);
9465 if (!popped) PUSH_INSN(ret, location, pop);
9466
9467 PUSH_LABEL(ret, set_label);
9468 PM_COMPILE_NOT_POPPED(cast->value);
9469 if (!popped) PUSH_INSN(ret, location, dup);
9470
9471 PUSH_INSN1(ret, location, setglobal, name);
9472 PUSH_LABEL(ret, end_label);
9473
9474 return;
9475 }
9476 case PM_GLOBAL_VARIABLE_READ_NODE: {
9477 // $foo
9478 // ^^^^
9480 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9481
9482 PUSH_INSN1(ret, location, getglobal, name);
9483 if (popped) PUSH_INSN(ret, location, pop);
9484
9485 return;
9486 }
9487 case PM_GLOBAL_VARIABLE_WRITE_NODE: {
9488 // $foo = 1
9489 // ^^^^^^^^
9491 PM_COMPILE_NOT_POPPED(cast->value);
9492 if (!popped) PUSH_INSN(ret, location, dup);
9493
9494 ID name = pm_constant_id_lookup(scope_node, cast->name);
9495 PUSH_INSN1(ret, location, setglobal, ID2SYM(name));
9496
9497 return;
9498 }
9499 case PM_HASH_NODE: {
9500 // {}
9501 // ^^
9502 //
9503 // If every node in the hash is static, then we can compile the entire
9504 // hash now instead of later.
9505 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9506 // We're only going to compile this node if it's not popped. If it
9507 // is popped, then we know we don't need to do anything since it's
9508 // statically known.
9509 if (!popped) {
9510 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
9511
9512 if (cast->elements.size == 0) {
9513 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
9514 }
9515 else {
9516 VALUE value = pm_static_literal_value(iseq, node, scope_node);
9517 PUSH_INSN1(ret, location, duphash, value);
9518 RB_OBJ_WRITTEN(iseq, Qundef, value);
9519 }
9520 }
9521 }
9522 else {
9523 // Here since we know there are possible side-effects inside the
9524 // hash contents, we're going to build it entirely at runtime. We'll
9525 // do this by pushing all of the key-value pairs onto the stack and
9526 // then combining them with newhash.
9527 //
9528 // If this hash is popped, then this serves only to ensure we enact
9529 // all side-effects (like method calls) that are contained within
9530 // the hash contents.
9531 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
9532 const pm_node_list_t *elements = &cast->elements;
9533
9534 if (popped) {
9535 // If this hash is popped, then we can iterate through each
9536 // element and compile it. The result of each compilation will
9537 // only include the side effects of the element itself.
9538 for (size_t index = 0; index < elements->size; index++) {
9539 PM_COMPILE_POPPED(elements->nodes[index]);
9540 }
9541 }
9542 else {
9543 pm_compile_hash_elements(iseq, node, elements, 0, Qundef, false, ret, scope_node);
9544 }
9545 }
9546
9547 return;
9548 }
9549 case PM_IF_NODE: {
9550 // if foo then bar end
9551 // ^^^^^^^^^^^^^^^^^^^
9552 //
9553 // bar if foo
9554 // ^^^^^^^^^^
9555 //
9556 // foo ? bar : baz
9557 // ^^^^^^^^^^^^^^^
9558 const pm_if_node_t *cast = (const pm_if_node_t *) node;
9559 pm_compile_conditional(iseq, &location, PM_IF_NODE, (const pm_node_t *) cast, cast->statements, cast->subsequent, cast->predicate, ret, popped, scope_node);
9560 return;
9561 }
9562 case PM_IMAGINARY_NODE: {
9563 // 1i
9564 // ^^
9565 if (!popped) {
9566 VALUE operand = parse_imaginary((const pm_imaginary_node_t *) node);
9567 PUSH_INSN1(ret, location, putobject, operand);
9568 }
9569 return;
9570 }
9571 case PM_IMPLICIT_NODE: {
9572 // Implicit nodes mark places in the syntax tree where explicit syntax
9573 // was omitted, but implied. For example,
9574 //
9575 // { foo: }
9576 //
9577 // In this case a method call/local variable read is implied by virtue
9578 // of the missing value. To compile these nodes, we simply compile the
9579 // value that is implied, which is helpfully supplied by the parser.
9580 const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node;
9581 PM_COMPILE(cast->value);
9582 return;
9583 }
9584 case PM_IN_NODE: {
9585 // In nodes are handled by the case match node directly, so we should
9586 // never end up hitting them through this path.
9587 rb_bug("Should not ever enter an in node directly");
9588 return;
9589 }
9590 case PM_INDEX_OPERATOR_WRITE_NODE: {
9591 // foo[bar] += baz
9592 // ^^^^^^^^^^^^^^^
9594 pm_compile_index_operator_write_node(iseq, cast, &location, ret, popped, scope_node);
9595 return;
9596 }
9597 case PM_INDEX_AND_WRITE_NODE: {
9598 // foo[bar] &&= baz
9599 // ^^^^^^^^^^^^^^^^
9601 pm_compile_index_control_flow_write_node(iseq, node, cast->receiver, cast->arguments, cast->block, cast->value, &location, ret, popped, scope_node);
9602 return;
9603 }
9604 case PM_INDEX_OR_WRITE_NODE: {
9605 // foo[bar] ||= baz
9606 // ^^^^^^^^^^^^^^^^
9608 pm_compile_index_control_flow_write_node(iseq, node, cast->receiver, cast->arguments, cast->block, cast->value, &location, ret, popped, scope_node);
9609 return;
9610 }
9611 case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: {
9612 // @foo &&= bar
9613 // ^^^^^^^^^^^^
9615 LABEL *end_label = NEW_LABEL(location.line);
9616
9617 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9618 VALUE name = ID2SYM(name_id);
9619
9620 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9621 if (!popped) PUSH_INSN(ret, location, dup);
9622
9623 PUSH_INSNL(ret, location, branchunless, end_label);
9624 if (!popped) PUSH_INSN(ret, location, pop);
9625
9626 PM_COMPILE_NOT_POPPED(cast->value);
9627 if (!popped) PUSH_INSN(ret, location, dup);
9628
9629 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9630 PUSH_LABEL(ret, end_label);
9631
9632 return;
9633 }
9634 case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: {
9635 // @foo += bar
9636 // ^^^^^^^^^^^
9638
9639 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9640 VALUE name = ID2SYM(name_id);
9641
9642 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9643 PM_COMPILE_NOT_POPPED(cast->value);
9644
9645 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9646 int flags = VM_CALL_ARGS_SIMPLE;
9647 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
9648
9649 if (!popped) PUSH_INSN(ret, location, dup);
9650 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9651
9652 return;
9653 }
9654 case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: {
9655 // @foo ||= bar
9656 // ^^^^^^^^^^^^
9658 LABEL *end_label = NEW_LABEL(location.line);
9659
9660 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9661 VALUE name = ID2SYM(name_id);
9662
9663 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9664 if (!popped) PUSH_INSN(ret, location, dup);
9665
9666 PUSH_INSNL(ret, location, branchif, end_label);
9667 if (!popped) PUSH_INSN(ret, location, pop);
9668
9669 PM_COMPILE_NOT_POPPED(cast->value);
9670 if (!popped) PUSH_INSN(ret, location, dup);
9671
9672 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9673 PUSH_LABEL(ret, end_label);
9674
9675 return;
9676 }
9677 case PM_INSTANCE_VARIABLE_READ_NODE: {
9678 // @foo
9679 // ^^^^
9680 if (!popped) {
9682 ID name = pm_constant_id_lookup(scope_node, cast->name);
9683 PUSH_INSN2(ret, location, getinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
9684 }
9685 return;
9686 }
9687 case PM_INSTANCE_VARIABLE_WRITE_NODE: {
9688 // @foo = 1
9689 // ^^^^^^^^
9691 PM_COMPILE_NOT_POPPED(cast->value);
9692 if (!popped) PUSH_INSN(ret, location, dup);
9693
9694 ID name = pm_constant_id_lookup(scope_node, cast->name);
9695 PUSH_INSN2(ret, location, setinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
9696
9697 return;
9698 }
9699 case PM_INTEGER_NODE: {
9700 // 1
9701 // ^
9702 if (!popped) {
9703 VALUE operand = parse_integer((const pm_integer_node_t *) node);
9704 PUSH_INSN1(ret, location, putobject, operand);
9705 }
9706 return;
9707 }
9708 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: {
9709 // if /foo #{bar}/ then end
9710 // ^^^^^^^^^^^^
9711 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9712 if (!popped) {
9713 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9714 PUSH_INSN1(ret, location, putobject, regexp);
9715 }
9716 }
9717 else {
9718 pm_compile_regexp_dynamic(iseq, node, &((const pm_interpolated_match_last_line_node_t *) node)->parts, &location, ret, popped, scope_node);
9719 }
9720
9721 PUSH_INSN1(ret, location, getglobal, rb_id2sym(idLASTLINE));
9722 PUSH_SEND(ret, location, idEqTilde, INT2NUM(1));
9723 if (popped) PUSH_INSN(ret, location, pop);
9724
9725 return;
9726 }
9727 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
9728 // /foo #{bar}/
9729 // ^^^^^^^^^^^^
9730 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) {
9731 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
9732 const rb_iseq_t *block_iseq = NULL;
9733 int ise_index = ISEQ_BODY(iseq)->ise_size++;
9734
9735 pm_scope_node_t next_scope_node;
9736 pm_scope_node_init(node, &next_scope_node, scope_node);
9737
9738 block_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, location.line);
9739 pm_scope_node_destroy(&next_scope_node);
9740
9741 ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
9742 PUSH_INSN2(ret, location, once, block_iseq, INT2FIX(ise_index));
9743 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
9744
9745 if (popped) PUSH_INSN(ret, location, pop);
9746 return;
9747 }
9748
9749 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9750 if (!popped) {
9751 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9752 PUSH_INSN1(ret, location, putobject, regexp);
9753 }
9754 }
9755 else {
9756 pm_compile_regexp_dynamic(iseq, node, &((const pm_interpolated_regular_expression_node_t *) node)->parts, &location, ret, popped, scope_node);
9757 if (popped) PUSH_INSN(ret, location, pop);
9758 }
9759
9760 return;
9761 }
9762 case PM_INTERPOLATED_STRING_NODE: {
9763 // "foo #{bar}"
9764 // ^^^^^^^^^^^^
9765 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9766 if (!popped) {
9767 VALUE string = pm_static_literal_value(iseq, node, scope_node);
9768
9769 if (PM_NODE_FLAG_P(node, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN)) {
9770 PUSH_INSN1(ret, location, putobject, string);
9771 }
9772 else if (PM_NODE_FLAG_P(node, PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) {
9773 PUSH_INSN1(ret, location, dupstring, string);
9774 }
9775 else {
9776 PUSH_INSN1(ret, location, dupchilledstring, string);
9777 }
9778 }
9779 }
9780 else {
9782 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL, PM_NODE_FLAG_P(cast, PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE), PM_NODE_FLAG_P(cast, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN));
9783 if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9784 if (popped) PUSH_INSN(ret, location, pop);
9785 }
9786
9787 return;
9788 }
9789 case PM_INTERPOLATED_SYMBOL_NODE: {
9790 // :"foo #{bar}"
9791 // ^^^^^^^^^^^^^
9793 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL, false, false);
9794
9795 if (length > 1) {
9796 PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9797 }
9798
9799 if (!popped) {
9800 PUSH_INSN(ret, location, intern);
9801 }
9802 else {
9803 PUSH_INSN(ret, location, pop);
9804 }
9805
9806 return;
9807 }
9808 case PM_INTERPOLATED_X_STRING_NODE: {
9809 // `foo #{bar}`
9810 // ^^^^^^^^^^^^
9812
9813 PUSH_INSN(ret, location, putself);
9814
9815 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, false, scope_node, NULL, NULL, false, false);
9816 if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9817
9818 PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
9819 if (popped) PUSH_INSN(ret, location, pop);
9820
9821 return;
9822 }
9823 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
9824 // -> { it }
9825 // ^^
9826 if (!popped) {
9827 pm_scope_node_t *current_scope_node = scope_node;
9828 int level = 0;
9829
9830 while (current_scope_node) {
9831 if (current_scope_node->parameters && PM_NODE_TYPE_P(current_scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
9832 PUSH_GETLOCAL(ret, location, current_scope_node->local_table_for_iseq_size, level);
9833 return;
9834 }
9835
9836 current_scope_node = current_scope_node->previous;
9837 level++;
9838 }
9839 rb_bug("Local `it` does not exist");
9840 }
9841
9842 return;
9843 }
9844 case PM_KEYWORD_HASH_NODE: {
9845 // foo(bar: baz)
9846 // ^^^^^^^^
9847 const pm_keyword_hash_node_t *cast = (const pm_keyword_hash_node_t *) node;
9848 const pm_node_list_t *elements = &cast->elements;
9849
9850 bool has_splat = false;
9851 for (size_t index = 0; index < elements->size; index++) {
9852 if (PM_NODE_TYPE_P(elements->nodes[index], PM_ASSOC_SPLAT_NODE)) {
9853 has_splat = true;
9854 break;
9855 }
9856 }
9857
9858 if (has_splat) {
9859 pm_compile_hash_elements(iseq, node, elements, 0, Qundef, false, ret, scope_node);
9860 }
9861 else {
9862 const pm_node_t *element;
9863 PM_NODE_LIST_FOREACH(elements, index, element) {
9864 PM_COMPILE(element);
9865 }
9866
9867 if (!popped) PUSH_INSN1(ret, location, newhash, INT2FIX(elements->size * 2));
9868 }
9869 return;
9870 }
9871 case PM_LAMBDA_NODE: {
9872 // -> {}
9873 // ^^^^^
9874 const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node;
9875
9876 pm_scope_node_t next_scope_node;
9877 pm_scope_node_init(node, &next_scope_node, scope_node);
9878
9879 int opening_lineno = pm_location_line_number_cached(&cast->opening_loc, scope_node);
9880 const rb_iseq_t *block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, opening_lineno);
9881 pm_scope_node_destroy(&next_scope_node);
9882
9883 VALUE argc = INT2FIX(0);
9884 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
9885 PUSH_CALL_WITH_BLOCK(ret, location, idLambda, argc, block);
9886 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) block);
9887
9888 if (popped) PUSH_INSN(ret, location, pop);
9889 return;
9890 }
9891 case PM_LOCAL_VARIABLE_AND_WRITE_NODE: {
9892 // foo &&= bar
9893 // ^^^^^^^^^^^
9895 LABEL *end_label = NEW_LABEL(location.line);
9896
9897 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9898 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9899 if (!popped) PUSH_INSN(ret, location, dup);
9900
9901 PUSH_INSNL(ret, location, branchunless, end_label);
9902 if (!popped) PUSH_INSN(ret, location, pop);
9903
9904 PM_COMPILE_NOT_POPPED(cast->value);
9905 if (!popped) PUSH_INSN(ret, location, dup);
9906
9907 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9908 PUSH_LABEL(ret, end_label);
9909
9910 return;
9911 }
9912 case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: {
9913 // foo += bar
9914 // ^^^^^^^^^^
9916
9917 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9918 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9919
9920 PM_COMPILE_NOT_POPPED(cast->value);
9921
9922 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9923 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
9924
9925 if (!popped) PUSH_INSN(ret, location, dup);
9926 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9927
9928 return;
9929 }
9930 case PM_LOCAL_VARIABLE_OR_WRITE_NODE: {
9931 // foo ||= bar
9932 // ^^^^^^^^^^^
9934
9935 LABEL *set_label = NEW_LABEL(location.line);
9936 LABEL *end_label = NEW_LABEL(location.line);
9937
9938 PUSH_INSN1(ret, location, putobject, Qtrue);
9939 PUSH_INSNL(ret, location, branchunless, set_label);
9940
9941 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9942 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9943 if (!popped) PUSH_INSN(ret, location, dup);
9944
9945 PUSH_INSNL(ret, location, branchif, end_label);
9946 if (!popped) PUSH_INSN(ret, location, pop);
9947
9948 PUSH_LABEL(ret, set_label);
9949 PM_COMPILE_NOT_POPPED(cast->value);
9950 if (!popped) PUSH_INSN(ret, location, dup);
9951
9952 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9953 PUSH_LABEL(ret, end_label);
9954
9955 return;
9956 }
9957 case PM_LOCAL_VARIABLE_READ_NODE: {
9958 // foo
9959 // ^^^
9960 if (!popped) {
9962 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9963 PUSH_GETLOCAL(ret, location, index.index, index.level);
9964 }
9965
9966 return;
9967 }
9968 case PM_LOCAL_VARIABLE_WRITE_NODE: {
9969 // foo = 1
9970 // ^^^^^^^
9972 PM_COMPILE_NOT_POPPED(cast->value);
9973 if (!popped) PUSH_INSN(ret, location, dup);
9974
9975 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9976 PUSH_SETLOCAL(ret, location, index.index, index.level);
9977 return;
9978 }
9979 case PM_MATCH_LAST_LINE_NODE: {
9980 // if /foo/ then end
9981 // ^^^^^
9982 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9983
9984 PUSH_INSN1(ret, location, putobject, regexp);
9985 PUSH_INSN2(ret, location, getspecial, INT2FIX(0), INT2FIX(0));
9986 PUSH_SEND(ret, location, idEqTilde, INT2NUM(1));
9987 if (popped) PUSH_INSN(ret, location, pop);
9988
9989 return;
9990 }
9991 case PM_MATCH_PREDICATE_NODE: {
9992 // foo in bar
9993 // ^^^^^^^^^^
9995
9996 // First, allocate some stack space for the cached return value of any
9997 // calls to #deconstruct.
9998 PUSH_INSN(ret, location, putnil);
9999
10000 // Next, compile the expression that we're going to match against.
10001 PM_COMPILE_NOT_POPPED(cast->value);
10002 PUSH_INSN(ret, location, dup);
10003
10004 // Now compile the pattern that is going to be used to match against the
10005 // expression.
10006 LABEL *matched_label = NEW_LABEL(location.line);
10007 LABEL *unmatched_label = NEW_LABEL(location.line);
10008 LABEL *done_label = NEW_LABEL(location.line);
10009 pm_compile_pattern(iseq, scope_node, cast->pattern, ret, matched_label, unmatched_label, false, true, 2);
10010
10011 // If the pattern did not match, then compile the necessary instructions
10012 // to handle pushing false onto the stack, then jump to the end.
10013 PUSH_LABEL(ret, unmatched_label);
10014 PUSH_INSN(ret, location, pop);
10015 PUSH_INSN(ret, location, pop);
10016
10017 if (!popped) PUSH_INSN1(ret, location, putobject, Qfalse);
10018 PUSH_INSNL(ret, location, jump, done_label);
10019 PUSH_INSN(ret, location, putnil);
10020
10021 // If the pattern did match, then compile the necessary instructions to
10022 // handle pushing true onto the stack, then jump to the end.
10023 PUSH_LABEL(ret, matched_label);
10024 PUSH_INSN1(ret, location, adjuststack, INT2FIX(2));
10025 if (!popped) PUSH_INSN1(ret, location, putobject, Qtrue);
10026 PUSH_INSNL(ret, location, jump, done_label);
10027
10028 PUSH_LABEL(ret, done_label);
10029 return;
10030 }
10031 case PM_MATCH_REQUIRED_NODE:
10032 // foo => bar
10033 // ^^^^^^^^^^
10034 //
10035 // A match required node represents pattern matching against a single
10036 // pattern using the => operator. For example,
10037 //
10038 // foo => bar
10039 //
10040 // This is somewhat analogous to compiling a case match statement with a
10041 // single pattern. In both cases, if the pattern fails it should
10042 // immediately raise an error.
10043 pm_compile_match_required_node(iseq, (const pm_match_required_node_t *) node, &location, ret, popped, scope_node);
10044 return;
10045 case PM_MATCH_WRITE_NODE:
10046 // /(?<foo>foo)/ =~ bar
10047 // ^^^^^^^^^^^^^^^^^^^^
10048 //
10049 // Match write nodes are specialized call nodes that have a regular
10050 // expression with valid named capture groups on the left, the =~
10051 // operator, and some value on the right. The nodes themselves simply
10052 // wrap the call with the local variable targets that will be written
10053 // when the call is executed.
10054 pm_compile_match_write_node(iseq, (const pm_match_write_node_t *) node, &location, ret, popped, scope_node);
10055 return;
10056 case PM_ERROR_RECOVERY_NODE:
10057 rb_bug("A pm_error_recovery_node_t should not exist in prism's AST.");
10058 return;
10059 case PM_MODULE_NODE: {
10060 // module Foo; end
10061 // ^^^^^^^^^^^^^^^
10062 const pm_module_node_t *cast = (const pm_module_node_t *) node;
10063
10064 ID module_id = pm_constant_id_lookup(scope_node, cast->name);
10065 VALUE module_name = rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(module_id)));
10066
10067 pm_scope_node_t next_scope_node;
10068 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
10069
10070 const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(&next_scope_node, module_name, ISEQ_TYPE_CLASS, location.line);
10071 pm_scope_node_destroy(&next_scope_node);
10072
10073 const int flags = VM_DEFINECLASS_TYPE_MODULE | pm_compile_class_path(iseq, cast->constant_path, &location, ret, false, scope_node);
10074 PUSH_INSN(ret, location, putnil);
10075 PUSH_INSN3(ret, location, defineclass, ID2SYM(module_id), module_iseq, INT2FIX(flags));
10076 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) module_iseq);
10077
10078 if (popped) PUSH_INSN(ret, location, pop);
10079 return;
10080 }
10081 case PM_REQUIRED_PARAMETER_NODE: {
10082 // def foo(bar); end
10083 // ^^^
10085 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, 0);
10086
10087 PUSH_SETLOCAL(ret, location, index.index, index.level);
10088 return;
10089 }
10090 case PM_MULTI_WRITE_NODE: {
10091 // foo, bar = baz
10092 // ^^^^^^^^^^^^^^
10093 //
10094 // A multi write node represents writing to multiple values using an =
10095 // operator. Importantly these nodes are only parsed when the left-hand
10096 // side of the operator has multiple targets. The right-hand side of the
10097 // operator having multiple targets represents an implicit array
10098 // instead.
10099 const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node;
10100
10101 DECL_ANCHOR(writes);
10102 DECL_ANCHOR(cleanup);
10103
10104 pm_multi_target_state_t state = { 0 };
10105 state.position = popped ? 0 : 1;
10106 pm_compile_multi_target_node(iseq, node, ret, writes, cleanup, scope_node, &state);
10107
10108 PM_COMPILE_NOT_POPPED(cast->value);
10109 if (!popped) PUSH_INSN(ret, location, dup);
10110
10111 PUSH_SEQ(ret, writes);
10112 if (!popped && state.stack_size >= 1) {
10113 // Make sure the value on the right-hand side of the = operator is
10114 // being returned before we pop the parent expressions.
10115 PUSH_INSN1(ret, location, setn, INT2FIX(state.stack_size));
10116 }
10117
10118 // Now, we need to go back and modify the topn instructions in order to
10119 // ensure they can correctly retrieve the parent expressions.
10120 pm_multi_target_state_update(&state);
10121
10122 PUSH_SEQ(ret, cleanup);
10123 return;
10124 }
10125 case PM_NEXT_NODE:
10126 // next
10127 // ^^^^
10128 //
10129 // next foo
10130 // ^^^^^^^^
10131 pm_compile_next_node(iseq, (const pm_next_node_t *) node, &location, ret, popped, scope_node);
10132 return;
10133 case PM_NIL_NODE: {
10134 // nil
10135 // ^^^
10136 if (!popped) {
10137 PUSH_INSN(ret, location, putnil);
10138 }
10139
10140 return;
10141 }
10142 case PM_NO_BLOCK_PARAMETER_NODE: {
10143 // def foo(&nil); end
10144 // ^^^^
10145 ISEQ_BODY(iseq)->param.flags.accepts_no_block = TRUE;
10146 return;
10147 }
10148 case PM_NO_KEYWORDS_PARAMETER_NODE: {
10149 // def foo(**nil); end
10150 // ^^^^^
10151 ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg = TRUE;
10152 return;
10153 }
10154 case PM_NUMBERED_REFERENCE_READ_NODE: {
10155 // $1
10156 // ^^
10157 if (!popped) {
10159
10160 if (cast->number != 0) {
10161 VALUE ref = pm_compile_numbered_reference_ref(cast);
10162 PUSH_INSN2(ret, location, getspecial, INT2FIX(1), ref);
10163 }
10164 else {
10165 PUSH_INSN(ret, location, putnil);
10166 }
10167 }
10168
10169 return;
10170 }
10171 case PM_OR_NODE:
10172 // a or b
10173 // ^^^^^^
10174 pm_compile_or_node(iseq, (const pm_or_node_t *) node, &location, ret, popped, scope_node);
10175 return;
10176 case PM_OPTIONAL_PARAMETER_NODE: {
10177 // def foo(bar = 1); end
10178 // ^^^^^^^
10180 PM_COMPILE_NOT_POPPED(cast->value);
10181
10182 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, 0);
10183 PUSH_SETLOCAL(ret, location, index.index, index.level);
10184
10185 return;
10186 }
10187 case PM_PARENTHESES_NODE: {
10188 // ()
10189 // ^^
10190 //
10191 // (1)
10192 // ^^^
10193 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
10194
10195 if (cast->body != NULL) {
10196 PM_COMPILE(cast->body);
10197 }
10198 else if (!popped) {
10199 PUSH_INSN(ret, location, putnil);
10200 }
10201
10202 return;
10203 }
10204 case PM_PRE_EXECUTION_NODE: {
10205 // BEGIN {}
10206 // ^^^^^^^^
10208
10209 LINK_ANCHOR *outer_pre = scope_node->pre_execution_anchor;
10210 RUBY_ASSERT(outer_pre != NULL);
10211
10212 // BEGIN{} nodes can be nested, so here we're going to do the same thing
10213 // that we did for the top-level compilation where we create two
10214 // anchors and then join them in the correct order into the resulting
10215 // anchor.
10216 DECL_ANCHOR(inner_pre);
10217 scope_node->pre_execution_anchor = inner_pre;
10218
10219 DECL_ANCHOR(inner_body);
10220
10221 if (cast->statements != NULL) {
10222 const pm_node_list_t *body = &cast->statements->body;
10223
10224 for (size_t index = 0; index < body->size; index++) {
10225 pm_compile_node(iseq, body->nodes[index], inner_body, true, scope_node);
10226 }
10227 }
10228
10229 if (!popped) {
10230 PUSH_INSN(inner_body, location, putnil);
10231 }
10232
10233 // Now that everything has been compiled, join both anchors together
10234 // into the correct outer pre execution anchor, and reset the value so
10235 // that subsequent BEGIN{} nodes can be compiled correctly.
10236 PUSH_SEQ(outer_pre, inner_pre);
10237 PUSH_SEQ(outer_pre, inner_body);
10238 scope_node->pre_execution_anchor = outer_pre;
10239
10240 return;
10241 }
10242 case PM_POST_EXECUTION_NODE: {
10243 // END {}
10244 // ^^^^^^
10245 const rb_iseq_t *child_iseq;
10246 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
10247
10248 pm_scope_node_t next_scope_node;
10249 pm_scope_node_init(node, &next_scope_node, scope_node);
10250 child_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, lineno);
10251 pm_scope_node_destroy(&next_scope_node);
10252
10253 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
10254
10255 int is_index = ISEQ_BODY(iseq)->ise_size++;
10256 PUSH_INSN2(ret, location, once, child_iseq, INT2FIX(is_index));
10257 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) child_iseq);
10258 if (popped) PUSH_INSN(ret, location, pop);
10259
10260 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
10261
10262 return;
10263 }
10264 case PM_RANGE_NODE: {
10265 // 0..5
10266 // ^^^^
10267 const pm_range_node_t *cast = (const pm_range_node_t *) node;
10268 bool exclude_end = PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END);
10269
10270 if (pm_optimizable_range_item_p(cast->left) && pm_optimizable_range_item_p(cast->right)) {
10271 if (!popped) {
10272 const pm_node_t *left = cast->left;
10273 const pm_node_t *right = cast->right;
10274
10275 VALUE val = rb_range_new(
10276 (left && PM_NODE_TYPE_P(left, PM_INTEGER_NODE)) ? parse_integer((const pm_integer_node_t *) left) : Qnil,
10277 (right && PM_NODE_TYPE_P(right, PM_INTEGER_NODE)) ? parse_integer((const pm_integer_node_t *) right) : Qnil,
10278 exclude_end
10279 );
10280
10282 PUSH_INSN1(ret, location, putobject, val);
10283 }
10284 }
10285 else {
10286 if (cast->left != NULL) {
10287 PM_COMPILE(cast->left);
10288 }
10289 else if (!popped) {
10290 PUSH_INSN(ret, location, putnil);
10291 }
10292
10293 if (cast->right != NULL) {
10294 PM_COMPILE(cast->right);
10295 }
10296 else if (!popped) {
10297 PUSH_INSN(ret, location, putnil);
10298 }
10299
10300 if (!popped) {
10301 PUSH_INSN1(ret, location, newrange, INT2FIX(exclude_end ? 1 : 0));
10302 }
10303 }
10304 return;
10305 }
10306 case PM_RATIONAL_NODE: {
10307 // 1r
10308 // ^^
10309 if (!popped) {
10310 PUSH_INSN1(ret, location, putobject, parse_rational((const pm_rational_node_t *) node));
10311 }
10312 return;
10313 }
10314 case PM_REDO_NODE:
10315 // redo
10316 // ^^^^
10317 pm_compile_redo_node(iseq, &location, ret, popped, scope_node);
10318 return;
10319 case PM_REGULAR_EXPRESSION_NODE: {
10320 // /foo/
10321 // ^^^^^
10322 if (!popped) {
10323 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
10324 PUSH_INSN1(ret, location, putobject, regexp);
10325 }
10326 return;
10327 }
10328 case PM_RESCUE_NODE:
10329 // begin; rescue; end
10330 // ^^^^^^^
10331 pm_compile_rescue_node(iseq, (const pm_rescue_node_t *) node, &location, ret, popped, scope_node);
10332 return;
10333 case PM_RESCUE_MODIFIER_NODE: {
10334 // foo rescue bar
10335 // ^^^^^^^^^^^^^^
10337
10338 pm_scope_node_t rescue_scope_node;
10339 pm_scope_node_init((const pm_node_t *) cast, &rescue_scope_node, scope_node);
10340
10341 rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(
10342 &rescue_scope_node,
10343 rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label),
10344 ISEQ_TYPE_RESCUE,
10345 pm_node_line_number_cached(cast->rescue_expression, scope_node)
10346 );
10347
10348 pm_scope_node_destroy(&rescue_scope_node);
10349
10350 LABEL *lstart = NEW_LABEL(location.line);
10351 LABEL *lend = NEW_LABEL(location.line);
10352 LABEL *lcont = NEW_LABEL(location.line);
10353
10354 lstart->rescued = LABEL_RESCUE_BEG;
10355 lend->rescued = LABEL_RESCUE_END;
10356
10357 PUSH_LABEL(ret, lstart);
10358 PM_COMPILE_NOT_POPPED(cast->expression);
10359 PUSH_LABEL(ret, lend);
10360
10361 PUSH_INSN(ret, location, nop);
10362 PUSH_LABEL(ret, lcont);
10363 if (popped) PUSH_INSN(ret, location, pop);
10364
10365 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
10366 PUSH_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
10367 return;
10368 }
10369 case PM_RETURN_NODE:
10370 // return
10371 // ^^^^^^
10372 //
10373 // return 1
10374 // ^^^^^^^^
10375 pm_compile_return_node(iseq, (const pm_return_node_t *) node, &location, ret, popped, scope_node);
10376 return;
10377 case PM_RETRY_NODE: {
10378 // retry
10379 // ^^^^^
10380 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
10381 PUSH_INSN(ret, location, putnil);
10382 PUSH_INSN1(ret, location, throw, INT2FIX(TAG_RETRY));
10383 if (popped) PUSH_INSN(ret, location, pop);
10384 }
10385 else {
10386 COMPILE_ERROR(iseq, location.line, "Invalid retry");
10387 return;
10388 }
10389 return;
10390 }
10391 case PM_SCOPE_NODE:
10392 pm_compile_scope_node(iseq, (pm_scope_node_t *) node, &location, ret, popped);
10393 return;
10394 case PM_SELF_NODE: {
10395 // self
10396 // ^^^^
10397 if (!popped) {
10398 PUSH_INSN(ret, location, putself);
10399 }
10400 return;
10401 }
10402 case PM_SHAREABLE_CONSTANT_NODE: {
10403 // A value that is being written to a constant that is being marked as
10404 // shared depending on the current lexical context.
10406 pm_node_flags_t shareability = (cast->base.flags & (PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL | PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING | PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY));
10407
10408 switch (PM_NODE_TYPE(cast->write)) {
10409 case PM_CONSTANT_WRITE_NODE:
10410 pm_compile_constant_write_node(iseq, (const pm_constant_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10411 break;
10412 case PM_CONSTANT_AND_WRITE_NODE:
10413 pm_compile_constant_and_write_node(iseq, (const pm_constant_and_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10414 break;
10415 case PM_CONSTANT_OR_WRITE_NODE:
10416 pm_compile_constant_or_write_node(iseq, (const pm_constant_or_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10417 break;
10418 case PM_CONSTANT_OPERATOR_WRITE_NODE:
10419 pm_compile_constant_operator_write_node(iseq, (const pm_constant_operator_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10420 break;
10421 case PM_CONSTANT_PATH_WRITE_NODE:
10422 pm_compile_constant_path_write_node(iseq, (const pm_constant_path_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10423 break;
10424 case PM_CONSTANT_PATH_AND_WRITE_NODE:
10425 pm_compile_constant_path_and_write_node(iseq, (const pm_constant_path_and_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10426 break;
10427 case PM_CONSTANT_PATH_OR_WRITE_NODE:
10428 pm_compile_constant_path_or_write_node(iseq, (const pm_constant_path_or_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10429 break;
10430 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE:
10431 pm_compile_constant_path_operator_write_node(iseq, (const pm_constant_path_operator_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10432 break;
10433 default:
10434 rb_bug("Unexpected node type for shareable constant write: %s", pm_node_type(PM_NODE_TYPE(cast->write)));
10435 break;
10436 }
10437
10438 return;
10439 }
10440 case PM_SINGLETON_CLASS_NODE: {
10441 // class << self; end
10442 // ^^^^^^^^^^^^^^^^^^
10444
10445 pm_scope_node_t next_scope_node;
10446 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
10447 const rb_iseq_t *child_iseq = NEW_ISEQ(&next_scope_node, rb_fstring_lit("singleton class"), ISEQ_TYPE_CLASS, location.line);
10448 pm_scope_node_destroy(&next_scope_node);
10449
10450 PM_COMPILE_NOT_POPPED(cast->expression);
10451 PUSH_INSN(ret, location, putnil);
10452
10453 ID singletonclass;
10454 CONST_ID(singletonclass, "singletonclass");
10455
10456 /* `class << self` in a class body and `class << Foo` (constant
10457 receiver) are stable. All other forms are potentially dynamic. */
10458 int sclass_flags = VM_DEFINECLASS_TYPE_SINGLETON_CLASS;
10459 if (!(PM_NODE_TYPE_P(cast->expression, PM_SELF_NODE) &&
10460 ISEQ_BODY(iseq)->type == ISEQ_TYPE_CLASS) &&
10461 !pm_cpath_const_p(cast->expression)) {
10462 sclass_flags |= VM_DEFINECLASS_FLAG_DYNAMIC_CREF;
10463 }
10464
10465 PUSH_INSN3(ret, location, defineclass, ID2SYM(singletonclass), child_iseq, INT2FIX(sclass_flags));
10466
10467 if (popped) PUSH_INSN(ret, location, pop);
10468 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) child_iseq);
10469
10470 return;
10471 }
10472 case PM_SOURCE_ENCODING_NODE: {
10473 // __ENCODING__
10474 // ^^^^^^^^^^^^
10475 if (!popped) {
10476 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10477 PUSH_INSN1(ret, location, putobject, value);
10478 }
10479 return;
10480 }
10481 case PM_SOURCE_FILE_NODE: {
10482 // __FILE__
10483 // ^^^^^^^^
10484 if (!popped) {
10485 const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node;
10486
10487 int string_type;
10488 if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FROZEN)) {
10489 string_type = ISEQ_FROZEN_STRING_LITERAL_ENABLED;
10490 }
10491 else if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_MUTABLE)) {
10492 string_type = ISEQ_FROZEN_STRING_LITERAL_DISABLED;
10493 }
10494 else {
10495 string_type = ISEQ_FROZEN_STRING_LITERAL_UNSET;
10496 }
10497
10498 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10499 PUSH_INSN1(ret, location, putobject, INT2FIX(string_type));
10500 PUSH_SEND(ret, location, id_core_iseq_path, INT2FIX(1));
10501 }
10502 return;
10503 }
10504 case PM_SOURCE_LINE_NODE: {
10505 // __LINE__
10506 // ^^^^^^^^
10507 if (!popped) {
10508 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10509 PUSH_INSN1(ret, location, putobject, value);
10510 }
10511 return;
10512 }
10513 case PM_SPLAT_NODE: {
10514 // foo(*bar)
10515 // ^^^^
10516 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
10517 if (cast->expression) {
10518 PM_COMPILE(cast->expression);
10519 }
10520
10521 if (!popped) {
10522 PUSH_INSN1(ret, location, splatarray, Qtrue);
10523 }
10524 return;
10525 }
10526 case PM_STATEMENTS_NODE: {
10527 // A list of statements.
10528 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
10529 const pm_node_list_t *body = &cast->body;
10530
10531 if (body->size > 0) {
10532 for (size_t index = 0; index < body->size - 1; index++) {
10533 PM_COMPILE_POPPED(body->nodes[index]);
10534 }
10535 PM_COMPILE(body->nodes[body->size - 1]);
10536 }
10537 else {
10538 PUSH_INSN(ret, location, putnil);
10539 }
10540 return;
10541 }
10542 case PM_STRING_NODE: {
10543 // "foo"
10544 // ^^^^^
10545 if (!popped) {
10546 const pm_string_node_t *cast = (const pm_string_node_t *) node;
10547 VALUE value = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
10548
10549 if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_FROZEN)) {
10550 PUSH_INSN1(ret, location, putobject, value);
10551 }
10552 else if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_MUTABLE)) {
10553 PUSH_INSN1(ret, location, dupstring, value);
10554 }
10555 else {
10556 PUSH_INSN1(ret, location, dupchilledstring, value);
10557 }
10558 }
10559 return;
10560 }
10561 case PM_SUPER_NODE:
10562 // super()
10563 // super(foo)
10564 // super(...)
10565 pm_compile_super_node(iseq, (const pm_super_node_t *) node, &location, ret, popped, scope_node);
10566 return;
10567 case PM_SYMBOL_NODE: {
10568 // :foo
10569 // ^^^^
10570 if (!popped) {
10571 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10572 PUSH_INSN1(ret, location, putobject, value);
10573 }
10574 return;
10575 }
10576 case PM_TRUE_NODE: {
10577 // true
10578 // ^^^^
10579 if (!popped) {
10580 PUSH_INSN1(ret, location, putobject, Qtrue);
10581 }
10582 return;
10583 }
10584 case PM_UNDEF_NODE: {
10585 // undef foo
10586 // ^^^^^^^^^
10587 const pm_undef_node_t *cast = (const pm_undef_node_t *) node;
10588 const pm_node_list_t *names = &cast->names;
10589
10590 for (size_t index = 0; index < names->size; index++) {
10591 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10592 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
10593
10594 PM_COMPILE_NOT_POPPED(names->nodes[index]);
10595 PUSH_SEND(ret, location, id_core_undef_method, INT2NUM(2));
10596
10597 if (index < names->size - 1) {
10598 PUSH_INSN(ret, location, pop);
10599 }
10600 }
10601
10602 if (popped) PUSH_INSN(ret, location, pop);
10603 return;
10604 }
10605 case PM_UNLESS_NODE: {
10606 // unless foo; bar end
10607 // ^^^^^^^^^^^^^^^^^^^
10608 //
10609 // bar unless foo
10610 // ^^^^^^^^^^^^^^
10611 const pm_unless_node_t *cast = (const pm_unless_node_t *) node;
10612 const pm_statements_node_t *statements = NULL;
10613 if (cast->else_clause != NULL) {
10614 statements = ((const pm_else_node_t *) cast->else_clause)->statements;
10615 }
10616
10617 pm_compile_conditional(iseq, &location, PM_UNLESS_NODE, (const pm_node_t *) cast, statements, (const pm_node_t *) cast->statements, cast->predicate, ret, popped, scope_node);
10618 return;
10619 }
10620 case PM_UNTIL_NODE: {
10621 // until foo; bar end
10622 // ^^^^^^^^^^^^^^^^^
10623 //
10624 // bar until foo
10625 // ^^^^^^^^^^^^^
10626 const pm_until_node_t *cast = (const pm_until_node_t *) node;
10627 pm_compile_loop(iseq, &location, cast->base.flags, PM_UNTIL_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node);
10628 return;
10629 }
10630 case PM_WHILE_NODE: {
10631 // while foo; bar end
10632 // ^^^^^^^^^^^^^^^^^^
10633 //
10634 // bar while foo
10635 // ^^^^^^^^^^^^^
10636 const pm_while_node_t *cast = (const pm_while_node_t *) node;
10637 pm_compile_loop(iseq, &location, cast->base.flags, PM_WHILE_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node);
10638 return;
10639 }
10640 case PM_X_STRING_NODE: {
10641 // `foo`
10642 // ^^^^^
10643 const pm_x_string_node_t *cast = (const pm_x_string_node_t *) node;
10644 VALUE value = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
10645
10646 PUSH_INSN(ret, location, putself);
10647 PUSH_INSN1(ret, location, putobject, value);
10648 PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
10649 if (popped) PUSH_INSN(ret, location, pop);
10650
10651 return;
10652 }
10653 case PM_YIELD_NODE:
10654 // yield
10655 // ^^^^^
10656 //
10657 // yield 1
10658 // ^^^^^^^
10659 pm_compile_yield_node(iseq, (const pm_yield_node_t *) node, &location, ret, popped, scope_node);
10660 return;
10661 default:
10662 rb_raise(rb_eNotImpError, "node type %s not implemented", pm_node_type(PM_NODE_TYPE(node)));
10663 return;
10664 }
10665}
10666
10667#undef PM_CONTAINER_P
10668
10670static inline bool
10671pm_iseq_pre_execution_p(rb_iseq_t *iseq)
10672{
10673 switch (ISEQ_BODY(iseq)->type) {
10674 case ISEQ_TYPE_TOP:
10675 case ISEQ_TYPE_EVAL:
10676 case ISEQ_TYPE_MAIN:
10677 return true;
10678 default:
10679 return false;
10680 }
10681}
10682
10690VALUE
10691pm_iseq_compile_node(rb_iseq_t *iseq, pm_scope_node_t *node)
10692{
10693 DECL_ANCHOR(ret);
10694
10695 if (pm_iseq_pre_execution_p(iseq)) {
10696 // Because these ISEQs can have BEGIN{}, we're going to create two
10697 // anchors to compile them, a "pre" and a "body". We'll mark the "pre"
10698 // on the scope node so that when BEGIN{} is found, its contents will be
10699 // added to the "pre" anchor.
10700 DECL_ANCHOR(pre);
10701 node->pre_execution_anchor = pre;
10702
10703 // Now we'll compile the body as normal. We won't compile directly into
10704 // the "ret" anchor yet because we want to add the "pre" anchor to the
10705 // beginning of the "ret" anchor first.
10706 DECL_ANCHOR(body);
10707 pm_compile_node(iseq, (const pm_node_t *) node, body, false, node);
10708
10709 // Now we'll join both anchors together so that the content is in the
10710 // correct order.
10711 PUSH_SEQ(ret, pre);
10712 PUSH_SEQ(ret, body);
10713 }
10714 else {
10715 // In other circumstances, we can just compile the node directly into
10716 // the "ret" anchor.
10717 pm_compile_node(iseq, (const pm_node_t *) node, ret, false, node);
10718 }
10719
10720 CHECK(iseq_setup_insn(iseq, ret));
10721 return iseq_setup(iseq, ret);
10722}
10723
10724void
10725pm_parse_result_init(pm_parse_result_t *result)
10726{
10727 memset(result, 0, sizeof(pm_parse_result_t));
10728 result->arena = pm_arena_new();
10729 result->options = pm_options_new();
10730 pm_options_line_set(result->options, 1);
10731}
10732
10737void
10738pm_parse_result_free(pm_parse_result_t *result)
10739{
10740 if (result->parsed) {
10741 SIZED_FREE_N(result->node.constants, pm_parser_constants_size(result->node.parser));
10742 pm_scope_node_destroy(&result->node);
10743 }
10744
10745 if (result->parser) pm_parser_free(result->parser);
10746 pm_arena_free(result->arena);
10747 if (result->source) pm_source_free(result->source);
10748 pm_options_free(result->options);
10749}
10750
10755static VALUE
10756pm_parse_process_error(const pm_parse_result_t *result)
10757{
10758 const pm_parser_t *parser = result->parser;
10759 pm_buffer_t *buffer = pm_buffer_new();
10760 if (buffer == NULL) {
10761 return rb_exc_new_cstr(rb_eNoMemError, "failed to allocate memory");
10762 }
10763
10764 pm_errors_format_type_t format_type;
10765 if (rb_stderr_tty_p()) {
10766 const char *no_color = getenv("NO_COLOR");
10767 if (no_color == NULL || no_color[0] == '\0') {
10768 format_type = PM_ERRORS_FORMAT_COLOR;
10769 } else {
10770 format_type = PM_ERRORS_FORMAT_STYLE;
10771 }
10772 } else {
10773 format_type = PM_ERRORS_FORMAT_PLAIN;
10774 }
10775
10776 pm_error_level_t error_level = pm_errors_format(parser, buffer, format_type);
10777
10778 rb_encoding *message_encoding = (error_level == PM_ERROR_LEVEL_LOAD) ? rb_locale_encoding() : rb_enc_find(pm_parser_encoding_name(parser));
10779 VALUE message = rb_enc_str_new(pm_buffer_value(buffer), (rb_len_t) pm_buffer_length(buffer), message_encoding);
10780 pm_buffer_free(buffer);
10781
10782 VALUE error = Qnil;
10783 switch (error_level) {
10784 case PM_ERROR_LEVEL_SYNTAX: {
10785 error = rb_exc_new_str(rb_eSyntaxError, message);
10786
10787 const pm_string_t *filepath = pm_parser_filepath(result->parser);
10788 rb_encoding *filepath_encoding = result->node.filepath_encoding != NULL ? result->node.filepath_encoding : rb_utf8_encoding();
10789
10790 VALUE path = rb_enc_str_new((const char *) pm_string_source(filepath), pm_string_length(filepath), filepath_encoding);
10791 rb_ivar_set(error, rb_intern_const("@path"), path);
10792 break;
10793 }
10795 error = rb_exc_new_str(rb_eArgError, message);
10796 break;
10798 error = rb_exc_new_str(rb_eLoadError, message);
10799 rb_ivar_set(error, rb_intern_const("@path"), Qnil);
10800 break;
10801 }
10802
10803 return error;
10804}
10805
10813static uint64_t
10814pm_source_hash(const pm_parser_t *parser)
10815{
10816 uint64_t checksum = pm_parser_checksum(parser);
10817 return checksum == 0 ? 1 : checksum;
10818}
10819
10825static VALUE
10826pm_parse_process(pm_parse_result_t *result, pm_node_t *node, VALUE *script_lines)
10827{
10828 pm_parser_t *parser = result->parser;
10829
10830 // First, set up the scope node so that the AST node is attached and can be
10831 // freed regardless of whether or we return an error.
10832 pm_scope_node_t *scope_node = &result->node;
10833 rb_encoding *filepath_encoding = scope_node->filepath_encoding;
10834 int coverage_enabled = scope_node->coverage_enabled;
10835
10836 pm_scope_node_init(node, scope_node, NULL);
10837 scope_node->filepath_encoding = filepath_encoding;
10838
10839 const char *encoding_name = pm_parser_encoding_name(parser);
10840 rb_encoding *encoding = rb_enc_find(encoding_name);
10841 if (!encoding) rb_bug("Encoding not found %s!", encoding_name);
10842 scope_node->encoding = encoding;
10843
10844 scope_node->coverage_enabled = coverage_enabled;
10845
10846 // If RubyVM.keep_script_lines is set to true, then we need to create that
10847 // array of script lines here.
10848 if (script_lines != NULL) {
10849 const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser);
10850 *script_lines = rb_ary_new_capa(line_offsets->size);
10851
10852 for (size_t index = 0; index < line_offsets->size; index++) {
10853 size_t offset = line_offsets->offsets[index];
10854 size_t length = index == line_offsets->size - 1 ? ((size_t) (pm_parser_end(parser) - (pm_parser_start(parser) + offset))) : (line_offsets->offsets[index + 1] - offset);
10855 rb_ary_push(*script_lines, rb_enc_str_new((const char *) pm_parser_start(parser) + offset, length, encoding));
10856 }
10857
10858 scope_node->script_lines = script_lines;
10859 }
10860
10861 const char *filepath = (const char *) pm_string_source(pm_parser_filepath(parser));
10862
10863 for (
10864 const pm_diagnostic_t *diagnostic = pm_parser_warnings_first(parser);
10865 diagnostic != NULL;
10866 diagnostic = pm_diagnostic_next(diagnostic)
10867 ) {
10868 pm_location_t loc = pm_diagnostic_location(diagnostic);
10869 int line = pm_location_line_number(parser, &loc);
10870
10871 if (pm_diagnostic_warning_level(diagnostic) == PM_WARNING_LEVEL_VERBOSE) {
10872 rb_enc_compile_warning(encoding, filepath, line, "%s", pm_diagnostic_message(diagnostic));
10873 }
10874 else {
10875 rb_enc_compile_warn(encoding, filepath, line, "%s", pm_diagnostic_message(diagnostic));
10876 }
10877 }
10878
10879 // If there are errors, raise an appropriate error and free the result.
10880 if (pm_parser_errors_size(parser) > 0) {
10881 VALUE error = pm_parse_process_error(result);
10882
10883 // TODO: We need to set the backtrace.
10884 // rb_funcallv(error, rb_intern("set_backtrace"), 1, &path);
10885 return error;
10886 }
10887
10888 // Now set up the constant pool and intern all of the various constants into
10889 // their corresponding IDs.
10890 scope_node->parser = parser;
10891 scope_node->source_hash = pm_source_hash(parser);
10892 scope_node->options = result->options;
10893 scope_node->line_offsets = pm_parser_line_offsets(parser);
10894 scope_node->start_line = pm_parser_start_line(parser);
10895
10896 size_t constants_size = pm_parser_constants_size(parser);
10897 ID *constants = constants_size ? xmalloc(constants_size * sizeof(ID)) : NULL;
10898 scope_node->constants = constants;
10899
10900 for (size_t index = 0; index < constants_size; index++) {
10901 const pm_constant_t *constant = pm_parser_constant_at(parser, index);
10902 constants[index] = rb_intern3((const char *) pm_constant_start(constant), pm_constant_length(constant), encoding);
10903 }
10904
10905 // If we got here, this is a success and we can return Qnil to indicate that
10906 // no error should be raised.
10907 result->parsed = true;
10908 return Qnil;
10909}
10910
10915static void
10916pm_options_frozen_string_literal_init(pm_options_t *options)
10917{
10918 int frozen_string_literal = rb_iseq_opt_frozen_string_literal();
10919
10920 switch (frozen_string_literal) {
10921 case ISEQ_FROZEN_STRING_LITERAL_UNSET:
10922 break;
10923 case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
10924 pm_options_frozen_string_literal_set(options, false);
10925 break;
10926 case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
10927 pm_options_frozen_string_literal_set(options, true);
10928 break;
10929 default:
10930 rb_bug("pm_options_frozen_string_literal_init: invalid frozen_string_literal=%d", frozen_string_literal);
10931 break;
10932 }
10933}
10934
10939static inline VALUE
10940pm_parse_file_script_lines(const pm_scope_node_t *scope_node, const pm_parser_t *parser)
10941{
10942 const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser);
10943 const char *start = (const char *) pm_parser_start(parser);
10944 const char *end = (const char *) pm_parser_end(parser);
10945
10946 // If we end exactly on a newline, then there's no need to push on a final
10947 // segment. If we don't, then we need to push on the last offset up to the
10948 // end of the string.
10949 size_t last_offset = line_offsets->offsets[line_offsets->size - 1];
10950 bool last_push = start + last_offset != end;
10951
10952 // Create the ruby strings that represent the lines of the source.
10953 VALUE lines = rb_ary_new_capa(line_offsets->size - (last_push ? 0 : 1));
10954
10955 for (size_t index = 0; index < line_offsets->size - 1; index++) {
10956 size_t offset = line_offsets->offsets[index];
10957 size_t length = line_offsets->offsets[index + 1] - offset;
10958
10959 rb_ary_push(lines, rb_enc_str_new(start + offset, length, scope_node->encoding));
10960 }
10961
10962 // Push on the last line if we need to.
10963 if (last_push) {
10964 rb_ary_push(lines, rb_enc_str_new(start + last_offset, end - (start + last_offset), scope_node->encoding));
10965 }
10966
10967 return lines;
10968}
10969
10971 VALUE path;
10972 VALUE io;
10973 int open_mode;
10974 int fd;
10975};
10976
10977static VALUE
10978close_file(VALUE args)
10979{
10980 struct load_from_fd_args *arg = (void *)args;
10981 if (arg->fd != -1) {
10982 close(arg->fd);
10983 }
10984 else if (!NIL_P(arg->io)) {
10985 rb_io_close(arg->io);
10986 }
10987 return Qnil;
10988}
10989
10990static VALUE
10991load_content(VALUE args)
10992{
10993 struct load_from_fd_args *arg = (void *)args;
10994 VALUE io = rb_io_fdopen(arg->fd, arg->open_mode, RSTRING_PTR(arg->path));
10995 arg->io = io;
10996 arg->fd = -1;
10998 return rb_funcall(io, rb_intern("read"), 0);
10999}
11000
11005VALUE
11006pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error)
11007{
11008 pm_source_init_result_t init_result;
11009 result->source = pm_source_mapped_new(RSTRING_PTR(filepath), O_RDONLY | O_NONBLOCK, &init_result);
11010
11011 if (init_result == PM_SOURCE_INIT_SUCCESS) {
11012 pm_options_frozen_string_literal_init(result->options);
11013 return Qnil;
11014 }
11015
11016 int err;
11017
11018 // For non-regular files (pipes, character devices), we need to read
11019 // through Ruby IO to properly release the GVL while waiting for data.
11020 if (init_result == PM_SOURCE_INIT_ERROR_NON_REGULAR) {
11021 struct load_from_fd_args args = {
11022 .path = filepath,
11023 .open_mode = O_RDONLY | O_NONBLOCK,
11024 .fd = rb_cloexec_open(RSTRING_PTR(filepath), args.open_mode, 0),
11025 .io = Qnil,
11026 };
11027 if (args.fd == -1) goto error_generic;
11028 VALUE contents = rb_ensure(load_content, (VALUE)&args, close_file, (VALUE)&args);
11029
11030 if (!RB_TYPE_P(contents, T_STRING)) goto error_generic;
11031
11032 rb_len_t len = RSTRING_LEN(contents);
11033 if (len < 0) goto error_generic;
11034
11035 size_t length = (size_t) len;
11036 uint8_t *source_data = xmalloc(length);
11037 memcpy(source_data, RSTRING_PTR(contents), length);
11038 result->source = pm_source_owned_new(source_data, length);
11039
11040 pm_options_frozen_string_literal_init(result->options);
11041 return Qnil;
11042 }
11043
11044 if (init_result == PM_SOURCE_INIT_ERROR_DIRECTORY) {
11045 err = EISDIR;
11046 } else {
11047error_generic:
11048#ifdef _WIN32
11049 err = rb_w32_map_errno(GetLastError());
11050#else
11051 err = errno;
11052#endif
11053 }
11054
11055 VALUE error;
11056 if (load_error) {
11057 VALUE message = rb_str_buf_new_cstr(strerror(err));
11058 rb_str_cat2(message, " -- ");
11059 rb_str_append(message, filepath);
11060
11061 error = rb_exc_new3(rb_eLoadError, message);
11062 rb_ivar_set(error, rb_intern_const("@path"), filepath);
11063 } else {
11064 error = rb_syserr_new_str(err, filepath);
11065 }
11066
11067 return error;
11068}
11069
11076VALUE
11077pm_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines)
11078{
11079 result->node.filepath_encoding = rb_enc_get(filepath);
11080 pm_options_filepath_set(result->options, RSTRING_PTR(filepath));
11081 RB_GC_GUARD(filepath);
11082
11083 pm_options_version_for_current_ruby_set(result->options);
11084
11085 result->parser = pm_parser_new(result->arena, pm_source_source(result->source), pm_source_length(result->source), result->options);
11086 pm_node_t *node = pm_parse(result->parser);
11087
11088 VALUE error = pm_parse_process(result, node, script_lines);
11089
11090 // If we're parsing a filepath, then we need to potentially support the
11091 // SCRIPT_LINES__ constant, which can be a hash that has an array of lines
11092 // of every read file.
11093 ID id_script_lines = rb_intern("SCRIPT_LINES__");
11094
11095 if (rb_const_defined_at(rb_cObject, id_script_lines)) {
11096 VALUE constant_script_lines = rb_const_get_at(rb_cObject, id_script_lines);
11097
11098 if (RB_TYPE_P(constant_script_lines, T_HASH)) {
11099 rb_hash_aset(constant_script_lines, filepath, pm_parse_file_script_lines(&result->node, result->parser));
11100 }
11101 }
11102
11103 return error;
11104}
11105
11110VALUE
11111pm_load_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines)
11112{
11113 VALUE error = pm_load_file(result, filepath, false);
11114 if (NIL_P(error)) {
11115 error = pm_parse_file(result, filepath, script_lines);
11116 }
11117
11118 return error;
11119}
11120
11127VALUE
11128pm_parse_string(pm_parse_result_t *result, VALUE source, VALUE filepath, VALUE *script_lines)
11129{
11130 rb_encoding *encoding = rb_enc_get(source);
11131 if (!rb_enc_asciicompat(encoding)) {
11132 return rb_exc_new_cstr(rb_eArgError, "invalid source encoding");
11133 }
11134
11135 pm_options_frozen_string_literal_init(result->options);
11136 result->source = pm_source_constant_new((const uint8_t *) RSTRING_PTR(source), (size_t) RSTRING_LEN(source));
11137 pm_options_encoding_set(result->options, rb_enc_name(encoding));
11138
11139 result->node.filepath_encoding = rb_enc_get(filepath);
11140 pm_options_filepath_set(result->options, RSTRING_PTR(filepath));
11141
11142 pm_options_version_for_current_ruby_set(result->options);
11143
11144 result->parser = pm_parser_new(result->arena, pm_source_source(result->source), pm_source_length(result->source), result->options);
11145 pm_node_t *node = pm_parse(result->parser);
11146
11147 VALUE error = pm_parse_process(result, node, script_lines);
11148
11149 RB_GC_GUARD(source);
11150 RB_GC_GUARD(filepath);
11151 return error;
11152}
11153
11154typedef struct {
11155 uint32_t node_id;
11156 const pm_node_t *node;
11158
11159static pm_visit_result_t
11160pm_node_find(const pm_node_t *node, void *data)
11161{
11162 pm_node_find_context_t *context = data;
11163
11164 if (node->node_id == context->node_id) {
11165 context->node = node;
11166 return PM_VISIT_STOP;
11167 }
11168
11169 return PM_VISIT_DESCEND;
11170}
11171
11175bool
11176pm_source_hash_matches(VALUE source, VALUE filepath, uint64_t source_hash)
11177{
11178 pm_parse_result_t result;
11179 pm_parse_result_init(&result);
11180 pm_parse_string(&result, source, filepath, NULL);
11181
11182 bool matches = result.parser != NULL && pm_source_hash(result.parser) == source_hash;
11183 pm_parse_result_free(&result);
11184
11185 RB_GC_GUARD(source);
11186 RB_GC_GUARD(filepath);
11187 return matches;
11188}
11189
11196bool
11197pm_node_source_location(VALUE source, VALUE filepath, int start_line,
11198 int node_id, uint64_t source_hash, rb_code_location_t *location)
11199{
11200 pm_parse_result_t result;
11201 pm_parse_result_init(&result);
11202
11203 pm_options_line_set(result.options, start_line);
11204 VALUE error = pm_parse_string(&result, source, filepath, NULL);
11205
11206 if (result.parser != NULL && pm_source_hash(result.parser) != source_hash) {
11207 pm_parse_result_free(&result);
11208 rb_raise(rb_eRuntimeError, "source has been modified");
11209 }
11210
11211 if (!NIL_P(error)) {
11212 pm_parse_result_free(&result);
11213 rb_exc_raise(error);
11214 }
11215
11216 pm_node_find_context_t context = {
11217 .node_id = (uint32_t) node_id,
11218 .node = NULL
11219 };
11220 pm_visit_node(result.node.ast_node, pm_node_find, &context);
11221
11222 bool found = context.node != NULL;
11223 if (found) {
11224 *location = pm_code_location(&result.node, context.node);
11225 }
11226
11227 RB_GC_GUARD(source);
11228 RB_GC_GUARD(filepath);
11229
11230 pm_parse_result_free(&result);
11231 return found;
11232}
11233
11234VALUE rb_io_gets_limit_internal(VALUE io, rb_len_t limit);
11235
11243static int
11244pm_parse_stdin_eof(void *stream)
11245{
11246 return RTEST(rb_io_eof((VALUE) stream));
11247}
11248
11257#define MAX_ENC_LEN 6
11258
11264static char *
11265pm_parse_stdin_fgets(char *string, int size, void *stream)
11266{
11267 RUBY_ASSERT(size > MAX_ENC_LEN);
11268
11269 /*
11270 * Request fewer bytes than the buffer can hold. Ruby's line reader may
11271 * return more bytes than requested when the limit falls in the middle of a
11272 * multi-byte character, and the reserved headroom guarantees the result
11273 * still fits.
11274 */
11275 VALUE line = rb_io_gets_limit_internal((VALUE) stream, size - MAX_ENC_LEN);
11276 if (NIL_P(line)) {
11277 return NULL;
11278 }
11279
11280 const char *cstr = RSTRING_PTR(line);
11281 rb_len_t length = RSTRING_LEN(line);
11282
11283 /*
11284 * Defensively clamp the copy. The line reader relaxes the limit to avoid
11285 * splitting a multi-byte character, so the returned string can be longer
11286 * than requested; we must never write past the caller's buffer. One byte
11287 * is reserved for the NUL terminator.
11288 */
11289 if (length > (rb_len_t) (size - 1)) {
11290 length = (rb_len_t) (size - 1);
11291 }
11292
11293 memcpy(string, cstr, (size_t) length);
11294 string[length] = '\0';
11295
11296 return string;
11297}
11298
11299#undef MAX_ENC_LEN
11300
11301// We need access to this function when we're done parsing stdin.
11302void rb_reset_argf_lineno(long n);
11303
11309VALUE
11310pm_parse_stdin(pm_parse_result_t *result)
11311{
11312 pm_options_frozen_string_literal_init(result->options);
11313
11314 result->source = pm_source_stream_new((void *) rb_stdin, pm_parse_stdin_fgets, pm_parse_stdin_eof);
11315 pm_node_t *node = pm_parse_stream(&result->parser, result->arena, result->source, result->options);
11316
11317 // When we're done parsing, we reset $. because we don't want the fact that
11318 // we went through an IO object to be visible to the user.
11319 rb_reset_argf_lineno(0);
11320
11321 return pm_parse_process(result, node, NULL);
11322}
11323
11324#define PM_VERSION_FOR_RELEASE(major, minor) PM_VERSION_FOR_RELEASE_IMPL(major, minor)
11325#define PM_VERSION_FOR_RELEASE_IMPL(major, minor) #major "." #minor
11326
11327void pm_options_version_for_current_ruby_set(pm_options_t *options) {
11328 const char *version = PM_VERSION_FOR_RELEASE(RUBY_API_VERSION_MAJOR, RUBY_API_VERSION_MINOR);
11329 pm_options_version_set(options, version, strlen(version));
11330}
11331
11332#undef NEW_ISEQ
11333#define NEW_ISEQ OLD_ISEQ
11334
11335#undef NEW_CHILD_ISEQ
11336#define NEW_CHILD_ISEQ OLD_CHILD_ISEQ
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
uint32_t pm_constant_id_t
A constant id is a unique identifier for a constant in the constant pool.
static PRISM_INLINE size_t pm_constant_id_index(pm_constant_id_t constant_id)
Returns the index of the constant with the given id among the constants of the parser that produced i...
@ PM_WARNING_LEVEL_VERBOSE
For warnings which should be emitted if $VERBOSE == true.
Definition diagnostic.h:46
pm_error_level_t
The levels of errors generated during parsing.
Definition diagnostic.h:27
@ PM_ERROR_LEVEL_ARGUMENT
For errors that should raise an argument error.
Definition diagnostic.h:32
@ PM_ERROR_LEVEL_LOAD
For errors that should raise a load error.
Definition diagnostic.h:35
@ PM_ERROR_LEVEL_SYNTAX
For errors that should raise a syntax error.
Definition diagnostic.h:29
pm_errors_format_type_t
The type of formatting to use when formatting errors.
@ PM_ERRORS_FORMAT_PLAIN
Format errors in a plain format with no colors or styles.
@ PM_ERRORS_FORMAT_COLOR
Format errors in a color format with bold and colors.
@ PM_ERRORS_FORMAT_STYLE
Format errors in a style format with bold only.
#define RUBY_EVENT_END
Encountered an end of a class clause.
Definition event.h:40
#define RUBY_EVENT_B_RETURN
Encountered a next statement.
Definition event.h:56
#define RUBY_EVENT_CLASS
Encountered a new class.
Definition event.h:39
#define RUBY_EVENT_LINE
Encountered a new line.
Definition event.h:38
#define RUBY_EVENT_RETURN
Encountered a return statement.
Definition event.h:42
#define RUBY_EVENT_B_CALL
Encountered an yield statement.
Definition event.h:55
#define RUBY_EVENT_CALL
A method, written in Ruby, is called.
Definition event.h:41
#define RUBY_EVENT_RESCUE
Encountered a rescue statement.
Definition event.h:61
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1700
#define ALLOCV
Old name of RB_ALLOCV.
Definition memory.h:404
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:400
#define RFLOAT_VALUE
Old name of rb_float_value.
Definition double.h:28
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define rb_str_cat2
Old name of rb_str_cat_cstr.
Definition string.h:1708
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
#define ULONG2NUM
Old name of RB_ULONG2NUM.
Definition long.h:60
#define FIXABLE
Old name of RB_FIXABLE.
Definition fixnum.h:25
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define ZALLOC_N
Old name of RB_ZALLOC_N.
Definition memory.h:401
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
#define ALLOC_N
Old name of RB_ALLOC_N.
Definition memory.h:399
#define rb_exc_new3
Old name of rb_exc_new_str.
Definition error.h:38
#define FLONUM_P
Old name of RB_FLONUM_P.
#define Qtrue
Old name of RUBY_Qtrue.
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define NIL_P
Old name of RB_NIL_P.
#define ALLOCV_N
Old name of RB_ALLOCV_N.
Definition memory.h:405
#define DBL2NUM
Old name of rb_float_new.
Definition double.h:29
#define UINT2NUM
Old name of RB_UINT2NUM.
Definition int.h:46
#define NUM2LEN
Old name of RB_NUM2LEN.
Definition len_t.h:34
#define CONST_ID
Old name of RUBY_CONST_ID.
Definition symbol.h:47
#define ALLOCV_END
Old name of RB_ALLOCV_END.
Definition memory.h:406
#define ruby_debug
This variable controls whether the interpreter is in debug mode.
Definition error.h:487
VALUE rb_eNotImpError
NotImplementedError exception.
Definition error.c:1467
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:678
VALUE rb_eStandardError
StandardError exception.
Definition error.c:1454
VALUE rb_syserr_new_str(int n, VALUE arg)
Identical to rb_syserr_new(), except it takes the message in Ruby's String instead of C's.
Definition error.c:4047
VALUE rb_eNoMemError
NoMemoryError exception.
Definition error.c:1468
VALUE rb_eLoadError
LoadError exception.
Definition error.c:1475
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1457
VALUE rb_eNoMatchingPatternError
NoMatchingPatternError exception.
Definition error.c:1470
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1455
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:464
VALUE rb_eNoMatchingPatternKeyError
NoMatchingPatternKeyError exception.
Definition error.c:1471
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Identical to rb_exc_new_cstr(), except it takes a Ruby's string instead of C's.
Definition error.c:1508
VALUE rb_eSyntaxError
SyntaxError exception.
Definition error.c:1474
VALUE rb_obj_reveal(VALUE obj, VALUE klass)
Make a hidden object visible again.
Definition object.c:103
VALUE rb_cArray
Array class.
VALUE rb_cObject
Object class.
Definition object.c:60
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:94
VALUE rb_stdin
STDIN constant.
Definition io.c:194
VALUE rb_cHash
Hash class.
Definition hash.c:123
VALUE rb_obj_freeze(VALUE obj)
Same as RB_OBJ_FREEZE(), but returns the given object.
Definition object.c:1315
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:504
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:492
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1148
VALUE rb_ary_entry(VALUE ary, rb_len_t off)
Queries an element of an array.
VALUE rb_ary_new_capa(rb_len_t capa)
Identical to rb_ary_new(), except it additionally specifies how many rooms of objects it should alloc...
void rb_ary_store(VALUE ary, rb_len_t key, VALUE val)
Destructively stores the passed value to the passed array's passed index.
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_hidden_new(rb_len_t capa)
Allocates a hidden (no class) empty array.
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
VALUE rb_ary_cat(VALUE ary, const VALUE *train, rb_len_t len)
Destructively appends multiple elements at the end of the array.
VALUE rb_ary_join(VALUE ary, VALUE sep)
Recursively stringises the elements of the passed array, flattens that result, then joins the sequenc...
#define INTEGER_PACK_NATIVE_BYTE_ORDER
Means either INTEGER_PACK_MSBYTE_FIRST or INTEGER_PACK_LSBYTE_FIRST, depending on the host processor'...
Definition bignum.h:550
#define INTEGER_PACK_LSWORD_FIRST
Stores/interprets the least significant word as the first word.
Definition bignum.h:532
VALUE rb_io_fdopen(int fd, int flags, const char *path)
Creates an IO instance whose backend is the given file descriptor.
Definition io.c:9571
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Opens a file that closes on exec.
Definition io.c:337
VALUE rb_io_eof(VALUE io)
Queries if the passed IO is at the end of file.
Definition io.c:2771
VALUE rb_io_close(VALUE io)
Closes the IO.
Definition io.c:5985
VALUE rb_range_new(VALUE beg, VALUE end, int excl)
Creates a new Range.
Definition range.c:77
VALUE rb_rational_new(VALUE num, VALUE den)
Constructs a Rational, with reduction.
Definition rational.c:2006
VALUE rb_str_append(VALUE dst, VALUE src)
Identical to rb_str_buf_append(), except it converts the right hand side before concatenating.
Definition string.c:3938
VALUE rb_str_tmp_new(rb_len_t len)
Allocates a "temporary" string.
Definition string.c:1811
#define rb_exc_new_cstr(exc, str)
Identical to rb_exc_new(), except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1695
#define rb_str_buf_new_cstr(str)
Identical to rb_str_new_cstr, except done differently.
Definition string.h:1664
VALUE rb_str_concat(VALUE dst, VALUE src)
Identical to rb_str_append(), except it also accepts an integer as a codepoint.
Definition string.c:4174
VALUE rb_str_freeze(VALUE str)
This is the implementation of String#freeze.
Definition string.c:3416
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1539
VALUE rb_obj_as_string(VALUE obj)
Try converting an object to its stringised representation using its to_s method, if any.
Definition string.c:1916
VALUE rb_ivar_set(VALUE obj, ID name, VALUE val)
Identical to rb_iv_set(), except it accepts the name as an ID instead of a C string.
Definition variable.c:2150
VALUE rb_const_get_at(VALUE space, ID name)
Identical to rb_const_defined_at(), except it returns the actual defined value.
Definition variable.c:3479
int rb_const_defined_at(VALUE space, ID name)
Identical to rb_const_defined(), except it doesn't look for parent classes.
Definition variable.c:3814
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:285
VALUE rb_id2sym(ID id)
Allocates an instance of rb_cSymbol that has the given id.
Definition symbol.c:1129
@ RUBY_IO_READABLE
IO::READABLE
Definition io.h:97
VALUE rb_io_wait(VALUE io, VALUE events, VALUE timeout)
Blocks until the passed IO is ready for the passed events.
Definition io.c:1516
int len
Length of the buffer.
Definition io.h:8
#define RB_OBJ_SET_SHAREABLE(obj)
Wrapper of rb_obj_set_shareable().
Definition ractor.h:290
#define RB_OBJ_SHAREABLE_P(obj)
Queries if the passed object has previously classified as shareable or not.
Definition ractor.h:255
static VALUE RB_OBJ_SET_FROZEN_SHAREABLE(VALUE obj)
Freezes and marks the object as shareable.
Definition ractor.h:301
VALUE rb_ractor_make_shareable(VALUE obj)
Destructively transforms the passed object so that multiple Ractors can share it.
Definition ractor.c:2012
#define DECIMAL_SIZE_OF(expr)
An approximation of decimal representation size.
Definition util.h:48
#define RUBY_API_VERSION_MAJOR
Major version.
Definition version.h:64
#define RUBY_API_VERSION_MINOR
Minor version.
Definition version.h:70
#define RB_INT2NUM
Just another name of rb_int2num_inline.
Definition int.h:37
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
VALUE type(ANYARGS)
ANYARGS-ed function type.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
T * cast(pm_node_t *node) noexcept
Cast the given node, which must not be null and must be of the given struct, to that struct.
Definition node.hpp:50
PRISM_NODISCARD PRISM_EXPORTED_FUNCTION pm_parser_t * pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_options_t *options) PRISM_NONNULL(1)
Allocate and initialize a parser with the given start and end pointers.
Definition prism.c:23528
PRISM_EXPORTED_FUNCTION void pm_parser_free(pm_parser_t *parser) PRISM_NONNULL(1)
Free both the memory held by the given parser and the parser itself.
Definition prism.c:23549
PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse(pm_parser_t *parser) PRISM_NONNULL(1)
Initiate the parser with the given parser.
Definition prism.c:23720
std::string_view constant(const pm_parser_t *parser PRISM_LIFETIMEBOUND, pm_constant_id_t constant_id) noexcept
The name of the constant with the given id, which must not be zero.
Definition parser.hpp:64
pm_visit_result_t
The value returned by a visitor callback, which determines where the walk goes next.
Definition node.h:49
@ PM_VISIT_STOP
End the walk without visiting any more nodes.
Definition node.h:57
@ PM_VISIT_DESCEND
Continue the walk by visiting this node's children.
Definition node.h:54
#define PM_NODE_LIST_FOREACH(list, index, node)
Loop through each node in the node list, writing each node to the given pm_node_t pointer.
Definition node.h:22
The main header file for the prism parser.
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:50
#define RARRAY_AREF(a, i)
References an object in an array.
Definition rarray.h:421
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
Definition rarray.h:51
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
pm_source_init_result_t
Represents the result of initializing a source from a file.
Definition source.h:50
@ PM_SOURCE_INIT_SUCCESS
Indicates that the source was successfully initialized.
Definition source.h:52
@ PM_SOURCE_INIT_ERROR_NON_REGULAR
Indicates that the file is not a regular file (e.g.
Definition source.h:69
@ PM_SOURCE_INIT_ERROR_DIRECTORY
Indicates that the file that was attempted to be opened was a directory.
Definition source.h:63
#define RTEST
This is an old name of RB_TEST.
AliasGlobalVariableNode.
Definition ast.h:1165
PM_NODE_ALIGNAS struct pm_node * new_name
AliasGlobalVariableNode::new_name.
Definition ast.h:1177
PM_NODE_ALIGNAS struct pm_node * old_name
AliasGlobalVariableNode::old_name.
Definition ast.h:1187
AliasMethodNode.
Definition ast.h:1212
PM_NODE_ALIGNAS struct pm_node * old_name
AliasMethodNode::old_name.
Definition ast.h:1246
PM_NODE_ALIGNAS struct pm_node * new_name
AliasMethodNode::new_name.
Definition ast.h:1230
AlternationPatternNode.
Definition ast.h:1271
AndNode.
Definition ast.h:1318
ArgumentsNode.
Definition ast.h:1378
pm_node_t base
The embedded base node.
Definition ast.h:1380
struct pm_node_list arguments
ArgumentsNode::arguments.
Definition ast.h:1390
ArrayNode.
Definition ast.h:1408
ArrayPatternNode.
Definition ast.h:1468
AssocNode.
Definition ast.h:1551
PM_NODE_ALIGNAS struct pm_node * value
AssocNode::value.
Definition ast.h:1582
PM_NODE_ALIGNAS struct pm_node * key
AssocNode::key.
Definition ast.h:1569
AssocSplatNode.
Definition ast.h:1607
PM_NODE_ALIGNAS struct pm_node * value
AssocSplatNode::value.
Definition ast.h:1619
BackReferenceReadNode.
Definition ast.h:1644
pm_node_t base
The embedded base node.
Definition ast.h:1646
BeginNode.
Definition ast.h:1674
BlockArgumentNode.
Definition ast.h:1751
BlockLocalVariableNode.
Definition ast.h:1791
BlockNode.
Definition ast.h:1818
BlockParameterNode.
Definition ast.h:1893
BlockParametersNode.
Definition ast.h:1946
BreakNode.
Definition ast.h:2019
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
BreakNode::arguments.
Definition ast.h:2031
CallAndWriteNode.
Definition ast.h:2062
CallNode.
Definition ast.h:2170
pm_location_t closing_loc
CallNode::closing_loc.
Definition ast.h:2251
pm_constant_id_t name
CallNode::name.
Definition ast.h:2211
pm_node_t base
The embedded base node.
Definition ast.h:2172
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
CallNode::arguments.
Definition ast.h:2241
pm_location_t message_loc
CallNode::message_loc.
Definition ast.h:2221
PM_NODE_ALIGNAS struct pm_node * block
CallNode::block.
Definition ast.h:2274
PM_NODE_ALIGNAS struct pm_node * receiver
CallNode::receiver.
Definition ast.h:2188
CallOperatorWriteNode.
Definition ast.h:2295
pm_constant_id_t read_name
CallOperatorWriteNode::read_name.
Definition ast.h:2337
pm_constant_id_t binary_operator
CallOperatorWriteNode::binary_operator.
Definition ast.h:2357
pm_constant_id_t write_name
CallOperatorWriteNode::write_name.
Definition ast.h:2347
PM_NODE_ALIGNAS struct pm_node * receiver
CallOperatorWriteNode::receiver.
Definition ast.h:2307
PM_NODE_ALIGNAS struct pm_node * value
CallOperatorWriteNode::value.
Definition ast.h:2377
CallOrWriteNode.
Definition ast.h:2398
CallTargetNode.
Definition ast.h:2499
CapturePatternNode.
Definition ast.h:2556
CaseMatchNode.
Definition ast.h:2605
struct pm_node_list conditions
CaseMatchNode::conditions.
Definition ast.h:2627
pm_node_t base
The embedded base node.
Definition ast.h:2607
PM_NODE_ALIGNAS struct pm_node * predicate
CaseMatchNode::predicate.
Definition ast.h:2617
PM_NODE_ALIGNAS struct pm_else_node * else_clause
CaseMatchNode::else_clause.
Definition ast.h:2637
CaseNode.
Definition ast.h:2674
ClassNode.
Definition ast.h:2741
ClassVariableAndWriteNode.
Definition ast.h:2827
ClassVariableOperatorWriteNode.
Definition ast.h:2884
ClassVariableOrWriteNode.
Definition ast.h:2926
ClassVariableReadNode.
Definition ast.h:2963
ClassVariableTargetNode.
Definition ast.h:2991
ClassVariableWriteNode.
Definition ast.h:3013
ConstantAndWriteNode.
Definition ast.h:3074
PM_NODE_ALIGNAS struct pm_node * value
ConstantAndWriteNode::value.
Definition ast.h:3096
pm_location_t name_loc
ConstantAndWriteNode::name_loc.
Definition ast.h:3086
pm_constant_id_t name
ConstantAndWriteNode::name.
Definition ast.h:3081
A list of constant IDs.
size_t size
The number of constant ids in the list.
size_t capacity
The number of constant ids that have been allocated in the list.
pm_constant_id_t * ids
The constant ids in the list.
ConstantOperatorWriteNode.
Definition ast.h:3111
pm_constant_id_t name
ConstantOperatorWriteNode::name.
Definition ast.h:3118
pm_location_t name_loc
ConstantOperatorWriteNode::name_loc.
Definition ast.h:3123
pm_constant_id_t binary_operator
ConstantOperatorWriteNode::binary_operator.
Definition ast.h:3138
PM_NODE_ALIGNAS struct pm_node * value
ConstantOperatorWriteNode::value.
Definition ast.h:3133
ConstantOrWriteNode.
Definition ast.h:3153
PM_NODE_ALIGNAS struct pm_node * value
ConstantOrWriteNode::value.
Definition ast.h:3175
pm_location_t name_loc
ConstantOrWriteNode::name_loc.
Definition ast.h:3165
pm_constant_id_t name
ConstantOrWriteNode::name.
Definition ast.h:3160
ConstantPathAndWriteNode.
Definition ast.h:3190
PM_NODE_ALIGNAS struct pm_constant_path_node * target
ConstantPathAndWriteNode::target.
Definition ast.h:3197
PM_NODE_ALIGNAS struct pm_node * value
ConstantPathAndWriteNode::value.
Definition ast.h:3207
ConstantPathNode.
Definition ast.h:3222
PM_NODE_ALIGNAS struct pm_node * parent
ConstantPathNode::parent.
Definition ast.h:3240
pm_constant_id_t name
ConstantPathNode::name.
Definition ast.h:3247
ConstantPathOperatorWriteNode.
Definition ast.h:3288
PM_NODE_ALIGNAS struct pm_constant_path_node * target
ConstantPathOperatorWriteNode::target.
Definition ast.h:3295
pm_constant_id_t binary_operator
ConstantPathOperatorWriteNode::binary_operator.
Definition ast.h:3310
PM_NODE_ALIGNAS struct pm_node * value
ConstantPathOperatorWriteNode::value.
Definition ast.h:3305
ConstantPathOrWriteNode.
Definition ast.h:3325
PM_NODE_ALIGNAS struct pm_node * value
ConstantPathOrWriteNode::value.
Definition ast.h:3342
PM_NODE_ALIGNAS struct pm_constant_path_node * target
ConstantPathOrWriteNode::target.
Definition ast.h:3332
ConstantPathTargetNode.
Definition ast.h:3357
ConstantPathWriteNode.
Definition ast.h:3400
PM_NODE_ALIGNAS struct pm_node * value
ConstantPathWriteNode::value.
Definition ast.h:3435
PM_NODE_ALIGNAS struct pm_constant_path_node * target
ConstantPathWriteNode::target.
Definition ast.h:3415
ConstantReadNode.
Definition ast.h:3450
ConstantTargetNode.
Definition ast.h:3478
ConstantWriteNode.
Definition ast.h:3500
PM_NODE_ALIGNAS struct pm_node * value
ConstantWriteNode::value.
Definition ast.h:3536
pm_constant_id_t name
ConstantWriteNode::name.
Definition ast.h:3513
DefNode.
Definition ast.h:3562
pm_constant_id_t name
DefNode::name.
Definition ast.h:3569
PM_NODE_ALIGNAS struct pm_parameters_node * parameters
DefNode::parameters.
Definition ast.h:3584
pm_node_t base
The embedded base node.
Definition ast.h:3564
PM_NODE_ALIGNAS struct pm_node * receiver
DefNode::receiver.
Definition ast.h:3579
PM_NODE_ALIGNAS struct pm_node * body
DefNode::body.
Definition ast.h:3589
pm_constant_id_list_t locals
DefNode::locals.
Definition ast.h:3594
DefinedNode.
Definition ast.h:3639
ElseNode.
Definition ast.h:3676
PM_NODE_ALIGNAS struct pm_statements_node * statements
ElseNode::statements.
Definition ast.h:3688
EmbeddedStatementsNode.
Definition ast.h:3708
EmbeddedVariableNode.
Definition ast.h:3740
EnsureNode.
Definition ast.h:3771
FindPatternNode.
Definition ast.h:3850
FlipFlopNode.
Definition ast.h:3945
PM_NODE_ALIGNAS struct pm_node * left
FlipFlopNode::left.
Definition ast.h:3952
pm_node_t base
The embedded base node.
Definition ast.h:3947
PM_NODE_ALIGNAS struct pm_node * right
FlipFlopNode::right.
Definition ast.h:3957
FloatNode.
Definition ast.h:3977
double value
FloatNode::value.
Definition ast.h:3986
ForNode.
Definition ast.h:4001
ForwardingSuperNode.
Definition ast.h:4132
PM_NODE_ALIGNAS struct pm_block_node * block
ForwardingSuperNode::block.
Definition ast.h:4152
GlobalVariableAndWriteNode.
Definition ast.h:4167
GlobalVariableOperatorWriteNode.
Definition ast.h:4204
GlobalVariableOrWriteNode.
Definition ast.h:4246
GlobalVariableReadNode.
Definition ast.h:4283
GlobalVariableTargetNode.
Definition ast.h:4311
GlobalVariableWriteNode.
Definition ast.h:4333
HashNode.
Definition ast.h:4394
HashPatternNode.
Definition ast.h:4453
IfNode.
Definition ast.h:4541
PM_NODE_ALIGNAS struct pm_node * predicate
IfNode::predicate.
Definition ast.h:4573
PM_NODE_ALIGNAS struct pm_statements_node * statements
IfNode::statements.
Definition ast.h:4600
ImaginaryNode.
Definition ast.h:4646
PM_NODE_ALIGNAS struct pm_node * numeric
ImaginaryNode::numeric.
Definition ast.h:4653
ImplicitNode.
Definition ast.h:4674
InNode.
Definition ast.h:4722
PM_NODE_ALIGNAS struct pm_node * pattern
InNode::pattern.
Definition ast.h:4729
PM_NODE_ALIGNAS struct pm_statements_node * statements
InNode::statements.
Definition ast.h:4734
IndexAndWriteNode.
Definition ast.h:4765
A direct-indexed lookup table mapping constant IDs to local variable indices.
int capacity
Total number of slots (constants_size + PM_INDEX_LOOKUP_SPECIALS).
bool owned
Whether the values array is heap-allocated and needs explicit free.
int * values
Array of local indices, indexed by pm_index_lookup_table_index.
IndexOperatorWriteNode.
Definition ast.h:4828
PM_NODE_ALIGNAS struct pm_block_argument_node * block
IndexOperatorWriteNode::block.
Definition ast.h:4860
PM_NODE_ALIGNAS struct pm_node * receiver
IndexOperatorWriteNode::receiver.
Definition ast.h:4835
PM_NODE_ALIGNAS struct pm_node * value
IndexOperatorWriteNode::value.
Definition ast.h:4875
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
IndexOperatorWriteNode::arguments.
Definition ast.h:4850
pm_constant_id_t binary_operator
IndexOperatorWriteNode::binary_operator.
Definition ast.h:4865
IndexOrWriteNode.
Definition ast.h:4896
IndexTargetNode.
Definition ast.h:4967
InstanceVariableAndWriteNode.
Definition ast.h:5009
InstanceVariableOperatorWriteNode.
Definition ast.h:5046
InstanceVariableOrWriteNode.
Definition ast.h:5088
InstanceVariableReadNode.
Definition ast.h:5125
InstanceVariableTargetNode.
Definition ast.h:5153
InstanceVariableWriteNode.
Definition ast.h:5175
IntegerNode.
Definition ast.h:5242
pm_integer_t value
IntegerNode::value.
Definition ast.h:5251
A structure represents an arbitrary-sized integer.
Definition integer.h:16
size_t length
The number of allocated values.
Definition integer.h:21
uint32_t value
Embedded value for small integer.
Definition integer.h:32
uint32_t * values
List of 32-bit integers.
Definition integer.h:26
bool negative
Whether or not the integer is negative.
Definition integer.h:38
InterpolatedMatchLastLineNode.
Definition ast.h:5279
InterpolatedRegularExpressionNode.
Definition ast.h:5324
InterpolatedStringNode.
Definition ast.h:5360
InterpolatedSymbolNode.
Definition ast.h:5392
InterpolatedXStringNode.
Definition ast.h:5424
KeywordHashNode.
Definition ast.h:5493
struct pm_node_list elements
KeywordHashNode::elements.
Definition ast.h:5500
KeywordRestParameterNode.
Definition ast.h:5519
LambdaNode.
Definition ast.h:5551
A line and column in a string.
uint32_t column
The column in bytes.
int32_t line
The line number.
A list of offsets of the start of lines in a string.
uint32_t * offsets
The list of offsets.
size_t size
The number of offsets in the list.
the getlocal and setlocal instructions require two parameters.
LocalVariableAndWriteNode.
Definition ast.h:5598
LocalVariableOperatorWriteNode.
Definition ast.h:5640
LocalVariableOrWriteNode.
Definition ast.h:5687
LocalVariableReadNode.
Definition ast.h:5729
LocalVariableTargetNode.
Definition ast.h:5777
uint32_t depth
LocalVariableTargetNode::depth.
Definition ast.h:5789
pm_constant_id_t name
LocalVariableTargetNode::name.
Definition ast.h:5784
LocalVariableWriteNode.
Definition ast.h:5804
This struct represents a slice in the source code, defined by an offset and a length.
Definition ast.h:579
uint32_t start
The offset of the location from the start of the source.
Definition ast.h:581
uint32_t length
The length of the location.
Definition ast.h:584
MatchLastLineNode.
Definition ast.h:5895
MatchPredicateNode.
Definition ast.h:5932
MatchRequiredNode.
Definition ast.h:5964
PM_NODE_ALIGNAS struct pm_node * pattern
MatchRequiredNode::pattern.
Definition ast.h:6025
PM_NODE_ALIGNAS struct pm_node * value
MatchRequiredNode::value.
Definition ast.h:5976
MatchWriteNode.
Definition ast.h:6050
PM_NODE_ALIGNAS struct pm_call_node * call
MatchWriteNode::call.
Definition ast.h:6057
struct pm_node_list targets
MatchWriteNode::targets.
Definition ast.h:6062
ModuleNode.
Definition ast.h:6077
MultiTargetNode.
Definition ast.h:6129
PM_NODE_ALIGNAS struct pm_node * rest
MultiTargetNode::rest.
Definition ast.h:6166
struct pm_node_list lefts
MultiTargetNode::lefts.
Definition ast.h:6146
struct pm_node_list rights
MultiTargetNode::rights.
Definition ast.h:6176
This is a node in the multi target state linked list.
As we're compiling a multi target, we need to track additional information whenever there is a parent...
MultiWriteNode.
Definition ast.h:6211
NextNode.
Definition ast.h:6313
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
NextNode::arguments.
Definition ast.h:6320
A list of nodes in the source, most often used for lists of children.
Definition ast.h:592
size_t size
The number of nodes in the list.
Definition ast.h:594
struct pm_node ** nodes
The nodes in the list.
Definition ast.h:600
This compiler defines its own concept of the location of a node.
int32_t line
This is the line number of a node.
uint32_t node_id
This is a unique identifier for the node.
This is the base structure that represents a node in the syntax tree.
Definition ast.h:1090
pm_node_type_t type
This represents the type of the node.
Definition ast.h:1095
uint32_t node_id
The unique identifier for this node, which is deterministic based on the source.
Definition ast.h:1109
pm_node_flags_t flags
This represents any flags on the node.
Definition ast.h:1101
pm_location_t location
This is the location of the node in the source.
Definition ast.h:1115
NumberedParametersNode.
Definition ast.h:6413
NumberedReferenceReadNode.
Definition ast.h:6435
uint32_t number
NumberedReferenceReadNode::number.
Definition ast.h:6450
OptionalKeywordParameterNode.
Definition ast.h:6469
OptionalParameterNode.
Definition ast.h:6505
OrNode.
Definition ast.h:6542
ParametersNode.
Definition ast.h:6596
PM_NODE_ALIGNAS struct pm_node * block
ParametersNode::block.
Definition ast.h:6633
struct pm_node_list requireds
ParametersNode::requireds.
Definition ast.h:6603
struct pm_node_list optionals
ParametersNode::optionals.
Definition ast.h:6608
struct pm_node_list posts
ParametersNode::posts.
Definition ast.h:6618
pm_node_t base
The embedded base node.
Definition ast.h:6598
PM_NODE_ALIGNAS struct pm_node * rest
ParametersNode::rest.
Definition ast.h:6613
struct pm_node_list keywords
ParametersNode::keywords.
Definition ast.h:6623
PM_NODE_ALIGNAS struct pm_node * keyword_rest
ParametersNode::keyword_rest.
Definition ast.h:6628
ParenthesesNode.
Definition ast.h:6651
bool parsed
Whether or not this parse result has performed its parsing yet.
pm_parser_t * parser
The parser that will do the actual parsing.
pm_source_t * source
The source backing the parse (file, string, or stream).
pm_scope_node_t node
The resulting scope node that will hold the generated AST.
pm_options_t * options
The options that will be passed to the parser.
pm_arena_t * arena
The arena allocator for AST-lifetime memory.
PinnedExpressionNode.
Definition ast.h:6683
PinnedVariableNode.
Definition ast.h:6740
PostExecutionNode.
Definition ast.h:6777
PreExecutionNode.
Definition ast.h:6814
ProgramNode.
Definition ast.h:6848
RangeNode.
Definition ast.h:6881
RationalNode.
Definition ast.h:6938
pm_integer_t denominator
RationalNode::denominator.
Definition ast.h:6958
pm_integer_t numerator
RationalNode::numerator.
Definition ast.h:6949
RegularExpressionNode.
Definition ast.h:7003
RequiredKeywordParameterNode.
Definition ast.h:7044
RequiredParameterNode.
Definition ast.h:7075
pm_constant_id_t name
RequiredParameterNode::name.
Definition ast.h:7082
RescueModifierNode.
Definition ast.h:7097
RescueNode.
Definition ast.h:7134
PM_NODE_ALIGNAS struct pm_statements_node * statements
RescueNode::statements.
Definition ast.h:7166
PM_NODE_ALIGNAS struct pm_rescue_node * subsequent
RescueNode::subsequent.
Definition ast.h:7171
PM_NODE_ALIGNAS struct pm_node * reference
RescueNode::reference.
Definition ast.h:7156
struct pm_node_list exceptions
RescueNode::exceptions.
Definition ast.h:7146
RestParameterNode.
Definition ast.h:7190
ReturnNode.
Definition ast.h:7239
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
ReturnNode::arguments.
Definition ast.h:7251
uint64_t source_hash
The source hash of the parsed source, propagated to every iseq.
rb_encoding * filepath_encoding
This is the encoding of the actual filepath object that will be used when a FILE node is compiled or ...
pm_index_lookup_table_t index_lookup_table
A flat lookup table mapping constant IDs (or special IDs) to local variable indices.
size_t last_line
Cached line hint for line offset list lookups.
struct iseq_link_anchor * pre_execution_anchor
This will only be set on the top-level scope node.
VALUE * script_lines
This is a pointer to the list of script lines for the ISEQs that will be associated with this scope n...
ShareableConstantNode.
Definition ast.h:7289
SingletonClassNode.
Definition ast.h:7313
SourceFileNode.
Definition ast.h:7383
SplatNode.
Definition ast.h:7424
PM_NODE_ALIGNAS struct pm_node * expression
SplatNode::expression.
Definition ast.h:7436
StatementsNode.
Definition ast.h:7451
StringNode.
Definition ast.h:7485
pm_string_t unescaped
StringNode::unescaped.
Definition ast.h:7507
A view of a sequence of bytes.
Definition stringy.h:23
SuperNode.
Definition ast.h:7527
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
SuperNode::arguments.
Definition ast.h:7546
PM_NODE_ALIGNAS struct pm_node * block
SuperNode::block.
Definition ast.h:7556
SymbolNode.
Definition ast.h:7579
pm_string_t unescaped
SymbolNode::unescaped.
Definition ast.h:7601
pm_node_t base
The embedded base node.
Definition ast.h:7581
UndefNode.
Definition ast.h:7633
UnlessNode.
Definition ast.h:7663
UntilNode.
Definition ast.h:7753
WhenNode.
Definition ast.h:7797
WhileNode.
Definition ast.h:7840
XStringNode.
Definition ast.h:7886
YieldNode.
Definition ast.h:7923
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
YieldNode::arguments.
Definition ast.h:7940
long rb_len_t
A signed integer type for lengths and indices of objects such as String and Array.
Definition value.h:142
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376