Ruby 4.1.0dev (2026-04-17 revision 11e3c78b61da705c783dd12fb7f158c0d256ede0)
prism_compile.c (11e3c78b61da705c783dd12fb7f158c0d256ede0)
1#include "prism.h"
2#include "ruby/version.h"
3
4#include <fcntl.h>
5
11typedef struct {
13 int32_t line;
14
16 uint32_t node_id;
18
19/******************************************************************************/
20/* These macros operate on pm_node_location_t structs as opposed to NODE*s. */
21/******************************************************************************/
22
23#define PUSH_ADJUST(seq, location, label) \
24 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), (int) (location).line))
25
26#define PUSH_ADJUST_RESTORE(seq, label) \
27 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), -1))
28
29#define PUSH_INSN(seq, location, insn) \
30 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 0))
31
32#define PUSH_INSN1(seq, location, insn, op1) \
33 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 1, (VALUE)(op1)))
34
35#define PUSH_INSN2(seq, location, insn, op1, op2) \
36 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 2, (VALUE)(op1), (VALUE)(op2)))
37
38#define PUSH_INSN3(seq, location, insn, op1, op2, op3) \
39 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3)))
40
41#define PUSH_INSNL(seq, location, insn, label) \
42 (PUSH_INSN1(seq, location, insn, label), LABEL_REF(label))
43
44#define PUSH_LABEL(seq, label) \
45 ADD_ELEM((seq), (LINK_ELEMENT *) (label))
46
47#define PUSH_SEND_R(seq, location, id, argc, block, flag, keywords) \
48 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (int) (location).line, (int) (location).node_id, (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords)))
49
50#define PUSH_SEND(seq, location, id, argc) \
51 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)INT2FIX(0), NULL)
52
53#define PUSH_SEND_WITH_FLAG(seq, location, id, argc, flag) \
54 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)(flag), NULL)
55
56#define PUSH_SEND_WITH_BLOCK(seq, location, id, argc, block) \
57 PUSH_SEND_R((seq), location, (id), (argc), (block), (VALUE)INT2FIX(0), NULL)
58
59#define PUSH_CALL(seq, location, id, argc) \
60 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
61
62#define PUSH_CALL_WITH_BLOCK(seq, location, id, argc, block) \
63 PUSH_SEND_R((seq), location, (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
64
65#define PUSH_TRACE(seq, event) \
66 ADD_ELEM((seq), (LINK_ELEMENT *) new_trace_body(iseq, (event), 0))
67
68#define PUSH_CATCH_ENTRY(type, ls, le, iseqv, lc) \
69 ADD_CATCH_ENTRY((type), (ls), (le), (iseqv), (lc))
70
71#define PUSH_SEQ(seq1, seq2) \
72 APPEND_LIST((seq1), (seq2))
73
74#define PUSH_SYNTHETIC_PUTNIL(seq, iseq) \
75 do { \
76 int lineno = ISEQ_COMPILE_DATA(iseq)->last_line; \
77 if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq)); \
78 ADD_SYNTHETIC_INSN(seq, lineno, -1, putnil); \
79 } while (0)
80
81/******************************************************************************/
82/* These functions compile getlocal/setlocal instructions but operate on */
83/* prism locations instead of NODEs. */
84/******************************************************************************/
85
86static void
87pm_iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int node_id, int idx, int level)
88{
89 if (iseq_local_block_param_p(iseq, idx, level)) {
90 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(getblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
91 }
92 else {
93 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(getlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
94 }
95 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qfalse);
96}
97
98static void
99pm_iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int node_id, int idx, int level)
100{
101 if (iseq_local_block_param_p(iseq, idx, level)) {
102 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(setblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
103 }
104 else {
105 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(setlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
106 }
107 update_lvar_state(iseq, level, idx);
108 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue);
109}
110
111#define PUSH_GETLOCAL(seq, location, idx, level) \
112 pm_iseq_add_getlocal(iseq, (seq), (int) (location).line, (int) (location).node_id, (idx), (level))
113
114#define PUSH_SETLOCAL(seq, location, idx, level) \
115 pm_iseq_add_setlocal(iseq, (seq), (int) (location).line, (int) (location).node_id, (idx), (level))
116
117/******************************************************************************/
118/* These are helper macros for the compiler. */
119/******************************************************************************/
120
121#define OLD_ISEQ NEW_ISEQ
122#undef NEW_ISEQ
123
124#define NEW_ISEQ(node, name, type, line_no) \
125 pm_new_child_iseq(iseq, (node), rb_fstring(name), 0, (type), (line_no))
126
127#define OLD_CHILD_ISEQ NEW_CHILD_ISEQ
128#undef NEW_CHILD_ISEQ
129
130#define NEW_CHILD_ISEQ(node, name, type, line_no) \
131 pm_new_child_iseq(iseq, (node), rb_fstring(name), iseq, (type), (line_no))
132
133#define PM_COMPILE(node) \
134 pm_compile_node(iseq, (node), ret, popped, scope_node)
135
136#define PM_COMPILE_INTO_ANCHOR(_ret, node) \
137 pm_compile_node(iseq, (node), _ret, popped, scope_node)
138
139#define PM_COMPILE_POPPED(node) \
140 pm_compile_node(iseq, (node), ret, true, scope_node)
141
142#define PM_COMPILE_NOT_POPPED(node) \
143 pm_compile_node(iseq, (node), ret, false, scope_node)
144
145// Direct-indexed lookup table. -1 means "not present".
146#define PM_INDEX_LOOKUP_TABLE_INIT { .values = NULL, .capacity = 0, .owned = false }
147
148static inline void
149pm_index_lookup_table_init(pm_index_lookup_table_t *table, int constants_size, rb_iseq_t *iseq)
150{
151 int capacity = constants_size + PM_INDEX_LOOKUP_SPECIALS;
152 table->values = compile_data_alloc2_type(iseq, int, capacity);
153 memset(table->values, -1, capacity * sizeof(int));
154 table->capacity = capacity;
155 table->owned = false;
156}
157
164static inline pm_line_column_t
165pm_line_offset_list_line_column_cached(const pm_line_offset_list_t *list, uint32_t cursor, int32_t start_line, size_t *last_line)
166{
167 size_t hint = *last_line;
168 size_t size = list->size;
169 const uint32_t *offsets = list->offsets;
170
171 RUBY_ASSERT(hint < size);
172
173 /* Check if the cursor is on the same line as the hint. */
174 if (offsets[hint] <= cursor) {
175 if (hint + 1 >= size || offsets[hint + 1] > cursor) {
176 *last_line = hint;
177 return ((pm_line_column_t) {
178 .line = ((int32_t) hint) + start_line,
179 .column = cursor - offsets[hint]
180 });
181 }
182
183 /* Linear scan forward (up to 8 lines before giving up). */
184 size_t limit = hint + 9;
185 if (limit > size) limit = size;
186 for (size_t idx = hint + 1; idx < limit; idx++) {
187 if (offsets[idx] > cursor) {
188 *last_line = idx - 1;
189 return ((pm_line_column_t) {
190 .line = ((int32_t) (idx - 1)) + start_line,
191 .column = cursor - offsets[idx - 1]
192 });
193 }
194 if (offsets[idx] == cursor) {
195 *last_line = idx;
196 return ((pm_line_column_t) { ((int32_t) idx) + start_line, 0 });
197 }
198 }
199 }
200 else {
201 /* Linear scan backward (up to 8 lines before giving up). */
202 size_t limit = hint > 8 ? hint - 8 : 0;
203 for (size_t idx = hint; idx > limit; idx--) {
204 if (offsets[idx - 1] <= cursor) {
205 *last_line = idx - 1;
206 return ((pm_line_column_t) {
207 .line = ((int32_t) (idx - 1)) + start_line,
208 .column = cursor - offsets[idx - 1]
209 });
210 }
211 }
212 }
213
214 /* Fall back to binary search. */
215 pm_line_column_t result = pm_line_offset_list_line_column(list, cursor, start_line);
216 *last_line = (size_t) (result.line - start_line);
217 return result;
218}
219
224static inline int32_t
225pm_line_offset_list_line_cached(const pm_line_offset_list_t *list, uint32_t cursor, int32_t start_line, size_t *last_line)
226{
227 return pm_line_offset_list_line_column_cached(list, cursor, start_line, last_line).line;
228}
229
230#define PM_NODE_START_LOCATION(node) \
231 ((pm_node_location_t) { .line = pm_line_offset_list_line_cached(scope_node->line_offsets, ((const pm_node_t *) (node))->location.start, scope_node->start_line, &scope_node->last_line), .node_id = ((const pm_node_t *) (node))->node_id })
232
233#define PM_NODE_END_LOCATION(node) \
234 ((pm_node_location_t) { .line = pm_line_offset_list_line_cached(scope_node->line_offsets, ((const pm_node_t *) (node))->location.start + ((const pm_node_t *) (node))->location.length, scope_node->start_line, &scope_node->last_line), .node_id = ((const pm_node_t *) (node))->node_id })
235
236#define PM_LOCATION_START_LOCATION(location, id) \
237 ((pm_node_location_t) { .line = pm_line_offset_list_line_cached(scope_node->line_offsets, (location)->start, scope_node->start_line, &scope_node->last_line), .node_id = id })
238
239#define PM_NODE_START_LINE_COLUMN(node) \
240 pm_line_offset_list_line_column_cached(scope_node->line_offsets, ((const pm_node_t *) (node))->location.start, scope_node->start_line, &scope_node->last_line)
241
242#define PM_NODE_END_LINE_COLUMN(node) \
243 pm_line_offset_list_line_column_cached(scope_node->line_offsets, ((const pm_node_t *) (node))->location.start + ((const pm_node_t *) (node))->location.length, scope_node->start_line, &scope_node->last_line)
244
245#define PM_LOCATION_START_LINE_COLUMN(location) \
246 pm_line_offset_list_line_column_cached(scope_node->line_offsets, (location)->start, scope_node->start_line, &scope_node->last_line)
247
248static int
249pm_location_line_number(const pm_parser_t *parser, const pm_location_t *location) {
250 return (int) pm_line_offset_list_line_column(pm_parser_line_offsets(parser), location->start, pm_parser_start_line(parser)).line;
251}
252
257static inline int
258pm_node_line_number_cached(const pm_node_t *node, pm_scope_node_t *scope_node)
259{
260 return (int) pm_line_offset_list_line_cached(scope_node->line_offsets, node->location.start, scope_node->start_line, &scope_node->last_line);
261}
262
263static inline int
264pm_location_line_number_cached(const pm_location_t *location, pm_scope_node_t *scope_node) {
265 return (int) pm_line_offset_list_line_cached(scope_node->line_offsets, location->start, scope_node->start_line, &scope_node->last_line);
266}
267
271static VALUE
272parse_integer_value(const pm_integer_t *integer)
273{
274 VALUE result;
275
276 if (integer->values == NULL) {
277 result = UINT2NUM(integer->value);
278 }
279 else {
280 // The pm_integer_t stores values as an array of uint32_t in
281 // least-significant-word-first order (base 2^32). We can convert
282 // directly to a Ruby Integer using rb_integer_unpack, avoiding the
283 // overhead of constructing a hex string and calling rb_funcall.
284 result = rb_integer_unpack(
285 integer->values,
286 integer->length,
287 sizeof(uint32_t),
288 0,
290 );
291 }
292
293 if (integer->negative) {
294 result = rb_int_uminus(result);
295 }
296
297 if (!SPECIAL_CONST_P(result)) {
298 RB_OBJ_SET_SHAREABLE(result); // bignum
299 }
300
301 return result;
302}
303
307static inline VALUE
308parse_integer(const pm_integer_node_t *node)
309{
310 return parse_integer_value(&node->value);
311}
312
316static VALUE
317parse_float(const pm_float_node_t *node)
318{
319 VALUE val = DBL2NUM(node->value);
320 if (!FLONUM_P(val)) {
321 RB_OBJ_SET_SHAREABLE(val);
322 }
323 return val;
324}
325
332static VALUE
333parse_rational(const pm_rational_node_t *node)
334{
335 VALUE numerator = parse_integer_value(&node->numerator);
336 VALUE denominator = parse_integer_value(&node->denominator);
337
338 return rb_ractor_make_shareable(rb_rational_new(numerator, denominator));
339}
340
347static VALUE
348parse_imaginary(const pm_imaginary_node_t *node)
349{
350 VALUE imaginary_part;
351 switch (PM_NODE_TYPE(node->numeric)) {
352 case PM_FLOAT_NODE: {
353 imaginary_part = parse_float((const pm_float_node_t *) node->numeric);
354 break;
355 }
356 case PM_INTEGER_NODE: {
357 imaginary_part = parse_integer((const pm_integer_node_t *) node->numeric);
358 break;
359 }
360 case PM_RATIONAL_NODE: {
361 imaginary_part = parse_rational((const pm_rational_node_t *) node->numeric);
362 break;
363 }
364 default:
365 rb_bug("Unexpected numeric type on imaginary number %s\n", pm_node_type(PM_NODE_TYPE(node->numeric)));
366 }
367
368 return RB_OBJ_SET_SHAREABLE(rb_complex_raw(INT2FIX(0), imaginary_part));
369}
370
371static inline VALUE
372parse_string(const pm_scope_node_t *scope_node, const pm_string_t *string)
373{
374 return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), scope_node->encoding);
375}
376
382static inline VALUE
383parse_string_encoded(const pm_node_t *node, const pm_string_t *string, rb_encoding *default_encoding)
384{
385 rb_encoding *encoding;
386
387 if (node->flags & PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING) {
388 encoding = rb_ascii8bit_encoding();
389 }
390 else if (node->flags & PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING) {
391 encoding = rb_utf8_encoding();
392 }
393 else {
394 encoding = default_encoding;
395 }
396
397 return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), encoding);
398}
399
400static inline VALUE
401parse_static_literal_string(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *string)
402{
403 rb_encoding *encoding;
404
405 if (node->flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) {
406 encoding = rb_ascii8bit_encoding();
407 }
408 else if (node->flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) {
409 encoding = rb_utf8_encoding();
410 }
411 else {
412 encoding = scope_node->encoding;
413 }
414
415 VALUE value = rb_enc_literal_str((const char *) pm_string_source(string), pm_string_length(string), encoding);
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, putstring, current_string);
778 } else {
779 PUSH_INSN1(ret, current_location, putchilledstring, current_string);
780 }
781 } else {
782 PUSH_INSN1(ret, current_location, putobject, current_string);
783 }
784
785 current_string = Qnil;
786 stack_size++;
787 }
788 }
789 else {
790 PUSH_INSN(ret, *node_location, putnil);
791 }
792
793 return stack_size;
794}
795
796static void
797pm_compile_regexp_dynamic(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *parts, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
798{
799 rb_encoding *explicit_regexp_encoding = parse_regexp_encoding(scope_node, node);
800 rb_encoding *implicit_regexp_encoding = explicit_regexp_encoding != NULL ? explicit_regexp_encoding : scope_node->encoding;
801
802 int length = pm_interpolated_node_compile(iseq, parts, node_location, ret, popped, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false, false);
803 PUSH_INSN2(ret, *node_location, toregexp, INT2FIX(parse_regexp_flags(node) & 0xFF), INT2FIX(length));
804}
805
806static VALUE
807pm_source_file_value(const pm_source_file_node_t *node, const pm_scope_node_t *scope_node)
808{
809 const pm_string_t *filepath = &node->filepath;
810 size_t length = pm_string_length(filepath);
811
812 if (length > 0) {
813 rb_encoding *filepath_encoding = scope_node->filepath_encoding != NULL ? scope_node->filepath_encoding : rb_utf8_encoding();
814 return rb_enc_interned_str((const char *) pm_string_source(filepath), length, filepath_encoding);
815 }
816 else {
817 return rb_fstring_lit("<compiled>");
818 }
819}
820
825static VALUE
826pm_static_literal_string(rb_iseq_t *iseq, VALUE string, int line_number)
827{
828 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
829 VALUE str = rb_str_with_debug_created_info(string, rb_iseq_path(iseq), line_number);
830 RB_OBJ_SET_SHAREABLE(str);
831 return str;
832 }
833 else {
834 return rb_fstring(string);
835 }
836}
837
843static VALUE
844pm_static_literal_value(rb_iseq_t *iseq, const pm_node_t *node, pm_scope_node_t *scope_node)
845{
846 // Every node that comes into this function should already be marked as
847 // static literal. If it's not, then we have a bug somewhere.
848 RUBY_ASSERT(PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL));
849
850 switch (PM_NODE_TYPE(node)) {
851 case PM_ARRAY_NODE: {
852 const pm_array_node_t *cast = (const pm_array_node_t *) node;
853 const pm_node_list_t *elements = &cast->elements;
854
855 VALUE value = rb_ary_hidden_new(elements->size);
856 for (size_t index = 0; index < elements->size; index++) {
857 rb_ary_push(value, pm_static_literal_value(iseq, elements->nodes[index], scope_node));
858 }
859
860 RB_OBJ_SET_FROZEN_SHAREABLE(value);
861 return value;
862 }
863 case PM_FALSE_NODE:
864 return Qfalse;
865 case PM_FLOAT_NODE:
866 return parse_float((const pm_float_node_t *) node);
867 case PM_HASH_NODE: {
868 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
869 const pm_node_list_t *elements = &cast->elements;
870
871 VALUE array = rb_ary_hidden_new(elements->size * 2);
872 for (size_t index = 0; index < elements->size; index++) {
873 RUBY_ASSERT(PM_NODE_TYPE_P(elements->nodes[index], PM_ASSOC_NODE));
874 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) elements->nodes[index];
875 VALUE pair[2] = { pm_static_literal_value(iseq, cast->key, scope_node), pm_static_literal_value(iseq, cast->value, scope_node) };
876 rb_ary_cat(array, pair, 2);
877 }
878
879 VALUE value = rb_hash_alloc_fixed_size(Qfalse, elements->size);
880 rb_hash_bulk_insert(RARRAY_LEN(array), RARRAY_CONST_PTR(array), value);
881 RB_GC_GUARD(array);
882
883 RB_OBJ_SET_FROZEN_SHAREABLE(value);
884 return value;
885 }
886 case PM_IMAGINARY_NODE:
887 return parse_imaginary((const pm_imaginary_node_t *) node);
888 case PM_INTEGER_NODE:
889 return parse_integer((const pm_integer_node_t *) node);
890 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: {
892 return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts);
893 }
894 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
896 return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts);
897 }
898 case PM_INTERPOLATED_STRING_NODE: {
899 VALUE string = pm_static_literal_concat(iseq, &((const pm_interpolated_string_node_t *) node)->parts, scope_node, NULL, NULL, false);
900 int line_number = pm_node_line_number_cached(node, scope_node);
901 return pm_static_literal_string(iseq, string, line_number);
902 }
903 case PM_INTERPOLATED_SYMBOL_NODE: {
905 VALUE string = pm_static_literal_concat(iseq, &cast->parts, scope_node, NULL, NULL, true);
906
907 return ID2SYM(rb_intern_str(string));
908 }
909 case PM_MATCH_LAST_LINE_NODE: {
910 const pm_match_last_line_node_t *cast = (const pm_match_last_line_node_t *) node;
911 return parse_regexp_literal(iseq, scope_node, (const pm_node_t *) cast, &cast->unescaped);
912 }
913 case PM_NIL_NODE:
914 return Qnil;
915 case PM_RATIONAL_NODE:
916 return parse_rational((const pm_rational_node_t *) node);
917 case PM_REGULAR_EXPRESSION_NODE: {
919 return parse_regexp_literal(iseq, scope_node, (const pm_node_t *) cast, &cast->unescaped);
920 }
921 case PM_SOURCE_ENCODING_NODE:
922 return rb_enc_from_encoding(scope_node->encoding);
923 case PM_SOURCE_FILE_NODE: {
924 const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node;
925 return pm_source_file_value(cast, scope_node);
926 }
927 case PM_SOURCE_LINE_NODE:
928 return INT2FIX(pm_node_line_number_cached(node, scope_node));
929 case PM_STRING_NODE: {
930 const pm_string_node_t *cast = (const pm_string_node_t *) node;
931 return parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
932 }
933 case PM_SYMBOL_NODE:
934 return ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) node));
935 case PM_TRUE_NODE:
936 return Qtrue;
937 default:
938 rb_bug("Don't have a literal value for node type %s", pm_node_type(PM_NODE_TYPE(node)));
939 return Qfalse;
940 }
941}
942
947pm_code_location(pm_scope_node_t *scope_node, const pm_node_t *node)
948{
949 const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(node);
950 const pm_line_column_t end_location = PM_NODE_END_LINE_COLUMN(node);
951
952 return (rb_code_location_t) {
953 .beg_pos = { .lineno = start_location.line, .column = start_location.column },
954 .end_pos = { .lineno = end_location.line, .column = end_location.column }
955 };
956}
957
963#define PM_BRANCH_COVERAGE_P(iseq) (ISEQ_COVERAGE(iseq) && ISEQ_BRANCH_COVERAGE(iseq))
964
965static void
966pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond,
967 LABEL *then_label, LABEL *else_label, pm_scope_node_t *scope_node);
968
969static void
970pm_compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_node_t *cond, LABEL *then_label, LABEL *else_label, pm_scope_node_t *scope_node)
971{
972 const pm_node_location_t location = PM_NODE_START_LOCATION(cond);
973
974 DECL_ANCHOR(seq);
975
976 LABEL *label = NEW_LABEL(location.line);
977 if (!then_label) then_label = label;
978 else if (!else_label) else_label = label;
979
980 pm_compile_branch_condition(iseq, seq, cond, then_label, else_label, scope_node);
981
982 if (LIST_INSN_SIZE_ONE(seq)) {
983 INSN *insn = (INSN *) ELEM_FIRST_INSN(FIRST_ELEMENT(seq));
984 if (insn->insn_id == BIN(jump) && (LABEL *)(insn->operands[0]) == label) return;
985 }
986
987 if (label->refcnt) {
988 PUSH_LABEL(seq, label);
989 }
990
991 PUSH_SEQ(ret, seq);
992 return;
993}
994
995static void
996pm_compile_flip_flop_bound(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
997{
998 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
999
1000 if (PM_NODE_TYPE_P(node, PM_INTEGER_NODE)) {
1001 PM_COMPILE_NOT_POPPED(node);
1002
1003 VALUE operand = ID2SYM(rb_intern("$."));
1004 PUSH_INSN1(ret, location, getglobal, operand);
1005
1006 PUSH_SEND(ret, location, idEq, INT2FIX(1));
1007 if (popped) PUSH_INSN(ret, location, pop);
1008 }
1009 else {
1010 PM_COMPILE(node);
1011 }
1012}
1013
1014static void
1015pm_compile_flip_flop(const pm_flip_flop_node_t *flip_flop_node, LABEL *else_label, LABEL *then_label, rb_iseq_t *iseq, const int lineno, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1016{
1017 const pm_node_location_t location = { .line = lineno, .node_id = -1 };
1018 LABEL *lend = NEW_LABEL(location.line);
1019
1020 int again = !(flip_flop_node->base.flags & PM_RANGE_FLAGS_EXCLUDE_END);
1021
1022 rb_num_t count = ISEQ_FLIP_CNT_INCREMENT(ISEQ_BODY(iseq)->local_iseq) + VM_SVAR_FLIPFLOP_START;
1023 VALUE key = INT2FIX(count);
1024
1025 PUSH_INSN2(ret, location, getspecial, key, INT2FIX(0));
1026 PUSH_INSNL(ret, location, branchif, lend);
1027
1028 if (flip_flop_node->left) {
1029 pm_compile_flip_flop_bound(iseq, flip_flop_node->left, ret, popped, scope_node);
1030 }
1031 else {
1032 PUSH_INSN(ret, location, putnil);
1033 }
1034
1035 PUSH_INSNL(ret, location, branchunless, else_label);
1036 PUSH_INSN1(ret, location, putobject, Qtrue);
1037 PUSH_INSN1(ret, location, setspecial, key);
1038 if (!again) {
1039 PUSH_INSNL(ret, location, jump, then_label);
1040 }
1041
1042 PUSH_LABEL(ret, lend);
1043 if (flip_flop_node->right) {
1044 pm_compile_flip_flop_bound(iseq, flip_flop_node->right, ret, popped, scope_node);
1045 }
1046 else {
1047 PUSH_INSN(ret, location, putnil);
1048 }
1049
1050 PUSH_INSNL(ret, location, branchunless, then_label);
1051 PUSH_INSN1(ret, location, putobject, Qfalse);
1052 PUSH_INSN1(ret, location, setspecial, key);
1053 PUSH_INSNL(ret, location, jump, then_label);
1054}
1055
1056static void pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition);
1057
1058static void
1059pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond, LABEL *then_label, LABEL *else_label, pm_scope_node_t *scope_node)
1060{
1061 const pm_node_location_t location = PM_NODE_START_LOCATION(cond);
1062
1063again:
1064 switch (PM_NODE_TYPE(cond)) {
1065 case PM_AND_NODE: {
1066 const pm_and_node_t *cast = (const pm_and_node_t *) cond;
1067 pm_compile_logical(iseq, ret, cast->left, NULL, else_label, scope_node);
1068
1069 cond = cast->right;
1070 goto again;
1071 }
1072 case PM_OR_NODE: {
1073 const pm_or_node_t *cast = (const pm_or_node_t *) cond;
1074 pm_compile_logical(iseq, ret, cast->left, then_label, NULL, scope_node);
1075
1076 cond = cast->right;
1077 goto again;
1078 }
1079 case PM_FALSE_NODE:
1080 case PM_NIL_NODE:
1081 PUSH_INSNL(ret, location, jump, else_label);
1082 return;
1083 case PM_FLOAT_NODE:
1084 case PM_IMAGINARY_NODE:
1085 case PM_INTEGER_NODE:
1086 case PM_LAMBDA_NODE:
1087 case PM_RATIONAL_NODE:
1088 case PM_REGULAR_EXPRESSION_NODE:
1089 case PM_STRING_NODE:
1090 case PM_SYMBOL_NODE:
1091 case PM_TRUE_NODE:
1092 PUSH_INSNL(ret, location, jump, then_label);
1093 return;
1094 case PM_FLIP_FLOP_NODE:
1095 pm_compile_flip_flop((const pm_flip_flop_node_t *) cond, else_label, then_label, iseq, location.line, ret, false, scope_node);
1096 return;
1097 case PM_DEFINED_NODE: {
1098 const pm_defined_node_t *cast = (const pm_defined_node_t *) cond;
1099 pm_compile_defined_expr(iseq, cast->value, &location, ret, false, scope_node, true);
1100 break;
1101 }
1102 default: {
1103 DECL_ANCHOR(cond_seq);
1104 pm_compile_node(iseq, cond, cond_seq, false, scope_node);
1105
1106 if (LIST_INSN_SIZE_ONE(cond_seq)) {
1107 INSN *insn = (INSN *) ELEM_FIRST_INSN(FIRST_ELEMENT(cond_seq));
1108
1109 if (insn->insn_id == BIN(putobject)) {
1110 if (RTEST(insn->operands[0])) {
1111 PUSH_INSNL(ret, location, jump, then_label);
1112 // maybe unreachable
1113 return;
1114 }
1115 else {
1116 PUSH_INSNL(ret, location, jump, else_label);
1117 return;
1118 }
1119 }
1120 }
1121
1122 PUSH_SEQ(ret, cond_seq);
1123 break;
1124 }
1125 }
1126
1127 PUSH_INSNL(ret, location, branchunless, else_label);
1128 PUSH_INSNL(ret, location, jump, then_label);
1129}
1130
1134static void
1135pm_compile_conditional(rb_iseq_t *iseq, const pm_node_location_t *node_location, pm_node_type_t type, const pm_node_t *node, const pm_statements_node_t *statements, const pm_node_t *subsequent, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1136{
1137 const pm_node_location_t location = *node_location;
1138 LABEL *then_label = NEW_LABEL(location.line);
1139 LABEL *else_label = NEW_LABEL(location.line);
1140 LABEL *end_label = NULL;
1141
1142 DECL_ANCHOR(cond_seq);
1143 pm_compile_branch_condition(iseq, cond_seq, predicate, then_label, else_label, scope_node);
1144 PUSH_SEQ(ret, cond_seq);
1145
1146 rb_code_location_t conditional_location = { 0 };
1147 VALUE branches = Qfalse;
1148
1149 if (then_label->refcnt && else_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) {
1150 conditional_location = pm_code_location(scope_node, node);
1151 branches = decl_branch_base(iseq, PTR2NUM(node), &conditional_location, type == PM_IF_NODE ? "if" : "unless");
1152 }
1153
1154 if (then_label->refcnt) {
1155 PUSH_LABEL(ret, then_label);
1156
1157 DECL_ANCHOR(then_seq);
1158
1159 if (statements != NULL) {
1160 pm_compile_node(iseq, (const pm_node_t *) statements, then_seq, popped, scope_node);
1161 }
1162 else if (!popped) {
1163 PUSH_SYNTHETIC_PUTNIL(then_seq, iseq);
1164 }
1165
1166 if (else_label->refcnt) {
1167 // Establish branch coverage for the then block.
1168 if (PM_BRANCH_COVERAGE_P(iseq)) {
1169 rb_code_location_t branch_location;
1170
1171 if (statements != NULL) {
1172 branch_location = pm_code_location(scope_node, (const pm_node_t *) statements);
1173 } else if (type == PM_IF_NODE) {
1174 pm_line_column_t predicate_end = PM_NODE_END_LINE_COLUMN(predicate);
1175 branch_location = (rb_code_location_t) {
1176 .beg_pos = { .lineno = predicate_end.line, .column = predicate_end.column },
1177 .end_pos = { .lineno = predicate_end.line, .column = predicate_end.column }
1178 };
1179 } else {
1180 branch_location = conditional_location;
1181 }
1182
1183 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, type == PM_IF_NODE ? "then" : "else", branches);
1184 }
1185
1186 end_label = NEW_LABEL(location.line);
1187 PUSH_INSNL(then_seq, location, jump, end_label);
1188 if (!popped) PUSH_INSN(then_seq, location, pop);
1189 }
1190
1191 PUSH_SEQ(ret, then_seq);
1192 }
1193
1194 if (else_label->refcnt) {
1195 PUSH_LABEL(ret, else_label);
1196
1197 DECL_ANCHOR(else_seq);
1198
1199 if (subsequent != NULL) {
1200 pm_compile_node(iseq, subsequent, else_seq, popped, scope_node);
1201 }
1202 else if (!popped) {
1203 PUSH_SYNTHETIC_PUTNIL(else_seq, iseq);
1204 }
1205
1206 // Establish branch coverage for the else block.
1207 if (then_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) {
1208 rb_code_location_t branch_location;
1209
1210 if (subsequent == NULL) {
1211 branch_location = conditional_location;
1212 } else if (PM_NODE_TYPE_P(subsequent, PM_ELSE_NODE)) {
1213 const pm_else_node_t *else_node = (const pm_else_node_t *) subsequent;
1214 branch_location = pm_code_location(scope_node, else_node->statements != NULL ? ((const pm_node_t *) else_node->statements) : (const pm_node_t *) else_node);
1215 } else {
1216 branch_location = pm_code_location(scope_node, (const pm_node_t *) subsequent);
1217 }
1218
1219 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 1, type == PM_IF_NODE ? "else" : "then", branches);
1220 }
1221
1222 PUSH_SEQ(ret, else_seq);
1223 }
1224
1225 if (end_label) {
1226 PUSH_LABEL(ret, end_label);
1227 }
1228
1229 return;
1230}
1231
1235static void
1236pm_compile_loop(rb_iseq_t *iseq, const pm_node_location_t *node_location, pm_node_flags_t flags, enum pm_node_type type, const pm_node_t *node, const pm_statements_node_t *statements, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1237{
1238 const pm_node_location_t location = *node_location;
1239
1240 LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
1241 LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
1242 LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
1243
1244 LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(location.line); /* next */
1245 LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(location.line); /* redo */
1246 LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(location.line); /* break */
1247 LABEL *end_label = NEW_LABEL(location.line);
1248 LABEL *adjust_label = NEW_LABEL(location.line);
1249
1250 LABEL *next_catch_label = NEW_LABEL(location.line);
1251 LABEL *tmp_label = NULL;
1252
1253 // We're pushing onto the ensure stack because breaks need to break out of
1254 // this loop and not break into the ensure statements within the same
1255 // lexical scope.
1257 push_ensure_entry(iseq, &enl, NULL, NULL);
1258
1259 // begin; end while true
1260 if (flags & PM_LOOP_FLAGS_BEGIN_MODIFIER) {
1261 tmp_label = NEW_LABEL(location.line);
1262 PUSH_INSNL(ret, location, jump, tmp_label);
1263 }
1264 else {
1265 // while true; end
1266 PUSH_INSNL(ret, location, jump, next_label);
1267 }
1268
1269 PUSH_LABEL(ret, adjust_label);
1270 PUSH_INSN(ret, location, putnil);
1271 PUSH_LABEL(ret, next_catch_label);
1272 PUSH_INSN(ret, location, pop);
1273 PUSH_INSNL(ret, location, jump, next_label);
1274 if (tmp_label) PUSH_LABEL(ret, tmp_label);
1275
1276 PUSH_LABEL(ret, redo_label);
1277
1278 // Establish branch coverage for the loop.
1279 if (PM_BRANCH_COVERAGE_P(iseq)) {
1280 rb_code_location_t loop_location = pm_code_location(scope_node, node);
1281 VALUE branches = decl_branch_base(iseq, PTR2NUM(node), &loop_location, type == PM_WHILE_NODE ? "while" : "until");
1282
1283 rb_code_location_t branch_location = statements != NULL ? pm_code_location(scope_node, (const pm_node_t *) statements) : loop_location;
1284 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, "body", branches);
1285 }
1286
1287 if (statements != NULL) PM_COMPILE_POPPED((const pm_node_t *) statements);
1288 PUSH_LABEL(ret, next_label);
1289
1290 if (type == PM_WHILE_NODE) {
1291 pm_compile_branch_condition(iseq, ret, predicate, redo_label, end_label, scope_node);
1292 }
1293 else if (type == PM_UNTIL_NODE) {
1294 pm_compile_branch_condition(iseq, ret, predicate, end_label, redo_label, scope_node);
1295 }
1296
1297 PUSH_LABEL(ret, end_label);
1298 PUSH_ADJUST_RESTORE(ret, adjust_label);
1299 PUSH_INSN(ret, location, putnil);
1300
1301 PUSH_LABEL(ret, break_label);
1302 if (popped) PUSH_INSN(ret, location, pop);
1303
1304 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL, break_label);
1305 PUSH_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL, next_catch_label);
1306 PUSH_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL, ISEQ_COMPILE_DATA(iseq)->redo_label);
1307
1308 ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
1309 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
1310 ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
1311 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->prev;
1312
1313 return;
1314}
1315
1316// This recurses through scopes and finds the local index at any scope level
1317// It also takes a pointer to depth, and increments depth appropriately
1318// according to the depth of the local.
1319static pm_local_index_t
1320pm_lookup_local_index(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, pm_constant_id_t constant_id, int start_depth)
1321{
1322 pm_local_index_t lindex = { 0 };
1323 int local_index;
1324
1325 int level;
1326 for (level = 0; level < start_depth; level++) {
1327 scope_node = scope_node->previous;
1328 }
1329
1330 while (!pm_index_lookup_table_lookup(&scope_node->index_lookup_table, constant_id, &local_index))
1331 {
1332 level++;
1333
1334 if (scope_node->previous) {
1335 scope_node = scope_node->previous;
1336 }
1337 else {
1338 // We have recursed up all scope nodes
1339 // and have not found the local yet
1340 rb_bug("Local with constant_id %u does not exist", (unsigned int) constant_id);
1341 }
1342 }
1343
1344 lindex.level = level;
1345 lindex.index = scope_node->local_table_for_iseq_size - (int) local_index;
1346 return lindex;
1347}
1348
1349// This returns the CRuby ID which maps to the pm_constant_id_t
1350//
1351// Constant_ids in prism are indexes of the constants in prism's constant pool.
1352// We add a constants mapping on the scope_node which is a mapping from
1353// these constant_id indexes to the CRuby IDs that they represent.
1354// This helper method allows easy access to those IDs
1355static inline ID
1356pm_constant_id_lookup(const pm_scope_node_t *scope_node, pm_constant_id_t constant_id)
1357{
1358 RUBY_ASSERT(constant_id >= 1 && constant_id <= pm_parser_constants_size(scope_node->parser));
1359 return scope_node->constants[constant_id - 1];
1360}
1361
1362static rb_iseq_t *
1363pm_new_child_iseq(rb_iseq_t *iseq, pm_scope_node_t *node, VALUE name, const rb_iseq_t *parent, enum rb_iseq_type type, int line_no)
1364{
1365 debugs("[new_child_iseq]> ---------------------------------------\n");
1366 int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
1367 rb_iseq_t *ret_iseq = pm_iseq_build(node, name,
1368 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
1369 line_no, parent,
1370 isolated_depth ? isolated_depth + 1 : 0,
1371 type, ISEQ_COMPILE_DATA(iseq)->option);
1372 debugs("[new_child_iseq]< ---------------------------------------\n");
1373 return ret_iseq;
1374}
1375
1376static int
1377pm_cpath_const_p(const pm_node_t *node)
1378{
1379 switch (PM_NODE_TYPE(node)) {
1380 case PM_CONSTANT_READ_NODE:
1381 return TRUE;
1382 case PM_CONSTANT_PATH_NODE:
1383 {
1384 const pm_node_t *parent = ((const pm_constant_path_node_t *) node)->parent;
1385 if (!parent) return TRUE; /* ::Foo */
1386 return pm_cpath_const_p(parent);
1387 }
1388 default:
1389 return FALSE;
1390 }
1391}
1392
1393static int
1394pm_compile_class_path(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1395{
1396 if (PM_NODE_TYPE_P(node, PM_CONSTANT_PATH_NODE)) {
1397 const pm_node_t *parent = ((const pm_constant_path_node_t *) node)->parent;
1398
1399 if (parent) {
1400 /* Bar::Foo or expr::Foo */
1401 PM_COMPILE(parent);
1402 int flags = VM_DEFINECLASS_FLAG_SCOPED;
1403 if (!pm_cpath_const_p(parent)) {
1404 flags |= VM_DEFINECLASS_FLAG_DYNAMIC_CREF;
1405 }
1406 return flags;
1407 }
1408 else {
1409 /* toplevel class ::Foo */
1410 PUSH_INSN1(ret, *node_location, putobject, rb_cObject);
1411 return VM_DEFINECLASS_FLAG_SCOPED;
1412 }
1413 }
1414 else {
1415 /* class at cbase Foo */
1416 PUSH_INSN1(ret, *node_location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
1417 return 0;
1418 }
1419}
1420
1425static void
1426pm_compile_call_and_or_write_node(rb_iseq_t *iseq, bool and_node, const pm_node_t *receiver, const pm_node_t *value, pm_constant_id_t write_name, pm_constant_id_t read_name, bool safe_nav, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1427{
1428 const pm_node_location_t location = *node_location;
1429 LABEL *lfin = NEW_LABEL(location.line);
1430 LABEL *lcfin = NEW_LABEL(location.line);
1431 LABEL *lskip = NULL;
1432
1433 int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
1434 ID id_read_name = pm_constant_id_lookup(scope_node, read_name);
1435
1436 PM_COMPILE_NOT_POPPED(receiver);
1437 if (safe_nav) {
1438 lskip = NEW_LABEL(location.line);
1439 PUSH_INSN(ret, location, dup);
1440 PUSH_INSNL(ret, location, branchnil, lskip);
1441 }
1442
1443 PUSH_INSN(ret, location, dup);
1444 PUSH_SEND_WITH_FLAG(ret, location, id_read_name, INT2FIX(0), INT2FIX(flag));
1445 if (!popped) PUSH_INSN(ret, location, dup);
1446
1447 if (and_node) {
1448 PUSH_INSNL(ret, location, branchunless, lcfin);
1449 }
1450 else {
1451 PUSH_INSNL(ret, location, branchif, lcfin);
1452 }
1453
1454 if (!popped) PUSH_INSN(ret, location, pop);
1455 PM_COMPILE_NOT_POPPED(value);
1456
1457 if (!popped) {
1458 PUSH_INSN(ret, location, swap);
1459 PUSH_INSN1(ret, location, topn, INT2FIX(1));
1460 }
1461
1462 ID id_write_name = pm_constant_id_lookup(scope_node, write_name);
1463 PUSH_SEND_WITH_FLAG(ret, location, id_write_name, INT2FIX(1), INT2FIX(flag));
1464 PUSH_INSNL(ret, location, jump, lfin);
1465
1466 PUSH_LABEL(ret, lcfin);
1467 if (!popped) PUSH_INSN(ret, location, swap);
1468
1469 PUSH_LABEL(ret, lfin);
1470
1471 if (lskip && popped) PUSH_LABEL(ret, lskip);
1472 PUSH_INSN(ret, location, pop);
1473 if (lskip && !popped) PUSH_LABEL(ret, lskip);
1474}
1475
1476static void pm_compile_shareable_constant_value(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_flags_t shareability, VALUE path, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, bool top);
1477
1483static void
1484pm_compile_hash_elements(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *elements, const pm_node_flags_t shareability, VALUE path, bool argument, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
1485{
1486 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
1487
1488 // If this element is not popped, then we need to create the hash on the
1489 // stack. Neighboring plain assoc nodes should be grouped together (either
1490 // by newhash or hash merge). Double splat nodes should be merged using the
1491 // merge_kwd method call.
1492 const int max_stack_length = 0x100;
1493 const unsigned int min_tmp_hash_length = 0x800;
1494
1495 int stack_length = 0;
1496 bool first_chunk = true;
1497
1498 // This is an optimization wherein we keep track of whether or not the
1499 // previous element was a static literal. If it was, then we do not attempt
1500 // to check if we have a subhash that can be optimized. If it was not, then
1501 // we do check.
1502 bool static_literal = false;
1503
1504 DECL_ANCHOR(anchor);
1505
1506 // Convert pushed elements to a hash, and merge if needed.
1507#define FLUSH_CHUNK \
1508 if (stack_length) { \
1509 if (first_chunk) { \
1510 PUSH_SEQ(ret, anchor); \
1511 PUSH_INSN1(ret, location, newhash, INT2FIX(stack_length)); \
1512 first_chunk = false; \
1513 } \
1514 else { \
1515 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); \
1516 PUSH_INSN(ret, location, swap); \
1517 PUSH_SEQ(ret, anchor); \
1518 PUSH_SEND(ret, location, id_core_hash_merge_ptr, INT2FIX(stack_length + 1)); \
1519 } \
1520 INIT_ANCHOR(anchor); \
1521 stack_length = 0; \
1522 }
1523
1524 for (size_t index = 0; index < elements->size; index++) {
1525 const pm_node_t *element = elements->nodes[index];
1526
1527 switch (PM_NODE_TYPE(element)) {
1528 case PM_ASSOC_NODE: {
1529 // Pre-allocation check (this branch can be omitted).
1530 if (
1531 (shareability == 0) &&
1532 PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL) && (
1533 (!static_literal && ((index + min_tmp_hash_length) < elements->size)) ||
1534 (first_chunk && stack_length == 0)
1535 )
1536 ) {
1537 // Count the elements that are statically-known.
1538 size_t count = 1;
1539 while (index + count < elements->size && PM_NODE_FLAG_P(elements->nodes[index + count], PM_NODE_FLAG_STATIC_LITERAL)) count++;
1540
1541 if ((first_chunk && stack_length == 0) || count >= min_tmp_hash_length) {
1542 // The subsequence of elements in this hash is long enough
1543 // to merit its own hash.
1544 VALUE ary = rb_ary_hidden_new(count);
1545
1546 // Create a hidden hash.
1547 for (size_t tmp_end = index + count; index < tmp_end; index++) {
1548 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[index];
1549
1550 VALUE elem[2] = {
1551 pm_static_literal_value(iseq, assoc->key, scope_node),
1552 pm_static_literal_value(iseq, assoc->value, scope_node)
1553 };
1554
1555 rb_ary_cat(ary, elem, 2);
1556 }
1557 index --;
1558
1559 VALUE hash = rb_hash_alloc_fixed_size(Qfalse, RARRAY_LEN(ary) / 2);
1560 rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary), hash);
1561 RB_GC_GUARD(ary);
1562 RB_OBJ_SET_FROZEN_SHAREABLE(hash);
1563
1564 // Emit optimized code.
1565 FLUSH_CHUNK;
1566 if (first_chunk) {
1567 PUSH_INSN1(ret, location, duphash, hash);
1568 first_chunk = false;
1569 }
1570 else {
1571 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1572 PUSH_INSN(ret, location, swap);
1573 PUSH_INSN1(ret, location, putobject, hash);
1574 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1575 }
1576
1577 break;
1578 }
1579 else {
1580 static_literal = true;
1581 }
1582 }
1583 else {
1584 static_literal = false;
1585 }
1586
1587 // If this is a plain assoc node, then we can compile it directly
1588 // and then add the total number of values on the stack.
1589 if (shareability == 0) {
1590 pm_compile_node(iseq, element, anchor, false, scope_node);
1591 }
1592 else {
1593 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
1594 pm_compile_shareable_constant_value(iseq, assoc->key, shareability, path, ret, scope_node, false);
1595 pm_compile_shareable_constant_value(iseq, assoc->value, shareability, path, ret, scope_node, false);
1596 }
1597
1598 if ((stack_length += 2) >= max_stack_length) FLUSH_CHUNK;
1599 break;
1600 }
1601 case PM_ASSOC_SPLAT_NODE: {
1602 FLUSH_CHUNK;
1603
1604 const pm_assoc_splat_node_t *assoc_splat = (const pm_assoc_splat_node_t *) element;
1605 bool empty_hash = assoc_splat->value != NULL && (
1606 (PM_NODE_TYPE_P(assoc_splat->value, PM_HASH_NODE) && ((const pm_hash_node_t *) assoc_splat->value)->elements.size == 0) ||
1607 PM_NODE_TYPE_P(assoc_splat->value, PM_NIL_NODE)
1608 );
1609
1610 bool first_element = first_chunk && stack_length == 0;
1611 bool last_element = index == elements->size - 1;
1612 bool only_element = first_element && last_element;
1613
1614 if (empty_hash) {
1615 if (only_element && argument) {
1616 // **{} appears at the only keyword argument in method call,
1617 // so it won't be modified.
1618 //
1619 // This is only done for method calls and not for literal
1620 // hashes, because literal hashes should always result in a
1621 // new hash.
1622 PUSH_INSN(ret, location, putnil);
1623 }
1624 else if (first_element) {
1625 // **{} appears as the first keyword argument, so it may be
1626 // modified. We need to create a fresh hash object.
1627 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
1628 }
1629 // Any empty keyword splats that are not the first can be
1630 // ignored since merging an empty hash into the existing hash is
1631 // the same as not merging it.
1632 }
1633 else {
1634 if (only_element && argument) {
1635 // ** is only keyword argument in the method call. Use it
1636 // directly. This will be not be flagged as mutable. This is
1637 // only done for method calls and not for literal hashes,
1638 // because literal hashes should always result in a new
1639 // hash.
1640 if (shareability == 0) {
1641 PM_COMPILE_NOT_POPPED(element);
1642 }
1643 else {
1644 pm_compile_shareable_constant_value(iseq, element, shareability, path, ret, scope_node, false);
1645 }
1646 }
1647 else {
1648 // There is more than one keyword argument, or this is not a
1649 // method call. In that case, we need to add an empty hash
1650 // (if first keyword), or merge the hash to the accumulated
1651 // hash (if not the first keyword).
1652 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1653
1654 if (first_element) {
1655 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
1656 }
1657 else {
1658 PUSH_INSN(ret, location, swap);
1659 }
1660
1661 if (shareability == 0) {
1662 PM_COMPILE_NOT_POPPED(element);
1663 }
1664 else {
1665 pm_compile_shareable_constant_value(iseq, element, shareability, path, ret, scope_node, false);
1666 }
1667
1668 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1669 }
1670 }
1671
1672 first_chunk = false;
1673 static_literal = false;
1674 break;
1675 }
1676 default:
1677 RUBY_ASSERT("Invalid node type for hash" && false);
1678 break;
1679 }
1680 }
1681
1682 FLUSH_CHUNK;
1683#undef FLUSH_CHUNK
1684}
1685
1686#define SPLATARRAY_FALSE 0
1687#define SPLATARRAY_TRUE 1
1688#define DUP_SINGLE_KW_SPLAT 2
1689
1690// This is details. Users should call pm_setup_args() instead.
1691static int
1692pm_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)
1693{
1694 const pm_node_location_t location = *node_location;
1695
1696 int orig_argc = 0;
1697 bool has_splat = false;
1698 bool has_keyword_splat = false;
1699
1700 if (arguments_node == NULL) {
1701 if (*flags & VM_CALL_FCALL) {
1702 *flags |= VM_CALL_VCALL;
1703 }
1704 }
1705 else {
1706 const pm_node_list_t *arguments = &arguments_node->arguments;
1707 has_keyword_splat = PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT);
1708
1709 // We count the number of elements post the splat node that are not keyword elements to
1710 // eventually pass as an argument to newarray
1711 int post_splat_counter = 0;
1712 const pm_node_t *argument;
1713
1714 PM_NODE_LIST_FOREACH(arguments, index, argument) {
1715 switch (PM_NODE_TYPE(argument)) {
1716 // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
1717 case PM_KEYWORD_HASH_NODE: {
1718 const pm_keyword_hash_node_t *keyword_arg = (const pm_keyword_hash_node_t *) argument;
1719 const pm_node_list_t *elements = &keyword_arg->elements;
1720
1721 if (has_keyword_splat || has_splat) {
1722 *flags |= VM_CALL_KW_SPLAT;
1723 has_keyword_splat = true;
1724
1725 if (elements->size > 1 || !(elements->size == 1 && PM_NODE_TYPE_P(elements->nodes[0], PM_ASSOC_SPLAT_NODE))) {
1726 // A new hash will be created for the keyword arguments
1727 // in this case, so mark the method as passing mutable
1728 // keyword splat.
1729 *flags |= VM_CALL_KW_SPLAT_MUT;
1730 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1731 }
1732 else if (*dup_rest & DUP_SINGLE_KW_SPLAT) {
1733 *flags |= VM_CALL_KW_SPLAT_MUT;
1734 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1735 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
1736 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1737 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1738 }
1739 else {
1740 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1741 }
1742 }
1743 else {
1744 // We need to first figure out if all elements of the
1745 // KeywordHashNode are AssocNodes with symbol keys.
1746 if (PM_NODE_FLAG_P(keyword_arg, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS)) {
1747 // If they are all symbol keys then we can pass them as
1748 // keyword arguments. The first thing we need to do is
1749 // deduplicate. We'll do this using the combination of a
1750 // Ruby hash and a Ruby array.
1751 VALUE stored_indices = rb_hash_new();
1752 VALUE keyword_indices = rb_ary_new_capa(elements->size);
1753
1754 size_t size = 0;
1755 for (size_t element_index = 0; element_index < elements->size; element_index++) {
1756 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1757
1758 // Retrieve the stored index from the hash for this
1759 // keyword.
1760 VALUE keyword = pm_static_literal_value(iseq, assoc->key, scope_node);
1761 VALUE stored_index = rb_hash_aref(stored_indices, keyword);
1762
1763 // If this keyword was already seen in the hash,
1764 // then mark the array at that index as false and
1765 // decrement the keyword size.
1766 if (!NIL_P(stored_index)) {
1767 rb_ary_store(keyword_indices, NUM2LONG(stored_index), Qfalse);
1768 size--;
1769 }
1770
1771 // Store (and possibly overwrite) the index for this
1772 // keyword in the hash, mark the array at that index
1773 // as true, and increment the keyword size.
1774 rb_hash_aset(stored_indices, keyword, ULONG2NUM(element_index));
1775 rb_ary_store(keyword_indices, (long) element_index, Qtrue);
1776 size++;
1777 }
1778
1779 *kw_arg = rb_xmalloc_mul_add(size, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
1780 *flags |= VM_CALL_KWARG;
1781
1782 VALUE *keywords = (*kw_arg)->keywords;
1783 (*kw_arg)->references = 0;
1784 (*kw_arg)->keyword_len = (int) size;
1785
1786 size_t keyword_index = 0;
1787 for (size_t element_index = 0; element_index < elements->size; element_index++) {
1788 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1789 bool popped = true;
1790
1791 if (rb_ary_entry(keyword_indices, (long) element_index) == Qtrue) {
1792 keywords[keyword_index++] = pm_static_literal_value(iseq, assoc->key, scope_node);
1793 popped = false;
1794 }
1795
1796 PM_COMPILE(assoc->value);
1797 }
1798
1799 RUBY_ASSERT(keyword_index == size);
1800 }
1801 else {
1802 // If they aren't all symbol keys then we need to
1803 // construct a new hash and pass that as an argument.
1804 orig_argc++;
1805 *flags |= VM_CALL_KW_SPLAT;
1806
1807 size_t size = elements->size;
1808 if (size > 1) {
1809 // A new hash will be created for the keyword
1810 // arguments in this case, so mark the method as
1811 // passing mutable keyword splat.
1812 *flags |= VM_CALL_KW_SPLAT_MUT;
1813 }
1814
1815 for (size_t element_index = 0; element_index < size; element_index++) {
1816 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1817 PM_COMPILE_NOT_POPPED(assoc->key);
1818 PM_COMPILE_NOT_POPPED(assoc->value);
1819 }
1820
1821 PUSH_INSN1(ret, location, newhash, INT2FIX(size * 2));
1822 }
1823 }
1824 break;
1825 }
1826 case PM_SPLAT_NODE: {
1827 *flags |= VM_CALL_ARGS_SPLAT;
1828 const pm_splat_node_t *splat_node = (const pm_splat_node_t *) argument;
1829
1830 if (splat_node->expression) {
1831 PM_COMPILE_NOT_POPPED(splat_node->expression);
1832 }
1833 else {
1834 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
1835 PUSH_GETLOCAL(ret, location, index.index, index.level);
1836 }
1837
1838 bool first_splat = !has_splat;
1839
1840 if (first_splat) {
1841 // If this is the first splat array seen and it's not the
1842 // last parameter, we want splatarray to dup it.
1843 //
1844 // foo(a, *b, c)
1845 // ^^
1846 if (index + 1 < arguments->size || has_regular_blockarg) {
1847 PUSH_INSN1(ret, location, splatarray, (*dup_rest & SPLATARRAY_TRUE) ? Qtrue : Qfalse);
1848 if (*dup_rest & SPLATARRAY_TRUE) *dup_rest &= ~SPLATARRAY_TRUE;
1849 }
1850 // If this is the first spalt array seen and it's the last
1851 // parameter, we don't want splatarray to dup it.
1852 //
1853 // foo(a, *b)
1854 // ^^
1855 else {
1856 PUSH_INSN1(ret, location, splatarray, Qfalse);
1857 }
1858 }
1859 else {
1860 // If this is not the first splat array seen and it is also
1861 // the last parameter, we don't want splatarray to dup it
1862 // and we need to concat the array.
1863 //
1864 // foo(a, *b, *c)
1865 // ^^
1866 PUSH_INSN(ret, location, concattoarray);
1867 }
1868
1869 has_splat = true;
1870 post_splat_counter = 0;
1871
1872 break;
1873 }
1874 case PM_FORWARDING_ARGUMENTS_NODE: { // not counted in argc return value
1875 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
1876
1877 if (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
1878 *flags |= VM_CALL_FORWARDING;
1879
1880 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_DOT3, 0);
1881 PUSH_GETLOCAL(ret, location, mult_local.index, mult_local.level);
1882
1883 break;
1884 }
1885
1886 if (has_splat) {
1887 // If we already have a splat, we're concatenating to existing array
1888 orig_argc += 1;
1889 } else {
1890 orig_argc += 2;
1891 }
1892
1893 *flags |= VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KW_SPLAT;
1894
1895 // Forwarding arguments nodes are treated as foo(*, **, &)
1896 // So foo(...) equals foo(*, **, &) and as such the local
1897 // table for this method is known in advance
1898 //
1899 // Push the *
1900 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
1901 PUSH_GETLOCAL(ret, location, mult_local.index, mult_local.level);
1902
1903 if (has_splat) {
1904 // If we already have a splat, we need to concatenate arrays
1905 PUSH_INSN(ret, location, concattoarray);
1906 } else {
1907 PUSH_INSN1(ret, location, splatarray, Qfalse);
1908 }
1909
1910 // Push the **
1911 pm_local_index_t pow_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_POW, 0);
1912 PUSH_GETLOCAL(ret, location, pow_local.index, pow_local.level);
1913
1914 // Push the &
1915 pm_local_index_t and_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_AND, 0);
1916 PUSH_INSN2(ret, location, getblockparamproxy, INT2FIX(and_local.index + VM_ENV_DATA_SIZE - 1), INT2FIX(and_local.level));
1917
1918 break;
1919 }
1920 default: {
1921 post_splat_counter++;
1922 PM_COMPILE_NOT_POPPED(argument);
1923
1924 // If we have a splat and we've seen a splat, we need to process
1925 // everything after the splat.
1926 if (has_splat) {
1927 // Stack items are turned into an array and concatenated in
1928 // the following cases:
1929 //
1930 // If the next node is a splat:
1931 //
1932 // foo(*a, b, *c)
1933 //
1934 // If the next node is a kwarg or kwarg splat:
1935 //
1936 // foo(*a, b, c: :d)
1937 // foo(*a, b, **c)
1938 //
1939 // If the next node is a forwarding argument:
1940 //
1941 // foo(*a, b, ...)
1942 //
1943 // If the next node is NULL (we have hit the end):
1944 //
1945 // foo(*a, b)
1946 if (index == arguments->size - 1) {
1947 RUBY_ASSERT(post_splat_counter > 0);
1948 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(post_splat_counter));
1949 }
1950 else {
1951 pm_node_t *next_arg = arguments->nodes[index + 1];
1952
1953 switch (PM_NODE_TYPE(next_arg)) {
1954 // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
1955 case PM_KEYWORD_HASH_NODE: {
1956 PUSH_INSN1(ret, location, newarray, INT2FIX(post_splat_counter));
1957 PUSH_INSN(ret, location, concatarray);
1958 break;
1959 }
1960 case PM_SPLAT_NODE: {
1961 PUSH_INSN1(ret, location, newarray, INT2FIX(post_splat_counter));
1962 PUSH_INSN(ret, location, concatarray);
1963 break;
1964 }
1965 case PM_FORWARDING_ARGUMENTS_NODE: {
1966 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(post_splat_counter));
1967 break;
1968 }
1969 default:
1970 break;
1971 }
1972 }
1973 }
1974 else {
1975 orig_argc++;
1976 }
1977 }
1978 }
1979 }
1980 }
1981
1982 if (has_splat) orig_argc++;
1983 if (has_keyword_splat) orig_argc++;
1984 return orig_argc;
1985}
1986
1991static inline bool
1992pm_setup_args_dup_rest_p(const pm_node_t *node)
1993{
1994 switch (PM_NODE_TYPE(node)) {
1995 case PM_BACK_REFERENCE_READ_NODE:
1996 case PM_CLASS_VARIABLE_READ_NODE:
1997 case PM_CONSTANT_READ_NODE:
1998 case PM_FALSE_NODE:
1999 case PM_FLOAT_NODE:
2000 case PM_GLOBAL_VARIABLE_READ_NODE:
2001 case PM_IMAGINARY_NODE:
2002 case PM_INSTANCE_VARIABLE_READ_NODE:
2003 case PM_INTEGER_NODE:
2004 case PM_LAMBDA_NODE:
2005 case PM_LOCAL_VARIABLE_READ_NODE:
2006 case PM_NIL_NODE:
2007 case PM_NUMBERED_REFERENCE_READ_NODE:
2008 case PM_RATIONAL_NODE:
2009 case PM_REGULAR_EXPRESSION_NODE:
2010 case PM_SELF_NODE:
2011 case PM_STRING_NODE:
2012 case PM_SYMBOL_NODE:
2013 case PM_TRUE_NODE:
2014 return false;
2015 case PM_CONSTANT_PATH_NODE: {
2016 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
2017 if (cast->parent != NULL) {
2018 return pm_setup_args_dup_rest_p(cast->parent);
2019 }
2020 return false;
2021 }
2022 case PM_IMPLICIT_NODE:
2023 return pm_setup_args_dup_rest_p(((const pm_implicit_node_t *) node)->value);
2024 case PM_ARRAY_NODE: {
2025 const pm_array_node_t *cast = (const pm_array_node_t *) node;
2026 for (size_t index = 0; index < cast->elements.size; index++) {
2027 if (pm_setup_args_dup_rest_p(cast->elements.nodes[index])) {
2028 return true;
2029 }
2030 }
2031 return false;
2032 }
2033 default:
2034 return true;
2035 }
2036}
2037
2041static int
2042pm_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)
2043{
2044 int dup_rest = SPLATARRAY_TRUE;
2045
2046 const pm_node_list_t *arguments;
2047 size_t arguments_size;
2048
2049 // Calls like foo(1, *f, **hash) that use splat and kwsplat could be
2050 // eligible for eliding duping the rest array (dup_reset=false).
2051 if (
2052 arguments_node != NULL &&
2053 (arguments = &arguments_node->arguments, arguments_size = arguments->size) >= 2 &&
2054 PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT) &&
2055 !PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS) &&
2056 PM_NODE_TYPE_P(arguments->nodes[arguments_size - 1], PM_KEYWORD_HASH_NODE)
2057 ) {
2058 // Start by assuming that dup_rest=false, then check each element of the
2059 // hash to ensure we don't need to flip it back to true (in case one of
2060 // the elements could potentially mutate the array).
2061 dup_rest = SPLATARRAY_FALSE;
2062
2063 const pm_keyword_hash_node_t *keyword_hash = (const pm_keyword_hash_node_t *) arguments->nodes[arguments_size - 1];
2064 const pm_node_list_t *elements = &keyword_hash->elements;
2065
2066 for (size_t index = 0; dup_rest == SPLATARRAY_FALSE && index < elements->size; index++) {
2067 const pm_node_t *element = elements->nodes[index];
2068
2069 switch (PM_NODE_TYPE(element)) {
2070 case PM_ASSOC_NODE: {
2071 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
2072 if (pm_setup_args_dup_rest_p(assoc->key) || pm_setup_args_dup_rest_p(assoc->value)) dup_rest = SPLATARRAY_TRUE;
2073 break;
2074 }
2075 case PM_ASSOC_SPLAT_NODE: {
2076 const pm_assoc_splat_node_t *assoc = (const pm_assoc_splat_node_t *) element;
2077 if (assoc->value != NULL && pm_setup_args_dup_rest_p(assoc->value)) dup_rest = SPLATARRAY_TRUE;
2078 break;
2079 }
2080 default:
2081 break;
2082 }
2083 }
2084 }
2085
2086 int initial_dup_rest = dup_rest;
2087 int argc;
2088
2089 if (block && PM_NODE_TYPE_P(block, PM_BLOCK_ARGUMENT_NODE)) {
2090 // We compile the `&block_arg` expression first and stitch it later
2091 // since the nature of the expression influences whether splat should
2092 // duplicate the array.
2093 bool regular_block_arg = true;
2094 const pm_node_t *block_expr = ((const pm_block_argument_node_t *)block)->expression;
2095
2096 if (block_expr && pm_setup_args_dup_rest_p(block_expr)) {
2097 dup_rest = SPLATARRAY_TRUE | DUP_SINGLE_KW_SPLAT;
2098 initial_dup_rest = dup_rest;
2099 }
2100
2101 DECL_ANCHOR(block_arg);
2102 pm_compile_node(iseq, block, block_arg, false, scope_node);
2103
2104 *flags |= VM_CALL_ARGS_BLOCKARG;
2105
2106 if (LIST_INSN_SIZE_ONE(block_arg)) {
2107 LINK_ELEMENT *elem = FIRST_ELEMENT(block_arg);
2108 if (IS_INSN(elem)) {
2109 INSN *iobj = (INSN *) elem;
2110 if (iobj->insn_id == BIN(getblockparam)) {
2111 iobj->insn_id = BIN(getblockparamproxy);
2112 }
2113
2114 // Allow splat without duplication for simple one-instruction
2115 // block arguments like `&arg`. It is known that this
2116 // optimization can be too aggressive in some cases. See
2117 // [Bug #16504].
2118 regular_block_arg = false;
2119 }
2120 }
2121
2122 argc = pm_setup_args_core(arguments_node, block, flags, regular_block_arg, kw_arg, &dup_rest, iseq, ret, scope_node, node_location);
2123 PUSH_SEQ(ret, block_arg);
2124 }
2125 else {
2126 argc = pm_setup_args_core(arguments_node, block, flags, false, kw_arg, &dup_rest, iseq, ret, scope_node, node_location);
2127 }
2128
2129 // If the dup_rest flag was consumed while compiling the arguments (which
2130 // effectively means we found the splat node), then it would have changed
2131 // during the call to pm_setup_args_core. In this case, we want to add the
2132 // VM_CALL_ARGS_SPLAT_MUT flag.
2133 if (*flags & VM_CALL_ARGS_SPLAT && dup_rest != initial_dup_rest) {
2134 *flags |= VM_CALL_ARGS_SPLAT_MUT;
2135 }
2136
2137 return argc;
2138}
2139
2150static void
2151pm_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)
2152{
2153 const pm_node_location_t location = *node_location;
2154 if (!popped) PUSH_INSN(ret, location, putnil);
2155
2156 PM_COMPILE_NOT_POPPED(node->receiver);
2157
2158 int boff = (node->block == NULL ? 0 : 1);
2159 int flag = PM_NODE_TYPE_P(node->receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
2160 struct rb_callinfo_kwarg *keywords = NULL;
2161 int argc = pm_setup_args(node->arguments, (const pm_node_t *) node->block, &flag, &keywords, iseq, ret, scope_node, node_location);
2162
2163 if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
2164 if (boff) {
2165 PUSH_INSN(ret, location, splatkw);
2166 }
2167 else {
2168 PUSH_INSN(ret, location, dup);
2169 PUSH_INSN(ret, location, splatkw);
2170 PUSH_INSN(ret, location, pop);
2171 }
2172 }
2173
2174 int dup_argn = argc + 1 + boff;
2175 int keyword_len = 0;
2176
2177 if (keywords) {
2178 keyword_len = keywords->keyword_len;
2179 dup_argn += keyword_len;
2180 }
2181
2182 PUSH_INSN1(ret, location, dupn, INT2FIX(dup_argn));
2183 PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords);
2184 PM_COMPILE_NOT_POPPED(node->value);
2185
2186 ID id_operator = pm_constant_id_lookup(scope_node, node->binary_operator);
2187 PUSH_SEND(ret, location, id_operator, INT2FIX(1));
2188
2189 if (!popped) {
2190 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2191 }
2192 if (flag & VM_CALL_ARGS_SPLAT) {
2193 if (flag & VM_CALL_KW_SPLAT) {
2194 PUSH_INSN1(ret, location, topn, INT2FIX(2 + boff));
2195
2196 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2197 PUSH_INSN1(ret, location, splatarray, Qtrue);
2198 flag |= VM_CALL_ARGS_SPLAT_MUT;
2199 }
2200
2201 PUSH_INSN(ret, location, swap);
2202 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2203 PUSH_INSN1(ret, location, setn, INT2FIX(2 + boff));
2204 PUSH_INSN(ret, location, pop);
2205 }
2206 else {
2207 if (boff > 0) {
2208 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2209 PUSH_INSN(ret, location, swap);
2210 PUSH_INSN(ret, location, pop);
2211 }
2212 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2213 PUSH_INSN(ret, location, swap);
2214 PUSH_INSN1(ret, location, splatarray, Qtrue);
2215 PUSH_INSN(ret, location, swap);
2216 flag |= VM_CALL_ARGS_SPLAT_MUT;
2217 }
2218 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2219 if (boff > 0) {
2220 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2221 PUSH_INSN(ret, location, pop);
2222 PUSH_INSN(ret, location, pop);
2223 }
2224 }
2225
2226 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc), NULL, INT2FIX(flag), keywords);
2227 }
2228 else if (flag & VM_CALL_KW_SPLAT) {
2229 if (boff > 0) {
2230 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2231 PUSH_INSN(ret, location, swap);
2232 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2233 PUSH_INSN(ret, location, pop);
2234 }
2235 PUSH_INSN(ret, location, swap);
2236 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2237 }
2238 else if (keyword_len) {
2239 PUSH_INSN(ret, location, dup);
2240 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 2));
2241 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 1));
2242 PUSH_INSN(ret, location, pop);
2243 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2244 }
2245 else {
2246 if (boff > 0) {
2247 PUSH_INSN(ret, location, swap);
2248 }
2249 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2250 }
2251
2252 PUSH_INSN(ret, location, pop);
2253}
2254
2267static void
2268pm_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)
2269{
2270 const pm_node_location_t location = *node_location;
2271 if (!popped) PUSH_INSN(ret, location, putnil);
2272 PM_COMPILE_NOT_POPPED(receiver);
2273
2274 int boff = (block == NULL ? 0 : 1);
2275 int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
2276 struct rb_callinfo_kwarg *keywords = NULL;
2277 int argc = pm_setup_args(arguments, (const pm_node_t *) block, &flag, &keywords, iseq, ret, scope_node, node_location);
2278
2279 if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
2280 if (boff) {
2281 PUSH_INSN(ret, location, splatkw);
2282 }
2283 else {
2284 PUSH_INSN(ret, location, dup);
2285 PUSH_INSN(ret, location, splatkw);
2286 PUSH_INSN(ret, location, pop);
2287 }
2288 }
2289
2290 int dup_argn = argc + 1 + boff;
2291 int keyword_len = 0;
2292
2293 if (keywords) {
2294 keyword_len = keywords->keyword_len;
2295 dup_argn += keyword_len;
2296 }
2297
2298 PUSH_INSN1(ret, location, dupn, INT2FIX(dup_argn));
2299 PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords);
2300
2301 LABEL *label = NEW_LABEL(location.line);
2302 LABEL *lfin = NEW_LABEL(location.line);
2303
2304 PUSH_INSN(ret, location, dup);
2305 if (PM_NODE_TYPE_P(node, PM_INDEX_AND_WRITE_NODE)) {
2306 PUSH_INSNL(ret, location, branchunless, label);
2307 }
2308 else {
2309 PUSH_INSNL(ret, location, branchif, label);
2310 }
2311
2312 PUSH_INSN(ret, location, pop);
2313 PM_COMPILE_NOT_POPPED(value);
2314
2315 if (!popped) {
2316 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2317 }
2318
2319 if (flag & VM_CALL_ARGS_SPLAT) {
2320 if (flag & VM_CALL_KW_SPLAT) {
2321 PUSH_INSN1(ret, location, topn, INT2FIX(2 + boff));
2322 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2323 PUSH_INSN1(ret, location, splatarray, Qtrue);
2324 flag |= VM_CALL_ARGS_SPLAT_MUT;
2325 }
2326
2327 PUSH_INSN(ret, location, swap);
2328 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2329 PUSH_INSN1(ret, location, setn, INT2FIX(2 + boff));
2330 PUSH_INSN(ret, location, pop);
2331 }
2332 else {
2333 if (boff > 0) {
2334 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2335 PUSH_INSN(ret, location, swap);
2336 PUSH_INSN(ret, location, pop);
2337 }
2338 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2339 PUSH_INSN(ret, location, swap);
2340 PUSH_INSN1(ret, location, splatarray, Qtrue);
2341 PUSH_INSN(ret, location, swap);
2342 flag |= VM_CALL_ARGS_SPLAT_MUT;
2343 }
2344 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2345 if (boff > 0) {
2346 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2347 PUSH_INSN(ret, location, pop);
2348 PUSH_INSN(ret, location, pop);
2349 }
2350 }
2351
2352 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc), NULL, INT2FIX(flag), keywords);
2353 }
2354 else if (flag & VM_CALL_KW_SPLAT) {
2355 if (boff > 0) {
2356 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2357 PUSH_INSN(ret, location, swap);
2358 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2359 PUSH_INSN(ret, location, pop);
2360 }
2361
2362 PUSH_INSN(ret, location, swap);
2363 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2364 }
2365 else if (keyword_len) {
2366 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 1));
2367 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 0));
2368 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2369 }
2370 else {
2371 if (boff > 0) {
2372 PUSH_INSN(ret, location, swap);
2373 }
2374 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2375 }
2376
2377 PUSH_INSN(ret, location, pop);
2378 PUSH_INSNL(ret, location, jump, lfin);
2379 PUSH_LABEL(ret, label);
2380 if (!popped) {
2381 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2382 }
2383 PUSH_INSN1(ret, location, adjuststack, INT2FIX(dup_argn + 1));
2384 PUSH_LABEL(ret, lfin);
2385}
2386
2387// When we compile a pattern matching expression, we use the stack as a scratch
2388// space to store lots of different values (consider it like we have a pattern
2389// matching function and we need space for a bunch of different local
2390// variables). The "base index" refers to the index on the stack where we
2391// started compiling the pattern matching expression. These offsets from that
2392// base index indicate the location of the various locals we need.
2393#define PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE 0
2394#define PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING 1
2395#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P 2
2396#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE 3
2397#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY 4
2398
2399// A forward declaration because this is the recursive function that handles
2400// compiling a pattern. It can be reentered by nesting patterns, as in the case
2401// of arrays or hashes.
2402static 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);
2403
2408static int
2409pm_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)
2410{
2411 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2412 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2413
2414 PUSH_INSN(ret, location, dup);
2415 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2416
2417 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2418 PUSH_INSN1(ret, location, putobject, message);
2419 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2420 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(2));
2421 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2422
2423 PUSH_INSN1(ret, location, putobject, Qfalse);
2424 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2425
2426 PUSH_INSN(ret, location, pop);
2427 PUSH_INSN(ret, location, pop);
2428 PUSH_LABEL(ret, match_succeeded_label);
2429
2430 return COMPILE_OK;
2431}
2432
2438static int
2439pm_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)
2440{
2441 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2442 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2443
2444 PUSH_INSN(ret, location, dup);
2445 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2446
2447 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2448 PUSH_INSN1(ret, location, putobject, message);
2449 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2450 PUSH_INSN(ret, location, dup);
2451 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2452 PUSH_INSN1(ret, location, putobject, length);
2453 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(4));
2454 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2455
2456 PUSH_INSN1(ret, location, putobject, Qfalse);
2457 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2458
2459 PUSH_INSN(ret, location, pop);
2460 PUSH_INSN(ret, location, pop);
2461 PUSH_LABEL(ret, match_succeeded_label);
2462
2463 return COMPILE_OK;
2464}
2465
2471static int
2472pm_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)
2473{
2474 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2475 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2476
2477 PUSH_INSN(ret, location, dup);
2478 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2479 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2480
2481 VALUE operand = rb_fstring_lit("%p === %p does not return true");
2482 PUSH_INSN1(ret, location, putobject, operand);
2483
2484 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2485 PUSH_INSN1(ret, location, topn, INT2FIX(5));
2486 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2487 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2488 PUSH_INSN1(ret, location, putobject, Qfalse);
2489 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2490 PUSH_INSN(ret, location, pop);
2491 PUSH_INSN(ret, location, pop);
2492
2493 PUSH_LABEL(ret, match_succeeded_label);
2494 PUSH_INSN1(ret, location, setn, INT2FIX(2));
2495 PUSH_INSN(ret, location, pop);
2496 PUSH_INSN(ret, location, pop);
2497
2498 return COMPILE_OK;
2499}
2500
2507static int
2508pm_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)
2509{
2510 LABEL *matched_label = NEW_LABEL(pm_node_line_number_cached(node, scope_node));
2511 CHECK(pm_compile_pattern(iseq, scope_node, node, ret, matched_label, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index));
2512 PUSH_LABEL(ret, matched_label);
2513 return COMPILE_OK;
2514}
2515
2521static int
2522pm_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)
2523{
2524 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2525
2526 if (use_deconstructed_cache) {
2527 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2528 PUSH_INSNL(ret, location, branchnil, deconstruct_label);
2529
2530 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2531 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2532
2533 PUSH_INSN(ret, location, pop);
2534 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE - 1));
2535 PUSH_INSNL(ret, location, jump, deconstructed_label);
2536 }
2537 else {
2538 PUSH_INSNL(ret, location, jump, deconstruct_label);
2539 }
2540
2541 PUSH_LABEL(ret, deconstruct_label);
2542 PUSH_INSN(ret, location, dup);
2543
2544 VALUE operand = ID2SYM(rb_intern("deconstruct"));
2545 PUSH_INSN1(ret, location, putobject, operand);
2546 PUSH_SEND(ret, location, idRespond_to, INT2FIX(1));
2547
2548 if (use_deconstructed_cache) {
2549 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE + 1));
2550 }
2551
2552 if (in_single_pattern) {
2553 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p does not respond to #deconstruct"), base_index + 1));
2554 }
2555
2556 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2557 PUSH_SEND(ret, location, rb_intern("deconstruct"), INT2FIX(0));
2558
2559 if (use_deconstructed_cache) {
2560 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2561 }
2562
2563 PUSH_INSN(ret, location, dup);
2564 PUSH_INSN1(ret, location, checktype, INT2FIX(T_ARRAY));
2565 PUSH_INSNL(ret, location, branchunless, type_error_label);
2566 PUSH_LABEL(ret, deconstructed_label);
2567
2568 return COMPILE_OK;
2569}
2570
2575static int
2576pm_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)
2577{
2578 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2579
2580 PUSH_INSN(ret, location, dup);
2581 PM_COMPILE_NOT_POPPED(node);
2582
2583 if (in_single_pattern) {
2584 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
2585 }
2586 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
2587 if (in_single_pattern) {
2588 CHECK(pm_compile_pattern_eqq_error(iseq, scope_node, node, ret, base_index + 3));
2589 }
2590 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2591 return COMPILE_OK;
2592}
2593
2598static void
2599pm_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)
2600{
2601 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2602 LABEL *key_error_label = NEW_LABEL(location.line);
2603 LABEL *cleanup_label = NEW_LABEL(location.line);
2604
2605 struct rb_callinfo_kwarg *kw_arg = rb_xmalloc_mul_add(2, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
2606 kw_arg->references = 0;
2607 kw_arg->keyword_len = 2;
2608 kw_arg->keywords[0] = ID2SYM(rb_intern("matchee"));
2609 kw_arg->keywords[1] = ID2SYM(rb_intern("key"));
2610
2611 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2612 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2613 PUSH_INSNL(ret, location, branchif, key_error_label);
2614
2615 PUSH_INSN1(ret, location, putobject, rb_eNoMatchingPatternError);
2616 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2617
2618 {
2619 VALUE operand = rb_fstring_lit("%p: %s");
2620 PUSH_INSN1(ret, location, putobject, operand);
2621 }
2622
2623 PUSH_INSN1(ret, location, topn, INT2FIX(4));
2624 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 6));
2625 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2626 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2627 PUSH_INSNL(ret, location, jump, cleanup_label);
2628
2629 PUSH_LABEL(ret, key_error_label);
2630 PUSH_INSN1(ret, location, putobject, rb_eNoMatchingPatternKeyError);
2631 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2632
2633 {
2634 VALUE operand = rb_fstring_lit("%p: %s");
2635 PUSH_INSN1(ret, location, putobject, operand);
2636 }
2637
2638 PUSH_INSN1(ret, location, topn, INT2FIX(4));
2639 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 6));
2640 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2641 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE + 4));
2642 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY + 5));
2643 PUSH_SEND_R(ret, location, rb_intern("new"), INT2FIX(1), NULL, INT2FIX(VM_CALL_KWARG), kw_arg);
2644 PUSH_SEND(ret, location, id_core_raise, INT2FIX(1));
2645 PUSH_LABEL(ret, cleanup_label);
2646
2647 PUSH_INSN1(ret, location, adjuststack, INT2FIX(7));
2648 if (!popped) PUSH_INSN(ret, location, putnil);
2649 PUSH_INSNL(ret, location, jump, done_label);
2650 PUSH_INSN1(ret, location, dupn, INT2FIX(5));
2651 if (popped) PUSH_INSN(ret, location, putnil);
2652}
2653
2657static int
2658pm_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)
2659{
2660 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2661
2662 switch (PM_NODE_TYPE(node)) {
2663 case PM_ARRAY_PATTERN_NODE: {
2664 // Array patterns in pattern matching are triggered by using commas in
2665 // a pattern or wrapping it in braces. They are represented by a
2666 // ArrayPatternNode. This looks like:
2667 //
2668 // foo => [1, 2, 3]
2669 //
2670 // It can optionally have a splat in the middle of it, which can
2671 // optionally have a name attached.
2672 const pm_array_pattern_node_t *cast = (const pm_array_pattern_node_t *) node;
2673
2674 const size_t requireds_size = cast->requireds.size;
2675 const size_t posts_size = cast->posts.size;
2676 const size_t minimum_size = requireds_size + posts_size;
2677
2678 bool rest_named = false;
2679 bool use_rest_size = false;
2680
2681 if (cast->rest != NULL) {
2682 rest_named = (PM_NODE_TYPE_P(cast->rest, PM_SPLAT_NODE) && ((const pm_splat_node_t *) cast->rest)->expression != NULL);
2683 use_rest_size = (rest_named || (!rest_named && posts_size > 0));
2684 }
2685
2686 LABEL *match_failed_label = NEW_LABEL(location.line);
2687 LABEL *type_error_label = NEW_LABEL(location.line);
2688 LABEL *deconstruct_label = NEW_LABEL(location.line);
2689 LABEL *deconstructed_label = NEW_LABEL(location.line);
2690
2691 if (use_rest_size) {
2692 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2693 PUSH_INSN(ret, location, swap);
2694 base_index++;
2695 }
2696
2697 if (cast->constant != NULL) {
2698 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2699 }
2700
2701 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));
2702
2703 PUSH_INSN(ret, location, dup);
2704 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2705 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2706 PUSH_SEND(ret, location, cast->rest == NULL ? idEq : idGE, INT2FIX(1));
2707 if (in_single_pattern) {
2708 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+)");
2709 CHECK(pm_compile_pattern_length_error(iseq, scope_node, node, ret, message, INT2FIX(minimum_size), base_index + 1));
2710 }
2711 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2712
2713 for (size_t index = 0; index < requireds_size; index++) {
2714 const pm_node_t *required = cast->requireds.nodes[index];
2715 PUSH_INSN(ret, location, dup);
2716 PUSH_INSN1(ret, location, putobject, INT2FIX(index));
2717 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2718 CHECK(pm_compile_pattern_match(iseq, scope_node, required, ret, match_failed_label, in_single_pattern, false, base_index + 1));
2719 }
2720
2721 if (cast->rest != NULL) {
2722 if (rest_named) {
2723 PUSH_INSN(ret, location, dup);
2724 PUSH_INSN1(ret, location, putobject, INT2FIX(requireds_size));
2725 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2726 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2727 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2728 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2729 PUSH_INSN1(ret, location, setn, INT2FIX(4));
2730 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2731 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));
2732 }
2733 else if (posts_size > 0) {
2734 PUSH_INSN(ret, location, dup);
2735 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2736 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2737 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2738 PUSH_INSN1(ret, location, setn, INT2FIX(2));
2739 PUSH_INSN(ret, location, pop);
2740 }
2741 }
2742
2743 for (size_t index = 0; index < posts_size; index++) {
2744 const pm_node_t *post = cast->posts.nodes[index];
2745 PUSH_INSN(ret, location, dup);
2746
2747 PUSH_INSN1(ret, location, putobject, INT2FIX(requireds_size + index));
2748 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2749 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2750 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2751 CHECK(pm_compile_pattern_match(iseq, scope_node, post, ret, match_failed_label, in_single_pattern, false, base_index + 1));
2752 }
2753
2754 PUSH_INSN(ret, location, pop);
2755 if (use_rest_size) {
2756 PUSH_INSN(ret, location, pop);
2757 }
2758
2759 PUSH_INSNL(ret, location, jump, matched_label);
2760 PUSH_INSN(ret, location, putnil);
2761 if (use_rest_size) {
2762 PUSH_INSN(ret, location, putnil);
2763 }
2764
2765 PUSH_LABEL(ret, type_error_label);
2766 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2767 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2768
2769 {
2770 VALUE operand = rb_fstring_lit("deconstruct must return Array");
2771 PUSH_INSN1(ret, location, putobject, operand);
2772 }
2773
2774 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2775 PUSH_INSN(ret, location, pop);
2776
2777 PUSH_LABEL(ret, match_failed_label);
2778 PUSH_INSN(ret, location, pop);
2779 if (use_rest_size) {
2780 PUSH_INSN(ret, location, pop);
2781 }
2782
2783 PUSH_INSNL(ret, location, jump, unmatched_label);
2784 break;
2785 }
2786 case PM_FIND_PATTERN_NODE: {
2787 // Find patterns in pattern matching are triggered by using commas in
2788 // a pattern or wrapping it in braces and using a splat on both the left
2789 // and right side of the pattern. This looks like:
2790 //
2791 // foo => [*, 1, 2, 3, *]
2792 //
2793 // There can be any number of requireds in the middle. The splats on
2794 // both sides can optionally have names attached.
2795 const pm_find_pattern_node_t *cast = (const pm_find_pattern_node_t *) node;
2796 const size_t size = cast->requireds.size;
2797
2798 LABEL *match_failed_label = NEW_LABEL(location.line);
2799 LABEL *type_error_label = NEW_LABEL(location.line);
2800 LABEL *deconstruct_label = NEW_LABEL(location.line);
2801 LABEL *deconstructed_label = NEW_LABEL(location.line);
2802
2803 if (cast->constant) {
2804 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2805 }
2806
2807 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));
2808
2809 PUSH_INSN(ret, location, dup);
2810 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2811 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2812 PUSH_SEND(ret, location, idGE, INT2FIX(1));
2813 if (in_single_pattern) {
2814 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));
2815 }
2816 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2817
2818 {
2819 LABEL *while_begin_label = NEW_LABEL(location.line);
2820 LABEL *next_loop_label = NEW_LABEL(location.line);
2821 LABEL *find_succeeded_label = NEW_LABEL(location.line);
2822 LABEL *find_failed_label = NEW_LABEL(location.line);
2823
2824 PUSH_INSN(ret, location, dup);
2825 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2826
2827 PUSH_INSN(ret, location, dup);
2828 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2829 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2830 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2831 PUSH_LABEL(ret, while_begin_label);
2832
2833 PUSH_INSN(ret, location, dup);
2834 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2835 PUSH_SEND(ret, location, idLE, INT2FIX(1));
2836 PUSH_INSNL(ret, location, branchunless, find_failed_label);
2837
2838 for (size_t index = 0; index < size; index++) {
2839 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2840 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2841
2842 if (index != 0) {
2843 PUSH_INSN1(ret, location, putobject, INT2FIX(index));
2844 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2845 }
2846
2847 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2848 CHECK(pm_compile_pattern_match(iseq, scope_node, cast->requireds.nodes[index], ret, next_loop_label, in_single_pattern, false, base_index + 4));
2849 }
2850
2851 const pm_splat_node_t *left = cast->left;
2852
2853 if (left->expression != NULL) {
2854 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2855 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2856 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2857 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2858 CHECK(pm_compile_pattern_match(iseq, scope_node, left->expression, ret, find_failed_label, in_single_pattern, false, base_index + 4));
2859 }
2860
2861 const pm_splat_node_t *right = cast->right;
2862
2863 if (right->expression != NULL) {
2864 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2865 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2866 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2867 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2868 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2869 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2870 pm_compile_pattern_match(iseq, scope_node, right->expression, ret, find_failed_label, in_single_pattern, false, base_index + 4);
2871 }
2872
2873 PUSH_INSNL(ret, location, jump, find_succeeded_label);
2874
2875 PUSH_LABEL(ret, next_loop_label);
2876 PUSH_INSN1(ret, location, putobject, INT2FIX(1));
2877 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2878 PUSH_INSNL(ret, location, jump, while_begin_label);
2879
2880 PUSH_LABEL(ret, find_failed_label);
2881 PUSH_INSN1(ret, location, adjuststack, INT2FIX(3));
2882 if (in_single_pattern) {
2883 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2884
2885 {
2886 VALUE operand = rb_fstring_lit("%p does not match to find pattern");
2887 PUSH_INSN1(ret, location, putobject, operand);
2888 }
2889
2890 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2891 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(2));
2892 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2893
2894 PUSH_INSN1(ret, location, putobject, Qfalse);
2895 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2896
2897 PUSH_INSN(ret, location, pop);
2898 PUSH_INSN(ret, location, pop);
2899 }
2900 PUSH_INSNL(ret, location, jump, match_failed_label);
2901 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2902
2903 PUSH_LABEL(ret, find_succeeded_label);
2904 PUSH_INSN1(ret, location, adjuststack, INT2FIX(3));
2905 }
2906
2907 PUSH_INSN(ret, location, pop);
2908 PUSH_INSNL(ret, location, jump, matched_label);
2909 PUSH_INSN(ret, location, putnil);
2910
2911 PUSH_LABEL(ret, type_error_label);
2912 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2913 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2914
2915 {
2916 VALUE operand = rb_fstring_lit("deconstruct must return Array");
2917 PUSH_INSN1(ret, location, putobject, operand);
2918 }
2919
2920 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2921 PUSH_INSN(ret, location, pop);
2922
2923 PUSH_LABEL(ret, match_failed_label);
2924 PUSH_INSN(ret, location, pop);
2925 PUSH_INSNL(ret, location, jump, unmatched_label);
2926
2927 break;
2928 }
2929 case PM_HASH_PATTERN_NODE: {
2930 // Hash patterns in pattern matching are triggered by using labels and
2931 // values in a pattern or by using the ** operator. They are represented
2932 // by the HashPatternNode. This looks like:
2933 //
2934 // foo => { a: 1, b: 2, **bar }
2935 //
2936 // It can optionally have an assoc splat in the middle of it, which can
2937 // optionally have a name.
2938 const pm_hash_pattern_node_t *cast = (const pm_hash_pattern_node_t *) node;
2939
2940 // We don't consider it a "rest" parameter if it's a ** that is unnamed.
2941 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);
2942 bool has_keys = cast->elements.size > 0 || cast->rest != NULL;
2943
2944 LABEL *match_failed_label = NEW_LABEL(location.line);
2945 LABEL *type_error_label = NEW_LABEL(location.line);
2946 VALUE keys = Qnil;
2947
2948 if (has_keys && !has_rest) {
2949 keys = rb_ary_new_capa(cast->elements.size);
2950
2951 for (size_t index = 0; index < cast->elements.size; index++) {
2952 const pm_node_t *element = cast->elements.nodes[index];
2953 RUBY_ASSERT(PM_NODE_TYPE_P(element, PM_ASSOC_NODE));
2954
2955 const pm_node_t *key = ((const pm_assoc_node_t *) element)->key;
2956 RUBY_ASSERT(PM_NODE_TYPE_P(key, PM_SYMBOL_NODE));
2957
2958 VALUE symbol = ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) key));
2959 rb_ary_push(keys, symbol);
2960 }
2961 }
2962
2963 if (cast->constant) {
2964 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2965 }
2966
2967 PUSH_INSN(ret, location, dup);
2968
2969 {
2970 VALUE operand = ID2SYM(rb_intern("deconstruct_keys"));
2971 PUSH_INSN1(ret, location, putobject, operand);
2972 }
2973
2974 PUSH_SEND(ret, location, idRespond_to, INT2FIX(1));
2975 if (in_single_pattern) {
2976 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p does not respond to #deconstruct_keys"), base_index + 1));
2977 }
2978 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2979
2980 if (NIL_P(keys)) {
2981 PUSH_INSN(ret, location, putnil);
2982 }
2983 else {
2984 rb_obj_hide(keys);
2985 RB_OBJ_SET_FROZEN_SHAREABLE(keys);
2986 PUSH_INSN1(ret, location, duparray, keys);
2987 RB_OBJ_WRITTEN(iseq, Qundef, keys);
2988 }
2989 PUSH_SEND(ret, location, rb_intern("deconstruct_keys"), INT2FIX(1));
2990
2991 PUSH_INSN(ret, location, dup);
2992 PUSH_INSN1(ret, location, checktype, INT2FIX(T_HASH));
2993 PUSH_INSNL(ret, location, branchunless, type_error_label);
2994
2995 if (has_rest) {
2996 PUSH_SEND(ret, location, rb_intern("dup"), INT2FIX(0));
2997 }
2998
2999 if (has_keys) {
3000 DECL_ANCHOR(match_values);
3001
3002 for (size_t index = 0; index < cast->elements.size; index++) {
3003 const pm_node_t *element = cast->elements.nodes[index];
3004 RUBY_ASSERT(PM_NODE_TYPE_P(element, PM_ASSOC_NODE));
3005
3006 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
3007 const pm_node_t *key = assoc->key;
3008 RUBY_ASSERT(PM_NODE_TYPE_P(key, PM_SYMBOL_NODE));
3009
3010 VALUE symbol = ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) key));
3011 PUSH_INSN(ret, location, dup);
3012 PUSH_INSN1(ret, location, putobject, symbol);
3013 PUSH_SEND(ret, location, rb_intern("key?"), INT2FIX(1));
3014
3015 if (in_single_pattern) {
3016 LABEL *match_succeeded_label = NEW_LABEL(location.line);
3017
3018 PUSH_INSN(ret, location, dup);
3019 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
3020
3021 {
3022 VALUE operand = rb_str_freeze(rb_sprintf("key not found: %+"PRIsVALUE, symbol));
3023 RB_OBJ_SET_SHAREABLE(operand);
3024 PUSH_INSN1(ret, location, putobject, operand);
3025 }
3026
3027 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 2));
3028 PUSH_INSN1(ret, location, putobject, Qtrue);
3029 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 3));
3030 PUSH_INSN1(ret, location, topn, INT2FIX(3));
3031 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE + 4));
3032 PUSH_INSN1(ret, location, putobject, symbol);
3033 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY + 5));
3034
3035 PUSH_INSN1(ret, location, adjuststack, INT2FIX(4));
3036 PUSH_LABEL(ret, match_succeeded_label);
3037 }
3038
3039 PUSH_INSNL(ret, location, branchunless, match_failed_label);
3040 PUSH_INSN(match_values, location, dup);
3041 PUSH_INSN1(match_values, location, putobject, symbol);
3042 PUSH_SEND(match_values, location, has_rest ? rb_intern("delete") : idAREF, INT2FIX(1));
3043
3044 const pm_node_t *value = assoc->value;
3045 if (PM_NODE_TYPE_P(value, PM_IMPLICIT_NODE)) {
3046 value = ((const pm_implicit_node_t *) value)->value;
3047 }
3048
3049 CHECK(pm_compile_pattern_match(iseq, scope_node, value, match_values, match_failed_label, in_single_pattern, false, base_index + 1));
3050 }
3051
3052 PUSH_SEQ(ret, match_values);
3053 }
3054 else {
3055 PUSH_INSN(ret, location, dup);
3056 PUSH_SEND(ret, location, idEmptyP, INT2FIX(0));
3057 if (in_single_pattern) {
3058 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p is not empty"), base_index + 1));
3059 }
3060 PUSH_INSNL(ret, location, branchunless, match_failed_label);
3061 }
3062
3063 if (has_rest) {
3064 switch (PM_NODE_TYPE(cast->rest)) {
3065 case PM_NO_KEYWORDS_PARAMETER_NODE: {
3066 PUSH_INSN(ret, location, dup);
3067 PUSH_SEND(ret, location, idEmptyP, INT2FIX(0));
3068 if (in_single_pattern) {
3069 pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("rest of %p is not empty"), base_index + 1);
3070 }
3071 PUSH_INSNL(ret, location, branchunless, match_failed_label);
3072 break;
3073 }
3074 case PM_ASSOC_SPLAT_NODE: {
3075 const pm_assoc_splat_node_t *splat = (const pm_assoc_splat_node_t *) cast->rest;
3076 PUSH_INSN(ret, location, dup);
3077 pm_compile_pattern_match(iseq, scope_node, splat->value, ret, match_failed_label, in_single_pattern, false, base_index + 1);
3078 break;
3079 }
3080 default:
3081 rb_bug("unreachable");
3082 break;
3083 }
3084 }
3085
3086 PUSH_INSN(ret, location, pop);
3087 PUSH_INSNL(ret, location, jump, matched_label);
3088 PUSH_INSN(ret, location, putnil);
3089
3090 PUSH_LABEL(ret, type_error_label);
3091 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
3092 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
3093
3094 {
3095 VALUE operand = rb_fstring_lit("deconstruct_keys must return Hash");
3096 PUSH_INSN1(ret, location, putobject, operand);
3097 }
3098
3099 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
3100 PUSH_INSN(ret, location, pop);
3101
3102 PUSH_LABEL(ret, match_failed_label);
3103 PUSH_INSN(ret, location, pop);
3104 PUSH_INSNL(ret, location, jump, unmatched_label);
3105 break;
3106 }
3107 case PM_CAPTURE_PATTERN_NODE: {
3108 // Capture patterns allow you to pattern match against an element in a
3109 // pattern and also capture the value into a local variable. This looks
3110 // like:
3111 //
3112 // [1] => [Integer => foo]
3113 //
3114 // In this case the `Integer => foo` will be represented by a
3115 // CapturePatternNode, which has both a value (the pattern to match
3116 // against) and a target (the place to write the variable into).
3117 const pm_capture_pattern_node_t *cast = (const pm_capture_pattern_node_t *) node;
3118
3119 LABEL *match_failed_label = NEW_LABEL(location.line);
3120
3121 PUSH_INSN(ret, location, dup);
3122 CHECK(pm_compile_pattern_match(iseq, scope_node, cast->value, ret, match_failed_label, in_single_pattern, use_deconstructed_cache, base_index + 1));
3123 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));
3124 PUSH_INSN(ret, location, putnil);
3125
3126 PUSH_LABEL(ret, match_failed_label);
3127 PUSH_INSN(ret, location, pop);
3128 PUSH_INSNL(ret, location, jump, unmatched_label);
3129
3130 break;
3131 }
3132 case PM_LOCAL_VARIABLE_TARGET_NODE: {
3133 // Local variables can be targeted by placing identifiers in the place
3134 // of a pattern. For example, foo in bar. This results in the value
3135 // being matched being written to that local variable.
3137 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
3138
3139 PUSH_SETLOCAL(ret, location, index.index, index.level);
3140 PUSH_INSNL(ret, location, jump, matched_label);
3141 break;
3142 }
3143 case PM_ALTERNATION_PATTERN_NODE: {
3144 // Alternation patterns allow you to specify multiple patterns in a
3145 // single expression using the | operator.
3147
3148 LABEL *matched_left_label = NEW_LABEL(location.line);
3149 LABEL *unmatched_left_label = NEW_LABEL(location.line);
3150
3151 // First, we're going to attempt to match against the left pattern. If
3152 // that pattern matches, then we'll skip matching the right pattern.
3153 PUSH_INSN(ret, location, dup);
3154 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));
3155
3156 // If we get here, then we matched on the left pattern. In this case we
3157 // should pop out the duplicate value that we preemptively added to
3158 // match against the right pattern and then jump to the match label.
3159 PUSH_LABEL(ret, matched_left_label);
3160 PUSH_INSN(ret, location, pop);
3161 PUSH_INSNL(ret, location, jump, matched_label);
3162 PUSH_INSN(ret, location, putnil);
3163
3164 // If we get here, then we didn't match on the left pattern. In this
3165 // case we attempt to match against the right pattern.
3166 PUSH_LABEL(ret, unmatched_left_label);
3167 CHECK(pm_compile_pattern(iseq, scope_node, cast->right, ret, matched_label, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index));
3168 break;
3169 }
3170 case PM_PARENTHESES_NODE:
3171 // Parentheses are allowed to wrap expressions in pattern matching and
3172 // they do nothing since they can only wrap individual expressions and
3173 // not groups. In this case we'll recurse back into this same function
3174 // with the body of the parentheses.
3175 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);
3176 case PM_PINNED_EXPRESSION_NODE:
3177 // Pinned expressions are a way to match against the value of an
3178 // expression that should be evaluated at runtime. This looks like:
3179 // foo in ^(bar). To compile these, we compile the expression as if it
3180 // were a literal value by falling through to the literal case.
3181 node = ((const pm_pinned_expression_node_t *) node)->expression;
3182 /* fallthrough */
3183 case PM_ARRAY_NODE:
3184 case PM_CLASS_VARIABLE_READ_NODE:
3185 case PM_CONSTANT_PATH_NODE:
3186 case PM_CONSTANT_READ_NODE:
3187 case PM_FALSE_NODE:
3188 case PM_FLOAT_NODE:
3189 case PM_GLOBAL_VARIABLE_READ_NODE:
3190 case PM_IMAGINARY_NODE:
3191 case PM_INSTANCE_VARIABLE_READ_NODE:
3192 case PM_IT_LOCAL_VARIABLE_READ_NODE:
3193 case PM_INTEGER_NODE:
3194 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
3195 case PM_INTERPOLATED_STRING_NODE:
3196 case PM_INTERPOLATED_SYMBOL_NODE:
3197 case PM_INTERPOLATED_X_STRING_NODE:
3198 case PM_LAMBDA_NODE:
3199 case PM_LOCAL_VARIABLE_READ_NODE:
3200 case PM_NIL_NODE:
3201 case PM_SOURCE_ENCODING_NODE:
3202 case PM_SOURCE_FILE_NODE:
3203 case PM_SOURCE_LINE_NODE:
3204 case PM_RANGE_NODE:
3205 case PM_RATIONAL_NODE:
3206 case PM_REGULAR_EXPRESSION_NODE:
3207 case PM_SELF_NODE:
3208 case PM_STRING_NODE:
3209 case PM_SYMBOL_NODE:
3210 case PM_TRUE_NODE:
3211 case PM_X_STRING_NODE: {
3212 // These nodes are all simple patterns, which means we'll use the
3213 // checkmatch instruction to match against them, which is effectively a
3214 // VM-level === operator.
3215 PM_COMPILE_NOT_POPPED(node);
3216 if (in_single_pattern) {
3217 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
3218 }
3219
3220 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
3221
3222 if (in_single_pattern) {
3223 pm_compile_pattern_eqq_error(iseq, scope_node, node, ret, base_index + 2);
3224 }
3225
3226 PUSH_INSNL(ret, location, branchif, matched_label);
3227 PUSH_INSNL(ret, location, jump, unmatched_label);
3228 break;
3229 }
3230 case PM_PINNED_VARIABLE_NODE: {
3231 // Pinned variables are a way to match against the value of a variable
3232 // without it looking like you're trying to write to the variable. This
3233 // looks like: foo in ^@bar. To compile these, we compile the variable
3234 // that they hold.
3235 const pm_pinned_variable_node_t *cast = (const pm_pinned_variable_node_t *) node;
3236 CHECK(pm_compile_pattern(iseq, scope_node, cast->variable, ret, matched_label, unmatched_label, in_single_pattern, true, base_index));
3237 break;
3238 }
3239 case PM_IF_NODE:
3240 case PM_UNLESS_NODE: {
3241 // If and unless nodes can show up here as guards on `in` clauses. This
3242 // looks like:
3243 //
3244 // case foo
3245 // in bar if baz?
3246 // qux
3247 // end
3248 //
3249 // Because we know they're in the modifier form and they can't have any
3250 // variation on this pattern, we compile them differently (more simply)
3251 // here than we would in the normal compilation path.
3252 const pm_node_t *predicate;
3253 const pm_node_t *statement;
3254
3255 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3256 const pm_if_node_t *cast = (const pm_if_node_t *) node;
3257 predicate = cast->predicate;
3258
3259 RUBY_ASSERT(cast->statements != NULL && cast->statements->body.size == 1);
3260 statement = cast->statements->body.nodes[0];
3261 }
3262 else {
3263 const pm_unless_node_t *cast = (const pm_unless_node_t *) node;
3264 predicate = cast->predicate;
3265
3266 RUBY_ASSERT(cast->statements != NULL && cast->statements->body.size == 1);
3267 statement = cast->statements->body.nodes[0];
3268 }
3269
3270 CHECK(pm_compile_pattern_match(iseq, scope_node, statement, ret, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index));
3271 PM_COMPILE_NOT_POPPED(predicate);
3272
3273 if (in_single_pattern) {
3274 LABEL *match_succeeded_label = NEW_LABEL(location.line);
3275
3276 PUSH_INSN(ret, location, dup);
3277 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3278 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
3279 }
3280 else {
3281 PUSH_INSNL(ret, location, branchunless, match_succeeded_label);
3282 }
3283
3284 {
3285 VALUE operand = rb_fstring_lit("guard clause does not return true");
3286 PUSH_INSN1(ret, location, putobject, operand);
3287 }
3288
3289 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
3290 PUSH_INSN1(ret, location, putobject, Qfalse);
3291 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
3292
3293 PUSH_INSN(ret, location, pop);
3294 PUSH_INSN(ret, location, pop);
3295
3296 PUSH_LABEL(ret, match_succeeded_label);
3297 }
3298
3299 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3300 PUSH_INSNL(ret, location, branchunless, unmatched_label);
3301 }
3302 else {
3303 PUSH_INSNL(ret, location, branchif, unmatched_label);
3304 }
3305
3306 PUSH_INSNL(ret, location, jump, matched_label);
3307 break;
3308 }
3309 default:
3310 // If we get here, then we have a node type that should not be in this
3311 // position. This would be a bug in the parser, because a different node
3312 // type should never have been created in this position in the tree.
3313 rb_bug("Unexpected node type in pattern matching expression: %s", pm_node_type(PM_NODE_TYPE(node)));
3314 break;
3315 }
3316
3317 return COMPILE_OK;
3318}
3319
3320#undef PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE
3321#undef PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING
3322#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P
3323#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE
3324#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY
3325
3326// Generate a scope node from the given node.
3327void
3328pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_t *previous)
3329{
3330 if (previous) {
3331 // Copy inherited fields from the parent scope in one shot, then
3332 // zero out the fields that are scope-specific.
3333 *scope = *previous;
3334 scope->locals = (pm_constant_id_list_t) { 0 };
3335 scope->parameters = NULL;
3336 scope->body = NULL;
3337 scope->local_table_for_iseq_size = 0;
3338 scope->index_lookup_table = (pm_index_lookup_table_t) PM_INDEX_LOOKUP_TABLE_INIT;
3339 scope->pre_execution_anchor = NULL;
3340 }
3341 else {
3342 memset(scope, 0, sizeof(pm_scope_node_t));
3343 }
3344
3345 scope->base.type = PM_SCOPE_NODE;
3346 scope->base.location.start = node->location.start;
3347 scope->base.location.length = node->location.length;
3348 scope->previous = previous;
3349 scope->ast_node = (pm_node_t *) node;
3350
3351 switch (PM_NODE_TYPE(node)) {
3352 case PM_BLOCK_NODE: {
3353 const pm_block_node_t *cast = (const pm_block_node_t *) node;
3354 scope->body = cast->body;
3355 scope->locals = cast->locals;
3356 scope->parameters = cast->parameters;
3357 break;
3358 }
3359 case PM_CLASS_NODE: {
3360 const pm_class_node_t *cast = (const pm_class_node_t *) node;
3361 scope->body = cast->body;
3362 scope->locals = cast->locals;
3363 break;
3364 }
3365 case PM_DEF_NODE: {
3366 const pm_def_node_t *cast = (const pm_def_node_t *) node;
3367 scope->parameters = (pm_node_t *) cast->parameters;
3368 scope->body = cast->body;
3369 scope->locals = cast->locals;
3370 break;
3371 }
3372 case PM_ENSURE_NODE: {
3373 const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node;
3374 scope->body = (pm_node_t *) node;
3375
3376 if (cast->statements != NULL) {
3377 scope->base.location.start = cast->statements->base.location.start;
3378 scope->base.location.length = cast->statements->base.location.length;
3379 }
3380
3381 break;
3382 }
3383 case PM_FOR_NODE: {
3384 const pm_for_node_t *cast = (const pm_for_node_t *) node;
3385 scope->body = (pm_node_t *) cast->statements;
3386 break;
3387 }
3388 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
3389 RUBY_ASSERT(node->flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE);
3390 scope->body = (pm_node_t *) node;
3391 break;
3392 }
3393 case PM_LAMBDA_NODE: {
3394 const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node;
3395 scope->parameters = cast->parameters;
3396 scope->body = cast->body;
3397 scope->locals = cast->locals;
3398 break;
3399 }
3400 case PM_MODULE_NODE: {
3401 const pm_module_node_t *cast = (const pm_module_node_t *) node;
3402 scope->body = cast->body;
3403 scope->locals = cast->locals;
3404 break;
3405 }
3406 case PM_POST_EXECUTION_NODE: {
3407 const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) node;
3408 scope->body = (pm_node_t *) cast->statements;
3409 break;
3410 }
3411 case PM_PROGRAM_NODE: {
3412 const pm_program_node_t *cast = (const pm_program_node_t *) node;
3413 scope->body = (pm_node_t *) cast->statements;
3414 scope->locals = cast->locals;
3415 break;
3416 }
3417 case PM_RESCUE_NODE: {
3418 const pm_rescue_node_t *cast = (const pm_rescue_node_t *) node;
3419 scope->body = (pm_node_t *) cast->statements;
3420 break;
3421 }
3422 case PM_RESCUE_MODIFIER_NODE: {
3423 const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node;
3424 scope->body = (pm_node_t *) cast->rescue_expression;
3425 break;
3426 }
3427 case PM_SINGLETON_CLASS_NODE: {
3428 const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node;
3429 scope->body = cast->body;
3430 scope->locals = cast->locals;
3431 break;
3432 }
3433 case PM_STATEMENTS_NODE: {
3434 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
3435 scope->body = (pm_node_t *) cast;
3436 break;
3437 }
3438 default:
3439 rb_bug("unreachable");
3440 break;
3441 }
3442}
3443
3444void
3445pm_scope_node_destroy(pm_scope_node_t *scope_node)
3446{
3447 if (scope_node->index_lookup_table.owned) {
3448 xfree(scope_node->index_lookup_table.values);
3449 }
3450}
3451
3463static void
3464pm_compile_retry_end_label(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *retry_end_l)
3465{
3466 INSN *iobj;
3467 LINK_ELEMENT *last_elem = LAST_ELEMENT(ret);
3468 iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem);
3469 while (!IS_INSN_ID(iobj, send) && !IS_INSN_ID(iobj, invokesuper) && !IS_INSN_ID(iobj, sendforward) && !IS_INSN_ID(iobj, invokesuperforward)) {
3470 iobj = (INSN*) get_prev_insn(iobj);
3471 }
3472 ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l);
3473
3474 // LINK_ANCHOR has a pointer to the last element, but
3475 // ELEM_INSERT_NEXT does not update it even if we add an insn to the
3476 // last of LINK_ANCHOR. So this updates it manually.
3477 if (&iobj->link == LAST_ELEMENT(ret)) {
3478 ret->last = (LINK_ELEMENT*) retry_end_l;
3479 }
3480}
3481
3482static const char *
3483pm_iseq_builtin_function_name(const pm_scope_node_t *scope_node, const pm_node_t *receiver, ID method_id)
3484{
3485 const char *name = rb_id2name(method_id);
3486 static const char prefix[] = "__builtin_";
3487 const size_t prefix_len = sizeof(prefix) - 1;
3488
3489 if (receiver == NULL) {
3490 if (UNLIKELY(strncmp(prefix, name, prefix_len) == 0)) {
3491 // __builtin_foo
3492 return &name[prefix_len];
3493 }
3494 }
3495 else if (PM_NODE_TYPE_P(receiver, PM_CALL_NODE)) {
3496 if (PM_NODE_FLAG_P(receiver, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
3497 const pm_call_node_t *cast = (const pm_call_node_t *) receiver;
3498 if (pm_constant_id_lookup(scope_node, cast->name) == rb_intern_const("__builtin")) {
3499 // __builtin.foo
3500 return name;
3501 }
3502 }
3503 }
3504 else if (PM_NODE_TYPE_P(receiver, PM_CONSTANT_READ_NODE)) {
3505 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) receiver;
3506 if (pm_constant_id_lookup(scope_node, cast->name) == rb_intern_const("Primitive")) {
3507 // Primitive.foo
3508 return name;
3509 }
3510 }
3511
3512 return NULL;
3513}
3514
3515// Compile Primitive.attr! :leaf, ...
3516static int
3517pm_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)
3518{
3519 if (arguments == NULL) {
3520 COMPILE_ERROR(iseq, node_location->line, "attr!: no argument");
3521 return COMPILE_NG;
3522 }
3523
3524 const pm_node_t *argument;
3525 PM_NODE_LIST_FOREACH(&arguments->arguments, index, argument) {
3526 if (!PM_NODE_TYPE_P(argument, PM_SYMBOL_NODE)) {
3527 COMPILE_ERROR(iseq, node_location->line, "non symbol argument to attr!: %s", pm_node_type(PM_NODE_TYPE(argument)));
3528 return COMPILE_NG;
3529 }
3530
3531 VALUE symbol = pm_static_literal_value(iseq, argument, scope_node);
3532 VALUE string = rb_sym2str(symbol);
3533
3534 if (strcmp(RSTRING_PTR(string), "leaf") == 0) {
3535 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_LEAF;
3536 }
3537 else if (strcmp(RSTRING_PTR(string), "inline_block") == 0) {
3538 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_INLINE_BLOCK;
3539 }
3540 else if (strcmp(RSTRING_PTR(string), "use_block") == 0) {
3541 iseq_set_use_block(iseq);
3542 }
3543 else if (strcmp(RSTRING_PTR(string), "c_trace") == 0) {
3544 // Let the iseq act like a C method in backtraces
3545 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_C_TRACE;
3546 }
3547 else if (strcmp(RSTRING_PTR(string), "without_interrupts") == 0) {
3548 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_WITHOUT_INTERRUPTS;
3549 }
3550 else {
3551 COMPILE_ERROR(iseq, node_location->line, "unknown argument to attr!: %s", RSTRING_PTR(string));
3552 return COMPILE_NG;
3553 }
3554 }
3555
3556 return COMPILE_OK;
3557}
3558
3559static int
3560pm_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)
3561{
3562 if (arguments == NULL) {
3563 COMPILE_ERROR(iseq, node_location->line, "arg!: no argument");
3564 return COMPILE_NG;
3565 }
3566
3567 if (arguments->arguments.size != 1) {
3568 COMPILE_ERROR(iseq, node_location->line, "arg!: too many argument");
3569 return COMPILE_NG;
3570 }
3571
3572 const pm_node_t *argument = arguments->arguments.nodes[0];
3573 if (!PM_NODE_TYPE_P(argument, PM_SYMBOL_NODE)) {
3574 COMPILE_ERROR(iseq, node_location->line, "non symbol argument to arg!: %s", pm_node_type(PM_NODE_TYPE(argument)));
3575 return COMPILE_NG;
3576 }
3577
3578 if (!popped) {
3579 ID name = parse_string_symbol(scope_node, ((const pm_symbol_node_t *) argument));
3580 int index = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size - get_local_var_idx(iseq, name);
3581
3582 debugs("id: %s idx: %d\n", rb_id2name(name), index);
3583 PUSH_GETLOCAL(ret, *node_location, index, get_lvar_level(iseq));
3584 }
3585
3586 return COMPILE_OK;
3587}
3588
3589static int
3590pm_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)
3591{
3592 const pm_node_t *ast_node = scope_node->ast_node;
3593 if (!PM_NODE_TYPE_P(ast_node, PM_DEF_NODE)) {
3594 rb_bug("mandatory_only?: not in method definition");
3595 return COMPILE_NG;
3596 }
3597
3598 const pm_def_node_t *def_node = (const pm_def_node_t *) ast_node;
3599 const pm_parameters_node_t *parameters_node = def_node->parameters;
3600 if (parameters_node == NULL) {
3601 rb_bug("mandatory_only?: in method definition with no parameters");
3602 return COMPILE_NG;
3603 }
3604
3605 const pm_node_t *body_node = def_node->body;
3606 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)) {
3607 rb_bug("mandatory_only?: not in method definition with plain statements");
3608 return COMPILE_NG;
3609 }
3610
3611 const pm_if_node_t *if_node = (const pm_if_node_t *) ((const pm_statements_node_t *) body_node)->body.nodes[0];
3612 if (if_node->predicate != ((const pm_node_t *) call_node)) {
3613 rb_bug("mandatory_only?: can't find mandatory node");
3614 return COMPILE_NG;
3615 }
3616
3617 pm_parameters_node_t parameters = {
3618 .base = parameters_node->base,
3619 .requireds = parameters_node->requireds
3620 };
3621
3622 const pm_def_node_t def = {
3623 .base = def_node->base,
3624 .name = def_node->name,
3625 .receiver = def_node->receiver,
3626 .parameters = &parameters,
3627 .body = (pm_node_t *) if_node->statements,
3628 .locals = {
3629 .ids = def_node->locals.ids,
3630 .size = parameters_node->requireds.size,
3631 .capacity = def_node->locals.capacity
3632 }
3633 };
3634
3635 pm_scope_node_t next_scope_node;
3636 pm_scope_node_init(&def.base, &next_scope_node, scope_node);
3637
3638 const rb_iseq_t *mandatory_only_iseq = pm_iseq_build(
3639 &next_scope_node,
3640 rb_iseq_base_label(iseq),
3641 rb_iseq_path(iseq),
3642 rb_iseq_realpath(iseq),
3643 node_location->line,
3644 NULL,
3645 0,
3646 ISEQ_TYPE_METHOD,
3647 ISEQ_COMPILE_DATA(iseq)->option
3648 );
3649 RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->mandatory_only_iseq, (VALUE)mandatory_only_iseq);
3650
3651 pm_scope_node_destroy(&next_scope_node);
3652 return COMPILE_OK;
3653}
3654
3655static int
3656pm_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)
3657{
3658 const pm_arguments_node_t *arguments = call_node->arguments;
3659
3660 if (parent_block != NULL) {
3661 COMPILE_ERROR(iseq, node_location->line, "should not call builtins here.");
3662 return COMPILE_NG;
3663 }
3664
3665#define BUILTIN_INLINE_PREFIX "_bi"
3666 char inline_func[sizeof(BUILTIN_INLINE_PREFIX) + DECIMAL_SIZE_OF(int)];
3667 bool cconst = false;
3668retry:;
3669 const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
3670
3671 if (bf == NULL) {
3672 if (strcmp("cstmt!", builtin_func) == 0 || strcmp("cexpr!", builtin_func) == 0) {
3673 // ok
3674 }
3675 else if (strcmp("cconst!", builtin_func) == 0) {
3676 cconst = true;
3677 }
3678 else if (strcmp("cinit!", builtin_func) == 0) {
3679 // ignore
3680 return COMPILE_OK;
3681 }
3682 else if (strcmp("attr!", builtin_func) == 0) {
3683 return pm_compile_builtin_attr(iseq, scope_node, arguments, node_location);
3684 }
3685 else if (strcmp("arg!", builtin_func) == 0) {
3686 return pm_compile_builtin_arg(iseq, ret, scope_node, arguments, node_location, popped);
3687 }
3688 else if (strcmp("mandatory_only?", builtin_func) == 0) {
3689 if (popped) {
3690 rb_bug("mandatory_only? should be in if condition");
3691 }
3692 else if (!LIST_INSN_SIZE_ZERO(ret)) {
3693 rb_bug("mandatory_only? should be put on top");
3694 }
3695
3696 PUSH_INSN1(ret, *node_location, putobject, Qfalse);
3697 return pm_compile_builtin_mandatory_only_method(iseq, scope_node, call_node, node_location);
3698 }
3699 else if (1) {
3700 rb_bug("can't find builtin function:%s", builtin_func);
3701 }
3702 else {
3703 COMPILE_ERROR(iseq, node_location->line, "can't find builtin function:%s", builtin_func);
3704 return COMPILE_NG;
3705 }
3706
3707 int inline_index = node_location->line;
3708 snprintf(inline_func, sizeof(inline_func), BUILTIN_INLINE_PREFIX "%d", inline_index);
3709 builtin_func = inline_func;
3710 arguments = NULL;
3711 goto retry;
3712 }
3713
3714 if (cconst) {
3715 typedef VALUE(*builtin_func0)(void *, VALUE);
3716 VALUE const_val = (*(builtin_func0)(uintptr_t)bf->func_ptr)(NULL, Qnil);
3717 PUSH_INSN1(ret, *node_location, putobject, const_val);
3718 return COMPILE_OK;
3719 }
3720
3721 // fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
3722
3723 DECL_ANCHOR(args_seq);
3724
3725 int flags = 0;
3726 struct rb_callinfo_kwarg *keywords = NULL;
3727 int argc = pm_setup_args(arguments, call_node->block, &flags, &keywords, iseq, args_seq, scope_node, node_location);
3728
3729 if (argc != bf->argc) {
3730 COMPILE_ERROR(iseq, node_location->line, "argc is not match for builtin function:%s (expect %d but %d)", builtin_func, bf->argc, argc);
3731 return COMPILE_NG;
3732 }
3733
3734 unsigned int start_index;
3735 if (delegate_call_p(iseq, argc, args_seq, &start_index)) {
3736 PUSH_INSN2(ret, *node_location, opt_invokebuiltin_delegate, bf, INT2FIX(start_index));
3737 }
3738 else {
3739 PUSH_SEQ(ret, args_seq);
3740 PUSH_INSN1(ret, *node_location, invokebuiltin, bf);
3741 }
3742
3743 if (popped) PUSH_INSN(ret, *node_location, pop);
3744 return COMPILE_OK;
3745}
3746
3750static void
3751pm_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)
3752{
3753 const pm_location_t *message_loc = &call_node->message_loc;
3754 if (message_loc->length == 0) message_loc = &call_node->base.location;
3755
3756 const pm_node_location_t location = PM_LOCATION_START_LOCATION(message_loc, call_node->base.node_id);
3757
3758 LABEL *else_label = NEW_LABEL(location.line);
3759 LABEL *end_label = NEW_LABEL(location.line);
3760 LABEL *retry_end_l = NEW_LABEL(location.line);
3761
3762 VALUE branches = Qfalse;
3763 rb_code_location_t code_location = { 0 };
3764 int node_id = location.node_id;
3765
3766 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
3767 if (PM_BRANCH_COVERAGE_P(iseq)) {
3768 uint32_t end_cursor = 0;
3769 bool end_found = false;
3770
3771 if (call_node->closing_loc.length > 0) {
3772 uint32_t cursor = call_node->closing_loc.start + call_node->closing_loc.length;
3773 end_cursor = cursor;
3774 end_found = true;
3775 }
3776
3777 if (call_node->arguments != NULL) {
3778 uint32_t cursor = call_node->arguments->base.location.start + call_node->arguments->base.location.length;
3779 if (!end_found || cursor > end_cursor) {
3780 end_cursor = cursor;
3781 end_found = true;
3782 }
3783 }
3784
3785 if (call_node->message_loc.length > 0) {
3786 uint32_t cursor = call_node->message_loc.start + call_node->message_loc.length;
3787 if (!end_found || cursor > end_cursor) {
3788 end_cursor = cursor;
3789 end_found = true;
3790 }
3791 }
3792
3793 if (!end_found) {
3794 end_cursor = call_node->closing_loc.start + call_node->closing_loc.length;
3795 }
3796
3797 const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(call_node);
3798 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);
3799
3800 code_location = (rb_code_location_t) {
3801 .beg_pos = { .lineno = start_location.line, .column = start_location.column },
3802 .end_pos = { .lineno = end_location.line, .column = end_location.column }
3803 };
3804
3805 branches = decl_branch_base(iseq, PTR2NUM(call_node), &code_location, "&.");
3806 }
3807
3808 PUSH_INSN(ret, location, dup);
3809 PUSH_INSNL(ret, location, branchnil, else_label);
3810
3811 add_trace_branch_coverage(iseq, ret, &code_location, node_id, 0, "then", branches);
3812 }
3813
3814 LINK_ELEMENT *opt_new_prelude = LAST_ELEMENT(ret);
3815
3816 int flags = 0;
3817 struct rb_callinfo_kwarg *kw_arg = NULL;
3818
3819 int orig_argc = pm_setup_args(call_node->arguments, call_node->block, &flags, &kw_arg, iseq, ret, scope_node, &location);
3820 const rb_iseq_t *previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
3821 const rb_iseq_t *block_iseq = NULL;
3822
3823 if (call_node->block != NULL && PM_NODE_TYPE_P(call_node->block, PM_BLOCK_NODE)) {
3824 // Scope associated with the block
3825 pm_scope_node_t next_scope_node;
3826 pm_scope_node_init(call_node->block, &next_scope_node, scope_node);
3827
3828 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));
3829 pm_scope_node_destroy(&next_scope_node);
3830 ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
3831 }
3832 else {
3833 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
3834 flags |= VM_CALL_VCALL;
3835 }
3836
3837 if (!flags) {
3838 flags |= VM_CALL_ARGS_SIMPLE;
3839 }
3840 }
3841
3842 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) {
3843 flags |= VM_CALL_FCALL;
3844 }
3845
3846 if (!popped && PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) {
3847 if (flags & VM_CALL_ARGS_BLOCKARG) {
3848 PUSH_INSN1(ret, location, topn, INT2FIX(1));
3849 if (flags & VM_CALL_ARGS_SPLAT) {
3850 PUSH_INSN1(ret, location, putobject, INT2FIX(-1));
3851 PUSH_SEND_WITH_FLAG(ret, location, idAREF, INT2FIX(1), INT2FIX(0));
3852 }
3853 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 3));
3854 PUSH_INSN(ret, location, pop);
3855 }
3856 else if (flags & VM_CALL_ARGS_SPLAT) {
3857 PUSH_INSN(ret, location, dup);
3858 PUSH_INSN1(ret, location, putobject, INT2FIX(-1));
3859 PUSH_SEND_WITH_FLAG(ret, location, idAREF, INT2FIX(1), INT2FIX(0));
3860 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 2));
3861 PUSH_INSN(ret, location, pop);
3862 }
3863 else {
3864 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 1));
3865 }
3866 }
3867
3868 if ((flags & VM_CALL_KW_SPLAT) && (flags & VM_CALL_ARGS_BLOCKARG) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
3869 PUSH_INSN(ret, location, splatkw);
3870 }
3871
3872 LABEL *not_basic_new = NEW_LABEL(location.line);
3873 LABEL *not_basic_new_finish = NEW_LABEL(location.line);
3874
3875 bool inline_new = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction &&
3876 method_id == rb_intern("new") &&
3877 call_node->block == NULL &&
3878 (flags & VM_CALL_ARGS_BLOCKARG) == 0;
3879
3880 if (inline_new) {
3881 if (LAST_ELEMENT(ret) == opt_new_prelude) {
3882 PUSH_INSN(ret, location, putnil);
3883 PUSH_INSN(ret, location, swap);
3884 }
3885 else {
3886 ELEM_INSERT_NEXT(opt_new_prelude, &new_insn_body(iseq, location.line, location.node_id, BIN(swap), 0)->link);
3887 ELEM_INSERT_NEXT(opt_new_prelude, &new_insn_body(iseq, location.line, location.node_id, BIN(putnil), 0)->link);
3888 }
3889
3890 // Jump unless the receiver uses the "basic" implementation of "new"
3891 VALUE ci;
3892 if (flags & VM_CALL_FORWARDING) {
3893 ci = (VALUE)new_callinfo(iseq, method_id, orig_argc + 1, flags, kw_arg, 0);
3894 }
3895 else {
3896 ci = (VALUE)new_callinfo(iseq, method_id, orig_argc, flags, kw_arg, 0);
3897 }
3898
3899 PUSH_INSN2(ret, location, opt_new, ci, not_basic_new);
3900 LABEL_REF(not_basic_new);
3901 // optimized path
3902 PUSH_SEND_R(ret, location, rb_intern("initialize"), INT2FIX(orig_argc), block_iseq, INT2FIX(flags | VM_CALL_FCALL), kw_arg);
3903 PUSH_INSNL(ret, location, jump, not_basic_new_finish);
3904
3905 PUSH_LABEL(ret, not_basic_new);
3906 // Fall back to normal send
3907 PUSH_SEND_R(ret, location, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg);
3908 PUSH_INSN(ret, location, swap);
3909
3910 PUSH_LABEL(ret, not_basic_new_finish);
3911 PUSH_INSN(ret, location, pop);
3912 }
3913 else {
3914 PUSH_SEND_R(ret, location, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg);
3915 }
3916
3917 if (block_iseq && ISEQ_BODY(block_iseq)->catch_table) {
3918 pm_compile_retry_end_label(iseq, ret, retry_end_l);
3919 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, start, retry_end_l, block_iseq, retry_end_l);
3920 }
3921
3922 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
3923 PUSH_INSNL(ret, location, jump, end_label);
3924 PUSH_LABEL(ret, else_label);
3925 add_trace_branch_coverage(iseq, ret, &code_location, node_id, 1, "else", branches);
3926 PUSH_LABEL(ret, end_label);
3927 }
3928
3929 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && !popped) {
3930 PUSH_INSN(ret, location, pop);
3931 }
3932
3933 if (popped) PUSH_INSN(ret, location, pop);
3934 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
3935}
3936
3941static inline VALUE
3942pm_compile_back_reference_ref(const pm_scope_node_t *scope_node, const pm_back_reference_read_node_t *node)
3943{
3944 const char *type = (const char *) (pm_parser_start(scope_node->parser) + node->base.location.start + 1);
3945
3946 // Since a back reference is `$<char>`, Ruby represents the ID as an
3947 // rb_intern on the value after the `$`.
3948 return INT2FIX(rb_intern2(type, 1)) << 1 | 1;
3949}
3950
3955static inline VALUE
3956pm_compile_numbered_reference_ref(const pm_numbered_reference_read_node_t *node)
3957{
3958 return INT2FIX(node->number << 1);
3959}
3960
3961static void
3962pm_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)
3963{
3964#define PUSH_VAL(type) (in_condition ? Qtrue : rb_iseq_defined_string(type))
3965
3966 // in_condition is the same as compile.c's needstr
3967 enum defined_type dtype = DEFINED_NOT_DEFINED;
3968 const pm_node_location_t location = *node_location;
3969
3970 switch (PM_NODE_TYPE(node)) {
3971/* DEFINED_NIL ****************************************************************/
3972 case PM_NIL_NODE:
3973 // defined?(nil)
3974 // ^^^
3975 dtype = DEFINED_NIL;
3976 break;
3977/* DEFINED_IVAR ***************************************************************/
3978 case PM_INSTANCE_VARIABLE_READ_NODE: {
3979 // defined?(@a)
3980 // ^^
3982 ID name = pm_constant_id_lookup(scope_node, cast->name);
3983
3984 PUSH_INSN3(ret, location, definedivar, ID2SYM(name), get_ivar_ic_value(iseq, name), PUSH_VAL(DEFINED_IVAR));
3985
3986 return;
3987 }
3988/* DEFINED_LVAR ***************************************************************/
3989 case PM_LOCAL_VARIABLE_READ_NODE:
3990 // a = 1; defined?(a)
3991 // ^
3992 case PM_IT_LOCAL_VARIABLE_READ_NODE:
3993 // 1.then { defined?(it) }
3994 // ^^
3995 dtype = DEFINED_LVAR;
3996 break;
3997/* DEFINED_GVAR ***************************************************************/
3998 case PM_GLOBAL_VARIABLE_READ_NODE: {
3999 // defined?($a)
4000 // ^^
4002 ID name = pm_constant_id_lookup(scope_node, cast->name);
4003
4004 PUSH_INSN(ret, location, putnil);
4005 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_GVAR), ID2SYM(name), PUSH_VAL(DEFINED_GVAR));
4006
4007 return;
4008 }
4009/* DEFINED_CVAR ***************************************************************/
4010 case PM_CLASS_VARIABLE_READ_NODE: {
4011 // defined?(@@a)
4012 // ^^^
4014 ID name = pm_constant_id_lookup(scope_node, cast->name);
4015
4016 PUSH_INSN(ret, location, putnil);
4017 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CVAR), ID2SYM(name), PUSH_VAL(DEFINED_CVAR));
4018
4019 return;
4020 }
4021/* DEFINED_CONST **************************************************************/
4022 case PM_CONSTANT_READ_NODE: {
4023 // defined?(A)
4024 // ^
4025 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
4026 ID name = pm_constant_id_lookup(scope_node, cast->name);
4027
4028 PUSH_INSN(ret, location, putnil);
4029 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), ID2SYM(name), PUSH_VAL(DEFINED_CONST));
4030
4031 return;
4032 }
4033/* DEFINED_YIELD **************************************************************/
4034 case PM_YIELD_NODE:
4035 // defined?(yield)
4036 // ^^^^^
4037 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
4038
4039 PUSH_INSN(ret, location, putnil);
4040 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_YIELD), 0, PUSH_VAL(DEFINED_YIELD));
4041
4042 return;
4043/* DEFINED_ZSUPER *************************************************************/
4044 case PM_SUPER_NODE: {
4045 // defined?(super 1, 2)
4046 // ^^^^^^^^^^
4047 const pm_super_node_t *cast = (const pm_super_node_t *) node;
4048
4049 if (cast->block != NULL && !PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE)) {
4050 dtype = DEFINED_EXPR;
4051 break;
4052 }
4053
4054 PUSH_INSN(ret, location, putnil);
4055 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_ZSUPER), 0, PUSH_VAL(DEFINED_ZSUPER));
4056 return;
4057 }
4058 case PM_FORWARDING_SUPER_NODE: {
4059 // defined?(super)
4060 // ^^^^^
4061 const pm_forwarding_super_node_t *cast = (const pm_forwarding_super_node_t *) node;
4062
4063 if (cast->block != NULL) {
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/* DEFINED_SELF ***************************************************************/
4073 case PM_SELF_NODE:
4074 // defined?(self)
4075 // ^^^^
4076 dtype = DEFINED_SELF;
4077 break;
4078/* DEFINED_TRUE ***************************************************************/
4079 case PM_TRUE_NODE:
4080 // defined?(true)
4081 // ^^^^
4082 dtype = DEFINED_TRUE;
4083 break;
4084/* DEFINED_FALSE **************************************************************/
4085 case PM_FALSE_NODE:
4086 // defined?(false)
4087 // ^^^^^
4088 dtype = DEFINED_FALSE;
4089 break;
4090/* DEFINED_ASGN ***************************************************************/
4091 case PM_CALL_AND_WRITE_NODE:
4092 // defined?(a.a &&= 1)
4093 // ^^^^^^^^^
4094 case PM_CALL_OPERATOR_WRITE_NODE:
4095 // defined?(a.a += 1)
4096 // ^^^^^^^^
4097 case PM_CALL_OR_WRITE_NODE:
4098 // defined?(a.a ||= 1)
4099 // ^^^^^^^^^
4100 case PM_CLASS_VARIABLE_AND_WRITE_NODE:
4101 // defined?(@@a &&= 1)
4102 // ^^^^^^^^^
4103 case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE:
4104 // defined?(@@a += 1)
4105 // ^^^^^^^^
4106 case PM_CLASS_VARIABLE_OR_WRITE_NODE:
4107 // defined?(@@a ||= 1)
4108 // ^^^^^^^^^
4109 case PM_CLASS_VARIABLE_WRITE_NODE:
4110 // defined?(@@a = 1)
4111 // ^^^^^^^
4112 case PM_CONSTANT_AND_WRITE_NODE:
4113 // defined?(A &&= 1)
4114 // ^^^^^^^
4115 case PM_CONSTANT_OPERATOR_WRITE_NODE:
4116 // defined?(A += 1)
4117 // ^^^^^^
4118 case PM_CONSTANT_OR_WRITE_NODE:
4119 // defined?(A ||= 1)
4120 // ^^^^^^^
4121 case PM_CONSTANT_PATH_AND_WRITE_NODE:
4122 // defined?(A::A &&= 1)
4123 // ^^^^^^^^^^
4124 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE:
4125 // defined?(A::A += 1)
4126 // ^^^^^^^^^
4127 case PM_CONSTANT_PATH_OR_WRITE_NODE:
4128 // defined?(A::A ||= 1)
4129 // ^^^^^^^^^^
4130 case PM_CONSTANT_PATH_WRITE_NODE:
4131 // defined?(A::A = 1)
4132 // ^^^^^^^^
4133 case PM_CONSTANT_WRITE_NODE:
4134 // defined?(A = 1)
4135 // ^^^^^
4136 case PM_GLOBAL_VARIABLE_AND_WRITE_NODE:
4137 // defined?($a &&= 1)
4138 // ^^^^^^^^
4139 case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE:
4140 // defined?($a += 1)
4141 // ^^^^^^^
4142 case PM_GLOBAL_VARIABLE_OR_WRITE_NODE:
4143 // defined?($a ||= 1)
4144 // ^^^^^^^^
4145 case PM_GLOBAL_VARIABLE_WRITE_NODE:
4146 // defined?($a = 1)
4147 // ^^^^^^
4148 case PM_INDEX_AND_WRITE_NODE:
4149 // defined?(a[1] &&= 1)
4150 // ^^^^^^^^^^
4151 case PM_INDEX_OPERATOR_WRITE_NODE:
4152 // defined?(a[1] += 1)
4153 // ^^^^^^^^^
4154 case PM_INDEX_OR_WRITE_NODE:
4155 // defined?(a[1] ||= 1)
4156 // ^^^^^^^^^^
4157 case PM_INSTANCE_VARIABLE_AND_WRITE_NODE:
4158 // defined?(@a &&= 1)
4159 // ^^^^^^^^
4160 case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE:
4161 // defined?(@a += 1)
4162 // ^^^^^^^
4163 case PM_INSTANCE_VARIABLE_OR_WRITE_NODE:
4164 // defined?(@a ||= 1)
4165 // ^^^^^^^^
4166 case PM_INSTANCE_VARIABLE_WRITE_NODE:
4167 // defined?(@a = 1)
4168 // ^^^^^^
4169 case PM_LOCAL_VARIABLE_AND_WRITE_NODE:
4170 // defined?(a &&= 1)
4171 // ^^^^^^^
4172 case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE:
4173 // defined?(a += 1)
4174 // ^^^^^^
4175 case PM_LOCAL_VARIABLE_OR_WRITE_NODE:
4176 // defined?(a ||= 1)
4177 // ^^^^^^^
4178 case PM_LOCAL_VARIABLE_WRITE_NODE:
4179 // defined?(a = 1)
4180 // ^^^^^
4181 case PM_MULTI_WRITE_NODE:
4182 // defined?((a, = 1))
4183 // ^^^^^^
4184 dtype = DEFINED_ASGN;
4185 break;
4186/* DEFINED_EXPR ***************************************************************/
4187 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
4188 // defined?((alias $a $b))
4189 // ^^^^^^^^^^^
4190 case PM_ALIAS_METHOD_NODE:
4191 // defined?((alias a b))
4192 // ^^^^^^^^^
4193 case PM_AND_NODE:
4194 // defined?(a and b)
4195 // ^^^^^^^
4196 case PM_BREAK_NODE:
4197 // defined?(break 1)
4198 // ^^^^^^^
4199 case PM_CASE_MATCH_NODE:
4200 // defined?(case 1; in 1; end)
4201 // ^^^^^^^^^^^^^^^^^
4202 case PM_CASE_NODE:
4203 // defined?(case 1; when 1; end)
4204 // ^^^^^^^^^^^^^^^^^^^
4205 case PM_CLASS_NODE:
4206 // defined?(class Foo; end)
4207 // ^^^^^^^^^^^^^^
4208 case PM_DEF_NODE:
4209 // defined?(def a() end)
4210 // ^^^^^^^^^^^
4211 case PM_DEFINED_NODE:
4212 // defined?(defined?(a))
4213 // ^^^^^^^^^^^
4214 case PM_FLIP_FLOP_NODE:
4215 // defined?(not (a .. b))
4216 // ^^^^^^
4217 case PM_FLOAT_NODE:
4218 // defined?(1.0)
4219 // ^^^
4220 case PM_FOR_NODE:
4221 // defined?(for a in 1 do end)
4222 // ^^^^^^^^^^^^^^^^^
4223 case PM_IF_NODE:
4224 // defined?(if a then end)
4225 // ^^^^^^^^^^^^^
4226 case PM_IMAGINARY_NODE:
4227 // defined?(1i)
4228 // ^^
4229 case PM_INTEGER_NODE:
4230 // defined?(1)
4231 // ^
4232 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE:
4233 // defined?(not /#{1}/)
4234 // ^^^^^^
4235 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
4236 // defined?(/#{1}/)
4237 // ^^^^^^
4238 case PM_INTERPOLATED_STRING_NODE:
4239 // defined?("#{1}")
4240 // ^^^^^^
4241 case PM_INTERPOLATED_SYMBOL_NODE:
4242 // defined?(:"#{1}")
4243 // ^^^^^^^
4244 case PM_INTERPOLATED_X_STRING_NODE:
4245 // defined?(`#{1}`)
4246 // ^^^^^^
4247 case PM_LAMBDA_NODE:
4248 // defined?(-> {})
4249 // ^^^^^
4250 case PM_MATCH_LAST_LINE_NODE:
4251 // defined?(not //)
4252 // ^^^^^^
4253 case PM_MATCH_PREDICATE_NODE:
4254 // defined?(1 in 1)
4255 // ^^^^^^
4256 case PM_MATCH_REQUIRED_NODE:
4257 // defined?(1 => 1)
4258 // ^^^^^^
4259 case PM_MATCH_WRITE_NODE:
4260 // defined?(/(?<a>)/ =~ "")
4261 // ^^^^^^^^^^^^^^
4262 case PM_MODULE_NODE:
4263 // defined?(module A end)
4264 // ^^^^^^^^^^^^
4265 case PM_NEXT_NODE:
4266 // defined?(next 1)
4267 // ^^^^^^
4268 case PM_OR_NODE:
4269 // defined?(a or b)
4270 // ^^^^^^
4271 case PM_POST_EXECUTION_NODE:
4272 // defined?((END {}))
4273 // ^^^^^^^^
4274 case PM_RANGE_NODE:
4275 // defined?(1..1)
4276 // ^^^^
4277 case PM_RATIONAL_NODE:
4278 // defined?(1r)
4279 // ^^
4280 case PM_REDO_NODE:
4281 // defined?(redo)
4282 // ^^^^
4283 case PM_REGULAR_EXPRESSION_NODE:
4284 // defined?(//)
4285 // ^^
4286 case PM_RESCUE_MODIFIER_NODE:
4287 // defined?(a rescue b)
4288 // ^^^^^^^^^^
4289 case PM_RETRY_NODE:
4290 // defined?(retry)
4291 // ^^^^^
4292 case PM_RETURN_NODE:
4293 // defined?(return)
4294 // ^^^^^^
4295 case PM_SINGLETON_CLASS_NODE:
4296 // defined?(class << self; end)
4297 // ^^^^^^^^^^^^^^^^^^
4298 case PM_SOURCE_ENCODING_NODE:
4299 // defined?(__ENCODING__)
4300 // ^^^^^^^^^^^^
4301 case PM_SOURCE_FILE_NODE:
4302 // defined?(__FILE__)
4303 // ^^^^^^^^
4304 case PM_SOURCE_LINE_NODE:
4305 // defined?(__LINE__)
4306 // ^^^^^^^^
4307 case PM_STRING_NODE:
4308 // defined?("")
4309 // ^^
4310 case PM_SYMBOL_NODE:
4311 // defined?(:a)
4312 // ^^
4313 case PM_UNDEF_NODE:
4314 // defined?((undef a))
4315 // ^^^^^^^
4316 case PM_UNLESS_NODE:
4317 // defined?(unless a then end)
4318 // ^^^^^^^^^^^^^^^^^
4319 case PM_UNTIL_NODE:
4320 // defined?(until a do end)
4321 // ^^^^^^^^^^^^^^
4322 case PM_WHILE_NODE:
4323 // defined?(while a do end)
4324 // ^^^^^^^^^^^^^^
4325 case PM_X_STRING_NODE:
4326 // defined?(``)
4327 // ^^
4328 dtype = DEFINED_EXPR;
4329 break;
4330/* DEFINED_REF ****************************************************************/
4331 case PM_BACK_REFERENCE_READ_NODE: {
4332 // defined?($+)
4333 // ^^
4335 VALUE ref = pm_compile_back_reference_ref(scope_node, cast);
4336
4337 PUSH_INSN(ret, location, putnil);
4338 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_REF), ref, PUSH_VAL(DEFINED_GVAR));
4339
4340 return;
4341 }
4342 case PM_NUMBERED_REFERENCE_READ_NODE: {
4343 // defined?($1)
4344 // ^^
4346 VALUE ref = pm_compile_numbered_reference_ref(cast);
4347
4348 PUSH_INSN(ret, location, putnil);
4349 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_REF), ref, PUSH_VAL(DEFINED_GVAR));
4350
4351 return;
4352 }
4353/* DEFINED_CONST_FROM *********************************************************/
4354 case PM_CONSTANT_PATH_NODE: {
4355 // defined?(A::A)
4356 // ^^^^
4357 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
4358 ID name = pm_constant_id_lookup(scope_node, cast->name);
4359
4360 if (cast->parent != NULL) {
4361 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4362 pm_compile_defined_expr0(iseq, cast->parent, node_location, ret, popped, scope_node, true, lfinish, false);
4363
4364 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4365 PM_COMPILE(cast->parent);
4366 }
4367 else {
4368 PUSH_INSN1(ret, location, putobject, rb_cObject);
4369 }
4370
4371 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), ID2SYM(name), PUSH_VAL(DEFINED_CONST));
4372 return;
4373 }
4374/* Containers *****************************************************************/
4375 case PM_BEGIN_NODE: {
4376 // defined?(begin end)
4377 // ^^^^^^^^^
4378 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
4379
4380 if (cast->rescue_clause == NULL && cast->ensure_clause == NULL && cast->else_clause == NULL) {
4381 if (cast->statements == NULL) {
4382 // If we have empty statements, then we want to return "nil".
4383 dtype = DEFINED_NIL;
4384 }
4385 else if (cast->statements->body.size == 1) {
4386 // If we have a begin node that is wrapping a single statement
4387 // then we want to recurse down to that statement and compile
4388 // it.
4389 pm_compile_defined_expr0(iseq, cast->statements->body.nodes[0], node_location, ret, popped, scope_node, in_condition, lfinish, false);
4390 return;
4391 }
4392 else {
4393 // Otherwise, we have a begin wrapping multiple statements, in
4394 // which case this is defined as "expression".
4395 dtype = DEFINED_EXPR;
4396 }
4397 } else {
4398 // If we have any of the other clauses besides the main begin/end,
4399 // this is defined as "expression".
4400 dtype = DEFINED_EXPR;
4401 }
4402
4403 break;
4404 }
4405 case PM_PARENTHESES_NODE: {
4406 // defined?(())
4407 // ^^
4408 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
4409
4410 if (cast->body == NULL) {
4411 // If we have empty parentheses, then we want to return "nil".
4412 dtype = DEFINED_NIL;
4413 }
4414 else if (PM_NODE_TYPE_P(cast->body, PM_STATEMENTS_NODE) && !PM_NODE_FLAG_P(cast, PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS)) {
4415 // If we have a parentheses node that is wrapping a single statement
4416 // then we want to recurse down to that statement and compile it.
4417 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);
4418 return;
4419 }
4420 else {
4421 // Otherwise, we have parentheses wrapping multiple statements, in
4422 // which case this is defined as "expression".
4423 dtype = DEFINED_EXPR;
4424 }
4425
4426 break;
4427 }
4428 case PM_ARRAY_NODE: {
4429 // defined?([])
4430 // ^^
4431 const pm_array_node_t *cast = (const pm_array_node_t *) node;
4432
4433 if (cast->elements.size > 0 && !lfinish[1]) {
4434 lfinish[1] = NEW_LABEL(location.line);
4435 }
4436
4437 for (size_t index = 0; index < cast->elements.size; index++) {
4438 pm_compile_defined_expr0(iseq, cast->elements.nodes[index], node_location, ret, popped, scope_node, true, lfinish, false);
4439 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4440 }
4441
4442 dtype = DEFINED_EXPR;
4443 break;
4444 }
4445 case PM_HASH_NODE:
4446 // defined?({ a: 1 })
4447 // ^^^^^^^^
4448 case PM_KEYWORD_HASH_NODE: {
4449 // defined?(a(a: 1))
4450 // ^^^^
4451 const pm_node_list_t *elements;
4452
4453 if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) {
4454 elements = &((const pm_hash_node_t *) node)->elements;
4455 }
4456 else {
4457 elements = &((const pm_keyword_hash_node_t *) node)->elements;
4458 }
4459
4460 if (elements->size > 0 && !lfinish[1]) {
4461 lfinish[1] = NEW_LABEL(location.line);
4462 }
4463
4464 for (size_t index = 0; index < elements->size; index++) {
4465 pm_compile_defined_expr0(iseq, elements->nodes[index], node_location, ret, popped, scope_node, true, lfinish, false);
4466 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4467 }
4468
4469 dtype = DEFINED_EXPR;
4470 break;
4471 }
4472 case PM_ASSOC_NODE: {
4473 // defined?({ a: 1 })
4474 // ^^^^
4475 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node;
4476
4477 pm_compile_defined_expr0(iseq, cast->key, node_location, ret, popped, scope_node, true, lfinish, false);
4478 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4479 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, true, lfinish, false);
4480
4481 return;
4482 }
4483 case PM_ASSOC_SPLAT_NODE: {
4484 // defined?({ **a })
4485 // ^^^^
4486 const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node;
4487
4488 if (cast->value == NULL) {
4489 dtype = DEFINED_EXPR;
4490 break;
4491 }
4492
4493 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, true, lfinish, false);
4494 return;
4495 }
4496 case PM_IMPLICIT_NODE: {
4497 // defined?({ a: })
4498 // ^^
4499 const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node;
4500 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4501 return;
4502 }
4503 case PM_CALL_NODE: {
4504#define BLOCK_P(cast) ((cast)->block != NULL && PM_NODE_TYPE_P((cast)->block, PM_BLOCK_NODE))
4505
4506 // defined?(a(1, 2, 3))
4507 // ^^^^^^^^^^
4508 const pm_call_node_t *cast = ((const pm_call_node_t *) node);
4509
4510 if (BLOCK_P(cast)) {
4511 dtype = DEFINED_EXPR;
4512 break;
4513 }
4514
4515 if (cast->receiver || cast->arguments || (cast->block && PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE))) {
4516 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4517 if (!lfinish[2]) lfinish[2] = NEW_LABEL(location.line);
4518 }
4519
4520 if (cast->arguments) {
4521 pm_compile_defined_expr0(iseq, (const pm_node_t *) cast->arguments, node_location, ret, popped, scope_node, true, lfinish, false);
4522 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4523 }
4524
4525 if (cast->block && PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE)) {
4526 pm_compile_defined_expr0(iseq, cast->block, node_location, ret, popped, scope_node, true, lfinish, false);
4527 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4528 }
4529
4530 if (cast->receiver) {
4531 if (PM_NODE_TYPE_P(cast->receiver, PM_CALL_NODE) && !BLOCK_P((const pm_call_node_t *) cast->receiver)) {
4532 // Special behavior here where we chain calls together. This is
4533 // the only path that sets explicit_receiver to true.
4534 pm_compile_defined_expr0(iseq, cast->receiver, node_location, ret, popped, scope_node, true, lfinish, true);
4535 PUSH_INSNL(ret, location, branchunless, lfinish[2]);
4536
4537 const pm_call_node_t *receiver = (const pm_call_node_t *) cast->receiver;
4538 ID method_id = pm_constant_id_lookup(scope_node, receiver->name);
4539
4540 pm_compile_call(iseq, receiver, ret, popped, scope_node, method_id, NULL);
4541 }
4542 else {
4543 pm_compile_defined_expr0(iseq, cast->receiver, node_location, ret, popped, scope_node, true, lfinish, false);
4544 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4545 PM_COMPILE(cast->receiver);
4546 }
4547
4548 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
4549
4550 if (explicit_receiver) PUSH_INSN(ret, location, dup);
4551 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_METHOD), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
4552 }
4553 else {
4554 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
4555
4556 PUSH_INSN(ret, location, putself);
4557 if (explicit_receiver) PUSH_INSN(ret, location, dup);
4558
4559 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_FUNC), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
4560 }
4561
4562 return;
4563
4564#undef BLOCK_P
4565 }
4566 case PM_ARGUMENTS_NODE: {
4567 // defined?(a(1, 2, 3))
4568 // ^^^^^^^
4569 const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node;
4570
4571 for (size_t index = 0; index < cast->arguments.size; index++) {
4572 pm_compile_defined_expr0(iseq, cast->arguments.nodes[index], node_location, ret, popped, scope_node, in_condition, lfinish, false);
4573 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4574 }
4575
4576 dtype = DEFINED_EXPR;
4577 break;
4578 }
4579 case PM_BLOCK_ARGUMENT_NODE:
4580 // defined?(a(&b))
4581 // ^^
4582 dtype = DEFINED_EXPR;
4583 break;
4584 case PM_FORWARDING_ARGUMENTS_NODE:
4585 // def a(...) = defined?(a(...))
4586 // ^^^
4587 dtype = DEFINED_EXPR;
4588 break;
4589 case PM_SPLAT_NODE: {
4590 // def a(*) = defined?(a(*))
4591 // ^
4592 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
4593
4594 if (cast->expression == NULL) {
4595 dtype = DEFINED_EXPR;
4596 break;
4597 }
4598
4599 pm_compile_defined_expr0(iseq, cast->expression, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4600
4601 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4602 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4603
4604 dtype = DEFINED_EXPR;
4605 break;
4606 }
4607 case PM_SHAREABLE_CONSTANT_NODE:
4608 // # shareable_constant_value: literal
4609 // defined?(A = 1)
4610 // ^^^^^
4611 pm_compile_defined_expr0(iseq, ((const pm_shareable_constant_node_t *) node)->write, node_location, ret, popped, scope_node, in_condition, lfinish, explicit_receiver);
4612 return;
4613/* Unreachable (parameters) ***************************************************/
4614 case PM_BLOCK_LOCAL_VARIABLE_NODE:
4615 case PM_BLOCK_PARAMETER_NODE:
4616 case PM_BLOCK_PARAMETERS_NODE:
4617 case PM_FORWARDING_PARAMETER_NODE:
4618 case PM_IMPLICIT_REST_NODE:
4619 case PM_IT_PARAMETERS_NODE:
4620 case PM_PARAMETERS_NODE:
4621 case PM_KEYWORD_REST_PARAMETER_NODE:
4622 case PM_NO_KEYWORDS_PARAMETER_NODE:
4623 case PM_NO_BLOCK_PARAMETER_NODE:
4624 case PM_NUMBERED_PARAMETERS_NODE:
4625 case PM_OPTIONAL_KEYWORD_PARAMETER_NODE:
4626 case PM_OPTIONAL_PARAMETER_NODE:
4627 case PM_REQUIRED_KEYWORD_PARAMETER_NODE:
4628 case PM_REQUIRED_PARAMETER_NODE:
4629 case PM_REST_PARAMETER_NODE:
4630/* Unreachable (pattern matching) *********************************************/
4631 case PM_ALTERNATION_PATTERN_NODE:
4632 case PM_ARRAY_PATTERN_NODE:
4633 case PM_CAPTURE_PATTERN_NODE:
4634 case PM_FIND_PATTERN_NODE:
4635 case PM_HASH_PATTERN_NODE:
4636 case PM_PINNED_EXPRESSION_NODE:
4637 case PM_PINNED_VARIABLE_NODE:
4638/* Unreachable (indirect writes) **********************************************/
4639 case PM_CALL_TARGET_NODE:
4640 case PM_CLASS_VARIABLE_TARGET_NODE:
4641 case PM_CONSTANT_PATH_TARGET_NODE:
4642 case PM_CONSTANT_TARGET_NODE:
4643 case PM_GLOBAL_VARIABLE_TARGET_NODE:
4644 case PM_INDEX_TARGET_NODE:
4645 case PM_INSTANCE_VARIABLE_TARGET_NODE:
4646 case PM_LOCAL_VARIABLE_TARGET_NODE:
4647 case PM_MULTI_TARGET_NODE:
4648/* Unreachable (clauses) ******************************************************/
4649 case PM_ELSE_NODE:
4650 case PM_ENSURE_NODE:
4651 case PM_IN_NODE:
4652 case PM_RESCUE_NODE:
4653 case PM_WHEN_NODE:
4654/* Unreachable (miscellaneous) ************************************************/
4655 case PM_BLOCK_NODE:
4656 case PM_EMBEDDED_STATEMENTS_NODE:
4657 case PM_EMBEDDED_VARIABLE_NODE:
4658 case PM_ERROR_RECOVERY_NODE:
4659 case PM_PRE_EXECUTION_NODE:
4660 case PM_PROGRAM_NODE:
4661 case PM_SCOPE_NODE:
4662 case PM_STATEMENTS_NODE:
4663 rb_bug("Unreachable node in defined?: %s", pm_node_type(PM_NODE_TYPE(node)));
4664 }
4665
4666 RUBY_ASSERT(dtype != DEFINED_NOT_DEFINED);
4667 PUSH_INSN1(ret, location, putobject, PUSH_VAL(dtype));
4668
4669#undef PUSH_VAL
4670}
4671
4672static void
4673pm_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)
4674{
4675 LINK_ELEMENT *lcur = ret->last;
4676 pm_compile_defined_expr0(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4677
4678 if (lfinish[1]) {
4679 LABEL *lstart = NEW_LABEL(node_location->line);
4680 LABEL *lend = NEW_LABEL(node_location->line);
4681
4683 rb_iseq_new_with_callback_new_callback(build_defined_rescue_iseq, NULL);
4684
4685 const rb_iseq_t *rescue = new_child_iseq_with_callback(
4686 iseq,
4687 ifunc,
4688 rb_str_concat(rb_str_new2("defined guard in "), ISEQ_BODY(iseq)->location.label),
4689 iseq,
4690 ISEQ_TYPE_RESCUE,
4691 0
4692 );
4693
4694 lstart->rescued = LABEL_RESCUE_BEG;
4695 lend->rescued = LABEL_RESCUE_END;
4696
4697 APPEND_LABEL(ret, lcur, lstart);
4698 PUSH_LABEL(ret, lend);
4699 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
4700 }
4701}
4702
4703static void
4704pm_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)
4705{
4706 LABEL *lfinish[3];
4707 LINK_ELEMENT *last = ret->last;
4708
4709 lfinish[0] = NEW_LABEL(node_location->line);
4710 lfinish[1] = 0;
4711 lfinish[2] = 0;
4712
4713 if (!popped) {
4714 pm_defined_expr(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish);
4715 }
4716
4717 if (lfinish[1]) {
4718 ELEM_INSERT_NEXT(last, &new_insn_body(iseq, node_location->line, node_location->node_id, BIN(putnil), 0)->link);
4719 PUSH_INSN(ret, *node_location, swap);
4720
4721 if (lfinish[2]) PUSH_LABEL(ret, lfinish[2]);
4722 PUSH_INSN(ret, *node_location, pop);
4723 PUSH_LABEL(ret, lfinish[1]);
4724
4725 }
4726
4727 PUSH_LABEL(ret, lfinish[0]);
4728}
4729
4730// This is exactly the same as add_ensure_iseq, except it compiled
4731// the node as a Prism node, and not a CRuby node
4732static void
4733pm_add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return, pm_scope_node_t *scope_node)
4734{
4735 RUBY_ASSERT(can_add_ensure_iseq(iseq));
4736
4738 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
4739 struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
4740 DECL_ANCHOR(ensure);
4741
4742 while (enlp) {
4743 if (enlp->erange != NULL) {
4744 DECL_ANCHOR(ensure_part);
4745 LABEL *lstart = NEW_LABEL(0);
4746 LABEL *lend = NEW_LABEL(0);
4747
4748 add_ensure_range(iseq, enlp->erange, lstart, lend);
4749
4750 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
4751 PUSH_LABEL(ensure_part, lstart);
4752 bool popped = true;
4753 PM_COMPILE_INTO_ANCHOR(ensure_part, (const pm_node_t *) enlp->ensure_node);
4754 PUSH_LABEL(ensure_part, lend);
4755 PUSH_SEQ(ensure, ensure_part);
4756 }
4757 else {
4758 if (!is_return) {
4759 break;
4760 }
4761 }
4762 enlp = enlp->prev;
4763 }
4764 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = prev_enlp;
4765 PUSH_SEQ(ret, ensure);
4766}
4767
4768
4769
4775static void
4776pm_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)
4777{
4778 RUBY_ASSERT((constant_id & PM_SPECIAL_CONSTANT_FLAG) == 0);
4779
4780 ID local = pm_constant_id_lookup(scope_node, constant_id);
4781 local_table_for_iseq->ids[local_index] = local;
4782 pm_index_lookup_table_insert(index_lookup_table, constant_id, local_index);
4783}
4784
4788static void
4789pm_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)
4790{
4791 local_table_for_iseq->ids[local_index] = local_name;
4792 pm_index_lookup_table_insert(index_lookup_table, special_id, local_index);
4793}
4794
4801static int
4802pm_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)
4803{
4804 for (size_t index = 0; index < node->lefts.size; index++) {
4805 const pm_node_t *left = node->lefts.nodes[index];
4806
4807 if (PM_NODE_TYPE_P(left, PM_REQUIRED_PARAMETER_NODE)) {
4808 if (!PM_NODE_FLAG_P(left, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4809 pm_insert_local_index(((const pm_required_parameter_node_t *) left)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4810 local_index++;
4811 }
4812 }
4813 else {
4814 RUBY_ASSERT(PM_NODE_TYPE_P(left, PM_MULTI_TARGET_NODE));
4815 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);
4816 }
4817 }
4818
4819 if (node->rest != NULL && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE)) {
4820 const pm_splat_node_t *rest = (const pm_splat_node_t *) node->rest;
4821
4822 if (rest->expression != NULL) {
4823 RUBY_ASSERT(PM_NODE_TYPE_P(rest->expression, PM_REQUIRED_PARAMETER_NODE));
4824
4825 if (!PM_NODE_FLAG_P(rest->expression, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4826 pm_insert_local_index(((const pm_required_parameter_node_t *) rest->expression)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4827 local_index++;
4828 }
4829 }
4830 }
4831
4832 for (size_t index = 0; index < node->rights.size; index++) {
4833 const pm_node_t *right = node->rights.nodes[index];
4834
4835 if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) {
4836 if (!PM_NODE_FLAG_P(right, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4837 pm_insert_local_index(((const pm_required_parameter_node_t *) right)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4838 local_index++;
4839 }
4840 }
4841 else {
4842 RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE));
4843 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);
4844 }
4845 }
4846
4847 return local_index;
4848}
4849
4854static inline void
4855pm_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)
4856{
4857 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
4858 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, node->name, 0);
4859 PUSH_SETLOCAL(ret, location, index.index, index.level);
4860}
4861
4870static void
4871pm_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)
4872{
4873 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
4874 bool has_rest = (node->rest && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE) && (((const pm_splat_node_t *) node->rest)->expression) != NULL);
4875 bool has_rights = node->rights.size > 0;
4876
4877 int flag = (has_rest || has_rights) ? 1 : 0;
4878 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->lefts.size), INT2FIX(flag));
4879
4880 for (size_t index = 0; index < node->lefts.size; index++) {
4881 const pm_node_t *left = node->lefts.nodes[index];
4882
4883 if (PM_NODE_TYPE_P(left, PM_REQUIRED_PARAMETER_NODE)) {
4884 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) left, ret, scope_node);
4885 }
4886 else {
4887 RUBY_ASSERT(PM_NODE_TYPE_P(left, PM_MULTI_TARGET_NODE));
4888 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) left, ret, scope_node);
4889 }
4890 }
4891
4892 if (has_rest) {
4893 if (has_rights) {
4894 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->rights.size), INT2FIX(3));
4895 }
4896
4897 const pm_node_t *rest = ((const pm_splat_node_t *) node->rest)->expression;
4898 RUBY_ASSERT(PM_NODE_TYPE_P(rest, PM_REQUIRED_PARAMETER_NODE));
4899
4900 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) rest, ret, scope_node);
4901 }
4902
4903 if (has_rights) {
4904 if (!has_rest) {
4905 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->rights.size), INT2FIX(2));
4906 }
4907
4908 for (size_t index = 0; index < node->rights.size; index++) {
4909 const pm_node_t *right = node->rights.nodes[index];
4910
4911 if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) {
4912 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) right, ret, scope_node);
4913 }
4914 else {
4915 RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE));
4916 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) right, ret, scope_node);
4917 }
4918 }
4919 }
4920}
4921
4927 // The pointer to the topn instruction that will need to be modified after
4928 // we know the total stack size of all of the targets.
4929 INSN *topn;
4930
4931 // The index of the stack from the base of the entire multi target at which
4932 // the parent expression is located.
4933 size_t stack_index;
4934
4935 // The number of slots in the stack that this node occupies.
4936 size_t stack_size;
4937
4938 // The position of the node in the list of targets.
4939 size_t position;
4940
4941 // A pointer to the next node in this linked list.
4942 struct pm_multi_target_state_node *next;
4944
4952typedef struct {
4953 // The total number of slots in the stack that this multi target occupies.
4954 size_t stack_size;
4955
4956 // The position of the current node being compiled. This is forwarded to
4957 // nodes when they are allocated.
4958 size_t position;
4959
4960 // A pointer to the head of this linked list.
4962
4963 // A pointer to the tail of this linked list.
4966
4970static void
4971pm_multi_target_state_push(pm_multi_target_state_t *state, INSN *topn, size_t stack_size)
4972{
4974 node->topn = topn;
4975 node->stack_index = state->stack_size + 1;
4976 node->stack_size = stack_size;
4977 node->position = state->position;
4978 node->next = NULL;
4979
4980 if (state->head == NULL) {
4981 state->head = node;
4982 state->tail = node;
4983 }
4984 else {
4985 state->tail->next = node;
4986 state->tail = node;
4987 }
4988
4989 state->stack_size += stack_size;
4990}
4991
4997static void
4998pm_multi_target_state_update(pm_multi_target_state_t *state)
4999{
5000 // If nothing was ever pushed onto the stack, then we don't need to do any
5001 // kind of updates.
5002 if (state->stack_size == 0) return;
5003
5004 pm_multi_target_state_node_t *current = state->head;
5006
5007 while (current != NULL) {
5008 VALUE offset = INT2FIX(state->stack_size - current->stack_index + current->position);
5009 current->topn->operands[0] = offset;
5010
5011 // stack_size will be > 1 in the case that we compiled an index target
5012 // and it had arguments. In this case, we use multiple topn instructions
5013 // to grab up all of the arguments as well, so those offsets need to be
5014 // updated as well.
5015 if (current->stack_size > 1) {
5016 INSN *insn = current->topn;
5017
5018 for (size_t index = 1; index < current->stack_size; index += 1) {
5019 LINK_ELEMENT *element = get_next_insn(insn);
5020 RUBY_ASSERT(IS_INSN(element));
5021
5022 insn = (INSN *) element;
5023 RUBY_ASSERT(insn->insn_id == BIN(topn));
5024
5025 insn->operands[0] = offset;
5026 }
5027 }
5028
5029 previous = current;
5030 current = current->next;
5031
5032 SIZED_FREE(previous);
5033 }
5034}
5035
5036static void
5037pm_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);
5038
5067static void
5068pm_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)
5069{
5070 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5071
5072 switch (PM_NODE_TYPE(node)) {
5073 case PM_LOCAL_VARIABLE_TARGET_NODE: {
5074 // Local variable targets have no parent expression, so they only need
5075 // to compile the write.
5076 //
5077 // for i in []; end
5078 //
5080 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
5081
5082 PUSH_SETLOCAL(writes, location, index.index, index.level);
5083 break;
5084 }
5085 case PM_CLASS_VARIABLE_TARGET_NODE: {
5086 // Class variable targets have no parent expression, so they only need
5087 // to compile the write.
5088 //
5089 // for @@i in []; end
5090 //
5092 ID name = pm_constant_id_lookup(scope_node, cast->name);
5093
5094 VALUE operand = ID2SYM(name);
5095 PUSH_INSN2(writes, location, setclassvariable, operand, get_cvar_ic_value(iseq, name));
5096 break;
5097 }
5098 case PM_CONSTANT_TARGET_NODE: {
5099 // Constant targets have no parent expression, so they only need to
5100 // compile the write.
5101 //
5102 // for I in []; end
5103 //
5104 const pm_constant_target_node_t *cast = (const pm_constant_target_node_t *) node;
5105 ID name = pm_constant_id_lookup(scope_node, cast->name);
5106
5107 VALUE operand = ID2SYM(name);
5108 PUSH_INSN1(writes, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5109 PUSH_INSN1(writes, location, setconstant, operand);
5110 break;
5111 }
5112 case PM_GLOBAL_VARIABLE_TARGET_NODE: {
5113 // Global variable targets have no parent expression, so they only need
5114 // to compile the write.
5115 //
5116 // for $i in []; end
5117 //
5119 ID name = pm_constant_id_lookup(scope_node, cast->name);
5120
5121 VALUE operand = ID2SYM(name);
5122 PUSH_INSN1(writes, location, setglobal, operand);
5123 break;
5124 }
5125 case PM_INSTANCE_VARIABLE_TARGET_NODE: {
5126 // Instance variable targets have no parent expression, so they only
5127 // need to compile the write.
5128 //
5129 // for @i in []; end
5130 //
5132 ID name = pm_constant_id_lookup(scope_node, cast->name);
5133
5134 VALUE operand = ID2SYM(name);
5135 PUSH_INSN2(writes, location, setinstancevariable, operand, get_ivar_ic_value(iseq, name));
5136 break;
5137 }
5138 case PM_CONSTANT_PATH_TARGET_NODE: {
5139 // Constant path targets have a parent expression that is the object
5140 // that owns the constant. This needs to be compiled first into the
5141 // parents sequence. If no parent is found, then it represents using the
5142 // unary :: operator to indicate a top-level constant. In that case we
5143 // need to push Object onto the stack.
5144 //
5145 // for I::J in []; end
5146 //
5148 ID name = pm_constant_id_lookup(scope_node, cast->name);
5149
5150 if (cast->parent != NULL) {
5151 pm_compile_node(iseq, cast->parent, parents, false, scope_node);
5152 }
5153 else {
5154 PUSH_INSN1(parents, location, putobject, rb_cObject);
5155 }
5156
5157 if (state == NULL) {
5158 PUSH_INSN(writes, location, swap);
5159 }
5160 else {
5161 PUSH_INSN1(writes, location, topn, INT2FIX(1));
5162 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), 1);
5163 }
5164
5165 VALUE operand = ID2SYM(name);
5166 PUSH_INSN1(writes, location, setconstant, operand);
5167
5168 if (state != NULL) {
5169 PUSH_INSN(cleanup, location, pop);
5170 }
5171
5172 break;
5173 }
5174 case PM_CALL_TARGET_NODE: {
5175 // Call targets have a parent expression that is the receiver of the
5176 // method being called. This needs to be compiled first into the parents
5177 // sequence. These nodes cannot have arguments, so the method call is
5178 // compiled with a single argument which represents the value being
5179 // written.
5180 //
5181 // for i.j in []; end
5182 //
5183 const pm_call_target_node_t *cast = (const pm_call_target_node_t *) node;
5184 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
5185
5186 pm_compile_node(iseq, cast->receiver, parents, false, scope_node);
5187
5188 LABEL *safe_label = NULL;
5189 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
5190 safe_label = NEW_LABEL(location.line);
5191 PUSH_INSN(parents, location, dup);
5192 PUSH_INSNL(parents, location, branchnil, safe_label);
5193 }
5194
5195 if (state != NULL) {
5196 PUSH_INSN1(writes, location, topn, INT2FIX(1));
5197 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), 1);
5198 PUSH_INSN(writes, location, swap);
5199 }
5200
5201 int flags = VM_CALL_ARGS_SIMPLE;
5202 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) flags |= VM_CALL_FCALL;
5203
5204 PUSH_SEND_WITH_FLAG(writes, location, method_id, INT2FIX(1), INT2FIX(flags));
5205 if (safe_label != NULL && state == NULL) PUSH_LABEL(writes, safe_label);
5206 PUSH_INSN(writes, location, pop);
5207 if (safe_label != NULL && state != NULL) PUSH_LABEL(writes, safe_label);
5208
5209 if (state != NULL) {
5210 PUSH_INSN(cleanup, location, pop);
5211 }
5212
5213 break;
5214 }
5215 case PM_INDEX_TARGET_NODE: {
5216 // Index targets have a parent expression that is the receiver of the
5217 // method being called and any additional arguments that are being
5218 // passed along with the value being written. The receiver and arguments
5219 // both need to be on the stack. Note that this is even more complicated
5220 // by the fact that these nodes can hold a block using the unary &
5221 // operator.
5222 //
5223 // for i[:j] in []; end
5224 //
5225 const pm_index_target_node_t *cast = (const pm_index_target_node_t *) node;
5226
5227 pm_compile_node(iseq, cast->receiver, parents, false, scope_node);
5228
5229 int flags = 0;
5230 struct rb_callinfo_kwarg *kwargs = NULL;
5231 int argc = pm_setup_args(cast->arguments, (const pm_node_t *) cast->block, &flags, &kwargs, iseq, parents, scope_node, &location);
5232
5233 if (state != NULL) {
5234 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5235 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), argc + 1);
5236
5237 if (argc == 0) {
5238 PUSH_INSN(writes, location, swap);
5239 }
5240 else {
5241 for (int index = 0; index < argc; index++) {
5242 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5243 }
5244 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5245 }
5246 }
5247
5248 // The argc that we're going to pass to the send instruction is the
5249 // number of arguments + 1 for the value being written. If there's a
5250 // splat, then we need to insert newarray and concatarray instructions
5251 // after the arguments have been written.
5252 int ci_argc = argc + 1;
5253 if (flags & VM_CALL_ARGS_SPLAT) {
5254 ci_argc--;
5255 PUSH_INSN1(writes, location, newarray, INT2FIX(1));
5256 PUSH_INSN(writes, location, concatarray);
5257 }
5258
5259 PUSH_SEND_R(writes, location, idASET, INT2NUM(ci_argc), NULL, INT2FIX(flags), kwargs);
5260 PUSH_INSN(writes, location, pop);
5261
5262 if (state != NULL) {
5263 if (argc != 0) {
5264 PUSH_INSN(writes, location, pop);
5265 }
5266
5267 for (int index = 0; index < argc + 1; index++) {
5268 PUSH_INSN(cleanup, location, pop);
5269 }
5270 }
5271
5272 break;
5273 }
5274 case PM_MULTI_TARGET_NODE: {
5275 // Multi target nodes represent a set of writes to multiple variables.
5276 // The parent expressions are the combined set of the parent expressions
5277 // of its inner target nodes.
5278 //
5279 // for i, j in []; end
5280 //
5281 size_t before_position;
5282 if (state != NULL) {
5283 before_position = state->position;
5284 state->position--;
5285 }
5286
5287 pm_compile_multi_target_node(iseq, node, parents, writes, cleanup, scope_node, state);
5288 if (state != NULL) state->position = before_position;
5289
5290 break;
5291 }
5292 case PM_SPLAT_NODE: {
5293 // Splat nodes capture all values into an array. They can be used
5294 // as targets in assignments or for loops.
5295 //
5296 // for *x in []; end
5297 //
5298 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
5299
5300 if (cast->expression != NULL) {
5301 pm_compile_target_node(iseq, cast->expression, parents, writes, cleanup, scope_node, state);
5302 }
5303
5304 break;
5305 }
5306 default:
5307 rb_bug("Unexpected node type: %s", pm_node_type(PM_NODE_TYPE(node)));
5308 break;
5309 }
5310}
5311
5317static void
5318pm_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)
5319{
5320 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5321 const pm_node_list_t *lefts;
5322 const pm_node_t *rest;
5323 const pm_node_list_t *rights;
5324
5325 switch (PM_NODE_TYPE(node)) {
5326 case PM_MULTI_TARGET_NODE: {
5327 const pm_multi_target_node_t *cast = (const pm_multi_target_node_t *) node;
5328 lefts = &cast->lefts;
5329 rest = cast->rest;
5330 rights = &cast->rights;
5331 break;
5332 }
5333 case PM_MULTI_WRITE_NODE: {
5334 const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node;
5335 lefts = &cast->lefts;
5336 rest = cast->rest;
5337 rights = &cast->rights;
5338 break;
5339 }
5340 default:
5341 rb_bug("Unsupported node %s", pm_node_type(PM_NODE_TYPE(node)));
5342 break;
5343 }
5344
5345 bool has_rest = (rest != NULL) && PM_NODE_TYPE_P(rest, PM_SPLAT_NODE) && ((const pm_splat_node_t *) rest)->expression != NULL;
5346 bool has_posts = rights->size > 0;
5347
5348 // The first instruction in the writes sequence is going to spread the
5349 // top value of the stack onto the number of values that we're going to
5350 // write.
5351 PUSH_INSN2(writes, location, expandarray, INT2FIX(lefts->size), INT2FIX((has_rest || has_posts) ? 1 : 0));
5352
5353 // We need to keep track of some additional state information as we're
5354 // going through the targets because we will need to revisit them once
5355 // we know how many values are being pushed onto the stack.
5356 pm_multi_target_state_t target_state = { 0 };
5357 if (state == NULL) state = &target_state;
5358
5359 size_t base_position = state->position;
5360 size_t splat_position = (has_rest || has_posts) ? 1 : 0;
5361
5362 // Next, we'll iterate through all of the leading targets.
5363 for (size_t index = 0; index < lefts->size; index++) {
5364 const pm_node_t *target = lefts->nodes[index];
5365 state->position = lefts->size - index + splat_position + base_position;
5366 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5367 }
5368
5369 // Next, we'll compile the rest target if there is one.
5370 if (has_rest) {
5371 const pm_node_t *target = ((const pm_splat_node_t *) rest)->expression;
5372 state->position = 1 + rights->size + base_position;
5373
5374 if (has_posts) {
5375 PUSH_INSN2(writes, location, expandarray, INT2FIX(rights->size), INT2FIX(3));
5376 }
5377
5378 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5379 }
5380
5381 // Finally, we'll compile the trailing targets.
5382 if (has_posts) {
5383 if (!has_rest && rest != NULL) {
5384 PUSH_INSN2(writes, location, expandarray, INT2FIX(rights->size), INT2FIX(2));
5385 }
5386
5387 for (size_t index = 0; index < rights->size; index++) {
5388 const pm_node_t *target = rights->nodes[index];
5389 state->position = rights->size - index + base_position;
5390 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5391 }
5392 }
5393}
5394
5400static void
5401pm_compile_for_node_index(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
5402{
5403 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5404
5405 switch (PM_NODE_TYPE(node)) {
5406 case PM_LOCAL_VARIABLE_TARGET_NODE: {
5407 // For local variables, all we have to do is retrieve the value and then
5408 // compile the index node.
5409 PUSH_GETLOCAL(ret, location, 1, 0);
5410 pm_compile_target_node(iseq, node, ret, ret, ret, scope_node, NULL);
5411 break;
5412 }
5413 case PM_CLASS_VARIABLE_TARGET_NODE:
5414 case PM_CONSTANT_TARGET_NODE:
5415 case PM_GLOBAL_VARIABLE_TARGET_NODE:
5416 case PM_INSTANCE_VARIABLE_TARGET_NODE:
5417 case PM_CONSTANT_PATH_TARGET_NODE:
5418 case PM_CALL_TARGET_NODE:
5419 case PM_INDEX_TARGET_NODE: {
5420 // For other targets, we need to potentially compile the parent or
5421 // owning expression of this target, then retrieve the value, expand it,
5422 // and then compile the necessary writes.
5423 DECL_ANCHOR(writes);
5424 DECL_ANCHOR(cleanup);
5425
5426 pm_multi_target_state_t state = { 0 };
5427 state.position = 1;
5428 pm_compile_target_node(iseq, node, ret, writes, cleanup, scope_node, &state);
5429
5430 PUSH_GETLOCAL(ret, location, 1, 0);
5431 PUSH_INSN2(ret, location, expandarray, INT2FIX(1), INT2FIX(0));
5432
5433 PUSH_SEQ(ret, writes);
5434 PUSH_SEQ(ret, cleanup);
5435
5436 pm_multi_target_state_update(&state);
5437 break;
5438 }
5439 case PM_SPLAT_NODE:
5440 case PM_MULTI_TARGET_NODE: {
5441 DECL_ANCHOR(writes);
5442 DECL_ANCHOR(cleanup);
5443
5444 pm_compile_target_node(iseq, node, ret, writes, cleanup, scope_node, NULL);
5445
5446 LABEL *not_single = NEW_LABEL(location.line);
5447 LABEL *not_ary = NEW_LABEL(location.line);
5448
5449 // When there are multiple targets, we'll do a bunch of work to convert
5450 // the value into an array before we expand it. Effectively we're trying
5451 // to accomplish:
5452 //
5453 // (args.length == 1 && Array.try_convert(args[0])) || args
5454 //
5455 PUSH_GETLOCAL(ret, location, 1, 0);
5456 PUSH_INSN(ret, location, dup);
5457 PUSH_CALL(ret, location, idLength, INT2FIX(0));
5458 PUSH_INSN1(ret, location, putobject, INT2FIX(1));
5459 PUSH_CALL(ret, location, idEq, INT2FIX(1));
5460 PUSH_INSNL(ret, location, branchunless, not_single);
5461 PUSH_INSN(ret, location, dup);
5462 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
5463 PUSH_CALL(ret, location, idAREF, INT2FIX(1));
5464 PUSH_INSN1(ret, location, putobject, rb_cArray);
5465 PUSH_INSN(ret, location, swap);
5466 PUSH_CALL(ret, location, rb_intern("try_convert"), INT2FIX(1));
5467 PUSH_INSN(ret, location, dup);
5468 PUSH_INSNL(ret, location, branchunless, not_ary);
5469 PUSH_INSN(ret, location, swap);
5470
5471 PUSH_LABEL(ret, not_ary);
5472 PUSH_INSN(ret, location, pop);
5473
5474 PUSH_LABEL(ret, not_single);
5475
5476 if (PM_NODE_TYPE_P(node, PM_SPLAT_NODE)) {
5477 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
5478 PUSH_INSN2(ret, location, expandarray, INT2FIX(0), INT2FIX(cast->expression == NULL ? 0 : 1));
5479 }
5480
5481 PUSH_SEQ(ret, writes);
5482 PUSH_SEQ(ret, cleanup);
5483 break;
5484 }
5485 default:
5486 rb_bug("Unexpected node type for index in for node: %s", pm_node_type(PM_NODE_TYPE(node)));
5487 break;
5488 }
5489}
5490
5491static void
5492pm_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)
5493{
5494 LABEL *lstart = NEW_LABEL(node_location->line);
5495 LABEL *lend = NEW_LABEL(node_location->line);
5496 LABEL *lcont = NEW_LABEL(node_location->line);
5497
5498 pm_scope_node_t rescue_scope_node;
5499 pm_scope_node_init((const pm_node_t *) cast->rescue_clause, &rescue_scope_node, scope_node);
5500
5501 rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(
5502 &rescue_scope_node,
5503 rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label),
5504 ISEQ_TYPE_RESCUE,
5505 pm_node_line_number_cached((const pm_node_t *) cast->rescue_clause, scope_node)
5506 );
5507
5508 pm_scope_node_destroy(&rescue_scope_node);
5509
5510 lstart->rescued = LABEL_RESCUE_BEG;
5511 lend->rescued = LABEL_RESCUE_END;
5512 PUSH_LABEL(ret, lstart);
5513
5514 bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
5515 ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
5516
5517 if (cast->statements != NULL) {
5518 PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->statements);
5519 }
5520 else {
5521 const pm_node_location_t location = PM_NODE_START_LOCATION(cast->rescue_clause);
5522 PUSH_INSN(ret, location, putnil);
5523 }
5524
5525 ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
5526 PUSH_LABEL(ret, lend);
5527
5528 if (cast->else_clause != NULL) {
5529 if (!popped) PUSH_INSN(ret, *node_location, pop);
5530 PM_COMPILE((const pm_node_t *) cast->else_clause);
5531 }
5532
5533 PUSH_INSN(ret, *node_location, nop);
5534 PUSH_LABEL(ret, lcont);
5535
5536 if (popped) PUSH_INSN(ret, *node_location, pop);
5537 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
5538 PUSH_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
5539}
5540
5541static void
5542pm_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)
5543{
5544 const pm_statements_node_t *statements = cast->ensure_clause->statements;
5545
5546 pm_node_location_t location;
5547 if (statements != NULL) {
5548 location = PM_NODE_START_LOCATION(statements);
5549 }
5550 else {
5551 location = *node_location;
5552 }
5553
5554 LABEL *lstart = NEW_LABEL(location.line);
5555 LABEL *lend = NEW_LABEL(location.line);
5556 LABEL *lcont = NEW_LABEL(location.line);
5557
5558 struct ensure_range er;
5560 struct ensure_range *erange;
5561
5562 DECL_ANCHOR(ensr);
5563 if (statements != NULL) {
5564 pm_compile_node(iseq, (const pm_node_t *) statements, ensr, true, scope_node);
5565 }
5566
5567 LINK_ELEMENT *last = ensr->last;
5568 bool last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave);
5569
5570 er.begin = lstart;
5571 er.end = lend;
5572 er.next = 0;
5573 push_ensure_entry(iseq, &enl, &er, (void *) cast->ensure_clause);
5574
5575 PUSH_LABEL(ret, lstart);
5576 if (cast->rescue_clause != NULL) {
5577 pm_compile_rescue(iseq, cast, node_location, ret, popped | last_leave, scope_node);
5578 }
5579 else if (cast->statements != NULL) {
5580 pm_compile_node(iseq, (const pm_node_t *) cast->statements, ret, popped | last_leave, scope_node);
5581 }
5582 else if (!(popped | last_leave)) {
5583 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
5584 }
5585
5586 PUSH_LABEL(ret, lend);
5587 PUSH_SEQ(ret, ensr);
5588 if (!popped && last_leave) PUSH_INSN(ret, *node_location, putnil);
5589 PUSH_LABEL(ret, lcont);
5590 if (last_leave) PUSH_INSN(ret, *node_location, pop);
5591
5592 pm_scope_node_t next_scope_node;
5593 pm_scope_node_init((const pm_node_t *) cast->ensure_clause, &next_scope_node, scope_node);
5594
5595 rb_iseq_t *child_iseq = NEW_CHILD_ISEQ(
5596 &next_scope_node,
5597 rb_str_concat(rb_str_new2("ensure in "), ISEQ_BODY(iseq)->location.label),
5598 ISEQ_TYPE_ENSURE,
5599 location.line
5600 );
5601
5602 pm_scope_node_destroy(&next_scope_node);
5603
5604 erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
5605 if (lstart->link.next != &lend->link) {
5606 while (erange) {
5607 PUSH_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end, child_iseq, lcont);
5608 erange = erange->next;
5609 }
5610 }
5611 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
5612}
5613
5618static inline bool
5619pm_opt_str_freeze_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
5620{
5621 return (
5622 !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) &&
5623 node->receiver != NULL &&
5624 PM_NODE_TYPE_P(node->receiver, PM_STRING_NODE) &&
5625 node->arguments == NULL &&
5626 node->block == NULL &&
5627 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
5628 );
5629}
5630
5635static void
5636pm_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)
5637{
5638 const pm_node_location_t location = PM_LOCATION_START_LOCATION(name_loc, node_id);
5639
5640 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
5641 ISEQ_BODY(iseq)->ic_size++;
5642 VALUE segments = rb_ary_new_from_args(1, name);
5643 RB_OBJ_SET_SHAREABLE(segments);
5644 PUSH_INSN1(ret, location, opt_getconstant_path, segments);
5645 }
5646 else {
5647 PUSH_INSN(ret, location, putnil);
5648 PUSH_INSN1(ret, location, putobject, Qtrue);
5649 PUSH_INSN1(ret, location, getconstant, name);
5650 }
5651}
5652
5657static VALUE
5658pm_constant_path_parts(const pm_node_t *node, const pm_scope_node_t *scope_node)
5659{
5660 VALUE parts = rb_ary_new();
5661
5662 while (true) {
5663 switch (PM_NODE_TYPE(node)) {
5664 case PM_CONSTANT_READ_NODE: {
5665 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
5666 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5667
5668 rb_ary_unshift(parts, name);
5669 return parts;
5670 }
5671 case PM_CONSTANT_PATH_NODE: {
5672 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
5673 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5674
5675 rb_ary_unshift(parts, name);
5676 if (cast->parent == NULL) {
5677 rb_ary_unshift(parts, ID2SYM(idNULL));
5678 return parts;
5679 }
5680
5681 node = cast->parent;
5682 break;
5683 }
5684 default:
5685 return Qnil;
5686 }
5687 }
5688}
5689
5695static void
5696pm_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)
5697{
5698 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5699
5700 switch (PM_NODE_TYPE(node)) {
5701 case PM_CONSTANT_READ_NODE: {
5702 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
5703 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5704
5705 PUSH_INSN1(body, location, putobject, Qtrue);
5706 PUSH_INSN1(body, location, getconstant, name);
5707 break;
5708 }
5709 case PM_CONSTANT_PATH_NODE: {
5710 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
5711 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5712
5713 if (cast->parent == NULL) {
5714 PUSH_INSN(body, location, pop);
5715 PUSH_INSN1(body, location, putobject, rb_cObject);
5716 PUSH_INSN1(body, location, putobject, Qtrue);
5717 PUSH_INSN1(body, location, getconstant, name);
5718 }
5719 else {
5720 pm_compile_constant_path(iseq, cast->parent, prefix, body, false, scope_node);
5721 PUSH_INSN1(body, location, putobject, Qfalse);
5722 PUSH_INSN1(body, location, getconstant, name);
5723 }
5724 break;
5725 }
5726 default:
5727 PM_COMPILE_INTO_ANCHOR(prefix, node);
5728 break;
5729 }
5730}
5731
5735static VALUE
5736pm_compile_shareable_constant_literal(rb_iseq_t *iseq, const pm_node_t *node, pm_scope_node_t *scope_node)
5737{
5738 switch (PM_NODE_TYPE(node)) {
5739 case PM_TRUE_NODE:
5740 case PM_FALSE_NODE:
5741 case PM_NIL_NODE:
5742 case PM_SYMBOL_NODE:
5743 case PM_REGULAR_EXPRESSION_NODE:
5744 case PM_SOURCE_LINE_NODE:
5745 case PM_INTEGER_NODE:
5746 case PM_FLOAT_NODE:
5747 case PM_RATIONAL_NODE:
5748 case PM_IMAGINARY_NODE:
5749 case PM_SOURCE_ENCODING_NODE:
5750 return pm_static_literal_value(iseq, node, scope_node);
5751 case PM_STRING_NODE:
5752 return parse_static_literal_string(iseq, scope_node, node, &((const pm_string_node_t *) node)->unescaped);
5753 case PM_SOURCE_FILE_NODE:
5754 return pm_source_file_value((const pm_source_file_node_t *) node, scope_node);
5755 case PM_ARRAY_NODE: {
5756 const pm_array_node_t *cast = (const pm_array_node_t *) node;
5757 VALUE result = rb_ary_new_capa(cast->elements.size);
5758
5759 for (size_t index = 0; index < cast->elements.size; index++) {
5760 VALUE element = pm_compile_shareable_constant_literal(iseq, cast->elements.nodes[index], scope_node);
5761 if (element == Qundef) return Qundef;
5762
5763 rb_ary_push(result, element);
5764 }
5765
5766 return rb_ractor_make_shareable(result);
5767 }
5768 case PM_HASH_NODE: {
5769 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
5770 VALUE result = rb_hash_alloc_fixed_size(rb_cHash, cast->elements.size);
5771
5772 for (size_t index = 0; index < cast->elements.size; index++) {
5773 const pm_node_t *element = cast->elements.nodes[index];
5774 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) return Qundef;
5775
5776 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
5777
5778 VALUE key = pm_compile_shareable_constant_literal(iseq, assoc->key, scope_node);
5779 if (key == Qundef) return Qundef;
5780
5781 VALUE value = pm_compile_shareable_constant_literal(iseq, assoc->value, scope_node);
5782 if (value == Qundef) return Qundef;
5783
5784 rb_hash_aset(result, key, value);
5785 }
5786
5787 return rb_ractor_make_shareable(result);
5788 }
5789 default:
5790 return Qundef;
5791 }
5792}
5793
5798static void
5799pm_compile_shareable_constant_value(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_flags_t shareability, VALUE path, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, bool top)
5800{
5801 VALUE literal = pm_compile_shareable_constant_literal(iseq, node, scope_node);
5802 if (literal != Qundef) {
5803 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5804 PUSH_INSN1(ret, location, putobject, literal);
5805 return;
5806 }
5807
5808 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5809 switch (PM_NODE_TYPE(node)) {
5810 case PM_ARRAY_NODE: {
5811 const pm_array_node_t *cast = (const pm_array_node_t *) node;
5812
5813 if (top) {
5814 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5815 }
5816
5817 for (size_t index = 0; index < cast->elements.size; index++) {
5818 pm_compile_shareable_constant_value(iseq, cast->elements.nodes[index], shareability, path, ret, scope_node, false);
5819 }
5820
5821 PUSH_INSN1(ret, location, newarray, INT2FIX(cast->elements.size));
5822
5823 if (top) {
5824 ID method_id = (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) ? rb_intern("make_shareable_copy") : rb_intern("make_shareable");
5825 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5826 }
5827
5828 return;
5829 }
5830 case PM_HASH_NODE: {
5831 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
5832
5833 if (top) {
5834 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5835 }
5836
5837 pm_compile_hash_elements(iseq, (const pm_node_t *) cast, &cast->elements, shareability, path, false, ret, scope_node);
5838
5839 if (top) {
5840 ID method_id = (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) ? rb_intern("make_shareable_copy") : rb_intern("make_shareable");
5841 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5842 }
5843
5844 return;
5845 }
5846 default: {
5847 DECL_ANCHOR(value_seq);
5848
5849 pm_compile_node(iseq, node, value_seq, false, scope_node);
5850 if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) {
5851 PUSH_SEND_WITH_FLAG(value_seq, location, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE));
5852 }
5853
5854 if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL) {
5855 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5856 PUSH_SEQ(ret, value_seq);
5857 if (!RB_OBJ_SHAREABLE_P(path)) {
5858 RB_OBJ_SET_SHAREABLE(path);
5859 }
5860 PUSH_INSN1(ret, location, putobject, path);
5861 PUSH_SEND_WITH_FLAG(ret, location, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE));
5862 }
5863 else if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) {
5864 if (top) PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5865 PUSH_SEQ(ret, value_seq);
5866 if (top) PUSH_SEND_WITH_FLAG(ret, location, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5867 }
5868 else if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING) {
5869 if (top) PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5870 PUSH_SEQ(ret, value_seq);
5871 if (top) PUSH_SEND_WITH_FLAG(ret, location, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5872 }
5873
5874 break;
5875 }
5876 }
5877}
5878
5883static void
5884pm_compile_constant_write_node(rb_iseq_t *iseq, const pm_constant_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5885{
5886 const pm_node_location_t location = *node_location;
5887 ID name_id = pm_constant_id_lookup(scope_node, node->name);
5888
5889 if (shareability != 0) {
5890 pm_compile_shareable_constant_value(iseq, node->value, shareability, rb_id2str(name_id), ret, scope_node, true);
5891 }
5892 else {
5893 PM_COMPILE_NOT_POPPED(node->value);
5894 }
5895
5896 if (!popped) PUSH_INSN(ret, location, dup);
5897 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5898
5899 VALUE operand = ID2SYM(name_id);
5900 PUSH_INSN1(ret, location, setconstant, operand);
5901}
5902
5907static void
5908pm_compile_constant_and_write_node(rb_iseq_t *iseq, const pm_constant_and_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5909{
5910 const pm_node_location_t location = *node_location;
5911
5912 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5913 LABEL *end_label = NEW_LABEL(location.line);
5914
5915 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5916 if (!popped) PUSH_INSN(ret, location, dup);
5917
5918 PUSH_INSNL(ret, location, branchunless, end_label);
5919 if (!popped) PUSH_INSN(ret, location, pop);
5920
5921 if (shareability != 0) {
5922 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5923 }
5924 else {
5925 PM_COMPILE_NOT_POPPED(node->value);
5926 }
5927
5928 if (!popped) PUSH_INSN(ret, location, dup);
5929 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5930 PUSH_INSN1(ret, location, setconstant, name);
5931 PUSH_LABEL(ret, end_label);
5932}
5933
5938static void
5939pm_compile_constant_or_write_node(rb_iseq_t *iseq, const pm_constant_or_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5940{
5941 const pm_node_location_t location = *node_location;
5942 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5943
5944 LABEL *set_label = NEW_LABEL(location.line);
5945 LABEL *end_label = NEW_LABEL(location.line);
5946
5947 PUSH_INSN(ret, location, putnil);
5948 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), name, Qtrue);
5949 PUSH_INSNL(ret, location, branchunless, set_label);
5950
5951 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5952 if (!popped) PUSH_INSN(ret, location, dup);
5953
5954 PUSH_INSNL(ret, location, branchif, end_label);
5955 if (!popped) PUSH_INSN(ret, location, pop);
5956 PUSH_LABEL(ret, set_label);
5957
5958 if (shareability != 0) {
5959 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5960 }
5961 else {
5962 PM_COMPILE_NOT_POPPED(node->value);
5963 }
5964
5965 if (!popped) PUSH_INSN(ret, location, dup);
5966 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5967 PUSH_INSN1(ret, location, setconstant, name);
5968 PUSH_LABEL(ret, end_label);
5969}
5970
5975static void
5976pm_compile_constant_operator_write_node(rb_iseq_t *iseq, const pm_constant_operator_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5977{
5978 const pm_node_location_t location = *node_location;
5979
5980 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5981 ID method_id = pm_constant_id_lookup(scope_node, node->binary_operator);
5982
5983 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5984
5985 if (shareability != 0) {
5986 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5987 }
5988 else {
5989 PM_COMPILE_NOT_POPPED(node->value);
5990 }
5991
5992 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5993 if (!popped) PUSH_INSN(ret, location, dup);
5994
5995 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5996 PUSH_INSN1(ret, location, setconstant, name);
5997}
5998
6003static VALUE
6004pm_constant_path_path(const pm_constant_path_node_t *node, const pm_scope_node_t *scope_node)
6005{
6006 VALUE parts = rb_ary_new();
6007 rb_ary_push(parts, rb_id2str(pm_constant_id_lookup(scope_node, node->name)));
6008
6009 const pm_node_t *current = node->parent;
6010 while (current != NULL && PM_NODE_TYPE_P(current, PM_CONSTANT_PATH_NODE)) {
6011 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) current;
6012 rb_ary_unshift(parts, rb_id2str(pm_constant_id_lookup(scope_node, cast->name)));
6013 current = cast->parent;
6014 }
6015
6016 if (current == NULL) {
6017 rb_ary_unshift(parts, rb_id2str(idNULL));
6018 }
6019 else if (PM_NODE_TYPE_P(current, PM_CONSTANT_READ_NODE)) {
6020 rb_ary_unshift(parts, rb_id2str(pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) current)->name)));
6021 }
6022 else {
6023 rb_ary_unshift(parts, rb_str_new_cstr("..."));
6024 }
6025
6026 return rb_ary_join(parts, rb_str_new_cstr("::"));
6027}
6028
6033static void
6034pm_compile_constant_path_write_node(rb_iseq_t *iseq, const pm_constant_path_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
6035{
6036 const pm_node_location_t location = *node_location;
6037 const pm_constant_path_node_t *target = node->target;
6038 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6039
6040 if (target->parent) {
6041 PM_COMPILE_NOT_POPPED((const pm_node_t *) target->parent);
6042 }
6043 else {
6044 PUSH_INSN1(ret, location, putobject, rb_cObject);
6045 }
6046
6047 if (shareability != 0) {
6048 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6049 }
6050 else {
6051 PM_COMPILE_NOT_POPPED(node->value);
6052 }
6053
6054 if (!popped) {
6055 PUSH_INSN(ret, location, swap);
6056 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6057 }
6058
6059 PUSH_INSN(ret, location, swap);
6060 PUSH_INSN1(ret, location, setconstant, name);
6061}
6062
6067static void
6068pm_compile_constant_path_and_write_node(rb_iseq_t *iseq, const pm_constant_path_and_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
6069{
6070 const pm_node_location_t location = *node_location;
6071 const pm_constant_path_node_t *target = node->target;
6072
6073 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6074 LABEL *lfin = NEW_LABEL(location.line);
6075
6076 if (target->parent) {
6077 PM_COMPILE_NOT_POPPED(target->parent);
6078 }
6079 else {
6080 PUSH_INSN1(ret, location, putobject, rb_cObject);
6081 }
6082
6083 PUSH_INSN(ret, location, dup);
6084 PUSH_INSN1(ret, location, putobject, Qtrue);
6085 PUSH_INSN1(ret, location, getconstant, name);
6086
6087 if (!popped) PUSH_INSN(ret, location, dup);
6088 PUSH_INSNL(ret, location, branchunless, lfin);
6089
6090 if (!popped) PUSH_INSN(ret, location, pop);
6091
6092 if (shareability != 0) {
6093 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6094 }
6095 else {
6096 PM_COMPILE_NOT_POPPED(node->value);
6097 }
6098
6099 if (popped) {
6100 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6101 }
6102 else {
6103 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
6104 PUSH_INSN(ret, location, swap);
6105 }
6106
6107 PUSH_INSN1(ret, location, setconstant, name);
6108 PUSH_LABEL(ret, lfin);
6109
6110 if (!popped) PUSH_INSN(ret, location, swap);
6111 PUSH_INSN(ret, location, pop);
6112}
6113
6118static void
6119pm_compile_constant_path_or_write_node(rb_iseq_t *iseq, const pm_constant_path_or_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
6120{
6121 const pm_node_location_t location = *node_location;
6122 const pm_constant_path_node_t *target = node->target;
6123
6124 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6125 LABEL *lassign = NEW_LABEL(location.line);
6126 LABEL *lfin = NEW_LABEL(location.line);
6127
6128 if (target->parent) {
6129 PM_COMPILE_NOT_POPPED(target->parent);
6130 }
6131 else {
6132 PUSH_INSN1(ret, location, putobject, rb_cObject);
6133 }
6134
6135 PUSH_INSN(ret, location, dup);
6136 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), name, Qtrue);
6137 PUSH_INSNL(ret, location, branchunless, lassign);
6138
6139 PUSH_INSN(ret, location, dup);
6140 PUSH_INSN1(ret, location, putobject, Qtrue);
6141 PUSH_INSN1(ret, location, getconstant, name);
6142
6143 if (!popped) PUSH_INSN(ret, location, dup);
6144 PUSH_INSNL(ret, location, branchif, lfin);
6145
6146 if (!popped) PUSH_INSN(ret, location, pop);
6147 PUSH_LABEL(ret, lassign);
6148
6149 if (shareability != 0) {
6150 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6151 }
6152 else {
6153 PM_COMPILE_NOT_POPPED(node->value);
6154 }
6155
6156 if (popped) {
6157 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6158 }
6159 else {
6160 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
6161 PUSH_INSN(ret, location, swap);
6162 }
6163
6164 PUSH_INSN1(ret, location, setconstant, name);
6165 PUSH_LABEL(ret, lfin);
6166
6167 if (!popped) PUSH_INSN(ret, location, swap);
6168 PUSH_INSN(ret, location, pop);
6169}
6170
6175static void
6176pm_compile_constant_path_operator_write_node(rb_iseq_t *iseq, const pm_constant_path_operator_write_node_t *node, const pm_node_flags_t shareability, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
6177{
6178 const pm_node_location_t location = *node_location;
6179 const pm_constant_path_node_t *target = node->target;
6180
6181 ID method_id = pm_constant_id_lookup(scope_node, node->binary_operator);
6182 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6183
6184 if (target->parent) {
6185 PM_COMPILE_NOT_POPPED(target->parent);
6186 }
6187 else {
6188 PUSH_INSN1(ret, location, putobject, rb_cObject);
6189 }
6190
6191 PUSH_INSN(ret, location, dup);
6192 PUSH_INSN1(ret, location, putobject, Qtrue);
6193 PUSH_INSN1(ret, location, getconstant, name);
6194
6195 if (shareability != 0) {
6196 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6197 }
6198 else {
6199 PM_COMPILE_NOT_POPPED(node->value);
6200 }
6201
6202 PUSH_CALL(ret, location, method_id, INT2FIX(1));
6203 PUSH_INSN(ret, location, swap);
6204
6205 if (!popped) {
6206 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6207 PUSH_INSN(ret, location, swap);
6208 }
6209
6210 PUSH_INSN1(ret, location, setconstant, name);
6211}
6212
6219#define PM_CONTAINER_P(node) (PM_NODE_TYPE_P(node, PM_ARRAY_NODE) || PM_NODE_TYPE_P(node, PM_HASH_NODE) || PM_NODE_TYPE_P(node, PM_RANGE_NODE))
6220
6225static inline void
6226pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped)
6227{
6228 const pm_node_location_t location = *node_location;
6229 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
6230
6231 pm_constant_id_list_t *locals = &scope_node->locals;
6232 pm_parameters_node_t *parameters_node = NULL;
6233 pm_node_list_t *keywords_list = NULL;
6234 pm_node_list_t *optionals_list = NULL;
6235 pm_node_list_t *posts_list = NULL;
6236 pm_node_list_t *requireds_list = NULL;
6237 pm_node_list_t *block_locals = NULL;
6238 bool trailing_comma = false;
6239
6240 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE) || PM_NODE_TYPE_P(scope_node->ast_node, PM_MODULE_NODE)) {
6241 PUSH_TRACE(ret, RUBY_EVENT_CLASS);
6242 }
6243
6244 if (scope_node->parameters != NULL) {
6245 switch (PM_NODE_TYPE(scope_node->parameters)) {
6246 case PM_BLOCK_PARAMETERS_NODE: {
6247 pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) scope_node->parameters;
6248 parameters_node = cast->parameters;
6249 block_locals = &cast->locals;
6250
6251 if (parameters_node) {
6252 if (parameters_node->rest && PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE)) {
6253 trailing_comma = true;
6254 }
6255 }
6256 break;
6257 }
6258 case PM_PARAMETERS_NODE: {
6259 parameters_node = (pm_parameters_node_t *) scope_node->parameters;
6260 break;
6261 }
6262 case PM_NUMBERED_PARAMETERS_NODE: {
6263 uint32_t maximum = ((const pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
6264 body->param.lead_num = maximum;
6265 body->param.flags.ambiguous_param0 = maximum == 1;
6266 break;
6267 }
6268 case PM_IT_PARAMETERS_NODE:
6269 body->param.lead_num = 1;
6270 body->param.flags.ambiguous_param0 = true;
6271 break;
6272 default:
6273 rb_bug("Unexpected node type for parameters: %s", pm_node_type(PM_NODE_TYPE(scope_node->parameters)));
6274 }
6275 }
6276
6277 struct rb_iseq_param_keyword *keyword = NULL;
6278
6279 if (parameters_node) {
6280 optionals_list = &parameters_node->optionals;
6281 requireds_list = &parameters_node->requireds;
6282 keywords_list = &parameters_node->keywords;
6283 posts_list = &parameters_node->posts;
6284 }
6285 else if (scope_node->parameters && (PM_NODE_TYPE_P(scope_node->parameters, PM_NUMBERED_PARAMETERS_NODE) || PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE))) {
6286 body->param.opt_num = 0;
6287 }
6288 else {
6289 body->param.lead_num = 0;
6290 body->param.opt_num = 0;
6291 }
6292
6293 //********STEP 1**********
6294 // Goal: calculate the table size for the locals, accounting for
6295 // hidden variables and multi target nodes
6296 size_t locals_size = locals->size;
6297
6298 // Index lookup table buffer size is only the number of the locals.
6299 // We'll initialize it after computing table_size below.
6300 pm_index_lookup_table_t index_lookup_table = PM_INDEX_LOOKUP_TABLE_INIT;
6301
6302 int table_size = (int) locals_size;
6303
6304 // For nodes have a hidden iteration variable. We add that to the local
6305 // table size here.
6306 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) table_size++;
6307
6308 if (keywords_list && keywords_list->size) {
6309 table_size++;
6310 }
6311
6312 if (requireds_list) {
6313 for (size_t i = 0; i < requireds_list->size; i++) {
6314 // For each MultiTargetNode, we're going to have one
6315 // additional anonymous local not represented in the locals table
6316 // We want to account for this in our table size
6317 pm_node_t *required = requireds_list->nodes[i];
6318 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
6319 table_size++;
6320 }
6321 else if (PM_NODE_TYPE_P(required, PM_REQUIRED_PARAMETER_NODE)) {
6322 if (PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6323 table_size++;
6324 }
6325 }
6326 }
6327 }
6328
6329 // If we have the `it` implicit local variable, we need to account for
6330 // it in the local table size.
6331 if (scope_node->parameters != NULL && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
6332 table_size++;
6333 }
6334
6335 // Ensure there is enough room in the local table for any
6336 // parameters that have been repeated
6337 // ex: def underscore_parameters(_, _ = 1, _ = 2); _; end
6338 // ^^^^^^^^^^^^
6339 if (optionals_list && optionals_list->size) {
6340 for (size_t i = 0; i < optionals_list->size; i++) {
6341 pm_node_t * node = optionals_list->nodes[i];
6342 if (PM_NODE_FLAG_P(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6343 table_size++;
6344 }
6345 }
6346 }
6347
6348 // If we have an anonymous "rest" node, we'll need to increase the local
6349 // table size to take it in to account.
6350 // def m(foo, *, bar)
6351 // ^
6352 if (parameters_node) {
6353 if (parameters_node->rest) {
6354 if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
6355 if (!((const pm_rest_parameter_node_t *) parameters_node->rest)->name || PM_NODE_FLAG_P(parameters_node->rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6356 table_size++;
6357 }
6358 }
6359 }
6360
6361 // def foo(_, **_); _; end
6362 // ^^^
6363 if (parameters_node->keyword_rest) {
6364 // def foo(...); end
6365 // ^^^
6366 // When we have a `...` as the keyword_rest, it's a forwarding_parameter_node and
6367 // we need to leave space for 4 locals: *, **, &, ...
6368 if (PM_NODE_TYPE_P(parameters_node->keyword_rest, PM_FORWARDING_PARAMETER_NODE)) {
6369 // Only optimize specifically methods like this: `foo(...)`
6370 if (requireds_list->size == 0 && optionals_list->size == 0 && keywords_list->size == 0) {
6371 ISEQ_BODY(iseq)->param.flags.use_block = TRUE;
6372 ISEQ_BODY(iseq)->param.flags.forwardable = TRUE;
6373 table_size += 1;
6374 }
6375 else {
6376 table_size += 4;
6377 }
6378 }
6379 else {
6380 const pm_keyword_rest_parameter_node_t *kw_rest = (const pm_keyword_rest_parameter_node_t *) parameters_node->keyword_rest;
6381
6382 // If it's anonymous or repeated, then we need to allocate stack space
6383 if (!kw_rest->name || PM_NODE_FLAG_P(kw_rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6384 table_size++;
6385 }
6386 }
6387 }
6388 }
6389
6390 if (posts_list) {
6391 for (size_t i = 0; i < posts_list->size; i++) {
6392 // For each MultiTargetNode, we're going to have one
6393 // additional anonymous local not represented in the locals table
6394 // We want to account for this in our table size
6395 pm_node_t *required = posts_list->nodes[i];
6396 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE) || PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6397 table_size++;
6398 }
6399 }
6400 }
6401
6402 if (keywords_list && keywords_list->size) {
6403 for (size_t i = 0; i < keywords_list->size; i++) {
6404 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6405 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6406 table_size++;
6407 }
6408 }
6409 }
6410
6411 if (parameters_node && parameters_node->block && PM_NODE_TYPE_P(parameters_node->block, PM_BLOCK_PARAMETER_NODE)) {
6412 const pm_block_parameter_node_t *block_node = (const pm_block_parameter_node_t *) parameters_node->block;
6413
6414 if (PM_NODE_FLAG_P(block_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER) || !block_node->name) {
6415 table_size++;
6416 }
6417 }
6418
6419 // We can create local_table_for_iseq with the correct size
6420 VALUE idtmp = 0;
6421 rb_ast_id_table_t *local_table_for_iseq = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
6422 local_table_for_iseq->size = table_size;
6423
6424 // Init the direct-indexed lookup table. The capacity is based on the
6425 // parser's constant pool size (for regular locals) plus special slots.
6426 pm_index_lookup_table_init(&index_lookup_table, (int) pm_parser_constants_size(scope_node->parser), iseq);
6427
6428 //********END OF STEP 1**********
6429
6430 //********STEP 2**********
6431 // Goal: populate iv index table as well as local table, keeping the
6432 // layout of the local table consistent with the layout of the
6433 // stack when calling the method
6434 //
6435 // Do a first pass on all of the parameters, setting their values in
6436 // the local_table_for_iseq, _except_ for Multis who get a hidden
6437 // variable in this step, and will get their names inserted in step 3
6438
6439 // local_index is a cursor that keeps track of the current
6440 // index into local_table_for_iseq. The local table is actually a list,
6441 // and the order of that list must match the order of the items pushed
6442 // on the stack. We need to take in to account things pushed on the
6443 // stack that _might not have a name_ (for example array destructuring).
6444 // This index helps us know which item we're dealing with and also give
6445 // those anonymous items temporary names (as below)
6446 int local_index = 0;
6447
6448 // Here we figure out local table indices and insert them in to the
6449 // index lookup table and local tables.
6450 //
6451 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6452 // ^^^^^^^^^^^^^
6453 if (requireds_list && requireds_list->size) {
6454 for (size_t i = 0; i < requireds_list->size; i++, local_index++) {
6455 ID local;
6456
6457 // For each MultiTargetNode, we're going to have one additional
6458 // anonymous local not represented in the locals table. We want
6459 // to account for this in our table size.
6460 pm_node_t *required = requireds_list->nodes[i];
6461
6462 switch (PM_NODE_TYPE(required)) {
6463 case PM_MULTI_TARGET_NODE: {
6464 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6465 // ^^^^^^^^^^
6466 local = rb_make_temporary_id(local_index);
6467 local_table_for_iseq->ids[local_index] = local;
6468 break;
6469 }
6470 case PM_REQUIRED_PARAMETER_NODE: {
6471 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6472 // ^
6473 const pm_required_parameter_node_t *param = (const pm_required_parameter_node_t *) required;
6474
6475 if (PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6476 ID local = pm_constant_id_lookup(scope_node, param->name);
6477 local_table_for_iseq->ids[local_index] = local;
6478 }
6479 else {
6480 pm_insert_local_index(param->name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6481 }
6482
6483 break;
6484 }
6485 default:
6486 rb_bug("Unsupported node in requireds in parameters %s", pm_node_type(PM_NODE_TYPE(required)));
6487 }
6488 }
6489
6490 body->param.lead_num = (int) requireds_list->size;
6491 body->param.flags.has_lead = true;
6492 }
6493
6494 if (scope_node->parameters != NULL && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
6495 local_table_for_iseq->ids[local_index++] = idItImplicit;
6496 }
6497
6498 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6499 // ^^^^^
6500 if (optionals_list && optionals_list->size) {
6501 body->param.opt_num = (int) optionals_list->size;
6502 body->param.flags.has_opt = true;
6503
6504 for (size_t i = 0; i < optionals_list->size; i++, local_index++) {
6505 pm_node_t * node = optionals_list->nodes[i];
6506 pm_constant_id_t name = ((const pm_optional_parameter_node_t *) node)->name;
6507
6508 if (PM_NODE_FLAG_P(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6509 ID local = pm_constant_id_lookup(scope_node, name);
6510 local_table_for_iseq->ids[local_index] = local;
6511 }
6512 else {
6513 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6514 }
6515 }
6516 }
6517
6518 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6519 // ^^
6520 if (parameters_node && parameters_node->rest) {
6521 body->param.rest_start = local_index;
6522
6523 // If there's a trailing comma, we'll have an implicit rest node,
6524 // and we don't want it to impact the rest variables on param
6525 if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
6526 body->param.flags.has_rest = true;
6527 RUBY_ASSERT(body->param.rest_start != -1);
6528
6529 pm_constant_id_t name = ((const pm_rest_parameter_node_t *) parameters_node->rest)->name;
6530
6531 if (name) {
6532 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6533 // ^^
6534 if (PM_NODE_FLAG_P(parameters_node->rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6535 ID local = pm_constant_id_lookup(scope_node, name);
6536 local_table_for_iseq->ids[local_index] = local;
6537 }
6538 else {
6539 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6540 }
6541 }
6542 else {
6543 // def foo(a, (b, *c, d), e = 1, *, g, (h, *i, j), k:, l: 1, **m, &n)
6544 // ^
6545 body->param.flags.anon_rest = true;
6546 pm_insert_local_special(PM_CONSTANT_MULT, idMULT, local_index, &index_lookup_table, local_table_for_iseq);
6547 }
6548
6549 local_index++;
6550 }
6551 }
6552
6553 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6554 // ^^^^^^^^^^^^^
6555 if (posts_list && posts_list->size) {
6556 body->param.post_num = (int) posts_list->size;
6557 body->param.post_start = local_index;
6558 body->param.flags.has_post = true;
6559
6560 for (size_t i = 0; i < posts_list->size; i++, local_index++) {
6561 ID local;
6562
6563 // For each MultiTargetNode, we're going to have one additional
6564 // anonymous local not represented in the locals table. We want
6565 // to account for this in our table size.
6566 const pm_node_t *post_node = posts_list->nodes[i];
6567
6568 switch (PM_NODE_TYPE(post_node)) {
6569 case PM_MULTI_TARGET_NODE: {
6570 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6571 // ^^^^^^^^^^
6572 local = rb_make_temporary_id(local_index);
6573 local_table_for_iseq->ids[local_index] = local;
6574 break;
6575 }
6576 case PM_REQUIRED_PARAMETER_NODE: {
6577 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6578 // ^
6579 const pm_required_parameter_node_t *param = (const pm_required_parameter_node_t *) post_node;
6580
6581 if (PM_NODE_FLAG_P(param, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6582 ID local = pm_constant_id_lookup(scope_node, param->name);
6583 local_table_for_iseq->ids[local_index] = local;
6584 }
6585 else {
6586 pm_insert_local_index(param->name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6587 }
6588 break;
6589 }
6590 default:
6591 rb_bug("Unsupported node in posts in parameters %s", pm_node_type(PM_NODE_TYPE(post_node)));
6592 }
6593 }
6594 }
6595
6596 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6597 // ^^^^^^^^
6598 // Keywords create an internal variable on the parse tree
6599 if (keywords_list && keywords_list->size) {
6600 keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6601 keyword->num = (int) keywords_list->size;
6602
6603 const VALUE default_values = rb_ary_hidden_new(1);
6604 const VALUE complex_mark = rb_str_tmp_new(0);
6605
6606 for (size_t i = 0; i < keywords_list->size; i++) {
6607 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6608 pm_constant_id_t name;
6609
6610 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6611 // ^^
6612 if (PM_NODE_TYPE_P(keyword_parameter_node, PM_REQUIRED_KEYWORD_PARAMETER_NODE)) {
6613 name = ((const pm_required_keyword_parameter_node_t *) keyword_parameter_node)->name;
6614 keyword->required_num++;
6615 ID local = pm_constant_id_lookup(scope_node, name);
6616
6617 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6618 local_table_for_iseq->ids[local_index] = local;
6619 }
6620 else {
6621 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6622 }
6623 local_index++;
6624 }
6625 }
6626
6627 for (size_t i = 0; i < keywords_list->size; i++) {
6628 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6629 pm_constant_id_t name;
6630
6631 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6632 // ^^^^
6633 if (PM_NODE_TYPE_P(keyword_parameter_node, PM_OPTIONAL_KEYWORD_PARAMETER_NODE)) {
6634 const pm_optional_keyword_parameter_node_t *cast = ((const pm_optional_keyword_parameter_node_t *) keyword_parameter_node);
6635
6636 pm_node_t *value = cast->value;
6637 name = cast->name;
6638
6639 if (PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL) && !PM_CONTAINER_P(value)) {
6640 rb_ary_push(default_values, pm_static_literal_value(iseq, value, scope_node));
6641 }
6642 else {
6643 rb_ary_push(default_values, complex_mark);
6644 }
6645
6646 ID local = pm_constant_id_lookup(scope_node, name);
6647 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6648 local_table_for_iseq->ids[local_index] = local;
6649 }
6650 else {
6651 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6652 }
6653 local_index++;
6654 }
6655
6656 }
6657
6658 if (RARRAY_LEN(default_values)) {
6659 VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
6660
6661 for (int i = 0; i < RARRAY_LEN(default_values); i++) {
6662 VALUE dv = RARRAY_AREF(default_values, i);
6663 if (dv == complex_mark) dv = Qundef;
6664 RB_OBJ_WRITE(iseq, &dvs[i], dv);
6665 }
6666
6667 keyword->default_values = dvs;
6668 }
6669
6670 // Hidden local for keyword arguments
6671 keyword->bits_start = local_index;
6672 ID local = rb_make_temporary_id(local_index);
6673 local_table_for_iseq->ids[local_index] = local;
6674 local_index++;
6675
6676 body->param.keyword = keyword;
6677 body->param.flags.has_kw = true;
6678 }
6679
6680 if (body->type == ISEQ_TYPE_BLOCK && local_index == 1 && requireds_list && requireds_list->size == 1 && !trailing_comma) {
6681 body->param.flags.ambiguous_param0 = true;
6682 }
6683
6684 if (parameters_node) {
6685 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6686 // ^^^
6687 if (parameters_node->keyword_rest) {
6688 switch (PM_NODE_TYPE(parameters_node->keyword_rest)) {
6689 case PM_NO_KEYWORDS_PARAMETER_NODE: {
6690 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **nil, &n)
6691 // ^^^^^
6692 body->param.flags.accepts_no_kwarg = true;
6693 break;
6694 }
6695 case PM_KEYWORD_REST_PARAMETER_NODE: {
6696 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6697 // ^^^
6698 const pm_keyword_rest_parameter_node_t *kw_rest_node = (const pm_keyword_rest_parameter_node_t *) parameters_node->keyword_rest;
6699 if (!body->param.flags.has_kw) {
6700 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6701 }
6702
6703 keyword->rest_start = local_index;
6704 body->param.flags.has_kwrest = true;
6705
6706 pm_constant_id_t constant_id = kw_rest_node->name;
6707 if (constant_id) {
6708 if (PM_NODE_FLAG_P(kw_rest_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6709 ID local = pm_constant_id_lookup(scope_node, constant_id);
6710 local_table_for_iseq->ids[local_index] = local;
6711 }
6712 else {
6713 pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6714 }
6715 }
6716 else {
6717 body->param.flags.anon_kwrest = true;
6718 pm_insert_local_special(PM_CONSTANT_POW, idPow, local_index, &index_lookup_table, local_table_for_iseq);
6719 }
6720
6721 local_index++;
6722 break;
6723 }
6724 case PM_FORWARDING_PARAMETER_NODE: {
6725 // def foo(...)
6726 // ^^^
6727 if (!ISEQ_BODY(iseq)->param.flags.forwardable) {
6728 // Add the anonymous *
6729 body->param.rest_start = local_index;
6730 body->param.flags.has_rest = true;
6731 body->param.flags.anon_rest = true;
6732 pm_insert_local_special(PM_CONSTANT_MULT, idMULT, local_index++, &index_lookup_table, local_table_for_iseq);
6733
6734 // Add the anonymous **
6735 RUBY_ASSERT(!body->param.flags.has_kw);
6736 body->param.flags.has_kw = false;
6737 body->param.flags.has_kwrest = true;
6738 body->param.flags.anon_kwrest = true;
6739 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6740 keyword->rest_start = local_index;
6741 pm_insert_local_special(PM_CONSTANT_POW, idPow, local_index++, &index_lookup_table, local_table_for_iseq);
6742
6743 // Add the anonymous &
6744 body->param.block_start = local_index;
6745 body->param.flags.has_block = true;
6746 pm_insert_local_special(PM_CONSTANT_AND, idAnd, local_index++, &index_lookup_table, local_table_for_iseq);
6747 }
6748
6749 // Add the ...
6750 pm_insert_local_special(PM_CONSTANT_DOT3, idDot3, local_index++, &index_lookup_table, local_table_for_iseq);
6751 break;
6752 }
6753 default:
6754 rb_bug("node type %s not expected as keyword_rest", pm_node_type(PM_NODE_TYPE(parameters_node->keyword_rest)));
6755 }
6756 }
6757
6758 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6759 // ^^
6760 if (parameters_node->block) {
6761 switch (PM_NODE_TYPE(parameters_node->block)) {
6762 case PM_BLOCK_PARAMETER_NODE: {
6763 body->param.block_start = local_index;
6764 body->param.flags.has_block = true;
6765
6766 iseq_set_use_block(iseq);
6767
6768 pm_constant_id_t name = ((const pm_block_parameter_node_t *) parameters_node->block)->name;
6769
6770 if (name) {
6771 if (PM_NODE_FLAG_P(parameters_node->block, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6772 ID local = pm_constant_id_lookup(scope_node, name);
6773 local_table_for_iseq->ids[local_index] = local;
6774 }
6775 else {
6776 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6777 }
6778 }
6779 else {
6780 pm_insert_local_special(PM_CONSTANT_AND, idAnd, local_index, &index_lookup_table, local_table_for_iseq);
6781 }
6782
6783 local_index++;
6784 break;
6785 }
6786 case PM_NO_BLOCK_PARAMETER_NODE: {
6787 body->param.flags.accepts_no_block = true;
6788 break;
6789 }
6790 default:
6791 rb_bug("node type %s not expected as block parameter", pm_node_type(PM_NODE_TYPE(parameters_node->block)));
6792 }
6793 }
6794 }
6795
6796 //********END OF STEP 2**********
6797 // The local table is now consistent with expected
6798 // stack layout
6799
6800 // If there's only one required element in the parameters
6801 // CRuby needs to recognize it as an ambiguous parameter
6802
6803 //********STEP 3**********
6804 // Goal: fill in the names of the parameters in MultiTargetNodes
6805 //
6806 // Go through requireds again to set the multis
6807
6808 if (requireds_list && requireds_list->size) {
6809 for (size_t i = 0; i < requireds_list->size; i++) {
6810 // For each MultiTargetNode, we're going to have one
6811 // additional anonymous local not represented in the locals table
6812 // We want to account for this in our table size
6813 const pm_node_t *required = requireds_list->nodes[i];
6814
6815 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
6816 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);
6817 }
6818 }
6819 }
6820
6821 // Go through posts again to set the multis
6822 if (posts_list && posts_list->size) {
6823 for (size_t i = 0; i < posts_list->size; i++) {
6824 // For each MultiTargetNode, we're going to have one
6825 // additional anonymous local not represented in the locals table
6826 // We want to account for this in our table size
6827 const pm_node_t *post = posts_list->nodes[i];
6828
6829 if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
6830 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);
6831 }
6832 }
6833 }
6834
6835 // Set any anonymous locals for the for node
6836 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
6837 if (PM_NODE_TYPE_P(((const pm_for_node_t *) scope_node->ast_node)->index, PM_LOCAL_VARIABLE_TARGET_NODE)) {
6838 body->param.lead_num++;
6839 }
6840 else {
6841 body->param.rest_start = local_index;
6842 body->param.flags.has_rest = true;
6843 }
6844
6845 ID local = rb_make_temporary_id(local_index);
6846 local_table_for_iseq->ids[local_index] = local;
6847 local_index++;
6848 }
6849
6850 // Fill in any NumberedParameters, if they exist
6851 if (scope_node->parameters && PM_NODE_TYPE_P(scope_node->parameters, PM_NUMBERED_PARAMETERS_NODE)) {
6852 int maximum = ((const pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
6853 RUBY_ASSERT(0 < maximum && maximum <= 9);
6854 for (int i = 0; i < maximum; i++, local_index++) {
6855 const uint8_t param_name[] = { '_', '1' + i };
6856 pm_constant_id_t constant_id = pm_parser_constant_find(scope_node->parser, param_name, 2);
6857 RUBY_ASSERT(constant_id && "parser should fill in any gaps in numbered parameters");
6858 pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6859 }
6860 body->param.lead_num = maximum;
6861 body->param.flags.has_lead = true;
6862 }
6863
6864 // Fill in the anonymous `it` parameter, if it exists
6865 if (scope_node->parameters && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
6866 body->param.lead_num = 1;
6867 body->param.flags.has_lead = true;
6868 }
6869
6870 //********END OF STEP 3**********
6871
6872 //********STEP 4**********
6873 // Goal: fill in the method body locals
6874 // To be explicit, these are the non-parameter locals
6875 // We fill in the block_locals, if they exist
6876 // lambda { |x; y| y }
6877 // ^
6878 if (block_locals && block_locals->size) {
6879 for (size_t i = 0; i < block_locals->size; i++, local_index++) {
6880 pm_constant_id_t constant_id = ((const pm_block_local_variable_node_t *) block_locals->nodes[i])->name;
6881 pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6882 }
6883 }
6884
6885 // Fill in any locals we missed
6886 if (scope_node->locals.size) {
6887 for (size_t i = 0; i < scope_node->locals.size; i++) {
6888 pm_constant_id_t constant_id = locals->ids[i];
6889 if (constant_id) {
6890 int existing;
6891 if (!pm_index_lookup_table_lookup(&index_lookup_table, constant_id, &existing)) {
6892 ID local = pm_constant_id_lookup(scope_node, constant_id);
6893 local_table_for_iseq->ids[local_index] = local;
6894 pm_index_lookup_table_insert(&index_lookup_table, constant_id, local_index);
6895 local_index++;
6896 }
6897 }
6898 }
6899 }
6900
6901 //********END OF STEP 4**********
6902
6903 // We set the index_lookup_table on the scope node so we can
6904 // refer to the parameters correctly.
6905 scope_node->index_lookup_table = index_lookup_table;
6906 iseq_calc_param_size(iseq);
6907
6908 if (ISEQ_BODY(iseq)->param.flags.forwardable) {
6909 // We're treating `...` as a parameter so that frame
6910 // pushing won't clobber it.
6911 ISEQ_BODY(iseq)->param.size += 1;
6912 }
6913
6914 // FIXME: args?
6915 iseq_set_local_table(iseq, local_table_for_iseq, 0);
6916 iseq_set_parameters_lvar_state(iseq);
6917
6918 scope_node->local_table_for_iseq_size = local_table_for_iseq->size;
6919
6920 if (keyword != NULL) {
6921 size_t keyword_start_index = keyword->bits_start - keyword->num;
6922 keyword->table = (ID *)&ISEQ_BODY(iseq)->local_table[keyword_start_index];
6923 }
6924
6925 //********STEP 5************
6926 // Goal: compile anything that needed to be compiled
6927 if (optionals_list && optionals_list->size) {
6928 LABEL **opt_table = (LABEL **) ALLOC_N(VALUE, optionals_list->size + 1);
6929 LABEL *label;
6930
6931 // TODO: Should we make an api for NEW_LABEL where you can pass
6932 // a pointer to the label it should fill out? We already
6933 // have a list of labels allocated above so it seems wasteful
6934 // to do the copies.
6935 for (size_t i = 0; i < optionals_list->size; i++) {
6936 label = NEW_LABEL(location.line);
6937 opt_table[i] = label;
6938 PUSH_LABEL(ret, label);
6939 pm_node_t *optional_node = optionals_list->nodes[i];
6940 PM_COMPILE_NOT_POPPED(optional_node);
6941 }
6942
6943 // Set the last label
6944 label = NEW_LABEL(location.line);
6945 opt_table[optionals_list->size] = label;
6946 PUSH_LABEL(ret, label);
6947
6948 body->param.opt_table = (const VALUE *) opt_table;
6949 }
6950
6951 if (keywords_list && keywords_list->size) {
6952 size_t optional_index = 0;
6953 for (size_t i = 0; i < keywords_list->size; i++) {
6954 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6955 pm_constant_id_t name;
6956
6957 switch (PM_NODE_TYPE(keyword_parameter_node)) {
6958 case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: {
6959 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6960 // ^^^^
6961 const pm_optional_keyword_parameter_node_t *cast = ((const pm_optional_keyword_parameter_node_t *) keyword_parameter_node);
6962
6963 pm_node_t *value = cast->value;
6964 name = cast->name;
6965
6966 if (!PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL) || PM_CONTAINER_P(value)) {
6967 LABEL *end_label = NEW_LABEL(location.line);
6968
6969 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, name, 0);
6970 int kw_bits_idx = table_size - body->param.keyword->bits_start;
6971 PUSH_INSN2(ret, location, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(optional_index));
6972 PUSH_INSNL(ret, location, branchif, end_label);
6973 PM_COMPILE(value);
6974 PUSH_SETLOCAL(ret, location, index.index, index.level);
6975 PUSH_LABEL(ret, end_label);
6976 }
6977 optional_index++;
6978 break;
6979 }
6980 case PM_REQUIRED_KEYWORD_PARAMETER_NODE:
6981 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6982 // ^^
6983 break;
6984 default:
6985 rb_bug("Unexpected keyword parameter node type %s", pm_node_type(PM_NODE_TYPE(keyword_parameter_node)));
6986 }
6987 }
6988 }
6989
6990 if (requireds_list && requireds_list->size) {
6991 for (size_t i = 0; i < requireds_list->size; i++) {
6992 // For each MultiTargetNode, we're going to have one additional
6993 // anonymous local not represented in the locals table. We want
6994 // to account for this in our table size.
6995 const pm_node_t *required = requireds_list->nodes[i];
6996
6997 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
6998 PUSH_GETLOCAL(ret, location, table_size - (int)i, 0);
6999 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) required, ret, scope_node);
7000 }
7001 }
7002 }
7003
7004 if (posts_list && posts_list->size) {
7005 for (size_t i = 0; i < posts_list->size; i++) {
7006 // For each MultiTargetNode, we're going to have one additional
7007 // anonymous local not represented in the locals table. We want
7008 // to account for this in our table size.
7009 const pm_node_t *post = posts_list->nodes[i];
7010
7011 if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
7012 PUSH_GETLOCAL(ret, location, table_size - body->param.post_start - (int) i, 0);
7013 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) post, ret, scope_node);
7014 }
7015 }
7016 }
7017
7018 switch (body->type) {
7019 case ISEQ_TYPE_PLAIN: {
7020 RUBY_ASSERT(PM_NODE_TYPE_P(scope_node->ast_node, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE));
7021
7023 pm_compile_regexp_dynamic(iseq, (const pm_node_t *) cast, &cast->parts, &location, ret, popped, scope_node);
7024
7025 break;
7026 }
7027 case ISEQ_TYPE_BLOCK: {
7028 LABEL *start = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(0);
7029 LABEL *end = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(0);
7030 const pm_node_location_t block_location = { .line = body->location.first_lineno, .node_id = scope_node->ast_node->node_id };
7031
7032 start->rescued = LABEL_RESCUE_BEG;
7033 end->rescued = LABEL_RESCUE_END;
7034
7035 // For nodes automatically assign the iteration variable to whatever
7036 // index variable. We need to handle that write here because it has
7037 // to happen in the context of the block. Note that this happens
7038 // before the B_CALL tracepoint event.
7039 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
7040 pm_compile_for_node_index(iseq, ((const pm_for_node_t *) scope_node->ast_node)->index, ret, scope_node);
7041 }
7042
7043 PUSH_TRACE(ret, RUBY_EVENT_B_CALL);
7044 PUSH_INSN(ret, block_location, nop);
7045 PUSH_LABEL(ret, start);
7046
7047 if (scope_node->body != NULL) {
7048 switch (PM_NODE_TYPE(scope_node->ast_node)) {
7049 case PM_POST_EXECUTION_NODE: {
7050 const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) scope_node->ast_node;
7051 PUSH_INSN1(ret, block_location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7052
7053 // We create another ScopeNode from the statements within the PostExecutionNode
7054 pm_scope_node_t next_scope_node;
7055 pm_scope_node_init((const pm_node_t *) cast->statements, &next_scope_node, scope_node);
7056
7057 const rb_iseq_t *block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(body->parent_iseq), ISEQ_TYPE_BLOCK, location.line);
7058 pm_scope_node_destroy(&next_scope_node);
7059
7060 PUSH_CALL_WITH_BLOCK(ret, block_location, id_core_set_postexe, INT2FIX(0), block);
7061 break;
7062 }
7063 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
7065 pm_compile_regexp_dynamic(iseq, (const pm_node_t *) cast, &cast->parts, &location, ret, popped, scope_node);
7066 break;
7067 }
7068 default:
7069 pm_compile_node(iseq, scope_node->body, ret, popped, scope_node);
7070 break;
7071 }
7072 }
7073 else {
7074 PUSH_INSN(ret, block_location, putnil);
7075 }
7076
7077 PUSH_LABEL(ret, end);
7078 PUSH_TRACE(ret, RUBY_EVENT_B_RETURN);
7079 ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
7080
7081 /* wide range catch handler must put at last */
7082 PUSH_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, NULL, start);
7083 PUSH_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, NULL, end);
7084 break;
7085 }
7086 case ISEQ_TYPE_ENSURE: {
7087 const pm_node_location_t statements_location = (scope_node->body != NULL ? PM_NODE_START_LOCATION(scope_node->body) : location);
7088 iseq_set_exception_local_table(iseq);
7089
7090 if (scope_node->body != NULL) {
7091 PM_COMPILE_POPPED((const pm_node_t *) scope_node->body);
7092 }
7093
7094 PUSH_GETLOCAL(ret, statements_location, 1, 0);
7095 PUSH_INSN1(ret, statements_location, throw, INT2FIX(0));
7096 return;
7097 }
7098 case ISEQ_TYPE_METHOD: {
7099 ISEQ_COMPILE_DATA(iseq)->root_node = (const void *) scope_node->body;
7100 PUSH_TRACE(ret, RUBY_EVENT_CALL);
7101
7102 if (scope_node->body) {
7103 PM_COMPILE((const pm_node_t *) scope_node->body);
7104 }
7105 else {
7106 PUSH_INSN(ret, location, putnil);
7107 }
7108
7109 ISEQ_COMPILE_DATA(iseq)->root_node = (const void *) scope_node->body;
7110 PUSH_TRACE(ret, RUBY_EVENT_RETURN);
7111
7112 ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
7113 break;
7114 }
7115 case ISEQ_TYPE_RESCUE: {
7116 iseq_set_exception_local_table(iseq);
7117 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_RESCUE_MODIFIER_NODE)) {
7118 LABEL *lab = NEW_LABEL(location.line);
7119 LABEL *rescue_end = NEW_LABEL(location.line);
7120 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
7121 PUSH_INSN1(ret, location, putobject, rb_eStandardError);
7122 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
7123 PUSH_INSNL(ret, location, branchif, lab);
7124 PUSH_INSNL(ret, location, jump, rescue_end);
7125 PUSH_LABEL(ret, lab);
7126 PUSH_TRACE(ret, RUBY_EVENT_RESCUE);
7127 PM_COMPILE((const pm_node_t *) scope_node->body);
7128 PUSH_INSN(ret, location, leave);
7129 PUSH_LABEL(ret, rescue_end);
7130 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
7131 }
7132 else {
7133 PM_COMPILE((const pm_node_t *) scope_node->ast_node);
7134 }
7135 PUSH_INSN1(ret, location, throw, INT2FIX(0));
7136
7137 return;
7138 }
7139 default:
7140 if (scope_node->body) {
7141 PM_COMPILE((const pm_node_t *) scope_node->body);
7142 }
7143 else {
7144 PUSH_INSN(ret, location, putnil);
7145 }
7146 break;
7147 }
7148
7149 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE) || PM_NODE_TYPE_P(scope_node->ast_node, PM_MODULE_NODE)) {
7150 const pm_node_location_t end_location = PM_NODE_END_LOCATION(scope_node->ast_node);
7151 PUSH_TRACE(ret, RUBY_EVENT_END);
7152 ISEQ_COMPILE_DATA(iseq)->last_line = end_location.line;
7153 }
7154
7155 if (!PM_NODE_TYPE_P(scope_node->ast_node, PM_ENSURE_NODE)) {
7156 const pm_node_location_t location = { .line = ISEQ_COMPILE_DATA(iseq)->last_line, .node_id = scope_node->ast_node->node_id };
7157 PUSH_INSN(ret, location, leave);
7158 }
7159}
7160
7161static inline void
7162pm_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)
7163{
7164 // alias $foo $bar
7165 // ^^^^^^^^^^^^^^^
7166 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7167
7168 {
7169 const pm_location_t *name_loc = &node->new_name->location;
7170 VALUE operand = ID2SYM(rb_intern3((const char *) (pm_parser_start(scope_node->parser) + name_loc->start), name_loc->length, scope_node->encoding));
7171 PUSH_INSN1(ret, *location, putobject, operand);
7172 }
7173
7174 {
7175 const pm_location_t *name_loc = &node->old_name->location;
7176 VALUE operand = ID2SYM(rb_intern3((const char *) (pm_parser_start(scope_node->parser) + name_loc->start), name_loc->length, scope_node->encoding));
7177 PUSH_INSN1(ret, *location, putobject, operand);
7178 }
7179
7180 PUSH_SEND(ret, *location, id_core_set_variable_alias, INT2FIX(2));
7181 if (popped) PUSH_INSN(ret, *location, pop);
7182}
7183
7184static inline void
7185pm_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)
7186{
7187 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7188 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
7189 PM_COMPILE_NOT_POPPED(node->new_name);
7190 PM_COMPILE_NOT_POPPED(node->old_name);
7191
7192 PUSH_SEND(ret, *location, id_core_set_method_alias, INT2FIX(3));
7193 if (popped) PUSH_INSN(ret, *location, pop);
7194}
7195
7196static inline void
7197pm_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)
7198{
7199 LABEL *end_label = NEW_LABEL(location->line);
7200
7201 PM_COMPILE_NOT_POPPED(node->left);
7202 if (!popped) PUSH_INSN(ret, *location, dup);
7203 PUSH_INSNL(ret, *location, branchunless, end_label);
7204
7205 if (!popped) PUSH_INSN(ret, *location, pop);
7206 PM_COMPILE(node->right);
7207 PUSH_LABEL(ret, end_label);
7208}
7209
7210static inline void
7211pm_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)
7212{
7213 // If every node in the array is static, then we can compile the entire
7214 // array now instead of later.
7215 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
7216 // We're only going to compile this node if it's not popped. If it
7217 // is popped, then we know we don't need to do anything since it's
7218 // statically known.
7219 if (!popped) {
7220 if (elements->size) {
7221 VALUE value = pm_static_literal_value(iseq, node, scope_node);
7222 RB_OBJ_SET_FROZEN_SHAREABLE(value);
7223 PUSH_INSN1(ret, *location, duparray, value);
7224 }
7225 else {
7226 PUSH_INSN1(ret, *location, newarray, INT2FIX(0));
7227 }
7228 }
7229 return;
7230 }
7231
7232 // Here since we know there are possible side-effects inside the
7233 // array contents, we're going to build it entirely at runtime.
7234 // We'll do this by pushing all of the elements onto the stack and
7235 // then combining them with newarray.
7236 //
7237 // If this array is popped, then this serves only to ensure we enact
7238 // all side-effects (like method calls) that are contained within
7239 // the array contents.
7240 //
7241 // We treat all sequences of non-splat elements as their
7242 // own arrays, followed by a newarray, and then continually
7243 // concat the arrays with the SplatNode nodes.
7244 const int max_new_array_size = 0x100;
7245 const unsigned int min_tmp_array_size = 0x40;
7246
7247 int new_array_size = 0;
7248 bool first_chunk = true;
7249
7250 // This is an optimization wherein we keep track of whether or not
7251 // the previous element was a static literal. If it was, then we do
7252 // not attempt to check if we have a subarray that can be optimized.
7253 // If it was not, then we do check.
7254 bool static_literal = false;
7255
7256 // Either create a new array, or push to the existing array.
7257#define FLUSH_CHUNK \
7258 if (new_array_size) { \
7259 if (first_chunk) PUSH_INSN1(ret, *location, newarray, INT2FIX(new_array_size)); \
7260 else PUSH_INSN1(ret, *location, pushtoarray, INT2FIX(new_array_size)); \
7261 first_chunk = false; \
7262 new_array_size = 0; \
7263 }
7264
7265 for (size_t index = 0; index < elements->size; index++) {
7266 const pm_node_t *element = elements->nodes[index];
7267
7268 if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) {
7269 FLUSH_CHUNK;
7270
7271 const pm_splat_node_t *splat_element = (const pm_splat_node_t *) element;
7272 if (splat_element->expression) {
7273 PM_COMPILE_NOT_POPPED(splat_element->expression);
7274 }
7275 else {
7276 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
7277 PUSH_GETLOCAL(ret, *location, index.index, index.level);
7278 }
7279
7280 if (first_chunk) {
7281 // If this is the first element of the array then we
7282 // need to splatarray the elements into the list.
7283 PUSH_INSN1(ret, *location, splatarray, Qtrue);
7284 first_chunk = false;
7285 }
7286 else {
7287 PUSH_INSN(ret, *location, concattoarray);
7288 }
7289
7290 static_literal = false;
7291 }
7292 else if (PM_NODE_TYPE_P(element, PM_KEYWORD_HASH_NODE)) {
7293 if (new_array_size == 0 && first_chunk) {
7294 PUSH_INSN1(ret, *location, newarray, INT2FIX(0));
7295 first_chunk = false;
7296 }
7297 else {
7298 FLUSH_CHUNK;
7299 }
7300
7301 // If we get here, then this is the last element of the
7302 // array/arguments, because it cannot be followed by
7303 // anything else without a syntax error. This looks like:
7304 //
7305 // [foo, bar, baz: qux]
7306 // ^^^^^^^^
7307 //
7308 // [foo, bar, **baz]
7309 // ^^^^^
7310 //
7311 const pm_keyword_hash_node_t *keyword_hash = (const pm_keyword_hash_node_t *) element;
7312 pm_compile_hash_elements(iseq, element, &keyword_hash->elements, 0, Qundef, false, ret, scope_node);
7313
7314 // This boolean controls the manner in which we push the
7315 // hash onto the array. If it's all keyword splats, then we
7316 // can use the very specialized pushtoarraykwsplat
7317 // instruction to check if it's empty before we push it.
7318 size_t splats = 0;
7319 while (splats < keyword_hash->elements.size && PM_NODE_TYPE_P(keyword_hash->elements.nodes[splats], PM_ASSOC_SPLAT_NODE)) splats++;
7320
7321 if (keyword_hash->elements.size == splats) {
7322 PUSH_INSN(ret, *location, pushtoarraykwsplat);
7323 }
7324 else {
7325 new_array_size++;
7326 }
7327 }
7328 else if (
7329 PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL) &&
7330 !PM_CONTAINER_P(element) &&
7331 !static_literal &&
7332 ((index + min_tmp_array_size) < elements->size)
7333 ) {
7334 // If we have a static literal, then there's the potential
7335 // to group a bunch of them together with a literal array
7336 // and then concat them together.
7337 size_t right_index = index + 1;
7338 while (
7339 right_index < elements->size &&
7340 PM_NODE_FLAG_P(elements->nodes[right_index], PM_NODE_FLAG_STATIC_LITERAL) &&
7341 !PM_CONTAINER_P(elements->nodes[right_index])
7342 ) right_index++;
7343
7344 size_t tmp_array_size = right_index - index;
7345 if (tmp_array_size >= min_tmp_array_size) {
7346 VALUE tmp_array = rb_ary_hidden_new(tmp_array_size);
7347
7348 // Create the temporary array.
7349 for (; tmp_array_size; tmp_array_size--)
7350 rb_ary_push(tmp_array, pm_static_literal_value(iseq, elements->nodes[index++], scope_node));
7351
7352 index--; // about to be incremented by for loop
7353 RB_OBJ_SET_FROZEN_SHAREABLE(tmp_array);
7354
7355 // Emit the optimized code.
7356 FLUSH_CHUNK;
7357 if (first_chunk) {
7358 PUSH_INSN1(ret, *location, duparray, tmp_array);
7359 first_chunk = false;
7360 }
7361 else {
7362 PUSH_INSN1(ret, *location, putobject, tmp_array);
7363 PUSH_INSN(ret, *location, concattoarray);
7364 }
7365 }
7366 else {
7367 PM_COMPILE_NOT_POPPED(element);
7368 if (++new_array_size >= max_new_array_size) FLUSH_CHUNK;
7369 static_literal = true;
7370 }
7371 } else {
7372 PM_COMPILE_NOT_POPPED(element);
7373 if (++new_array_size >= max_new_array_size) FLUSH_CHUNK;
7374 static_literal = false;
7375 }
7376 }
7377
7378 FLUSH_CHUNK;
7379 if (popped) PUSH_INSN(ret, *location, pop);
7380
7381#undef FLUSH_CHUNK
7382}
7383
7384static inline void
7385pm_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)
7386{
7387 unsigned long throw_flag = 0;
7388
7389 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
7390 /* while/until */
7391 LABEL *splabel = NEW_LABEL(0);
7392 PUSH_LABEL(ret, splabel);
7393 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
7394
7395 if (node->arguments != NULL) {
7396 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
7397 }
7398 else {
7399 PUSH_INSN(ret, *location, putnil);
7400 }
7401
7402 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
7403 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
7404 PUSH_ADJUST_RESTORE(ret, splabel);
7405 if (!popped) PUSH_INSN(ret, *location, putnil);
7406 }
7407 else {
7408 const rb_iseq_t *ip = iseq;
7409
7410 while (ip) {
7411 if (!ISEQ_COMPILE_DATA(ip)) {
7412 ip = 0;
7413 break;
7414 }
7415
7416 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
7417 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
7418 }
7419 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
7420 throw_flag = 0;
7421 }
7422 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
7423 COMPILE_ERROR(iseq, location->line, "Invalid break");
7424 return;
7425 }
7426 else {
7427 ip = ISEQ_BODY(ip)->parent_iseq;
7428 continue;
7429 }
7430
7431 /* escape from block */
7432 if (node->arguments != NULL) {
7433 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
7434 }
7435 else {
7436 PUSH_INSN(ret, *location, putnil);
7437 }
7438
7439 PUSH_INSN1(ret, *location, throw, INT2FIX(throw_flag | TAG_BREAK));
7440 if (popped) PUSH_INSN(ret, *location, pop);
7441
7442 return;
7443 }
7444
7445 COMPILE_ERROR(iseq, location->line, "Invalid break");
7446 }
7447}
7448
7449static inline void
7450pm_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)
7451{
7452 ID method_id = pm_constant_id_lookup(scope_node, node->name);
7453
7454 const pm_location_t *message_loc = &node->message_loc;
7455 if (message_loc->length == 0) message_loc = &node->base.location;
7456
7457 const pm_node_location_t location = PM_LOCATION_START_LOCATION(message_loc, node->base.node_id);
7458 const char *builtin_func;
7459
7460 if (UNLIKELY(iseq_has_builtin_function_table(iseq)) && (builtin_func = pm_iseq_builtin_function_name(scope_node, node->receiver, method_id)) != NULL) {
7461 pm_compile_builtin_function_call(iseq, ret, scope_node, node, &location, popped, ISEQ_COMPILE_DATA(iseq)->current_block, builtin_func);
7462 return;
7463 }
7464
7465 LABEL *start = NEW_LABEL(location.line);
7466 if (node->block) PUSH_LABEL(ret, start);
7467
7468 switch (method_id) {
7469 case idUMinus: {
7470 if (pm_opt_str_freeze_p(iseq, node)) {
7471 VALUE value = parse_static_literal_string(iseq, scope_node, node->receiver, &((const pm_string_node_t * ) node->receiver)->unescaped);
7472 const struct rb_callinfo *callinfo = new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE);
7473 PUSH_INSN2(ret, location, opt_str_uminus, value, callinfo);
7474 if (popped) PUSH_INSN(ret, location, pop);
7475 return;
7476 }
7477 break;
7478 }
7479 case idFreeze: {
7480 if (pm_opt_str_freeze_p(iseq, node)) {
7481 VALUE value = parse_static_literal_string(iseq, scope_node, node->receiver, &((const pm_string_node_t * ) node->receiver)->unescaped);
7482 const struct rb_callinfo *callinfo = new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE);
7483 PUSH_INSN2(ret, location, opt_str_freeze, value, callinfo);
7484 if (popped) PUSH_INSN(ret, location, pop);
7485 return;
7486 }
7487 break;
7488 }
7489 }
7490
7491 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && !popped) {
7492 PUSH_INSN(ret, location, putnil);
7493 }
7494
7495 if (node->receiver == NULL) {
7496 PUSH_INSN(ret, location, putself);
7497 }
7498 else {
7499 if (method_id == idCall && PM_NODE_TYPE_P(node->receiver, PM_LOCAL_VARIABLE_READ_NODE)) {
7500 const pm_local_variable_read_node_t *read_node_cast = (const pm_local_variable_read_node_t *) node->receiver;
7501 uint32_t node_id = node->receiver->node_id;
7502 int idx, level;
7503
7504 if (iseq_block_param_id_p(iseq, pm_constant_id_lookup(scope_node, read_node_cast->name), &idx, &level)) {
7505 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)));
7506 }
7507 else {
7508 PM_COMPILE_NOT_POPPED(node->receiver);
7509 }
7510 }
7511 else {
7512 PM_COMPILE_NOT_POPPED(node->receiver);
7513 }
7514 }
7515
7516 pm_compile_call(iseq, node, ret, popped, scope_node, method_id, start);
7517 return;
7518}
7519
7520static inline void
7521pm_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)
7522{
7523 int flag = 0;
7524
7525 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) {
7526 flag = VM_CALL_FCALL;
7527 }
7528
7529 PM_COMPILE_NOT_POPPED(node->receiver);
7530
7531 LABEL *safe_label = NULL;
7532 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
7533 safe_label = NEW_LABEL(location->line);
7534 PUSH_INSN(ret, *location, dup);
7535 PUSH_INSNL(ret, *location, branchnil, safe_label);
7536 }
7537
7538 PUSH_INSN(ret, *location, dup);
7539
7540 ID id_read_name = pm_constant_id_lookup(scope_node, node->read_name);
7541 PUSH_SEND_WITH_FLAG(ret, *location, id_read_name, INT2FIX(0), INT2FIX(flag));
7542
7543 PM_COMPILE_NOT_POPPED(node->value);
7544 ID id_operator = pm_constant_id_lookup(scope_node, node->binary_operator);
7545 PUSH_SEND(ret, *location, id_operator, INT2FIX(1));
7546
7547 if (!popped) {
7548 PUSH_INSN(ret, *location, swap);
7549 PUSH_INSN1(ret, *location, topn, INT2FIX(1));
7550 }
7551
7552 ID id_write_name = pm_constant_id_lookup(scope_node, node->write_name);
7553 PUSH_SEND_WITH_FLAG(ret, *location, id_write_name, INT2FIX(1), INT2FIX(flag));
7554
7555 if (safe_label != NULL && popped) PUSH_LABEL(ret, safe_label);
7556 PUSH_INSN(ret, *location, pop);
7557 if (safe_label != NULL && !popped) PUSH_LABEL(ret, safe_label);
7558}
7559
7576static VALUE
7577pm_compile_case_node_dispatch(rb_iseq_t *iseq, VALUE dispatch, const pm_node_t *node, LABEL *label, pm_scope_node_t *scope_node)
7578{
7579 VALUE key = Qundef;
7580 switch (PM_NODE_TYPE(node)) {
7581 case PM_FLOAT_NODE: {
7582 key = pm_static_literal_value(iseq, node, scope_node);
7583 double intptr;
7584
7585 if (modf(RFLOAT_VALUE(key), &intptr) == 0.0) {
7586 key = (FIXABLE(intptr) ? LONG2FIX((long) intptr) : rb_dbl2big(intptr));
7587 }
7588
7589 break;
7590 }
7591 case PM_FALSE_NODE:
7592 case PM_INTEGER_NODE:
7593 case PM_NIL_NODE:
7594 case PM_SOURCE_FILE_NODE:
7595 case PM_SOURCE_LINE_NODE:
7596 case PM_SYMBOL_NODE:
7597 case PM_TRUE_NODE:
7598 key = pm_static_literal_value(iseq, node, scope_node);
7599 break;
7600 case PM_STRING_NODE: {
7601 const pm_string_node_t *cast = (const pm_string_node_t *) node;
7602 key = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
7603 break;
7604 }
7605 default:
7606 return Qundef;
7607 }
7608
7609 if (NIL_P(rb_hash_lookup(dispatch, key))) {
7610 rb_hash_aset(dispatch, key, ((VALUE) label) | 1);
7611 }
7612 return dispatch;
7613}
7614
7618static inline void
7619pm_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)
7620{
7621 const pm_node_location_t location = *node_location;
7622 const pm_node_list_t *conditions = &cast->conditions;
7623
7624 // This is the anchor that we will compile the conditions of the various
7625 // `when` nodes into. If a match is found, they will need to jump into
7626 // the body_seq anchor to the correct spot.
7627 DECL_ANCHOR(cond_seq);
7628
7629 // This is the anchor that we will compile the bodies of the various
7630 // `when` nodes into. We'll make sure that the clauses that are compiled
7631 // jump into the correct spots within this anchor.
7632 DECL_ANCHOR(body_seq);
7633
7634 // This is the label where all of the when clauses will jump to if they
7635 // have matched and are done executing their bodies.
7636 LABEL *end_label = NEW_LABEL(location.line);
7637
7638 // If we have a predicate on this case statement, then it's going to
7639 // compare all of the various when clauses to the predicate. If we
7640 // don't, then it's basically an if-elsif-else chain.
7641 if (cast->predicate == NULL) {
7642 // Establish branch coverage for the case node.
7643 VALUE branches = Qfalse;
7644 rb_code_location_t case_location = { 0 };
7645 int branch_id = 0;
7646
7647 if (PM_BRANCH_COVERAGE_P(iseq)) {
7648 case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
7649 branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case");
7650 }
7651
7652 // Loop through each clauses in the case node and compile each of
7653 // the conditions within them into cond_seq. If they match, they
7654 // should jump into their respective bodies in body_seq.
7655 for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) {
7656 const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index];
7657 const pm_node_list_t *conditions = &clause->conditions;
7658
7659 int clause_lineno = pm_node_line_number_cached((const pm_node_t *) clause, scope_node);
7660 LABEL *label = NEW_LABEL(clause_lineno);
7661 PUSH_LABEL(body_seq, label);
7662
7663 // Establish branch coverage for the when clause.
7664 if (PM_BRANCH_COVERAGE_P(iseq)) {
7665 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));
7666 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches);
7667 }
7668
7669 if (clause->statements != NULL) {
7670 pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node);
7671 }
7672 else if (!popped) {
7673 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
7674 }
7675
7676 PUSH_INSNL(body_seq, location, jump, end_label);
7677
7678 // Compile each of the conditions for the when clause into the
7679 // cond_seq. Each one should have a unique condition and should
7680 // jump to the subsequent one if it doesn't match.
7681 for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) {
7682 const pm_node_t *condition = conditions->nodes[condition_index];
7683
7684 if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) {
7685 pm_node_location_t cond_location = PM_NODE_START_LOCATION(condition);
7686 PUSH_INSN(cond_seq, cond_location, putnil);
7687 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7688 PUSH_INSN1(cond_seq, cond_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
7689 PUSH_INSNL(cond_seq, cond_location, branchif, label);
7690 }
7691 else {
7692 LABEL *next_label = NEW_LABEL(pm_node_line_number_cached(condition, scope_node));
7693 pm_compile_branch_condition(iseq, cond_seq, condition, label, next_label, scope_node);
7694 PUSH_LABEL(cond_seq, next_label);
7695 }
7696 }
7697 }
7698
7699 // Establish branch coverage for the else clause (implicit or
7700 // explicit).
7701 if (PM_BRANCH_COVERAGE_P(iseq)) {
7702 rb_code_location_t branch_location;
7703
7704 if (cast->else_clause == NULL) {
7705 branch_location = case_location;
7706 } else if (cast->else_clause->statements == NULL) {
7707 branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->else_clause);
7708 } else {
7709 branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->else_clause->statements);
7710 }
7711
7712 add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
7713 }
7714
7715 // Compile the else clause if there is one.
7716 if (cast->else_clause != NULL) {
7717 pm_compile_node(iseq, (const pm_node_t *) cast->else_clause, cond_seq, popped, scope_node);
7718 }
7719 else if (!popped) {
7720 PUSH_SYNTHETIC_PUTNIL(cond_seq, iseq);
7721 }
7722
7723 // Finally, jump to the end label if none of the other conditions
7724 // have matched.
7725 PUSH_INSNL(cond_seq, location, jump, end_label);
7726 PUSH_SEQ(ret, cond_seq);
7727 }
7728 else {
7729 // Establish branch coverage for the case node.
7730 VALUE branches = Qfalse;
7731 rb_code_location_t case_location = { 0 };
7732 int branch_id = 0;
7733
7734 if (PM_BRANCH_COVERAGE_P(iseq)) {
7735 case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
7736 branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case");
7737 }
7738
7739 // This is the label where everything will fall into if none of the
7740 // conditions matched.
7741 LABEL *else_label = NEW_LABEL(location.line);
7742
7743 // It's possible for us to speed up the case node by using a
7744 // dispatch hash. This is a hash that maps the conditions of the
7745 // various when clauses to the labels of their bodies. If we can
7746 // compile the conditions into a hash key, then we can use a hash
7747 // lookup to jump directly to the correct when clause body.
7748 VALUE dispatch = Qundef;
7749 if (ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
7750 dispatch = rb_hash_new();
7751 RHASH_TBL_RAW(dispatch)->type = &cdhash_type;
7752 }
7753
7754 // We're going to loop through each of the conditions in the case
7755 // node and compile each of their contents into both the cond_seq
7756 // and the body_seq. Each condition will use its own label to jump
7757 // from its conditions into its body.
7758 //
7759 // Note that none of the code in the loop below should be adding
7760 // anything to ret, as we're going to be laying out the entire case
7761 // node instructions later.
7762 for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) {
7763 const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index];
7764 pm_node_location_t clause_location = PM_NODE_START_LOCATION((const pm_node_t *) clause);
7765
7766 const pm_node_list_t *conditions = &clause->conditions;
7767 LABEL *label = NEW_LABEL(clause_location.line);
7768
7769 // Compile each of the conditions for the when clause into the
7770 // cond_seq. Each one should have a unique comparison that then
7771 // jumps into the body if it matches.
7772 for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) {
7773 const pm_node_t *condition = conditions->nodes[condition_index];
7774 const pm_node_location_t condition_location = PM_NODE_START_LOCATION(condition);
7775
7776 // If we haven't already abandoned the optimization, then
7777 // we're going to try to compile the condition into the
7778 // dispatch hash.
7779 if (dispatch != Qundef) {
7780 dispatch = pm_compile_case_node_dispatch(iseq, dispatch, condition, label, scope_node);
7781 }
7782
7783 if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) {
7784 PUSH_INSN(cond_seq, condition_location, dup);
7785 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7786 PUSH_INSN1(cond_seq, condition_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
7787 }
7788 else {
7789 if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
7790 const pm_string_node_t *string = (const pm_string_node_t *) condition;
7791 VALUE value = parse_static_literal_string(iseq, scope_node, condition, &string->unescaped);
7792 PUSH_INSN1(cond_seq, condition_location, putobject, value);
7793 }
7794 else {
7795 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7796 }
7797
7798 PUSH_INSN1(cond_seq, condition_location, topn, INT2FIX(1));
7799 PUSH_SEND_WITH_FLAG(cond_seq, condition_location, idEqq, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
7800 }
7801
7802 PUSH_INSNL(cond_seq, condition_location, branchif, label);
7803 }
7804
7805 // Now, add the label to the body and compile the body of the
7806 // when clause. This involves popping the predicate, compiling
7807 // the statements to be executed, and then compiling a jump to
7808 // the end of the case node.
7809 PUSH_LABEL(body_seq, label);
7810 PUSH_INSN(body_seq, clause_location, pop);
7811
7812 // Establish branch coverage for the when clause.
7813 if (PM_BRANCH_COVERAGE_P(iseq)) {
7814 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));
7815 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches);
7816 }
7817
7818 if (clause->statements != NULL) {
7819 pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node);
7820 }
7821 else if (!popped) {
7822 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
7823 }
7824
7825 PUSH_INSNL(body_seq, clause_location, jump, end_label);
7826 }
7827
7828 // Now that we have compiled the conditions and the bodies of the
7829 // various when clauses, we can compile the predicate, lay out the
7830 // conditions, compile the fallback subsequent if there is one, and
7831 // finally put in the bodies of the when clauses.
7832 PM_COMPILE_NOT_POPPED(cast->predicate);
7833
7834 // If we have a dispatch hash, then we'll use it here to create the
7835 // optimization.
7836 if (dispatch != Qundef) {
7837 PUSH_INSN(ret, location, dup);
7838 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.
7839 PUSH_INSN2(ret, location, opt_case_dispatch, dispatch, else_label);
7840 LABEL_REF(else_label);
7841 }
7842
7843 PUSH_SEQ(ret, cond_seq);
7844
7845 // Compile either the explicit else clause or an implicit else
7846 // clause.
7847 PUSH_LABEL(ret, else_label);
7848
7849 if (cast->else_clause != NULL) {
7850 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));
7851 PUSH_INSN(ret, else_location, pop);
7852
7853 // Establish branch coverage for the else clause.
7854 if (PM_BRANCH_COVERAGE_P(iseq)) {
7855 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));
7856 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
7857 }
7858
7859 PM_COMPILE((const pm_node_t *) cast->else_clause);
7860 PUSH_INSNL(ret, else_location, jump, end_label);
7861 }
7862 else {
7863 PUSH_INSN(ret, location, pop);
7864
7865 // Establish branch coverage for the implicit else clause.
7866 if (PM_BRANCH_COVERAGE_P(iseq)) {
7867 add_trace_branch_coverage(iseq, ret, &case_location, case_location.beg_pos.column, branch_id, "else", branches);
7868 }
7869
7870 if (!popped) PUSH_INSN(ret, location, putnil);
7871 PUSH_INSNL(ret, location, jump, end_label);
7872 }
7873 }
7874
7875 PUSH_SEQ(ret, body_seq);
7876 PUSH_LABEL(ret, end_label);
7877}
7878
7879static inline void
7880pm_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)
7881{
7882 // This is the anchor that we will compile the bodies of the various
7883 // `in` nodes into. We'll make sure that the patterns that are compiled
7884 // jump into the correct spots within this anchor.
7885 DECL_ANCHOR(body_seq);
7886
7887 // This is the anchor that we will compile the patterns of the various
7888 // `in` nodes into. If a match is found, they will need to jump into the
7889 // body_seq anchor to the correct spot.
7890 DECL_ANCHOR(cond_seq);
7891
7892 // This label is used to indicate the end of the entire node. It is
7893 // jumped to after the entire stack is cleaned up.
7894 LABEL *end_label = NEW_LABEL(location->line);
7895
7896 // This label is used as the fallback for the case match. If no match is
7897 // found, then we jump to this label. This is either an `else` clause or
7898 // an error handler.
7899 LABEL *else_label = NEW_LABEL(location->line);
7900
7901 // We're going to use this to uniquely identify each branch so that we
7902 // can track coverage information.
7903 rb_code_location_t case_location = { 0 };
7904 VALUE branches = Qfalse;
7905 int branch_id = 0;
7906
7907 if (PM_BRANCH_COVERAGE_P(iseq)) {
7908 case_location = pm_code_location(scope_node, (const pm_node_t *) node);
7909 branches = decl_branch_base(iseq, PTR2NUM(node), &case_location, "case");
7910 }
7911
7912 // If there is only one pattern, then the behavior changes a bit. It
7913 // effectively gets treated as a match required node (this is how it is
7914 // represented in the other parser).
7915 bool in_single_pattern = node->else_clause == NULL && node->conditions.size == 1;
7916
7917 // First, we're going to push a bunch of stuff onto the stack that is
7918 // going to serve as our scratch space.
7919 if (in_single_pattern) {
7920 PUSH_INSN(ret, *location, putnil); // key error key
7921 PUSH_INSN(ret, *location, putnil); // key error matchee
7922 PUSH_INSN1(ret, *location, putobject, Qfalse); // key error?
7923 PUSH_INSN(ret, *location, putnil); // error string
7924 }
7925
7926 // Now we're going to compile the value to match against.
7927 PUSH_INSN(ret, *location, putnil); // deconstruct cache
7928 PM_COMPILE_NOT_POPPED(node->predicate);
7929
7930 // Next, we'll loop through every in clause and compile its body into
7931 // the body_seq anchor and its pattern into the cond_seq anchor. We'll
7932 // make sure the pattern knows how to jump correctly into the body if it
7933 // finds a match.
7934 for (size_t index = 0; index < node->conditions.size; index++) {
7935 const pm_node_t *condition = node->conditions.nodes[index];
7936 RUBY_ASSERT(PM_NODE_TYPE_P(condition, PM_IN_NODE));
7937
7938 const pm_in_node_t *in_node = (const pm_in_node_t *) condition;
7939 const pm_node_location_t in_location = PM_NODE_START_LOCATION(in_node);
7940 const pm_node_location_t pattern_location = PM_NODE_START_LOCATION(in_node->pattern);
7941
7942 if (branch_id) {
7943 PUSH_INSN(body_seq, in_location, putnil);
7944 }
7945
7946 LABEL *body_label = NEW_LABEL(in_location.line);
7947 PUSH_LABEL(body_seq, body_label);
7948 PUSH_INSN1(body_seq, in_location, adjuststack, INT2FIX(in_single_pattern ? 6 : 2));
7949
7950 // Establish branch coverage for the in clause.
7951 if (PM_BRANCH_COVERAGE_P(iseq)) {
7952 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));
7953 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "in", branches);
7954 }
7955
7956 if (in_node->statements != NULL) {
7957 PM_COMPILE_INTO_ANCHOR(body_seq, (const pm_node_t *) in_node->statements);
7958 }
7959 else if (!popped) {
7960 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
7961 }
7962
7963 PUSH_INSNL(body_seq, in_location, jump, end_label);
7964 LABEL *next_pattern_label = NEW_LABEL(pattern_location.line);
7965
7966 PUSH_INSN(cond_seq, pattern_location, dup);
7967 pm_compile_pattern(iseq, scope_node, in_node->pattern, cond_seq, body_label, next_pattern_label, in_single_pattern, true, 2);
7968 PUSH_LABEL(cond_seq, next_pattern_label);
7969 LABEL_UNREMOVABLE(next_pattern_label);
7970 }
7971
7972 if (node->else_clause != NULL) {
7973 // If we have an `else` clause, then this becomes our fallback (and
7974 // there is no need to compile in code to potentially raise an
7975 // error).
7976 const pm_else_node_t *else_node = node->else_clause;
7977
7978 PUSH_LABEL(cond_seq, else_label);
7979 PUSH_INSN(cond_seq, *location, pop);
7980 PUSH_INSN(cond_seq, *location, pop);
7981
7982 // Establish branch coverage for the else clause.
7983 if (PM_BRANCH_COVERAGE_P(iseq)) {
7984 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));
7985 add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
7986 }
7987
7988 PM_COMPILE_INTO_ANCHOR(cond_seq, (const pm_node_t *) else_node);
7989 PUSH_INSNL(cond_seq, *location, jump, end_label);
7990 PUSH_INSN(cond_seq, *location, putnil);
7991 if (popped) PUSH_INSN(cond_seq, *location, putnil);
7992 }
7993 else {
7994 // Otherwise, if we do not have an `else` clause, we will compile in
7995 // the code to handle raising an appropriate error.
7996 PUSH_LABEL(cond_seq, else_label);
7997
7998 // Establish branch coverage for the implicit else clause.
7999 add_trace_branch_coverage(iseq, cond_seq, &case_location, case_location.beg_pos.column, branch_id, "else", branches);
8000
8001 if (in_single_pattern) {
8002 pm_compile_pattern_error_handler(iseq, scope_node, (const pm_node_t *) node, cond_seq, end_label, popped);
8003 }
8004 else {
8005 PUSH_INSN1(cond_seq, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8006 PUSH_INSN1(cond_seq, *location, putobject, rb_eNoMatchingPatternError);
8007 PUSH_INSN1(cond_seq, *location, topn, INT2FIX(2));
8008 PUSH_SEND(cond_seq, *location, id_core_raise, INT2FIX(2));
8009
8010 PUSH_INSN1(cond_seq, *location, adjuststack, INT2FIX(3));
8011 if (!popped) PUSH_INSN(cond_seq, *location, putnil);
8012 PUSH_INSNL(cond_seq, *location, jump, end_label);
8013 PUSH_INSN1(cond_seq, *location, dupn, INT2FIX(1));
8014 if (popped) PUSH_INSN(cond_seq, *location, putnil);
8015 }
8016 }
8017
8018 // At the end of all of this compilation, we will add the code for the
8019 // conditions first, then the various bodies, then mark the end of the
8020 // entire sequence with the end label.
8021 PUSH_SEQ(ret, cond_seq);
8022 PUSH_SEQ(ret, body_seq);
8023 PUSH_LABEL(ret, end_label);
8024}
8025
8026static inline void
8027pm_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)
8028{
8029 const rb_iseq_t *block = NULL;
8030 const rb_iseq_t *previous_block = NULL;
8031 LABEL *retry_label = NULL;
8032 LABEL *retry_end_l = NULL;
8033
8034 if (node->block != NULL) {
8035 previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
8036 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
8037
8038 retry_label = NEW_LABEL(location->line);
8039 retry_end_l = NEW_LABEL(location->line);
8040
8041 PUSH_LABEL(ret, retry_label);
8042 }
8043 else {
8044 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
8045 }
8046
8047 PUSH_INSN(ret, *location, putself);
8048 int flag = VM_CALL_ZSUPER | VM_CALL_SUPER | VM_CALL_FCALL;
8049
8050 if (node->block != NULL) {
8051 pm_scope_node_t next_scope_node;
8052 pm_scope_node_init((const pm_node_t *) node->block, &next_scope_node, scope_node);
8053
8054 ISEQ_COMPILE_DATA(iseq)->current_block = block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location->line);
8055 pm_scope_node_destroy(&next_scope_node);
8056 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) block);
8057 }
8058
8059 DECL_ANCHOR(args);
8060
8061 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
8062 const rb_iseq_t *local_iseq = body->local_iseq;
8063 const struct rb_iseq_constant_body *const local_body = ISEQ_BODY(local_iseq);
8064
8065 int argc = 0;
8066 int depth = get_lvar_level(iseq);
8067
8068 if (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
8069 flag |= VM_CALL_FORWARDING;
8070 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_DOT3, 0);
8071 PUSH_GETLOCAL(ret, *location, mult_local.index, mult_local.level);
8072
8073 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, 0, flag, NULL, block != NULL);
8074 PUSH_INSN2(ret, *location, invokesuperforward, callinfo, block);
8075
8076 if (popped) PUSH_INSN(ret, *location, pop);
8077 if (node->block) {
8078 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
8079 }
8080 return;
8081 }
8082
8083 if (local_body->param.flags.has_lead) {
8084 /* required arguments */
8085 for (int i = 0; i < local_body->param.lead_num; i++) {
8086 int idx = local_body->local_table_size - i;
8087 PUSH_GETLOCAL(args, *location, idx, depth);
8088 }
8089 argc += local_body->param.lead_num;
8090 }
8091
8092 if (local_body->param.flags.has_opt) {
8093 /* optional arguments */
8094 for (int j = 0; j < local_body->param.opt_num; j++) {
8095 int idx = local_body->local_table_size - (argc + j);
8096 PUSH_GETLOCAL(args, *location, idx, depth);
8097 }
8098 argc += local_body->param.opt_num;
8099 }
8100
8101 if (local_body->param.flags.has_rest) {
8102 /* rest argument */
8103 int idx = local_body->local_table_size - local_body->param.rest_start;
8104 PUSH_GETLOCAL(args, *location, idx, depth);
8105 PUSH_INSN1(args, *location, splatarray, Qfalse);
8106
8107 argc = local_body->param.rest_start + 1;
8108 flag |= VM_CALL_ARGS_SPLAT;
8109 }
8110
8111 if (local_body->param.flags.has_post) {
8112 /* post arguments */
8113 int post_len = local_body->param.post_num;
8114 int post_start = local_body->param.post_start;
8115
8116 int j = 0;
8117 for (; j < post_len; j++) {
8118 int idx = local_body->local_table_size - (post_start + j);
8119 PUSH_GETLOCAL(args, *location, idx, depth);
8120 }
8121
8122 if (local_body->param.flags.has_rest) {
8123 // argc remains unchanged from rest branch
8124 PUSH_INSN1(args, *location, newarray, INT2FIX(j));
8125 PUSH_INSN(args, *location, concatarray);
8126 }
8127 else {
8128 argc = post_len + post_start;
8129 }
8130 }
8131
8132 const struct rb_iseq_param_keyword *const local_keyword = local_body->param.keyword;
8133 if (local_body->param.flags.has_kw) {
8134 int local_size = local_body->local_table_size;
8135 argc++;
8136
8137 PUSH_INSN1(args, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8138
8139 if (local_body->param.flags.has_kwrest) {
8140 int idx = local_body->local_table_size - local_keyword->rest_start;
8141 PUSH_GETLOCAL(args, *location, idx, depth);
8142 RUBY_ASSERT(local_keyword->num > 0);
8143 PUSH_SEND(args, *location, rb_intern("dup"), INT2FIX(0));
8144 }
8145 else {
8146 PUSH_INSN1(args, *location, newhash, INT2FIX(0));
8147 }
8148 int i = 0;
8149 for (; i < local_keyword->num; ++i) {
8150 ID id = local_keyword->table[i];
8151 int idx = local_size - get_local_var_idx(local_iseq, id);
8152
8153 {
8154 VALUE operand = ID2SYM(id);
8155 PUSH_INSN1(args, *location, putobject, operand);
8156 }
8157
8158 PUSH_GETLOCAL(args, *location, idx, depth);
8159 }
8160
8161 PUSH_SEND(args, *location, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
8162 flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT;
8163 }
8164 else if (local_body->param.flags.has_kwrest) {
8165 int idx = local_body->local_table_size - local_keyword->rest_start;
8166 PUSH_GETLOCAL(args, *location, idx, depth);
8167 argc++;
8168 flag |= VM_CALL_KW_SPLAT;
8169 }
8170
8171 PUSH_SEQ(ret, args);
8172
8173 {
8174 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flag, NULL, block != NULL);
8175 PUSH_INSN2(ret, *location, invokesuper, callinfo, block);
8176 }
8177
8178 if (node->block != NULL) {
8179 pm_compile_retry_end_label(iseq, ret, retry_end_l);
8180 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, block, retry_end_l);
8181 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
8182 }
8183
8184 if (popped) PUSH_INSN(ret, *location, pop);
8185}
8186
8187static inline void
8188pm_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)
8189{
8190 LABEL *matched_label = NEW_LABEL(location->line);
8191 LABEL *unmatched_label = NEW_LABEL(location->line);
8192 LABEL *done_label = NEW_LABEL(location->line);
8193
8194 // First, we're going to push a bunch of stuff onto the stack that is
8195 // going to serve as our scratch space.
8196 PUSH_INSN(ret, *location, putnil); // key error key
8197 PUSH_INSN(ret, *location, putnil); // key error matchee
8198 PUSH_INSN1(ret, *location, putobject, Qfalse); // key error?
8199 PUSH_INSN(ret, *location, putnil); // error string
8200 PUSH_INSN(ret, *location, putnil); // deconstruct cache
8201
8202 // Next we're going to compile the value expression such that it's on
8203 // the stack.
8204 PM_COMPILE_NOT_POPPED(node->value);
8205
8206 // Here we'll dup it so that it can be used for comparison, but also be
8207 // used for error handling.
8208 PUSH_INSN(ret, *location, dup);
8209
8210 // Next we'll compile the pattern. We indicate to the pm_compile_pattern
8211 // function that this is the only pattern that will be matched against
8212 // through the in_single_pattern parameter. We also indicate that the
8213 // value to compare against is 2 slots from the top of the stack (the
8214 // base_index parameter).
8215 pm_compile_pattern(iseq, scope_node, node->pattern, ret, matched_label, unmatched_label, true, true, 2);
8216
8217 // If the pattern did not match the value, then we're going to compile
8218 // in our error handler code. This will determine which error to raise
8219 // and raise it.
8220 PUSH_LABEL(ret, unmatched_label);
8221 pm_compile_pattern_error_handler(iseq, scope_node, (const pm_node_t *) node, ret, done_label, popped);
8222
8223 // If the pattern did match, we'll clean up the values we've pushed onto
8224 // the stack and then push nil onto the stack if it's not popped.
8225 PUSH_LABEL(ret, matched_label);
8226 PUSH_INSN1(ret, *location, adjuststack, INT2FIX(6));
8227 if (!popped) PUSH_INSN(ret, *location, putnil);
8228 PUSH_INSNL(ret, *location, jump, done_label);
8229
8230 PUSH_LABEL(ret, done_label);
8231}
8232
8233static inline void
8234pm_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)
8235{
8236 LABEL *fail_label = NEW_LABEL(location->line);
8237 LABEL *end_label = NEW_LABEL(location->line);
8238
8239 // First, we'll compile the call so that all of its instructions are
8240 // present. Then we'll compile all of the local variable targets.
8241 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->call);
8242
8243 // Now, check if the match was successful. If it was, then we'll
8244 // continue on and assign local variables. Otherwise we'll skip over the
8245 // assignment code.
8246 {
8247 VALUE operand = rb_id2sym(idBACKREF);
8248 PUSH_INSN1(ret, *location, getglobal, operand);
8249 }
8250
8251 PUSH_INSN(ret, *location, dup);
8252 PUSH_INSNL(ret, *location, branchunless, fail_label);
8253
8254 // If there's only a single local variable target, we can skip some of
8255 // the bookkeeping, so we'll put a special branch here.
8256 size_t targets_count = node->targets.size;
8257
8258 if (targets_count == 1) {
8259 const pm_node_t *target = node->targets.nodes[0];
8260 RUBY_ASSERT(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
8261
8262 const pm_local_variable_target_node_t *local_target = (const pm_local_variable_target_node_t *) target;
8263 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, local_target->name, local_target->depth);
8264
8265 {
8266 VALUE operand = rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name));
8267 PUSH_INSN1(ret, *location, putobject, operand);
8268 }
8269
8270 PUSH_SEND(ret, *location, idAREF, INT2FIX(1));
8271 PUSH_LABEL(ret, fail_label);
8272 PUSH_SETLOCAL(ret, *location, index.index, index.level);
8273 if (popped) PUSH_INSN(ret, *location, pop);
8274 return;
8275 }
8276
8277 DECL_ANCHOR(fail_anchor);
8278
8279 // Otherwise there is more than one local variable target, so we'll need
8280 // to do some bookkeeping.
8281 for (size_t targets_index = 0; targets_index < targets_count; targets_index++) {
8282 const pm_node_t *target = node->targets.nodes[targets_index];
8283 RUBY_ASSERT(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
8284
8285 const pm_local_variable_target_node_t *local_target = (const pm_local_variable_target_node_t *) target;
8286 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, local_target->name, local_target->depth);
8287
8288 if (((size_t) targets_index) < (targets_count - 1)) {
8289 PUSH_INSN(ret, *location, dup);
8290 }
8291
8292 {
8293 VALUE operand = rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name));
8294 PUSH_INSN1(ret, *location, putobject, operand);
8295 }
8296
8297 PUSH_SEND(ret, *location, idAREF, INT2FIX(1));
8298 PUSH_SETLOCAL(ret, *location, index.index, index.level);
8299
8300 PUSH_INSN(fail_anchor, *location, putnil);
8301 PUSH_SETLOCAL(fail_anchor, *location, index.index, index.level);
8302 }
8303
8304 // Since we matched successfully, now we'll jump to the end.
8305 PUSH_INSNL(ret, *location, jump, end_label);
8306
8307 // In the case that the match failed, we'll loop through each local
8308 // variable target and set all of them to `nil`.
8309 PUSH_LABEL(ret, fail_label);
8310 PUSH_INSN(ret, *location, pop);
8311 PUSH_SEQ(ret, fail_anchor);
8312
8313 // Finally, we can push the end label for either case.
8314 PUSH_LABEL(ret, end_label);
8315 if (popped) PUSH_INSN(ret, *location, pop);
8316}
8317
8318static inline void
8319pm_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)
8320{
8321 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
8322 LABEL *splabel = NEW_LABEL(0);
8323 PUSH_LABEL(ret, splabel);
8324
8325 if (node->arguments) {
8326 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8327 }
8328 else {
8329 PUSH_INSN(ret, *location, putnil);
8330 }
8331 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8332
8333 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
8334 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8335
8336 PUSH_ADJUST_RESTORE(ret, splabel);
8337 if (!popped) PUSH_INSN(ret, *location, putnil);
8338 }
8339 else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
8340 LABEL *splabel = NEW_LABEL(0);
8341
8342 PUSH_LABEL(ret, splabel);
8343 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->start_label);
8344
8345 if (node->arguments != NULL) {
8346 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8347 }
8348 else {
8349 PUSH_INSN(ret, *location, putnil);
8350 }
8351
8352 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8353 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
8354 PUSH_ADJUST_RESTORE(ret, splabel);
8355 splabel->unremovable = FALSE;
8356
8357 if (!popped) PUSH_INSN(ret, *location, putnil);
8358 }
8359 else {
8360 const rb_iseq_t *ip = iseq;
8361 unsigned long throw_flag = 0;
8362
8363 while (ip) {
8364 if (!ISEQ_COMPILE_DATA(ip)) {
8365 ip = 0;
8366 break;
8367 }
8368
8369 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
8370 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8371 /* while loop */
8372 break;
8373 }
8374 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8375 break;
8376 }
8377 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8378 COMPILE_ERROR(iseq, location->line, "Invalid next");
8379 return;
8380 }
8381
8382 ip = ISEQ_BODY(ip)->parent_iseq;
8383 }
8384
8385 if (ip != 0) {
8386 if (node->arguments) {
8387 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8388 }
8389 else {
8390 PUSH_INSN(ret, *location, putnil);
8391 }
8392
8393 PUSH_INSN1(ret, *location, throw, INT2FIX(throw_flag | TAG_NEXT));
8394 if (popped) PUSH_INSN(ret, *location, pop);
8395 }
8396 else {
8397 COMPILE_ERROR(iseq, location->line, "Invalid next");
8398 }
8399 }
8400}
8401
8402static inline void
8403pm_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)
8404{
8405 if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
8406 LABEL *splabel = NEW_LABEL(0);
8407
8408 PUSH_LABEL(ret, splabel);
8409 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
8410 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8411
8412 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
8413 PUSH_ADJUST_RESTORE(ret, splabel);
8414 if (!popped) PUSH_INSN(ret, *location, putnil);
8415 }
8416 else if (ISEQ_BODY(iseq)->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
8417 LABEL *splabel = NEW_LABEL(0);
8418
8419 PUSH_LABEL(ret, splabel);
8420 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8421 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->start_label);
8422
8423 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8424 PUSH_ADJUST_RESTORE(ret, splabel);
8425 if (!popped) PUSH_INSN(ret, *location, putnil);
8426 }
8427 else {
8428 const rb_iseq_t *ip = iseq;
8429
8430 while (ip) {
8431 if (!ISEQ_COMPILE_DATA(ip)) {
8432 ip = 0;
8433 break;
8434 }
8435
8436 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8437 break;
8438 }
8439 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8440 break;
8441 }
8442 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8443 COMPILE_ERROR(iseq, location->line, "Invalid redo");
8444 return;
8445 }
8446
8447 ip = ISEQ_BODY(ip)->parent_iseq;
8448 }
8449
8450 if (ip != 0) {
8451 PUSH_INSN(ret, *location, putnil);
8452 PUSH_INSN1(ret, *location, throw, INT2FIX(VM_THROW_NO_ESCAPE_FLAG | TAG_REDO));
8453 if (popped) PUSH_INSN(ret, *location, pop);
8454 }
8455 else {
8456 COMPILE_ERROR(iseq, location->line, "Invalid redo");
8457 }
8458 }
8459}
8460
8461static inline void
8462pm_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)
8463{
8464 iseq_set_exception_local_table(iseq);
8465
8466 // First, establish the labels that we need to be able to jump to within
8467 // this compilation block.
8468 LABEL *exception_match_label = NEW_LABEL(location->line);
8469 LABEL *rescue_end_label = NEW_LABEL(location->line);
8470
8471 // Next, compile each of the exceptions that we're going to be
8472 // handling. For each one, we'll add instructions to check if the
8473 // exception matches the raised one, and if it does then jump to the
8474 // exception_match_label label. Otherwise it will fall through to the
8475 // subsequent check. If there are no exceptions, we'll only check
8476 // StandardError.
8477 const pm_node_list_t *exceptions = &node->exceptions;
8478
8479 if (exceptions->size > 0) {
8480 for (size_t index = 0; index < exceptions->size; index++) {
8481 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8482 PM_COMPILE(exceptions->nodes[index]);
8483 int checkmatch_flags = VM_CHECKMATCH_TYPE_RESCUE;
8484 if (PM_NODE_TYPE_P(exceptions->nodes[index], PM_SPLAT_NODE)) {
8485 checkmatch_flags |= VM_CHECKMATCH_ARRAY;
8486 }
8487 PUSH_INSN1(ret, *location, checkmatch, INT2FIX(checkmatch_flags));
8488 PUSH_INSNL(ret, *location, branchif, exception_match_label);
8489 }
8490 }
8491 else {
8492 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8493 PUSH_INSN1(ret, *location, putobject, rb_eStandardError);
8494 PUSH_INSN1(ret, *location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
8495 PUSH_INSNL(ret, *location, branchif, exception_match_label);
8496 }
8497
8498 // If none of the exceptions that we are matching against matched, then
8499 // we'll jump straight to the rescue_end_label label.
8500 PUSH_INSNL(ret, *location, jump, rescue_end_label);
8501
8502 // Here we have the exception_match_label, which is where the
8503 // control-flow goes in the case that one of the exceptions matched.
8504 // Here we will compile the instructions to handle the exception.
8505 PUSH_LABEL(ret, exception_match_label);
8506 PUSH_TRACE(ret, RUBY_EVENT_RESCUE);
8507
8508 // If we have a reference to the exception, then we'll compile the write
8509 // into the instruction sequence. This can look quite different
8510 // depending on the kind of write being performed.
8511 if (node->reference) {
8512 DECL_ANCHOR(writes);
8513 DECL_ANCHOR(cleanup);
8514
8515 pm_compile_target_node(iseq, node->reference, ret, writes, cleanup, scope_node, NULL);
8516 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8517
8518 PUSH_SEQ(ret, writes);
8519 PUSH_SEQ(ret, cleanup);
8520 }
8521
8522 // If we have statements to execute, we'll compile them here. Otherwise
8523 // we'll push nil onto the stack.
8524 if (node->statements != NULL) {
8525 // We'll temporarily remove the end_label location from the iseq
8526 // when compiling the statements so that next/redo statements
8527 // inside the body will throw to the correct place instead of
8528 // jumping straight to the end of this iseq
8529 LABEL *prev_end = ISEQ_COMPILE_DATA(iseq)->end_label;
8530 ISEQ_COMPILE_DATA(iseq)->end_label = NULL;
8531
8532 PM_COMPILE((const pm_node_t *) node->statements);
8533
8534 // Now restore the end_label
8535 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end;
8536 }
8537 else {
8538 PUSH_INSN(ret, *location, putnil);
8539 }
8540
8541 PUSH_INSN(ret, *location, leave);
8542
8543 // Here we'll insert the rescue_end_label label, which is jumped to if
8544 // none of the exceptions matched. It will cause the control-flow to
8545 // either jump to the next rescue clause or it will fall through to the
8546 // subsequent instruction returning the raised error.
8547 PUSH_LABEL(ret, rescue_end_label);
8548 if (node->subsequent != NULL) {
8549 PM_COMPILE((const pm_node_t *) node->subsequent);
8550 }
8551 else {
8552 PUSH_GETLOCAL(ret, *location, 1, 0);
8553 }
8554}
8555
8556static inline void
8557pm_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)
8558{
8559 const pm_arguments_node_t *arguments = node->arguments;
8560 enum rb_iseq_type type = ISEQ_BODY(iseq)->type;
8561 LABEL *splabel = 0;
8562
8563 const rb_iseq_t *parent_iseq = iseq;
8564 enum rb_iseq_type parent_type = ISEQ_BODY(parent_iseq)->type;
8565 while (parent_type == ISEQ_TYPE_RESCUE || parent_type == ISEQ_TYPE_ENSURE) {
8566 if (!(parent_iseq = ISEQ_BODY(parent_iseq)->parent_iseq)) break;
8567 parent_type = ISEQ_BODY(parent_iseq)->type;
8568 }
8569
8570 switch (parent_type) {
8571 case ISEQ_TYPE_TOP:
8572 case ISEQ_TYPE_MAIN:
8573 if (arguments) {
8574 rb_warn("argument of top-level return is ignored");
8575 }
8576 if (parent_iseq == iseq) {
8577 type = ISEQ_TYPE_METHOD;
8578 }
8579 break;
8580 default:
8581 break;
8582 }
8583
8584 if (type == ISEQ_TYPE_METHOD) {
8585 splabel = NEW_LABEL(0);
8586 PUSH_LABEL(ret, splabel);
8587 PUSH_ADJUST(ret, *location, 0);
8588 }
8589
8590 if (arguments != NULL) {
8591 PM_COMPILE_NOT_POPPED((const pm_node_t *) arguments);
8592 }
8593 else {
8594 PUSH_INSN(ret, *location, putnil);
8595 }
8596
8597 if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
8598 pm_add_ensure_iseq(ret, iseq, 1, scope_node);
8599 PUSH_TRACE(ret, RUBY_EVENT_RETURN);
8600 PUSH_INSN(ret, *location, leave);
8601 PUSH_ADJUST_RESTORE(ret, splabel);
8602 if (!popped) PUSH_INSN(ret, *location, putnil);
8603 }
8604 else {
8605 PUSH_INSN1(ret, *location, throw, INT2FIX(TAG_RETURN));
8606 if (popped) PUSH_INSN(ret, *location, pop);
8607 }
8608}
8609
8610static inline void
8611pm_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)
8612{
8613 DECL_ANCHOR(args);
8614
8615 LABEL *retry_label = NEW_LABEL(location->line);
8616 LABEL *retry_end_l = NEW_LABEL(location->line);
8617
8618 const rb_iseq_t *previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
8619 const rb_iseq_t *current_block;
8620 ISEQ_COMPILE_DATA(iseq)->current_block = current_block = NULL;
8621
8622 PUSH_LABEL(ret, retry_label);
8623 PUSH_INSN(ret, *location, putself);
8624
8625 int flags = 0;
8626 struct rb_callinfo_kwarg *keywords = NULL;
8627 int argc = pm_setup_args(node->arguments, node->block, &flags, &keywords, iseq, ret, scope_node, location);
8628 bool is_forwardable = (node->arguments != NULL) && PM_NODE_FLAG_P(node->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING);
8629 flags |= VM_CALL_SUPER | VM_CALL_FCALL;
8630
8631 if (node->block && PM_NODE_TYPE_P(node->block, PM_BLOCK_NODE)) {
8632 pm_scope_node_t next_scope_node;
8633 pm_scope_node_init(node->block, &next_scope_node, scope_node);
8634
8635 ISEQ_COMPILE_DATA(iseq)->current_block = current_block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location->line);
8636 pm_scope_node_destroy(&next_scope_node);
8637 }
8638
8639 if (!node->block) {
8640 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
8641 }
8642
8643 if ((flags & VM_CALL_ARGS_BLOCKARG) && (flags & VM_CALL_KW_SPLAT) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
8644 PUSH_INSN(args, *location, splatkw);
8645 }
8646
8647 PUSH_SEQ(ret, args);
8648 if (is_forwardable && ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
8649 flags |= VM_CALL_FORWARDING;
8650
8651 {
8652 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, current_block != NULL);
8653 PUSH_INSN2(ret, *location, invokesuperforward, callinfo, current_block);
8654 }
8655 }
8656 else {
8657 {
8658 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, current_block != NULL);
8659 PUSH_INSN2(ret, *location, invokesuper, callinfo, current_block);
8660 }
8661
8662 }
8663
8664 pm_compile_retry_end_label(iseq, ret, retry_end_l);
8665
8666 if (popped) PUSH_INSN(ret, *location, pop);
8667 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
8668 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, current_block, retry_end_l);
8669}
8670
8671static inline void
8672pm_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)
8673{
8674 switch (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->type) {
8675 case ISEQ_TYPE_TOP:
8676 case ISEQ_TYPE_MAIN:
8677 case ISEQ_TYPE_CLASS:
8678 COMPILE_ERROR(iseq, location->line, "Invalid yield");
8679 return;
8680 default: /* valid */;
8681 }
8682
8683 int argc = 0;
8684 int flags = 0;
8685 struct rb_callinfo_kwarg *keywords = NULL;
8686
8687 if (node->arguments) {
8688 argc = pm_setup_args(node->arguments, NULL, &flags, &keywords, iseq, ret, scope_node, location);
8689 }
8690
8691 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, FALSE);
8692 PUSH_INSN1(ret, *location, invokeblock, callinfo);
8693
8694 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
8695 if (popped) PUSH_INSN(ret, *location, pop);
8696
8697 int level = 0;
8698 for (const rb_iseq_t *tmp_iseq = iseq; tmp_iseq != ISEQ_BODY(iseq)->local_iseq; level++) {
8699 tmp_iseq = ISEQ_BODY(tmp_iseq)->parent_iseq;
8700 }
8701
8702 if (level > 0) access_outer_variables(iseq, level, rb_intern("yield"), true);
8703}
8704
8715static void
8716pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8717{
8718 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
8719 int lineno = (int) location.line;
8720
8721 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)) {
8722 // If this node is a begin node and it has empty statements and also
8723 // has a rescue clause, then the other parser considers it as
8724 // starting on the same line as the rescue, as opposed to the
8725 // location of the begin keyword. We replicate that behavior here.
8726 lineno = (int) PM_NODE_START_LINE_COLUMN(((const pm_begin_node_t *) node)->rescue_clause).line;
8727 }
8728
8729 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_NEWLINE) && ISEQ_COMPILE_DATA(iseq)->last_line != lineno) {
8730 // If this node has the newline flag set and it is on a new line
8731 // from the previous nodes that have been compiled for this ISEQ,
8732 // then we need to emit a newline event.
8733 int event = RUBY_EVENT_LINE;
8734
8735 ISEQ_COMPILE_DATA(iseq)->last_line = lineno;
8736 if (lineno > 0 && ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
8737 event |= RUBY_EVENT_COVERAGE_LINE;
8738 }
8739 PUSH_TRACE(ret, event);
8740 }
8741
8742 switch (PM_NODE_TYPE(node)) {
8743 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
8744 // alias $foo $bar
8745 // ^^^^^^^^^^^^^^^
8746 pm_compile_alias_global_variable_node(iseq, (const pm_alias_global_variable_node_t *) node, &location, ret, popped, scope_node);
8747 return;
8748 case PM_ALIAS_METHOD_NODE:
8749 // alias foo bar
8750 // ^^^^^^^^^^^^^
8751 pm_compile_alias_method_node(iseq, (const pm_alias_method_node_t *) node, &location, ret, popped, scope_node);
8752 return;
8753 case PM_AND_NODE:
8754 // a and b
8755 // ^^^^^^^
8756 pm_compile_and_node(iseq, (const pm_and_node_t *) node, &location, ret, popped, scope_node);
8757 return;
8758 case PM_ARGUMENTS_NODE: {
8759 // break foo
8760 // ^^^
8761 //
8762 // These are ArgumentsNodes that are not compiled directly by their
8763 // parent call nodes, used in the cases of NextNodes, ReturnNodes, and
8764 // BreakNodes. They can create an array like ArrayNode.
8765 const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node;
8766 const pm_node_list_t *elements = &cast->arguments;
8767
8768 if (elements->size == 1) {
8769 // If we are only returning a single element through one of the jump
8770 // nodes, then we will only compile that node directly.
8771 PM_COMPILE(elements->nodes[0]);
8772 }
8773 else {
8774 pm_compile_array_node(iseq, (const pm_node_t *) cast, elements, &location, ret, popped, scope_node);
8775 }
8776 return;
8777 }
8778 case PM_ARRAY_NODE: {
8779 // [foo, bar, baz]
8780 // ^^^^^^^^^^^^^^^
8781 const pm_array_node_t *cast = (const pm_array_node_t *) node;
8782 pm_compile_array_node(iseq, (const pm_node_t *) cast, &cast->elements, &location, ret, popped, scope_node);
8783 return;
8784 }
8785 case PM_ASSOC_NODE: {
8786 // { foo: 1 }
8787 // ^^^^^^
8788 //
8789 // foo(bar: 1)
8790 // ^^^^^^
8791 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node;
8792
8793 PM_COMPILE(cast->key);
8794 PM_COMPILE(cast->value);
8795
8796 return;
8797 }
8798 case PM_ASSOC_SPLAT_NODE: {
8799 // { **foo }
8800 // ^^^^^
8801 //
8802 // def foo(**); bar(**); end
8803 // ^^
8804 const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node;
8805
8806 if (cast->value != NULL) {
8807 PM_COMPILE(cast->value);
8808 }
8809 else if (!popped) {
8810 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_POW, 0);
8811 PUSH_GETLOCAL(ret, location, index.index, index.level);
8812 }
8813
8814 return;
8815 }
8816 case PM_BACK_REFERENCE_READ_NODE: {
8817 // $+
8818 // ^^
8819 if (!popped) {
8821 VALUE backref = pm_compile_back_reference_ref(scope_node, cast);
8822
8823 PUSH_INSN2(ret, location, getspecial, INT2FIX(1), backref);
8824 }
8825 return;
8826 }
8827 case PM_BEGIN_NODE: {
8828 // begin end
8829 // ^^^^^^^^^
8830 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
8831
8832 if (cast->ensure_clause) {
8833 // Compiling the ensure clause will compile the rescue clause (if
8834 // there is one), which will compile the begin statements.
8835 pm_compile_ensure(iseq, cast, &location, ret, popped, scope_node);
8836 }
8837 else if (cast->rescue_clause) {
8838 // Compiling rescue will compile begin statements (if applicable).
8839 pm_compile_rescue(iseq, cast, &location, ret, popped, scope_node);
8840 }
8841 else {
8842 // If there is neither ensure or rescue, the just compile the
8843 // statements.
8844 if (cast->statements != NULL) {
8845 PM_COMPILE((const pm_node_t *) cast->statements);
8846 }
8847 else if (!popped) {
8848 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
8849 }
8850 }
8851 return;
8852 }
8853 case PM_BLOCK_ARGUMENT_NODE: {
8854 // foo(&bar)
8855 // ^^^^
8856 const pm_block_argument_node_t *cast = (const pm_block_argument_node_t *) node;
8857
8858 if (cast->expression != NULL) {
8859 PM_COMPILE(cast->expression);
8860 }
8861 else {
8862 // If there's no expression, this must be block forwarding.
8863 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_AND, 0);
8864 PUSH_INSN2(ret, location, getblockparamproxy, INT2FIX(local_index.index + VM_ENV_DATA_SIZE - 1), INT2FIX(local_index.level));
8865 }
8866 return;
8867 }
8868 case PM_BREAK_NODE:
8869 // break
8870 // ^^^^^
8871 //
8872 // break foo
8873 // ^^^^^^^^^
8874 pm_compile_break_node(iseq, (const pm_break_node_t *) node, &location, ret, popped, scope_node);
8875 return;
8876 case PM_CALL_NODE:
8877 // foo
8878 // ^^^
8879 //
8880 // foo.bar
8881 // ^^^^^^^
8882 //
8883 // foo.bar() {}
8884 // ^^^^^^^^^^^^
8885 pm_compile_call_node(iseq, (const pm_call_node_t *) node, ret, popped, scope_node);
8886 return;
8887 case PM_CALL_AND_WRITE_NODE: {
8888 // foo.bar &&= baz
8889 // ^^^^^^^^^^^^^^^
8890 const pm_call_and_write_node_t *cast = (const pm_call_and_write_node_t *) node;
8891 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);
8892 return;
8893 }
8894 case PM_CALL_OR_WRITE_NODE: {
8895 // foo.bar ||= baz
8896 // ^^^^^^^^^^^^^^^
8897 const pm_call_or_write_node_t *cast = (const pm_call_or_write_node_t *) node;
8898 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);
8899 return;
8900 }
8901 case PM_CALL_OPERATOR_WRITE_NODE:
8902 // foo.bar += baz
8903 // ^^^^^^^^^^^^^^^
8904 //
8905 // Call operator writes occur when you have a call node on the left-hand
8906 // side of a write operator that is not `=`. As an example,
8907 // `foo.bar *= 1`. This breaks down to caching the receiver on the
8908 // stack and then performing three method calls, one to read the value,
8909 // one to compute the result, and one to write the result back to the
8910 // receiver.
8911 pm_compile_call_operator_write_node(iseq, (const pm_call_operator_write_node_t *) node, &location, ret, popped, scope_node);
8912 return;
8913 case PM_CASE_NODE:
8914 // case foo; when bar; end
8915 // ^^^^^^^^^^^^^^^^^^^^^^^
8916 pm_compile_case_node(iseq, (const pm_case_node_t *) node, &location, ret, popped, scope_node);
8917 return;
8918 case PM_CASE_MATCH_NODE:
8919 // case foo; in bar; end
8920 // ^^^^^^^^^^^^^^^^^^^^^
8921 //
8922 // If you use the `case` keyword to create a case match node, it will
8923 // match against all of the `in` clauses until it finds one that
8924 // matches. If it doesn't find one, it can optionally fall back to an
8925 // `else` clause. If none is present and a match wasn't found, it will
8926 // raise an appropriate error.
8927 pm_compile_case_match_node(iseq, (const pm_case_match_node_t *) node, &location, ret, popped, scope_node);
8928 return;
8929 case PM_CLASS_NODE: {
8930 // class Foo; end
8931 // ^^^^^^^^^^^^^^
8932 const pm_class_node_t *cast = (const pm_class_node_t *) node;
8933
8934 ID class_id = pm_constant_id_lookup(scope_node, cast->name);
8935 VALUE class_name = rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(class_id)));
8936
8937 pm_scope_node_t next_scope_node;
8938 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
8939
8940 const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(&next_scope_node, class_name, ISEQ_TYPE_CLASS, location.line);
8941 pm_scope_node_destroy(&next_scope_node);
8942
8943 // TODO: Once we merge constant path nodes correctly, fix this flag
8944 const int flags = VM_DEFINECLASS_TYPE_CLASS |
8945 (cast->superclass ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
8946 pm_compile_class_path(iseq, cast->constant_path, &location, ret, false, scope_node);
8947
8948 if (cast->superclass) {
8949 PM_COMPILE_NOT_POPPED(cast->superclass);
8950 }
8951 else {
8952 PUSH_INSN(ret, location, putnil);
8953 }
8954
8955 {
8956 VALUE operand = ID2SYM(class_id);
8957 PUSH_INSN3(ret, location, defineclass, operand, class_iseq, INT2FIX(flags));
8958 }
8959 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq);
8960
8961 if (popped) PUSH_INSN(ret, location, pop);
8962 return;
8963 }
8964 case PM_CLASS_VARIABLE_AND_WRITE_NODE: {
8965 // @@foo &&= bar
8966 // ^^^^^^^^^^^^^
8968 LABEL *end_label = NEW_LABEL(location.line);
8969
8970 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
8971 VALUE name = ID2SYM(name_id);
8972
8973 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
8974 if (!popped) PUSH_INSN(ret, location, dup);
8975
8976 PUSH_INSNL(ret, location, branchunless, end_label);
8977 if (!popped) PUSH_INSN(ret, location, pop);
8978
8979 PM_COMPILE_NOT_POPPED(cast->value);
8980 if (!popped) PUSH_INSN(ret, location, dup);
8981
8982 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
8983 PUSH_LABEL(ret, end_label);
8984
8985 return;
8986 }
8987 case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: {
8988 // @@foo += bar
8989 // ^^^^^^^^^^^^
8991
8992 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
8993 VALUE name = ID2SYM(name_id);
8994
8995 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
8996 PM_COMPILE_NOT_POPPED(cast->value);
8997
8998 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
8999 int flags = VM_CALL_ARGS_SIMPLE;
9000 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
9001
9002 if (!popped) PUSH_INSN(ret, location, dup);
9003 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
9004
9005 return;
9006 }
9007 case PM_CLASS_VARIABLE_OR_WRITE_NODE: {
9008 // @@foo ||= bar
9009 // ^^^^^^^^^^^^^
9011 LABEL *end_label = NEW_LABEL(location.line);
9012 LABEL *start_label = NEW_LABEL(location.line);
9013
9014 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9015 VALUE name = ID2SYM(name_id);
9016
9017 PUSH_INSN(ret, location, putnil);
9018 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CVAR), name, Qtrue);
9019 PUSH_INSNL(ret, location, branchunless, start_label);
9020
9021 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
9022 if (!popped) PUSH_INSN(ret, location, dup);
9023
9024 PUSH_INSNL(ret, location, branchif, end_label);
9025 if (!popped) PUSH_INSN(ret, location, pop);
9026
9027 PUSH_LABEL(ret, start_label);
9028 PM_COMPILE_NOT_POPPED(cast->value);
9029 if (!popped) PUSH_INSN(ret, location, dup);
9030
9031 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
9032 PUSH_LABEL(ret, end_label);
9033
9034 return;
9035 }
9036 case PM_CLASS_VARIABLE_READ_NODE: {
9037 // @@foo
9038 // ^^^^^
9039 if (!popped) {
9041 ID name = pm_constant_id_lookup(scope_node, cast->name);
9042 PUSH_INSN2(ret, location, getclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
9043 }
9044 return;
9045 }
9046 case PM_CLASS_VARIABLE_WRITE_NODE: {
9047 // @@foo = 1
9048 // ^^^^^^^^^
9050 PM_COMPILE_NOT_POPPED(cast->value);
9051 if (!popped) PUSH_INSN(ret, location, dup);
9052
9053 ID name = pm_constant_id_lookup(scope_node, cast->name);
9054 PUSH_INSN2(ret, location, setclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
9055
9056 return;
9057 }
9058 case PM_CONSTANT_PATH_NODE: {
9059 // Foo::Bar
9060 // ^^^^^^^^
9061 VALUE parts;
9062
9063 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache && ((parts = pm_constant_path_parts(node, scope_node)) != Qnil)) {
9064 ISEQ_BODY(iseq)->ic_size++;
9065 RB_OBJ_SET_SHAREABLE(parts);
9066 PUSH_INSN1(ret, location, opt_getconstant_path, parts);
9067 }
9068 else {
9069 DECL_ANCHOR(prefix);
9070 DECL_ANCHOR(body);
9071
9072 pm_compile_constant_path(iseq, node, prefix, body, popped, scope_node);
9073 if (LIST_INSN_SIZE_ZERO(prefix)) {
9074 PUSH_INSN(ret, location, putnil);
9075 }
9076 else {
9077 PUSH_SEQ(ret, prefix);
9078 }
9079
9080 PUSH_SEQ(ret, body);
9081 }
9082
9083 if (popped) PUSH_INSN(ret, location, pop);
9084 return;
9085 }
9086 case PM_CONSTANT_PATH_AND_WRITE_NODE: {
9087 // Foo::Bar &&= baz
9088 // ^^^^^^^^^^^^^^^^
9090 pm_compile_constant_path_and_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9091 return;
9092 }
9093 case PM_CONSTANT_PATH_OR_WRITE_NODE: {
9094 // Foo::Bar ||= baz
9095 // ^^^^^^^^^^^^^^^^
9097 pm_compile_constant_path_or_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9098 return;
9099 }
9100 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: {
9101 // Foo::Bar += baz
9102 // ^^^^^^^^^^^^^^^
9104 pm_compile_constant_path_operator_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9105 return;
9106 }
9107 case PM_CONSTANT_PATH_WRITE_NODE: {
9108 // Foo::Bar = 1
9109 // ^^^^^^^^^^^^
9111 pm_compile_constant_path_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9112 return;
9113 }
9114 case PM_CONSTANT_READ_NODE: {
9115 // Foo
9116 // ^^^
9117 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
9118 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9119
9120 pm_compile_constant_read(iseq, name, &cast->base.location, location.node_id, ret, scope_node);
9121 if (popped) PUSH_INSN(ret, location, pop);
9122
9123 return;
9124 }
9125 case PM_CONSTANT_AND_WRITE_NODE: {
9126 // Foo &&= bar
9127 // ^^^^^^^^^^^
9129 pm_compile_constant_and_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9130 return;
9131 }
9132 case PM_CONSTANT_OR_WRITE_NODE: {
9133 // Foo ||= bar
9134 // ^^^^^^^^^^^
9135 const pm_constant_or_write_node_t *cast = (const pm_constant_or_write_node_t *) node;
9136 pm_compile_constant_or_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9137 return;
9138 }
9139 case PM_CONSTANT_OPERATOR_WRITE_NODE: {
9140 // Foo += bar
9141 // ^^^^^^^^^^
9143 pm_compile_constant_operator_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9144 return;
9145 }
9146 case PM_CONSTANT_WRITE_NODE: {
9147 // Foo = 1
9148 // ^^^^^^^
9149 const pm_constant_write_node_t *cast = (const pm_constant_write_node_t *) node;
9150 pm_compile_constant_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9151 return;
9152 }
9153 case PM_DEF_NODE: {
9154 // def foo; end
9155 // ^^^^^^^^^^^^
9156 //
9157 // def self.foo; end
9158 // ^^^^^^^^^^^^^^^^^
9159 const pm_def_node_t *cast = (const pm_def_node_t *) node;
9160 ID method_name = pm_constant_id_lookup(scope_node, cast->name);
9161
9162 pm_scope_node_t next_scope_node;
9163 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9164
9165 rb_iseq_t *method_iseq = NEW_ISEQ(&next_scope_node, rb_id2str(method_name), ISEQ_TYPE_METHOD, location.line);
9166 pm_scope_node_destroy(&next_scope_node);
9167
9168 if (cast->receiver) {
9169 PM_COMPILE_NOT_POPPED(cast->receiver);
9170 PUSH_INSN2(ret, location, definesmethod, ID2SYM(method_name), method_iseq);
9171 }
9172 else {
9173 PUSH_INSN2(ret, location, definemethod, ID2SYM(method_name), method_iseq);
9174 }
9175 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) method_iseq);
9176
9177 if (!popped) {
9178 PUSH_INSN1(ret, location, putobject, ID2SYM(method_name));
9179 }
9180
9181 return;
9182 }
9183 case PM_DEFINED_NODE: {
9184 // defined?(a)
9185 // ^^^^^^^^^^^
9186 const pm_defined_node_t *cast = (const pm_defined_node_t *) node;
9187 pm_compile_defined_expr(iseq, cast->value, &location, ret, popped, scope_node, false);
9188 return;
9189 }
9190 case PM_EMBEDDED_STATEMENTS_NODE: {
9191 // "foo #{bar}"
9192 // ^^^^^^
9194
9195 if (cast->statements != NULL) {
9196 PM_COMPILE((const pm_node_t *) (cast->statements));
9197 }
9198 else {
9199 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
9200 }
9201
9202 if (popped) PUSH_INSN(ret, location, pop);
9203 return;
9204 }
9205 case PM_EMBEDDED_VARIABLE_NODE: {
9206 // "foo #@bar"
9207 // ^^^^^
9208 const pm_embedded_variable_node_t *cast = (const pm_embedded_variable_node_t *) node;
9209 PM_COMPILE(cast->variable);
9210 return;
9211 }
9212 case PM_FALSE_NODE: {
9213 // false
9214 // ^^^^^
9215 if (!popped) {
9216 PUSH_INSN1(ret, location, putobject, Qfalse);
9217 }
9218 return;
9219 }
9220 case PM_ENSURE_NODE: {
9221 const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node;
9222
9223 if (cast->statements != NULL) {
9224 PM_COMPILE((const pm_node_t *) cast->statements);
9225 }
9226
9227 return;
9228 }
9229 case PM_ELSE_NODE: {
9230 // if foo then bar else baz end
9231 // ^^^^^^^^^^^^
9232 const pm_else_node_t *cast = (const pm_else_node_t *) node;
9233
9234 if (cast->statements != NULL) {
9235 PM_COMPILE((const pm_node_t *) cast->statements);
9236 }
9237 else if (!popped) {
9238 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
9239 }
9240
9241 return;
9242 }
9243 case PM_FLIP_FLOP_NODE: {
9244 // if foo .. bar; end
9245 // ^^^^^^^^^^
9246 const pm_flip_flop_node_t *cast = (const pm_flip_flop_node_t *) node;
9247
9248 LABEL *final_label = NEW_LABEL(location.line);
9249 LABEL *then_label = NEW_LABEL(location.line);
9250 LABEL *else_label = NEW_LABEL(location.line);
9251
9252 pm_compile_flip_flop(cast, else_label, then_label, iseq, location.line, ret, popped, scope_node);
9253
9254 PUSH_LABEL(ret, then_label);
9255 PUSH_INSN1(ret, location, putobject, Qtrue);
9256 PUSH_INSNL(ret, location, jump, final_label);
9257 PUSH_LABEL(ret, else_label);
9258 PUSH_INSN1(ret, location, putobject, Qfalse);
9259 PUSH_LABEL(ret, final_label);
9260
9261 return;
9262 }
9263 case PM_FLOAT_NODE: {
9264 // 1.0
9265 // ^^^
9266 if (!popped) {
9267 VALUE operand = parse_float((const pm_float_node_t *) node);
9268 PUSH_INSN1(ret, location, putobject, operand);
9269 }
9270 return;
9271 }
9272 case PM_FOR_NODE: {
9273 // for foo in bar do end
9274 // ^^^^^^^^^^^^^^^^^^^^^
9275 const pm_for_node_t *cast = (const pm_for_node_t *) node;
9276
9277 LABEL *retry_label = NEW_LABEL(location.line);
9278 LABEL *retry_end_l = NEW_LABEL(location.line);
9279
9280 // First, compile the collection that we're going to be iterating over.
9281 PUSH_LABEL(ret, retry_label);
9282 PM_COMPILE_NOT_POPPED(cast->collection);
9283
9284 // Next, create the new scope that is going to contain the block that
9285 // will be passed to the each method.
9286 pm_scope_node_t next_scope_node;
9287 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9288
9289 const rb_iseq_t *child_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location.line);
9290 pm_scope_node_destroy(&next_scope_node);
9291
9292 const rb_iseq_t *prev_block = ISEQ_COMPILE_DATA(iseq)->current_block;
9293 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
9294
9295 // Now, create the method call to each that will be used to iterate over
9296 // the collection, and pass the newly created iseq as the block.
9297 PUSH_SEND_WITH_BLOCK(ret, location, idEach, INT2FIX(0), child_iseq);
9298 pm_compile_retry_end_label(iseq, ret, retry_end_l);
9299
9300 if (popped) PUSH_INSN(ret, location, pop);
9301 ISEQ_COMPILE_DATA(iseq)->current_block = prev_block;
9302 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
9303 return;
9304 }
9305 case PM_FORWARDING_ARGUMENTS_NODE:
9306 rb_bug("Cannot compile a ForwardingArgumentsNode directly\n");
9307 return;
9308 case PM_FORWARDING_SUPER_NODE:
9309 // super
9310 // ^^^^^
9311 //
9312 // super {}
9313 // ^^^^^^^^
9314 pm_compile_forwarding_super_node(iseq, (const pm_forwarding_super_node_t *) node, &location, ret, popped, scope_node);
9315 return;
9316 case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: {
9317 // $foo &&= bar
9318 // ^^^^^^^^^^^^
9320 LABEL *end_label = NEW_LABEL(location.line);
9321
9322 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9323 PUSH_INSN1(ret, location, getglobal, name);
9324 if (!popped) PUSH_INSN(ret, location, dup);
9325
9326 PUSH_INSNL(ret, location, branchunless, end_label);
9327 if (!popped) PUSH_INSN(ret, location, pop);
9328
9329 PM_COMPILE_NOT_POPPED(cast->value);
9330 if (!popped) PUSH_INSN(ret, location, dup);
9331
9332 PUSH_INSN1(ret, location, setglobal, name);
9333 PUSH_LABEL(ret, end_label);
9334
9335 return;
9336 }
9337 case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: {
9338 // $foo += bar
9339 // ^^^^^^^^^^^
9341
9342 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9343 PUSH_INSN1(ret, location, getglobal, name);
9344 PM_COMPILE_NOT_POPPED(cast->value);
9345
9346 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9347 int flags = VM_CALL_ARGS_SIMPLE;
9348 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
9349
9350 if (!popped) PUSH_INSN(ret, location, dup);
9351 PUSH_INSN1(ret, location, setglobal, name);
9352
9353 return;
9354 }
9355 case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: {
9356 // $foo ||= bar
9357 // ^^^^^^^^^^^^
9359 LABEL *set_label = NEW_LABEL(location.line);
9360 LABEL *end_label = NEW_LABEL(location.line);
9361
9362 PUSH_INSN(ret, location, putnil);
9363 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9364
9365 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_GVAR), name, Qtrue);
9366 PUSH_INSNL(ret, location, branchunless, set_label);
9367
9368 PUSH_INSN1(ret, location, getglobal, name);
9369 if (!popped) PUSH_INSN(ret, location, dup);
9370
9371 PUSH_INSNL(ret, location, branchif, end_label);
9372 if (!popped) PUSH_INSN(ret, location, pop);
9373
9374 PUSH_LABEL(ret, set_label);
9375 PM_COMPILE_NOT_POPPED(cast->value);
9376 if (!popped) PUSH_INSN(ret, location, dup);
9377
9378 PUSH_INSN1(ret, location, setglobal, name);
9379 PUSH_LABEL(ret, end_label);
9380
9381 return;
9382 }
9383 case PM_GLOBAL_VARIABLE_READ_NODE: {
9384 // $foo
9385 // ^^^^
9387 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9388
9389 PUSH_INSN1(ret, location, getglobal, name);
9390 if (popped) PUSH_INSN(ret, location, pop);
9391
9392 return;
9393 }
9394 case PM_GLOBAL_VARIABLE_WRITE_NODE: {
9395 // $foo = 1
9396 // ^^^^^^^^
9398 PM_COMPILE_NOT_POPPED(cast->value);
9399 if (!popped) PUSH_INSN(ret, location, dup);
9400
9401 ID name = pm_constant_id_lookup(scope_node, cast->name);
9402 PUSH_INSN1(ret, location, setglobal, ID2SYM(name));
9403
9404 return;
9405 }
9406 case PM_HASH_NODE: {
9407 // {}
9408 // ^^
9409 //
9410 // If every node in the hash is static, then we can compile the entire
9411 // hash now instead of later.
9412 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9413 // We're only going to compile this node if it's not popped. If it
9414 // is popped, then we know we don't need to do anything since it's
9415 // statically known.
9416 if (!popped) {
9417 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
9418
9419 if (cast->elements.size == 0) {
9420 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
9421 }
9422 else {
9423 VALUE value = pm_static_literal_value(iseq, node, scope_node);
9424 PUSH_INSN1(ret, location, duphash, value);
9425 RB_OBJ_WRITTEN(iseq, Qundef, value);
9426 }
9427 }
9428 }
9429 else {
9430 // Here since we know there are possible side-effects inside the
9431 // hash contents, we're going to build it entirely at runtime. We'll
9432 // do this by pushing all of the key-value pairs onto the stack and
9433 // then combining them with newhash.
9434 //
9435 // If this hash is popped, then this serves only to ensure we enact
9436 // all side-effects (like method calls) that are contained within
9437 // the hash contents.
9438 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
9439 const pm_node_list_t *elements = &cast->elements;
9440
9441 if (popped) {
9442 // If this hash is popped, then we can iterate through each
9443 // element and compile it. The result of each compilation will
9444 // only include the side effects of the element itself.
9445 for (size_t index = 0; index < elements->size; index++) {
9446 PM_COMPILE_POPPED(elements->nodes[index]);
9447 }
9448 }
9449 else {
9450 pm_compile_hash_elements(iseq, node, elements, 0, Qundef, false, ret, scope_node);
9451 }
9452 }
9453
9454 return;
9455 }
9456 case PM_IF_NODE: {
9457 // if foo then bar end
9458 // ^^^^^^^^^^^^^^^^^^^
9459 //
9460 // bar if foo
9461 // ^^^^^^^^^^
9462 //
9463 // foo ? bar : baz
9464 // ^^^^^^^^^^^^^^^
9465 const pm_if_node_t *cast = (const pm_if_node_t *) node;
9466 pm_compile_conditional(iseq, &location, PM_IF_NODE, (const pm_node_t *) cast, cast->statements, cast->subsequent, cast->predicate, ret, popped, scope_node);
9467 return;
9468 }
9469 case PM_IMAGINARY_NODE: {
9470 // 1i
9471 // ^^
9472 if (!popped) {
9473 VALUE operand = parse_imaginary((const pm_imaginary_node_t *) node);
9474 PUSH_INSN1(ret, location, putobject, operand);
9475 }
9476 return;
9477 }
9478 case PM_IMPLICIT_NODE: {
9479 // Implicit nodes mark places in the syntax tree where explicit syntax
9480 // was omitted, but implied. For example,
9481 //
9482 // { foo: }
9483 //
9484 // In this case a method call/local variable read is implied by virtue
9485 // of the missing value. To compile these nodes, we simply compile the
9486 // value that is implied, which is helpfully supplied by the parser.
9487 const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node;
9488 PM_COMPILE(cast->value);
9489 return;
9490 }
9491 case PM_IN_NODE: {
9492 // In nodes are handled by the case match node directly, so we should
9493 // never end up hitting them through this path.
9494 rb_bug("Should not ever enter an in node directly");
9495 return;
9496 }
9497 case PM_INDEX_OPERATOR_WRITE_NODE: {
9498 // foo[bar] += baz
9499 // ^^^^^^^^^^^^^^^
9501 pm_compile_index_operator_write_node(iseq, cast, &location, ret, popped, scope_node);
9502 return;
9503 }
9504 case PM_INDEX_AND_WRITE_NODE: {
9505 // foo[bar] &&= baz
9506 // ^^^^^^^^^^^^^^^^
9507 const pm_index_and_write_node_t *cast = (const pm_index_and_write_node_t *) node;
9508 pm_compile_index_control_flow_write_node(iseq, node, cast->receiver, cast->arguments, cast->block, cast->value, &location, ret, popped, scope_node);
9509 return;
9510 }
9511 case PM_INDEX_OR_WRITE_NODE: {
9512 // foo[bar] ||= baz
9513 // ^^^^^^^^^^^^^^^^
9514 const pm_index_or_write_node_t *cast = (const pm_index_or_write_node_t *) node;
9515 pm_compile_index_control_flow_write_node(iseq, node, cast->receiver, cast->arguments, cast->block, cast->value, &location, ret, popped, scope_node);
9516 return;
9517 }
9518 case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: {
9519 // @foo &&= bar
9520 // ^^^^^^^^^^^^
9522 LABEL *end_label = NEW_LABEL(location.line);
9523
9524 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9525 VALUE name = ID2SYM(name_id);
9526
9527 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9528 if (!popped) PUSH_INSN(ret, location, dup);
9529
9530 PUSH_INSNL(ret, location, branchunless, end_label);
9531 if (!popped) PUSH_INSN(ret, location, pop);
9532
9533 PM_COMPILE_NOT_POPPED(cast->value);
9534 if (!popped) PUSH_INSN(ret, location, dup);
9535
9536 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9537 PUSH_LABEL(ret, end_label);
9538
9539 return;
9540 }
9541 case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: {
9542 // @foo += bar
9543 // ^^^^^^^^^^^
9545
9546 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9547 VALUE name = ID2SYM(name_id);
9548
9549 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9550 PM_COMPILE_NOT_POPPED(cast->value);
9551
9552 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9553 int flags = VM_CALL_ARGS_SIMPLE;
9554 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
9555
9556 if (!popped) PUSH_INSN(ret, location, dup);
9557 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9558
9559 return;
9560 }
9561 case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: {
9562 // @foo ||= bar
9563 // ^^^^^^^^^^^^
9565 LABEL *end_label = NEW_LABEL(location.line);
9566
9567 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9568 VALUE name = ID2SYM(name_id);
9569
9570 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9571 if (!popped) PUSH_INSN(ret, location, dup);
9572
9573 PUSH_INSNL(ret, location, branchif, end_label);
9574 if (!popped) PUSH_INSN(ret, location, pop);
9575
9576 PM_COMPILE_NOT_POPPED(cast->value);
9577 if (!popped) PUSH_INSN(ret, location, dup);
9578
9579 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9580 PUSH_LABEL(ret, end_label);
9581
9582 return;
9583 }
9584 case PM_INSTANCE_VARIABLE_READ_NODE: {
9585 // @foo
9586 // ^^^^
9587 if (!popped) {
9589 ID name = pm_constant_id_lookup(scope_node, cast->name);
9590 PUSH_INSN2(ret, location, getinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
9591 }
9592 return;
9593 }
9594 case PM_INSTANCE_VARIABLE_WRITE_NODE: {
9595 // @foo = 1
9596 // ^^^^^^^^
9598 PM_COMPILE_NOT_POPPED(cast->value);
9599 if (!popped) PUSH_INSN(ret, location, dup);
9600
9601 ID name = pm_constant_id_lookup(scope_node, cast->name);
9602 PUSH_INSN2(ret, location, setinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
9603
9604 return;
9605 }
9606 case PM_INTEGER_NODE: {
9607 // 1
9608 // ^
9609 if (!popped) {
9610 VALUE operand = parse_integer((const pm_integer_node_t *) node);
9611 PUSH_INSN1(ret, location, putobject, operand);
9612 }
9613 return;
9614 }
9615 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: {
9616 // if /foo #{bar}/ then end
9617 // ^^^^^^^^^^^^
9618 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9619 if (!popped) {
9620 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9621 PUSH_INSN1(ret, location, putobject, regexp);
9622 }
9623 }
9624 else {
9625 pm_compile_regexp_dynamic(iseq, node, &((const pm_interpolated_match_last_line_node_t *) node)->parts, &location, ret, popped, scope_node);
9626 }
9627
9628 PUSH_INSN1(ret, location, getglobal, rb_id2sym(idLASTLINE));
9629 PUSH_SEND(ret, location, idEqTilde, INT2NUM(1));
9630 if (popped) PUSH_INSN(ret, location, pop);
9631
9632 return;
9633 }
9634 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
9635 // /foo #{bar}/
9636 // ^^^^^^^^^^^^
9637 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) {
9638 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
9639 const rb_iseq_t *block_iseq = NULL;
9640 int ise_index = ISEQ_BODY(iseq)->ise_size++;
9641
9642 pm_scope_node_t next_scope_node;
9643 pm_scope_node_init(node, &next_scope_node, scope_node);
9644
9645 block_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, location.line);
9646 pm_scope_node_destroy(&next_scope_node);
9647
9648 ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
9649 PUSH_INSN2(ret, location, once, block_iseq, INT2FIX(ise_index));
9650 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
9651
9652 if (popped) PUSH_INSN(ret, location, pop);
9653 return;
9654 }
9655
9656 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9657 if (!popped) {
9658 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9659 PUSH_INSN1(ret, location, putobject, regexp);
9660 }
9661 }
9662 else {
9663 pm_compile_regexp_dynamic(iseq, node, &((const pm_interpolated_regular_expression_node_t *) node)->parts, &location, ret, popped, scope_node);
9664 if (popped) PUSH_INSN(ret, location, pop);
9665 }
9666
9667 return;
9668 }
9669 case PM_INTERPOLATED_STRING_NODE: {
9670 // "foo #{bar}"
9671 // ^^^^^^^^^^^^
9672 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9673 if (!popped) {
9674 VALUE string = pm_static_literal_value(iseq, node, scope_node);
9675
9676 if (PM_NODE_FLAG_P(node, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN)) {
9677 PUSH_INSN1(ret, location, putobject, string);
9678 }
9679 else if (PM_NODE_FLAG_P(node, PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) {
9680 PUSH_INSN1(ret, location, putstring, string);
9681 }
9682 else {
9683 PUSH_INSN1(ret, location, putchilledstring, string);
9684 }
9685 }
9686 }
9687 else {
9689 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));
9690 if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9691 if (popped) PUSH_INSN(ret, location, pop);
9692 }
9693
9694 return;
9695 }
9696 case PM_INTERPOLATED_SYMBOL_NODE: {
9697 // :"foo #{bar}"
9698 // ^^^^^^^^^^^^^
9700 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL, false, false);
9701
9702 if (length > 1) {
9703 PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9704 }
9705
9706 if (!popped) {
9707 PUSH_INSN(ret, location, intern);
9708 }
9709 else {
9710 PUSH_INSN(ret, location, pop);
9711 }
9712
9713 return;
9714 }
9715 case PM_INTERPOLATED_X_STRING_NODE: {
9716 // `foo #{bar}`
9717 // ^^^^^^^^^^^^
9719
9720 PUSH_INSN(ret, location, putself);
9721
9722 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, false, scope_node, NULL, NULL, false, false);
9723 if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9724
9725 PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
9726 if (popped) PUSH_INSN(ret, location, pop);
9727
9728 return;
9729 }
9730 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
9731 // -> { it }
9732 // ^^
9733 if (!popped) {
9734 pm_scope_node_t *current_scope_node = scope_node;
9735 int level = 0;
9736
9737 while (current_scope_node) {
9738 if (current_scope_node->parameters && PM_NODE_TYPE_P(current_scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
9739 PUSH_GETLOCAL(ret, location, current_scope_node->local_table_for_iseq_size, level);
9740 return;
9741 }
9742
9743 current_scope_node = current_scope_node->previous;
9744 level++;
9745 }
9746 rb_bug("Local `it` does not exist");
9747 }
9748
9749 return;
9750 }
9751 case PM_KEYWORD_HASH_NODE: {
9752 // foo(bar: baz)
9753 // ^^^^^^^^
9754 const pm_keyword_hash_node_t *cast = (const pm_keyword_hash_node_t *) node;
9755 const pm_node_list_t *elements = &cast->elements;
9756
9757 const pm_node_t *element;
9758 PM_NODE_LIST_FOREACH(elements, index, element) {
9759 PM_COMPILE(element);
9760 }
9761
9762 if (!popped) PUSH_INSN1(ret, location, newhash, INT2FIX(elements->size * 2));
9763 return;
9764 }
9765 case PM_LAMBDA_NODE: {
9766 // -> {}
9767 // ^^^^^
9768 const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node;
9769
9770 pm_scope_node_t next_scope_node;
9771 pm_scope_node_init(node, &next_scope_node, scope_node);
9772
9773 int opening_lineno = pm_location_line_number_cached(&cast->opening_loc, scope_node);
9774 const rb_iseq_t *block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, opening_lineno);
9775 pm_scope_node_destroy(&next_scope_node);
9776
9777 VALUE argc = INT2FIX(0);
9778 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
9779 PUSH_CALL_WITH_BLOCK(ret, location, idLambda, argc, block);
9780 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) block);
9781
9782 if (popped) PUSH_INSN(ret, location, pop);
9783 return;
9784 }
9785 case PM_LOCAL_VARIABLE_AND_WRITE_NODE: {
9786 // foo &&= bar
9787 // ^^^^^^^^^^^
9789 LABEL *end_label = NEW_LABEL(location.line);
9790
9791 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9792 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9793 if (!popped) PUSH_INSN(ret, location, dup);
9794
9795 PUSH_INSNL(ret, location, branchunless, end_label);
9796 if (!popped) PUSH_INSN(ret, location, pop);
9797
9798 PM_COMPILE_NOT_POPPED(cast->value);
9799 if (!popped) PUSH_INSN(ret, location, dup);
9800
9801 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9802 PUSH_LABEL(ret, end_label);
9803
9804 return;
9805 }
9806 case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: {
9807 // foo += bar
9808 // ^^^^^^^^^^
9810
9811 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9812 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9813
9814 PM_COMPILE_NOT_POPPED(cast->value);
9815
9816 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9817 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
9818
9819 if (!popped) PUSH_INSN(ret, location, dup);
9820 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9821
9822 return;
9823 }
9824 case PM_LOCAL_VARIABLE_OR_WRITE_NODE: {
9825 // foo ||= bar
9826 // ^^^^^^^^^^^
9828
9829 LABEL *set_label = NEW_LABEL(location.line);
9830 LABEL *end_label = NEW_LABEL(location.line);
9831
9832 PUSH_INSN1(ret, location, putobject, Qtrue);
9833 PUSH_INSNL(ret, location, branchunless, set_label);
9834
9835 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9836 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9837 if (!popped) PUSH_INSN(ret, location, dup);
9838
9839 PUSH_INSNL(ret, location, branchif, end_label);
9840 if (!popped) PUSH_INSN(ret, location, pop);
9841
9842 PUSH_LABEL(ret, set_label);
9843 PM_COMPILE_NOT_POPPED(cast->value);
9844 if (!popped) PUSH_INSN(ret, location, dup);
9845
9846 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9847 PUSH_LABEL(ret, end_label);
9848
9849 return;
9850 }
9851 case PM_LOCAL_VARIABLE_READ_NODE: {
9852 // foo
9853 // ^^^
9854 if (!popped) {
9856 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9857 PUSH_GETLOCAL(ret, location, index.index, index.level);
9858 }
9859
9860 return;
9861 }
9862 case PM_LOCAL_VARIABLE_WRITE_NODE: {
9863 // foo = 1
9864 // ^^^^^^^
9866 PM_COMPILE_NOT_POPPED(cast->value);
9867 if (!popped) PUSH_INSN(ret, location, dup);
9868
9869 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9870 PUSH_SETLOCAL(ret, location, index.index, index.level);
9871 return;
9872 }
9873 case PM_MATCH_LAST_LINE_NODE: {
9874 // if /foo/ then end
9875 // ^^^^^
9876 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9877
9878 PUSH_INSN1(ret, location, putobject, regexp);
9879 PUSH_INSN2(ret, location, getspecial, INT2FIX(0), INT2FIX(0));
9880 PUSH_SEND(ret, location, idEqTilde, INT2NUM(1));
9881 if (popped) PUSH_INSN(ret, location, pop);
9882
9883 return;
9884 }
9885 case PM_MATCH_PREDICATE_NODE: {
9886 // foo in bar
9887 // ^^^^^^^^^^
9888 const pm_match_predicate_node_t *cast = (const pm_match_predicate_node_t *) node;
9889
9890 // First, allocate some stack space for the cached return value of any
9891 // calls to #deconstruct.
9892 PUSH_INSN(ret, location, putnil);
9893
9894 // Next, compile the expression that we're going to match against.
9895 PM_COMPILE_NOT_POPPED(cast->value);
9896 PUSH_INSN(ret, location, dup);
9897
9898 // Now compile the pattern that is going to be used to match against the
9899 // expression.
9900 LABEL *matched_label = NEW_LABEL(location.line);
9901 LABEL *unmatched_label = NEW_LABEL(location.line);
9902 LABEL *done_label = NEW_LABEL(location.line);
9903 pm_compile_pattern(iseq, scope_node, cast->pattern, ret, matched_label, unmatched_label, false, true, 2);
9904
9905 // If the pattern did not match, then compile the necessary instructions
9906 // to handle pushing false onto the stack, then jump to the end.
9907 PUSH_LABEL(ret, unmatched_label);
9908 PUSH_INSN(ret, location, pop);
9909 PUSH_INSN(ret, location, pop);
9910
9911 if (!popped) PUSH_INSN1(ret, location, putobject, Qfalse);
9912 PUSH_INSNL(ret, location, jump, done_label);
9913 PUSH_INSN(ret, location, putnil);
9914
9915 // If the pattern did match, then compile the necessary instructions to
9916 // handle pushing true onto the stack, then jump to the end.
9917 PUSH_LABEL(ret, matched_label);
9918 PUSH_INSN1(ret, location, adjuststack, INT2FIX(2));
9919 if (!popped) PUSH_INSN1(ret, location, putobject, Qtrue);
9920 PUSH_INSNL(ret, location, jump, done_label);
9921
9922 PUSH_LABEL(ret, done_label);
9923 return;
9924 }
9925 case PM_MATCH_REQUIRED_NODE:
9926 // foo => bar
9927 // ^^^^^^^^^^
9928 //
9929 // A match required node represents pattern matching against a single
9930 // pattern using the => operator. For example,
9931 //
9932 // foo => bar
9933 //
9934 // This is somewhat analogous to compiling a case match statement with a
9935 // single pattern. In both cases, if the pattern fails it should
9936 // immediately raise an error.
9937 pm_compile_match_required_node(iseq, (const pm_match_required_node_t *) node, &location, ret, popped, scope_node);
9938 return;
9939 case PM_MATCH_WRITE_NODE:
9940 // /(?<foo>foo)/ =~ bar
9941 // ^^^^^^^^^^^^^^^^^^^^
9942 //
9943 // Match write nodes are specialized call nodes that have a regular
9944 // expression with valid named capture groups on the left, the =~
9945 // operator, and some value on the right. The nodes themselves simply
9946 // wrap the call with the local variable targets that will be written
9947 // when the call is executed.
9948 pm_compile_match_write_node(iseq, (const pm_match_write_node_t *) node, &location, ret, popped, scope_node);
9949 return;
9950 case PM_ERROR_RECOVERY_NODE:
9951 rb_bug("A pm_error_recovery_node_t should not exist in prism's AST.");
9952 return;
9953 case PM_MODULE_NODE: {
9954 // module Foo; end
9955 // ^^^^^^^^^^^^^^^
9956 const pm_module_node_t *cast = (const pm_module_node_t *) node;
9957
9958 ID module_id = pm_constant_id_lookup(scope_node, cast->name);
9959 VALUE module_name = rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(module_id)));
9960
9961 pm_scope_node_t next_scope_node;
9962 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9963
9964 const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(&next_scope_node, module_name, ISEQ_TYPE_CLASS, location.line);
9965 pm_scope_node_destroy(&next_scope_node);
9966
9967 const int flags = VM_DEFINECLASS_TYPE_MODULE | pm_compile_class_path(iseq, cast->constant_path, &location, ret, false, scope_node);
9968 PUSH_INSN(ret, location, putnil);
9969 PUSH_INSN3(ret, location, defineclass, ID2SYM(module_id), module_iseq, INT2FIX(flags));
9970 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) module_iseq);
9971
9972 if (popped) PUSH_INSN(ret, location, pop);
9973 return;
9974 }
9975 case PM_REQUIRED_PARAMETER_NODE: {
9976 // def foo(bar); end
9977 // ^^^
9979 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, 0);
9980
9981 PUSH_SETLOCAL(ret, location, index.index, index.level);
9982 return;
9983 }
9984 case PM_MULTI_WRITE_NODE: {
9985 // foo, bar = baz
9986 // ^^^^^^^^^^^^^^
9987 //
9988 // A multi write node represents writing to multiple values using an =
9989 // operator. Importantly these nodes are only parsed when the left-hand
9990 // side of the operator has multiple targets. The right-hand side of the
9991 // operator having multiple targets represents an implicit array
9992 // instead.
9993 const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node;
9994
9995 DECL_ANCHOR(writes);
9996 DECL_ANCHOR(cleanup);
9997
9998 pm_multi_target_state_t state = { 0 };
9999 state.position = popped ? 0 : 1;
10000 pm_compile_multi_target_node(iseq, node, ret, writes, cleanup, scope_node, &state);
10001
10002 PM_COMPILE_NOT_POPPED(cast->value);
10003 if (!popped) PUSH_INSN(ret, location, dup);
10004
10005 PUSH_SEQ(ret, writes);
10006 if (!popped && state.stack_size >= 1) {
10007 // Make sure the value on the right-hand side of the = operator is
10008 // being returned before we pop the parent expressions.
10009 PUSH_INSN1(ret, location, setn, INT2FIX(state.stack_size));
10010 }
10011
10012 // Now, we need to go back and modify the topn instructions in order to
10013 // ensure they can correctly retrieve the parent expressions.
10014 pm_multi_target_state_update(&state);
10015
10016 PUSH_SEQ(ret, cleanup);
10017 return;
10018 }
10019 case PM_NEXT_NODE:
10020 // next
10021 // ^^^^
10022 //
10023 // next foo
10024 // ^^^^^^^^
10025 pm_compile_next_node(iseq, (const pm_next_node_t *) node, &location, ret, popped, scope_node);
10026 return;
10027 case PM_NIL_NODE: {
10028 // nil
10029 // ^^^
10030 if (!popped) {
10031 PUSH_INSN(ret, location, putnil);
10032 }
10033
10034 return;
10035 }
10036 case PM_NO_BLOCK_PARAMETER_NODE: {
10037 // def foo(&nil); end
10038 // ^^^^
10039 ISEQ_BODY(iseq)->param.flags.accepts_no_block = TRUE;
10040 return;
10041 }
10042 case PM_NO_KEYWORDS_PARAMETER_NODE: {
10043 // def foo(**nil); end
10044 // ^^^^^
10045 ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg = TRUE;
10046 return;
10047 }
10048 case PM_NUMBERED_REFERENCE_READ_NODE: {
10049 // $1
10050 // ^^
10051 if (!popped) {
10053
10054 if (cast->number != 0) {
10055 VALUE ref = pm_compile_numbered_reference_ref(cast);
10056 PUSH_INSN2(ret, location, getspecial, INT2FIX(1), ref);
10057 }
10058 else {
10059 PUSH_INSN(ret, location, putnil);
10060 }
10061 }
10062
10063 return;
10064 }
10065 case PM_OR_NODE: {
10066 // a or b
10067 // ^^^^^^
10068 const pm_or_node_t *cast = (const pm_or_node_t *) node;
10069
10070 LABEL *end_label = NEW_LABEL(location.line);
10071 PM_COMPILE_NOT_POPPED(cast->left);
10072
10073 if (!popped) PUSH_INSN(ret, location, dup);
10074 PUSH_INSNL(ret, location, branchif, end_label);
10075
10076 if (!popped) PUSH_INSN(ret, location, pop);
10077 PM_COMPILE(cast->right);
10078 PUSH_LABEL(ret, end_label);
10079
10080 return;
10081 }
10082 case PM_OPTIONAL_PARAMETER_NODE: {
10083 // def foo(bar = 1); end
10084 // ^^^^^^^
10086 PM_COMPILE_NOT_POPPED(cast->value);
10087
10088 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, 0);
10089 PUSH_SETLOCAL(ret, location, index.index, index.level);
10090
10091 return;
10092 }
10093 case PM_PARENTHESES_NODE: {
10094 // ()
10095 // ^^
10096 //
10097 // (1)
10098 // ^^^
10099 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
10100
10101 if (cast->body != NULL) {
10102 PM_COMPILE(cast->body);
10103 }
10104 else if (!popped) {
10105 PUSH_INSN(ret, location, putnil);
10106 }
10107
10108 return;
10109 }
10110 case PM_PRE_EXECUTION_NODE: {
10111 // BEGIN {}
10112 // ^^^^^^^^
10113 const pm_pre_execution_node_t *cast = (const pm_pre_execution_node_t *) node;
10114
10115 LINK_ANCHOR *outer_pre = scope_node->pre_execution_anchor;
10116 RUBY_ASSERT(outer_pre != NULL);
10117
10118 // BEGIN{} nodes can be nested, so here we're going to do the same thing
10119 // that we did for the top-level compilation where we create two
10120 // anchors and then join them in the correct order into the resulting
10121 // anchor.
10122 DECL_ANCHOR(inner_pre);
10123 scope_node->pre_execution_anchor = inner_pre;
10124
10125 DECL_ANCHOR(inner_body);
10126
10127 if (cast->statements != NULL) {
10128 const pm_node_list_t *body = &cast->statements->body;
10129
10130 for (size_t index = 0; index < body->size; index++) {
10131 pm_compile_node(iseq, body->nodes[index], inner_body, true, scope_node);
10132 }
10133 }
10134
10135 if (!popped) {
10136 PUSH_INSN(inner_body, location, putnil);
10137 }
10138
10139 // Now that everything has been compiled, join both anchors together
10140 // into the correct outer pre execution anchor, and reset the value so
10141 // that subsequent BEGIN{} nodes can be compiled correctly.
10142 PUSH_SEQ(outer_pre, inner_pre);
10143 PUSH_SEQ(outer_pre, inner_body);
10144 scope_node->pre_execution_anchor = outer_pre;
10145
10146 return;
10147 }
10148 case PM_POST_EXECUTION_NODE: {
10149 // END {}
10150 // ^^^^^^
10151 const rb_iseq_t *child_iseq;
10152 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
10153
10154 pm_scope_node_t next_scope_node;
10155 pm_scope_node_init(node, &next_scope_node, scope_node);
10156 child_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, lineno);
10157 pm_scope_node_destroy(&next_scope_node);
10158
10159 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
10160
10161 int is_index = ISEQ_BODY(iseq)->ise_size++;
10162 PUSH_INSN2(ret, location, once, child_iseq, INT2FIX(is_index));
10163 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) child_iseq);
10164 if (popped) PUSH_INSN(ret, location, pop);
10165
10166 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
10167
10168 return;
10169 }
10170 case PM_RANGE_NODE: {
10171 // 0..5
10172 // ^^^^
10173 const pm_range_node_t *cast = (const pm_range_node_t *) node;
10174 bool exclude_end = PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END);
10175
10176 if (pm_optimizable_range_item_p(cast->left) && pm_optimizable_range_item_p(cast->right)) {
10177 if (!popped) {
10178 const pm_node_t *left = cast->left;
10179 const pm_node_t *right = cast->right;
10180
10181 VALUE val = rb_range_new(
10182 (left && PM_NODE_TYPE_P(left, PM_INTEGER_NODE)) ? parse_integer((const pm_integer_node_t *) left) : Qnil,
10183 (right && PM_NODE_TYPE_P(right, PM_INTEGER_NODE)) ? parse_integer((const pm_integer_node_t *) right) : Qnil,
10184 exclude_end
10185 );
10186
10187 RB_OBJ_SET_SHAREABLE(val);
10188 PUSH_INSN1(ret, location, putobject, val);
10189 }
10190 }
10191 else {
10192 if (cast->left != NULL) {
10193 PM_COMPILE(cast->left);
10194 }
10195 else if (!popped) {
10196 PUSH_INSN(ret, location, putnil);
10197 }
10198
10199 if (cast->right != NULL) {
10200 PM_COMPILE(cast->right);
10201 }
10202 else if (!popped) {
10203 PUSH_INSN(ret, location, putnil);
10204 }
10205
10206 if (!popped) {
10207 PUSH_INSN1(ret, location, newrange, INT2FIX(exclude_end ? 1 : 0));
10208 }
10209 }
10210 return;
10211 }
10212 case PM_RATIONAL_NODE: {
10213 // 1r
10214 // ^^
10215 if (!popped) {
10216 PUSH_INSN1(ret, location, putobject, parse_rational((const pm_rational_node_t *) node));
10217 }
10218 return;
10219 }
10220 case PM_REDO_NODE:
10221 // redo
10222 // ^^^^
10223 pm_compile_redo_node(iseq, &location, ret, popped, scope_node);
10224 return;
10225 case PM_REGULAR_EXPRESSION_NODE: {
10226 // /foo/
10227 // ^^^^^
10228 if (!popped) {
10229 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
10230 PUSH_INSN1(ret, location, putobject, regexp);
10231 }
10232 return;
10233 }
10234 case PM_RESCUE_NODE:
10235 // begin; rescue; end
10236 // ^^^^^^^
10237 pm_compile_rescue_node(iseq, (const pm_rescue_node_t *) node, &location, ret, popped, scope_node);
10238 return;
10239 case PM_RESCUE_MODIFIER_NODE: {
10240 // foo rescue bar
10241 // ^^^^^^^^^^^^^^
10242 const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node;
10243
10244 pm_scope_node_t rescue_scope_node;
10245 pm_scope_node_init((const pm_node_t *) cast, &rescue_scope_node, scope_node);
10246
10247 rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(
10248 &rescue_scope_node,
10249 rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label),
10250 ISEQ_TYPE_RESCUE,
10251 pm_node_line_number_cached(cast->rescue_expression, scope_node)
10252 );
10253
10254 pm_scope_node_destroy(&rescue_scope_node);
10255
10256 LABEL *lstart = NEW_LABEL(location.line);
10257 LABEL *lend = NEW_LABEL(location.line);
10258 LABEL *lcont = NEW_LABEL(location.line);
10259
10260 lstart->rescued = LABEL_RESCUE_BEG;
10261 lend->rescued = LABEL_RESCUE_END;
10262
10263 PUSH_LABEL(ret, lstart);
10264 PM_COMPILE_NOT_POPPED(cast->expression);
10265 PUSH_LABEL(ret, lend);
10266
10267 PUSH_INSN(ret, location, nop);
10268 PUSH_LABEL(ret, lcont);
10269 if (popped) PUSH_INSN(ret, location, pop);
10270
10271 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
10272 PUSH_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
10273 return;
10274 }
10275 case PM_RETURN_NODE:
10276 // return
10277 // ^^^^^^
10278 //
10279 // return 1
10280 // ^^^^^^^^
10281 pm_compile_return_node(iseq, (const pm_return_node_t *) node, &location, ret, popped, scope_node);
10282 return;
10283 case PM_RETRY_NODE: {
10284 // retry
10285 // ^^^^^
10286 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
10287 PUSH_INSN(ret, location, putnil);
10288 PUSH_INSN1(ret, location, throw, INT2FIX(TAG_RETRY));
10289 if (popped) PUSH_INSN(ret, location, pop);
10290 }
10291 else {
10292 COMPILE_ERROR(iseq, location.line, "Invalid retry");
10293 return;
10294 }
10295 return;
10296 }
10297 case PM_SCOPE_NODE:
10298 pm_compile_scope_node(iseq, (pm_scope_node_t *) node, &location, ret, popped);
10299 return;
10300 case PM_SELF_NODE: {
10301 // self
10302 // ^^^^
10303 if (!popped) {
10304 PUSH_INSN(ret, location, putself);
10305 }
10306 return;
10307 }
10308 case PM_SHAREABLE_CONSTANT_NODE: {
10309 // A value that is being written to a constant that is being marked as
10310 // shared depending on the current lexical context.
10312 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));
10313
10314 switch (PM_NODE_TYPE(cast->write)) {
10315 case PM_CONSTANT_WRITE_NODE:
10316 pm_compile_constant_write_node(iseq, (const pm_constant_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10317 break;
10318 case PM_CONSTANT_AND_WRITE_NODE:
10319 pm_compile_constant_and_write_node(iseq, (const pm_constant_and_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10320 break;
10321 case PM_CONSTANT_OR_WRITE_NODE:
10322 pm_compile_constant_or_write_node(iseq, (const pm_constant_or_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10323 break;
10324 case PM_CONSTANT_OPERATOR_WRITE_NODE:
10325 pm_compile_constant_operator_write_node(iseq, (const pm_constant_operator_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10326 break;
10327 case PM_CONSTANT_PATH_WRITE_NODE:
10328 pm_compile_constant_path_write_node(iseq, (const pm_constant_path_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10329 break;
10330 case PM_CONSTANT_PATH_AND_WRITE_NODE:
10331 pm_compile_constant_path_and_write_node(iseq, (const pm_constant_path_and_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10332 break;
10333 case PM_CONSTANT_PATH_OR_WRITE_NODE:
10334 pm_compile_constant_path_or_write_node(iseq, (const pm_constant_path_or_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10335 break;
10336 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE:
10337 pm_compile_constant_path_operator_write_node(iseq, (const pm_constant_path_operator_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10338 break;
10339 default:
10340 rb_bug("Unexpected node type for shareable constant write: %s", pm_node_type(PM_NODE_TYPE(cast->write)));
10341 break;
10342 }
10343
10344 return;
10345 }
10346 case PM_SINGLETON_CLASS_NODE: {
10347 // class << self; end
10348 // ^^^^^^^^^^^^^^^^^^
10349 const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node;
10350
10351 pm_scope_node_t next_scope_node;
10352 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
10353 const rb_iseq_t *child_iseq = NEW_ISEQ(&next_scope_node, rb_fstring_lit("singleton class"), ISEQ_TYPE_CLASS, location.line);
10354 pm_scope_node_destroy(&next_scope_node);
10355
10356 PM_COMPILE_NOT_POPPED(cast->expression);
10357 PUSH_INSN(ret, location, putnil);
10358
10359 ID singletonclass;
10360 CONST_ID(singletonclass, "singletonclass");
10361
10362 /* `class << self` in a class body and `class << Foo` (constant
10363 receiver) are stable. All other forms are potentially dynamic. */
10364 int sclass_flags = VM_DEFINECLASS_TYPE_SINGLETON_CLASS;
10365 if (!(PM_NODE_TYPE_P(cast->expression, PM_SELF_NODE) &&
10366 ISEQ_BODY(iseq)->type == ISEQ_TYPE_CLASS) &&
10367 !pm_cpath_const_p(cast->expression)) {
10368 sclass_flags |= VM_DEFINECLASS_FLAG_DYNAMIC_CREF;
10369 }
10370
10371 PUSH_INSN3(ret, location, defineclass, ID2SYM(singletonclass), child_iseq, INT2FIX(sclass_flags));
10372
10373 if (popped) PUSH_INSN(ret, location, pop);
10374 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) child_iseq);
10375
10376 return;
10377 }
10378 case PM_SOURCE_ENCODING_NODE: {
10379 // __ENCODING__
10380 // ^^^^^^^^^^^^
10381 if (!popped) {
10382 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10383 PUSH_INSN1(ret, location, putobject, value);
10384 }
10385 return;
10386 }
10387 case PM_SOURCE_FILE_NODE: {
10388 // __FILE__
10389 // ^^^^^^^^
10390 if (!popped) {
10391 const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node;
10392 VALUE string = pm_source_file_value(cast, scope_node);
10393
10394 if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FROZEN)) {
10395 PUSH_INSN1(ret, location, putobject, string);
10396 }
10397 else if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_MUTABLE)) {
10398 PUSH_INSN1(ret, location, putstring, string);
10399 }
10400 else {
10401 PUSH_INSN1(ret, location, putchilledstring, string);
10402 }
10403 }
10404 return;
10405 }
10406 case PM_SOURCE_LINE_NODE: {
10407 // __LINE__
10408 // ^^^^^^^^
10409 if (!popped) {
10410 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10411 PUSH_INSN1(ret, location, putobject, value);
10412 }
10413 return;
10414 }
10415 case PM_SPLAT_NODE: {
10416 // foo(*bar)
10417 // ^^^^
10418 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
10419 if (cast->expression) {
10420 PM_COMPILE(cast->expression);
10421 }
10422
10423 if (!popped) {
10424 PUSH_INSN1(ret, location, splatarray, Qtrue);
10425 }
10426 return;
10427 }
10428 case PM_STATEMENTS_NODE: {
10429 // A list of statements.
10430 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
10431 const pm_node_list_t *body = &cast->body;
10432
10433 if (body->size > 0) {
10434 for (size_t index = 0; index < body->size - 1; index++) {
10435 PM_COMPILE_POPPED(body->nodes[index]);
10436 }
10437 PM_COMPILE(body->nodes[body->size - 1]);
10438 }
10439 else {
10440 PUSH_INSN(ret, location, putnil);
10441 }
10442 return;
10443 }
10444 case PM_STRING_NODE: {
10445 // "foo"
10446 // ^^^^^
10447 if (!popped) {
10448 const pm_string_node_t *cast = (const pm_string_node_t *) node;
10449 VALUE value = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
10450
10451 if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_FROZEN)) {
10452 PUSH_INSN1(ret, location, putobject, value);
10453 }
10454 else if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_MUTABLE)) {
10455 PUSH_INSN1(ret, location, putstring, value);
10456 }
10457 else {
10458 PUSH_INSN1(ret, location, putchilledstring, value);
10459 }
10460 }
10461 return;
10462 }
10463 case PM_SUPER_NODE:
10464 // super()
10465 // super(foo)
10466 // super(...)
10467 pm_compile_super_node(iseq, (const pm_super_node_t *) node, &location, ret, popped, scope_node);
10468 return;
10469 case PM_SYMBOL_NODE: {
10470 // :foo
10471 // ^^^^
10472 if (!popped) {
10473 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10474 PUSH_INSN1(ret, location, putobject, value);
10475 }
10476 return;
10477 }
10478 case PM_TRUE_NODE: {
10479 // true
10480 // ^^^^
10481 if (!popped) {
10482 PUSH_INSN1(ret, location, putobject, Qtrue);
10483 }
10484 return;
10485 }
10486 case PM_UNDEF_NODE: {
10487 // undef foo
10488 // ^^^^^^^^^
10489 const pm_undef_node_t *cast = (const pm_undef_node_t *) node;
10490 const pm_node_list_t *names = &cast->names;
10491
10492 for (size_t index = 0; index < names->size; index++) {
10493 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10494 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
10495
10496 PM_COMPILE_NOT_POPPED(names->nodes[index]);
10497 PUSH_SEND(ret, location, id_core_undef_method, INT2NUM(2));
10498
10499 if (index < names->size - 1) {
10500 PUSH_INSN(ret, location, pop);
10501 }
10502 }
10503
10504 if (popped) PUSH_INSN(ret, location, pop);
10505 return;
10506 }
10507 case PM_UNLESS_NODE: {
10508 // unless foo; bar end
10509 // ^^^^^^^^^^^^^^^^^^^
10510 //
10511 // bar unless foo
10512 // ^^^^^^^^^^^^^^
10513 const pm_unless_node_t *cast = (const pm_unless_node_t *) node;
10514 const pm_statements_node_t *statements = NULL;
10515 if (cast->else_clause != NULL) {
10516 statements = ((const pm_else_node_t *) cast->else_clause)->statements;
10517 }
10518
10519 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);
10520 return;
10521 }
10522 case PM_UNTIL_NODE: {
10523 // until foo; bar end
10524 // ^^^^^^^^^^^^^^^^^
10525 //
10526 // bar until foo
10527 // ^^^^^^^^^^^^^
10528 const pm_until_node_t *cast = (const pm_until_node_t *) node;
10529 pm_compile_loop(iseq, &location, cast->base.flags, PM_UNTIL_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node);
10530 return;
10531 }
10532 case PM_WHILE_NODE: {
10533 // while foo; bar end
10534 // ^^^^^^^^^^^^^^^^^^
10535 //
10536 // bar while foo
10537 // ^^^^^^^^^^^^^
10538 const pm_while_node_t *cast = (const pm_while_node_t *) node;
10539 pm_compile_loop(iseq, &location, cast->base.flags, PM_WHILE_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node);
10540 return;
10541 }
10542 case PM_X_STRING_NODE: {
10543 // `foo`
10544 // ^^^^^
10545 const pm_x_string_node_t *cast = (const pm_x_string_node_t *) node;
10546 VALUE value = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
10547
10548 PUSH_INSN(ret, location, putself);
10549 PUSH_INSN1(ret, location, putobject, value);
10550 PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
10551 if (popped) PUSH_INSN(ret, location, pop);
10552
10553 return;
10554 }
10555 case PM_YIELD_NODE:
10556 // yield
10557 // ^^^^^
10558 //
10559 // yield 1
10560 // ^^^^^^^
10561 pm_compile_yield_node(iseq, (const pm_yield_node_t *) node, &location, ret, popped, scope_node);
10562 return;
10563 default:
10564 rb_raise(rb_eNotImpError, "node type %s not implemented", pm_node_type(PM_NODE_TYPE(node)));
10565 return;
10566 }
10567}
10568
10569#undef PM_CONTAINER_P
10570
10572static inline bool
10573pm_iseq_pre_execution_p(rb_iseq_t *iseq)
10574{
10575 switch (ISEQ_BODY(iseq)->type) {
10576 case ISEQ_TYPE_TOP:
10577 case ISEQ_TYPE_EVAL:
10578 case ISEQ_TYPE_MAIN:
10579 return true;
10580 default:
10581 return false;
10582 }
10583}
10584
10592VALUE
10593pm_iseq_compile_node(rb_iseq_t *iseq, pm_scope_node_t *node)
10594{
10595 DECL_ANCHOR(ret);
10596
10597 if (pm_iseq_pre_execution_p(iseq)) {
10598 // Because these ISEQs can have BEGIN{}, we're going to create two
10599 // anchors to compile them, a "pre" and a "body". We'll mark the "pre"
10600 // on the scope node so that when BEGIN{} is found, its contents will be
10601 // added to the "pre" anchor.
10602 DECL_ANCHOR(pre);
10603 node->pre_execution_anchor = pre;
10604
10605 // Now we'll compile the body as normal. We won't compile directly into
10606 // the "ret" anchor yet because we want to add the "pre" anchor to the
10607 // beginning of the "ret" anchor first.
10608 DECL_ANCHOR(body);
10609 pm_compile_node(iseq, (const pm_node_t *) node, body, false, node);
10610
10611 // Now we'll join both anchors together so that the content is in the
10612 // correct order.
10613 PUSH_SEQ(ret, pre);
10614 PUSH_SEQ(ret, body);
10615 }
10616 else {
10617 // In other circumstances, we can just compile the node directly into
10618 // the "ret" anchor.
10619 pm_compile_node(iseq, (const pm_node_t *) node, ret, false, node);
10620 }
10621
10622 CHECK(iseq_setup_insn(iseq, ret));
10623 return iseq_setup(iseq, ret);
10624}
10625
10626void
10627pm_parse_result_init(pm_parse_result_t *result)
10628{
10629 memset(result, 0, sizeof(pm_parse_result_t));
10630 result->arena = pm_arena_new();
10631 result->options = pm_options_new();
10632 pm_options_line_set(result->options, 1);
10633}
10634
10639void
10640pm_parse_result_free(pm_parse_result_t *result)
10641{
10642 if (result->parsed) {
10643 SIZED_FREE_N(result->node.constants, pm_parser_constants_size(result->node.parser));
10644 pm_scope_node_destroy(&result->node);
10645 }
10646
10647 if (result->parser) pm_parser_free(result->parser);
10648 pm_arena_free(result->arena);
10649 if (result->source) pm_source_free(result->source);
10650 pm_options_free(result->options);
10651}
10652
10654typedef struct {
10657
10659 int32_t line;
10660
10663
10665 uint32_t column_end;
10667
10669typedef struct {
10671 const char *number_prefix;
10672
10674 const char *blank_prefix;
10675
10677 const char *divider;
10678
10681
10685
10686#define PM_COLOR_BOLD "\033[1m"
10687#define PM_COLOR_GRAY "\033[2m"
10688#define PM_COLOR_RED "\033[1;31m"
10689#define PM_COLOR_RESET "\033[m"
10690#define PM_ERROR_TRUNCATE 30
10691
10693typedef struct {
10694 pm_parse_error_t *errors;
10695 size_t count;
10696 size_t capacity;
10697 const pm_line_offset_list_t *line_offsets;
10698 int32_t start_line;
10700
10701static void
10702pm_error_collect_callback(const pm_diagnostic_t *diagnostic, void *data)
10703{
10704 pm_error_collect_t *ctx = (pm_error_collect_t *) data;
10705 pm_location_t loc = pm_diagnostic_location(diagnostic);
10706
10707 pm_line_column_t start = pm_line_offset_list_line_column(ctx->line_offsets, loc.start, ctx->start_line);
10708 pm_line_column_t end = pm_line_offset_list_line_column(ctx->line_offsets, loc.start + loc.length, ctx->start_line);
10709
10710 uint32_t column_end;
10711 if (start.line == end.line) {
10712 column_end = end.column;
10713 } else {
10714 column_end = (uint32_t) (ctx->line_offsets->offsets[start.line - ctx->start_line + 1] - ctx->line_offsets->offsets[start.line - ctx->start_line] - 1);
10715 }
10716
10717 // Ensure we have at least one column of error.
10718 if (start.column == column_end) column_end++;
10719
10720 // Insert into sorted position (insertion sort).
10721 size_t index = 0;
10722 while (
10723 (index < ctx->count) &&
10724 (
10725 (ctx->errors[index].line < start.line) ||
10726 ((ctx->errors[index].line == start.line) && (ctx->errors[index].column_start < start.column))
10727 )
10728 ) index++;
10729
10730 if (index < ctx->count) {
10731 memmove(&ctx->errors[index + 1], &ctx->errors[index], sizeof(pm_parse_error_t) * (ctx->count - index));
10732 }
10733
10734 ctx->errors[index] = (pm_parse_error_t) {
10735 .error = diagnostic,
10736 .line = start.line,
10737 .column_start = start.column,
10738 .column_end = column_end
10739 };
10740 ctx->count++;
10741}
10742
10743static inline pm_parse_error_t *
10744pm_parse_errors_format_sort(const pm_parser_t *parser, size_t error_count, const pm_line_offset_list_t *line_offsets) {
10745 pm_parse_error_t *errors = xcalloc(error_count, sizeof(pm_parse_error_t));
10746 if (errors == NULL) return NULL;
10747
10748 pm_error_collect_t ctx = {
10749 .errors = errors,
10750 .count = 0,
10751 .capacity = error_count,
10752 .line_offsets = line_offsets,
10753 .start_line = pm_parser_start_line(parser)
10754 };
10755
10756 pm_parser_errors_each(parser, pm_error_collect_callback, &ctx);
10757
10758 return errors;
10759}
10760
10761static inline void
10762pm_parse_errors_format_line(const pm_parser_t *parser, const pm_line_offset_list_t *line_offsets, const char *number_prefix, int32_t line, uint32_t column_start, uint32_t column_end, VALUE buffer) {
10763 int32_t line_delta = line - pm_parser_start_line(parser);
10764 assert(line_delta >= 0);
10765
10766 size_t index = (size_t) line_delta;
10767 assert(index < line_offsets->size);
10768
10769 const uint8_t *start = &pm_parser_start(parser)[line_offsets->offsets[index]];
10770 const uint8_t *end;
10771
10772 if (index >= line_offsets->size - 1) {
10773 end = pm_parser_end(parser);
10774 } else {
10775 end = &pm_parser_start(parser)[line_offsets->offsets[index + 1]];
10776 }
10777
10778 rb_str_catf(buffer, number_prefix, line);
10779
10780 // Here we determine if we should truncate the end of the line.
10781 bool truncate_end = false;
10782 if ((column_end != 0) && ((end - (start + column_end)) >= PM_ERROR_TRUNCATE)) {
10783 const uint8_t *end_candidate = start + column_end + PM_ERROR_TRUNCATE;
10784
10785 for (const uint8_t *ptr = start; ptr < end_candidate;) {
10786 size_t char_width = pm_parser_encoding_char_width(parser, ptr, pm_parser_end(parser) - ptr);
10787
10788 // If we failed to decode a character, then just bail out and
10789 // truncate at the fixed width.
10790 if (char_width == 0) break;
10791
10792 // If this next character would go past the end candidate,
10793 // then we need to truncate before it.
10794 if (ptr + char_width > end_candidate) {
10795 end_candidate = ptr;
10796 break;
10797 }
10798
10799 ptr += char_width;
10800 }
10801
10802 end = end_candidate;
10803 truncate_end = true;
10804 }
10805
10806 // Here we determine if we should truncate the start of the line.
10807 if (column_start >= PM_ERROR_TRUNCATE) {
10808 rb_str_cat(buffer, "... ", 4);
10809 start += column_start;
10810 }
10811
10812 rb_str_cat(buffer, (const char *) start, (size_t) (end - start));
10813
10814 if (truncate_end) {
10815 rb_str_cat(buffer, " ...\n", 5);
10816 } else if (end == pm_parser_end(parser) && end[-1] != '\n') {
10817 rb_str_cat(buffer, "\n", 1);
10818 }
10819}
10820
10824static void
10825pm_parse_errors_format_with(const pm_parser_t *parser, pm_parse_error_t *errors, size_t error_count, VALUE buffer, int highlight, bool inline_messages) {
10826 assert(error_count != 0);
10827
10828 const int32_t start_line = pm_parser_start_line(parser);
10829 const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser);
10830
10831 // Now we're going to determine how we're going to format line numbers and
10832 // blank lines based on the maximum number of digits in the line numbers
10833 // that are going to be displaid.
10834 pm_parse_error_format_t error_format;
10835 int32_t first_line_number = errors[0].line;
10836 int32_t last_line_number = errors[error_count - 1].line;
10837
10838 // If we have a maximum line number that is negative, then we're going to
10839 // use the absolute value for comparison but multiple by 10 to additionally
10840 // have a column for the negative sign.
10841 if (first_line_number < 0) first_line_number = (-first_line_number) * 10;
10842 if (last_line_number < 0) last_line_number = (-last_line_number) * 10;
10843 int32_t max_line_number = first_line_number > last_line_number ? first_line_number : last_line_number;
10844
10845 if (max_line_number < 10) {
10846 if (highlight > 0) {
10847 error_format = (pm_parse_error_format_t) {
10848 .number_prefix = PM_COLOR_GRAY "%1" PRIi32 " | " PM_COLOR_RESET,
10849 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10850 .divider = PM_COLOR_GRAY " ~~~~~" PM_COLOR_RESET "\n"
10851 };
10852 } else {
10853 error_format = (pm_parse_error_format_t) {
10854 .number_prefix = "%1" PRIi32 " | ",
10855 .blank_prefix = " | ",
10856 .divider = " ~~~~~\n"
10857 };
10858 }
10859 } else if (max_line_number < 100) {
10860 if (highlight > 0) {
10861 error_format = (pm_parse_error_format_t) {
10862 .number_prefix = PM_COLOR_GRAY "%2" PRIi32 " | " PM_COLOR_RESET,
10863 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10864 .divider = PM_COLOR_GRAY " ~~~~~~" PM_COLOR_RESET "\n"
10865 };
10866 } else {
10867 error_format = (pm_parse_error_format_t) {
10868 .number_prefix = "%2" PRIi32 " | ",
10869 .blank_prefix = " | ",
10870 .divider = " ~~~~~~\n"
10871 };
10872 }
10873 } else if (max_line_number < 1000) {
10874 if (highlight > 0) {
10875 error_format = (pm_parse_error_format_t) {
10876 .number_prefix = PM_COLOR_GRAY "%3" PRIi32 " | " PM_COLOR_RESET,
10877 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10878 .divider = PM_COLOR_GRAY " ~~~~~~~" PM_COLOR_RESET "\n"
10879 };
10880 } else {
10881 error_format = (pm_parse_error_format_t) {
10882 .number_prefix = "%3" PRIi32 " | ",
10883 .blank_prefix = " | ",
10884 .divider = " ~~~~~~~\n"
10885 };
10886 }
10887 } else if (max_line_number < 10000) {
10888 if (highlight > 0) {
10889 error_format = (pm_parse_error_format_t) {
10890 .number_prefix = PM_COLOR_GRAY "%4" PRIi32 " | " PM_COLOR_RESET,
10891 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10892 .divider = PM_COLOR_GRAY " ~~~~~~~~" PM_COLOR_RESET "\n"
10893 };
10894 } else {
10895 error_format = (pm_parse_error_format_t) {
10896 .number_prefix = "%4" PRIi32 " | ",
10897 .blank_prefix = " | ",
10898 .divider = " ~~~~~~~~\n"
10899 };
10900 }
10901 } else {
10902 if (highlight > 0) {
10903 error_format = (pm_parse_error_format_t) {
10904 .number_prefix = PM_COLOR_GRAY "%5" PRIi32 " | " PM_COLOR_RESET,
10905 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10906 .divider = PM_COLOR_GRAY " ~~~~~~~~" PM_COLOR_RESET "\n"
10907 };
10908 } else {
10909 error_format = (pm_parse_error_format_t) {
10910 .number_prefix = "%5" PRIi32 " | ",
10911 .blank_prefix = " | ",
10912 .divider = " ~~~~~~~~\n"
10913 };
10914 }
10915 }
10916
10917 error_format.blank_prefix_length = strlen(error_format.blank_prefix);
10918 error_format.divider_length = strlen(error_format.divider);
10919
10920 // Now we're going to iterate through every error in our error list and
10921 // display it. While we're iterating, we will display some padding lines of
10922 // the source before the error to give some context. We'll be careful not to
10923 // display the same line twice in case the errors are close enough in the
10924 // source.
10925 int32_t last_line = pm_parser_start_line(parser) - 1;
10926 uint32_t last_column_start = 0;
10927
10928 for (size_t index = 0; index < error_count; index++) {
10929 pm_parse_error_t *error = &errors[index];
10930
10931 // Here we determine how many lines of padding of the source to display,
10932 // based on the difference from the last line that was displaid.
10933 if (error->line - last_line > 1) {
10934 if (error->line - last_line > 2) {
10935 if ((index != 0) && (error->line - last_line > 3)) {
10936 rb_str_cat(buffer, error_format.divider, error_format.divider_length);
10937 }
10938
10939 rb_str_cat(buffer, " ", 2);
10940 pm_parse_errors_format_line(parser, line_offsets, error_format.number_prefix, error->line - 2, 0, 0, buffer);
10941 }
10942
10943 rb_str_cat(buffer, " ", 2);
10944 pm_parse_errors_format_line(parser, line_offsets, error_format.number_prefix, error->line - 1, 0, 0, buffer);
10945 }
10946
10947 // If this is the first error or we're on a new line, then we'll display
10948 // the line that has the error in it.
10949 if ((index == 0) || (error->line != last_line)) {
10950 if (highlight > 1) {
10951 rb_str_cat_cstr(buffer, PM_COLOR_RED "> " PM_COLOR_RESET);
10952 } else if (highlight > 0) {
10953 rb_str_cat_cstr(buffer, PM_COLOR_BOLD "> " PM_COLOR_RESET);
10954 } else {
10955 rb_str_cat_cstr(buffer, "> ");
10956 }
10957
10958 last_column_start = error->column_start;
10959
10960 // Find the maximum column end of all the errors on this line.
10961 uint32_t column_end = error->column_end;
10962 for (size_t next_index = index + 1; next_index < error_count; next_index++) {
10963 if (errors[next_index].line != error->line) break;
10964 if (errors[next_index].column_end > column_end) column_end = errors[next_index].column_end;
10965 }
10966
10967 pm_parse_errors_format_line(parser, line_offsets, error_format.number_prefix, error->line, error->column_start, column_end, buffer);
10968 }
10969
10970 const uint8_t *start = &pm_parser_start(parser)[line_offsets->offsets[error->line - start_line]];
10971 if (start == pm_parser_end(parser)) rb_str_cat(buffer, "\n", 1);
10972
10973 // Now we'll display the actual error message. We'll do this by first
10974 // putting the prefix to the line, then a bunch of blank spaces
10975 // depending on the column, then as many carets as we need to display
10976 // the width of the error, then the error message itself.
10977 //
10978 // Note that this doesn't take into account the width of the actual
10979 // character when displaid in the terminal. For some east-asian
10980 // languages or emoji, this means it can be thrown off pretty badly. We
10981 // will need to solve this eventually.
10982 rb_str_cat(buffer, " ", 2);
10983 rb_str_cat(buffer, error_format.blank_prefix, error_format.blank_prefix_length);
10984
10985 size_t column = 0;
10986 if (last_column_start >= PM_ERROR_TRUNCATE) {
10987 rb_str_cat(buffer, " ", 4);
10988 column = last_column_start;
10989 }
10990
10991 while (column < error->column_start) {
10992 rb_str_cat(buffer, " ", 1);
10993
10994 size_t char_width = pm_parser_encoding_char_width(parser, start + column, pm_parser_end(parser) - (start + column));
10995 column += (char_width == 0 ? 1 : char_width);
10996 }
10997
10998 if (highlight > 1) rb_str_cat_cstr(buffer, PM_COLOR_RED);
10999 else if (highlight > 0) rb_str_cat_cstr(buffer, PM_COLOR_BOLD);
11000 rb_str_cat(buffer, "^", 1);
11001
11002 size_t char_width = pm_parser_encoding_char_width(parser, start + column, pm_parser_end(parser) - (start + column));
11003 column += (char_width == 0 ? 1 : char_width);
11004
11005 while (column < error->column_end) {
11006 rb_str_cat(buffer, "~", 1);
11007
11008 size_t char_width = pm_parser_encoding_char_width(parser, start + column, pm_parser_end(parser) - (start + column));
11009 column += (char_width == 0 ? 1 : char_width);
11010 }
11011
11012 if (highlight > 0) rb_str_cat_cstr(buffer, PM_COLOR_RESET);
11013
11014 if (inline_messages) {
11015 rb_str_cat(buffer, " ", 1);
11016 assert(error->error != NULL);
11017
11018 const char *message = pm_diagnostic_message(error->error);
11019 rb_str_cat(buffer, message, strlen(message));
11020 }
11021
11022 rb_str_cat(buffer, "\n", 1);
11023
11024 // Here we determine how many lines of padding to display after the
11025 // error, depending on where the next error is in source.
11026 last_line = error->line;
11027 int32_t next_line;
11028
11029 if (index == error_count - 1) {
11030 next_line = (((int32_t) line_offsets->size) + pm_parser_start_line(parser));
11031
11032 // If the file ends with a newline, subtract one from our "next_line"
11033 // so that we don't output an extra line at the end of the file
11034 if ((pm_parser_start(parser) + line_offsets->offsets[line_offsets->size - 1]) == pm_parser_end(parser)) {
11035 next_line--;
11036 }
11037 }
11038 else {
11039 next_line = errors[index + 1].line;
11040 }
11041
11042 if (next_line - last_line > 1) {
11043 rb_str_cat(buffer, " ", 2);
11044 pm_parse_errors_format_line(parser, line_offsets, error_format.number_prefix, ++last_line, 0, 0, buffer);
11045 }
11046
11047 if (next_line - last_line > 1) {
11048 rb_str_cat(buffer, " ", 2);
11049 pm_parse_errors_format_line(parser, line_offsets, error_format.number_prefix, ++last_line, 0, 0, buffer);
11050 }
11051 }
11052
11053}
11054
11058static void
11059pm_parse_errors_format(const pm_parser_t *parser, size_t error_count, VALUE buffer, int highlight, bool inline_messages) {
11060 const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser);
11061
11062 pm_parse_error_t *errors = pm_parse_errors_format_sort(parser, error_count, line_offsets);
11063 if (errors == NULL) return;
11064
11065 pm_parse_errors_format_with(parser, errors, error_count, buffer, highlight, inline_messages);
11066 SIZED_FREE_N(errors, error_count);
11067}
11068
11069#undef PM_ERROR_TRUNCATE
11070#undef PM_COLOR_GRAY
11071#undef PM_COLOR_RED
11072#undef PM_COLOR_RESET
11073
11080static bool
11081pm_parse_process_error_utf8_p(const pm_parser_t *parser, pm_location_t location)
11082{
11083 const size_t start_line = pm_line_offset_list_line_column(pm_parser_line_offsets(parser), location.start, 1).line;
11084 const size_t end_line = pm_line_offset_list_line_column(pm_parser_line_offsets(parser), location.start + location.length, 1).line;
11085
11086 const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser);
11087 const uint8_t *start = pm_parser_start(parser) + line_offsets->offsets[start_line - 1];
11088 const uint8_t *end = ((end_line == line_offsets->size) ? pm_parser_end(parser) : (pm_parser_start(parser) + line_offsets->offsets[end_line]));
11089
11090 rb_encoding *utf8 = rb_utf8_encoding();
11091 while (start < end) {
11092 int width = rb_enc_precise_mbclen((const char *) start, (const char *) end, utf8);
11093 if (!MBCLEN_CHARFOUND_P(width)) return false;
11094 start += MBCLEN_CHARFOUND_LEN(width);
11095 }
11096
11097 return true;
11098}
11099
11101typedef struct {
11102 const pm_parse_result_t *result;
11103 const pm_parser_t *parser;
11104 const pm_string_t *filepath;
11105 VALUE buffer;
11106 int highlight;
11107 bool valid_utf8;
11108 bool found_argument_error;
11109 bool found_load_error;
11110 VALUE early_return;
11111 const pm_diagnostic_t *first_error;
11112 size_t error_count;
11114
11115static void
11116pm_process_error_check_callback(const pm_diagnostic_t *diagnostic, void *data)
11117{
11119 pm_location_t loc = pm_diagnostic_location(diagnostic);
11120
11121 if (ctx->first_error == NULL) ctx->first_error = diagnostic;
11122 ctx->error_count++;
11123
11124 switch (pm_diagnostic_error_level(diagnostic)) {
11126 if (ctx->valid_utf8 && !pm_parse_process_error_utf8_p(ctx->parser, loc)) {
11127 ctx->valid_utf8 = false;
11128 }
11129 break;
11131 if (ctx->found_argument_error || ctx->found_load_error) break;
11132 ctx->found_argument_error = true;
11133
11134 int32_t line_number = (int32_t) pm_location_line_number(ctx->parser, &loc);
11135
11136 rb_str_catf(
11137 ctx->buffer,
11138 "%.*s:%" PRIi32 ": %s",
11139 (int) pm_string_length(ctx->filepath),
11140 pm_string_source(ctx->filepath),
11141 line_number,
11142 pm_diagnostic_message(diagnostic)
11143 );
11144
11145 if (pm_parse_process_error_utf8_p(ctx->parser, loc)) {
11146 rb_str_cat(ctx->buffer, "\n", 1);
11147 // Format just this one error. We construct a single-element sorted
11148 // array manually and call the format function with count=1.
11149 const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(ctx->parser);
11150 int32_t start_line = pm_parser_start_line(ctx->parser);
11151 pm_line_column_t start_lc = pm_line_offset_list_line_column(line_offsets, loc.start, start_line);
11152 pm_line_column_t end_lc = pm_line_offset_list_line_column(line_offsets, loc.start + loc.length, start_line);
11153
11154 uint32_t col_end;
11155 if (start_lc.line == end_lc.line) {
11156 col_end = end_lc.column;
11157 } else {
11158 col_end = (uint32_t) (line_offsets->offsets[start_lc.line - start_line + 1] - line_offsets->offsets[start_lc.line - start_line] - 1);
11159 }
11160 if (start_lc.column == col_end) col_end++;
11161
11162 pm_parse_error_t single_error = {
11163 .error = diagnostic,
11164 .line = start_lc.line,
11165 .column_start = start_lc.column,
11166 .column_end = col_end
11167 };
11168 pm_parse_errors_format_with(ctx->parser, &single_error, 1, ctx->buffer, ctx->highlight, false);
11169 }
11170
11171 ctx->early_return = rb_exc_new_str(rb_eArgError, ctx->buffer);
11172 break;
11173 }
11174 case PM_ERROR_LEVEL_LOAD: {
11175 if (ctx->found_argument_error || ctx->found_load_error) break;
11176 ctx->found_load_error = true;
11177
11178 VALUE message = rb_enc_str_new_cstr(pm_diagnostic_message(diagnostic), rb_locale_encoding());
11179 VALUE value = rb_exc_new3(rb_eLoadError, message);
11180 rb_ivar_set(value, rb_intern_const("@path"), Qnil);
11181 ctx->early_return = value;
11182 break;
11183 }
11184 }
11185}
11186
11188typedef struct {
11189 const pm_parser_t *parser;
11190 const pm_string_t *filepath;
11191 VALUE buffer;
11192 bool first;
11194
11195static void
11196pm_error_simple_format_callback(const pm_diagnostic_t *diagnostic, void *data)
11197{
11199 pm_location_t loc = pm_diagnostic_location(diagnostic);
11200
11201 if (!ctx->first) rb_str_cat(ctx->buffer, "\n", 1);
11202 ctx->first = false;
11203
11204 rb_str_catf(ctx->buffer, "%.*s:%" PRIi32 ": %s",
11205 (int) pm_string_length(ctx->filepath),
11206 pm_string_source(ctx->filepath),
11207 (int32_t) pm_location_line_number(ctx->parser, &loc),
11208 pm_diagnostic_message(diagnostic));
11209}
11210
11215static VALUE
11216pm_parse_process_error(const pm_parse_result_t *result)
11217{
11218 const pm_parser_t *parser = result->parser;
11219 size_t error_count = pm_parser_errors_size(parser);
11220
11221 VALUE buffer = rb_str_buf_new(0);
11222 const pm_string_t *filepath = pm_parser_filepath(parser);
11223
11224 int highlight = rb_stderr_tty_p();
11225 if (highlight) {
11226 const char *no_color = getenv("NO_COLOR");
11227 highlight = (no_color == NULL || no_color[0] == '\0') ? 2 : 1;
11228 }
11229
11230 // First pass: check for argument/load errors and UTF-8 validity.
11232 .result = result,
11233 .parser = parser,
11234 .filepath = filepath,
11235 .buffer = buffer,
11236 .highlight = highlight,
11237 .valid_utf8 = true,
11238 .found_argument_error = false,
11239 .found_load_error = false,
11240 .early_return = Qundef,
11241 .first_error = NULL,
11242 .error_count = 0
11243 };
11244
11245 pm_parser_errors_each(parser, pm_process_error_check_callback, &ctx);
11246
11247 // If we found an argument or load error, return it immediately.
11248 if (ctx.early_return != Qundef) {
11249 return ctx.early_return;
11250 }
11251
11252 // Format the header line.
11253 pm_location_t first_loc = pm_diagnostic_location(ctx.first_error);
11254 rb_str_catf(
11255 buffer,
11256 "%.*s:%" PRIi32 ": syntax error%s found\n",
11257 (int) pm_string_length(filepath),
11258 pm_string_source(filepath),
11259 (int32_t) pm_location_line_number(parser, &first_loc),
11260 (error_count > 1) ? "s" : ""
11261 );
11262
11263 if (ctx.valid_utf8) {
11264 pm_parse_errors_format(parser, error_count, buffer, highlight, true);
11265 }
11266 else {
11267 pm_error_simple_format_ctx_t simple_ctx = {
11268 .parser = parser,
11269 .filepath = filepath,
11270 .buffer = buffer,
11271 .first = true
11272 };
11273 pm_parser_errors_each(parser, pm_error_simple_format_callback, &simple_ctx);
11274 }
11275
11276 rb_enc_associate(buffer, result->node.encoding);
11277 VALUE error = rb_exc_new_str(rb_eSyntaxError, buffer);
11278
11279 rb_encoding *filepath_encoding = result->node.filepath_encoding != NULL ? result->node.filepath_encoding : rb_utf8_encoding();
11280 VALUE path = rb_enc_str_new((const char *) pm_string_source(filepath), pm_string_length(filepath), filepath_encoding);
11281
11282 rb_ivar_set(error, rb_intern_const("@path"), path);
11283
11284 return error;
11285}
11286
11288typedef struct {
11289 ID *constants;
11290 rb_encoding *encoding;
11291 size_t index;
11293
11294static void
11295pm_intern_constants_callback(const pm_constant_t *constant, void *data)
11296{
11298 ctx->constants[ctx->index++] = rb_intern3((const char *) pm_constant_start(constant), pm_constant_length(constant), ctx->encoding);
11299}
11300
11302typedef struct {
11303 const pm_parser_t *parser;
11304 rb_encoding *encoding;
11305 const char *filepath;
11307
11308static void
11309pm_warning_emit_callback(const pm_diagnostic_t *diagnostic, void *data) {
11311 pm_location_t loc = pm_diagnostic_location(diagnostic);
11312 int line = pm_location_line_number(ctx->parser, &loc);
11313
11314 if (pm_diagnostic_warning_level(diagnostic) == PM_WARNING_LEVEL_VERBOSE) {
11315 rb_enc_compile_warning(ctx->encoding, ctx->filepath, line, "%s", pm_diagnostic_message(diagnostic));
11316 }
11317 else {
11318 rb_enc_compile_warn(ctx->encoding, ctx->filepath, line, "%s", pm_diagnostic_message(diagnostic));
11319 }
11320}
11321
11327static VALUE
11328pm_parse_process(pm_parse_result_t *result, pm_node_t *node, VALUE *script_lines)
11329{
11330 pm_parser_t *parser = result->parser;
11331
11332 // First, set up the scope node so that the AST node is attached and can be
11333 // freed regardless of whether or we return an error.
11334 pm_scope_node_t *scope_node = &result->node;
11335 rb_encoding *filepath_encoding = scope_node->filepath_encoding;
11336 int coverage_enabled = scope_node->coverage_enabled;
11337
11338 pm_scope_node_init(node, scope_node, NULL);
11339 scope_node->filepath_encoding = filepath_encoding;
11340
11341 const char *encoding_name = pm_parser_encoding_name(parser);
11342 scope_node->encoding = rb_enc_find(encoding_name);
11343 if (!scope_node->encoding) rb_bug("Encoding not found %s!", encoding_name);
11344
11345 scope_node->coverage_enabled = coverage_enabled;
11346
11347 // If RubyVM.keep_script_lines is set to true, then we need to create that
11348 // array of script lines here.
11349 if (script_lines != NULL) {
11350 const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser);
11351 *script_lines = rb_ary_new_capa(line_offsets->size);
11352
11353 for (size_t index = 0; index < line_offsets->size; index++) {
11354 size_t offset = line_offsets->offsets[index];
11355 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);
11356 rb_ary_push(*script_lines, rb_enc_str_new((const char *) pm_parser_start(parser) + offset, length, scope_node->encoding));
11357 }
11358
11359 scope_node->script_lines = script_lines;
11360 }
11361
11362 // Emit all of the various warnings from the parse.
11363 pm_warning_emit_ctx_t warning_ctx = {
11364 .parser = parser,
11365 .encoding = scope_node->encoding,
11366 .filepath = (const char *) pm_string_source(pm_parser_filepath(parser))
11367 };
11368 pm_parser_warnings_each(parser, pm_warning_emit_callback, &warning_ctx);
11369
11370 // If there are errors, raise an appropriate error and free the result.
11371 if (pm_parser_errors_size(parser) > 0) {
11372 VALUE error = pm_parse_process_error(result);
11373
11374 // TODO: We need to set the backtrace.
11375 // rb_funcallv(error, rb_intern("set_backtrace"), 1, &path);
11376 return error;
11377 }
11378
11379 // Now set up the constant pool and intern all of the various constants into
11380 // their corresponding IDs.
11381 scope_node->parser = parser;
11382 scope_node->options = result->options;
11383 scope_node->line_offsets = pm_parser_line_offsets(parser);
11384 scope_node->start_line = pm_parser_start_line(parser);
11385 size_t constants_size = pm_parser_constants_size(parser);
11386 scope_node->constants = constants_size ? xmalloc(constants_size * sizeof(ID)) : NULL;
11387
11388 pm_intern_constants_ctx_t intern_ctx = { .constants = scope_node->constants, .encoding = scope_node->encoding, .index = 0 };
11389 pm_parser_constants_each(parser, pm_intern_constants_callback, &intern_ctx);
11390
11391 // If we got here, this is a success and we can return Qnil to indicate that
11392 // no error should be raised.
11393 result->parsed = true;
11394 return Qnil;
11395}
11396
11401static void
11402pm_options_frozen_string_literal_init(pm_options_t *options)
11403{
11404 int frozen_string_literal = rb_iseq_opt_frozen_string_literal();
11405
11406 switch (frozen_string_literal) {
11407 case ISEQ_FROZEN_STRING_LITERAL_UNSET:
11408 break;
11409 case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
11410 pm_options_frozen_string_literal_set(options, false);
11411 break;
11412 case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
11413 pm_options_frozen_string_literal_set(options, true);
11414 break;
11415 default:
11416 rb_bug("pm_options_frozen_string_literal_init: invalid frozen_string_literal=%d", frozen_string_literal);
11417 break;
11418 }
11419}
11420
11425static inline VALUE
11426pm_parse_file_script_lines(const pm_scope_node_t *scope_node, const pm_parser_t *parser)
11427{
11428 const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser);
11429 const char *start = (const char *) pm_parser_start(parser);
11430 const char *end = (const char *) pm_parser_end(parser);
11431
11432 // If we end exactly on a newline, then there's no need to push on a final
11433 // segment. If we don't, then we need to push on the last offset up to the
11434 // end of the string.
11435 size_t last_offset = line_offsets->offsets[line_offsets->size - 1];
11436 bool last_push = start + last_offset != end;
11437
11438 // Create the ruby strings that represent the lines of the source.
11439 VALUE lines = rb_ary_new_capa(line_offsets->size - (last_push ? 0 : 1));
11440
11441 for (size_t index = 0; index < line_offsets->size - 1; index++) {
11442 size_t offset = line_offsets->offsets[index];
11443 size_t length = line_offsets->offsets[index + 1] - offset;
11444
11445 rb_ary_push(lines, rb_enc_str_new(start + offset, length, scope_node->encoding));
11446 }
11447
11448 // Push on the last line if we need to.
11449 if (last_push) {
11450 rb_ary_push(lines, rb_enc_str_new(start + last_offset, end - (start + last_offset), scope_node->encoding));
11451 }
11452
11453 return lines;
11454}
11455
11457 VALUE path;
11458 VALUE io;
11459 int open_mode;
11460 int fd;
11461};
11462
11463static VALUE
11464close_file(VALUE args)
11465{
11466 struct load_from_fd_args *arg = (void *)args;
11467 if (arg->fd != -1) {
11468 close(arg->fd);
11469 }
11470 else if (!NIL_P(arg->io)) {
11471 rb_io_close(arg->io);
11472 }
11473 return Qnil;
11474}
11475
11476static VALUE
11477load_content(VALUE args)
11478{
11479 struct load_from_fd_args *arg = (void *)args;
11480 VALUE io = rb_io_fdopen(arg->fd, arg->open_mode, RSTRING_PTR(arg->path));
11481 arg->io = io;
11482 arg->fd = -1;
11484 return rb_funcall(io, rb_intern("read"), 0);
11485}
11486
11491VALUE
11492pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error)
11493{
11494 pm_source_init_result_t init_result;
11495 result->source = pm_source_mapped_new(RSTRING_PTR(filepath), O_RDONLY | O_NONBLOCK, &init_result);
11496
11497 if (init_result == PM_SOURCE_INIT_SUCCESS) {
11498 pm_options_frozen_string_literal_init(result->options);
11499 return Qnil;
11500 }
11501
11502 int err;
11503
11504 // For non-regular files (pipes, character devices), we need to read
11505 // through Ruby IO to properly release the GVL while waiting for data.
11506 if (init_result == PM_SOURCE_INIT_ERROR_NON_REGULAR) {
11507 struct load_from_fd_args args = {
11508 .path = filepath,
11509 .open_mode = O_RDONLY | O_NONBLOCK,
11510 .fd = rb_cloexec_open(RSTRING_PTR(filepath), args.open_mode, 0),
11511 .io = Qnil,
11512 };
11513 if (args.fd == -1) goto error_generic;
11514 VALUE contents = rb_ensure(load_content, (VALUE)&args, close_file, (VALUE)&args);
11515
11516 if (!RB_TYPE_P(contents, T_STRING)) goto error_generic;
11517
11518 long len = RSTRING_LEN(contents);
11519 if (len < 0) goto error_generic;
11520
11521 size_t length = (size_t) len;
11522 uint8_t *source_data = xmalloc(length);
11523 memcpy(source_data, RSTRING_PTR(contents), length);
11524 result->source = pm_source_owned_new(source_data, length);
11525
11526 pm_options_frozen_string_literal_init(result->options);
11527 return Qnil;
11528 }
11529
11530 if (init_result == PM_SOURCE_INIT_ERROR_DIRECTORY) {
11531 err = EISDIR;
11532 } else {
11533error_generic:
11534#ifdef _WIN32
11535 err = rb_w32_map_errno(GetLastError());
11536#else
11537 err = errno;
11538#endif
11539 }
11540
11541 VALUE error;
11542 if (load_error) {
11543 VALUE message = rb_str_buf_new_cstr(strerror(err));
11544 rb_str_cat2(message, " -- ");
11545 rb_str_append(message, filepath);
11546
11547 error = rb_exc_new3(rb_eLoadError, message);
11548 rb_ivar_set(error, rb_intern_const("@path"), filepath);
11549 } else {
11550 error = rb_syserr_new(err, RSTRING_PTR(filepath));
11551 RB_GC_GUARD(filepath);
11552 }
11553
11554 return error;
11555}
11556
11563VALUE
11564pm_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines)
11565{
11566 result->node.filepath_encoding = rb_enc_get(filepath);
11567 pm_options_filepath_set(result->options, RSTRING_PTR(filepath));
11568 RB_GC_GUARD(filepath);
11569
11570 pm_options_version_for_current_ruby_set(result->options);
11571
11572 result->parser = pm_parser_new(result->arena, pm_source_source(result->source), pm_source_length(result->source), result->options);
11573 pm_node_t *node = pm_parse(result->parser);
11574
11575 VALUE error = pm_parse_process(result, node, script_lines);
11576
11577 // If we're parsing a filepath, then we need to potentially support the
11578 // SCRIPT_LINES__ constant, which can be a hash that has an array of lines
11579 // of every read file.
11580 ID id_script_lines = rb_intern("SCRIPT_LINES__");
11581
11582 if (rb_const_defined_at(rb_cObject, id_script_lines)) {
11583 VALUE constant_script_lines = rb_const_get_at(rb_cObject, id_script_lines);
11584
11585 if (RB_TYPE_P(constant_script_lines, T_HASH)) {
11586 rb_hash_aset(constant_script_lines, filepath, pm_parse_file_script_lines(&result->node, result->parser));
11587 }
11588 }
11589
11590 return error;
11591}
11592
11597VALUE
11598pm_load_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines)
11599{
11600 VALUE error = pm_load_file(result, filepath, false);
11601 if (NIL_P(error)) {
11602 error = pm_parse_file(result, filepath, script_lines);
11603 }
11604
11605 return error;
11606}
11607
11614VALUE
11615pm_parse_string(pm_parse_result_t *result, VALUE source, VALUE filepath, VALUE *script_lines)
11616{
11617 rb_encoding *encoding = rb_enc_get(source);
11618 if (!rb_enc_asciicompat(encoding)) {
11619 return rb_exc_new_cstr(rb_eArgError, "invalid source encoding");
11620 }
11621
11622 pm_options_frozen_string_literal_init(result->options);
11623 result->source = pm_source_constant_new((const uint8_t *) RSTRING_PTR(source), (size_t) RSTRING_LEN(source));
11624 pm_options_encoding_set(result->options, rb_enc_name(encoding));
11625
11626 result->node.filepath_encoding = rb_enc_get(filepath);
11627 pm_options_filepath_set(result->options, RSTRING_PTR(filepath));
11628 RB_GC_GUARD(filepath);
11629
11630 pm_options_version_for_current_ruby_set(result->options);
11631
11632 result->parser = pm_parser_new(result->arena, pm_source_source(result->source), pm_source_length(result->source), result->options);
11633 pm_node_t *node = pm_parse(result->parser);
11634
11635 return pm_parse_process(result, node, script_lines);
11636}
11637
11639 VALUE rb_stdin;
11640 int eof_seen;
11641};
11642
11643static int
11644pm_parse_stdin_eof(void *stream)
11645{
11646 struct rb_stdin_wrapper * wrapped_stdin = (struct rb_stdin_wrapper *)stream;
11647 return wrapped_stdin->eof_seen;
11648}
11649
11650VALUE rb_io_gets_limit_internal(VALUE io, long limit);
11651
11655static char *
11656pm_parse_stdin_fgets(char *string, int size, void *stream)
11657{
11658 RUBY_ASSERT(size > 0);
11659
11660 struct rb_stdin_wrapper * wrapped_stdin = (struct rb_stdin_wrapper *)stream;
11661
11662 VALUE line = rb_io_gets_limit_internal(wrapped_stdin->rb_stdin, size - 1);
11663 if (NIL_P(line)) {
11664 return NULL;
11665 }
11666
11667 const char *cstr = RSTRING_PTR(line);
11668 long length = RSTRING_LEN(line);
11669
11670 memcpy(string, cstr, length);
11671 string[length] = '\0';
11672
11673 // We're reading strings from stdin via gets. We'll assume that if the
11674 // string is smaller than the requested length, and doesn't end with a
11675 // newline, that we hit EOF.
11676 if (length < (size - 1) && string[length - 1] != '\n') {
11677 wrapped_stdin->eof_seen = 1;
11678 }
11679
11680 return string;
11681}
11682
11683// We need access to this function when we're done parsing stdin.
11684void rb_reset_argf_lineno(long n);
11685
11691VALUE
11692pm_parse_stdin(pm_parse_result_t *result)
11693{
11694 pm_options_frozen_string_literal_init(result->options);
11695
11696 struct rb_stdin_wrapper wrapped_stdin = {
11697 rb_stdin,
11698 0
11699 };
11700
11701 result->source = pm_source_stream_new((void *) &wrapped_stdin, pm_parse_stdin_fgets, pm_parse_stdin_eof);
11702 pm_node_t *node = pm_parse_stream(&result->parser, result->arena, result->source, result->options);
11703
11704 // When we're done parsing, we reset $. because we don't want the fact that
11705 // we went through an IO object to be visible to the user.
11706 rb_reset_argf_lineno(0);
11707
11708 return pm_parse_process(result, node, NULL);
11709}
11710
11711#define PM_VERSION_FOR_RELEASE(major, minor) PM_VERSION_FOR_RELEASE_IMPL(major, minor)
11712#define PM_VERSION_FOR_RELEASE_IMPL(major, minor) #major "." #minor
11713
11714void pm_options_version_for_current_ruby_set(pm_options_t *options) {
11715 const char *version = PM_VERSION_FOR_RELEASE(RUBY_API_VERSION_MAJOR, RUBY_API_VERSION_MINOR);
11716 pm_options_version_set(options, version, strlen(version));
11717}
11718
11719#undef NEW_ISEQ
11720#define NEW_ISEQ OLD_ISEQ
11721
11722#undef NEW_CHILD_ISEQ
11723#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_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
#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 MBCLEN_CHARFOUND_LEN(ret)
Old name of ONIGENC_MBCLEN_CHARFOUND_LEN.
Definition encoding.h:517
#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 MBCLEN_CHARFOUND_P(ret)
Old name of ONIGENC_MBCLEN_CHARFOUND_P.
Definition encoding.h:516
#define DBL2NUM
Old name of rb_float_new.
Definition double.h:29
#define xcalloc
Old name of ruby_xcalloc.
Definition xmalloc.h:55
#define NUM2LONG
Old name of RB_NUM2LONG.
Definition long.h:51
#define UINT2NUM
Old name of RB_UINT2NUM.
Definition int.h:46
#define CONST_ID
Old name of RUBY_CONST_ID.
Definition symbol.h:47
#define ruby_debug
This variable controls whether the interpreter is in debug mode.
Definition error.h:487
VALUE rb_eNotImpError
NotImplementedError exception.
Definition error.c:1437
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:661
VALUE rb_eStandardError
StandardError exception.
Definition error.c:1424
VALUE rb_eLoadError
LoadError exception.
Definition error.c:1445
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1427
VALUE rb_eNoMatchingPatternError
NoMatchingPatternError exception.
Definition error.c:1440
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:467
VALUE rb_eNoMatchingPatternKeyError
NoMatchingPatternKeyError exception.
Definition error.c:1441
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:1478
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition eval.c:1169
VALUE rb_eSyntaxError
SyntaxError exception.
Definition error.c:1444
VALUE rb_syserr_new(int n, const char *mesg)
Creates an exception object that represents the given C errno.
Definition error.c:3953
VALUE rb_cArray
Array class.
VALUE rb_cObject
Object class.
Definition object.c:61
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:95
VALUE rb_stdin
STDIN constant.
Definition io.c:201
VALUE rb_cHash
Hash class.
Definition hash.c:109
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1313
#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:615
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:603
int rb_enc_str_coderange(VALUE str)
Scans the passed string to collect its code range.
Definition string.c:945
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:12773
VALUE rb_enc_str_new_cstr(const char *ptr, rb_encoding *enc)
Identical to rb_enc_str_new(), except it assumes the passed pointer is a pointer to a C string.
Definition string.c:1153
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1120
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:546
#define INTEGER_PACK_LSWORD_FIRST
Stores/interprets the least significant word as the first word.
Definition bignum.h:528
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:9365
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Opens a file that closes on exec.
Definition io.c:328
VALUE rb_io_close(VALUE io)
Closes the IO.
Definition io.c:5780
VALUE rb_range_new(VALUE beg, VALUE end, int excl)
Creates a new Range.
Definition range.c:69
VALUE rb_rational_new(VALUE num, VALUE den)
Constructs a Rational, with reduction.
Definition rational.c:1985
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:3834
VALUE rb_str_tmp_new(long len)
Allocates a "temporary" string.
Definition string.c:1744
#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
VALUE rb_str_cat(VALUE dst, const char *src, long srclen)
Destructively appends the passed contents to the string.
Definition string.c:3602
#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:4071
VALUE rb_str_freeze(VALUE str)
This is the implementation of String#freeze.
Definition string.c:3312
#define rb_str_cat_cstr(buf, str)
Identical to rb_str_cat(), except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1657
VALUE rb_str_buf_new(long capa)
Allocates a "string buffer".
Definition string.c:1716
#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:1848
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:2047
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:3482
int rb_const_defined_at(VALUE space, ID name)
Identical to rb_const_defined(), except it doesn't look for parent classes.
Definition variable.c:3814
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:285
VALUE rb_id2sym(ID id)
Allocates an instance of rb_cSymbol that has the given id.
Definition symbol.c:1005
VALUE rb_sym2str(VALUE symbol)
Obtain a frozen string representation of a symbol (not including the leading colon).
Definition symbol.c:1024
@ 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:1482
int len
Length of the buffer.
Definition io.h:8
#define RB_OBJ_SHAREABLE_P(obj)
Queries if the passed object has previously classified as shareable or not.
Definition ractor.h:235
VALUE rb_ractor_make_shareable(VALUE obj)
Destructively transforms the passed object so that multiple Ractors can share it.
Definition ractor.c:1551
#define DECIMAL_SIZE_OF(expr)
An approximation of decimal representation size.
Definition util.h:48
#define RUBY_API_VERSION_MAJOR
Major version.
Definition version.h:64
#define RUBY_API_VERSION_MINOR
Minor version.
Definition version.h:70
#define RB_INT2NUM
Just another name of rb_int2num_inline.
Definition int.h:37
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
VALUE type(ANYARGS)
ANYARGS-ed function type.
PRISM_EXPORTED_FUNCTION PRISM_NODISCARD pm_parser_t * pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_options_t *options) PRISM_NONNULL(1)
Allocate and initialize a parser with the given start and end pointers.
Definition prism.c:22602
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:22635
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:22806
#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:51
#define RARRAY_AREF(a, i)
Definition rarray.h:403
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
Definition rarray.h:52
#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:39
@ PM_SOURCE_INIT_SUCCESS
Indicates that the source was successfully initialized.
Definition source.h:41
@ PM_SOURCE_INIT_ERROR_NON_REGULAR
Indicates that the file is not a regular file (e.g.
Definition source.h:58
@ PM_SOURCE_INIT_ERROR_DIRECTORY
Indicates that the file that was attempted to be opened was a directory.
Definition source.h:52
#define RTEST
This is an old name of RB_TEST.
AliasGlobalVariableNode.
Definition ast.h:1138
PM_NODE_ALIGNAS struct pm_node * new_name
AliasGlobalVariableNode::new_name.
Definition ast.h:1150
PM_NODE_ALIGNAS struct pm_node * old_name
AliasGlobalVariableNode::old_name.
Definition ast.h:1160
AliasMethodNode.
Definition ast.h:1185
PM_NODE_ALIGNAS struct pm_node * old_name
AliasMethodNode::old_name.
Definition ast.h:1219
PM_NODE_ALIGNAS struct pm_node * new_name
AliasMethodNode::new_name.
Definition ast.h:1203
AlternationPatternNode.
Definition ast.h:1244
PM_NODE_ALIGNAS struct pm_node * left
AlternationPatternNode::left.
Definition ast.h:1256
PM_NODE_ALIGNAS struct pm_node * right
AlternationPatternNode::right.
Definition ast.h:1266
AndNode.
Definition ast.h:1291
PM_NODE_ALIGNAS struct pm_node * left
AndNode::left.
Definition ast.h:1306
PM_NODE_ALIGNAS struct pm_node * right
AndNode::right.
Definition ast.h:1319
ArgumentsNode.
Definition ast.h:1351
pm_node_t base
The embedded base node.
Definition ast.h:1353
struct pm_node_list arguments
ArgumentsNode::arguments.
Definition ast.h:1363
ArrayNode.
Definition ast.h:1381
struct pm_node_list elements
ArrayNode::elements.
Definition ast.h:1390
ArrayPatternNode.
Definition ast.h:1441
struct pm_node_list requireds
ArrayPatternNode::requireds.
Definition ast.h:1469
PM_NODE_ALIGNAS struct pm_node * rest
ArrayPatternNode::rest.
Definition ast.h:1479
PM_NODE_ALIGNAS struct pm_node * constant
ArrayPatternNode::constant.
Definition ast.h:1459
struct pm_node_list posts
ArrayPatternNode::posts.
Definition ast.h:1489
AssocNode.
Definition ast.h:1524
PM_NODE_ALIGNAS struct pm_node * value
AssocNode::value.
Definition ast.h:1555
PM_NODE_ALIGNAS struct pm_node * key
AssocNode::key.
Definition ast.h:1542
AssocSplatNode.
Definition ast.h:1580
PM_NODE_ALIGNAS struct pm_node * value
AssocSplatNode::value.
Definition ast.h:1592
BackReferenceReadNode.
Definition ast.h:1617
pm_node_t base
The embedded base node.
Definition ast.h:1619
BeginNode.
Definition ast.h:1647
PM_NODE_ALIGNAS struct pm_else_node * else_clause
BeginNode::else_clause.
Definition ast.h:1689
PM_NODE_ALIGNAS struct pm_ensure_node * ensure_clause
BeginNode::ensure_clause.
Definition ast.h:1699
PM_NODE_ALIGNAS struct pm_statements_node * statements
BeginNode::statements.
Definition ast.h:1669
PM_NODE_ALIGNAS struct pm_rescue_node * rescue_clause
BeginNode::rescue_clause.
Definition ast.h:1679
BlockArgumentNode.
Definition ast.h:1724
PM_NODE_ALIGNAS struct pm_node * expression
BlockArgumentNode::expression.
Definition ast.h:1736
BlockLocalVariableNode.
Definition ast.h:1764
BlockNode.
Definition ast.h:1791
PM_NODE_ALIGNAS struct pm_node * parameters
BlockNode::parameters.
Definition ast.h:1817
pm_constant_id_list_t locals
BlockNode::locals.
Definition ast.h:1803
PM_NODE_ALIGNAS struct pm_node * body
BlockNode::body.
Definition ast.h:1827
BlockParameterNode.
Definition ast.h:1866
BlockParametersNode.
Definition ast.h:1919
BreakNode.
Definition ast.h:1992
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
BreakNode::arguments.
Definition ast.h:2004
CallAndWriteNode.
Definition ast.h:2035
PM_NODE_ALIGNAS struct pm_node * receiver
CallAndWriteNode::receiver.
Definition ast.h:2047
PM_NODE_ALIGNAS struct pm_node * value
CallAndWriteNode::value.
Definition ast.h:2107
pm_constant_id_t read_name
CallAndWriteNode::read_name.
Definition ast.h:2077
pm_constant_id_t write_name
CallAndWriteNode::write_name.
Definition ast.h:2087
CallNode.
Definition ast.h:2143
pm_location_t closing_loc
CallNode::closing_loc.
Definition ast.h:2224
pm_constant_id_t name
CallNode::name.
Definition ast.h:2184
pm_node_t base
The embedded base node.
Definition ast.h:2145
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
CallNode::arguments.
Definition ast.h:2214
pm_location_t message_loc
CallNode::message_loc.
Definition ast.h:2194
PM_NODE_ALIGNAS struct pm_node * block
CallNode::block.
Definition ast.h:2247
PM_NODE_ALIGNAS struct pm_node * receiver
CallNode::receiver.
Definition ast.h:2161
CallOperatorWriteNode.
Definition ast.h:2268
pm_constant_id_t read_name
CallOperatorWriteNode::read_name.
Definition ast.h:2310
pm_constant_id_t binary_operator
CallOperatorWriteNode::binary_operator.
Definition ast.h:2330
pm_constant_id_t write_name
CallOperatorWriteNode::write_name.
Definition ast.h:2320
PM_NODE_ALIGNAS struct pm_node * receiver
CallOperatorWriteNode::receiver.
Definition ast.h:2280
PM_NODE_ALIGNAS struct pm_node * value
CallOperatorWriteNode::value.
Definition ast.h:2350
CallOrWriteNode.
Definition ast.h:2371
PM_NODE_ALIGNAS struct pm_node * receiver
CallOrWriteNode::receiver.
Definition ast.h:2383
PM_NODE_ALIGNAS struct pm_node * value
CallOrWriteNode::value.
Definition ast.h:2443
pm_constant_id_t write_name
CallOrWriteNode::write_name.
Definition ast.h:2423
pm_constant_id_t read_name
CallOrWriteNode::read_name.
Definition ast.h:2413
CallTargetNode.
Definition ast.h:2472
pm_constant_id_t name
CallTargetNode::name.
Definition ast.h:2504
PM_NODE_ALIGNAS struct pm_node * receiver
CallTargetNode::receiver.
Definition ast.h:2484
CapturePatternNode.
Definition ast.h:2529
PM_NODE_ALIGNAS struct pm_node * value
CapturePatternNode::value.
Definition ast.h:2541
PM_NODE_ALIGNAS struct pm_local_variable_target_node * target
CapturePatternNode::target.
Definition ast.h:2551
CaseMatchNode.
Definition ast.h:2578
struct pm_node_list conditions
CaseMatchNode::conditions.
Definition ast.h:2600
PM_NODE_ALIGNAS struct pm_node * predicate
CaseMatchNode::predicate.
Definition ast.h:2590
PM_NODE_ALIGNAS struct pm_else_node * else_clause
CaseMatchNode::else_clause.
Definition ast.h:2610
CaseNode.
Definition ast.h:2647
PM_NODE_ALIGNAS struct pm_else_node * else_clause
CaseNode::else_clause.
Definition ast.h:2679
struct pm_node_list conditions
CaseNode::conditions.
Definition ast.h:2669
PM_NODE_ALIGNAS struct pm_node * predicate
CaseNode::predicate.
Definition ast.h:2659
ClassNode.
Definition ast.h:2714
pm_constant_id_list_t locals
ClassNode::locals.
Definition ast.h:2721
PM_NODE_ALIGNAS struct pm_node * superclass
ClassNode::superclass.
Definition ast.h:2756
pm_constant_id_t name
ClassNode::name.
Definition ast.h:2785
PM_NODE_ALIGNAS struct pm_node * constant_path
ClassNode::constant_path.
Definition ast.h:2736
PM_NODE_ALIGNAS struct pm_node * body
ClassNode::body.
Definition ast.h:2766
ClassVariableAndWriteNode.
Definition ast.h:2800
PM_NODE_ALIGNAS struct pm_node * value
ClassVariableAndWriteNode::value.
Definition ast.h:2842
pm_constant_id_t name
ClassVariableAndWriteNode::name.
Definition ast.h:2812
ClassVariableOperatorWriteNode.
Definition ast.h:2857
pm_constant_id_t name
ClassVariableOperatorWriteNode::name.
Definition ast.h:2864
PM_NODE_ALIGNAS struct pm_node * value
ClassVariableOperatorWriteNode::value.
Definition ast.h:2879
pm_constant_id_t binary_operator
ClassVariableOperatorWriteNode::binary_operator.
Definition ast.h:2884
ClassVariableOrWriteNode.
Definition ast.h:2899
PM_NODE_ALIGNAS struct pm_node * value
ClassVariableOrWriteNode::value.
Definition ast.h:2921
pm_constant_id_t name
ClassVariableOrWriteNode::name.
Definition ast.h:2906
ClassVariableReadNode.
Definition ast.h:2936
pm_constant_id_t name
ClassVariableReadNode::name.
Definition ast.h:2949
ClassVariableTargetNode.
Definition ast.h:2964
pm_constant_id_t name
ClassVariableTargetNode::name.
Definition ast.h:2971
ClassVariableWriteNode.
Definition ast.h:2986
PM_NODE_ALIGNAS struct pm_node * value
ClassVariableWriteNode::value.
Definition ast.h:3022
pm_constant_id_t name
ClassVariableWriteNode::name.
Definition ast.h:2999
ConstantAndWriteNode.
Definition ast.h:3047
PM_NODE_ALIGNAS struct pm_node * value
ConstantAndWriteNode::value.
Definition ast.h:3069
pm_location_t name_loc
ConstantAndWriteNode::name_loc.
Definition ast.h:3059
pm_constant_id_t name
ConstantAndWriteNode::name.
Definition ast.h:3054
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:3084
pm_constant_id_t name
ConstantOperatorWriteNode::name.
Definition ast.h:3091
pm_location_t name_loc
ConstantOperatorWriteNode::name_loc.
Definition ast.h:3096
pm_constant_id_t binary_operator
ConstantOperatorWriteNode::binary_operator.
Definition ast.h:3111
PM_NODE_ALIGNAS struct pm_node * value
ConstantOperatorWriteNode::value.
Definition ast.h:3106
ConstantOrWriteNode.
Definition ast.h:3126
PM_NODE_ALIGNAS struct pm_node * value
ConstantOrWriteNode::value.
Definition ast.h:3148
pm_location_t name_loc
ConstantOrWriteNode::name_loc.
Definition ast.h:3138
pm_constant_id_t name
ConstantOrWriteNode::name.
Definition ast.h:3133
ConstantPathAndWriteNode.
Definition ast.h:3163
PM_NODE_ALIGNAS struct pm_constant_path_node * target
ConstantPathAndWriteNode::target.
Definition ast.h:3170
PM_NODE_ALIGNAS struct pm_node * value
ConstantPathAndWriteNode::value.
Definition ast.h:3180
ConstantPathNode.
Definition ast.h:3195
PM_NODE_ALIGNAS struct pm_node * parent
ConstantPathNode::parent.
Definition ast.h:3213
pm_constant_id_t name
ConstantPathNode::name.
Definition ast.h:3220
ConstantPathOperatorWriteNode.
Definition ast.h:3261
PM_NODE_ALIGNAS struct pm_constant_path_node * target
ConstantPathOperatorWriteNode::target.
Definition ast.h:3268
pm_constant_id_t binary_operator
ConstantPathOperatorWriteNode::binary_operator.
Definition ast.h:3283
PM_NODE_ALIGNAS struct pm_node * value
ConstantPathOperatorWriteNode::value.
Definition ast.h:3278
ConstantPathOrWriteNode.
Definition ast.h:3298
PM_NODE_ALIGNAS struct pm_node * value
ConstantPathOrWriteNode::value.
Definition ast.h:3315
PM_NODE_ALIGNAS struct pm_constant_path_node * target
ConstantPathOrWriteNode::target.
Definition ast.h:3305
ConstantPathTargetNode.
Definition ast.h:3330
pm_constant_id_t name
ConstantPathTargetNode::name.
Definition ast.h:3342
PM_NODE_ALIGNAS struct pm_node * parent
ConstantPathTargetNode::parent.
Definition ast.h:3337
ConstantPathWriteNode.
Definition ast.h:3373
PM_NODE_ALIGNAS struct pm_node * value
ConstantPathWriteNode::value.
Definition ast.h:3408
PM_NODE_ALIGNAS struct pm_constant_path_node * target
ConstantPathWriteNode::target.
Definition ast.h:3388
ConstantReadNode.
Definition ast.h:3423
pm_node_t base
The embedded base node.
Definition ast.h:3425
pm_constant_id_t name
ConstantReadNode::name.
Definition ast.h:3436
ConstantTargetNode.
Definition ast.h:3451
pm_constant_id_t name
ConstantTargetNode::name.
Definition ast.h:3458
ConstantWriteNode.
Definition ast.h:3473
PM_NODE_ALIGNAS struct pm_node * value
ConstantWriteNode::value.
Definition ast.h:3509
pm_constant_id_t name
ConstantWriteNode::name.
Definition ast.h:3486
DefNode.
Definition ast.h:3535
pm_constant_id_t name
DefNode::name.
Definition ast.h:3542
PM_NODE_ALIGNAS struct pm_parameters_node * parameters
DefNode::parameters.
Definition ast.h:3557
pm_node_t base
The embedded base node.
Definition ast.h:3537
PM_NODE_ALIGNAS struct pm_node * receiver
DefNode::receiver.
Definition ast.h:3552
PM_NODE_ALIGNAS struct pm_node * body
DefNode::body.
Definition ast.h:3562
pm_constant_id_list_t locals
DefNode::locals.
Definition ast.h:3567
DefinedNode.
Definition ast.h:3612
PM_NODE_ALIGNAS struct pm_node * value
DefinedNode::value.
Definition ast.h:3624
ElseNode.
Definition ast.h:3649
PM_NODE_ALIGNAS struct pm_statements_node * statements
ElseNode::statements.
Definition ast.h:3661
EmbeddedStatementsNode.
Definition ast.h:3681
PM_NODE_ALIGNAS struct pm_statements_node * statements
EmbeddedStatementsNode::statements.
Definition ast.h:3693
EmbeddedVariableNode.
Definition ast.h:3713
PM_NODE_ALIGNAS struct pm_node * variable
EmbeddedVariableNode::variable.
Definition ast.h:3725
EnsureNode.
Definition ast.h:3744
PM_NODE_ALIGNAS struct pm_statements_node * statements
EnsureNode::statements.
Definition ast.h:3756
Context struct for collecting errors via callback.
Callback for formatting non-UTF8 errors.
FindPatternNode.
Definition ast.h:3823
PM_NODE_ALIGNAS struct pm_splat_node * left
FindPatternNode::left.
Definition ast.h:3848
PM_NODE_ALIGNAS struct pm_splat_node * right
FindPatternNode::right.
Definition ast.h:3874
PM_NODE_ALIGNAS struct pm_node * constant
FindPatternNode::constant.
Definition ast.h:3835
struct pm_node_list requireds
FindPatternNode::requireds.
Definition ast.h:3861
FlipFlopNode.
Definition ast.h:3918
PM_NODE_ALIGNAS struct pm_node * left
FlipFlopNode::left.
Definition ast.h:3925
pm_node_t base
The embedded base node.
Definition ast.h:3920
PM_NODE_ALIGNAS struct pm_node * right
FlipFlopNode::right.
Definition ast.h:3930
FloatNode.
Definition ast.h:3950
double value
FloatNode::value.
Definition ast.h:3959
ForNode.
Definition ast.h:3974
PM_NODE_ALIGNAS struct pm_node * collection
ForNode::collection.
Definition ast.h:3996
PM_NODE_ALIGNAS struct pm_statements_node * statements
ForNode::statements.
Definition ast.h:4008
ForwardingSuperNode.
Definition ast.h:4105
PM_NODE_ALIGNAS struct pm_block_node * block
ForwardingSuperNode::block.
Definition ast.h:4125
GlobalVariableAndWriteNode.
Definition ast.h:4140
PM_NODE_ALIGNAS struct pm_node * value
GlobalVariableAndWriteNode::value.
Definition ast.h:4162
pm_constant_id_t name
GlobalVariableAndWriteNode::name.
Definition ast.h:4147
GlobalVariableOperatorWriteNode.
Definition ast.h:4177
PM_NODE_ALIGNAS struct pm_node * value
GlobalVariableOperatorWriteNode::value.
Definition ast.h:4199
pm_constant_id_t name
GlobalVariableOperatorWriteNode::name.
Definition ast.h:4184
pm_constant_id_t binary_operator
GlobalVariableOperatorWriteNode::binary_operator.
Definition ast.h:4204
GlobalVariableOrWriteNode.
Definition ast.h:4219
pm_constant_id_t name
GlobalVariableOrWriteNode::name.
Definition ast.h:4226
PM_NODE_ALIGNAS struct pm_node * value
GlobalVariableOrWriteNode::value.
Definition ast.h:4241
GlobalVariableReadNode.
Definition ast.h:4256
pm_constant_id_t name
GlobalVariableReadNode::name.
Definition ast.h:4269
GlobalVariableTargetNode.
Definition ast.h:4284
pm_constant_id_t name
GlobalVariableTargetNode::name.
Definition ast.h:4291
GlobalVariableWriteNode.
Definition ast.h:4306
pm_constant_id_t name
GlobalVariableWriteNode::name.
Definition ast.h:4319
PM_NODE_ALIGNAS struct pm_node * value
GlobalVariableWriteNode::value.
Definition ast.h:4342
HashNode.
Definition ast.h:4367
struct pm_node_list elements
HashNode::elements.
Definition ast.h:4392
HashPatternNode.
Definition ast.h:4426
PM_NODE_ALIGNAS struct pm_node * constant
HashPatternNode::constant.
Definition ast.h:4441
struct pm_node_list elements
HashPatternNode::elements.
Definition ast.h:4451
PM_NODE_ALIGNAS struct pm_node * rest
HashPatternNode::rest.
Definition ast.h:4467
IfNode.
Definition ast.h:4514
PM_NODE_ALIGNAS struct pm_node * predicate
IfNode::predicate.
Definition ast.h:4546
PM_NODE_ALIGNAS struct pm_statements_node * statements
IfNode::statements.
Definition ast.h:4573
ImaginaryNode.
Definition ast.h:4619
PM_NODE_ALIGNAS struct pm_node * numeric
ImaginaryNode::numeric.
Definition ast.h:4626
ImplicitNode.
Definition ast.h:4647
PM_NODE_ALIGNAS struct pm_node * value
ImplicitNode::value.
Definition ast.h:4654
InNode.
Definition ast.h:4695
PM_NODE_ALIGNAS struct pm_node * pattern
InNode::pattern.
Definition ast.h:4702
PM_NODE_ALIGNAS struct pm_statements_node * statements
InNode::statements.
Definition ast.h:4707
IndexAndWriteNode.
Definition ast.h:4738
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
IndexAndWriteNode::arguments.
Definition ast.h:4760
PM_NODE_ALIGNAS struct pm_block_argument_node * block
IndexAndWriteNode::block.
Definition ast.h:4770
PM_NODE_ALIGNAS struct pm_node * receiver
IndexAndWriteNode::receiver.
Definition ast.h:4745
PM_NODE_ALIGNAS struct pm_node * value
IndexAndWriteNode::value.
Definition ast.h:4780
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:4801
PM_NODE_ALIGNAS struct pm_block_argument_node * block
IndexOperatorWriteNode::block.
Definition ast.h:4833
PM_NODE_ALIGNAS struct pm_node * receiver
IndexOperatorWriteNode::receiver.
Definition ast.h:4808
PM_NODE_ALIGNAS struct pm_node * value
IndexOperatorWriteNode::value.
Definition ast.h:4848
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
IndexOperatorWriteNode::arguments.
Definition ast.h:4823
pm_constant_id_t binary_operator
IndexOperatorWriteNode::binary_operator.
Definition ast.h:4838
IndexOrWriteNode.
Definition ast.h:4869
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
IndexOrWriteNode::arguments.
Definition ast.h:4891
PM_NODE_ALIGNAS struct pm_node * value
IndexOrWriteNode::value.
Definition ast.h:4911
PM_NODE_ALIGNAS struct pm_node * receiver
IndexOrWriteNode::receiver.
Definition ast.h:4876
PM_NODE_ALIGNAS struct pm_block_argument_node * block
IndexOrWriteNode::block.
Definition ast.h:4901
IndexTargetNode.
Definition ast.h:4940
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
IndexTargetNode::arguments.
Definition ast.h:4957
PM_NODE_ALIGNAS struct pm_block_argument_node * block
IndexTargetNode::block.
Definition ast.h:4967
PM_NODE_ALIGNAS struct pm_node * receiver
IndexTargetNode::receiver.
Definition ast.h:4947
InstanceVariableAndWriteNode.
Definition ast.h:4982
PM_NODE_ALIGNAS struct pm_node * value
InstanceVariableAndWriteNode::value.
Definition ast.h:5004
pm_constant_id_t name
InstanceVariableAndWriteNode::name.
Definition ast.h:4989
InstanceVariableOperatorWriteNode.
Definition ast.h:5019
pm_constant_id_t binary_operator
InstanceVariableOperatorWriteNode::binary_operator.
Definition ast.h:5046
PM_NODE_ALIGNAS struct pm_node * value
InstanceVariableOperatorWriteNode::value.
Definition ast.h:5041
pm_constant_id_t name
InstanceVariableOperatorWriteNode::name.
Definition ast.h:5026
InstanceVariableOrWriteNode.
Definition ast.h:5061
pm_constant_id_t name
InstanceVariableOrWriteNode::name.
Definition ast.h:5068
PM_NODE_ALIGNAS struct pm_node * value
InstanceVariableOrWriteNode::value.
Definition ast.h:5083
InstanceVariableReadNode.
Definition ast.h:5098
pm_constant_id_t name
InstanceVariableReadNode::name.
Definition ast.h:5111
InstanceVariableTargetNode.
Definition ast.h:5126
pm_constant_id_t name
InstanceVariableTargetNode::name.
Definition ast.h:5133
InstanceVariableWriteNode.
Definition ast.h:5148
PM_NODE_ALIGNAS struct pm_node * value
InstanceVariableWriteNode::value.
Definition ast.h:5184
pm_constant_id_t name
InstanceVariableWriteNode::name.
Definition ast.h:5161
IntegerNode.
Definition ast.h:5215
pm_integer_t value
IntegerNode::value.
Definition ast.h:5224
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:5252
InterpolatedRegularExpressionNode.
Definition ast.h:5297
InterpolatedStringNode.
Definition ast.h:5333
struct pm_node_list parts
InterpolatedStringNode::parts.
Definition ast.h:5345
InterpolatedSymbolNode.
Definition ast.h:5365
struct pm_node_list parts
InterpolatedSymbolNode::parts.
Definition ast.h:5377
InterpolatedXStringNode.
Definition ast.h:5397
struct pm_node_list parts
InterpolatedXStringNode::parts.
Definition ast.h:5409
KeywordHashNode.
Definition ast.h:5466
struct pm_node_list elements
KeywordHashNode::elements.
Definition ast.h:5473
KeywordRestParameterNode.
Definition ast.h:5492
LambdaNode.
Definition ast.h:5524
pm_location_t opening_loc
LambdaNode::opening_loc.
Definition ast.h:5541
pm_constant_id_list_t locals
LambdaNode::locals.
Definition ast.h:5531
PM_NODE_ALIGNAS struct pm_node * body
LambdaNode::body.
Definition ast.h:5556
PM_NODE_ALIGNAS struct pm_node * parameters
LambdaNode::parameters.
Definition ast.h:5551
A line and column in a string.
uint32_t column
The column in bytes.
int32_t line
The line number.
A list of offsets of the start of lines in a string.
uint32_t * offsets
The list of offsets.
size_t size
The number of offsets in the list.
the getlocal and setlocal instructions require two parameters.
LocalVariableAndWriteNode.
Definition ast.h:5571
pm_constant_id_t name
LocalVariableAndWriteNode::name.
Definition ast.h:5593
uint32_t depth
LocalVariableAndWriteNode::depth.
Definition ast.h:5598
PM_NODE_ALIGNAS struct pm_node * value
LocalVariableAndWriteNode::value.
Definition ast.h:5588
LocalVariableOperatorWriteNode.
Definition ast.h:5613
uint32_t depth
LocalVariableOperatorWriteNode::depth.
Definition ast.h:5645
pm_constant_id_t binary_operator
LocalVariableOperatorWriteNode::binary_operator.
Definition ast.h:5640
PM_NODE_ALIGNAS struct pm_node * value
LocalVariableOperatorWriteNode::value.
Definition ast.h:5630
pm_constant_id_t name
LocalVariableOperatorWriteNode::name.
Definition ast.h:5635
LocalVariableOrWriteNode.
Definition ast.h:5660
PM_NODE_ALIGNAS struct pm_node * value
LocalVariableOrWriteNode::value.
Definition ast.h:5677
uint32_t depth
LocalVariableOrWriteNode::depth.
Definition ast.h:5687
pm_constant_id_t name
LocalVariableOrWriteNode::name.
Definition ast.h:5682
LocalVariableReadNode.
Definition ast.h:5702
uint32_t depth
LocalVariableReadNode::depth.
Definition ast.h:5732
pm_constant_id_t name
LocalVariableReadNode::name.
Definition ast.h:5719
LocalVariableTargetNode.
Definition ast.h:5750
uint32_t depth
LocalVariableTargetNode::depth.
Definition ast.h:5762
pm_constant_id_t name
LocalVariableTargetNode::name.
Definition ast.h:5757
LocalVariableWriteNode.
Definition ast.h:5777
PM_NODE_ALIGNAS struct pm_node * value
LocalVariableWriteNode::value.
Definition ast.h:5830
uint32_t depth
LocalVariableWriteNode::depth.
Definition ast.h:5803
pm_constant_id_t name
LocalVariableWriteNode::name.
Definition ast.h:5790
This struct represents a slice in the source code, defined by an offset and a length.
Definition ast.h:554
uint32_t start
The offset of the location from the start of the source.
Definition ast.h:556
uint32_t length
The length of the location.
Definition ast.h:559
MatchLastLineNode.
Definition ast.h:5868
MatchPredicateNode.
Definition ast.h:5905
PM_NODE_ALIGNAS struct pm_node * pattern
MatchPredicateNode::pattern.
Definition ast.h:5917
PM_NODE_ALIGNAS struct pm_node * value
MatchPredicateNode::value.
Definition ast.h:5912
MatchRequiredNode.
Definition ast.h:5937
PM_NODE_ALIGNAS struct pm_node * pattern
MatchRequiredNode::pattern.
Definition ast.h:5998
PM_NODE_ALIGNAS struct pm_node * value
MatchRequiredNode::value.
Definition ast.h:5949
MatchWriteNode.
Definition ast.h:6023
PM_NODE_ALIGNAS struct pm_call_node * call
MatchWriteNode::call.
Definition ast.h:6030
struct pm_node_list targets
MatchWriteNode::targets.
Definition ast.h:6035
ModuleNode.
Definition ast.h:6050
PM_NODE_ALIGNAS struct pm_node * constant_path
ModuleNode::constant_path.
Definition ast.h:6067
PM_NODE_ALIGNAS struct pm_node * body
ModuleNode::body.
Definition ast.h:6072
pm_constant_id_list_t locals
ModuleNode::locals.
Definition ast.h:6057
pm_constant_id_t name
ModuleNode::name.
Definition ast.h:6082
MultiTargetNode.
Definition ast.h:6102
PM_NODE_ALIGNAS struct pm_node * rest
MultiTargetNode::rest.
Definition ast.h:6139
struct pm_node_list lefts
MultiTargetNode::lefts.
Definition ast.h:6119
struct pm_node_list rights
MultiTargetNode::rights.
Definition ast.h:6149
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:6184
PM_NODE_ALIGNAS struct pm_node * rest
MultiWriteNode::rest.
Definition ast.h:6221
struct pm_node_list rights
MultiWriteNode::rights.
Definition ast.h:6231
PM_NODE_ALIGNAS struct pm_node * value
MultiWriteNode::value.
Definition ast.h:6271
struct pm_node_list lefts
MultiWriteNode::lefts.
Definition ast.h:6201
NextNode.
Definition ast.h:6286
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
NextNode::arguments.
Definition ast.h:6293
A list of nodes in the source, most often used for lists of children.
Definition ast.h:567
size_t size
The number of nodes in the list.
Definition ast.h:569
struct pm_node ** nodes
The nodes in the list.
Definition ast.h:575
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:1065
pm_node_type_t type
This represents the type of the node.
Definition ast.h:1070
uint32_t node_id
The unique identifier for this node, which is deterministic based on the source.
Definition ast.h:1082
pm_node_flags_t flags
This represents any flags on the node.
Definition ast.h:1076
pm_location_t location
This is the location of the node in the source.
Definition ast.h:1088
NumberedParametersNode.
Definition ast.h:6386
NumberedReferenceReadNode.
Definition ast.h:6408
uint32_t number
NumberedReferenceReadNode::number.
Definition ast.h:6423
OptionalKeywordParameterNode.
Definition ast.h:6442
pm_constant_id_t name
OptionalKeywordParameterNode::name.
Definition ast.h:6449
PM_NODE_ALIGNAS struct pm_node * value
OptionalKeywordParameterNode::value.
Definition ast.h:6459
OptionalParameterNode.
Definition ast.h:6478
pm_constant_id_t name
OptionalParameterNode::name.
Definition ast.h:6485
PM_NODE_ALIGNAS struct pm_node * value
OptionalParameterNode::value.
Definition ast.h:6500
OrNode.
Definition ast.h:6515
PM_NODE_ALIGNAS struct pm_node * right
OrNode::right.
Definition ast.h:6543
PM_NODE_ALIGNAS struct pm_node * left
OrNode::left.
Definition ast.h:6530
ParametersNode.
Definition ast.h:6569
PM_NODE_ALIGNAS struct pm_node * block
ParametersNode::block.
Definition ast.h:6606
struct pm_node_list requireds
ParametersNode::requireds.
Definition ast.h:6576
struct pm_node_list optionals
ParametersNode::optionals.
Definition ast.h:6581
struct pm_node_list posts
ParametersNode::posts.
Definition ast.h:6591
pm_node_t base
The embedded base node.
Definition ast.h:6571
PM_NODE_ALIGNAS struct pm_node * rest
ParametersNode::rest.
Definition ast.h:6586
struct pm_node_list keywords
ParametersNode::keywords.
Definition ast.h:6596
PM_NODE_ALIGNAS struct pm_node * keyword_rest
ParametersNode::keyword_rest.
Definition ast.h:6601
ParenthesesNode.
Definition ast.h:6624
PM_NODE_ALIGNAS struct pm_node * body
ParenthesesNode::body.
Definition ast.h:6631
The format that will be used to format the errors into the output.
size_t blank_prefix_length
The length of the blank prefix.
const char * blank_prefix
The prefix that will be used for blank lines.
size_t divider_length
The length of the divider.
const char * number_prefix
The prefix that will be used for line numbers.
const char * divider
The divider that will be used between sections of source code.
An error that is going to be formatted into the output.
uint32_t column_end
The column end of the diagnostic message.
int32_t line
The start line of the diagnostic message.
const pm_diagnostic_t * error
A pointer to the diagnostic that was generated during parsing.
uint32_t column_start
The column start of the diagnostic message.
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:6656
PinnedVariableNode.
Definition ast.h:6713
PM_NODE_ALIGNAS struct pm_node * variable
PinnedVariableNode::variable.
Definition ast.h:6725
PostExecutionNode.
Definition ast.h:6750
PM_NODE_ALIGNAS struct pm_statements_node * statements
PostExecutionNode::statements.
Definition ast.h:6757
PreExecutionNode.
Definition ast.h:6787
PM_NODE_ALIGNAS struct pm_statements_node * statements
PreExecutionNode::statements.
Definition ast.h:6794
Context for the error processing callback used in pm_parse_process_error.
ProgramNode.
Definition ast.h:6821
PM_NODE_ALIGNAS struct pm_statements_node * statements
ProgramNode::statements.
Definition ast.h:6833
RangeNode.
Definition ast.h:6854
PM_NODE_ALIGNAS struct pm_node * right
RangeNode::right.
Definition ast.h:6883
PM_NODE_ALIGNAS struct pm_node * left
RangeNode::left.
Definition ast.h:6869
RationalNode.
Definition ast.h:6911
pm_integer_t denominator
RationalNode::denominator.
Definition ast.h:6931
pm_integer_t numerator
RationalNode::numerator.
Definition ast.h:6922
RegularExpressionNode.
Definition ast.h:6976
RequiredKeywordParameterNode.
Definition ast.h:7017
RequiredParameterNode.
Definition ast.h:7048
pm_constant_id_t name
RequiredParameterNode::name.
Definition ast.h:7055
RescueModifierNode.
Definition ast.h:7070
PM_NODE_ALIGNAS struct pm_node * expression
RescueModifierNode::expression.
Definition ast.h:7077
PM_NODE_ALIGNAS struct pm_node * rescue_expression
RescueModifierNode::rescue_expression.
Definition ast.h:7087
RescueNode.
Definition ast.h:7107
PM_NODE_ALIGNAS struct pm_statements_node * statements
RescueNode::statements.
Definition ast.h:7139
PM_NODE_ALIGNAS struct pm_rescue_node * subsequent
RescueNode::subsequent.
Definition ast.h:7144
PM_NODE_ALIGNAS struct pm_node * reference
RescueNode::reference.
Definition ast.h:7129
struct pm_node_list exceptions
RescueNode::exceptions.
Definition ast.h:7119
RestParameterNode.
Definition ast.h:7163
ReturnNode.
Definition ast.h:7212
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
ReturnNode::arguments.
Definition ast.h:7224
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:7262
PM_NODE_ALIGNAS struct pm_node * write
ShareableConstantNode::write.
Definition ast.h:7271
pm_node_t base
The embedded base node.
Definition ast.h:7264
SingletonClassNode.
Definition ast.h:7286
PM_NODE_ALIGNAS struct pm_node * body
SingletonClassNode::body.
Definition ast.h:7313
pm_constant_id_list_t locals
SingletonClassNode::locals.
Definition ast.h:7293
PM_NODE_ALIGNAS struct pm_node * expression
SingletonClassNode::expression.
Definition ast.h:7308
SourceFileNode.
Definition ast.h:7356
pm_string_t filepath
SourceFileNode::filepath.
Definition ast.h:7365
SplatNode.
Definition ast.h:7397
PM_NODE_ALIGNAS struct pm_node * expression
SplatNode::expression.
Definition ast.h:7409
StatementsNode.
Definition ast.h:7424
struct pm_node_list body
StatementsNode::body.
Definition ast.h:7431
pm_node_t base
The embedded base node.
Definition ast.h:7426
StringNode.
Definition ast.h:7458
pm_string_t unescaped
StringNode::unescaped.
Definition ast.h:7480
A generic string type that can have various ownership semantics.
Definition stringy.h:18
SuperNode.
Definition ast.h:7500
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
SuperNode::arguments.
Definition ast.h:7519
PM_NODE_ALIGNAS struct pm_node * block
SuperNode::block.
Definition ast.h:7529
SymbolNode.
Definition ast.h:7552
pm_string_t unescaped
SymbolNode::unescaped.
Definition ast.h:7574
pm_node_t base
The embedded base node.
Definition ast.h:7554
UndefNode.
Definition ast.h:7606
struct pm_node_list names
UndefNode::names.
Definition ast.h:7613
UnlessNode.
Definition ast.h:7636
PM_NODE_ALIGNAS struct pm_node * predicate
UnlessNode::predicate.
Definition ast.h:7664
PM_NODE_ALIGNAS struct pm_statements_node * statements
UnlessNode::statements.
Definition ast.h:7685
PM_NODE_ALIGNAS struct pm_else_node * else_clause
UnlessNode::else_clause.
Definition ast.h:7695
UntilNode.
Definition ast.h:7726
pm_node_t base
The embedded base node.
Definition ast.h:7728
Context for emitting warnings via callback.
WhenNode.
Definition ast.h:7770
WhileNode.
Definition ast.h:7813
pm_node_t base
The embedded base node.
Definition ast.h:7815
XStringNode.
Definition ast.h:7859
pm_string_t unescaped
XStringNode::unescaped.
Definition ast.h:7881
YieldNode.
Definition ast.h:7896
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
YieldNode::arguments.
Definition ast.h:7913
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