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