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