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