Ruby 4.1.0dev (2026-09-22 revision ad23fd4a519fde4fc96c7326fb9b0347cbc347b8)
prism_compile.c (ad23fd4a519fde4fc96c7326fb9b0347cbc347b8)
1#include "prism.h"
2#include "ruby/version.h"
3
4#include <fcntl.h>
5
11typedef struct {
13 int32_t line;
14
16 uint32_t node_id;
18
19/******************************************************************************/
20/* These macros operate on pm_node_location_t structs as opposed to NODE*s. */
21/******************************************************************************/
22
23#define PUSH_ADJUST(seq, location, label) \
24 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), (int) (location).line))
25
26#define PUSH_ADJUST_RESTORE(seq, label) \
27 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), -1))
28
29#define PUSH_INSN(seq, location, insn) \
30 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 0))
31
32#define PUSH_INSN1(seq, location, insn, op1) \
33 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 1, (VALUE)(op1)))
34
35#define PUSH_INSN2(seq, location, insn, op1, op2) \
36 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 2, (VALUE)(op1), (VALUE)(op2)))
37
38#define PUSH_INSN3(seq, location, insn, op1, op2, op3) \
39 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3)))
40
41#define PUSH_INSNL(seq, location, insn, label) \
42 (PUSH_INSN1(seq, location, insn, label), LABEL_REF(label))
43
44#define PUSH_LABEL(seq, label) \
45 ADD_ELEM((seq), (LINK_ELEMENT *) (label))
46
47#define PUSH_SEND_R(seq, location, id, argc, block, flag, keywords) \
48 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (int) (location).line, (int) (location).node_id, (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords)))
49
50#define PUSH_SEND(seq, location, id, argc) \
51 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)INT2FIX(0), NULL)
52
53#define PUSH_SEND_WITH_FLAG(seq, location, id, argc, flag) \
54 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)(flag), NULL)
55
56#define PUSH_SEND_WITH_BLOCK(seq, location, id, argc, block) \
57 PUSH_SEND_R((seq), location, (id), (argc), (block), (VALUE)INT2FIX(0), NULL)
58
59#define PUSH_CALL(seq, location, id, argc) \
60 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
61
62#define PUSH_CALL_WITH_BLOCK(seq, location, id, argc, block) \
63 PUSH_SEND_R((seq), location, (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
64
65#define PUSH_TRACE(seq, event) \
66 ADD_ELEM((seq), (LINK_ELEMENT *) new_trace_body(iseq, (event), 0))
67
68#define PUSH_CATCH_ENTRY(type, ls, le, iseqv, lc) \
69 ADD_CATCH_ENTRY((type), (ls), (le), (iseqv), (lc))
70
71#define PUSH_SEQ(seq1, seq2) \
72 APPEND_LIST((seq1), (seq2))
73
74#define PUSH_SYNTHETIC_PUTNIL(seq, iseq) \
75 do { \
76 int lineno = ISEQ_COMPILE_DATA(iseq)->last_line; \
77 if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq)); \
78 ADD_SYNTHETIC_INSN(seq, lineno, -1, putnil); \
79 } while (0)
80
81/******************************************************************************/
82/* These functions compile getlocal/setlocal instructions but operate on */
83/* prism locations instead of NODEs. */
84/******************************************************************************/
85
86static void
87pm_iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int node_id, int idx, int level)
88{
89 if (iseq_local_block_param_p(iseq, idx, level)) {
90 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(getblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
91 }
92 else {
93 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(getlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
94 }
95 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qfalse);
96}
97
98static void
99pm_iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int node_id, int idx, int level)
100{
101 if (iseq_local_block_param_p(iseq, idx, level)) {
102 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(setblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
103 }
104 else {
105 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(setlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
106 }
107 update_lvar_state(iseq, level, idx);
108 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue);
109}
110
111#define PUSH_GETLOCAL(seq, location, idx, level) \
112 pm_iseq_add_getlocal(iseq, (seq), (int) (location).line, (int) (location).node_id, (idx), (level))
113
114#define PUSH_SETLOCAL(seq, location, idx, level) \
115 pm_iseq_add_setlocal(iseq, (seq), (int) (location).line, (int) (location).node_id, (idx), (level))
116
117/******************************************************************************/
118/* These are helper macros for the compiler. */
119/******************************************************************************/
120
121#define OLD_ISEQ NEW_ISEQ
122#undef NEW_ISEQ
123
124#define NEW_ISEQ(node, name, type, line_no) \
125 pm_new_child_iseq(iseq, (node), rb_fstring(name), 0, (type), (line_no))
126
127#define OLD_CHILD_ISEQ NEW_CHILD_ISEQ
128#undef NEW_CHILD_ISEQ
129
130#define NEW_CHILD_ISEQ(node, name, type, line_no) \
131 pm_new_child_iseq(iseq, (node), rb_fstring(name), iseq, (type), (line_no))
132
133#define PM_COMPILE(node) \
134 pm_compile_node(iseq, (node), ret, popped, scope_node)
135
136#define PM_COMPILE_INTO_ANCHOR(_ret, node) \
137 pm_compile_node(iseq, (node), _ret, popped, scope_node)
138
139#define PM_COMPILE_POPPED(node) \
140 pm_compile_node(iseq, (node), ret, true, scope_node)
141
142#define PM_COMPILE_NOT_POPPED(node) \
143 pm_compile_node(iseq, (node), ret, false, scope_node)
144
145// Direct-indexed lookup table. -1 means "not present".
146#define PM_INDEX_LOOKUP_TABLE_INIT { .values = NULL, .capacity = 0, .owned = false }
147
148static inline void
149pm_index_lookup_table_init(pm_index_lookup_table_t *table, int constants_size, rb_iseq_t *iseq)
150{
151 int capacity = constants_size + PM_INDEX_LOOKUP_SPECIALS;
152 table->values = compile_data_alloc2_type(iseq, int, capacity);
153 memset(table->values, -1, capacity * sizeof(int));
154 table->capacity = capacity;
155 table->owned = false;
156}
157
164static inline pm_line_column_t
165pm_line_offset_list_line_column_cached(const pm_line_offset_list_t *list, uint32_t cursor, int32_t start_line, size_t *last_line)
166{
167 size_t hint = *last_line;
168 size_t size = list->size;
169 const uint32_t *offsets = list->offsets;
170
171 RUBY_ASSERT(hint < size);
172
173 /* Check if the cursor is on the same line as the hint. */
174 if (offsets[hint] <= cursor) {
175 if (hint + 1 >= size || offsets[hint + 1] > cursor) {
176 *last_line = hint;
177 return ((pm_line_column_t) {
178 .line = ((int32_t) hint) + start_line,
179 .column = cursor - offsets[hint]
180 });
181 }
182
183 /* Linear scan forward (up to 8 lines before giving up). */
184 size_t limit = hint + 9;
185 if (limit > size) limit = size;
186 for (size_t idx = hint + 1; idx < limit; idx++) {
187 if (offsets[idx] > cursor) {
188 *last_line = idx - 1;
189 return ((pm_line_column_t) {
190 .line = ((int32_t) (idx - 1)) + start_line,
191 .column = cursor - offsets[idx - 1]
192 });
193 }
194 if (offsets[idx] == cursor) {
195 *last_line = idx;
196 return ((pm_line_column_t) { ((int32_t) idx) + start_line, 0 });
197 }
198 }
199 }
200 else {
201 /* Linear scan backward (up to 8 lines before giving up). */
202 size_t limit = hint > 8 ? hint - 8 : 0;
203 for (size_t idx = hint; idx > limit; idx--) {
204 if (offsets[idx - 1] <= cursor) {
205 *last_line = idx - 1;
206 return ((pm_line_column_t) {
207 .line = ((int32_t) (idx - 1)) + start_line,
208 .column = cursor - offsets[idx - 1]
209 });
210 }
211 }
212 }
213
214 /* Fall back to binary search. */
215 pm_line_column_t result = pm_line_offset_list_line_column(list, cursor, start_line);
216 *last_line = (size_t) (result.line - start_line);
217 return result;
218}
219
224static inline int32_t
225pm_line_offset_list_line_cached(const pm_line_offset_list_t *list, uint32_t cursor, int32_t start_line, size_t *last_line)
226{
227 return pm_line_offset_list_line_column_cached(list, cursor, start_line, last_line).line;
228}
229
230#define PM_NODE_START_LOCATION(node) \
231 ((pm_node_location_t) { .line = pm_line_offset_list_line_cached(scope_node->line_offsets, ((const pm_node_t *) (node))->location.start, scope_node->start_line, &scope_node->last_line), .node_id = ((const pm_node_t *) (node))->node_id })
232
233#define PM_NODE_END_LOCATION(node) \
234 ((pm_node_location_t) { .line = pm_line_offset_list_line_cached(scope_node->line_offsets, ((const pm_node_t *) (node))->location.start + ((const pm_node_t *) (node))->location.length, scope_node->start_line, &scope_node->last_line), .node_id = ((const pm_node_t *) (node))->node_id })
235
236#define PM_LOCATION_START_LOCATION(location, id) \
237 ((pm_node_location_t) { .line = pm_line_offset_list_line_cached(scope_node->line_offsets, (location)->start, scope_node->start_line, &scope_node->last_line), .node_id = id })
238
239#define PM_NODE_START_LINE_COLUMN(node) \
240 pm_line_offset_list_line_column_cached(scope_node->line_offsets, ((const pm_node_t *) (node))->location.start, scope_node->start_line, &scope_node->last_line)
241
242#define PM_NODE_END_LINE_COLUMN(node) \
243 pm_line_offset_list_line_column_cached(scope_node->line_offsets, ((const pm_node_t *) (node))->location.start + ((const pm_node_t *) (node))->location.length, scope_node->start_line, &scope_node->last_line)
244
245#define PM_LOCATION_START_LINE_COLUMN(location) \
246 pm_line_offset_list_line_column_cached(scope_node->line_offsets, (location)->start, scope_node->start_line, &scope_node->last_line)
247
248static int
249pm_location_line_number(const pm_parser_t *parser, const pm_location_t *location) {
250 return (int) pm_line_offset_list_line_column(pm_parser_line_offsets(parser), location->start, pm_parser_start_line(parser)).line;
251}
252
257static inline int
258pm_node_line_number_cached(const pm_node_t *node, pm_scope_node_t *scope_node)
259{
260 return (int) pm_line_offset_list_line_cached(scope_node->line_offsets, node->location.start, scope_node->start_line, &scope_node->last_line);
261}
262
263static inline int
264pm_location_line_number_cached(const pm_location_t *location, pm_scope_node_t *scope_node) {
265 return (int) pm_line_offset_list_line_cached(scope_node->line_offsets, location->start, scope_node->start_line, &scope_node->last_line);
266}
267
271static VALUE
272parse_integer_value(const pm_integer_t *integer)
273{
274 VALUE result;
275
276 if (integer->values == NULL) {
277 result = UINT2NUM(integer->value);
278 }
279 else {
280 // The pm_integer_t stores values as an array of uint32_t in
281 // least-significant-word-first order (base 2^32). We can convert
282 // directly to a Ruby Integer using rb_integer_unpack, avoiding the
283 // overhead of constructing a hex string and calling rb_funcall.
284 result = rb_integer_unpack(
285 integer->values,
286 integer->length,
287 sizeof(uint32_t),
288 0,
290 );
291 }
292
293 if (integer->negative) {
294 result = rb_int_uminus(result);
295 }
296
297 if (!SPECIAL_CONST_P(result)) {
298 RB_OBJ_SET_SHAREABLE(result); // bignum
299 }
300
301 return result;
302}
303
307static inline VALUE
308parse_integer(const pm_integer_node_t *node)
309{
310 return parse_integer_value(&node->value);
311}
312
316static VALUE
317parse_float(const pm_float_node_t *node)
318{
319 VALUE val = DBL2NUM(node->value);
320 if (!FLONUM_P(val)) {
322 }
323 return val;
324}
325
332static VALUE
333parse_rational(const pm_rational_node_t *node)
334{
335 VALUE numerator = parse_integer_value(&node->numerator);
336 VALUE denominator = parse_integer_value(&node->denominator);
337
338 return rb_ractor_make_shareable(rb_rational_new(numerator, denominator));
339}
340
347static VALUE
348parse_imaginary(const pm_imaginary_node_t *node)
349{
350 VALUE imaginary_part;
351 switch (PM_NODE_TYPE(node->numeric)) {
352 case PM_FLOAT_NODE: {
353 imaginary_part = parse_float((const pm_float_node_t *) node->numeric);
354 break;
355 }
356 case PM_INTEGER_NODE: {
357 imaginary_part = parse_integer((const pm_integer_node_t *) node->numeric);
358 break;
359 }
360 case PM_RATIONAL_NODE: {
361 imaginary_part = parse_rational((const pm_rational_node_t *) node->numeric);
362 break;
363 }
364 default:
365 rb_bug("Unexpected numeric type on imaginary number %s\n", pm_node_type(PM_NODE_TYPE(node->numeric)));
366 }
367
368 return RB_OBJ_SET_SHAREABLE(rb_complex_raw(INT2FIX(0), imaginary_part));
369}
370
371static inline VALUE
372parse_string(const pm_scope_node_t *scope_node, const pm_string_t *string)
373{
374 return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), scope_node->encoding);
375}
376
382static inline VALUE
383parse_string_encoded(const pm_node_t *node, const pm_string_t *string, rb_encoding *default_encoding)
384{
385 rb_encoding *encoding;
386
387 if (node->flags & PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING) {
388 encoding = rb_ascii8bit_encoding();
389 }
390 else if (node->flags & PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING) {
391 encoding = rb_utf8_encoding();
392 }
393 else {
394 encoding = default_encoding;
395 }
396
397 return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), encoding);
398}
399
400static inline VALUE
401parse_static_literal_string(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *string)
402{
403 rb_encoding *encoding;
404
405 if (node->flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) {
406 encoding = rb_ascii8bit_encoding();
407 }
408 else if (node->flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) {
409 encoding = rb_utf8_encoding();
410 }
411 else {
412 encoding = scope_node->encoding;
413 }
414
415 VALUE value = rb_enc_literal_str((const char *) pm_string_source(string), pm_string_length(string), encoding);
416 rb_enc_str_coderange(value);
417
418 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
419 int line_number = pm_node_line_number_cached(node, scope_node);
420 value = rb_ractor_make_shareable(rb_str_with_debug_created_info(value, rb_iseq_path(iseq), line_number));
421 }
422
423 return value;
424}
425
426static inline ID
427parse_string_symbol(const pm_scope_node_t *scope_node, const pm_symbol_node_t *symbol)
428{
429 rb_encoding *encoding;
430 if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING) {
431 encoding = rb_utf8_encoding();
432 }
433 else if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING) {
434 encoding = rb_ascii8bit_encoding();
435 }
436 else if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING) {
437 encoding = rb_usascii_encoding();
438 }
439 else {
440 encoding = scope_node->encoding;
441 }
442
443 return rb_intern3((const char *) pm_string_source(&symbol->unescaped), pm_string_length(&symbol->unescaped), encoding);
444}
445
446static int
447pm_optimizable_range_item_p(const pm_node_t *node)
448{
449 return (!node || PM_NODE_TYPE_P(node, PM_INTEGER_NODE) || PM_NODE_TYPE_P(node, PM_NIL_NODE));
450}
451
453static VALUE
454parse_regexp_error(rb_iseq_t *iseq, int32_t line_number, const char *fmt, ...)
455{
456 va_list args;
457 va_start(args, fmt);
458 VALUE error = rb_syntax_error_append(Qnil, rb_iseq_path(iseq), line_number, -1, NULL, "%" PRIsVALUE, args);
459 va_end(args);
460 rb_exc_raise(error);
461}
462
463static VALUE
464parse_regexp_string_part(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *unescaped, rb_encoding *implicit_regexp_encoding, rb_encoding *explicit_regexp_encoding)
465{
466 // If we were passed an explicit regexp encoding, then we need to double
467 // check that it's okay here for this fragment of the string.
468 rb_encoding *encoding;
469
470 if (explicit_regexp_encoding != NULL) {
471 encoding = explicit_regexp_encoding;
472 }
473 else if (node->flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) {
474 encoding = rb_ascii8bit_encoding();
475 }
476 else if (node->flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) {
477 encoding = rb_utf8_encoding();
478 }
479 else {
480 encoding = implicit_regexp_encoding;
481 }
482
483 VALUE string = rb_enc_str_new((const char *) pm_string_source(unescaped), pm_string_length(unescaped), encoding);
484 VALUE error = rb_reg_check_preprocess(string);
485
486 if (error != Qnil) parse_regexp_error(iseq, pm_node_line_number_cached(node, scope_node), "%" PRIsVALUE, rb_obj_as_string(error));
487 return string;
488}
489
490static VALUE
491pm_static_literal_concat(rb_iseq_t *iseq, const pm_node_list_t *nodes, pm_scope_node_t *scope_node, rb_encoding *implicit_regexp_encoding, rb_encoding *explicit_regexp_encoding, bool top)
492{
493 VALUE current = Qnil;
494
495 for (size_t index = 0; index < nodes->size; index++) {
496 const pm_node_t *part = nodes->nodes[index];
497 VALUE string;
498
499 switch (PM_NODE_TYPE(part)) {
500 case PM_STRING_NODE:
501 if (implicit_regexp_encoding != NULL) {
502 if (top) {
503 string = parse_regexp_string_part(iseq, scope_node, part, &((const pm_string_node_t *) part)->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
504 }
505 else {
506 string = parse_string_encoded(part, &((const pm_string_node_t *) part)->unescaped, scope_node->encoding);
507 VALUE error = rb_reg_check_preprocess(string);
508 if (error != Qnil) parse_regexp_error(iseq, pm_node_line_number_cached(part, scope_node), "%" PRIsVALUE, rb_obj_as_string(error));
509 }
510 }
511 else {
512 string = parse_string_encoded(part, &((const pm_string_node_t *) part)->unescaped, scope_node->encoding);
513 }
514 break;
515 case PM_INTERPOLATED_STRING_NODE:
516 string = pm_static_literal_concat(iseq, &((const pm_interpolated_string_node_t *) part)->parts, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
517 break;
518 case PM_EMBEDDED_STATEMENTS_NODE: {
520 string = pm_static_literal_concat(iseq, &cast->statements->body, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
521 break;
522 }
523 default:
524 RUBY_ASSERT(false && "unexpected node type in pm_static_literal_concat");
525 return Qnil;
526 }
527
528 if (current != Qnil) {
529 current = rb_str_concat(current, string);
530 }
531 else {
532 current = string;
533 }
534 }
535
536 return top ? rb_fstring(current) : current;
537}
538
539#define RE_OPTION_ENCODING_SHIFT 8
540#define RE_OPTION_ENCODING(encoding) (((encoding) & 0xFF) << RE_OPTION_ENCODING_SHIFT)
541#define ARG_ENCODING_NONE 32
542#define ARG_ENCODING_FIXED 16
543#define ENC_ASCII8BIT 1
544#define ENC_EUC_JP 2
545#define ENC_Windows_31J 3
546#define ENC_UTF8 4
547
552static int
553parse_regexp_flags(const pm_node_t *node)
554{
555 int flags = 0;
556
557 // Check "no encoding" first so that flags don't get clobbered
558 // We're calling `rb_char_to_option_kcode` in this case so that
559 // we don't need to have access to `ARG_ENCODING_NONE`
560 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) {
561 flags |= ARG_ENCODING_NONE;
562 }
563
564 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) {
565 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_EUC_JP));
566 }
567
568 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) {
569 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_Windows_31J));
570 }
571
572 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) {
573 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_UTF8));
574 }
575
576 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) {
577 flags |= ONIG_OPTION_IGNORECASE;
578 }
579
580 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) {
581 flags |= ONIG_OPTION_MULTILINE;
582 }
583
584 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) {
585 flags |= ONIG_OPTION_EXTEND;
586 }
587
588 return flags;
589}
590
591#undef RE_OPTION_ENCODING_SHIFT
592#undef RE_OPTION_ENCODING
593#undef ARG_ENCODING_FIXED
594#undef ARG_ENCODING_NONE
595#undef ENC_ASCII8BIT
596#undef ENC_EUC_JP
597#undef ENC_Windows_31J
598#undef ENC_UTF8
599
600static rb_encoding *
601parse_regexp_encoding(const pm_scope_node_t *scope_node, const pm_node_t *node)
602{
603 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING) || PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) {
604 return rb_ascii8bit_encoding();
605 }
606 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) {
607 return rb_utf8_encoding();
608 }
609 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) {
610 return rb_enc_get_from_index(ENCINDEX_EUC_JP);
611 }
612 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) {
613 return rb_enc_get_from_index(ENCINDEX_Windows_31J);
614 }
615 else {
616 return NULL;
617 }
618}
619
620static VALUE
621parse_regexp(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, VALUE string)
622{
623 VALUE errinfo = rb_errinfo();
624
625 int32_t line_number = pm_node_line_number_cached(node, scope_node);
626 VALUE regexp = rb_reg_compile(string, parse_regexp_flags(node), (const char *) pm_string_source(pm_parser_filepath(scope_node->parser)), line_number);
627
628 if (NIL_P(regexp)) {
629 VALUE message = rb_attr_get(rb_errinfo(), idMesg);
630 rb_set_errinfo(errinfo);
631
632 parse_regexp_error(iseq, line_number, "%" PRIsVALUE, message);
633 return Qnil;
634 }
635
636 return RB_OBJ_SET_SHAREABLE(rb_obj_freeze(regexp));
637}
638
639static inline VALUE
640parse_regexp_literal(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *unescaped)
641{
642 rb_encoding *regexp_encoding = parse_regexp_encoding(scope_node, node);
643 if (regexp_encoding == NULL) regexp_encoding = scope_node->encoding;
644
645 VALUE string = rb_enc_str_new((const char *) pm_string_source(unescaped), pm_string_length(unescaped), regexp_encoding);
646 RB_OBJ_SET_SHAREABLE(string);
647 return parse_regexp(iseq, scope_node, node, string);
648}
649
650static inline VALUE
651parse_regexp_concat(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, const pm_node_list_t *parts)
652{
653 rb_encoding *explicit_regexp_encoding = parse_regexp_encoding(scope_node, node);
654 rb_encoding *implicit_regexp_encoding = explicit_regexp_encoding != NULL ? explicit_regexp_encoding : scope_node->encoding;
655
656 VALUE string = pm_static_literal_concat(iseq, parts, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
657 return parse_regexp(iseq, scope_node, node, string);
658}
659
660static void pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node);
661
662static int
663pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, rb_encoding *implicit_regexp_encoding, rb_encoding *explicit_regexp_encoding, bool mutable_result, bool frozen_result)
664{
665 int stack_size = 0;
666 size_t parts_size = parts->size;
667 bool interpolated = false;
668
669 if (parts_size > 0) {
670 VALUE current_string = Qnil;
671 pm_node_location_t current_location = *node_location;
672
673 for (size_t index = 0; index < parts_size; index++) {
674 const pm_node_t *part = parts->nodes[index];
675
676 if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
677 const pm_string_node_t *string_node = (const pm_string_node_t *) part;
678 VALUE string_value;
679
680 if (implicit_regexp_encoding == NULL) {
681 string_value = parse_string_encoded(part, &string_node->unescaped, scope_node->encoding);
682 }
683 else {
684 string_value = parse_regexp_string_part(iseq, scope_node, (const pm_node_t *) string_node, &string_node->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
685 }
686
687 if (RTEST(current_string)) {
688 current_string = rb_str_concat(current_string, string_value);
689 }
690 else {
691 current_string = string_value;
692 if (index != 0) current_location = PM_NODE_END_LOCATION(part);
693 }
694 }
695 else {
696 interpolated = true;
697
698 if (
699 PM_NODE_TYPE_P(part, PM_EMBEDDED_STATEMENTS_NODE) &&
700 ((const pm_embedded_statements_node_t *) part)->statements != NULL &&
701 ((const pm_embedded_statements_node_t *) part)->statements->body.size == 1 &&
702 PM_NODE_TYPE_P(((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0], PM_STRING_NODE)
703 ) {
704 const pm_string_node_t *string_node = (const pm_string_node_t *) ((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0];
705 VALUE string_value;
706
707 if (implicit_regexp_encoding == NULL) {
708 string_value = parse_string_encoded(part, &string_node->unescaped, scope_node->encoding);
709 }
710 else {
711 string_value = parse_regexp_string_part(iseq, scope_node, (const pm_node_t *) string_node, &string_node->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
712 }
713
714 if (RTEST(current_string)) {
715 current_string = rb_str_concat(current_string, string_value);
716 }
717 else {
718 current_string = string_value;
719 current_location = PM_NODE_START_LOCATION(part);
720 }
721 }
722 else {
723 if (!RTEST(current_string)) {
724 rb_encoding *encoding;
725
726 if (implicit_regexp_encoding != NULL) {
727 if (explicit_regexp_encoding != NULL) {
728 encoding = explicit_regexp_encoding;
729 }
730 else if (pm_parser_encoding_us_ascii(scope_node->parser)) {
731 encoding = rb_ascii8bit_encoding();
732 }
733 else {
734 encoding = implicit_regexp_encoding;
735 }
736 }
737 else {
738 encoding = scope_node->encoding;
739 }
740
741 if (parts_size == 1) {
742 current_string = rb_enc_str_new(NULL, 0, encoding);
743 }
744 }
745
746 if (RTEST(current_string)) {
747 VALUE operand = rb_fstring(current_string);
748 PUSH_INSN1(ret, current_location, putobject, operand);
749 stack_size++;
750 }
751
752 PM_COMPILE_NOT_POPPED(part);
753
754 const pm_node_location_t current_location = PM_NODE_START_LOCATION(part);
755 PUSH_INSN(ret, current_location, dup);
756
757 {
758 const struct rb_callinfo *callinfo = new_callinfo(iseq, idTo_s, 0, VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE, NULL, FALSE);
759 PUSH_INSN1(ret, current_location, objtostring, callinfo);
760 }
761
762 PUSH_INSN(ret, current_location, anytostring);
763
764 current_string = Qnil;
765 stack_size++;
766 }
767 }
768 }
769
770 if (RTEST(current_string)) {
771 current_string = rb_fstring(current_string);
772
773 if (stack_size == 0) {
774 if (frozen_result) {
775 PUSH_INSN1(ret, current_location, putobject, current_string);
776 } else if (mutable_result || interpolated) {
777 PUSH_INSN1(ret, current_location, dupstring, current_string);
778 } else {
779 PUSH_INSN1(ret, current_location, dupchilledstring, current_string);
780 }
781 } else {
782 PUSH_INSN1(ret, current_location, putobject, current_string);
783 }
784
785 current_string = Qnil;
786 stack_size++;
787 }
788 }
789 else {
790 PUSH_INSN(ret, *node_location, putnil);
791 }
792
793 return stack_size;
794}
795
796static void
797pm_compile_regexp_dynamic(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *parts, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
798{
799 rb_encoding *explicit_regexp_encoding = parse_regexp_encoding(scope_node, node);
800 rb_encoding *implicit_regexp_encoding = explicit_regexp_encoding != NULL ? explicit_regexp_encoding : scope_node->encoding;
801
802 int length = pm_interpolated_node_compile(iseq, parts, node_location, ret, popped, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false, false);
803 PUSH_INSN2(ret, *node_location, toregexp, INT2FIX(parse_regexp_flags(node) & 0xFF), INT2FIX(length));
804}
805
806static VALUE
807pm_source_file_value(const pm_source_file_node_t *node, const pm_scope_node_t *scope_node)
808{
809 const pm_string_t *filepath = &node->filepath;
810 size_t length = pm_string_length(filepath);
811
812 if (length > 0) {
813 rb_encoding *filepath_encoding = scope_node->filepath_encoding != NULL ? scope_node->filepath_encoding : rb_utf8_encoding();
814 return rb_enc_interned_str((const char *) pm_string_source(filepath), length, filepath_encoding);
815 }
816 else {
817 return rb_fstring_lit("<compiled>");
818 }
819}
820
825static VALUE
826pm_static_literal_string(rb_iseq_t *iseq, VALUE string, int line_number)
827{
828 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
829 VALUE str = rb_str_with_debug_created_info(string, rb_iseq_path(iseq), line_number);
831 return str;
832 }
833 else {
834 return rb_fstring(string);
835 }
836}
837
843static VALUE
844pm_static_literal_value(rb_iseq_t *iseq, const pm_node_t *node, pm_scope_node_t *scope_node)
845{
846 // Every node that comes into this function should already be marked as
847 // static literal. If it's not, then we have a bug somewhere.
848 RUBY_ASSERT(PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL));
849
850 switch (PM_NODE_TYPE(node)) {
851 case PM_ARRAY_NODE: {
852 const pm_array_node_t *cast = (const pm_array_node_t *) node;
853 const pm_node_list_t *elements = &cast->elements;
854
855 VALUE value = rb_ary_hidden_new(elements->size);
856 for (size_t index = 0; index < elements->size; index++) {
857 rb_ary_push(value, pm_static_literal_value(iseq, elements->nodes[index], scope_node));
858 }
859
861 return value;
862 }
863 case PM_FALSE_NODE:
864 return Qfalse;
865 case PM_FLOAT_NODE:
866 return parse_float((const pm_float_node_t *) node);
867 case PM_HASH_NODE: {
868 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
869 const pm_node_list_t *elements = &cast->elements;
870
871 VALUE array = rb_ary_hidden_new(elements->size * 2);
872 for (size_t index = 0; index < elements->size; index++) {
873 RUBY_ASSERT(PM_NODE_TYPE_P(elements->nodes[index], PM_ASSOC_NODE));
874 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) elements->nodes[index];
875 VALUE pair[2] = { pm_static_literal_value(iseq, cast->key, scope_node), pm_static_literal_value(iseq, cast->value, scope_node) };
876 rb_ary_cat(array, pair, 2);
877 }
878
879 VALUE value = rb_hash_alloc_fixed_size(Qfalse, elements->size);
880 rb_hash_bulk_insert(RARRAY_LEN(array), RARRAY_CONST_PTR(array), value);
881 RB_GC_GUARD(array);
882
884 return value;
885 }
886 case PM_IMAGINARY_NODE:
887 return parse_imaginary((const pm_imaginary_node_t *) node);
888 case PM_INTEGER_NODE:
889 return parse_integer((const pm_integer_node_t *) node);
890 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: {
892 return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts);
893 }
894 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
896 return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts);
897 }
898 case PM_INTERPOLATED_STRING_NODE: {
899 VALUE string = pm_static_literal_concat(iseq, &((const pm_interpolated_string_node_t *) node)->parts, scope_node, NULL, NULL, false);
900 int line_number = pm_node_line_number_cached(node, scope_node);
901 return pm_static_literal_string(iseq, string, line_number);
902 }
903 case PM_INTERPOLATED_SYMBOL_NODE: {
905 VALUE string = pm_static_literal_concat(iseq, &cast->parts, scope_node, NULL, NULL, true);
906
907 return ID2SYM(rb_intern_str(string));
908 }
909 case PM_MATCH_LAST_LINE_NODE: {
910 const pm_match_last_line_node_t *cast = (const pm_match_last_line_node_t *) node;
911 return parse_regexp_literal(iseq, scope_node, (const pm_node_t *) cast, &cast->unescaped);
912 }
913 case PM_NIL_NODE:
914 return Qnil;
915 case PM_RATIONAL_NODE:
916 return parse_rational((const pm_rational_node_t *) node);
917 case PM_REGULAR_EXPRESSION_NODE: {
919 return parse_regexp_literal(iseq, scope_node, (const pm_node_t *) cast, &cast->unescaped);
920 }
921 case PM_SOURCE_ENCODING_NODE:
922 return rb_enc_from_encoding(scope_node->encoding);
923 case PM_SOURCE_LINE_NODE:
924 return INT2FIX(pm_node_line_number_cached(node, scope_node));
925 case PM_STRING_NODE: {
926 const pm_string_node_t *cast = (const pm_string_node_t *) node;
927 return parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
928 }
929 case PM_SYMBOL_NODE:
930 return ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) node));
931 case PM_TRUE_NODE:
932 return Qtrue;
933 default:
934 rb_bug("Don't have a literal value for node type %s", pm_node_type(PM_NODE_TYPE(node)));
935 return Qfalse;
936 }
937}
938
943pm_code_location(pm_scope_node_t *scope_node, const pm_node_t *node)
944{
945 const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(node);
946 const pm_line_column_t end_location = PM_NODE_END_LINE_COLUMN(node);
947
948 return (rb_code_location_t) {
949 .beg_pos = { .lineno = start_location.line, .column = start_location.column },
950 .end_pos = { .lineno = end_location.line, .column = end_location.column }
951 };
952}
953
959#define PM_BRANCH_COVERAGE_P(iseq) (ISEQ_COVERAGE(iseq) && ISEQ_BRANCH_COVERAGE(iseq))
960
961static void
962pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond,
963 LABEL *then_label, LABEL *else_label, pm_scope_node_t *scope_node);
964
965static void
966pm_compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_node_t *cond, LABEL *then_label, LABEL *else_label, pm_scope_node_t *scope_node)
967{
968 const pm_node_location_t location = PM_NODE_START_LOCATION(cond);
969
970 DECL_ANCHOR(seq);
971
972 LABEL *label = NEW_LABEL(location.line);
973 if (!then_label) then_label = label;
974 else if (!else_label) else_label = label;
975
976 pm_compile_branch_condition(iseq, seq, cond, then_label, else_label, scope_node);
977
978 if (LIST_INSN_SIZE_ONE(seq)) {
979 INSN *insn = (INSN *) ELEM_FIRST_INSN(FIRST_ELEMENT(seq));
980 if (insn->insn_id == BIN(jump) && (LABEL *)(insn->operands[0]) == label) return;
981 }
982
983 if (label->refcnt) {
984 PUSH_LABEL(seq, label);
985 }
986
987 PUSH_SEQ(ret, seq);
988 return;
989}
990
991static void
992pm_compile_flip_flop_bound(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
993{
994 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
995
996 if (PM_NODE_TYPE_P(node, PM_INTEGER_NODE)) {
997 PM_COMPILE_NOT_POPPED(node);
998
999 VALUE operand = ID2SYM(rb_intern("$."));
1000 PUSH_INSN1(ret, location, getglobal, operand);
1001
1002 PUSH_SEND(ret, location, idEq, INT2FIX(1));
1003 if (popped) PUSH_INSN(ret, location, pop);
1004 }
1005 else {
1006 PM_COMPILE(node);
1007 }
1008}
1009
1010static void
1011pm_compile_flip_flop(const pm_flip_flop_node_t *flip_flop_node, LABEL *else_label, LABEL *then_label, rb_iseq_t *iseq, const int lineno, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1012{
1013 const pm_node_location_t location = { .line = lineno, .node_id = -1 };
1014 LABEL *lend = NEW_LABEL(location.line);
1015
1016 int again = !(flip_flop_node->base.flags & PM_RANGE_FLAGS_EXCLUDE_END);
1017
1018 rb_num_t count = ISEQ_FLIP_CNT_INCREMENT(ISEQ_BODY(iseq)->local_iseq) + VM_SVAR_FLIPFLOP_START;
1019 VALUE key = INT2FIX(count);
1020
1021 PUSH_INSN2(ret, location, getspecial, key, INT2FIX(0));
1022 PUSH_INSNL(ret, location, branchif, lend);
1023
1024 if (flip_flop_node->left) {
1025 pm_compile_flip_flop_bound(iseq, flip_flop_node->left, ret, popped, scope_node);
1026 }
1027 else {
1028 PUSH_INSN(ret, location, putnil);
1029 }
1030
1031 PUSH_INSNL(ret, location, branchunless, else_label);
1032 PUSH_INSN1(ret, location, putobject, Qtrue);
1033 PUSH_INSN1(ret, location, setspecial, key);
1034 if (!again) {
1035 PUSH_INSNL(ret, location, jump, then_label);
1036 }
1037
1038 PUSH_LABEL(ret, lend);
1039 if (flip_flop_node->right) {
1040 pm_compile_flip_flop_bound(iseq, flip_flop_node->right, ret, popped, scope_node);
1041 }
1042 else {
1043 PUSH_INSN(ret, location, putnil);
1044 }
1045
1046 PUSH_INSNL(ret, location, branchunless, then_label);
1047 PUSH_INSN1(ret, location, putobject, Qfalse);
1048 PUSH_INSN1(ret, location, setspecial, key);
1049 PUSH_INSNL(ret, location, jump, then_label);
1050}
1051
1052static void pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition);
1053
1054static void
1055pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond, LABEL *then_label, LABEL *else_label, pm_scope_node_t *scope_node)
1056{
1057 const pm_node_location_t location = PM_NODE_START_LOCATION(cond);
1058
1059again:
1060 switch (PM_NODE_TYPE(cond)) {
1061 case PM_AND_NODE: {
1062 const pm_and_node_t *cast = (const pm_and_node_t *) cond;
1063 pm_compile_logical(iseq, ret, cast->left, NULL, else_label, scope_node);
1064
1065 cond = cast->right;
1066 goto again;
1067 }
1068 case PM_OR_NODE: {
1069 const pm_or_node_t *cast = (const pm_or_node_t *) cond;
1070 pm_compile_logical(iseq, ret, cast->left, then_label, NULL, scope_node);
1071
1072 cond = cast->right;
1073 goto again;
1074 }
1075 case PM_FALSE_NODE:
1076 case PM_NIL_NODE:
1077 PUSH_INSNL(ret, location, jump, else_label);
1078 return;
1079 case PM_FLOAT_NODE:
1080 case PM_IMAGINARY_NODE:
1081 case PM_INTEGER_NODE:
1082 case PM_LAMBDA_NODE:
1083 case PM_RATIONAL_NODE:
1084 case PM_REGULAR_EXPRESSION_NODE:
1085 case PM_STRING_NODE:
1086 case PM_SYMBOL_NODE:
1087 case PM_TRUE_NODE:
1088 PUSH_INSNL(ret, location, jump, then_label);
1089 return;
1090 case PM_FLIP_FLOP_NODE:
1091 pm_compile_flip_flop((const pm_flip_flop_node_t *) cond, else_label, then_label, iseq, location.line, ret, false, scope_node);
1092 return;
1093 case PM_DEFINED_NODE: {
1094 const pm_defined_node_t *cast = (const pm_defined_node_t *) cond;
1095 pm_compile_defined_expr(iseq, cast->value, &location, ret, false, scope_node, true);
1096 break;
1097 }
1098 default: {
1099 DECL_ANCHOR(cond_seq);
1100 pm_compile_node(iseq, cond, cond_seq, false, scope_node);
1101
1102 if (LIST_INSN_SIZE_ONE(cond_seq)) {
1103 INSN *insn = (INSN *) ELEM_FIRST_INSN(FIRST_ELEMENT(cond_seq));
1104
1105 if (insn->insn_id == BIN(putobject)) {
1106 if (RTEST(insn->operands[0])) {
1107 PUSH_INSNL(ret, location, jump, then_label);
1108 // maybe unreachable
1109 return;
1110 }
1111 else {
1112 PUSH_INSNL(ret, location, jump, else_label);
1113 return;
1114 }
1115 }
1116 }
1117
1118 PUSH_SEQ(ret, cond_seq);
1119 break;
1120 }
1121 }
1122
1123 PUSH_INSNL(ret, location, branchunless, else_label);
1124 PUSH_INSNL(ret, location, jump, then_label);
1125}
1126
1130static void
1131pm_compile_conditional(rb_iseq_t *iseq, const pm_node_location_t *node_location, pm_node_type_t type, const pm_node_t *node, const pm_statements_node_t *statements, const pm_node_t *subsequent, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1132{
1133 const pm_node_location_t location = *node_location;
1134 LABEL *then_label = NEW_LABEL(location.line);
1135 LABEL *else_label = NEW_LABEL(location.line);
1136 LABEL *end_label = NULL;
1137
1138 DECL_ANCHOR(cond_seq);
1139 pm_compile_branch_condition(iseq, cond_seq, predicate, then_label, else_label, scope_node);
1140 PUSH_SEQ(ret, cond_seq);
1141
1142 rb_code_location_t conditional_location = { 0 };
1143 VALUE branches = Qfalse;
1144
1145 if (then_label->refcnt && else_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) {
1146 conditional_location = pm_code_location(scope_node, node);
1147 branches = decl_branch_base(iseq, (int) node->node_id, &conditional_location, type == PM_IF_NODE ? "if" : "unless");
1148 }
1149
1150 if (then_label->refcnt) {
1151 PUSH_LABEL(ret, then_label);
1152
1153 DECL_ANCHOR(then_seq);
1154
1155 if (statements != NULL) {
1156 pm_compile_node(iseq, (const pm_node_t *) statements, then_seq, popped, scope_node);
1157 }
1158 else if (!popped) {
1159 PUSH_SYNTHETIC_PUTNIL(then_seq, iseq);
1160 }
1161
1162 if (else_label->refcnt) {
1163 // Establish branch coverage for the then block.
1164 if (PM_BRANCH_COVERAGE_P(iseq)) {
1165 rb_code_location_t branch_location;
1166
1167 if (statements != NULL) {
1168 branch_location = pm_code_location(scope_node, (const pm_node_t *) statements);
1169 } else if (type == PM_IF_NODE) {
1170 pm_line_column_t predicate_end = PM_NODE_END_LINE_COLUMN(predicate);
1171 branch_location = (rb_code_location_t) {
1172 .beg_pos = { .lineno = predicate_end.line, .column = predicate_end.column },
1173 .end_pos = { .lineno = predicate_end.line, .column = predicate_end.column }
1174 };
1175 } else {
1176 branch_location = conditional_location;
1177 }
1178
1179 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, type == PM_IF_NODE ? "then" : "else", branches);
1180 }
1181
1182 end_label = NEW_LABEL(location.line);
1183 PUSH_INSNL(then_seq, location, jump, end_label);
1184 if (!popped) PUSH_INSN(then_seq, location, pop);
1185 }
1186
1187 PUSH_SEQ(ret, then_seq);
1188 }
1189
1190 if (else_label->refcnt) {
1191 PUSH_LABEL(ret, else_label);
1192
1193 DECL_ANCHOR(else_seq);
1194
1195 if (subsequent != NULL) {
1196 pm_compile_node(iseq, subsequent, else_seq, popped, scope_node);
1197 }
1198 else if (!popped) {
1199 PUSH_SYNTHETIC_PUTNIL(else_seq, iseq);
1200 }
1201
1202 // Establish branch coverage for the else block.
1203 if (then_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) {
1204 rb_code_location_t branch_location;
1205
1206 if (subsequent == NULL) {
1207 branch_location = conditional_location;
1208 } else if (PM_NODE_TYPE_P(subsequent, PM_ELSE_NODE)) {
1209 const pm_else_node_t *else_node = (const pm_else_node_t *) subsequent;
1210 branch_location = pm_code_location(scope_node, else_node->statements != NULL ? ((const pm_node_t *) else_node->statements) : (const pm_node_t *) else_node);
1211 } else {
1212 branch_location = pm_code_location(scope_node, (const pm_node_t *) subsequent);
1213 }
1214
1215 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 1, type == PM_IF_NODE ? "else" : "then", branches);
1216 }
1217
1218 PUSH_SEQ(ret, else_seq);
1219 }
1220
1221 if (end_label) {
1222 PUSH_LABEL(ret, end_label);
1223 }
1224
1225 return;
1226}
1227
1231static void
1232pm_compile_loop(rb_iseq_t *iseq, const pm_node_location_t *node_location, pm_node_flags_t flags, enum pm_node_type type, const pm_node_t *node, const pm_statements_node_t *statements, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1233{
1234 const pm_node_location_t location = *node_location;
1235
1236 LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
1237 LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
1238 LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
1239
1240 LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(location.line); /* next */
1241 LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(location.line); /* redo */
1242 LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(location.line); /* break */
1243 LABEL *end_label = NEW_LABEL(location.line);
1244 LABEL *adjust_label = NEW_LABEL(location.line);
1245
1246 LABEL *next_catch_label = NEW_LABEL(location.line);
1247 LABEL *tmp_label = NULL;
1248
1249 // We're pushing onto the ensure stack because breaks need to break out of
1250 // this loop and not break into the ensure statements within the same
1251 // lexical scope.
1253 push_ensure_entry(iseq, &enl, NULL, NULL);
1254
1255 // begin; end while true
1256 if (flags & PM_LOOP_FLAGS_BEGIN_MODIFIER) {
1257 tmp_label = NEW_LABEL(location.line);
1258 PUSH_INSNL(ret, location, jump, tmp_label);
1259 }
1260 else {
1261 // while true; end
1262 PUSH_INSNL(ret, location, jump, next_label);
1263 }
1264
1265 PUSH_LABEL(ret, adjust_label);
1266 PUSH_INSN(ret, location, putnil);
1267 PUSH_LABEL(ret, next_catch_label);
1268 PUSH_INSN(ret, location, pop);
1269 PUSH_INSNL(ret, location, jump, next_label);
1270 if (tmp_label) PUSH_LABEL(ret, tmp_label);
1271
1272 PUSH_LABEL(ret, redo_label);
1273
1274 // Establish branch coverage for the loop.
1275 if (PM_BRANCH_COVERAGE_P(iseq)) {
1276 rb_code_location_t loop_location = pm_code_location(scope_node, node);
1277 VALUE branches = decl_branch_base(iseq, (int) node->node_id, &loop_location, type == PM_WHILE_NODE ? "while" : "until");
1278
1279 rb_code_location_t branch_location = statements != NULL ? pm_code_location(scope_node, (const pm_node_t *) statements) : loop_location;
1280 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, "body", branches);
1281 }
1282
1283 if (statements != NULL) PM_COMPILE_POPPED((const pm_node_t *) statements);
1284 PUSH_LABEL(ret, next_label);
1285
1286 if (type == PM_WHILE_NODE) {
1287 pm_compile_branch_condition(iseq, ret, predicate, redo_label, end_label, scope_node);
1288 }
1289 else if (type == PM_UNTIL_NODE) {
1290 pm_compile_branch_condition(iseq, ret, predicate, end_label, redo_label, scope_node);
1291 }
1292
1293 PUSH_LABEL(ret, end_label);
1294 PUSH_ADJUST_RESTORE(ret, adjust_label);
1295 PUSH_INSN(ret, location, putnil);
1296
1297 PUSH_LABEL(ret, break_label);
1298 if (popped) PUSH_INSN(ret, location, pop);
1299
1300 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL, break_label);
1301 PUSH_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL, next_catch_label);
1302 PUSH_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL, ISEQ_COMPILE_DATA(iseq)->redo_label);
1303
1304 ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
1305 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
1306 ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
1307 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->prev;
1308
1309 return;
1310}
1311
1312// This recurses through scopes and finds the local index at any scope level
1313// It also takes a pointer to depth, and increments depth appropriately
1314// according to the depth of the local.
1315static pm_local_index_t
1316pm_lookup_local_index(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, pm_constant_id_t constant_id, int start_depth)
1317{
1318 pm_local_index_t lindex = { 0 };
1319 int local_index;
1320
1321 int level;
1322 for (level = 0; level < start_depth; level++) {
1323 scope_node = scope_node->previous;
1324 }
1325
1326 while (!pm_index_lookup_table_lookup(&scope_node->index_lookup_table, constant_id, &local_index))
1327 {
1328 level++;
1329
1330 if (scope_node->previous) {
1331 scope_node = scope_node->previous;
1332 }
1333 else {
1334 // We have recursed up all scope nodes
1335 // and have not found the local yet
1336 rb_bug("Local with constant_id %u does not exist", (unsigned int) constant_id);
1337 }
1338 }
1339
1340 lindex.level = level;
1341 lindex.index = scope_node->local_table_for_iseq_size - (int) local_index;
1342 return lindex;
1343}
1344
1345// This returns the CRuby ID which maps to the pm_constant_id_t
1346//
1347// Constant_ids in prism are indexes of the constants in prism's constant pool.
1348// We add a constants mapping on the scope_node which is a mapping from
1349// these constant_id indexes to the CRuby IDs that they represent.
1350// This helper method allows easy access to those IDs
1351static inline ID
1352pm_constant_id_lookup(const pm_scope_node_t *scope_node, pm_constant_id_t constant_id)
1353{
1354 RUBY_ASSERT(constant_id >= 1 && constant_id <= pm_parser_constants_size(scope_node->parser));
1355 return scope_node->constants[constant_id - 1];
1356}
1357
1358static rb_iseq_t *
1359pm_new_child_iseq(rb_iseq_t *iseq, pm_scope_node_t *node, VALUE name, const rb_iseq_t *parent, enum rb_iseq_type type, int line_no)
1360{
1361 debugs("[new_child_iseq]> ---------------------------------------\n");
1362 int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
1363 rb_iseq_t *ret_iseq = pm_iseq_build(node, name,
1364 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
1365 line_no, parent,
1366 isolated_depth ? isolated_depth + 1 : 0,
1367 type, ISEQ_COMPILE_DATA(iseq)->option);
1368 debugs("[new_child_iseq]< ---------------------------------------\n");
1369 return ret_iseq;
1370}
1371
1372static int
1373pm_cpath_const_p(const pm_node_t *node)
1374{
1375 switch (PM_NODE_TYPE(node)) {
1376 case PM_CONSTANT_READ_NODE:
1377 return TRUE;
1378 case PM_CONSTANT_PATH_NODE:
1379 {
1380 const pm_node_t *parent = ((const pm_constant_path_node_t *) node)->parent;
1381 if (!parent) return TRUE; /* ::Foo */
1382 return pm_cpath_const_p(parent);
1383 }
1384 default:
1385 return FALSE;
1386 }
1387}
1388
1389static int
1390pm_compile_class_path(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1391{
1392 if (PM_NODE_TYPE_P(node, PM_CONSTANT_PATH_NODE)) {
1393 const pm_node_t *parent = ((const pm_constant_path_node_t *) node)->parent;
1394
1395 if (parent) {
1396 /* Bar::Foo or expr::Foo */
1397 PM_COMPILE(parent);
1398 int flags = VM_DEFINECLASS_FLAG_SCOPED;
1399 if (!pm_cpath_const_p(parent)) {
1400 flags |= VM_DEFINECLASS_FLAG_DYNAMIC_CREF;
1401 }
1402 return flags;
1403 }
1404 else {
1405 /* toplevel class ::Foo */
1406 PUSH_INSN1(ret, *node_location, putobject, rb_cObject);
1407 return VM_DEFINECLASS_FLAG_SCOPED;
1408 }
1409 }
1410 else {
1411 /* class at cbase Foo */
1412 PUSH_INSN1(ret, *node_location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
1413 return 0;
1414 }
1415}
1416
1421static void
1422pm_compile_call_and_or_write_node(rb_iseq_t *iseq, bool and_node, const pm_node_t *receiver, const pm_node_t *value, pm_constant_id_t write_name, pm_constant_id_t read_name, bool safe_nav, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1423{
1424 const pm_node_location_t location = *node_location;
1425 LABEL *lfin = NEW_LABEL(location.line);
1426 LABEL *lcfin = NEW_LABEL(location.line);
1427 LABEL *lskip = NULL;
1428
1429 int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
1430 ID id_read_name = pm_constant_id_lookup(scope_node, read_name);
1431
1432 PM_COMPILE_NOT_POPPED(receiver);
1433 if (safe_nav) {
1434 lskip = NEW_LABEL(location.line);
1435 PUSH_INSN(ret, location, dup);
1436 PUSH_INSNL(ret, location, branchnil, lskip);
1437 }
1438
1439 PUSH_INSN(ret, location, dup);
1440 PUSH_SEND_WITH_FLAG(ret, location, id_read_name, INT2FIX(0), INT2FIX(flag));
1441 if (!popped) PUSH_INSN(ret, location, dup);
1442
1443 if (and_node) {
1444 PUSH_INSNL(ret, location, branchunless, lcfin);
1445 }
1446 else {
1447 PUSH_INSNL(ret, location, branchif, lcfin);
1448 }
1449
1450 if (!popped) PUSH_INSN(ret, location, pop);
1451 PM_COMPILE_NOT_POPPED(value);
1452
1453 if (!popped) {
1454 PUSH_INSN(ret, location, swap);
1455 PUSH_INSN1(ret, location, topn, INT2FIX(1));
1456 }
1457
1458 ID id_write_name = pm_constant_id_lookup(scope_node, write_name);
1459 PUSH_SEND_WITH_FLAG(ret, location, id_write_name, INT2FIX(1), INT2FIX(flag));
1460 PUSH_INSNL(ret, location, jump, lfin);
1461
1462 PUSH_LABEL(ret, lcfin);
1463 if (!popped) PUSH_INSN(ret, location, swap);
1464
1465 PUSH_LABEL(ret, lfin);
1466
1467 if (lskip && popped) PUSH_LABEL(ret, lskip);
1468 PUSH_INSN(ret, location, pop);
1469 if (lskip && !popped) PUSH_LABEL(ret, lskip);
1470}
1471
1472static void pm_compile_shareable_constant_value(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_flags_t shareability, VALUE path, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, bool top);
1473
1479static void
1480pm_compile_hash_elements(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *elements, const pm_node_flags_t shareability, VALUE path, bool argument, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
1481{
1482 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
1483
1484 // If this element is not popped, then we need to create the hash on the
1485 // stack. Neighboring plain assoc nodes should be grouped together (either
1486 // by newhash or hash merge). Double splat nodes should be merged using the
1487 // merge_kwd method call.
1488 const int max_stack_length = 0x100;
1489 const unsigned int min_tmp_hash_length = 0x800;
1490
1491 int stack_length = 0;
1492 bool first_chunk = true;
1493 bool owned_hash = false;
1494
1495 // This is an optimization wherein we keep track of whether or not the
1496 // previous element was a static literal. If it was, then we do not attempt
1497 // to check if we have a subhash that can be optimized. If it was not, then
1498 // we do check.
1499 bool static_literal = false;
1500
1501 DECL_ANCHOR(anchor);
1502
1503 // Convert pushed elements to a hash, and merge if needed.
1504#define FLUSH_CHUNK \
1505 if (stack_length) { \
1506 if (first_chunk) { \
1507 PUSH_SEQ(ret, anchor); \
1508 PUSH_INSN1(ret, location, newhash, INT2FIX(stack_length)); \
1509 first_chunk = false; \
1510 } \
1511 else { \
1512 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); \
1513 PUSH_INSN(ret, location, swap); \
1514 PUSH_SEQ(ret, anchor); \
1515 if (owned_hash) { \
1516 PUSH_SEND(ret, location, id_core_hash_merge_bang_ptr, INT2FIX(stack_length + 1)); \
1517 } \
1518 else { \
1519 PUSH_SEND(ret, location, id_core_hash_merge_ptr, INT2FIX(stack_length + 1)); \
1520 owned_hash = true; \
1521 } \
1522 } \
1523 INIT_ANCHOR(anchor); \
1524 stack_length = 0; \
1525 }
1526
1527 for (size_t index = 0; index < elements->size; index++) {
1528 const pm_node_t *element = elements->nodes[index];
1529
1530 switch (PM_NODE_TYPE(element)) {
1531 case PM_ASSOC_NODE: {
1532 // Pre-allocation check (this branch can be omitted).
1533 if (
1534 (shareability == 0) &&
1535 PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL) && (
1536 (!static_literal && ((index + min_tmp_hash_length) < elements->size)) ||
1537 (first_chunk && stack_length == 0)
1538 )
1539 ) {
1540 // Count the elements that are statically-known.
1541 size_t count = 1;
1542 while (index + count < elements->size && PM_NODE_FLAG_P(elements->nodes[index + count], PM_NODE_FLAG_STATIC_LITERAL)) count++;
1543
1544 bool first_element = first_chunk && stack_length == 0;
1545
1546 if (first_element || count >= min_tmp_hash_length) {
1547 // The subsequence of elements in this hash is long enough
1548 // to merit its own hash.
1549 VALUE ary = rb_ary_hidden_new(count);
1550
1551 // Create a hidden hash.
1552 for (size_t tmp_end = index + count; index < tmp_end; index++) {
1553 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[index];
1554
1555 VALUE elem[2] = {
1556 pm_static_literal_value(iseq, assoc->key, scope_node),
1557 pm_static_literal_value(iseq, assoc->value, scope_node)
1558 };
1559
1560 rb_ary_cat(ary, elem, 2);
1561 }
1562 index --;
1563
1564 VALUE hash = rb_hash_alloc_fixed_size(Qfalse, RARRAY_LEN(ary) / 2);
1565 rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary), hash);
1566 RB_GC_GUARD(ary);
1568
1569 // Emit optimized code.
1570 FLUSH_CHUNK;
1571 if (first_chunk) {
1572 if (count == elements->size) {
1573 // A fully literal hash.
1574 PUSH_INSN1(ret, location, duphash, hash);
1575 }
1576 else {
1577 // Partial hash that will be merged with a newly built one, no need to dup
1578 PUSH_INSN1(ret, location, putobject, rb_obj_reveal(hash, rb_cHash));
1579 }
1580 first_chunk = false;
1581 }
1582 else {
1583 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1584 PUSH_INSN(ret, location, swap);
1585 PUSH_INSN1(ret, location, putobject, hash);
1586 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1587 }
1588
1589 break;
1590 }
1591 else {
1592 static_literal = true;
1593 }
1594 }
1595 else {
1596 static_literal = false;
1597 }
1598
1599 // If this is a plain assoc node, then we can compile it directly
1600 // and then add the total number of values on the stack.
1601 if (shareability == 0) {
1602 pm_compile_node(iseq, element, anchor, false, scope_node);
1603 }
1604 else {
1605 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
1606 pm_compile_shareable_constant_value(iseq, assoc->key, shareability, path, ret, scope_node, false);
1607 pm_compile_shareable_constant_value(iseq, assoc->value, shareability, path, ret, scope_node, false);
1608 }
1609
1610 if ((stack_length += 2) >= max_stack_length) FLUSH_CHUNK;
1611 break;
1612 }
1613 case PM_ASSOC_SPLAT_NODE: {
1614 FLUSH_CHUNK;
1615
1616 const pm_assoc_splat_node_t *assoc_splat = (const pm_assoc_splat_node_t *) element;
1617 bool empty_hash = assoc_splat->value != NULL && (
1618 (PM_NODE_TYPE_P(assoc_splat->value, PM_HASH_NODE) && ((const pm_hash_node_t *) assoc_splat->value)->elements.size == 0) ||
1619 PM_NODE_TYPE_P(assoc_splat->value, PM_NIL_NODE)
1620 );
1621
1622 bool first_element = first_chunk && stack_length == 0;
1623 bool last_element = index == elements->size - 1;
1624 bool only_element = first_element && last_element;
1625
1626 if (only_element) {
1627 if (argument) {
1628 if (empty_hash) {
1629 // **{} appears at the only keyword argument in method call
1630 // We can substitute it for `**nil`.
1631 PUSH_INSN(ret, location, putnil);
1632 }
1633 else {
1634 // ** is the only element.
1635 // Since we're in a method call, we can use it directly.
1636 // This will be not be flagged as mutable.
1637 if (shareability == 0) {
1638 PM_COMPILE_NOT_POPPED(element);
1639 }
1640 else {
1641 pm_compile_shareable_constant_value(iseq, element, shareability, path, ret, scope_node, false);
1642 }
1643 }
1644 }
1645 else {
1646 // {**something}
1647 // We can simply call `core_hash_coerce(something)` to ensure it is coerced
1648 // into a mutable Hash.
1649 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1650 if (shareability == 0) {
1651 PM_COMPILE_NOT_POPPED(element);
1652 }
1653 else {
1654 pm_compile_shareable_constant_value(iseq, element, shareability, path, ret, scope_node, false);
1655 }
1656 PUSH_SEND(ret, location, id_core_hash_coerce, INT2FIX(1));
1657 }
1658 }
1659 else {
1660 if (!first_element) {
1661 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1662 PUSH_INSN(ret, location, swap);
1663 }
1664
1665 if (shareability == 0) {
1666 PM_COMPILE_NOT_POPPED(element);
1667 }
1668 else {
1669 pm_compile_shareable_constant_value(iseq, element, shareability, path, ret, scope_node, false);
1670 }
1671
1672 if (!first_element) {
1673 if (owned_hash) {
1674 PUSH_SEND(ret, location, id_core_hash_merge_bang_kwd, INT2FIX(2));
1675 }
1676 else {
1677 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1678 owned_hash = true;
1679 }
1680 }
1681 }
1682
1683 first_chunk = false;
1684 static_literal = false;
1685 break;
1686 }
1687 default:
1688 RUBY_ASSERT("Invalid node type for hash" && false);
1689 break;
1690 }
1691 }
1692
1693 FLUSH_CHUNK;
1694#undef FLUSH_CHUNK
1695}
1696
1697#define SPLATARRAY_FALSE 0
1698#define SPLATARRAY_TRUE 1
1699#define DUP_SINGLE_KW_SPLAT 2
1700
1701// This is details. Users should call pm_setup_args() instead.
1702static int
1703pm_setup_args_core(const pm_arguments_node_t *arguments_node, const pm_node_t *block, int *flags, const bool has_regular_blockarg, struct rb_callinfo_kwarg **kw_arg, int *dup_rest, rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_node_location_t *node_location)
1704{
1705 const pm_node_location_t location = *node_location;
1706
1707 int orig_argc = 0;
1708 bool has_splat = false;
1709 bool has_keyword_splat = false;
1710
1711 if (arguments_node == NULL) {
1712 if (*flags & VM_CALL_FCALL) {
1713 *flags |= VM_CALL_VCALL;
1714 }
1715 }
1716 else {
1717 const pm_node_list_t *arguments = &arguments_node->arguments;
1718 has_keyword_splat = PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT);
1719
1720 // We count the number of elements post the splat node that are not keyword elements to
1721 // eventually pass as an argument to newarray
1722 int post_splat_counter = 0;
1723 const pm_node_t *argument;
1724
1725 PM_NODE_LIST_FOREACH(arguments, index, argument) {
1726 switch (PM_NODE_TYPE(argument)) {
1727 // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
1728 case PM_KEYWORD_HASH_NODE: {
1729 const pm_keyword_hash_node_t *keyword_arg = (const pm_keyword_hash_node_t *) argument;
1730 const pm_node_list_t *elements = &keyword_arg->elements;
1731
1732 if (has_keyword_splat || has_splat) {
1733 *flags |= VM_CALL_KW_SPLAT;
1734 has_keyword_splat = true;
1735
1736 if (elements->size > 1 || !(elements->size == 1 && PM_NODE_TYPE_P(elements->nodes[0], PM_ASSOC_SPLAT_NODE))) {
1737 // A new hash will be created for the keyword arguments
1738 // in this case, so mark the method as passing mutable
1739 // keyword splat.
1740 *flags |= VM_CALL_KW_SPLAT_MUT;
1741 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1742 }
1743 else if (*dup_rest & DUP_SINGLE_KW_SPLAT) {
1744 *flags |= VM_CALL_KW_SPLAT_MUT;
1745 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1746 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
1747 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1748 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1749 }
1750 else {
1751 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1752 }
1753 }
1754 else {
1755 // We need to first figure out if all elements of the
1756 // KeywordHashNode are AssocNodes with symbol keys.
1757 if (PM_NODE_FLAG_P(keyword_arg, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS)) {
1758 // If they are all symbol keys then we can pass them as
1759 // keyword arguments. The first thing we need to do is
1760 // deduplicate. We'll do this using the combination of a
1761 // Ruby hash and a Ruby array.
1762 VALUE stored_indices = rb_hash_new();
1763 VALUE keyword_indices = rb_ary_new_capa(elements->size);
1764
1765 size_t size = 0;
1766 for (size_t element_index = 0; element_index < elements->size; element_index++) {
1767 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1768
1769 // Retrieve the stored index from the hash for this
1770 // keyword.
1771 VALUE keyword = pm_static_literal_value(iseq, assoc->key, scope_node);
1772 VALUE stored_index = rb_hash_aref(stored_indices, keyword);
1773
1774 // If this keyword was already seen in the hash,
1775 // then mark the array at that index as false and
1776 // decrement the keyword size.
1777 if (!NIL_P(stored_index)) {
1778 rb_ary_store(keyword_indices, NUM2LONG(stored_index), Qfalse);
1779 size--;
1780 }
1781
1782 // Store (and possibly overwrite) the index for this
1783 // keyword in the hash, mark the array at that index
1784 // as true, and increment the keyword size.
1785 rb_hash_aset(stored_indices, keyword, ULONG2NUM(element_index));
1786 rb_ary_store(keyword_indices, (long) element_index, Qtrue);
1787 size++;
1788 }
1789
1790 if (size > VM_CALL_KW_LEN_MAX) {
1791 COMPILE_ERROR(iseq, node_location->line, "too many keyword arguments (%d, maximum is %d)",
1792 (int) size, (int) VM_CALL_KW_LEN_MAX);
1793 }
1794
1795 *kw_arg = rb_xmalloc_mul_add(size, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
1796 *flags |= VM_CALL_KWARG;
1797
1798 VALUE *keywords = (*kw_arg)->keywords;
1799 (*kw_arg)->references = 0;
1800 (*kw_arg)->keyword_len = (int) size;
1801
1802 size_t keyword_index = 0;
1803 for (size_t element_index = 0; element_index < elements->size; element_index++) {
1804 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1805 bool popped = true;
1806
1807 if (rb_ary_entry(keyword_indices, (long) element_index) == Qtrue) {
1808 keywords[keyword_index++] = pm_static_literal_value(iseq, assoc->key, scope_node);
1809 popped = false;
1810 }
1811
1812 PM_COMPILE(assoc->value);
1813 }
1814
1815 RUBY_ASSERT(keyword_index == size);
1816 }
1817 else {
1818 // If they aren't all symbol keys then we need to
1819 // construct a new hash and pass that as an argument.
1820 orig_argc++;
1821 *flags |= VM_CALL_KW_SPLAT;
1822
1823 size_t size = elements->size;
1824 if (size > 1) {
1825 // A new hash will be created for the keyword
1826 // arguments in this case, so mark the method as
1827 // passing mutable keyword splat.
1828 *flags |= VM_CALL_KW_SPLAT_MUT;
1829 }
1830
1831 for (size_t element_index = 0; element_index < size; element_index++) {
1832 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1833 PM_COMPILE_NOT_POPPED(assoc->key);
1834 PM_COMPILE_NOT_POPPED(assoc->value);
1835 }
1836
1837 PUSH_INSN1(ret, location, newhash, INT2FIX(size * 2));
1838 }
1839 }
1840 break;
1841 }
1842 case PM_SPLAT_NODE: {
1843 *flags |= VM_CALL_ARGS_SPLAT;
1844 const pm_splat_node_t *splat_node = (const pm_splat_node_t *) argument;
1845
1846 if (splat_node->expression) {
1847 PM_COMPILE_NOT_POPPED(splat_node->expression);
1848 }
1849 else {
1850 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
1851 PUSH_GETLOCAL(ret, location, index.index, index.level);
1852 }
1853
1854 bool first_splat = !has_splat;
1855
1856 if (first_splat) {
1857 // If this is the first splat array seen and it's not the
1858 // last parameter, we want splatarray to dup it.
1859 //
1860 // foo(a, *b, c)
1861 // ^^
1862 if (index + 1 < arguments->size || has_regular_blockarg) {
1863 PUSH_INSN1(ret, location, splatarray, (*dup_rest & SPLATARRAY_TRUE) ? Qtrue : Qfalse);
1864 if (*dup_rest & SPLATARRAY_TRUE) *dup_rest &= ~SPLATARRAY_TRUE;
1865 }
1866 // If this is the first spalt array seen and it's the last
1867 // parameter, we don't want splatarray to dup it.
1868 //
1869 // foo(a, *b)
1870 // ^^
1871 else {
1872 PUSH_INSN1(ret, location, splatarray, Qfalse);
1873 }
1874 }
1875 else {
1876 // If this is not the first splat array seen and it is also
1877 // the last parameter, we don't want splatarray to dup it
1878 // and we need to concat the array.
1879 //
1880 // foo(a, *b, *c)
1881 // ^^
1882 PUSH_INSN(ret, location, concattoarray);
1883 }
1884
1885 has_splat = true;
1886 post_splat_counter = 0;
1887
1888 break;
1889 }
1890 case PM_FORWARDING_ARGUMENTS_NODE: { // not counted in argc return value
1891 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
1892
1893 if (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
1894 *flags |= VM_CALL_FORWARDING;
1895
1896 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_DOT3, 0);
1897 PUSH_GETLOCAL(ret, location, mult_local.index, mult_local.level);
1898
1899 break;
1900 }
1901
1902 if (has_splat) {
1903 // If we already have a splat, we're concatenating to existing array
1904 orig_argc += 1;
1905 } else {
1906 orig_argc += 2;
1907 }
1908
1909 *flags |= VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KW_SPLAT;
1910
1911 // Forwarding arguments nodes are treated as foo(*, **, &)
1912 // So foo(...) equals foo(*, **, &) and as such the local
1913 // table for this method is known in advance
1914 //
1915 // Push the *
1916 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
1917 PUSH_GETLOCAL(ret, location, mult_local.index, mult_local.level);
1918
1919 if (has_splat) {
1920 // If we already have a splat, we need to concatenate arrays
1921 PUSH_INSN(ret, location, concattoarray);
1922 } else {
1923 PUSH_INSN1(ret, location, splatarray, Qfalse);
1924 }
1925
1926 // Push the **
1927 pm_local_index_t pow_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_POW, 0);
1928 PUSH_GETLOCAL(ret, location, pow_local.index, pow_local.level);
1929
1930 // Push the &
1931 pm_local_index_t and_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_AND, 0);
1932 PUSH_INSN2(ret, location, getblockparamproxy, INT2FIX(and_local.index + VM_ENV_DATA_SIZE - 1), INT2FIX(and_local.level));
1933
1934 break;
1935 }
1936 default: {
1937 post_splat_counter++;
1938 PM_COMPILE_NOT_POPPED(argument);
1939
1940 // If we have a splat and we've seen a splat, we need to process
1941 // everything after the splat.
1942 if (has_splat) {
1943 // Stack items are turned into an array and concatenated in
1944 // the following cases:
1945 //
1946 // If the next node is a splat:
1947 //
1948 // foo(*a, b, *c)
1949 //
1950 // If the next node is a kwarg or kwarg splat:
1951 //
1952 // foo(*a, b, c: :d)
1953 // foo(*a, b, **c)
1954 //
1955 // If the next node is a forwarding argument:
1956 //
1957 // foo(*a, b, ...)
1958 //
1959 // If the next node is NULL (we have hit the end):
1960 //
1961 // foo(*a, b)
1962 if (index == arguments->size - 1) {
1963 RUBY_ASSERT(post_splat_counter > 0);
1964 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(post_splat_counter));
1965 }
1966 else {
1967 pm_node_t *next_arg = arguments->nodes[index + 1];
1968
1969 switch (PM_NODE_TYPE(next_arg)) {
1970 // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
1971 case PM_KEYWORD_HASH_NODE: {
1972 PUSH_INSN1(ret, location, newarray, INT2FIX(post_splat_counter));
1973 PUSH_INSN(ret, location, concatarray);
1974 break;
1975 }
1976 case PM_SPLAT_NODE: {
1977 PUSH_INSN1(ret, location, newarray, INT2FIX(post_splat_counter));
1978 PUSH_INSN(ret, location, concatarray);
1979 break;
1980 }
1981 case PM_FORWARDING_ARGUMENTS_NODE: {
1982 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(post_splat_counter));
1983 break;
1984 }
1985 default:
1986 break;
1987 }
1988 }
1989 }
1990 else {
1991 orig_argc++;
1992 }
1993 }
1994 }
1995 }
1996 }
1997
1998 if (has_splat) orig_argc++;
1999 if (has_keyword_splat) orig_argc++;
2000 return orig_argc;
2001}
2002
2007static inline bool
2008pm_setup_args_dup_rest_p(const pm_node_t *node)
2009{
2010 switch (PM_NODE_TYPE(node)) {
2011 case PM_BACK_REFERENCE_READ_NODE:
2012 case PM_CLASS_VARIABLE_READ_NODE:
2013 case PM_CONSTANT_READ_NODE:
2014 case PM_FALSE_NODE:
2015 case PM_FLOAT_NODE:
2016 case PM_GLOBAL_VARIABLE_READ_NODE:
2017 case PM_IMAGINARY_NODE:
2018 case PM_INSTANCE_VARIABLE_READ_NODE:
2019 case PM_INTEGER_NODE:
2020 case PM_LAMBDA_NODE:
2021 case PM_LOCAL_VARIABLE_READ_NODE:
2022 case PM_NIL_NODE:
2023 case PM_NUMBERED_REFERENCE_READ_NODE:
2024 case PM_RATIONAL_NODE:
2025 case PM_REGULAR_EXPRESSION_NODE:
2026 case PM_SELF_NODE:
2027 case PM_STRING_NODE:
2028 case PM_SYMBOL_NODE:
2029 case PM_TRUE_NODE:
2030 return false;
2031 case PM_CONSTANT_PATH_NODE: {
2032 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
2033 if (cast->parent != NULL) {
2034 return pm_setup_args_dup_rest_p(cast->parent);
2035 }
2036 return false;
2037 }
2038 case PM_IMPLICIT_NODE:
2039 return pm_setup_args_dup_rest_p(((const pm_implicit_node_t *) node)->value);
2040 case PM_ARRAY_NODE: {
2041 const pm_array_node_t *cast = (const pm_array_node_t *) node;
2042 for (size_t index = 0; index < cast->elements.size; index++) {
2043 if (pm_setup_args_dup_rest_p(cast->elements.nodes[index])) {
2044 return true;
2045 }
2046 }
2047 return false;
2048 }
2049 default:
2050 return true;
2051 }
2052}
2053
2057static int
2058pm_setup_args(const pm_arguments_node_t *arguments_node, const pm_node_t *block, int *flags, struct rb_callinfo_kwarg **kw_arg, rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_node_location_t *node_location)
2059{
2060 int dup_rest = SPLATARRAY_TRUE;
2061
2062 const pm_node_list_t *arguments;
2063 size_t arguments_size;
2064
2065 // Calls like foo(1, *f, **hash) that use splat and kwsplat could be
2066 // eligible for eliding duping the rest array (dup_reset=false).
2067 if (
2068 arguments_node != NULL &&
2069 (arguments = &arguments_node->arguments, arguments_size = arguments->size) >= 2 &&
2070 PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT) &&
2071 !PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS) &&
2072 PM_NODE_TYPE_P(arguments->nodes[arguments_size - 1], PM_KEYWORD_HASH_NODE)
2073 ) {
2074 // Start by assuming that dup_rest=false, then check each element of the
2075 // hash to ensure we don't need to flip it back to true (in case one of
2076 // the elements could potentially mutate the array).
2077 dup_rest = SPLATARRAY_FALSE;
2078
2079 const pm_keyword_hash_node_t *keyword_hash = (const pm_keyword_hash_node_t *) arguments->nodes[arguments_size - 1];
2080 const pm_node_list_t *elements = &keyword_hash->elements;
2081
2082 for (size_t index = 0; dup_rest == SPLATARRAY_FALSE && index < elements->size; index++) {
2083 const pm_node_t *element = elements->nodes[index];
2084
2085 switch (PM_NODE_TYPE(element)) {
2086 case PM_ASSOC_NODE: {
2087 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
2088 if (pm_setup_args_dup_rest_p(assoc->key) || pm_setup_args_dup_rest_p(assoc->value)) dup_rest = SPLATARRAY_TRUE;
2089 break;
2090 }
2091 case PM_ASSOC_SPLAT_NODE: {
2092 const pm_assoc_splat_node_t *assoc = (const pm_assoc_splat_node_t *) element;
2093 if (assoc->value != NULL && pm_setup_args_dup_rest_p(assoc->value)) dup_rest = SPLATARRAY_TRUE;
2094 break;
2095 }
2096 default:
2097 break;
2098 }
2099 }
2100 }
2101
2102 int initial_dup_rest = dup_rest;
2103 int argc;
2104
2105 if (block && PM_NODE_TYPE_P(block, PM_BLOCK_ARGUMENT_NODE)) {
2106 // We compile the `&block_arg` expression first and stitch it later
2107 // since the nature of the expression influences whether splat should
2108 // duplicate the array.
2109 bool regular_block_arg = true;
2110 const pm_node_t *block_expr = ((const pm_block_argument_node_t *)block)->expression;
2111
2112 if (block_expr && pm_setup_args_dup_rest_p(block_expr)) {
2113 dup_rest = SPLATARRAY_TRUE | DUP_SINGLE_KW_SPLAT;
2114 initial_dup_rest = dup_rest;
2115 }
2116
2117 DECL_ANCHOR(block_arg);
2118 pm_compile_node(iseq, block, block_arg, false, scope_node);
2119
2120 *flags |= VM_CALL_ARGS_BLOCKARG;
2121
2122 if (LIST_INSN_SIZE_ONE(block_arg)) {
2123 LINK_ELEMENT *elem = FIRST_ELEMENT(block_arg);
2124 if (IS_INSN(elem)) {
2125 INSN *iobj = (INSN *) elem;
2126 if (iobj->insn_id == BIN(getblockparam)) {
2127 iobj->insn_id = BIN(getblockparamproxy);
2128 }
2129
2130 // Allow splat without duplication for simple one-instruction
2131 // block arguments like `&arg`. It is known that this
2132 // optimization can be too aggressive in some cases. See
2133 // [Bug #16504].
2134 regular_block_arg = false;
2135 }
2136 }
2137
2138 argc = pm_setup_args_core(arguments_node, block, flags, regular_block_arg, kw_arg, &dup_rest, iseq, ret, scope_node, node_location);
2139 PUSH_SEQ(ret, block_arg);
2140 }
2141 else {
2142 argc = pm_setup_args_core(arguments_node, block, flags, false, kw_arg, &dup_rest, iseq, ret, scope_node, node_location);
2143 }
2144
2145 // If the dup_rest flag was consumed while compiling the arguments (which
2146 // effectively means we found the splat node), then it would have changed
2147 // during the call to pm_setup_args_core. In this case, we want to add the
2148 // VM_CALL_ARGS_SPLAT_MUT flag.
2149 if (*flags & VM_CALL_ARGS_SPLAT && dup_rest != initial_dup_rest) {
2150 *flags |= VM_CALL_ARGS_SPLAT_MUT;
2151 }
2152
2153 return argc;
2154}
2155
2166static void
2167pm_compile_index_operator_write_node(rb_iseq_t *iseq, const pm_index_operator_write_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
2168{
2169 const pm_node_location_t location = *node_location;
2170 if (!popped) PUSH_INSN(ret, location, putnil);
2171
2172 PM_COMPILE_NOT_POPPED(node->receiver);
2173
2174 int boff = (node->block == NULL ? 0 : 1);
2175 int flag = PM_NODE_TYPE_P(node->receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
2176 struct rb_callinfo_kwarg *keywords = NULL;
2177 int argc = pm_setup_args(node->arguments, (const pm_node_t *) node->block, &flag, &keywords, iseq, ret, scope_node, node_location);
2178
2179 if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
2180 if (boff) {
2181 PUSH_INSN(ret, location, splatkw);
2182 }
2183 else {
2184 PUSH_INSN(ret, location, dup);
2185 PUSH_INSN(ret, location, splatkw);
2186 PUSH_INSN(ret, location, pop);
2187 }
2188 }
2189
2190 int dup_argn = argc + 1 + boff;
2191 int keyword_len = 0;
2192
2193 if (keywords) {
2194 keyword_len = keywords->keyword_len;
2195 dup_argn += keyword_len;
2196 }
2197
2198 PUSH_INSN1(ret, location, dupn, INT2FIX(dup_argn));
2199 PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords);
2200 PM_COMPILE_NOT_POPPED(node->value);
2201
2202 ID id_operator = pm_constant_id_lookup(scope_node, node->binary_operator);
2203 PUSH_SEND(ret, location, id_operator, INT2FIX(1));
2204
2205 if (!popped) {
2206 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2207 }
2208 if (flag & VM_CALL_ARGS_SPLAT) {
2209 if (flag & VM_CALL_KW_SPLAT) {
2210 PUSH_INSN1(ret, location, topn, INT2FIX(2 + boff));
2211
2212 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2213 PUSH_INSN1(ret, location, splatarray, Qtrue);
2214 flag |= VM_CALL_ARGS_SPLAT_MUT;
2215 }
2216
2217 PUSH_INSN(ret, location, swap);
2218 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2219 PUSH_INSN1(ret, location, setn, INT2FIX(2 + boff));
2220 PUSH_INSN(ret, location, pop);
2221 }
2222 else {
2223 if (boff > 0) {
2224 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2225 PUSH_INSN(ret, location, swap);
2226 PUSH_INSN(ret, location, pop);
2227 }
2228 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2229 PUSH_INSN(ret, location, swap);
2230 PUSH_INSN1(ret, location, splatarray, Qtrue);
2231 PUSH_INSN(ret, location, swap);
2232 flag |= VM_CALL_ARGS_SPLAT_MUT;
2233 }
2234 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2235 if (boff > 0) {
2236 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2237 PUSH_INSN(ret, location, pop);
2238 PUSH_INSN(ret, location, pop);
2239 }
2240 }
2241
2242 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc), NULL, INT2FIX(flag), keywords);
2243 }
2244 else if (flag & VM_CALL_KW_SPLAT) {
2245 if (boff > 0) {
2246 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2247 PUSH_INSN(ret, location, swap);
2248 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2249 PUSH_INSN(ret, location, pop);
2250 }
2251 PUSH_INSN(ret, location, swap);
2252 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2253 }
2254 else if (keyword_len) {
2255 PUSH_INSN(ret, location, dup);
2256 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 2));
2257 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 1));
2258 PUSH_INSN(ret, location, pop);
2259 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2260 }
2261 else {
2262 if (boff > 0) {
2263 PUSH_INSN(ret, location, swap);
2264 }
2265 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2266 }
2267
2268 PUSH_INSN(ret, location, pop);
2269}
2270
2283static void
2284pm_compile_index_control_flow_write_node(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_t *receiver, const pm_arguments_node_t *arguments, const pm_block_argument_node_t *block, const pm_node_t *value, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
2285{
2286 const pm_node_location_t location = *node_location;
2287 if (!popped) PUSH_INSN(ret, location, putnil);
2288 PM_COMPILE_NOT_POPPED(receiver);
2289
2290 int boff = (block == NULL ? 0 : 1);
2291 int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
2292 struct rb_callinfo_kwarg *keywords = NULL;
2293 int argc = pm_setup_args(arguments, (const pm_node_t *) block, &flag, &keywords, iseq, ret, scope_node, node_location);
2294
2295 if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
2296 if (boff) {
2297 PUSH_INSN(ret, location, splatkw);
2298 }
2299 else {
2300 PUSH_INSN(ret, location, dup);
2301 PUSH_INSN(ret, location, splatkw);
2302 PUSH_INSN(ret, location, pop);
2303 }
2304 }
2305
2306 int dup_argn = argc + 1 + boff;
2307 int keyword_len = 0;
2308
2309 if (keywords) {
2310 keyword_len = keywords->keyword_len;
2311 dup_argn += keyword_len;
2312 }
2313
2314 PUSH_INSN1(ret, location, dupn, INT2FIX(dup_argn));
2315 PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords);
2316
2317 LABEL *label = NEW_LABEL(location.line);
2318 LABEL *lfin = NEW_LABEL(location.line);
2319
2320 PUSH_INSN(ret, location, dup);
2321 if (PM_NODE_TYPE_P(node, PM_INDEX_AND_WRITE_NODE)) {
2322 PUSH_INSNL(ret, location, branchunless, label);
2323 }
2324 else {
2325 PUSH_INSNL(ret, location, branchif, label);
2326 }
2327
2328 PUSH_INSN(ret, location, pop);
2329 PM_COMPILE_NOT_POPPED(value);
2330
2331 if (!popped) {
2332 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2333 }
2334
2335 if (flag & VM_CALL_ARGS_SPLAT) {
2336 if (flag & VM_CALL_KW_SPLAT) {
2337 PUSH_INSN1(ret, location, topn, INT2FIX(2 + boff));
2338 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2339 PUSH_INSN1(ret, location, splatarray, Qtrue);
2340 flag |= VM_CALL_ARGS_SPLAT_MUT;
2341 }
2342
2343 PUSH_INSN(ret, location, swap);
2344 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2345 PUSH_INSN1(ret, location, setn, INT2FIX(2 + boff));
2346 PUSH_INSN(ret, location, pop);
2347 }
2348 else {
2349 if (boff > 0) {
2350 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2351 PUSH_INSN(ret, location, swap);
2352 PUSH_INSN(ret, location, pop);
2353 }
2354 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2355 PUSH_INSN(ret, location, swap);
2356 PUSH_INSN1(ret, location, splatarray, Qtrue);
2357 PUSH_INSN(ret, location, swap);
2358 flag |= VM_CALL_ARGS_SPLAT_MUT;
2359 }
2360 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2361 if (boff > 0) {
2362 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2363 PUSH_INSN(ret, location, pop);
2364 PUSH_INSN(ret, location, pop);
2365 }
2366 }
2367
2368 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc), NULL, INT2FIX(flag), keywords);
2369 }
2370 else if (flag & VM_CALL_KW_SPLAT) {
2371 if (boff > 0) {
2372 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2373 PUSH_INSN(ret, location, swap);
2374 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2375 PUSH_INSN(ret, location, pop);
2376 }
2377
2378 PUSH_INSN(ret, location, swap);
2379 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2380 }
2381 else if (keyword_len) {
2382 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 1));
2383 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 0));
2384 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2385 }
2386 else {
2387 if (boff > 0) {
2388 PUSH_INSN(ret, location, swap);
2389 }
2390 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2391 }
2392
2393 PUSH_INSN(ret, location, pop);
2394 PUSH_INSNL(ret, location, jump, lfin);
2395 PUSH_LABEL(ret, label);
2396 if (!popped) {
2397 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2398 }
2399 PUSH_INSN1(ret, location, adjuststack, INT2FIX(dup_argn + 1));
2400 PUSH_LABEL(ret, lfin);
2401}
2402
2403// When we compile a pattern matching expression, we use the stack as a scratch
2404// space to store lots of different values (consider it like we have a pattern
2405// matching function and we need space for a bunch of different local
2406// variables). The "base index" refers to the index on the stack where we
2407// started compiling the pattern matching expression. These offsets from that
2408// base index indicate the location of the various locals we need.
2409#define PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE 0
2410#define PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING 1
2411#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P 2
2412#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE 3
2413#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY 4
2414
2415// A forward declaration because this is the recursive function that handles
2416// compiling a pattern. It can be reentered by nesting patterns, as in the case
2417// of arrays or hashes.
2418static int pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *matched_label, LABEL *unmatched_label, bool in_single_pattern, bool use_deconstructed_cache, unsigned int base_index);
2419
2424static int
2425pm_compile_pattern_generic_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, VALUE message, unsigned int base_index)
2426{
2427 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2428 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2429
2430 PUSH_INSN(ret, location, dup);
2431 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2432
2433 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2434 PUSH_INSN1(ret, location, putobject, message);
2435 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2436 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(2));
2437 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2438
2439 PUSH_INSN1(ret, location, putobject, Qfalse);
2440 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2441
2442 PUSH_INSN(ret, location, pop);
2443 PUSH_INSN(ret, location, pop);
2444 PUSH_LABEL(ret, match_succeeded_label);
2445
2446 return COMPILE_OK;
2447}
2448
2454static int
2455pm_compile_pattern_length_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, VALUE message, VALUE length, unsigned int base_index)
2456{
2457 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2458 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2459
2460 PUSH_INSN(ret, location, dup);
2461 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2462
2463 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2464 PUSH_INSN1(ret, location, putobject, message);
2465 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2466 PUSH_INSN(ret, location, dup);
2467 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2468 PUSH_INSN1(ret, location, putobject, length);
2469 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(4));
2470 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2471
2472 PUSH_INSN1(ret, location, putobject, Qfalse);
2473 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2474
2475 PUSH_INSN(ret, location, pop);
2476 PUSH_INSN(ret, location, pop);
2477 PUSH_LABEL(ret, match_succeeded_label);
2478
2479 return COMPILE_OK;
2480}
2481
2487static int
2488pm_compile_pattern_eqq_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, unsigned int base_index)
2489{
2490 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2491 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2492
2493 PUSH_INSN(ret, location, dup);
2494 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2495 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2496
2497 VALUE operand = rb_fstring_lit("%p === %p does not return true");
2498 PUSH_INSN1(ret, location, putobject, operand);
2499
2500 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2501 PUSH_INSN1(ret, location, topn, INT2FIX(5));
2502 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2503 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2504 PUSH_INSN1(ret, location, putobject, Qfalse);
2505 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2506 PUSH_INSN(ret, location, pop);
2507 PUSH_INSN(ret, location, pop);
2508
2509 PUSH_LABEL(ret, match_succeeded_label);
2510 PUSH_INSN1(ret, location, setn, INT2FIX(2));
2511 PUSH_INSN(ret, location, pop);
2512 PUSH_INSN(ret, location, pop);
2513
2514 return COMPILE_OK;
2515}
2516
2523static int
2524pm_compile_pattern_match(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *unmatched_label, bool in_single_pattern, bool use_deconstructed_cache, unsigned int base_index)
2525{
2526 LABEL *matched_label = NEW_LABEL(pm_node_line_number_cached(node, scope_node));
2527 CHECK(pm_compile_pattern(iseq, scope_node, node, ret, matched_label, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index));
2528 PUSH_LABEL(ret, matched_label);
2529 return COMPILE_OK;
2530}
2531
2537static int
2538pm_compile_pattern_deconstruct(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *deconstruct_label, LABEL *match_failed_label, LABEL *deconstructed_label, LABEL *type_error_label, bool in_single_pattern, bool use_deconstructed_cache, unsigned int base_index)
2539{
2540 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2541
2542 if (use_deconstructed_cache) {
2543 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2544 PUSH_INSNL(ret, location, branchnil, deconstruct_label);
2545
2546 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2547 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2548
2549 PUSH_INSN(ret, location, pop);
2550 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE - 1));
2551 PUSH_INSNL(ret, location, jump, deconstructed_label);
2552 }
2553 else {
2554 PUSH_INSNL(ret, location, jump, deconstruct_label);
2555 }
2556
2557 PUSH_LABEL(ret, deconstruct_label);
2558 PUSH_INSN(ret, location, dup);
2559
2560 VALUE operand = ID2SYM(rb_intern("deconstruct"));
2561 PUSH_INSN1(ret, location, putobject, operand);
2562 PUSH_SEND(ret, location, idRespond_to, INT2FIX(1));
2563
2564 if (use_deconstructed_cache) {
2565 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE + 1));
2566 }
2567
2568 if (in_single_pattern) {
2569 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p does not respond to #deconstruct"), base_index + 1));
2570 }
2571
2572 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2573 PUSH_SEND(ret, location, rb_intern("deconstruct"), INT2FIX(0));
2574
2575 if (use_deconstructed_cache) {
2576 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2577 }
2578
2579 PUSH_INSN(ret, location, dup);
2580 PUSH_INSN1(ret, location, checktype, INT2FIX(T_ARRAY));
2581 PUSH_INSNL(ret, location, branchunless, type_error_label);
2582 PUSH_LABEL(ret, deconstructed_label);
2583
2584 return COMPILE_OK;
2585}
2586
2591static int
2592pm_compile_pattern_constant(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *match_failed_label, bool in_single_pattern, unsigned int base_index)
2593{
2594 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2595
2596 PUSH_INSN(ret, location, dup);
2597 PM_COMPILE_NOT_POPPED(node);
2598
2599 if (in_single_pattern) {
2600 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
2601 }
2602 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
2603 if (in_single_pattern) {
2604 CHECK(pm_compile_pattern_eqq_error(iseq, scope_node, node, ret, base_index + 3));
2605 }
2606 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2607 return COMPILE_OK;
2608}
2609
2614static void
2615pm_compile_pattern_error_handler(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *done_label, bool popped)
2616{
2617 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2618 LABEL *key_error_label = NEW_LABEL(location.line);
2619 LABEL *cleanup_label = NEW_LABEL(location.line);
2620
2621 struct rb_callinfo_kwarg *kw_arg = rb_xmalloc_mul_add(2, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
2622 kw_arg->references = 0;
2623 kw_arg->keyword_len = 2;
2624 kw_arg->keywords[0] = ID2SYM(rb_intern("matchee"));
2625 kw_arg->keywords[1] = ID2SYM(rb_intern("key"));
2626
2627 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2628 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2629 PUSH_INSNL(ret, location, branchif, key_error_label);
2630
2631 PUSH_INSN1(ret, location, putobject, rb_eNoMatchingPatternError);
2632 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2633
2634 {
2635 VALUE operand = rb_fstring_lit("%p: %s");
2636 PUSH_INSN1(ret, location, putobject, operand);
2637 }
2638
2639 PUSH_INSN1(ret, location, topn, INT2FIX(4));
2640 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 6));
2641 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2642 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2643 PUSH_INSNL(ret, location, jump, cleanup_label);
2644
2645 PUSH_LABEL(ret, key_error_label);
2646 PUSH_INSN1(ret, location, putobject, rb_eNoMatchingPatternKeyError);
2647 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2648
2649 {
2650 VALUE operand = rb_fstring_lit("%p: %s");
2651 PUSH_INSN1(ret, location, putobject, operand);
2652 }
2653
2654 PUSH_INSN1(ret, location, topn, INT2FIX(4));
2655 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 6));
2656 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2657 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE + 4));
2658 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY + 5));
2659 PUSH_SEND_R(ret, location, rb_intern("new"), INT2FIX(1), NULL, INT2FIX(VM_CALL_KWARG), kw_arg);
2660 PUSH_SEND(ret, location, id_core_raise, INT2FIX(1));
2661 PUSH_LABEL(ret, cleanup_label);
2662
2663 PUSH_INSN1(ret, location, adjuststack, INT2FIX(7));
2664 if (!popped) PUSH_INSN(ret, location, putnil);
2665 PUSH_INSNL(ret, location, jump, done_label);
2666 PUSH_INSN1(ret, location, dupn, INT2FIX(5));
2667 if (popped) PUSH_INSN(ret, location, putnil);
2668}
2669
2673static int
2674pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *matched_label, LABEL *unmatched_label, bool in_single_pattern, bool use_deconstructed_cache, unsigned int base_index)
2675{
2676 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
2677
2678 switch (PM_NODE_TYPE(node)) {
2679 case PM_ARRAY_PATTERN_NODE: {
2680 // Array patterns in pattern matching are triggered by using commas in
2681 // a pattern or wrapping it in braces. They are represented by a
2682 // ArrayPatternNode. This looks like:
2683 //
2684 // foo => [1, 2, 3]
2685 //
2686 // It can optionally have a splat in the middle of it, which can
2687 // optionally have a name attached.
2688 const pm_array_pattern_node_t *cast = (const pm_array_pattern_node_t *) node;
2689
2690 const size_t requireds_size = cast->requireds.size;
2691 const size_t posts_size = cast->posts.size;
2692 const size_t minimum_size = requireds_size + posts_size;
2693
2694 bool rest_named = false;
2695 bool use_rest_size = false;
2696
2697 if (cast->rest != NULL) {
2698 rest_named = (PM_NODE_TYPE_P(cast->rest, PM_SPLAT_NODE) && ((const pm_splat_node_t *) cast->rest)->expression != NULL);
2699 use_rest_size = (rest_named || (!rest_named && posts_size > 0));
2700 }
2701
2702 LABEL *match_failed_label = NEW_LABEL(location.line);
2703 LABEL *type_error_label = NEW_LABEL(location.line);
2704 LABEL *deconstruct_label = NEW_LABEL(location.line);
2705 LABEL *deconstructed_label = NEW_LABEL(location.line);
2706
2707 if (use_rest_size) {
2708 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2709 PUSH_INSN(ret, location, swap);
2710 base_index++;
2711 }
2712
2713 if (cast->constant != NULL) {
2714 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2715 }
2716
2717 CHECK(pm_compile_pattern_deconstruct(iseq, scope_node, node, ret, deconstruct_label, match_failed_label, deconstructed_label, type_error_label, in_single_pattern, use_deconstructed_cache, base_index));
2718
2719 PUSH_INSN(ret, location, dup);
2720 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2721 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2722 PUSH_SEND(ret, location, cast->rest == NULL ? idEq : idGE, INT2FIX(1));
2723 if (in_single_pattern) {
2724 VALUE message = cast->rest == NULL ? rb_fstring_lit("%p length mismatch (given %p, expected %p)") : rb_fstring_lit("%p length mismatch (given %p, expected %p+)");
2725 CHECK(pm_compile_pattern_length_error(iseq, scope_node, node, ret, message, INT2FIX(minimum_size), base_index + 1));
2726 }
2727 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2728
2729 for (size_t index = 0; index < requireds_size; index++) {
2730 const pm_node_t *required = cast->requireds.nodes[index];
2731 PUSH_INSN(ret, location, dup);
2732 PUSH_INSN1(ret, location, putobject, INT2FIX(index));
2733 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2734 CHECK(pm_compile_pattern_match(iseq, scope_node, required, ret, match_failed_label, in_single_pattern, false, base_index + 1));
2735 }
2736
2737 if (cast->rest != NULL) {
2738 if (rest_named) {
2739 PUSH_INSN(ret, location, dup);
2740 PUSH_INSN1(ret, location, putobject, INT2FIX(requireds_size));
2741 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2742 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2743 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2744 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2745 PUSH_INSN1(ret, location, setn, INT2FIX(4));
2746 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2747 CHECK(pm_compile_pattern_match(iseq, scope_node, ((const pm_splat_node_t *) cast->rest)->expression, ret, match_failed_label, in_single_pattern, false, base_index + 1));
2748 }
2749 else if (posts_size > 0) {
2750 PUSH_INSN(ret, location, dup);
2751 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2752 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2753 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2754 PUSH_INSN1(ret, location, setn, INT2FIX(2));
2755 PUSH_INSN(ret, location, pop);
2756 }
2757 }
2758
2759 for (size_t index = 0; index < posts_size; index++) {
2760 const pm_node_t *post = cast->posts.nodes[index];
2761 PUSH_INSN(ret, location, dup);
2762
2763 PUSH_INSN1(ret, location, putobject, INT2FIX(requireds_size + index));
2764 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2765 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2766 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2767 CHECK(pm_compile_pattern_match(iseq, scope_node, post, ret, match_failed_label, in_single_pattern, false, base_index + 1));
2768 }
2769
2770 PUSH_INSN(ret, location, pop);
2771 if (use_rest_size) {
2772 PUSH_INSN(ret, location, pop);
2773 }
2774
2775 PUSH_INSNL(ret, location, jump, matched_label);
2776 PUSH_INSN(ret, location, putnil);
2777 if (use_rest_size) {
2778 PUSH_INSN(ret, location, putnil);
2779 }
2780
2781 PUSH_LABEL(ret, type_error_label);
2782 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2783 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2784
2785 {
2786 VALUE operand = rb_fstring_lit("deconstruct must return Array");
2787 PUSH_INSN1(ret, location, putobject, operand);
2788 }
2789
2790 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2791 PUSH_INSN(ret, location, pop);
2792
2793 PUSH_LABEL(ret, match_failed_label);
2794 PUSH_INSN(ret, location, pop);
2795 if (use_rest_size) {
2796 PUSH_INSN(ret, location, pop);
2797 }
2798
2799 PUSH_INSNL(ret, location, jump, unmatched_label);
2800 break;
2801 }
2802 case PM_FIND_PATTERN_NODE: {
2803 // Find patterns in pattern matching are triggered by using commas in
2804 // a pattern or wrapping it in braces and using a splat on both the left
2805 // and right side of the pattern. This looks like:
2806 //
2807 // foo => [*, 1, 2, 3, *]
2808 //
2809 // There can be any number of requireds in the middle. The splats on
2810 // both sides can optionally have names attached.
2811 const pm_find_pattern_node_t *cast = (const pm_find_pattern_node_t *) node;
2812 const size_t size = cast->requireds.size;
2813
2814 LABEL *match_failed_label = NEW_LABEL(location.line);
2815 LABEL *type_error_label = NEW_LABEL(location.line);
2816 LABEL *deconstruct_label = NEW_LABEL(location.line);
2817 LABEL *deconstructed_label = NEW_LABEL(location.line);
2818
2819 if (cast->constant) {
2820 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2821 }
2822
2823 CHECK(pm_compile_pattern_deconstruct(iseq, scope_node, node, ret, deconstruct_label, match_failed_label, deconstructed_label, type_error_label, in_single_pattern, use_deconstructed_cache, base_index));
2824
2825 PUSH_INSN(ret, location, dup);
2826 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2827 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2828 PUSH_SEND(ret, location, idGE, INT2FIX(1));
2829 if (in_single_pattern) {
2830 CHECK(pm_compile_pattern_length_error(iseq, scope_node, node, ret, rb_fstring_lit("%p length mismatch (given %p, expected %p+)"), INT2FIX(size), base_index + 1));
2831 }
2832 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2833
2834 {
2835 LABEL *while_begin_label = NEW_LABEL(location.line);
2836 LABEL *next_loop_label = NEW_LABEL(location.line);
2837 LABEL *find_succeeded_label = NEW_LABEL(location.line);
2838 LABEL *find_failed_label = NEW_LABEL(location.line);
2839
2840 PUSH_INSN(ret, location, dup);
2841 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2842
2843 PUSH_INSN(ret, location, dup);
2844 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2845 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2846 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2847 PUSH_LABEL(ret, while_begin_label);
2848
2849 PUSH_INSN(ret, location, dup);
2850 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2851 PUSH_SEND(ret, location, idLE, INT2FIX(1));
2852 PUSH_INSNL(ret, location, branchunless, find_failed_label);
2853
2854 for (size_t index = 0; index < size; index++) {
2855 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2856 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2857
2858 if (index != 0) {
2859 PUSH_INSN1(ret, location, putobject, INT2FIX(index));
2860 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2861 }
2862
2863 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2864 CHECK(pm_compile_pattern_match(iseq, scope_node, cast->requireds.nodes[index], ret, next_loop_label, in_single_pattern, false, base_index + 4));
2865 }
2866
2867 const pm_splat_node_t *left = cast->left;
2868
2869 if (left->expression != NULL) {
2870 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2871 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2872 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2873 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2874 CHECK(pm_compile_pattern_match(iseq, scope_node, left->expression, ret, find_failed_label, in_single_pattern, false, base_index + 4));
2875 }
2876
2877 const pm_splat_node_t *right = cast->right;
2878
2879 if (right->expression != NULL) {
2880 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2881 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2882 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2883 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2884 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2885 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2886 pm_compile_pattern_match(iseq, scope_node, right->expression, ret, find_failed_label, in_single_pattern, false, base_index + 4);
2887 }
2888
2889 PUSH_INSNL(ret, location, jump, find_succeeded_label);
2890
2891 PUSH_LABEL(ret, next_loop_label);
2892 PUSH_INSN1(ret, location, putobject, INT2FIX(1));
2893 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2894 PUSH_INSNL(ret, location, jump, while_begin_label);
2895
2896 PUSH_LABEL(ret, find_failed_label);
2897 PUSH_INSN1(ret, location, adjuststack, INT2FIX(3));
2898 if (in_single_pattern) {
2899 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2900
2901 {
2902 VALUE operand = rb_fstring_lit("%p does not match to find pattern");
2903 PUSH_INSN1(ret, location, putobject, operand);
2904 }
2905
2906 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2907 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(2));
2908 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2909
2910 PUSH_INSN1(ret, location, putobject, Qfalse);
2911 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2912
2913 PUSH_INSN(ret, location, pop);
2914 PUSH_INSN(ret, location, pop);
2915 }
2916 PUSH_INSNL(ret, location, jump, match_failed_label);
2917 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2918
2919 PUSH_LABEL(ret, find_succeeded_label);
2920 PUSH_INSN1(ret, location, adjuststack, INT2FIX(3));
2921 }
2922
2923 PUSH_INSN(ret, location, pop);
2924 PUSH_INSNL(ret, location, jump, matched_label);
2925 PUSH_INSN(ret, location, putnil);
2926
2927 PUSH_LABEL(ret, type_error_label);
2928 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2929 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2930
2931 {
2932 VALUE operand = rb_fstring_lit("deconstruct must return Array");
2933 PUSH_INSN1(ret, location, putobject, operand);
2934 }
2935
2936 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2937 PUSH_INSN(ret, location, pop);
2938
2939 PUSH_LABEL(ret, match_failed_label);
2940 PUSH_INSN(ret, location, pop);
2941 PUSH_INSNL(ret, location, jump, unmatched_label);
2942
2943 break;
2944 }
2945 case PM_HASH_PATTERN_NODE: {
2946 // Hash patterns in pattern matching are triggered by using labels and
2947 // values in a pattern or by using the ** operator. They are represented
2948 // by the HashPatternNode. This looks like:
2949 //
2950 // foo => { a: 1, b: 2, **bar }
2951 //
2952 // It can optionally have an assoc splat in the middle of it, which can
2953 // optionally have a name.
2954 const pm_hash_pattern_node_t *cast = (const pm_hash_pattern_node_t *) node;
2955
2956 // We don't consider it a "rest" parameter if it's a ** that is unnamed.
2957 bool has_rest = cast->rest != NULL && !(PM_NODE_TYPE_P(cast->rest, PM_ASSOC_SPLAT_NODE) && ((const pm_assoc_splat_node_t *) cast->rest)->value == NULL);
2958 bool has_keys = cast->elements.size > 0 || cast->rest != NULL;
2959
2960 LABEL *match_failed_label = NEW_LABEL(location.line);
2961 LABEL *type_error_label = NEW_LABEL(location.line);
2962 VALUE keys = Qnil;
2963
2964 if (has_keys && !has_rest) {
2965 keys = rb_ary_new_capa(cast->elements.size);
2966
2967 for (size_t index = 0; index < cast->elements.size; index++) {
2968 const pm_node_t *element = cast->elements.nodes[index];
2969 RUBY_ASSERT(PM_NODE_TYPE_P(element, PM_ASSOC_NODE));
2970
2971 const pm_node_t *key = ((const pm_assoc_node_t *) element)->key;
2972 RUBY_ASSERT(PM_NODE_TYPE_P(key, PM_SYMBOL_NODE));
2973
2974 VALUE symbol = ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) key));
2975 rb_ary_push(keys, symbol);
2976 }
2977 }
2978
2979 if (cast->constant) {
2980 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2981 }
2982
2983 PUSH_INSN(ret, location, dup);
2984
2985 {
2986 VALUE operand = ID2SYM(rb_intern("deconstruct_keys"));
2987 PUSH_INSN1(ret, location, putobject, operand);
2988 }
2989
2990 PUSH_SEND(ret, location, idRespond_to, INT2FIX(1));
2991 if (in_single_pattern) {
2992 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p does not respond to #deconstruct_keys"), base_index + 1));
2993 }
2994 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2995
2996 if (NIL_P(keys)) {
2997 PUSH_INSN(ret, location, putnil);
2998 }
2999 else {
3000 rb_obj_hide(keys);
3002 PUSH_INSN1(ret, location, duparray, keys);
3003 RB_OBJ_WRITTEN(iseq, Qundef, keys);
3004 }
3005 PUSH_SEND(ret, location, rb_intern("deconstruct_keys"), INT2FIX(1));
3006
3007 PUSH_INSN(ret, location, dup);
3008 PUSH_INSN1(ret, location, checktype, INT2FIX(T_HASH));
3009 PUSH_INSNL(ret, location, branchunless, type_error_label);
3010
3011 if (has_rest) {
3012 PUSH_SEND(ret, location, rb_intern("dup"), INT2FIX(0));
3013 }
3014
3015 if (has_keys) {
3016 DECL_ANCHOR(match_values);
3017
3018 for (size_t index = 0; index < cast->elements.size; index++) {
3019 const pm_node_t *element = cast->elements.nodes[index];
3020 RUBY_ASSERT(PM_NODE_TYPE_P(element, PM_ASSOC_NODE));
3021
3022 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
3023 const pm_node_t *key = assoc->key;
3024 RUBY_ASSERT(PM_NODE_TYPE_P(key, PM_SYMBOL_NODE));
3025
3026 VALUE symbol = ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) key));
3027 PUSH_INSN(ret, location, dup);
3028 PUSH_INSN1(ret, location, putobject, symbol);
3029 PUSH_SEND(ret, location, rb_intern("key?"), INT2FIX(1));
3030
3031 if (in_single_pattern) {
3032 LABEL *match_succeeded_label = NEW_LABEL(location.line);
3033
3034 PUSH_INSN(ret, location, dup);
3035 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
3036
3037 {
3038 VALUE operand = rb_str_freeze(rb_sprintf("key not found: %+"PRIsVALUE, symbol));
3039 RB_OBJ_SET_SHAREABLE(operand);
3040 PUSH_INSN1(ret, location, putobject, operand);
3041 }
3042
3043 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 2));
3044 PUSH_INSN1(ret, location, putobject, Qtrue);
3045 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 3));
3046 PUSH_INSN1(ret, location, topn, INT2FIX(3));
3047 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE + 4));
3048 PUSH_INSN1(ret, location, putobject, symbol);
3049 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY + 5));
3050
3051 PUSH_INSN1(ret, location, adjuststack, INT2FIX(4));
3052 PUSH_LABEL(ret, match_succeeded_label);
3053 }
3054
3055 PUSH_INSNL(ret, location, branchunless, match_failed_label);
3056 PUSH_INSN(match_values, location, dup);
3057 PUSH_INSN1(match_values, location, putobject, symbol);
3058 PUSH_SEND(match_values, location, has_rest ? rb_intern("delete") : idAREF, INT2FIX(1));
3059
3060 const pm_node_t *value = assoc->value;
3061 if (PM_NODE_TYPE_P(value, PM_IMPLICIT_NODE)) {
3062 value = ((const pm_implicit_node_t *) value)->value;
3063 }
3064
3065 CHECK(pm_compile_pattern_match(iseq, scope_node, value, match_values, match_failed_label, in_single_pattern, false, base_index + 1));
3066 }
3067
3068 PUSH_SEQ(ret, match_values);
3069 }
3070 else {
3071 PUSH_INSN(ret, location, dup);
3072 PUSH_SEND(ret, location, idEmptyP, INT2FIX(0));
3073 if (in_single_pattern) {
3074 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p is not empty"), base_index + 1));
3075 }
3076 PUSH_INSNL(ret, location, branchunless, match_failed_label);
3077 }
3078
3079 if (has_rest) {
3080 switch (PM_NODE_TYPE(cast->rest)) {
3081 case PM_NO_KEYWORDS_PARAMETER_NODE: {
3082 PUSH_INSN(ret, location, dup);
3083 PUSH_SEND(ret, location, idEmptyP, INT2FIX(0));
3084 if (in_single_pattern) {
3085 pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("rest of %p is not empty"), base_index + 1);
3086 }
3087 PUSH_INSNL(ret, location, branchunless, match_failed_label);
3088 break;
3089 }
3090 case PM_ASSOC_SPLAT_NODE: {
3091 const pm_assoc_splat_node_t *splat = (const pm_assoc_splat_node_t *) cast->rest;
3092 PUSH_INSN(ret, location, dup);
3093 pm_compile_pattern_match(iseq, scope_node, splat->value, ret, match_failed_label, in_single_pattern, false, base_index + 1);
3094 break;
3095 }
3096 default:
3097 rb_bug("unreachable");
3098 break;
3099 }
3100 }
3101
3102 PUSH_INSN(ret, location, pop);
3103 PUSH_INSNL(ret, location, jump, matched_label);
3104 PUSH_INSN(ret, location, putnil);
3105
3106 PUSH_LABEL(ret, type_error_label);
3107 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
3108 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
3109
3110 {
3111 VALUE operand = rb_fstring_lit("deconstruct_keys must return Hash");
3112 PUSH_INSN1(ret, location, putobject, operand);
3113 }
3114
3115 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
3116 PUSH_INSN(ret, location, pop);
3117
3118 PUSH_LABEL(ret, match_failed_label);
3119 PUSH_INSN(ret, location, pop);
3120 PUSH_INSNL(ret, location, jump, unmatched_label);
3121 break;
3122 }
3123 case PM_CAPTURE_PATTERN_NODE: {
3124 // Capture patterns allow you to pattern match against an element in a
3125 // pattern and also capture the value into a local variable. This looks
3126 // like:
3127 //
3128 // [1] => [Integer => foo]
3129 //
3130 // In this case the `Integer => foo` will be represented by a
3131 // CapturePatternNode, which has both a value (the pattern to match
3132 // against) and a target (the place to write the variable into).
3133 const pm_capture_pattern_node_t *cast = (const pm_capture_pattern_node_t *) node;
3134
3135 LABEL *match_failed_label = NEW_LABEL(location.line);
3136
3137 PUSH_INSN(ret, location, dup);
3138 CHECK(pm_compile_pattern_match(iseq, scope_node, cast->value, ret, match_failed_label, in_single_pattern, use_deconstructed_cache, base_index + 1));
3139 CHECK(pm_compile_pattern(iseq, scope_node, (const pm_node_t *) cast->target, ret, matched_label, match_failed_label, in_single_pattern, false, base_index));
3140 PUSH_INSN(ret, location, putnil);
3141
3142 PUSH_LABEL(ret, match_failed_label);
3143 PUSH_INSN(ret, location, pop);
3144 PUSH_INSNL(ret, location, jump, unmatched_label);
3145
3146 break;
3147 }
3148 case PM_LOCAL_VARIABLE_TARGET_NODE: {
3149 // Local variables can be targeted by placing identifiers in the place
3150 // of a pattern. For example, foo in bar. This results in the value
3151 // being matched being written to that local variable.
3153 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
3154
3155 PUSH_SETLOCAL(ret, location, index.index, index.level);
3156 PUSH_INSNL(ret, location, jump, matched_label);
3157 break;
3158 }
3159 case PM_ALTERNATION_PATTERN_NODE: {
3160 // Alternation patterns allow you to specify multiple patterns in a
3161 // single expression using the | operator.
3163
3164 LABEL *matched_left_label = NEW_LABEL(location.line);
3165 LABEL *unmatched_left_label = NEW_LABEL(location.line);
3166
3167 // First, we're going to attempt to match against the left pattern. If
3168 // that pattern matches, then we'll skip matching the right pattern.
3169 PUSH_INSN(ret, location, dup);
3170 CHECK(pm_compile_pattern(iseq, scope_node, cast->left, ret, matched_left_label, unmatched_left_label, in_single_pattern, use_deconstructed_cache, base_index + 1));
3171
3172 // If we get here, then we matched on the left pattern. In this case we
3173 // should pop out the duplicate value that we preemptively added to
3174 // match against the right pattern and then jump to the match label.
3175 PUSH_LABEL(ret, matched_left_label);
3176 PUSH_INSN(ret, location, pop);
3177 PUSH_INSNL(ret, location, jump, matched_label);
3178 PUSH_INSN(ret, location, putnil);
3179
3180 // If we get here, then we didn't match on the left pattern. In this
3181 // case we attempt to match against the right pattern.
3182 PUSH_LABEL(ret, unmatched_left_label);
3183 CHECK(pm_compile_pattern(iseq, scope_node, cast->right, ret, matched_label, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index));
3184 break;
3185 }
3186 case PM_PARENTHESES_NODE:
3187 // Parentheses are allowed to wrap expressions in pattern matching and
3188 // they do nothing since they can only wrap individual expressions and
3189 // not groups. In this case we'll recurse back into this same function
3190 // with the body of the parentheses.
3191 return pm_compile_pattern(iseq, scope_node, ((const pm_parentheses_node_t *) node)->body, ret, matched_label, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index);
3192 case PM_PINNED_EXPRESSION_NODE:
3193 // Pinned expressions are a way to match against the value of an
3194 // expression that should be evaluated at runtime. This looks like:
3195 // foo in ^(bar). To compile these, we compile the expression as if it
3196 // were a literal value by falling through to the literal case.
3197 node = ((const pm_pinned_expression_node_t *) node)->expression;
3198 /* fallthrough */
3199 case PM_ARRAY_NODE:
3200 case PM_CLASS_VARIABLE_READ_NODE:
3201 case PM_CONSTANT_PATH_NODE:
3202 case PM_CONSTANT_READ_NODE:
3203 case PM_FALSE_NODE:
3204 case PM_FLOAT_NODE:
3205 case PM_GLOBAL_VARIABLE_READ_NODE:
3206 case PM_IMAGINARY_NODE:
3207 case PM_INSTANCE_VARIABLE_READ_NODE:
3208 case PM_IT_LOCAL_VARIABLE_READ_NODE:
3209 case PM_INTEGER_NODE:
3210 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
3211 case PM_INTERPOLATED_STRING_NODE:
3212 case PM_INTERPOLATED_SYMBOL_NODE:
3213 case PM_INTERPOLATED_X_STRING_NODE:
3214 case PM_LAMBDA_NODE:
3215 case PM_LOCAL_VARIABLE_READ_NODE:
3216 case PM_NIL_NODE:
3217 case PM_SOURCE_ENCODING_NODE:
3218 case PM_SOURCE_FILE_NODE:
3219 case PM_SOURCE_LINE_NODE:
3220 case PM_RANGE_NODE:
3221 case PM_RATIONAL_NODE:
3222 case PM_REGULAR_EXPRESSION_NODE:
3223 case PM_SELF_NODE:
3224 case PM_STRING_NODE:
3225 case PM_SYMBOL_NODE:
3226 case PM_TRUE_NODE:
3227 case PM_X_STRING_NODE: {
3228 // These nodes are all simple patterns, which means we'll use the
3229 // checkmatch instruction to match against them, which is effectively a
3230 // VM-level === operator.
3231 PM_COMPILE_NOT_POPPED(node);
3232 if (in_single_pattern) {
3233 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
3234 }
3235
3236 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
3237
3238 if (in_single_pattern) {
3239 pm_compile_pattern_eqq_error(iseq, scope_node, node, ret, base_index + 2);
3240 }
3241
3242 PUSH_INSNL(ret, location, branchif, matched_label);
3243 PUSH_INSNL(ret, location, jump, unmatched_label);
3244 break;
3245 }
3246 case PM_PINNED_VARIABLE_NODE: {
3247 // Pinned variables are a way to match against the value of a variable
3248 // without it looking like you're trying to write to the variable. This
3249 // looks like: foo in ^@bar. To compile these, we compile the variable
3250 // that they hold.
3251 const pm_pinned_variable_node_t *cast = (const pm_pinned_variable_node_t *) node;
3252 CHECK(pm_compile_pattern(iseq, scope_node, cast->variable, ret, matched_label, unmatched_label, in_single_pattern, true, base_index));
3253 break;
3254 }
3255 case PM_IF_NODE:
3256 case PM_UNLESS_NODE: {
3257 // If and unless nodes can show up here as guards on `in` clauses. This
3258 // looks like:
3259 //
3260 // case foo
3261 // in bar if baz?
3262 // qux
3263 // end
3264 //
3265 // Because we know they're in the modifier form and they can't have any
3266 // variation on this pattern, we compile them differently (more simply)
3267 // here than we would in the normal compilation path.
3268 const pm_node_t *predicate;
3269 const pm_node_t *statement;
3270
3271 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3272 const pm_if_node_t *cast = (const pm_if_node_t *) node;
3273 predicate = cast->predicate;
3274
3275 RUBY_ASSERT(cast->statements != NULL && cast->statements->body.size == 1);
3276 statement = cast->statements->body.nodes[0];
3277 }
3278 else {
3279 const pm_unless_node_t *cast = (const pm_unless_node_t *) node;
3280 predicate = cast->predicate;
3281
3282 RUBY_ASSERT(cast->statements != NULL && cast->statements->body.size == 1);
3283 statement = cast->statements->body.nodes[0];
3284 }
3285
3286 CHECK(pm_compile_pattern_match(iseq, scope_node, statement, ret, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index));
3287 PM_COMPILE_NOT_POPPED(predicate);
3288
3289 if (in_single_pattern) {
3290 LABEL *match_succeeded_label = NEW_LABEL(location.line);
3291
3292 PUSH_INSN(ret, location, dup);
3293 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3294 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
3295 }
3296 else {
3297 PUSH_INSNL(ret, location, branchunless, match_succeeded_label);
3298 }
3299
3300 {
3301 VALUE operand = rb_fstring_lit("guard clause does not return true");
3302 PUSH_INSN1(ret, location, putobject, operand);
3303 }
3304
3305 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
3306 PUSH_INSN1(ret, location, putobject, Qfalse);
3307 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
3308
3309 PUSH_INSN(ret, location, pop);
3310 PUSH_INSN(ret, location, pop);
3311
3312 PUSH_LABEL(ret, match_succeeded_label);
3313 }
3314
3315 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3316 PUSH_INSNL(ret, location, branchunless, unmatched_label);
3317 }
3318 else {
3319 PUSH_INSNL(ret, location, branchif, unmatched_label);
3320 }
3321
3322 PUSH_INSNL(ret, location, jump, matched_label);
3323 break;
3324 }
3325 default:
3326 // If we get here, then we have a node type that should not be in this
3327 // position. This would be a bug in the parser, because a different node
3328 // type should never have been created in this position in the tree.
3329 rb_bug("Unexpected node type in pattern matching expression: %s", pm_node_type(PM_NODE_TYPE(node)));
3330 break;
3331 }
3332
3333 return COMPILE_OK;
3334}
3335
3336#undef PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE
3337#undef PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING
3338#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P
3339#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE
3340#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY
3341
3342// Generate a scope node from the given node.
3343void
3344pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_t *previous)
3345{
3346 if (previous) {
3347 // Copy inherited fields from the parent scope in one shot, then
3348 // zero out the fields that are scope-specific.
3349 *scope = *previous;
3350 scope->locals = (pm_constant_id_list_t) { 0 };
3351 scope->parameters = NULL;
3352 scope->body = NULL;
3353 scope->local_table_for_iseq_size = 0;
3354 scope->index_lookup_table = (pm_index_lookup_table_t) PM_INDEX_LOOKUP_TABLE_INIT;
3355 scope->pre_execution_anchor = NULL;
3356 }
3357 else {
3358 memset(scope, 0, sizeof(pm_scope_node_t));
3359 }
3360
3361 scope->base.type = PM_SCOPE_NODE;
3362 scope->base.location.start = node->location.start;
3363 scope->base.location.length = node->location.length;
3364 scope->previous = previous;
3365 scope->ast_node = (pm_node_t *) node;
3366
3367 switch (PM_NODE_TYPE(node)) {
3368 case PM_BLOCK_NODE: {
3369 const pm_block_node_t *cast = (const pm_block_node_t *) node;
3370 scope->body = cast->body;
3371 scope->locals = cast->locals;
3372 scope->parameters = cast->parameters;
3373 break;
3374 }
3375 case PM_CLASS_NODE: {
3376 const pm_class_node_t *cast = (const pm_class_node_t *) node;
3377 scope->body = cast->body;
3378 scope->locals = cast->locals;
3379 break;
3380 }
3381 case PM_DEF_NODE: {
3382 const pm_def_node_t *cast = (const pm_def_node_t *) node;
3383 scope->parameters = (pm_node_t *) cast->parameters;
3384 scope->body = cast->body;
3385 scope->locals = cast->locals;
3386 break;
3387 }
3388 case PM_ENSURE_NODE: {
3389 const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node;
3390 scope->body = (pm_node_t *) node;
3391
3392 if (cast->statements != NULL) {
3393 scope->base.location.start = cast->statements->base.location.start;
3394 scope->base.location.length = cast->statements->base.location.length;
3395 }
3396
3397 break;
3398 }
3399 case PM_FOR_NODE: {
3400 const pm_for_node_t *cast = (const pm_for_node_t *) node;
3401 scope->body = (pm_node_t *) cast->statements;
3402 break;
3403 }
3404 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
3405 RUBY_ASSERT(node->flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE);
3406 scope->body = (pm_node_t *) node;
3407 break;
3408 }
3409 case PM_LAMBDA_NODE: {
3410 const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node;
3411 scope->parameters = cast->parameters;
3412 scope->body = cast->body;
3413 scope->locals = cast->locals;
3414 break;
3415 }
3416 case PM_MODULE_NODE: {
3417 const pm_module_node_t *cast = (const pm_module_node_t *) node;
3418 scope->body = cast->body;
3419 scope->locals = cast->locals;
3420 break;
3421 }
3422 case PM_POST_EXECUTION_NODE: {
3423 const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) node;
3424 scope->body = (pm_node_t *) cast->statements;
3425 break;
3426 }
3427 case PM_PROGRAM_NODE: {
3428 const pm_program_node_t *cast = (const pm_program_node_t *) node;
3429 scope->body = (pm_node_t *) cast->statements;
3430 scope->locals = cast->locals;
3431 break;
3432 }
3433 case PM_RESCUE_NODE: {
3434 const pm_rescue_node_t *cast = (const pm_rescue_node_t *) node;
3435 scope->body = (pm_node_t *) cast->statements;
3436 break;
3437 }
3438 case PM_RESCUE_MODIFIER_NODE: {
3439 const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node;
3440 scope->body = (pm_node_t *) cast->rescue_expression;
3441 break;
3442 }
3443 case PM_SINGLETON_CLASS_NODE: {
3444 const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node;
3445 scope->body = cast->body;
3446 scope->locals = cast->locals;
3447 break;
3448 }
3449 case PM_STATEMENTS_NODE: {
3450 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
3451 scope->body = (pm_node_t *) cast;
3452 break;
3453 }
3454 default:
3455 rb_bug("unreachable");
3456 break;
3457 }
3458}
3459
3460void
3461pm_scope_node_destroy(pm_scope_node_t *scope_node)
3462{
3463 if (scope_node->index_lookup_table.owned) {
3464 xfree(scope_node->index_lookup_table.values);
3465 }
3466}
3467
3479static void
3480pm_compile_retry_end_label(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *retry_end_l)
3481{
3482 INSN *iobj;
3483 LINK_ELEMENT *last_elem = LAST_ELEMENT(ret);
3484 iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem);
3485 while (!IS_INSN_ID(iobj, send) && !IS_INSN_ID(iobj, invokesuper) && !IS_INSN_ID(iobj, sendforward) && !IS_INSN_ID(iobj, invokesuperforward)) {
3486 iobj = (INSN*) get_prev_insn(iobj);
3487 }
3488 ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l);
3489
3490 // LINK_ANCHOR has a pointer to the last element, but
3491 // ELEM_INSERT_NEXT does not update it even if we add an insn to the
3492 // last of LINK_ANCHOR. So this updates it manually.
3493 if (&iobj->link == LAST_ELEMENT(ret)) {
3494 ret->last = (LINK_ELEMENT*) retry_end_l;
3495 }
3496}
3497
3498static const char *
3499pm_iseq_builtin_function_name(const pm_scope_node_t *scope_node, const pm_node_t *receiver, ID method_id)
3500{
3501 const char *name = rb_id2name(method_id);
3502 static const char prefix[] = "__builtin_";
3503 const size_t prefix_len = sizeof(prefix) - 1;
3504
3505 if (receiver == NULL) {
3506 if (UNLIKELY(strncmp(prefix, name, prefix_len) == 0)) {
3507 // __builtin_foo
3508 return &name[prefix_len];
3509 }
3510 }
3511 else if (PM_NODE_TYPE_P(receiver, PM_CALL_NODE)) {
3512 if (PM_NODE_FLAG_P(receiver, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
3513 const pm_call_node_t *cast = (const pm_call_node_t *) receiver;
3514 if (pm_constant_id_lookup(scope_node, cast->name) == rb_intern_const("__builtin")) {
3515 // __builtin.foo
3516 return name;
3517 }
3518 }
3519 }
3520 else if (PM_NODE_TYPE_P(receiver, PM_CONSTANT_READ_NODE)) {
3521 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) receiver;
3522 if (pm_constant_id_lookup(scope_node, cast->name) == rb_intern_const("Primitive")) {
3523 // Primitive.foo
3524 return name;
3525 }
3526 }
3527
3528 return NULL;
3529}
3530
3531// Compile Primitive.attr! :leaf, ...
3532static int
3533pm_compile_builtin_attr(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_arguments_node_t *arguments, const pm_node_location_t *node_location)
3534{
3535 if (arguments == NULL) {
3536 COMPILE_ERROR(iseq, node_location->line, "attr!: no argument");
3537 return COMPILE_NG;
3538 }
3539
3540 const pm_node_t *argument;
3541 PM_NODE_LIST_FOREACH(&arguments->arguments, index, argument) {
3542 if (!PM_NODE_TYPE_P(argument, PM_SYMBOL_NODE)) {
3543 COMPILE_ERROR(iseq, node_location->line, "non symbol argument to attr!: %s", pm_node_type(PM_NODE_TYPE(argument)));
3544 return COMPILE_NG;
3545 }
3546
3547 VALUE symbol = pm_static_literal_value(iseq, argument, scope_node);
3548 if (compile_builtin_attr_symbol(iseq, symbol) != COMPILE_OK) {
3549 COMPILE_ERROR(iseq, node_location->line, "unknown argument to attr!: %" PRIsVALUE, symbol);
3550 return COMPILE_NG;
3551 }
3552 }
3553
3554 return COMPILE_OK;
3555}
3556
3557static int
3558pm_compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_scope_node_t *scope_node, const pm_arguments_node_t *arguments, const pm_node_location_t *node_location, int popped)
3559{
3560 if (arguments == NULL) {
3561 COMPILE_ERROR(iseq, node_location->line, "arg!: no argument");
3562 return COMPILE_NG;
3563 }
3564
3565 if (arguments->arguments.size != 1) {
3566 COMPILE_ERROR(iseq, node_location->line, "arg!: too many argument");
3567 return COMPILE_NG;
3568 }
3569
3570 const pm_node_t *argument = arguments->arguments.nodes[0];
3571 if (!PM_NODE_TYPE_P(argument, PM_SYMBOL_NODE)) {
3572 COMPILE_ERROR(iseq, node_location->line, "non symbol argument to arg!: %s", pm_node_type(PM_NODE_TYPE(argument)));
3573 return COMPILE_NG;
3574 }
3575
3576 if (!popped) {
3577 ID name = parse_string_symbol(scope_node, ((const pm_symbol_node_t *) argument));
3578 int index = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size - get_local_var_idx(iseq, name);
3579
3580 debugs("id: %s idx: %d\n", rb_id2name(name), index);
3581 PUSH_GETLOCAL(ret, *node_location, index, get_lvar_level(iseq));
3582 }
3583
3584 return COMPILE_OK;
3585}
3586
3587static int
3588pm_compile_builtin_mandatory_only_method(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_call_node_t *call_node, const pm_node_location_t *node_location)
3589{
3590 const pm_node_t *ast_node = scope_node->ast_node;
3591 if (!PM_NODE_TYPE_P(ast_node, PM_DEF_NODE)) {
3592 rb_bug("mandatory_only?: not in method definition");
3593 return COMPILE_NG;
3594 }
3595
3596 const pm_def_node_t *def_node = (const pm_def_node_t *) ast_node;
3597 const pm_parameters_node_t *parameters_node = def_node->parameters;
3598 if (parameters_node == NULL) {
3599 rb_bug("mandatory_only?: in method definition with no parameters");
3600 return COMPILE_NG;
3601 }
3602
3603 const pm_node_t *body_node = def_node->body;
3604 if (body_node == NULL || !PM_NODE_TYPE_P(body_node, PM_STATEMENTS_NODE) || (((const pm_statements_node_t *) body_node)->body.size != 1) || !PM_NODE_TYPE_P(((const pm_statements_node_t *) body_node)->body.nodes[0], PM_IF_NODE)) {
3605 rb_bug("mandatory_only?: not in method definition with plain statements");
3606 return COMPILE_NG;
3607 }
3608
3609 const pm_if_node_t *if_node = (const pm_if_node_t *) ((const pm_statements_node_t *) body_node)->body.nodes[0];
3610 if (if_node->predicate != ((const pm_node_t *) call_node)) {
3611 rb_bug("mandatory_only?: can't find mandatory node");
3612 return COMPILE_NG;
3613 }
3614
3615 pm_parameters_node_t parameters = {
3616 .base = parameters_node->base,
3617 .requireds = parameters_node->requireds
3618 };
3619
3620 const pm_def_node_t def = {
3621 .base = def_node->base,
3622 .name = def_node->name,
3623 .receiver = def_node->receiver,
3624 .parameters = &parameters,
3625 .body = (pm_node_t *) if_node->statements,
3626 .locals = {
3627 .ids = def_node->locals.ids,
3628 .size = parameters_node->requireds.size,
3629 .capacity = def_node->locals.capacity
3630 }
3631 };
3632
3633 pm_scope_node_t next_scope_node;
3634 pm_scope_node_init(&def.base, &next_scope_node, scope_node);
3635
3636 const rb_iseq_t *mandatory_only_iseq = pm_iseq_build(
3637 &next_scope_node,
3638 rb_iseq_base_label(iseq),
3639 rb_iseq_path(iseq),
3640 rb_iseq_realpath(iseq),
3641 node_location->line,
3642 NULL,
3643 0,
3644 ISEQ_TYPE_METHOD,
3645 ISEQ_COMPILE_DATA(iseq)->option
3646 );
3647 RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->mandatory_only_iseq, (VALUE)mandatory_only_iseq);
3648
3649 pm_scope_node_destroy(&next_scope_node);
3650 return COMPILE_OK;
3651}
3652
3653static int
3654pm_compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_call_node_t *call_node, const pm_node_location_t *node_location, int popped, const rb_iseq_t *parent_block, const char *builtin_func)
3655{
3656 const pm_arguments_node_t *arguments = call_node->arguments;
3657
3658 if (parent_block != NULL) {
3659 COMPILE_ERROR(iseq, node_location->line, "should not call builtins here.");
3660 return COMPILE_NG;
3661 }
3662
3663#define BUILTIN_INLINE_PREFIX "_bi"
3664 char inline_func[sizeof(BUILTIN_INLINE_PREFIX) + DECIMAL_SIZE_OF(int)];
3665 bool cconst = false;
3666retry:;
3667 const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
3668
3669 if (bf == NULL) {
3670 if (strcmp("cstmt!", builtin_func) == 0 || strcmp("cexpr!", builtin_func) == 0) {
3671 // ok
3672 }
3673 else if (strcmp("cconst!", builtin_func) == 0) {
3674 cconst = true;
3675 }
3676 else if (strcmp("cinit!", builtin_func) == 0) {
3677 // ignore
3678 return COMPILE_OK;
3679 }
3680 else if (strcmp("attr!", builtin_func) == 0) {
3681 return pm_compile_builtin_attr(iseq, scope_node, arguments, node_location);
3682 }
3683 else if (strcmp("arg!", builtin_func) == 0) {
3684 return pm_compile_builtin_arg(iseq, ret, scope_node, arguments, node_location, popped);
3685 }
3686 else if (strcmp("mandatory_only?", builtin_func) == 0) {
3687 if (popped) {
3688 rb_bug("mandatory_only? should be in if condition");
3689 }
3690 else if (!LIST_INSN_SIZE_ZERO(ret)) {
3691 rb_bug("mandatory_only? should be put on top");
3692 }
3693
3694 PUSH_INSN1(ret, *node_location, putobject, Qfalse);
3695 return pm_compile_builtin_mandatory_only_method(iseq, scope_node, call_node, node_location);
3696 }
3697 else if (1) {
3698 rb_bug("can't find builtin function:%s", builtin_func);
3699 }
3700 else {
3701 COMPILE_ERROR(iseq, node_location->line, "can't find builtin function:%s", builtin_func);
3702 return COMPILE_NG;
3703 }
3704
3705 int inline_index = node_location->line;
3706 snprintf(inline_func, sizeof(inline_func), BUILTIN_INLINE_PREFIX "%d", inline_index);
3707 builtin_func = inline_func;
3708 arguments = NULL;
3709 goto retry;
3710 }
3711
3712 if (cconst) {
3713 typedef VALUE(*builtin_func0)(void *, VALUE);
3714 VALUE const_val = (*(builtin_func0)(uintptr_t)bf->func_ptr)(NULL, Qnil);
3715 PUSH_INSN1(ret, *node_location, putobject, const_val);
3716 return COMPILE_OK;
3717 }
3718
3719 // fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
3720
3721 DECL_ANCHOR(args_seq);
3722
3723 int flags = 0;
3724 struct rb_callinfo_kwarg *keywords = NULL;
3725 int argc = pm_setup_args(arguments, call_node->block, &flags, &keywords, iseq, args_seq, scope_node, node_location);
3726
3727 if (argc != bf->argc) {
3728 COMPILE_ERROR(iseq, node_location->line, "argc is not match for builtin function:%s (expect %d but %d)", builtin_func, bf->argc, argc);
3729 return COMPILE_NG;
3730 }
3731
3732 unsigned int start_index;
3733 if (delegate_call_p(iseq, argc, args_seq, &start_index)) {
3734 PUSH_INSN2(ret, *node_location, opt_invokebuiltin_delegate, bf, INT2FIX(start_index));
3735 }
3736 else {
3737 PUSH_SEQ(ret, args_seq);
3738 PUSH_INSN1(ret, *node_location, invokebuiltin, bf);
3739 }
3740
3741 if (popped) PUSH_INSN(ret, *node_location, pop);
3742 return COMPILE_OK;
3743}
3744
3748static void
3749pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, ID method_id, LABEL *start)
3750{
3751 const pm_location_t *message_loc = &call_node->message_loc;
3752 if (message_loc->length == 0) message_loc = &call_node->base.location;
3753
3754 const pm_node_location_t location = PM_LOCATION_START_LOCATION(message_loc, call_node->base.node_id);
3755
3756 LABEL *else_label = NEW_LABEL(location.line);
3757 LABEL *end_label = NEW_LABEL(location.line);
3758 LABEL *retry_end_l = NEW_LABEL(location.line);
3759
3760 VALUE branches = Qfalse;
3761 rb_code_location_t code_location = { 0 };
3762 int node_id = location.node_id;
3763
3764 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
3765 if (PM_BRANCH_COVERAGE_P(iseq)) {
3766 uint32_t end_cursor = 0;
3767 bool end_found = false;
3768
3769 if (call_node->closing_loc.length > 0) {
3770 uint32_t cursor = call_node->closing_loc.start + call_node->closing_loc.length;
3771 end_cursor = cursor;
3772 end_found = true;
3773 }
3774
3775 if (call_node->arguments != NULL) {
3776 uint32_t cursor = call_node->arguments->base.location.start + call_node->arguments->base.location.length;
3777 if (!end_found || cursor > end_cursor) {
3778 end_cursor = cursor;
3779 end_found = true;
3780 }
3781 }
3782
3783 if (call_node->message_loc.length > 0) {
3784 uint32_t cursor = call_node->message_loc.start + call_node->message_loc.length;
3785 if (!end_found || cursor > end_cursor) {
3786 end_cursor = cursor;
3787 end_found = true;
3788 }
3789 }
3790
3791 if (!end_found) {
3792 end_cursor = call_node->closing_loc.start + call_node->closing_loc.length;
3793 }
3794
3795 const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(call_node);
3796 const pm_line_column_t end_location = pm_line_offset_list_line_column_cached(scope_node->line_offsets, end_cursor, scope_node->start_line, &scope_node->last_line);
3797
3798 code_location = (rb_code_location_t) {
3799 .beg_pos = { .lineno = start_location.line, .column = start_location.column },
3800 .end_pos = { .lineno = end_location.line, .column = end_location.column }
3801 };
3802
3803 branches = decl_branch_base(iseq, (int) call_node->base.node_id, &code_location, "&.");
3804 }
3805
3806 PUSH_INSN(ret, location, dup);
3807 PUSH_INSNL(ret, location, branchnil, else_label);
3808
3809 add_trace_branch_coverage(iseq, ret, &code_location, node_id, 0, "then", branches);
3810 }
3811
3812 LINK_ELEMENT *opt_new_prelude = LAST_ELEMENT(ret);
3813
3814 int flags = 0;
3815 struct rb_callinfo_kwarg *kw_arg = NULL;
3816
3817 int orig_argc = pm_setup_args(call_node->arguments, call_node->block, &flags, &kw_arg, iseq, ret, scope_node, &location);
3818 const rb_iseq_t *previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
3819 const rb_iseq_t *block_iseq = NULL;
3820
3821 if (call_node->block != NULL && PM_NODE_TYPE_P(call_node->block, PM_BLOCK_NODE)) {
3822 // Scope associated with the block
3823 pm_scope_node_t next_scope_node;
3824 pm_scope_node_init(call_node->block, &next_scope_node, scope_node);
3825
3826 block_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, pm_node_line_number_cached(call_node->block, scope_node));
3827 pm_scope_node_destroy(&next_scope_node);
3828 ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
3829 }
3830 else {
3831 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
3832 flags |= VM_CALL_VCALL;
3833 }
3834
3835 if (!flags) {
3836 flags |= VM_CALL_ARGS_SIMPLE;
3837 }
3838 }
3839
3840 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) {
3841 flags |= VM_CALL_FCALL;
3842 }
3843
3844 if (!popped && PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) {
3845 if (flags & VM_CALL_ARGS_BLOCKARG) {
3846 PUSH_INSN1(ret, location, topn, INT2FIX(1));
3847 if (flags & VM_CALL_ARGS_SPLAT) {
3848 PUSH_INSN1(ret, location, putobject, INT2FIX(-1));
3849 PUSH_SEND_WITH_FLAG(ret, location, idAREF, INT2FIX(1), INT2FIX(0));
3850 }
3851 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 3));
3852 PUSH_INSN(ret, location, pop);
3853 }
3854 else if (flags & VM_CALL_ARGS_SPLAT) {
3855 PUSH_INSN(ret, location, dup);
3856 PUSH_INSN1(ret, location, putobject, INT2FIX(-1));
3857 PUSH_SEND_WITH_FLAG(ret, location, idAREF, INT2FIX(1), INT2FIX(0));
3858 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 2));
3859 PUSH_INSN(ret, location, pop);
3860 }
3861 else {
3862 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 1));
3863 }
3864 }
3865
3866 if ((flags & VM_CALL_KW_SPLAT) && (flags & VM_CALL_ARGS_BLOCKARG) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
3867 PUSH_INSN(ret, location, splatkw);
3868 }
3869
3870 LABEL *not_basic_new = NEW_LABEL(location.line);
3871 LABEL *not_basic_new_finish = NEW_LABEL(location.line);
3872
3873 bool inline_new = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction &&
3874 method_id == rb_intern("new") &&
3875 call_node->block == NULL &&
3876 (flags & VM_CALL_ARGS_BLOCKARG) == 0;
3877
3878 if (inline_new) {
3879 if (LAST_ELEMENT(ret) == opt_new_prelude) {
3880 PUSH_INSN(ret, location, putnil);
3881 PUSH_INSN(ret, location, swap);
3882 }
3883 else {
3884 ELEM_INSERT_NEXT(opt_new_prelude, &new_insn_body(iseq, location.line, location.node_id, BIN(swap), 0)->link);
3885 ELEM_INSERT_NEXT(opt_new_prelude, &new_insn_body(iseq, location.line, location.node_id, BIN(putnil), 0)->link);
3886 }
3887
3888 rb_callinfo_kwarg_retain(kw_arg);
3889
3890 // Jump unless the receiver uses the "basic" implementation of "new"
3891 VALUE ci;
3892 if (flags & VM_CALL_FORWARDING) {
3893 ci = (VALUE)new_callinfo(iseq, method_id, orig_argc + 1, flags, kw_arg, 0);
3894 }
3895 else {
3896 ci = (VALUE)new_callinfo(iseq, method_id, orig_argc, flags, kw_arg, 0);
3897 }
3898
3899 PUSH_INSN2(ret, location, opt_new, ci, not_basic_new);
3900 LABEL_REF(not_basic_new);
3901 // optimized path
3902 PUSH_SEND_R(ret, location, rb_intern("initialize"), INT2FIX(orig_argc), block_iseq, INT2FIX(flags | VM_CALL_FCALL), kw_arg);
3903 PUSH_INSNL(ret, location, jump, not_basic_new_finish);
3904
3905 PUSH_LABEL(ret, not_basic_new);
3906 // Fall back to normal send
3907 PUSH_SEND_R(ret, location, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg);
3908 PUSH_INSN(ret, location, swap);
3909
3910 PUSH_LABEL(ret, not_basic_new_finish);
3911 PUSH_INSN(ret, location, pop);
3912
3913 rb_callinfo_kwarg_release(kw_arg);
3914 }
3915 else {
3916 PUSH_SEND_R(ret, location, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg);
3917 }
3918
3919 if (block_iseq && ISEQ_BODY(block_iseq)->catch_table) {
3920 pm_compile_retry_end_label(iseq, ret, retry_end_l);
3921 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, start, retry_end_l, block_iseq, retry_end_l);
3922 }
3923
3924 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
3925 PUSH_INSNL(ret, location, jump, end_label);
3926 PUSH_LABEL(ret, else_label);
3927 add_trace_branch_coverage(iseq, ret, &code_location, node_id, 1, "else", branches);
3928 PUSH_LABEL(ret, end_label);
3929 }
3930
3931 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && !popped) {
3932 PUSH_INSN(ret, location, pop);
3933 }
3934
3935 if (popped) PUSH_INSN(ret, location, pop);
3936 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
3937}
3938
3943static inline VALUE
3944pm_compile_back_reference_ref(const pm_scope_node_t *scope_node, const pm_back_reference_read_node_t *node)
3945{
3946 const char *type = (const char *) (pm_parser_start(scope_node->parser) + node->base.location.start + 1);
3947
3948 // Since a back reference is `$<char>`, Ruby represents the ID as an
3949 // rb_intern on the value after the `$`.
3950 return INT2FIX(rb_intern2(type, 1)) << 1 | 1;
3951}
3952
3957static inline VALUE
3958pm_compile_numbered_reference_ref(const pm_numbered_reference_read_node_t *node)
3959{
3960 return INT2FIX(node->number << 1);
3961}
3962
3963static void
3964pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition, LABEL **lfinish, bool explicit_receiver)
3965{
3966#define PUSH_VAL(type) (in_condition ? Qtrue : rb_iseq_defined_string(type))
3967
3968 // in_condition is the same as compile.c's needstr
3969 enum defined_type dtype = DEFINED_NOT_DEFINED;
3970 const pm_node_location_t location = *node_location;
3971
3972 switch (PM_NODE_TYPE(node)) {
3973/* DEFINED_NIL ****************************************************************/
3974 case PM_NIL_NODE:
3975 // defined?(nil)
3976 // ^^^
3977 dtype = DEFINED_NIL;
3978 break;
3979/* DEFINED_IVAR ***************************************************************/
3980 case PM_INSTANCE_VARIABLE_READ_NODE: {
3981 // defined?(@a)
3982 // ^^
3984 ID name = pm_constant_id_lookup(scope_node, cast->name);
3985
3986 PUSH_INSN3(ret, location, definedivar, ID2SYM(name), get_ivar_ic_value(iseq, name), PUSH_VAL(DEFINED_IVAR));
3987
3988 return;
3989 }
3990/* DEFINED_LVAR ***************************************************************/
3991 case PM_LOCAL_VARIABLE_READ_NODE:
3992 // a = 1; defined?(a)
3993 // ^
3994 case PM_IT_LOCAL_VARIABLE_READ_NODE:
3995 // 1.then { defined?(it) }
3996 // ^^
3997 dtype = DEFINED_LVAR;
3998 break;
3999/* DEFINED_GVAR ***************************************************************/
4000 case PM_GLOBAL_VARIABLE_READ_NODE: {
4001 // defined?($a)
4002 // ^^
4004 ID name = pm_constant_id_lookup(scope_node, cast->name);
4005
4006 PUSH_INSN(ret, location, putnil);
4007 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_GVAR), ID2SYM(name), PUSH_VAL(DEFINED_GVAR));
4008
4009 return;
4010 }
4011/* DEFINED_CVAR ***************************************************************/
4012 case PM_CLASS_VARIABLE_READ_NODE: {
4013 // defined?(@@a)
4014 // ^^^
4016 ID name = pm_constant_id_lookup(scope_node, cast->name);
4017
4018 PUSH_INSN(ret, location, putnil);
4019 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CVAR), ID2SYM(name), PUSH_VAL(DEFINED_CVAR));
4020
4021 return;
4022 }
4023/* DEFINED_CONST **************************************************************/
4024 case PM_CONSTANT_READ_NODE: {
4025 // defined?(A)
4026 // ^
4027 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
4028 ID name = pm_constant_id_lookup(scope_node, cast->name);
4029
4030 PUSH_INSN(ret, location, putnil);
4031 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), ID2SYM(name), PUSH_VAL(DEFINED_CONST));
4032
4033 return;
4034 }
4035/* DEFINED_YIELD **************************************************************/
4036 case PM_YIELD_NODE:
4037 // defined?(yield)
4038 // ^^^^^
4039 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
4040
4041 PUSH_INSN(ret, location, putnil);
4042 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_YIELD), 0, PUSH_VAL(DEFINED_YIELD));
4043
4044 return;
4045/* DEFINED_ZSUPER *************************************************************/
4046 case PM_SUPER_NODE: {
4047 // defined?(super 1, 2)
4048 // ^^^^^^^^^^
4049 const pm_super_node_t *cast = (const pm_super_node_t *) node;
4050
4051 if (cast->block != NULL && !PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE)) {
4052 dtype = DEFINED_EXPR;
4053 break;
4054 }
4055
4056 PUSH_INSN(ret, location, putnil);
4057 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_ZSUPER), 0, PUSH_VAL(DEFINED_ZSUPER));
4058 return;
4059 }
4060 case PM_FORWARDING_SUPER_NODE: {
4061 // defined?(super)
4062 // ^^^^^
4063 const pm_forwarding_super_node_t *cast = (const pm_forwarding_super_node_t *) node;
4064
4065 if (cast->block != NULL) {
4066 dtype = DEFINED_EXPR;
4067 break;
4068 }
4069
4070 PUSH_INSN(ret, location, putnil);
4071 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_ZSUPER), 0, PUSH_VAL(DEFINED_ZSUPER));
4072 return;
4073 }
4074/* DEFINED_SELF ***************************************************************/
4075 case PM_SELF_NODE:
4076 // defined?(self)
4077 // ^^^^
4078 dtype = DEFINED_SELF;
4079 break;
4080/* DEFINED_TRUE ***************************************************************/
4081 case PM_TRUE_NODE:
4082 // defined?(true)
4083 // ^^^^
4084 dtype = DEFINED_TRUE;
4085 break;
4086/* DEFINED_FALSE **************************************************************/
4087 case PM_FALSE_NODE:
4088 // defined?(false)
4089 // ^^^^^
4090 dtype = DEFINED_FALSE;
4091 break;
4092/* DEFINED_ASGN ***************************************************************/
4093 case PM_CALL_AND_WRITE_NODE:
4094 // defined?(a.a &&= 1)
4095 // ^^^^^^^^^
4096 case PM_CALL_OPERATOR_WRITE_NODE:
4097 // defined?(a.a += 1)
4098 // ^^^^^^^^
4099 case PM_CALL_OR_WRITE_NODE:
4100 // defined?(a.a ||= 1)
4101 // ^^^^^^^^^
4102 case PM_CLASS_VARIABLE_AND_WRITE_NODE:
4103 // defined?(@@a &&= 1)
4104 // ^^^^^^^^^
4105 case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE:
4106 // defined?(@@a += 1)
4107 // ^^^^^^^^
4108 case PM_CLASS_VARIABLE_OR_WRITE_NODE:
4109 // defined?(@@a ||= 1)
4110 // ^^^^^^^^^
4111 case PM_CLASS_VARIABLE_WRITE_NODE:
4112 // defined?(@@a = 1)
4113 // ^^^^^^^
4114 case PM_CONSTANT_AND_WRITE_NODE:
4115 // defined?(A &&= 1)
4116 // ^^^^^^^
4117 case PM_CONSTANT_OPERATOR_WRITE_NODE:
4118 // defined?(A += 1)
4119 // ^^^^^^
4120 case PM_CONSTANT_OR_WRITE_NODE:
4121 // defined?(A ||= 1)
4122 // ^^^^^^^
4123 case PM_CONSTANT_PATH_AND_WRITE_NODE:
4124 // defined?(A::A &&= 1)
4125 // ^^^^^^^^^^
4126 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE:
4127 // defined?(A::A += 1)
4128 // ^^^^^^^^^
4129 case PM_CONSTANT_PATH_OR_WRITE_NODE:
4130 // defined?(A::A ||= 1)
4131 // ^^^^^^^^^^
4132 case PM_CONSTANT_PATH_WRITE_NODE:
4133 // defined?(A::A = 1)
4134 // ^^^^^^^^
4135 case PM_CONSTANT_WRITE_NODE:
4136 // defined?(A = 1)
4137 // ^^^^^
4138 case PM_GLOBAL_VARIABLE_AND_WRITE_NODE:
4139 // defined?($a &&= 1)
4140 // ^^^^^^^^
4141 case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE:
4142 // defined?($a += 1)
4143 // ^^^^^^^
4144 case PM_GLOBAL_VARIABLE_OR_WRITE_NODE:
4145 // defined?($a ||= 1)
4146 // ^^^^^^^^
4147 case PM_GLOBAL_VARIABLE_WRITE_NODE:
4148 // defined?($a = 1)
4149 // ^^^^^^
4150 case PM_INDEX_AND_WRITE_NODE:
4151 // defined?(a[1] &&= 1)
4152 // ^^^^^^^^^^
4153 case PM_INDEX_OPERATOR_WRITE_NODE:
4154 // defined?(a[1] += 1)
4155 // ^^^^^^^^^
4156 case PM_INDEX_OR_WRITE_NODE:
4157 // defined?(a[1] ||= 1)
4158 // ^^^^^^^^^^
4159 case PM_INSTANCE_VARIABLE_AND_WRITE_NODE:
4160 // defined?(@a &&= 1)
4161 // ^^^^^^^^
4162 case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE:
4163 // defined?(@a += 1)
4164 // ^^^^^^^
4165 case PM_INSTANCE_VARIABLE_OR_WRITE_NODE:
4166 // defined?(@a ||= 1)
4167 // ^^^^^^^^
4168 case PM_INSTANCE_VARIABLE_WRITE_NODE:
4169 // defined?(@a = 1)
4170 // ^^^^^^
4171 case PM_LOCAL_VARIABLE_AND_WRITE_NODE:
4172 // defined?(a &&= 1)
4173 // ^^^^^^^
4174 case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE:
4175 // defined?(a += 1)
4176 // ^^^^^^
4177 case PM_LOCAL_VARIABLE_OR_WRITE_NODE:
4178 // defined?(a ||= 1)
4179 // ^^^^^^^
4180 case PM_LOCAL_VARIABLE_WRITE_NODE:
4181 // defined?(a = 1)
4182 // ^^^^^
4183 case PM_MULTI_WRITE_NODE:
4184 // defined?((a, = 1))
4185 // ^^^^^^
4186 dtype = DEFINED_ASGN;
4187 break;
4188/* DEFINED_EXPR ***************************************************************/
4189 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
4190 // defined?((alias $a $b))
4191 // ^^^^^^^^^^^
4192 case PM_ALIAS_METHOD_NODE:
4193 // defined?((alias a b))
4194 // ^^^^^^^^^
4195 case PM_AND_NODE:
4196 // defined?(a and b)
4197 // ^^^^^^^
4198 case PM_BREAK_NODE:
4199 // defined?(break 1)
4200 // ^^^^^^^
4201 case PM_CASE_MATCH_NODE:
4202 // defined?(case 1; in 1; end)
4203 // ^^^^^^^^^^^^^^^^^
4204 case PM_CASE_NODE:
4205 // defined?(case 1; when 1; end)
4206 // ^^^^^^^^^^^^^^^^^^^
4207 case PM_CLASS_NODE:
4208 // defined?(class Foo; end)
4209 // ^^^^^^^^^^^^^^
4210 case PM_DEF_NODE:
4211 // defined?(def a() end)
4212 // ^^^^^^^^^^^
4213 case PM_DEFINED_NODE:
4214 // defined?(defined?(a))
4215 // ^^^^^^^^^^^
4216 case PM_FLIP_FLOP_NODE:
4217 // defined?(not (a .. b))
4218 // ^^^^^^
4219 case PM_FLOAT_NODE:
4220 // defined?(1.0)
4221 // ^^^
4222 case PM_FOR_NODE:
4223 // defined?(for a in 1 do end)
4224 // ^^^^^^^^^^^^^^^^^
4225 case PM_IF_NODE:
4226 // defined?(if a then end)
4227 // ^^^^^^^^^^^^^
4228 case PM_IMAGINARY_NODE:
4229 // defined?(1i)
4230 // ^^
4231 case PM_INTEGER_NODE:
4232 // defined?(1)
4233 // ^
4234 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE:
4235 // defined?(not /#{1}/)
4236 // ^^^^^^
4237 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
4238 // defined?(/#{1}/)
4239 // ^^^^^^
4240 case PM_INTERPOLATED_STRING_NODE:
4241 // defined?("#{1}")
4242 // ^^^^^^
4243 case PM_INTERPOLATED_SYMBOL_NODE:
4244 // defined?(:"#{1}")
4245 // ^^^^^^^
4246 case PM_INTERPOLATED_X_STRING_NODE:
4247 // defined?(`#{1}`)
4248 // ^^^^^^
4249 case PM_LAMBDA_NODE:
4250 // defined?(-> {})
4251 // ^^^^^
4252 case PM_MATCH_LAST_LINE_NODE:
4253 // defined?(not //)
4254 // ^^^^^^
4255 case PM_MATCH_PREDICATE_NODE:
4256 // defined?(1 in 1)
4257 // ^^^^^^
4258 case PM_MATCH_REQUIRED_NODE:
4259 // defined?(1 => 1)
4260 // ^^^^^^
4261 case PM_MATCH_WRITE_NODE:
4262 // defined?(/(?<a>)/ =~ "")
4263 // ^^^^^^^^^^^^^^
4264 case PM_MODULE_NODE:
4265 // defined?(module A end)
4266 // ^^^^^^^^^^^^
4267 case PM_NEXT_NODE:
4268 // defined?(next 1)
4269 // ^^^^^^
4270 case PM_OR_NODE:
4271 // defined?(a or b)
4272 // ^^^^^^
4273 case PM_POST_EXECUTION_NODE:
4274 // defined?((END {}))
4275 // ^^^^^^^^
4276 case PM_RANGE_NODE:
4277 // defined?(1..1)
4278 // ^^^^
4279 case PM_RATIONAL_NODE:
4280 // defined?(1r)
4281 // ^^
4282 case PM_REDO_NODE:
4283 // defined?(redo)
4284 // ^^^^
4285 case PM_REGULAR_EXPRESSION_NODE:
4286 // defined?(//)
4287 // ^^
4288 case PM_RESCUE_MODIFIER_NODE:
4289 // defined?(a rescue b)
4290 // ^^^^^^^^^^
4291 case PM_RETRY_NODE:
4292 // defined?(retry)
4293 // ^^^^^
4294 case PM_RETURN_NODE:
4295 // defined?(return)
4296 // ^^^^^^
4297 case PM_SINGLETON_CLASS_NODE:
4298 // defined?(class << self; end)
4299 // ^^^^^^^^^^^^^^^^^^
4300 case PM_SOURCE_ENCODING_NODE:
4301 // defined?(__ENCODING__)
4302 // ^^^^^^^^^^^^
4303 case PM_SOURCE_FILE_NODE:
4304 // defined?(__FILE__)
4305 // ^^^^^^^^
4306 case PM_SOURCE_LINE_NODE:
4307 // defined?(__LINE__)
4308 // ^^^^^^^^
4309 case PM_STRING_NODE:
4310 // defined?("")
4311 // ^^
4312 case PM_SYMBOL_NODE:
4313 // defined?(:a)
4314 // ^^
4315 case PM_UNDEF_NODE:
4316 // defined?((undef a))
4317 // ^^^^^^^
4318 case PM_UNLESS_NODE:
4319 // defined?(unless a then end)
4320 // ^^^^^^^^^^^^^^^^^
4321 case PM_UNTIL_NODE:
4322 // defined?(until a do end)
4323 // ^^^^^^^^^^^^^^
4324 case PM_WHILE_NODE:
4325 // defined?(while a do end)
4326 // ^^^^^^^^^^^^^^
4327 case PM_X_STRING_NODE:
4328 // defined?(``)
4329 // ^^
4330 dtype = DEFINED_EXPR;
4331 break;
4332/* DEFINED_REF ****************************************************************/
4333 case PM_BACK_REFERENCE_READ_NODE: {
4334 // defined?($+)
4335 // ^^
4337 VALUE ref = pm_compile_back_reference_ref(scope_node, cast);
4338
4339 PUSH_INSN(ret, location, putnil);
4340 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_REF), ref, PUSH_VAL(DEFINED_GVAR));
4341
4342 return;
4343 }
4344 case PM_NUMBERED_REFERENCE_READ_NODE: {
4345 // defined?($1)
4346 // ^^
4348 VALUE ref = pm_compile_numbered_reference_ref(cast);
4349
4350 PUSH_INSN(ret, location, putnil);
4351 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_REF), ref, PUSH_VAL(DEFINED_GVAR));
4352
4353 return;
4354 }
4355/* DEFINED_CONST_FROM *********************************************************/
4356 case PM_CONSTANT_PATH_NODE: {
4357 // defined?(A::A)
4358 // ^^^^
4359 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
4360 ID name = pm_constant_id_lookup(scope_node, cast->name);
4361
4362 if (cast->parent != NULL) {
4363 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4364 pm_compile_defined_expr0(iseq, cast->parent, node_location, ret, popped, scope_node, true, lfinish, false);
4365
4366 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4367 PM_COMPILE(cast->parent);
4368 }
4369 else {
4370 PUSH_INSN1(ret, location, putobject, rb_cObject);
4371 }
4372
4373 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), ID2SYM(name), PUSH_VAL(DEFINED_CONST));
4374 return;
4375 }
4376/* Containers *****************************************************************/
4377 case PM_BEGIN_NODE: {
4378 // defined?(begin end)
4379 // ^^^^^^^^^
4380 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
4381
4382 if (cast->rescue_clause == NULL && cast->ensure_clause == NULL && cast->else_clause == NULL) {
4383 if (cast->statements == NULL) {
4384 // If we have empty statements, then we want to return "nil".
4385 dtype = DEFINED_NIL;
4386 }
4387 else if (cast->statements->body.size == 1) {
4388 // If we have a begin node that is wrapping a single statement
4389 // then we want to recurse down to that statement and compile
4390 // it.
4391 pm_compile_defined_expr0(iseq, cast->statements->body.nodes[0], node_location, ret, popped, scope_node, in_condition, lfinish, false);
4392 return;
4393 }
4394 else {
4395 // Otherwise, we have a begin wrapping multiple statements, in
4396 // which case this is defined as "expression".
4397 dtype = DEFINED_EXPR;
4398 }
4399 } else {
4400 // If we have any of the other clauses besides the main begin/end,
4401 // this is defined as "expression".
4402 dtype = DEFINED_EXPR;
4403 }
4404
4405 break;
4406 }
4407 case PM_PARENTHESES_NODE: {
4408 // defined?(())
4409 // ^^
4410 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
4411
4412 if (cast->body == NULL) {
4413 // If we have empty parentheses, then we want to return "nil".
4414 dtype = DEFINED_NIL;
4415 }
4416 else if (PM_NODE_TYPE_P(cast->body, PM_STATEMENTS_NODE) && !PM_NODE_FLAG_P(cast, PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS)) {
4417 // If we have a parentheses node that is wrapping a single statement
4418 // then we want to recurse down to that statement and compile it.
4419 pm_compile_defined_expr0(iseq, ((const pm_statements_node_t *) cast->body)->body.nodes[0], node_location, ret, popped, scope_node, in_condition, lfinish, false);
4420 return;
4421 }
4422 else {
4423 // Otherwise, we have parentheses wrapping multiple statements, in
4424 // which case this is defined as "expression".
4425 dtype = DEFINED_EXPR;
4426 }
4427
4428 break;
4429 }
4430 case PM_ARRAY_NODE: {
4431 // defined?([])
4432 // ^^
4433 const pm_array_node_t *cast = (const pm_array_node_t *) node;
4434
4435 if (cast->elements.size > 0 && !lfinish[1]) {
4436 lfinish[1] = NEW_LABEL(location.line);
4437 }
4438
4439 for (size_t index = 0; index < cast->elements.size; index++) {
4440 pm_compile_defined_expr0(iseq, cast->elements.nodes[index], node_location, ret, popped, scope_node, true, lfinish, false);
4441 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4442 }
4443
4444 dtype = DEFINED_EXPR;
4445 break;
4446 }
4447 case PM_HASH_NODE:
4448 // defined?({ a: 1 })
4449 // ^^^^^^^^
4450 case PM_KEYWORD_HASH_NODE: {
4451 // defined?(a(a: 1))
4452 // ^^^^
4453 const pm_node_list_t *elements;
4454
4455 if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) {
4456 elements = &((const pm_hash_node_t *) node)->elements;
4457 }
4458 else {
4459 elements = &((const pm_keyword_hash_node_t *) node)->elements;
4460 }
4461
4462 if (elements->size > 0 && !lfinish[1]) {
4463 lfinish[1] = NEW_LABEL(location.line);
4464 }
4465
4466 for (size_t index = 0; index < elements->size; index++) {
4467 pm_compile_defined_expr0(iseq, elements->nodes[index], node_location, ret, popped, scope_node, true, lfinish, false);
4468 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4469 }
4470
4471 dtype = DEFINED_EXPR;
4472 break;
4473 }
4474 case PM_ASSOC_NODE: {
4475 // defined?({ a: 1 })
4476 // ^^^^
4477 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node;
4478
4479 pm_compile_defined_expr0(iseq, cast->key, node_location, ret, popped, scope_node, true, lfinish, false);
4480 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4481 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, true, lfinish, false);
4482
4483 return;
4484 }
4485 case PM_ASSOC_SPLAT_NODE: {
4486 // defined?({ **a })
4487 // ^^^^
4488 const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node;
4489
4490 if (cast->value == NULL) {
4491 dtype = DEFINED_EXPR;
4492 break;
4493 }
4494
4495 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, true, lfinish, false);
4496 return;
4497 }
4498 case PM_IMPLICIT_NODE: {
4499 // defined?({ a: })
4500 // ^^
4501 const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node;
4502 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4503 return;
4504 }
4505 case PM_CALL_NODE: {
4506#define BLOCK_P(cast) ((cast)->block != NULL && PM_NODE_TYPE_P((cast)->block, PM_BLOCK_NODE))
4507
4508 // defined?(a(1, 2, 3))
4509 // ^^^^^^^^^^
4510 const pm_call_node_t *cast = ((const pm_call_node_t *) node);
4511
4512 if (BLOCK_P(cast)) {
4513 dtype = DEFINED_EXPR;
4514 break;
4515 }
4516
4517 if (cast->receiver || cast->arguments || (cast->block && PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE))) {
4518 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4519 if (!lfinish[2]) lfinish[2] = NEW_LABEL(location.line);
4520 }
4521
4522 if (cast->arguments) {
4523 pm_compile_defined_expr0(iseq, (const pm_node_t *) cast->arguments, node_location, ret, popped, scope_node, true, lfinish, false);
4524 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4525 }
4526
4527 if (cast->block && PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE)) {
4528 pm_compile_defined_expr0(iseq, cast->block, node_location, ret, popped, scope_node, true, lfinish, false);
4529 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4530 }
4531
4532 if (cast->receiver) {
4533 if (PM_NODE_TYPE_P(cast->receiver, PM_CALL_NODE) && !BLOCK_P((const pm_call_node_t *) cast->receiver)) {
4534 // Special behavior here where we chain calls together. This is
4535 // the only path that sets explicit_receiver to true.
4536 pm_compile_defined_expr0(iseq, cast->receiver, node_location, ret, popped, scope_node, true, lfinish, true);
4537 PUSH_INSNL(ret, location, branchunless, lfinish[2]);
4538
4539 const pm_call_node_t *receiver = (const pm_call_node_t *) cast->receiver;
4540 ID method_id = pm_constant_id_lookup(scope_node, receiver->name);
4541
4542 pm_compile_call(iseq, receiver, ret, popped, scope_node, method_id, NULL);
4543 }
4544 else {
4545 pm_compile_defined_expr0(iseq, cast->receiver, node_location, ret, popped, scope_node, true, lfinish, false);
4546 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4547 PM_COMPILE(cast->receiver);
4548 }
4549
4550 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
4551
4552 if (explicit_receiver) PUSH_INSN(ret, location, dup);
4553 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_METHOD), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
4554 }
4555 else {
4556 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
4557
4558 PUSH_INSN(ret, location, putself);
4559 if (explicit_receiver) PUSH_INSN(ret, location, dup);
4560
4561 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_FUNC), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
4562 }
4563
4564 return;
4565
4566#undef BLOCK_P
4567 }
4568 case PM_ARGUMENTS_NODE: {
4569 // defined?(a(1, 2, 3))
4570 // ^^^^^^^
4571 const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node;
4572
4573 for (size_t index = 0; index < cast->arguments.size; index++) {
4574 pm_compile_defined_expr0(iseq, cast->arguments.nodes[index], node_location, ret, popped, scope_node, in_condition, lfinish, false);
4575 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4576 }
4577
4578 dtype = DEFINED_EXPR;
4579 break;
4580 }
4581 case PM_BLOCK_ARGUMENT_NODE:
4582 // defined?(a(&b))
4583 // ^^
4584 dtype = DEFINED_EXPR;
4585 break;
4586 case PM_FORWARDING_ARGUMENTS_NODE:
4587 // def a(...) = defined?(a(...))
4588 // ^^^
4589 dtype = DEFINED_EXPR;
4590 break;
4591 case PM_SPLAT_NODE: {
4592 // def a(*) = defined?(a(*))
4593 // ^
4594 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
4595
4596 if (cast->expression == NULL) {
4597 dtype = DEFINED_EXPR;
4598 break;
4599 }
4600
4601 pm_compile_defined_expr0(iseq, cast->expression, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4602
4603 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4604 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4605
4606 dtype = DEFINED_EXPR;
4607 break;
4608 }
4609 case PM_SHAREABLE_CONSTANT_NODE:
4610 // # shareable_constant_value: literal
4611 // defined?(A = 1)
4612 // ^^^^^
4613 pm_compile_defined_expr0(iseq, ((const pm_shareable_constant_node_t *) node)->write, node_location, ret, popped, scope_node, in_condition, lfinish, explicit_receiver);
4614 return;
4615/* Unreachable (parameters) ***************************************************/
4616 case PM_BLOCK_LOCAL_VARIABLE_NODE:
4617 case PM_BLOCK_PARAMETER_NODE:
4618 case PM_BLOCK_PARAMETERS_NODE:
4619 case PM_FORWARDING_PARAMETER_NODE:
4620 case PM_IMPLICIT_REST_NODE:
4621 case PM_IT_PARAMETERS_NODE:
4622 case PM_PARAMETERS_NODE:
4623 case PM_KEYWORD_REST_PARAMETER_NODE:
4624 case PM_NO_KEYWORDS_PARAMETER_NODE:
4625 case PM_NO_BLOCK_PARAMETER_NODE:
4626 case PM_NUMBERED_PARAMETERS_NODE:
4627 case PM_OPTIONAL_KEYWORD_PARAMETER_NODE:
4628 case PM_OPTIONAL_PARAMETER_NODE:
4629 case PM_REQUIRED_KEYWORD_PARAMETER_NODE:
4630 case PM_REQUIRED_PARAMETER_NODE:
4631 case PM_REST_PARAMETER_NODE:
4632/* Unreachable (pattern matching) *********************************************/
4633 case PM_ALTERNATION_PATTERN_NODE:
4634 case PM_ARRAY_PATTERN_NODE:
4635 case PM_CAPTURE_PATTERN_NODE:
4636 case PM_FIND_PATTERN_NODE:
4637 case PM_HASH_PATTERN_NODE:
4638 case PM_PINNED_EXPRESSION_NODE:
4639 case PM_PINNED_VARIABLE_NODE:
4640/* Unreachable (indirect writes) **********************************************/
4641 case PM_CALL_TARGET_NODE:
4642 case PM_CLASS_VARIABLE_TARGET_NODE:
4643 case PM_CONSTANT_PATH_TARGET_NODE:
4644 case PM_CONSTANT_TARGET_NODE:
4645 case PM_GLOBAL_VARIABLE_TARGET_NODE:
4646 case PM_INDEX_TARGET_NODE:
4647 case PM_INSTANCE_VARIABLE_TARGET_NODE:
4648 case PM_LOCAL_VARIABLE_TARGET_NODE:
4649 case PM_MULTI_TARGET_NODE:
4650/* Unreachable (clauses) ******************************************************/
4651 case PM_ELSE_NODE:
4652 case PM_ENSURE_NODE:
4653 case PM_IN_NODE:
4654 case PM_RESCUE_NODE:
4655 case PM_WHEN_NODE:
4656/* Unreachable (miscellaneous) ************************************************/
4657 case PM_BLOCK_NODE:
4658 case PM_EMBEDDED_STATEMENTS_NODE:
4659 case PM_EMBEDDED_VARIABLE_NODE:
4660 case PM_ERROR_RECOVERY_NODE:
4661 case PM_PRE_EXECUTION_NODE:
4662 case PM_PROGRAM_NODE:
4663 case PM_SCOPE_NODE:
4664 case PM_STATEMENTS_NODE:
4665 rb_bug("Unreachable node in defined?: %s", pm_node_type(PM_NODE_TYPE(node)));
4666 }
4667
4668 RUBY_ASSERT(dtype != DEFINED_NOT_DEFINED);
4669 PUSH_INSN1(ret, location, putobject, PUSH_VAL(dtype));
4670
4671#undef PUSH_VAL
4672}
4673
4674static void
4675pm_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition, LABEL **lfinish)
4676{
4677 LINK_ELEMENT *lcur = ret->last;
4678 pm_compile_defined_expr0(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4679
4680 if (lfinish[1]) {
4681 LABEL *lstart = NEW_LABEL(node_location->line);
4682 LABEL *lend = NEW_LABEL(node_location->line);
4683
4685 rb_iseq_new_with_callback_new_callback(build_defined_rescue_iseq, NULL);
4686
4687 const rb_iseq_t *rescue = new_child_iseq_with_callback(
4688 iseq,
4689 ifunc,
4690 rb_str_concat(rb_str_new2("defined guard in "), ISEQ_BODY(iseq)->location.label),
4691 iseq,
4692 ISEQ_TYPE_RESCUE,
4693 0
4694 );
4695
4696 lstart->rescued = LABEL_RESCUE_BEG;
4697 lend->rescued = LABEL_RESCUE_END;
4698
4699 APPEND_LABEL(ret, lcur, lstart);
4700 PUSH_LABEL(ret, lend);
4701 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
4702 }
4703}
4704
4705static void
4706pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition)
4707{
4708 LABEL *lfinish[3];
4709 LINK_ELEMENT *last = ret->last;
4710
4711 lfinish[0] = NEW_LABEL(node_location->line);
4712 lfinish[1] = 0;
4713 lfinish[2] = 0;
4714
4715 if (!popped) {
4716 pm_defined_expr(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish);
4717 }
4718
4719 if (lfinish[1]) {
4720 ELEM_INSERT_NEXT(last, &new_insn_body(iseq, node_location->line, node_location->node_id, BIN(putnil), 0)->link);
4721 PUSH_INSN(ret, *node_location, swap);
4722
4723 if (lfinish[2]) PUSH_LABEL(ret, lfinish[2]);
4724 PUSH_INSN(ret, *node_location, pop);
4725 PUSH_LABEL(ret, lfinish[1]);
4726
4727 }
4728
4729 PUSH_LABEL(ret, lfinish[0]);
4730}
4731
4732// This is exactly the same as add_ensure_iseq, except it compiled
4733// the node as a Prism node, and not a CRuby node
4734static void
4735pm_add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return, pm_scope_node_t *scope_node)
4736{
4737 RUBY_ASSERT(can_add_ensure_iseq(iseq));
4738
4740 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
4741 struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
4742 DECL_ANCHOR(ensure);
4743
4744 while (enlp) {
4745 if (enlp->erange != NULL) {
4746 DECL_ANCHOR(ensure_part);
4747 LABEL *lstart = NEW_LABEL(0);
4748 LABEL *lend = NEW_LABEL(0);
4749
4750 add_ensure_range(iseq, enlp->erange, lstart, lend);
4751
4752 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
4753 PUSH_LABEL(ensure_part, lstart);
4754 bool popped = true;
4755 PM_COMPILE_INTO_ANCHOR(ensure_part, (const pm_node_t *) enlp->ensure_node);
4756 PUSH_LABEL(ensure_part, lend);
4757 PUSH_SEQ(ensure, ensure_part);
4758 }
4759 else {
4760 if (!is_return) {
4761 break;
4762 }
4763 }
4764 enlp = enlp->prev;
4765 }
4766 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = prev_enlp;
4767 PUSH_SEQ(ret, ensure);
4768}
4769
4770
4771
4777static void
4778pm_insert_local_index(pm_constant_id_t constant_id, int local_index, pm_index_lookup_table_t *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node)
4779{
4780 RUBY_ASSERT((constant_id & PM_SPECIAL_CONSTANT_FLAG) == 0);
4781
4782 ID local = pm_constant_id_lookup(scope_node, constant_id);
4783 local_table_for_iseq->ids[local_index] = local;
4784 pm_index_lookup_table_insert(index_lookup_table, constant_id, local_index);
4785}
4786
4790static void
4791pm_insert_local_special(pm_constant_id_t special_id, ID local_name, int local_index, pm_index_lookup_table_t *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq)
4792{
4793 local_table_for_iseq->ids[local_index] = local_name;
4794 pm_index_lookup_table_insert(index_lookup_table, special_id, local_index);
4795}
4796
4803static int
4804pm_compile_destructured_param_locals(const pm_multi_target_node_t *node, pm_index_lookup_table_t *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node, int local_index)
4805{
4806 for (size_t index = 0; index < node->lefts.size; index++) {
4807 const pm_node_t *left = node->lefts.nodes[index];
4808
4809 if (PM_NODE_TYPE_P(left, PM_REQUIRED_PARAMETER_NODE)) {
4810 if (!PM_NODE_FLAG_P(left, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4811 pm_insert_local_index(((const pm_required_parameter_node_t *) left)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4812 local_index++;
4813 }
4814 }
4815 else {
4816 RUBY_ASSERT(PM_NODE_TYPE_P(left, PM_MULTI_TARGET_NODE));
4817 local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) left, index_lookup_table, local_table_for_iseq, scope_node, local_index);
4818 }
4819 }
4820
4821 if (node->rest != NULL && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE)) {
4822 const pm_splat_node_t *rest = (const pm_splat_node_t *) node->rest;
4823
4824 if (rest->expression != NULL) {
4825 RUBY_ASSERT(PM_NODE_TYPE_P(rest->expression, PM_REQUIRED_PARAMETER_NODE));
4826
4827 if (!PM_NODE_FLAG_P(rest->expression, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4828 pm_insert_local_index(((const pm_required_parameter_node_t *) rest->expression)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4829 local_index++;
4830 }
4831 }
4832 }
4833
4834 for (size_t index = 0; index < node->rights.size; index++) {
4835 const pm_node_t *right = node->rights.nodes[index];
4836
4837 if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) {
4838 if (!PM_NODE_FLAG_P(right, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4839 pm_insert_local_index(((const pm_required_parameter_node_t *) right)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4840 local_index++;
4841 }
4842 }
4843 else {
4844 RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE));
4845 local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) right, index_lookup_table, local_table_for_iseq, scope_node, local_index);
4846 }
4847 }
4848
4849 return local_index;
4850}
4851
4856static inline void
4857pm_compile_destructured_param_write(rb_iseq_t *iseq, const pm_required_parameter_node_t *node, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
4858{
4859 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
4860 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, node->name, 0);
4861 PUSH_SETLOCAL(ret, location, index.index, index.level);
4862}
4863
4872static void
4873pm_compile_destructured_param_writes(rb_iseq_t *iseq, const pm_multi_target_node_t *node, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
4874{
4875 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
4876 bool has_rest = (node->rest && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE) && (((const pm_splat_node_t *) node->rest)->expression) != NULL);
4877 bool has_rights = node->rights.size > 0;
4878
4879 int flag = (has_rest || has_rights) ? 1 : 0;
4880 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->lefts.size), INT2FIX(flag));
4881
4882 for (size_t index = 0; index < node->lefts.size; index++) {
4883 const pm_node_t *left = node->lefts.nodes[index];
4884
4885 if (PM_NODE_TYPE_P(left, PM_REQUIRED_PARAMETER_NODE)) {
4886 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) left, ret, scope_node);
4887 }
4888 else {
4889 RUBY_ASSERT(PM_NODE_TYPE_P(left, PM_MULTI_TARGET_NODE));
4890 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) left, ret, scope_node);
4891 }
4892 }
4893
4894 if (has_rest) {
4895 if (has_rights) {
4896 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->rights.size), INT2FIX(3));
4897 }
4898
4899 const pm_node_t *rest = ((const pm_splat_node_t *) node->rest)->expression;
4900 RUBY_ASSERT(PM_NODE_TYPE_P(rest, PM_REQUIRED_PARAMETER_NODE));
4901
4902 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) rest, ret, scope_node);
4903 }
4904
4905 if (has_rights) {
4906 if (!has_rest) {
4907 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->rights.size), INT2FIX(2));
4908 }
4909
4910 for (size_t index = 0; index < node->rights.size; index++) {
4911 const pm_node_t *right = node->rights.nodes[index];
4912
4913 if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) {
4914 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) right, ret, scope_node);
4915 }
4916 else {
4917 RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE));
4918 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) right, ret, scope_node);
4919 }
4920 }
4921 }
4922}
4923
4929 // The pointer to the topn instruction that will need to be modified after
4930 // we know the total stack size of all of the targets.
4931 INSN *topn;
4932
4933 // The index of the stack from the base of the entire multi target at which
4934 // the parent expression is located.
4935 size_t stack_index;
4936
4937 // The number of slots in the stack that this node occupies.
4938 size_t stack_size;
4939
4940 // The position of the node in the list of targets.
4941 size_t position;
4942
4943 // A pointer to the next node in this linked list.
4944 struct pm_multi_target_state_node *next;
4946
4954typedef struct {
4955 // The total number of slots in the stack that this multi target occupies.
4956 size_t stack_size;
4957
4958 // The position of the current node being compiled. This is forwarded to
4959 // nodes when they are allocated.
4960 size_t position;
4961
4962 // A pointer to the head of this linked list.
4964
4965 // A pointer to the tail of this linked list.
4968
4972static void
4973pm_multi_target_state_push(pm_multi_target_state_t *state, INSN *topn, size_t stack_size)
4974{
4976 node->topn = topn;
4977 node->stack_index = state->stack_size + 1;
4978 node->stack_size = stack_size;
4979 node->position = state->position;
4980 node->next = NULL;
4981
4982 if (state->head == NULL) {
4983 state->head = node;
4984 state->tail = node;
4985 }
4986 else {
4987 state->tail->next = node;
4988 state->tail = node;
4989 }
4990
4991 state->stack_size += stack_size;
4992}
4993
4999static void
5000pm_multi_target_state_update(pm_multi_target_state_t *state)
5001{
5002 // If nothing was ever pushed onto the stack, then we don't need to do any
5003 // kind of updates.
5004 if (state->stack_size == 0) return;
5005
5006 pm_multi_target_state_node_t *current = state->head;
5008
5009 while (current != NULL) {
5010 VALUE offset = INT2FIX(state->stack_size - current->stack_index + current->position);
5011 current->topn->operands[0] = offset;
5012
5013 // stack_size will be > 1 in the case that we compiled an index target
5014 // and it had arguments. In this case, we use multiple topn instructions
5015 // to grab up all of the arguments as well, so those offsets need to be
5016 // updated as well.
5017 if (current->stack_size > 1) {
5018 INSN *insn = current->topn;
5019
5020 for (size_t index = 1; index < current->stack_size; index += 1) {
5021 LINK_ELEMENT *element = get_next_insn(insn);
5022 RUBY_ASSERT(IS_INSN(element));
5023
5024 insn = (INSN *) element;
5025 RUBY_ASSERT(insn->insn_id == BIN(topn));
5026
5027 insn->operands[0] = offset;
5028 }
5029 }
5030
5031 previous = current;
5032 current = current->next;
5033
5034 SIZED_FREE(previous);
5035 }
5036}
5037
5038static void
5039pm_compile_multi_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const parents, LINK_ANCHOR *const writes, LINK_ANCHOR *const cleanup, pm_scope_node_t *scope_node, pm_multi_target_state_t *state);
5040
5069static void
5070pm_compile_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const parents, LINK_ANCHOR *const writes, LINK_ANCHOR *const cleanup, pm_scope_node_t *scope_node, pm_multi_target_state_t *state)
5071{
5072 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5073
5074 switch (PM_NODE_TYPE(node)) {
5075 case PM_LOCAL_VARIABLE_TARGET_NODE: {
5076 // Local variable targets have no parent expression, so they only need
5077 // to compile the write.
5078 //
5079 // for i in []; end
5080 //
5082 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
5083
5084 PUSH_SETLOCAL(writes, location, index.index, index.level);
5085 break;
5086 }
5087 case PM_CLASS_VARIABLE_TARGET_NODE: {
5088 // Class variable targets have no parent expression, so they only need
5089 // to compile the write.
5090 //
5091 // for @@i in []; end
5092 //
5094 ID name = pm_constant_id_lookup(scope_node, cast->name);
5095
5096 VALUE operand = ID2SYM(name);
5097 PUSH_INSN2(writes, location, setclassvariable, operand, get_cvar_ic_value(iseq, name));
5098 break;
5099 }
5100 case PM_CONSTANT_TARGET_NODE: {
5101 // Constant targets have no parent expression, so they only need to
5102 // compile the write.
5103 //
5104 // for I in []; end
5105 //
5106 const pm_constant_target_node_t *cast = (const pm_constant_target_node_t *) node;
5107 ID name = pm_constant_id_lookup(scope_node, cast->name);
5108
5109 VALUE operand = ID2SYM(name);
5110 PUSH_INSN1(writes, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5111 PUSH_INSN1(writes, location, setconstant, operand);
5112 break;
5113 }
5114 case PM_GLOBAL_VARIABLE_TARGET_NODE: {
5115 // Global variable targets have no parent expression, so they only need
5116 // to compile the write.
5117 //
5118 // for $i in []; end
5119 //
5121 ID name = pm_constant_id_lookup(scope_node, cast->name);
5122
5123 VALUE operand = ID2SYM(name);
5124 PUSH_INSN1(writes, location, setglobal, operand);
5125 break;
5126 }
5127 case PM_INSTANCE_VARIABLE_TARGET_NODE: {
5128 // Instance variable targets have no parent expression, so they only
5129 // need to compile the write.
5130 //
5131 // for @i in []; end
5132 //
5134 ID name = pm_constant_id_lookup(scope_node, cast->name);
5135
5136 VALUE operand = ID2SYM(name);
5137 PUSH_INSN2(writes, location, setinstancevariable, operand, get_ivar_ic_value(iseq, name));
5138 break;
5139 }
5140 case PM_CONSTANT_PATH_TARGET_NODE: {
5141 // Constant path targets have a parent expression that is the object
5142 // that owns the constant. This needs to be compiled first into the
5143 // parents sequence. If no parent is found, then it represents using the
5144 // unary :: operator to indicate a top-level constant. In that case we
5145 // need to push Object onto the stack.
5146 //
5147 // for I::J in []; end
5148 //
5150 ID name = pm_constant_id_lookup(scope_node, cast->name);
5151
5152 if (cast->parent != NULL) {
5153 pm_compile_node(iseq, cast->parent, parents, false, scope_node);
5154 }
5155 else {
5156 PUSH_INSN1(parents, location, putobject, rb_cObject);
5157 }
5158
5159 if (state == NULL) {
5160 PUSH_INSN(writes, location, swap);
5161 }
5162 else {
5163 PUSH_INSN1(writes, location, topn, INT2FIX(1));
5164 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), 1);
5165 }
5166
5167 VALUE operand = ID2SYM(name);
5168 PUSH_INSN1(writes, location, setconstant, operand);
5169
5170 if (state != NULL) {
5171 PUSH_INSN(cleanup, location, pop);
5172 }
5173
5174 break;
5175 }
5176 case PM_CALL_TARGET_NODE: {
5177 // Call targets have a parent expression that is the receiver of the
5178 // method being called. This needs to be compiled first into the parents
5179 // sequence. These nodes cannot have arguments, so the method call is
5180 // compiled with a single argument which represents the value being
5181 // written.
5182 //
5183 // for i.j in []; end
5184 //
5185 const pm_call_target_node_t *cast = (const pm_call_target_node_t *) node;
5186 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
5187
5188 pm_compile_node(iseq, cast->receiver, parents, false, scope_node);
5189
5190 LABEL *safe_label = NULL;
5191 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
5192 safe_label = NEW_LABEL(location.line);
5193 PUSH_INSN(parents, location, dup);
5194 PUSH_INSNL(parents, location, branchnil, safe_label);
5195 }
5196
5197 if (state != NULL) {
5198 PUSH_INSN1(writes, location, topn, INT2FIX(1));
5199 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), 1);
5200 PUSH_INSN(writes, location, swap);
5201 }
5202
5203 int flags = VM_CALL_ARGS_SIMPLE;
5204 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) flags |= VM_CALL_FCALL;
5205
5206 PUSH_SEND_WITH_FLAG(writes, location, method_id, INT2FIX(1), INT2FIX(flags));
5207 if (safe_label != NULL && state == NULL) PUSH_LABEL(writes, safe_label);
5208 PUSH_INSN(writes, location, pop);
5209 if (safe_label != NULL && state != NULL) PUSH_LABEL(writes, safe_label);
5210
5211 if (state != NULL) {
5212 PUSH_INSN(cleanup, location, pop);
5213 }
5214
5215 break;
5216 }
5217 case PM_INDEX_TARGET_NODE: {
5218 // Index targets have a parent expression that is the receiver of the
5219 // method being called and any additional arguments that are being
5220 // passed along with the value being written. The receiver and arguments
5221 // both need to be on the stack. Note that this is even more complicated
5222 // by the fact that these nodes can hold a block using the unary &
5223 // operator.
5224 //
5225 // for i[:j] in []; end
5226 //
5227 const pm_index_target_node_t *cast = (const pm_index_target_node_t *) node;
5228
5229 pm_compile_node(iseq, cast->receiver, parents, false, scope_node);
5230
5231 int flags = 0;
5232 struct rb_callinfo_kwarg *kwargs = NULL;
5233 int argc = pm_setup_args(cast->arguments, (const pm_node_t *) cast->block, &flags, &kwargs, iseq, parents, scope_node, &location);
5234
5235 if (state != NULL) {
5236 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5237 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), argc + 1);
5238
5239 if (argc == 0) {
5240 PUSH_INSN(writes, location, swap);
5241 }
5242 else {
5243 for (int index = 0; index < argc; index++) {
5244 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5245 }
5246 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5247 }
5248 }
5249
5250 // The argc that we're going to pass to the send instruction is the
5251 // number of arguments + 1 for the value being written. If there's a
5252 // splat, then we need to insert newarray and concatarray instructions
5253 // after the arguments have been written.
5254 int ci_argc = argc + 1;
5255 if (flags & VM_CALL_ARGS_SPLAT) {
5256 ci_argc--;
5257 PUSH_INSN1(writes, location, newarray, INT2FIX(1));
5258 PUSH_INSN(writes, location, concatarray);
5259 }
5260
5261 PUSH_SEND_R(writes, location, idASET, INT2NUM(ci_argc), NULL, INT2FIX(flags), kwargs);
5262 PUSH_INSN(writes, location, pop);
5263
5264 if (state != NULL) {
5265 if (argc != 0) {
5266 PUSH_INSN(writes, location, pop);
5267 }
5268
5269 for (int index = 0; index < argc + 1; index++) {
5270 PUSH_INSN(cleanup, location, pop);
5271 }
5272 }
5273
5274 break;
5275 }
5276 case PM_MULTI_TARGET_NODE: {
5277 // Multi target nodes represent a set of writes to multiple variables.
5278 // The parent expressions are the combined set of the parent expressions
5279 // of its inner target nodes.
5280 //
5281 // for i, j in []; end
5282 //
5283 size_t before_position;
5284 if (state != NULL) {
5285 before_position = state->position;
5286 state->position--;
5287 }
5288
5289 pm_compile_multi_target_node(iseq, node, parents, writes, cleanup, scope_node, state);
5290 if (state != NULL) state->position = before_position;
5291
5292 break;
5293 }
5294 case PM_SPLAT_NODE: {
5295 // Splat nodes capture all values into an array. They can be used
5296 // as targets in assignments or for loops.
5297 //
5298 // for *x in []; end
5299 //
5300 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
5301
5302 if (cast->expression != NULL) {
5303 pm_compile_target_node(iseq, cast->expression, parents, writes, cleanup, scope_node, state);
5304 }
5305
5306 break;
5307 }
5308 default:
5309 rb_bug("Unexpected node type: %s", pm_node_type(PM_NODE_TYPE(node)));
5310 break;
5311 }
5312}
5313
5319static void
5320pm_compile_multi_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const parents, LINK_ANCHOR *const writes, LINK_ANCHOR *const cleanup, pm_scope_node_t *scope_node, pm_multi_target_state_t *state)
5321{
5322 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5323 const pm_node_list_t *lefts;
5324 const pm_node_t *rest;
5325 const pm_node_list_t *rights;
5326
5327 switch (PM_NODE_TYPE(node)) {
5328 case PM_MULTI_TARGET_NODE: {
5329 const pm_multi_target_node_t *cast = (const pm_multi_target_node_t *) node;
5330 lefts = &cast->lefts;
5331 rest = cast->rest;
5332 rights = &cast->rights;
5333 break;
5334 }
5335 case PM_MULTI_WRITE_NODE: {
5336 const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node;
5337 lefts = &cast->lefts;
5338 rest = cast->rest;
5339 rights = &cast->rights;
5340 break;
5341 }
5342 default:
5343 rb_bug("Unsupported node %s", pm_node_type(PM_NODE_TYPE(node)));
5344 break;
5345 }
5346
5347 bool has_rest = (rest != NULL) && PM_NODE_TYPE_P(rest, PM_SPLAT_NODE) && ((const pm_splat_node_t *) rest)->expression != NULL;
5348 bool has_posts = rights->size > 0;
5349
5350 // The first instruction in the writes sequence is going to spread the
5351 // top value of the stack onto the number of values that we're going to
5352 // write.
5353 PUSH_INSN2(writes, location, expandarray, INT2FIX(lefts->size), INT2FIX((has_rest || has_posts) ? 1 : 0));
5354
5355 // We need to keep track of some additional state information as we're
5356 // going through the targets because we will need to revisit them once
5357 // we know how many values are being pushed onto the stack.
5358 pm_multi_target_state_t target_state = { 0 };
5359 if (state == NULL) state = &target_state;
5360
5361 size_t base_position = state->position;
5362 size_t splat_position = (has_rest || has_posts) ? 1 : 0;
5363
5364 // Next, we'll iterate through all of the leading targets.
5365 for (size_t index = 0; index < lefts->size; index++) {
5366 const pm_node_t *target = lefts->nodes[index];
5367 state->position = lefts->size - index + splat_position + base_position;
5368 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5369 }
5370
5371 // Next, we'll compile the rest target if there is one.
5372 if (has_rest) {
5373 const pm_node_t *target = ((const pm_splat_node_t *) rest)->expression;
5374 state->position = 1 + rights->size + base_position;
5375
5376 if (has_posts) {
5377 PUSH_INSN2(writes, location, expandarray, INT2FIX(rights->size), INT2FIX(3));
5378 }
5379
5380 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5381 }
5382
5383 // Finally, we'll compile the trailing targets.
5384 if (has_posts) {
5385 if (!has_rest && rest != NULL) {
5386 PUSH_INSN2(writes, location, expandarray, INT2FIX(rights->size), INT2FIX(2));
5387 }
5388
5389 for (size_t index = 0; index < rights->size; index++) {
5390 const pm_node_t *target = rights->nodes[index];
5391 state->position = rights->size - index + base_position;
5392 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5393 }
5394 }
5395}
5396
5402static void
5403pm_compile_for_node_index(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
5404{
5405 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5406
5407 switch (PM_NODE_TYPE(node)) {
5408 case PM_LOCAL_VARIABLE_TARGET_NODE: {
5409 // For local variables, all we have to do is retrieve the value and then
5410 // compile the index node.
5411 PUSH_GETLOCAL(ret, location, 1, 0);
5412 pm_compile_target_node(iseq, node, ret, ret, ret, scope_node, NULL);
5413 break;
5414 }
5415 case PM_CLASS_VARIABLE_TARGET_NODE:
5416 case PM_CONSTANT_TARGET_NODE:
5417 case PM_GLOBAL_VARIABLE_TARGET_NODE:
5418 case PM_INSTANCE_VARIABLE_TARGET_NODE:
5419 case PM_CONSTANT_PATH_TARGET_NODE:
5420 case PM_CALL_TARGET_NODE:
5421 case PM_INDEX_TARGET_NODE: {
5422 // For other targets, we need to potentially compile the parent or
5423 // owning expression of this target, then retrieve the value, expand it,
5424 // and then compile the necessary writes.
5425 DECL_ANCHOR(writes);
5426 DECL_ANCHOR(cleanup);
5427
5428 pm_multi_target_state_t state = { 0 };
5429 state.position = 1;
5430 pm_compile_target_node(iseq, node, ret, writes, cleanup, scope_node, &state);
5431
5432 PUSH_GETLOCAL(ret, location, 1, 0);
5433 PUSH_INSN2(ret, location, expandarray, INT2FIX(1), INT2FIX(0));
5434
5435 PUSH_SEQ(ret, writes);
5436 PUSH_SEQ(ret, cleanup);
5437
5438 pm_multi_target_state_update(&state);
5439 break;
5440 }
5441 case PM_SPLAT_NODE:
5442 case PM_MULTI_TARGET_NODE: {
5443 DECL_ANCHOR(writes);
5444 DECL_ANCHOR(cleanup);
5445
5446 pm_compile_target_node(iseq, node, ret, writes, cleanup, scope_node, NULL);
5447
5448 LABEL *not_single = NEW_LABEL(location.line);
5449 LABEL *not_ary = NEW_LABEL(location.line);
5450
5451 // When there are multiple targets, we'll do a bunch of work to convert
5452 // the value into an array before we expand it. Effectively we're trying
5453 // to accomplish:
5454 //
5455 // (args.length == 1 && Array.try_convert(args[0])) || args
5456 //
5457 PUSH_GETLOCAL(ret, location, 1, 0);
5458 PUSH_INSN(ret, location, dup);
5459 PUSH_CALL(ret, location, idLength, INT2FIX(0));
5460 PUSH_INSN1(ret, location, putobject, INT2FIX(1));
5461 PUSH_CALL(ret, location, idEq, INT2FIX(1));
5462 PUSH_INSNL(ret, location, branchunless, not_single);
5463 PUSH_INSN(ret, location, dup);
5464 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
5465 PUSH_CALL(ret, location, idAREF, INT2FIX(1));
5466 PUSH_INSN1(ret, location, putobject, rb_cArray);
5467 PUSH_INSN(ret, location, swap);
5468 PUSH_CALL(ret, location, rb_intern("try_convert"), INT2FIX(1));
5469 PUSH_INSN(ret, location, dup);
5470 PUSH_INSNL(ret, location, branchunless, not_ary);
5471 PUSH_INSN(ret, location, swap);
5472
5473 PUSH_LABEL(ret, not_ary);
5474 PUSH_INSN(ret, location, pop);
5475
5476 PUSH_LABEL(ret, not_single);
5477
5478 if (PM_NODE_TYPE_P(node, PM_SPLAT_NODE)) {
5479 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
5480 PUSH_INSN2(ret, location, expandarray, INT2FIX(0), INT2FIX(cast->expression == NULL ? 0 : 1));
5481 }
5482
5483 PUSH_SEQ(ret, writes);
5484 PUSH_SEQ(ret, cleanup);
5485 break;
5486 }
5487 default:
5488 rb_bug("Unexpected node type for index in for node: %s", pm_node_type(PM_NODE_TYPE(node)));
5489 break;
5490 }
5491}
5492
5493static void
5494pm_compile_rescue(rb_iseq_t *iseq, const pm_begin_node_t *cast, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5495{
5496 LABEL *lstart = NEW_LABEL(node_location->line);
5497 LABEL *lend = NEW_LABEL(node_location->line);
5498 LABEL *lcont = NEW_LABEL(node_location->line);
5499
5500 pm_scope_node_t rescue_scope_node;
5501 pm_scope_node_init((const pm_node_t *) cast->rescue_clause, &rescue_scope_node, scope_node);
5502
5503 rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(
5504 &rescue_scope_node,
5505 rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label),
5506 ISEQ_TYPE_RESCUE,
5507 pm_node_line_number_cached((const pm_node_t *) cast->rescue_clause, scope_node)
5508 );
5509
5510 pm_scope_node_destroy(&rescue_scope_node);
5511
5512 lstart->rescued = LABEL_RESCUE_BEG;
5513 lend->rescued = LABEL_RESCUE_END;
5514 PUSH_LABEL(ret, lstart);
5515
5516 bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
5517 ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
5518
5519 if (cast->statements != NULL) {
5520 PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->statements);
5521 }
5522 else {
5523 const pm_node_location_t location = PM_NODE_START_LOCATION(cast->rescue_clause);
5524 PUSH_INSN(ret, location, putnil);
5525 }
5526
5527 ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
5528 PUSH_LABEL(ret, lend);
5529
5530 if (cast->else_clause != NULL) {
5531 if (!popped) PUSH_INSN(ret, *node_location, pop);
5532 PM_COMPILE((const pm_node_t *) cast->else_clause);
5533 }
5534
5535 PUSH_INSN(ret, *node_location, nop);
5536 PUSH_LABEL(ret, lcont);
5537
5538 if (popped) PUSH_INSN(ret, *node_location, pop);
5539 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
5540 PUSH_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
5541}
5542
5543static void
5544pm_compile_ensure(rb_iseq_t *iseq, const pm_begin_node_t *cast, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5545{
5546 const pm_statements_node_t *statements = cast->ensure_clause->statements;
5547
5548 pm_node_location_t location;
5549 if (statements != NULL) {
5550 location = PM_NODE_START_LOCATION(statements);
5551 }
5552 else {
5553 location = *node_location;
5554 }
5555
5556 LABEL *lstart = NEW_LABEL(location.line);
5557 LABEL *lend = NEW_LABEL(location.line);
5558 LABEL *lcont = NEW_LABEL(location.line);
5559
5560 struct ensure_range er;
5562 struct ensure_range *erange;
5563
5564 DECL_ANCHOR(ensr);
5565 if (statements != NULL) {
5566 pm_compile_node(iseq, (const pm_node_t *) statements, ensr, true, scope_node);
5567 }
5568
5569 LINK_ELEMENT *last = ensr->last;
5570 bool last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave);
5571
5572 er.begin = lstart;
5573 er.end = lend;
5574 er.next = 0;
5575 push_ensure_entry(iseq, &enl, &er, (void *) cast->ensure_clause);
5576
5577 PUSH_LABEL(ret, lstart);
5578 if (cast->rescue_clause != NULL) {
5579 pm_compile_rescue(iseq, cast, node_location, ret, popped | last_leave, scope_node);
5580 }
5581 else if (cast->statements != NULL) {
5582 pm_compile_node(iseq, (const pm_node_t *) cast->statements, ret, popped | last_leave, scope_node);
5583 }
5584 else if (!(popped | last_leave)) {
5585 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
5586 }
5587
5588 PUSH_LABEL(ret, lend);
5589 PUSH_SEQ(ret, ensr);
5590 if (!popped && last_leave) PUSH_INSN(ret, *node_location, putnil);
5591 PUSH_LABEL(ret, lcont);
5592 if (last_leave) PUSH_INSN(ret, *node_location, pop);
5593
5594 pm_scope_node_t next_scope_node;
5595 pm_scope_node_init((const pm_node_t *) cast->ensure_clause, &next_scope_node, scope_node);
5596
5597 rb_iseq_t *child_iseq = NEW_CHILD_ISEQ(
5598 &next_scope_node,
5599 rb_str_concat(rb_str_new2("ensure in "), ISEQ_BODY(iseq)->location.label),
5600 ISEQ_TYPE_ENSURE,
5601 location.line
5602 );
5603
5604 pm_scope_node_destroy(&next_scope_node);
5605
5606 erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
5607 if (lstart->link.next != &lend->link) {
5608 while (erange) {
5609 PUSH_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end, child_iseq, lcont);
5610 erange = erange->next;
5611 }
5612 }
5613 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
5614}
5615
5620static inline bool
5621pm_opt_str_freeze_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
5622{
5623 return (
5624 !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) &&
5625 node->receiver != NULL &&
5626 PM_NODE_TYPE_P(node->receiver, PM_STRING_NODE) &&
5627 node->arguments == NULL &&
5628 node->block == NULL &&
5629 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
5630 );
5631}
5632
5637static void
5638pm_compile_constant_read(rb_iseq_t *iseq, VALUE name, const pm_location_t *name_loc, uint32_t node_id, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
5639{
5640 const pm_node_location_t location = PM_LOCATION_START_LOCATION(name_loc, node_id);
5641
5642 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
5643 ISEQ_BODY(iseq)->ic_size++;
5644 VALUE segments = rb_ary_new_from_args(1, name);
5645 RB_OBJ_SET_SHAREABLE(segments);
5646 PUSH_INSN1(ret, location, opt_getconstant_path, segments);
5647 }
5648 else {
5649 PUSH_INSN(ret, location, putnil);
5650 PUSH_INSN1(ret, location, putobject, Qtrue);
5651 PUSH_INSN1(ret, location, getconstant, name);
5652 }
5653}
5654
5659static VALUE
5660pm_constant_path_parts(const pm_node_t *node, const pm_scope_node_t *scope_node)
5661{
5662 VALUE parts = rb_ary_new();
5663
5664 while (true) {
5665 switch (PM_NODE_TYPE(node)) {
5666 case PM_CONSTANT_READ_NODE: {
5667 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
5668 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5669
5670 rb_ary_unshift(parts, name);
5671 return parts;
5672 }
5673 case PM_CONSTANT_PATH_NODE: {
5674 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
5675 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5676
5677 rb_ary_unshift(parts, name);
5678 if (cast->parent == NULL) {
5679 rb_ary_unshift(parts, ID2SYM(idNULL));
5680 return parts;
5681 }
5682
5683 node = cast->parent;
5684 break;
5685 }
5686 default:
5687 return Qnil;
5688 }
5689 }
5690}
5691
5697static void
5698pm_compile_constant_path(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const prefix, LINK_ANCHOR *const body, bool popped, pm_scope_node_t *scope_node)
5699{
5700 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5701
5702 switch (PM_NODE_TYPE(node)) {
5703 case PM_CONSTANT_READ_NODE: {
5704 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
5705 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5706
5707 PUSH_INSN1(body, location, putobject, Qtrue);
5708 PUSH_INSN1(body, location, getconstant, name);
5709 break;
5710 }
5711 case PM_CONSTANT_PATH_NODE: {
5712 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
5713 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5714
5715 if (cast->parent == NULL) {
5716 PUSH_INSN(body, location, pop);
5717 PUSH_INSN1(body, location, putobject, rb_cObject);
5718 PUSH_INSN1(body, location, putobject, Qtrue);
5719 PUSH_INSN1(body, location, getconstant, name);
5720 }
5721 else {
5722 pm_compile_constant_path(iseq, cast->parent, prefix, body, false, scope_node);
5723 PUSH_INSN1(body, location, putobject, Qfalse);
5724 PUSH_INSN1(body, location, getconstant, name);
5725 }
5726 break;
5727 }
5728 default:
5729 PM_COMPILE_INTO_ANCHOR(prefix, node);
5730 break;
5731 }
5732}
5733
5737static VALUE
5738pm_compile_shareable_constant_literal(rb_iseq_t *iseq, const pm_node_t *node, pm_scope_node_t *scope_node)
5739{
5740 switch (PM_NODE_TYPE(node)) {
5741 case PM_TRUE_NODE:
5742 case PM_FALSE_NODE:
5743 case PM_NIL_NODE:
5744 case PM_SYMBOL_NODE:
5745 case PM_REGULAR_EXPRESSION_NODE:
5746 case PM_SOURCE_LINE_NODE:
5747 case PM_INTEGER_NODE:
5748 case PM_FLOAT_NODE:
5749 case PM_RATIONAL_NODE:
5750 case PM_IMAGINARY_NODE:
5751 case PM_SOURCE_ENCODING_NODE:
5752 return pm_static_literal_value(iseq, node, scope_node);
5753 case PM_STRING_NODE:
5754 return parse_static_literal_string(iseq, scope_node, node, &((const pm_string_node_t *) node)->unescaped);
5755 case PM_SOURCE_FILE_NODE:
5756 return pm_source_file_value((const pm_source_file_node_t *) node, scope_node);
5757 case PM_ARRAY_NODE: {
5758 const pm_array_node_t *cast = (const pm_array_node_t *) node;
5759 VALUE result = rb_ary_new_capa(cast->elements.size);
5760
5761 for (size_t index = 0; index < cast->elements.size; index++) {
5762 VALUE element = pm_compile_shareable_constant_literal(iseq, cast->elements.nodes[index], scope_node);
5763 if (element == Qundef) return Qundef;
5764
5765 rb_ary_push(result, element);
5766 }
5767
5768 return rb_ractor_make_shareable(result);
5769 }
5770 case PM_HASH_NODE: {
5771 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
5772 VALUE result = rb_hash_alloc_fixed_size(rb_cHash, cast->elements.size);
5773
5774 for (size_t index = 0; index < cast->elements.size; index++) {
5775 const pm_node_t *element = cast->elements.nodes[index];
5776 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) return Qundef;
5777
5778 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
5779
5780 VALUE key = pm_compile_shareable_constant_literal(iseq, assoc->key, scope_node);
5781 if (key == Qundef) return Qundef;
5782
5783 VALUE value = pm_compile_shareable_constant_literal(iseq, assoc->value, scope_node);
5784 if (value == Qundef) return Qundef;
5785
5786 rb_hash_aset(result, key, value);
5787 }
5788
5789 return rb_ractor_make_shareable(result);
5790 }
5791 default:
5792 return Qundef;
5793 }
5794}
5795
5800static void
5801pm_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)
5802{
5803 VALUE literal = pm_compile_shareable_constant_literal(iseq, node, scope_node);
5804 if (literal != Qundef) {
5805 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5806 PUSH_INSN1(ret, location, putobject, literal);
5807 return;
5808 }
5809
5810 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
5811 switch (PM_NODE_TYPE(node)) {
5812 case PM_ARRAY_NODE: {
5813 const pm_array_node_t *cast = (const pm_array_node_t *) node;
5814
5815 if (top) {
5816 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5817 }
5818
5819 for (size_t index = 0; index < cast->elements.size; index++) {
5820 pm_compile_shareable_constant_value(iseq, cast->elements.nodes[index], shareability, path, ret, scope_node, false);
5821 }
5822
5823 PUSH_INSN1(ret, location, newarray, INT2FIX(cast->elements.size));
5824
5825 if (top) {
5826 ID method_id = (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) ? rb_intern("make_shareable_copy") : rb_intern("make_shareable");
5827 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5828 }
5829
5830 return;
5831 }
5832 case PM_HASH_NODE: {
5833 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
5834
5835 if (top) {
5836 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5837 }
5838
5839 pm_compile_hash_elements(iseq, (const pm_node_t *) cast, &cast->elements, shareability, path, false, ret, scope_node);
5840
5841 if (top) {
5842 ID method_id = (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) ? rb_intern("make_shareable_copy") : rb_intern("make_shareable");
5843 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5844 }
5845
5846 return;
5847 }
5848 default: {
5849 DECL_ANCHOR(value_seq);
5850
5851 pm_compile_node(iseq, node, value_seq, false, scope_node);
5852 if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) {
5853 PUSH_SEND_WITH_FLAG(value_seq, location, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE));
5854 }
5855
5856 if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL) {
5857 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5858 PUSH_SEQ(ret, value_seq);
5859 if (!RB_OBJ_SHAREABLE_P(path)) {
5861 }
5862 PUSH_INSN1(ret, location, putobject, path);
5863 PUSH_SEND_WITH_FLAG(ret, location, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE));
5864 }
5865 else if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) {
5866 if (top) PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5867 PUSH_SEQ(ret, value_seq);
5868 if (top) PUSH_SEND_WITH_FLAG(ret, location, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5869 }
5870 else if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING) {
5871 if (top) PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5872 PUSH_SEQ(ret, value_seq);
5873 if (top) PUSH_SEND_WITH_FLAG(ret, location, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5874 }
5875
5876 break;
5877 }
5878 }
5879}
5880
5885static void
5886pm_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)
5887{
5888 const pm_node_location_t location = *node_location;
5889 ID name_id = pm_constant_id_lookup(scope_node, node->name);
5890
5891 if (shareability != 0) {
5892 pm_compile_shareable_constant_value(iseq, node->value, shareability, rb_id2str(name_id), ret, scope_node, true);
5893 }
5894 else {
5895 PM_COMPILE_NOT_POPPED(node->value);
5896 }
5897
5898 if (!popped) PUSH_INSN(ret, location, dup);
5899 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5900
5901 VALUE operand = ID2SYM(name_id);
5902 PUSH_INSN1(ret, location, setconstant, operand);
5903}
5904
5909static void
5910pm_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)
5911{
5912 const pm_node_location_t location = *node_location;
5913
5914 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5915 LABEL *end_label = NEW_LABEL(location.line);
5916
5917 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5918 if (!popped) PUSH_INSN(ret, location, dup);
5919
5920 PUSH_INSNL(ret, location, branchunless, end_label);
5921 if (!popped) PUSH_INSN(ret, location, pop);
5922
5923 if (shareability != 0) {
5924 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5925 }
5926 else {
5927 PM_COMPILE_NOT_POPPED(node->value);
5928 }
5929
5930 if (!popped) PUSH_INSN(ret, location, dup);
5931 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5932 PUSH_INSN1(ret, location, setconstant, name);
5933 PUSH_LABEL(ret, end_label);
5934}
5935
5940static void
5941pm_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)
5942{
5943 const pm_node_location_t location = *node_location;
5944 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5945
5946 LABEL *set_label = NEW_LABEL(location.line);
5947 LABEL *end_label = NEW_LABEL(location.line);
5948
5949 PUSH_INSN(ret, location, putnil);
5950 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), name, Qtrue);
5951 PUSH_INSNL(ret, location, branchunless, set_label);
5952
5953 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5954 if (!popped) PUSH_INSN(ret, location, dup);
5955
5956 PUSH_INSNL(ret, location, branchif, end_label);
5957 if (!popped) PUSH_INSN(ret, location, pop);
5958 PUSH_LABEL(ret, set_label);
5959
5960 if (shareability != 0) {
5961 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5962 }
5963 else {
5964 PM_COMPILE_NOT_POPPED(node->value);
5965 }
5966
5967 if (!popped) PUSH_INSN(ret, location, dup);
5968 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5969 PUSH_INSN1(ret, location, setconstant, name);
5970 PUSH_LABEL(ret, end_label);
5971}
5972
5977static void
5978pm_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)
5979{
5980 const pm_node_location_t location = *node_location;
5981
5982 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5983 ID method_id = pm_constant_id_lookup(scope_node, node->binary_operator);
5984
5985 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5986
5987 if (shareability != 0) {
5988 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5989 }
5990 else {
5991 PM_COMPILE_NOT_POPPED(node->value);
5992 }
5993
5994 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5995 if (!popped) PUSH_INSN(ret, location, dup);
5996
5997 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5998 PUSH_INSN1(ret, location, setconstant, name);
5999}
6000
6005static VALUE
6006pm_constant_path_path(const pm_constant_path_node_t *node, const pm_scope_node_t *scope_node)
6007{
6008 VALUE parts = rb_ary_new();
6009 rb_ary_push(parts, rb_id2str(pm_constant_id_lookup(scope_node, node->name)));
6010
6011 const pm_node_t *current = node->parent;
6012 while (current != NULL && PM_NODE_TYPE_P(current, PM_CONSTANT_PATH_NODE)) {
6013 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) current;
6014 rb_ary_unshift(parts, rb_id2str(pm_constant_id_lookup(scope_node, cast->name)));
6015 current = cast->parent;
6016 }
6017
6018 if (current == NULL) {
6019 rb_ary_unshift(parts, rb_id2str(idNULL));
6020 }
6021 else if (PM_NODE_TYPE_P(current, PM_CONSTANT_READ_NODE)) {
6022 rb_ary_unshift(parts, rb_id2str(pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) current)->name)));
6023 }
6024 else {
6025 rb_ary_unshift(parts, rb_str_new_cstr("..."));
6026 }
6027
6028 return rb_ary_join(parts, rb_str_new_cstr("::"));
6029}
6030
6035static void
6036pm_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)
6037{
6038 const pm_node_location_t location = *node_location;
6039 const pm_constant_path_node_t *target = node->target;
6040 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6041
6042 if (target->parent) {
6043 PM_COMPILE_NOT_POPPED((const pm_node_t *) target->parent);
6044 }
6045 else {
6046 PUSH_INSN1(ret, location, putobject, rb_cObject);
6047 }
6048
6049 if (shareability != 0) {
6050 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6051 }
6052 else {
6053 PM_COMPILE_NOT_POPPED(node->value);
6054 }
6055
6056 if (!popped) {
6057 PUSH_INSN(ret, location, swap);
6058 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6059 }
6060
6061 PUSH_INSN(ret, location, swap);
6062 PUSH_INSN1(ret, location, setconstant, name);
6063}
6064
6069static void
6070pm_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)
6071{
6072 const pm_node_location_t location = *node_location;
6073 const pm_constant_path_node_t *target = node->target;
6074
6075 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6076 LABEL *lfin = NEW_LABEL(location.line);
6077
6078 if (target->parent) {
6079 PM_COMPILE_NOT_POPPED(target->parent);
6080 }
6081 else {
6082 PUSH_INSN1(ret, location, putobject, rb_cObject);
6083 }
6084
6085 PUSH_INSN(ret, location, dup);
6086 PUSH_INSN1(ret, location, putobject, Qtrue);
6087 PUSH_INSN1(ret, location, getconstant, name);
6088
6089 if (!popped) PUSH_INSN(ret, location, dup);
6090 PUSH_INSNL(ret, location, branchunless, lfin);
6091
6092 if (!popped) PUSH_INSN(ret, location, pop);
6093
6094 if (shareability != 0) {
6095 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6096 }
6097 else {
6098 PM_COMPILE_NOT_POPPED(node->value);
6099 }
6100
6101 if (popped) {
6102 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6103 }
6104 else {
6105 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
6106 PUSH_INSN(ret, location, swap);
6107 }
6108
6109 PUSH_INSN1(ret, location, setconstant, name);
6110 PUSH_LABEL(ret, lfin);
6111
6112 if (!popped) PUSH_INSN(ret, location, swap);
6113 PUSH_INSN(ret, location, pop);
6114}
6115
6120static void
6121pm_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)
6122{
6123 const pm_node_location_t location = *node_location;
6124 const pm_constant_path_node_t *target = node->target;
6125
6126 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6127 LABEL *lassign = NEW_LABEL(location.line);
6128 LABEL *lfin = NEW_LABEL(location.line);
6129
6130 if (target->parent) {
6131 PM_COMPILE_NOT_POPPED(target->parent);
6132 }
6133 else {
6134 PUSH_INSN1(ret, location, putobject, rb_cObject);
6135 }
6136
6137 PUSH_INSN(ret, location, dup);
6138 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), name, Qtrue);
6139 PUSH_INSNL(ret, location, branchunless, lassign);
6140
6141 PUSH_INSN(ret, location, dup);
6142 PUSH_INSN1(ret, location, putobject, Qtrue);
6143 PUSH_INSN1(ret, location, getconstant, name);
6144
6145 if (!popped) PUSH_INSN(ret, location, dup);
6146 PUSH_INSNL(ret, location, branchif, lfin);
6147
6148 if (!popped) PUSH_INSN(ret, location, pop);
6149 PUSH_LABEL(ret, lassign);
6150
6151 if (shareability != 0) {
6152 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6153 }
6154 else {
6155 PM_COMPILE_NOT_POPPED(node->value);
6156 }
6157
6158 if (popped) {
6159 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6160 }
6161 else {
6162 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
6163 PUSH_INSN(ret, location, swap);
6164 }
6165
6166 PUSH_INSN1(ret, location, setconstant, name);
6167 PUSH_LABEL(ret, lfin);
6168
6169 if (!popped) PUSH_INSN(ret, location, swap);
6170 PUSH_INSN(ret, location, pop);
6171}
6172
6177static void
6178pm_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)
6179{
6180 const pm_node_location_t location = *node_location;
6181 const pm_constant_path_node_t *target = node->target;
6182
6183 ID method_id = pm_constant_id_lookup(scope_node, node->binary_operator);
6184 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6185
6186 if (target->parent) {
6187 PM_COMPILE_NOT_POPPED(target->parent);
6188 }
6189 else {
6190 PUSH_INSN1(ret, location, putobject, rb_cObject);
6191 }
6192
6193 PUSH_INSN(ret, location, dup);
6194 PUSH_INSN1(ret, location, putobject, Qtrue);
6195 PUSH_INSN1(ret, location, getconstant, name);
6196
6197 if (shareability != 0) {
6198 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6199 }
6200 else {
6201 PM_COMPILE_NOT_POPPED(node->value);
6202 }
6203
6204 PUSH_CALL(ret, location, method_id, INT2FIX(1));
6205 PUSH_INSN(ret, location, swap);
6206
6207 if (!popped) {
6208 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6209 PUSH_INSN(ret, location, swap);
6210 }
6211
6212 PUSH_INSN1(ret, location, setconstant, name);
6213}
6214
6221#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))
6222
6227static inline void
6228pm_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)
6229{
6230 const pm_node_location_t location = *node_location;
6231 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
6232
6233 pm_constant_id_list_t *locals = &scope_node->locals;
6234 pm_parameters_node_t *parameters_node = NULL;
6235 pm_node_list_t *keywords_list = NULL;
6236 pm_node_list_t *optionals_list = NULL;
6237 pm_node_list_t *posts_list = NULL;
6238 pm_node_list_t *requireds_list = NULL;
6239 pm_node_list_t *block_locals = NULL;
6240 bool trailing_comma = false;
6241
6242 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE) || PM_NODE_TYPE_P(scope_node->ast_node, PM_MODULE_NODE)) {
6243 PUSH_TRACE(ret, RUBY_EVENT_CLASS);
6244 }
6245
6246 if (scope_node->parameters != NULL) {
6247 switch (PM_NODE_TYPE(scope_node->parameters)) {
6248 case PM_BLOCK_PARAMETERS_NODE: {
6249 pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) scope_node->parameters;
6250 parameters_node = cast->parameters;
6251 block_locals = &cast->locals;
6252
6253 if (parameters_node) {
6254 if (parameters_node->rest && PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE)) {
6255 trailing_comma = true;
6256 }
6257 }
6258 break;
6259 }
6260 case PM_PARAMETERS_NODE: {
6261 parameters_node = (pm_parameters_node_t *) scope_node->parameters;
6262 break;
6263 }
6264 case PM_NUMBERED_PARAMETERS_NODE: {
6265 uint32_t maximum = ((const pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
6266 body->param.lead_num = maximum;
6267 body->param.flags.ambiguous_param0 = maximum == 1;
6268 break;
6269 }
6270 case PM_IT_PARAMETERS_NODE:
6271 body->param.lead_num = 1;
6272 body->param.flags.ambiguous_param0 = true;
6273 break;
6274 default:
6275 rb_bug("Unexpected node type for parameters: %s", pm_node_type(PM_NODE_TYPE(scope_node->parameters)));
6276 }
6277 }
6278
6279 struct rb_iseq_param_keyword *keyword = NULL;
6280
6281 if (parameters_node) {
6282 optionals_list = &parameters_node->optionals;
6283 requireds_list = &parameters_node->requireds;
6284 keywords_list = &parameters_node->keywords;
6285 posts_list = &parameters_node->posts;
6286 }
6287 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))) {
6288 body->param.opt_num = 0;
6289 }
6290 else {
6291 body->param.lead_num = 0;
6292 body->param.opt_num = 0;
6293 }
6294
6295 //********STEP 1**********
6296 // Goal: calculate the table size for the locals, accounting for
6297 // hidden variables and multi target nodes
6298 size_t locals_size = locals->size;
6299
6300 // Index lookup table buffer size is only the number of the locals.
6301 // We'll initialize it after computing table_size below.
6302 pm_index_lookup_table_t index_lookup_table = PM_INDEX_LOOKUP_TABLE_INIT;
6303
6304 int table_size = (int) locals_size;
6305
6306 // For nodes have a hidden iteration variable. We add that to the local
6307 // table size here.
6308 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) table_size++;
6309
6310 if (keywords_list && keywords_list->size) {
6311 table_size++;
6312 }
6313
6314 if (requireds_list) {
6315 for (size_t i = 0; i < requireds_list->size; i++) {
6316 // For each MultiTargetNode, we're going to have one
6317 // additional anonymous local not represented in the locals table
6318 // We want to account for this in our table size
6319 pm_node_t *required = requireds_list->nodes[i];
6320 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
6321 table_size++;
6322 }
6323 else if (PM_NODE_TYPE_P(required, PM_REQUIRED_PARAMETER_NODE)) {
6324 if (PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6325 table_size++;
6326 }
6327 }
6328 }
6329 }
6330
6331 // If we have the `it` implicit local variable, we need to account for
6332 // it in the local table size.
6333 if (scope_node->parameters != NULL && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
6334 table_size++;
6335 }
6336
6337 // Ensure there is enough room in the local table for any
6338 // parameters that have been repeated
6339 // ex: def underscore_parameters(_, _ = 1, _ = 2); _; end
6340 // ^^^^^^^^^^^^
6341 if (optionals_list && optionals_list->size) {
6342 for (size_t i = 0; i < optionals_list->size; i++) {
6343 pm_node_t * node = optionals_list->nodes[i];
6344 if (PM_NODE_FLAG_P(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6345 table_size++;
6346 }
6347 }
6348 }
6349
6350 // If we have an anonymous "rest" node, we'll need to increase the local
6351 // table size to take it in to account.
6352 // def m(foo, *, bar)
6353 // ^
6354 if (parameters_node) {
6355 if (parameters_node->rest) {
6356 if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
6357 if (!((const pm_rest_parameter_node_t *) parameters_node->rest)->name || PM_NODE_FLAG_P(parameters_node->rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6358 table_size++;
6359 }
6360 }
6361 }
6362
6363 // def foo(_, **_); _; end
6364 // ^^^
6365 if (parameters_node->keyword_rest) {
6366 // def foo(...); end
6367 // ^^^
6368 // When we have a `...` as the keyword_rest, it's a forwarding_parameter_node and
6369 // we need to leave space for 4 locals: *, **, &, ...
6370 if (PM_NODE_TYPE_P(parameters_node->keyword_rest, PM_FORWARDING_PARAMETER_NODE)) {
6371 // Only optimize specifically methods like this: `foo(...)`
6372 if (requireds_list->size == 0 && optionals_list->size == 0 && keywords_list->size == 0) {
6373 ISEQ_BODY(iseq)->param.flags.use_block = TRUE;
6374 ISEQ_BODY(iseq)->param.flags.forwardable = TRUE;
6375 table_size += 1;
6376 }
6377 else {
6378 table_size += 4;
6379 }
6380 }
6381 else {
6382 const pm_keyword_rest_parameter_node_t *kw_rest = (const pm_keyword_rest_parameter_node_t *) parameters_node->keyword_rest;
6383
6384 // If it's anonymous or repeated, then we need to allocate stack space
6385 if (!kw_rest->name || PM_NODE_FLAG_P(kw_rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6386 table_size++;
6387 }
6388 }
6389 }
6390 }
6391
6392 if (posts_list) {
6393 for (size_t i = 0; i < posts_list->size; i++) {
6394 // For each MultiTargetNode, we're going to have one
6395 // additional anonymous local not represented in the locals table
6396 // We want to account for this in our table size
6397 pm_node_t *required = posts_list->nodes[i];
6398 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE) || PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6399 table_size++;
6400 }
6401 }
6402 }
6403
6404 if (keywords_list && keywords_list->size) {
6405 for (size_t i = 0; i < keywords_list->size; i++) {
6406 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6407 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6408 table_size++;
6409 }
6410 }
6411 }
6412
6413 if (parameters_node && parameters_node->block && PM_NODE_TYPE_P(parameters_node->block, PM_BLOCK_PARAMETER_NODE)) {
6414 const pm_block_parameter_node_t *block_node = (const pm_block_parameter_node_t *) parameters_node->block;
6415
6416 if (PM_NODE_FLAG_P(block_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER) || !block_node->name) {
6417 table_size++;
6418 }
6419 }
6420
6421 // We can create local_table_for_iseq with the correct size
6422 VALUE idtmp = 0;
6423 rb_ast_id_table_t *local_table_for_iseq = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
6424 local_table_for_iseq->size = table_size;
6425
6426 // Init the direct-indexed lookup table. The capacity is based on the
6427 // parser's constant pool size (for regular locals) plus special slots.
6428 pm_index_lookup_table_init(&index_lookup_table, (int) pm_parser_constants_size(scope_node->parser), iseq);
6429
6430 //********END OF STEP 1**********
6431
6432 //********STEP 2**********
6433 // Goal: populate iv index table as well as local table, keeping the
6434 // layout of the local table consistent with the layout of the
6435 // stack when calling the method
6436 //
6437 // Do a first pass on all of the parameters, setting their values in
6438 // the local_table_for_iseq, _except_ for Multis who get a hidden
6439 // variable in this step, and will get their names inserted in step 3
6440
6441 // local_index is a cursor that keeps track of the current
6442 // index into local_table_for_iseq. The local table is actually a list,
6443 // and the order of that list must match the order of the items pushed
6444 // on the stack. We need to take in to account things pushed on the
6445 // stack that _might not have a name_ (for example array destructuring).
6446 // This index helps us know which item we're dealing with and also give
6447 // those anonymous items temporary names (as below)
6448 int local_index = 0;
6449
6450 // Here we figure out local table indices and insert them in to the
6451 // index lookup table and local tables.
6452 //
6453 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6454 // ^^^^^^^^^^^^^
6455 if (requireds_list && requireds_list->size) {
6456 for (size_t i = 0; i < requireds_list->size; i++, local_index++) {
6457 ID local;
6458
6459 // For each MultiTargetNode, we're going to have one additional
6460 // anonymous local not represented in the locals table. We want
6461 // to account for this in our table size.
6462 pm_node_t *required = requireds_list->nodes[i];
6463
6464 switch (PM_NODE_TYPE(required)) {
6465 case PM_MULTI_TARGET_NODE: {
6466 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6467 // ^^^^^^^^^^
6468 local = rb_make_temporary_id(local_index);
6469 local_table_for_iseq->ids[local_index] = local;
6470 break;
6471 }
6472 case PM_REQUIRED_PARAMETER_NODE: {
6473 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6474 // ^
6475 const pm_required_parameter_node_t *param = (const pm_required_parameter_node_t *) required;
6476
6477 if (PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6478 ID local = pm_constant_id_lookup(scope_node, param->name);
6479 local_table_for_iseq->ids[local_index] = local;
6480 }
6481 else {
6482 pm_insert_local_index(param->name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6483 }
6484
6485 break;
6486 }
6487 default:
6488 rb_bug("Unsupported node in requireds in parameters %s", pm_node_type(PM_NODE_TYPE(required)));
6489 }
6490 }
6491
6492 body->param.lead_num = (int) requireds_list->size;
6493 body->param.flags.has_lead = true;
6494 }
6495
6496 if (scope_node->parameters != NULL && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
6497 local_table_for_iseq->ids[local_index++] = idItImplicit;
6498 }
6499
6500 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6501 // ^^^^^
6502 if (optionals_list && optionals_list->size) {
6503 body->param.opt_num = (int) optionals_list->size;
6504 body->param.flags.has_opt = true;
6505
6506 for (size_t i = 0; i < optionals_list->size; i++, local_index++) {
6507 pm_node_t * node = optionals_list->nodes[i];
6508 pm_constant_id_t name = ((const pm_optional_parameter_node_t *) node)->name;
6509
6510 if (PM_NODE_FLAG_P(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6511 ID local = pm_constant_id_lookup(scope_node, name);
6512 local_table_for_iseq->ids[local_index] = local;
6513 }
6514 else {
6515 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6516 }
6517 }
6518 }
6519
6520 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6521 // ^^
6522 if (parameters_node && parameters_node->rest) {
6523 body->param.rest_start = local_index;
6524
6525 // If there's a trailing comma, we'll have an implicit rest node,
6526 // and we don't want it to impact the rest variables on param
6527 if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
6528 body->param.flags.has_rest = true;
6529 RUBY_ASSERT(body->param.rest_start != -1);
6530
6531 pm_constant_id_t name = ((const pm_rest_parameter_node_t *) parameters_node->rest)->name;
6532
6533 if (name) {
6534 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6535 // ^^
6536 if (PM_NODE_FLAG_P(parameters_node->rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6537 ID local = pm_constant_id_lookup(scope_node, name);
6538 local_table_for_iseq->ids[local_index] = local;
6539 }
6540 else {
6541 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6542 }
6543 }
6544 else {
6545 // def foo(a, (b, *c, d), e = 1, *, g, (h, *i, j), k:, l: 1, **m, &n)
6546 // ^
6547 body->param.flags.anon_rest = true;
6548 pm_insert_local_special(PM_CONSTANT_MULT, idMULT, local_index, &index_lookup_table, local_table_for_iseq);
6549 }
6550
6551 local_index++;
6552 }
6553 }
6554
6555 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6556 // ^^^^^^^^^^^^^
6557 if (posts_list && posts_list->size) {
6558 body->param.post_num = (int) posts_list->size;
6559 body->param.post_start = local_index;
6560 body->param.flags.has_post = true;
6561
6562 for (size_t i = 0; i < posts_list->size; i++, local_index++) {
6563 ID local;
6564
6565 // For each MultiTargetNode, we're going to have one additional
6566 // anonymous local not represented in the locals table. We want
6567 // to account for this in our table size.
6568 const pm_node_t *post_node = posts_list->nodes[i];
6569
6570 switch (PM_NODE_TYPE(post_node)) {
6571 case PM_MULTI_TARGET_NODE: {
6572 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6573 // ^^^^^^^^^^
6574 local = rb_make_temporary_id(local_index);
6575 local_table_for_iseq->ids[local_index] = local;
6576 break;
6577 }
6578 case PM_REQUIRED_PARAMETER_NODE: {
6579 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6580 // ^
6581 const pm_required_parameter_node_t *param = (const pm_required_parameter_node_t *) post_node;
6582
6583 if (PM_NODE_FLAG_P(param, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6584 ID local = pm_constant_id_lookup(scope_node, param->name);
6585 local_table_for_iseq->ids[local_index] = local;
6586 }
6587 else {
6588 pm_insert_local_index(param->name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6589 }
6590 break;
6591 }
6592 default:
6593 rb_bug("Unsupported node in posts in parameters %s", pm_node_type(PM_NODE_TYPE(post_node)));
6594 }
6595 }
6596 }
6597
6598 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6599 // ^^^^^^^^
6600 // Keywords create an internal variable on the parse tree
6601 if (keywords_list && keywords_list->size) {
6602 if (keywords_list->size > VM_CALL_KW_LEN_MAX) {
6603 COMPILE_ERROR(iseq, node_location->line, "too many keyword parameters (%d, maximum is %d)",
6604 (int) keywords_list->size, (int) VM_CALL_KW_LEN_MAX);
6605 }
6606
6607 keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6608 keyword->num = (int) keywords_list->size;
6609
6610 const VALUE default_values = rb_ary_hidden_new(1);
6611 const VALUE complex_mark = rb_str_tmp_new(0);
6612
6613 for (size_t i = 0; i < keywords_list->size; i++) {
6614 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6615 pm_constant_id_t name;
6616
6617 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6618 // ^^
6619 if (PM_NODE_TYPE_P(keyword_parameter_node, PM_REQUIRED_KEYWORD_PARAMETER_NODE)) {
6620 name = ((const pm_required_keyword_parameter_node_t *) keyword_parameter_node)->name;
6621 keyword->required_num++;
6622 ID local = pm_constant_id_lookup(scope_node, name);
6623
6624 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6625 local_table_for_iseq->ids[local_index] = local;
6626 }
6627 else {
6628 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6629 }
6630 local_index++;
6631 }
6632 }
6633
6634 for (size_t i = 0; i < keywords_list->size; i++) {
6635 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6636 pm_constant_id_t name;
6637
6638 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6639 // ^^^^
6640 if (PM_NODE_TYPE_P(keyword_parameter_node, PM_OPTIONAL_KEYWORD_PARAMETER_NODE)) {
6641 const pm_optional_keyword_parameter_node_t *cast = ((const pm_optional_keyword_parameter_node_t *) keyword_parameter_node);
6642
6643 pm_node_t *value = cast->value;
6644 name = cast->name;
6645
6646 if (PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL) && !PM_CONTAINER_P(value)) {
6647 rb_ary_push(default_values, pm_static_literal_value(iseq, value, scope_node));
6648 }
6649 else {
6650 rb_ary_push(default_values, complex_mark);
6651 }
6652
6653 ID local = pm_constant_id_lookup(scope_node, name);
6654 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6655 local_table_for_iseq->ids[local_index] = local;
6656 }
6657 else {
6658 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6659 }
6660 local_index++;
6661 }
6662
6663 }
6664
6665 if (RARRAY_LEN(default_values)) {
6666 VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
6667
6668 for (int i = 0; i < RARRAY_LEN(default_values); i++) {
6669 VALUE dv = RARRAY_AREF(default_values, i);
6670 if (dv == complex_mark) dv = Qundef;
6671 RB_OBJ_WRITE(iseq, &dvs[i], dv);
6672 }
6673
6674 keyword->default_values = dvs;
6675 }
6676
6677 // Hidden local for keyword arguments
6678 keyword->bits_start = local_index;
6679 ID local = rb_make_temporary_id(local_index);
6680 local_table_for_iseq->ids[local_index] = local;
6681 local_index++;
6682
6683 body->param.keyword = keyword;
6684 body->param.flags.has_kw = true;
6685 }
6686
6687 if (body->type == ISEQ_TYPE_BLOCK && local_index == 1 && requireds_list && requireds_list->size == 1 && !trailing_comma) {
6688 body->param.flags.ambiguous_param0 = true;
6689 }
6690
6691 if (parameters_node) {
6692 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6693 // ^^^
6694 if (parameters_node->keyword_rest) {
6695 switch (PM_NODE_TYPE(parameters_node->keyword_rest)) {
6696 case PM_NO_KEYWORDS_PARAMETER_NODE: {
6697 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **nil, &n)
6698 // ^^^^^
6699 body->param.flags.accepts_no_kwarg = true;
6700 break;
6701 }
6702 case PM_KEYWORD_REST_PARAMETER_NODE: {
6703 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6704 // ^^^
6705 const pm_keyword_rest_parameter_node_t *kw_rest_node = (const pm_keyword_rest_parameter_node_t *) parameters_node->keyword_rest;
6706 if (!body->param.flags.has_kw) {
6707 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6708 }
6709
6710 keyword->rest_start = local_index;
6711 body->param.flags.has_kwrest = true;
6712
6713 pm_constant_id_t constant_id = kw_rest_node->name;
6714 if (constant_id) {
6715 if (PM_NODE_FLAG_P(kw_rest_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6716 ID local = pm_constant_id_lookup(scope_node, constant_id);
6717 local_table_for_iseq->ids[local_index] = local;
6718 }
6719 else {
6720 pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6721 }
6722 }
6723 else {
6724 body->param.flags.anon_kwrest = true;
6725 pm_insert_local_special(PM_CONSTANT_POW, idPow, local_index, &index_lookup_table, local_table_for_iseq);
6726 }
6727
6728 local_index++;
6729 break;
6730 }
6731 case PM_FORWARDING_PARAMETER_NODE: {
6732 // def foo(...)
6733 // ^^^
6734 if (!ISEQ_BODY(iseq)->param.flags.forwardable) {
6735 // Add the anonymous *
6736 body->param.rest_start = local_index;
6737 body->param.flags.has_rest = true;
6738 body->param.flags.anon_rest = true;
6739 pm_insert_local_special(PM_CONSTANT_MULT, idMULT, local_index++, &index_lookup_table, local_table_for_iseq);
6740
6741 // Add the anonymous **
6742 RUBY_ASSERT(!body->param.flags.has_kw);
6743 body->param.flags.has_kw = false;
6744 body->param.flags.has_kwrest = true;
6745 body->param.flags.anon_kwrest = true;
6746 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6747 keyword->rest_start = local_index;
6748 pm_insert_local_special(PM_CONSTANT_POW, idPow, local_index++, &index_lookup_table, local_table_for_iseq);
6749
6750 // Add the anonymous &
6751 body->param.block_start = local_index;
6752 body->param.flags.has_block = true;
6753 pm_insert_local_special(PM_CONSTANT_AND, idAnd, local_index++, &index_lookup_table, local_table_for_iseq);
6754 }
6755
6756 // Add the ...
6757 pm_insert_local_special(PM_CONSTANT_DOT3, idDot3, local_index++, &index_lookup_table, local_table_for_iseq);
6758 break;
6759 }
6760 default:
6761 rb_bug("node type %s not expected as keyword_rest", pm_node_type(PM_NODE_TYPE(parameters_node->keyword_rest)));
6762 }
6763 }
6764
6765 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6766 // ^^
6767 if (parameters_node->block) {
6768 switch (PM_NODE_TYPE(parameters_node->block)) {
6769 case PM_BLOCK_PARAMETER_NODE: {
6770 body->param.block_start = local_index;
6771 body->param.flags.has_block = true;
6772
6773 iseq_set_use_block(iseq);
6774
6775 pm_constant_id_t name = ((const pm_block_parameter_node_t *) parameters_node->block)->name;
6776
6777 if (name) {
6778 if (PM_NODE_FLAG_P(parameters_node->block, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6779 ID local = pm_constant_id_lookup(scope_node, name);
6780 local_table_for_iseq->ids[local_index] = local;
6781 }
6782 else {
6783 pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6784 }
6785 }
6786 else {
6787 pm_insert_local_special(PM_CONSTANT_AND, idAnd, local_index, &index_lookup_table, local_table_for_iseq);
6788 }
6789
6790 local_index++;
6791 break;
6792 }
6793 case PM_NO_BLOCK_PARAMETER_NODE: {
6794 body->param.flags.accepts_no_block = true;
6795 break;
6796 }
6797 default:
6798 rb_bug("node type %s not expected as block parameter", pm_node_type(PM_NODE_TYPE(parameters_node->block)));
6799 }
6800 }
6801 }
6802
6803 //********END OF STEP 2**********
6804 // The local table is now consistent with expected
6805 // stack layout
6806
6807 // If there's only one required element in the parameters
6808 // CRuby needs to recognize it as an ambiguous parameter
6809
6810 //********STEP 3**********
6811 // Goal: fill in the names of the parameters in MultiTargetNodes
6812 //
6813 // Go through requireds again to set the multis
6814
6815 if (requireds_list && requireds_list->size) {
6816 for (size_t i = 0; i < requireds_list->size; i++) {
6817 // For each MultiTargetNode, we're going to have one
6818 // additional anonymous local not represented in the locals table
6819 // We want to account for this in our table size
6820 const pm_node_t *required = requireds_list->nodes[i];
6821
6822 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
6823 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);
6824 }
6825 }
6826 }
6827
6828 // Go through posts again to set the multis
6829 if (posts_list && posts_list->size) {
6830 for (size_t i = 0; i < posts_list->size; i++) {
6831 // For each MultiTargetNode, we're going to have one
6832 // additional anonymous local not represented in the locals table
6833 // We want to account for this in our table size
6834 const pm_node_t *post = posts_list->nodes[i];
6835
6836 if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
6837 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);
6838 }
6839 }
6840 }
6841
6842 // Set any anonymous locals for the for node
6843 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
6844 if (PM_NODE_TYPE_P(((const pm_for_node_t *) scope_node->ast_node)->index, PM_LOCAL_VARIABLE_TARGET_NODE)) {
6845 body->param.lead_num++;
6846 }
6847 else {
6848 body->param.rest_start = local_index;
6849 body->param.flags.has_rest = true;
6850 }
6851
6852 ID local = rb_make_temporary_id(local_index);
6853 local_table_for_iseq->ids[local_index] = local;
6854 local_index++;
6855 }
6856
6857 // Fill in any NumberedParameters, if they exist
6858 if (scope_node->parameters && PM_NODE_TYPE_P(scope_node->parameters, PM_NUMBERED_PARAMETERS_NODE)) {
6859 int maximum = ((const pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
6860 RUBY_ASSERT(0 < maximum && maximum <= 9);
6861 for (int i = 0; i < maximum; i++, local_index++) {
6862 const uint8_t param_name[] = { '_', '1' + i };
6863 pm_constant_id_t constant_id = pm_parser_constant_find(scope_node->parser, param_name, 2);
6864 RUBY_ASSERT(constant_id && "parser should fill in any gaps in numbered parameters");
6865 pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6866 }
6867 body->param.lead_num = maximum;
6868 body->param.flags.has_lead = true;
6869 }
6870
6871 // Fill in the anonymous `it` parameter, if it exists
6872 if (scope_node->parameters && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
6873 body->param.lead_num = 1;
6874 body->param.flags.has_lead = true;
6875 }
6876
6877 //********END OF STEP 3**********
6878
6879 //********STEP 4**********
6880 // Goal: fill in the method body locals
6881 // To be explicit, these are the non-parameter locals
6882 // We fill in the block_locals, if they exist
6883 // lambda { |x; y| y }
6884 // ^
6885 if (block_locals && block_locals->size) {
6886 for (size_t i = 0; i < block_locals->size; i++, local_index++) {
6887 pm_constant_id_t constant_id = ((const pm_block_local_variable_node_t *) block_locals->nodes[i])->name;
6888 pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
6889 }
6890 }
6891
6892 // Fill in any locals we missed
6893 if (scope_node->locals.size) {
6894 for (size_t i = 0; i < scope_node->locals.size; i++) {
6895 pm_constant_id_t constant_id = locals->ids[i];
6896 if (constant_id) {
6897 int existing;
6898 if (!pm_index_lookup_table_lookup(&index_lookup_table, constant_id, &existing)) {
6899 ID local = pm_constant_id_lookup(scope_node, constant_id);
6900 local_table_for_iseq->ids[local_index] = local;
6901 pm_index_lookup_table_insert(&index_lookup_table, constant_id, local_index);
6902 local_index++;
6903 }
6904 }
6905 }
6906 }
6907
6908 //********END OF STEP 4**********
6909
6910 // We set the index_lookup_table on the scope node so we can
6911 // refer to the parameters correctly.
6912 scope_node->index_lookup_table = index_lookup_table;
6913 iseq_calc_param_size(iseq);
6914
6915 if (ISEQ_BODY(iseq)->param.flags.forwardable) {
6916 // We're treating `...` as a parameter so that frame
6917 // pushing won't clobber it.
6918 ISEQ_BODY(iseq)->param.size += 1;
6919 }
6920
6921 // FIXME: args?
6922 iseq_set_local_table(iseq, local_table_for_iseq, 0);
6923 iseq_set_parameters_lvar_state(iseq);
6924
6925 scope_node->local_table_for_iseq_size = local_table_for_iseq->size;
6926
6927 if (keyword != NULL) {
6928 size_t keyword_start_index = keyword->bits_start - keyword->num;
6929 keyword->table = (ID *)&ISEQ_BODY(iseq)->local_table[keyword_start_index];
6930 }
6931
6932 //********STEP 5************
6933 // Goal: compile anything that needed to be compiled
6934 if (optionals_list && optionals_list->size) {
6935 LABEL **opt_table = (LABEL **) ALLOC_N(VALUE, optionals_list->size + 1);
6936 LABEL *label;
6937
6938 // TODO: Should we make an api for NEW_LABEL where you can pass
6939 // a pointer to the label it should fill out? We already
6940 // have a list of labels allocated above so it seems wasteful
6941 // to do the copies.
6942 for (size_t i = 0; i < optionals_list->size; i++) {
6943 label = NEW_LABEL(location.line);
6944 opt_table[i] = label;
6945 PUSH_LABEL(ret, label);
6946 pm_node_t *optional_node = optionals_list->nodes[i];
6947 PM_COMPILE_NOT_POPPED(optional_node);
6948 }
6949
6950 // Set the last label
6951 label = NEW_LABEL(location.line);
6952 opt_table[optionals_list->size] = label;
6953 PUSH_LABEL(ret, label);
6954
6955 body->param.opt_table = (const VALUE *) opt_table;
6956 }
6957
6958 if (keywords_list && keywords_list->size) {
6959 size_t optional_index = 0;
6960 for (size_t i = 0; i < keywords_list->size; i++) {
6961 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6962 pm_constant_id_t name;
6963
6964 switch (PM_NODE_TYPE(keyword_parameter_node)) {
6965 case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: {
6966 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6967 // ^^^^
6968 const pm_optional_keyword_parameter_node_t *cast = ((const pm_optional_keyword_parameter_node_t *) keyword_parameter_node);
6969
6970 pm_node_t *value = cast->value;
6971 name = cast->name;
6972
6973 if (!PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL) || PM_CONTAINER_P(value)) {
6974 LABEL *end_label = NEW_LABEL(location.line);
6975
6976 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, name, 0);
6977 int kw_bits_idx = table_size - body->param.keyword->bits_start;
6978 PUSH_INSN2(ret, location, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(optional_index));
6979 PUSH_INSNL(ret, location, branchif, end_label);
6980 PM_COMPILE(value);
6981 PUSH_SETLOCAL(ret, location, index.index, index.level);
6982 PUSH_LABEL(ret, end_label);
6983 }
6984 optional_index++;
6985 break;
6986 }
6987 case PM_REQUIRED_KEYWORD_PARAMETER_NODE:
6988 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6989 // ^^
6990 break;
6991 default:
6992 rb_bug("Unexpected keyword parameter node type %s", pm_node_type(PM_NODE_TYPE(keyword_parameter_node)));
6993 }
6994 }
6995 }
6996
6997 if (requireds_list && requireds_list->size) {
6998 for (size_t i = 0; i < requireds_list->size; i++) {
6999 // For each MultiTargetNode, we're going to have one additional
7000 // anonymous local not represented in the locals table. We want
7001 // to account for this in our table size.
7002 const pm_node_t *required = requireds_list->nodes[i];
7003
7004 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
7005 PUSH_GETLOCAL(ret, location, table_size - (int)i, 0);
7006 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) required, ret, scope_node);
7007 }
7008 }
7009 }
7010
7011 if (posts_list && posts_list->size) {
7012 for (size_t i = 0; i < posts_list->size; i++) {
7013 // For each MultiTargetNode, we're going to have one additional
7014 // anonymous local not represented in the locals table. We want
7015 // to account for this in our table size.
7016 const pm_node_t *post = posts_list->nodes[i];
7017
7018 if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
7019 PUSH_GETLOCAL(ret, location, table_size - body->param.post_start - (int) i, 0);
7020 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) post, ret, scope_node);
7021 }
7022 }
7023 }
7024
7025 switch (body->type) {
7026 case ISEQ_TYPE_PLAIN: {
7027 RUBY_ASSERT(PM_NODE_TYPE_P(scope_node->ast_node, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE));
7028
7030 pm_compile_regexp_dynamic(iseq, (const pm_node_t *) cast, &cast->parts, &location, ret, popped, scope_node);
7031
7032 break;
7033 }
7034 case ISEQ_TYPE_BLOCK: {
7035 LABEL *start = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(0);
7036 LABEL *end = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(0);
7037 const pm_node_location_t block_location = { .line = body->location.first_lineno, .node_id = scope_node->ast_node->node_id };
7038
7039 start->rescued = LABEL_RESCUE_BEG;
7040 end->rescued = LABEL_RESCUE_END;
7041
7042 // For nodes automatically assign the iteration variable to whatever
7043 // index variable. We need to handle that write here because it has
7044 // to happen in the context of the block. Note that this happens
7045 // before the B_CALL tracepoint event.
7046 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
7047 pm_compile_for_node_index(iseq, ((const pm_for_node_t *) scope_node->ast_node)->index, ret, scope_node);
7048 }
7049
7050 PUSH_TRACE(ret, RUBY_EVENT_B_CALL);
7051 PUSH_INSN(ret, block_location, nop);
7052 PUSH_LABEL(ret, start);
7053
7054 if (scope_node->body != NULL) {
7055 switch (PM_NODE_TYPE(scope_node->ast_node)) {
7056 case PM_POST_EXECUTION_NODE: {
7057 const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) scope_node->ast_node;
7058 PUSH_INSN1(ret, block_location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7059
7060 // We create another ScopeNode from the statements within the PostExecutionNode
7061 pm_scope_node_t next_scope_node;
7062 pm_scope_node_init((const pm_node_t *) cast->statements, &next_scope_node, scope_node);
7063
7064 const rb_iseq_t *block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(body->parent_iseq), ISEQ_TYPE_BLOCK, location.line);
7065 pm_scope_node_destroy(&next_scope_node);
7066
7067 PUSH_CALL_WITH_BLOCK(ret, block_location, id_core_set_postexe, INT2FIX(0), block);
7068 break;
7069 }
7070 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
7072 pm_compile_regexp_dynamic(iseq, (const pm_node_t *) cast, &cast->parts, &location, ret, popped, scope_node);
7073 break;
7074 }
7075 default:
7076 pm_compile_node(iseq, scope_node->body, ret, popped, scope_node);
7077 break;
7078 }
7079 }
7080 else {
7081 PUSH_INSN(ret, block_location, putnil);
7082 }
7083
7084 PUSH_LABEL(ret, end);
7085 PUSH_TRACE(ret, RUBY_EVENT_B_RETURN);
7086 ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
7087
7088 /* wide range catch handler must put at last */
7089 PUSH_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, NULL, start);
7090 PUSH_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, NULL, end);
7091 break;
7092 }
7093 case ISEQ_TYPE_ENSURE: {
7094 const pm_node_location_t statements_location = (scope_node->body != NULL ? PM_NODE_START_LOCATION(scope_node->body) : location);
7095 iseq_set_exception_local_table(iseq);
7096
7097 if (scope_node->body != NULL) {
7098 PM_COMPILE_POPPED((const pm_node_t *) scope_node->body);
7099 }
7100
7101 PUSH_GETLOCAL(ret, statements_location, 1, 0);
7102 PUSH_INSN1(ret, statements_location, throw, INT2FIX(0));
7103 return;
7104 }
7105 case ISEQ_TYPE_METHOD: {
7106 ISEQ_COMPILE_DATA(iseq)->root_node = (const void *) scope_node->body;
7107 PUSH_TRACE(ret, RUBY_EVENT_CALL);
7108
7109 if (scope_node->body) {
7110 PM_COMPILE((const pm_node_t *) scope_node->body);
7111 }
7112 else {
7113 PUSH_INSN(ret, location, putnil);
7114 }
7115
7116 ISEQ_COMPILE_DATA(iseq)->root_node = (const void *) scope_node->body;
7117 PUSH_TRACE(ret, RUBY_EVENT_RETURN);
7118
7119 ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
7120 break;
7121 }
7122 case ISEQ_TYPE_RESCUE: {
7123 iseq_set_exception_local_table(iseq);
7124 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_RESCUE_MODIFIER_NODE)) {
7125 LABEL *lab = NEW_LABEL(location.line);
7126 LABEL *rescue_end = NEW_LABEL(location.line);
7127 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
7128 PUSH_INSN1(ret, location, putobject, rb_eStandardError);
7129 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
7130 PUSH_INSNL(ret, location, branchif, lab);
7131 PUSH_INSNL(ret, location, jump, rescue_end);
7132 PUSH_LABEL(ret, lab);
7133 PUSH_TRACE(ret, RUBY_EVENT_RESCUE);
7134 PM_COMPILE((const pm_node_t *) scope_node->body);
7135 PUSH_INSN(ret, location, leave);
7136 PUSH_LABEL(ret, rescue_end);
7137 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
7138 }
7139 else {
7140 PM_COMPILE((const pm_node_t *) scope_node->ast_node);
7141 }
7142 PUSH_INSN1(ret, location, throw, INT2FIX(0));
7143
7144 return;
7145 }
7146 default:
7147 if (scope_node->body) {
7148 PM_COMPILE((const pm_node_t *) scope_node->body);
7149 }
7150 else {
7151 PUSH_INSN(ret, location, putnil);
7152 }
7153 break;
7154 }
7155
7156 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE) || PM_NODE_TYPE_P(scope_node->ast_node, PM_MODULE_NODE)) {
7157 const pm_node_location_t end_location = PM_NODE_END_LOCATION(scope_node->ast_node);
7158 PUSH_TRACE(ret, RUBY_EVENT_END);
7159 ISEQ_COMPILE_DATA(iseq)->last_line = end_location.line;
7160 }
7161
7162 if (!PM_NODE_TYPE_P(scope_node->ast_node, PM_ENSURE_NODE)) {
7163 const pm_node_location_t location = { .line = ISEQ_COMPILE_DATA(iseq)->last_line, .node_id = scope_node->ast_node->node_id };
7164 PUSH_INSN(ret, location, leave);
7165 }
7166}
7167
7168static inline void
7169pm_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)
7170{
7171 // alias $foo $bar
7172 // ^^^^^^^^^^^^^^^
7173 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7174
7175 {
7176 const pm_location_t *name_loc = &node->new_name->location;
7177 VALUE operand = ID2SYM(rb_intern3((const char *) (pm_parser_start(scope_node->parser) + name_loc->start), name_loc->length, scope_node->encoding));
7178 PUSH_INSN1(ret, *location, putobject, operand);
7179 }
7180
7181 {
7182 const pm_location_t *name_loc = &node->old_name->location;
7183 VALUE operand = ID2SYM(rb_intern3((const char *) (pm_parser_start(scope_node->parser) + name_loc->start), name_loc->length, scope_node->encoding));
7184 PUSH_INSN1(ret, *location, putobject, operand);
7185 }
7186
7187 PUSH_SEND(ret, *location, id_core_set_variable_alias, INT2FIX(2));
7188 if (popped) PUSH_INSN(ret, *location, pop);
7189}
7190
7191static inline void
7192pm_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)
7193{
7194 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7195 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
7196 PM_COMPILE_NOT_POPPED(node->new_name);
7197 PM_COMPILE_NOT_POPPED(node->old_name);
7198
7199 PUSH_SEND(ret, *location, id_core_set_method_alias, INT2FIX(3));
7200 if (popped) PUSH_INSN(ret, *location, pop);
7201}
7202
7203static inline void
7204pm_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)
7205{
7206 LABEL *end_label = NEW_LABEL(location->line);
7207
7208 PM_COMPILE_NOT_POPPED(node->left);
7209 if (!popped) PUSH_INSN(ret, *location, dup);
7210 PUSH_INSNL(ret, *location, branchunless, end_label);
7211
7212 if (!popped) PUSH_INSN(ret, *location, pop);
7213 PM_COMPILE(node->right);
7214 PUSH_LABEL(ret, end_label);
7215}
7216
7217static inline void
7218pm_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)
7219{
7220 // If every node in the array is static, then we can compile the entire
7221 // array now instead of later.
7222 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
7223 // We're only going to compile this node if it's not popped. If it
7224 // is popped, then we know we don't need to do anything since it's
7225 // statically known.
7226 if (!popped) {
7227 if (elements->size) {
7228 VALUE value = pm_static_literal_value(iseq, node, scope_node);
7230 PUSH_INSN1(ret, *location, duparray, value);
7231 }
7232 else {
7233 PUSH_INSN1(ret, *location, newarray, INT2FIX(0));
7234 }
7235 }
7236 return;
7237 }
7238
7239 // Here since we know there are possible side-effects inside the
7240 // array contents, we're going to build it entirely at runtime.
7241 // We'll do this by pushing all of the elements onto the stack and
7242 // then combining them with newarray.
7243 //
7244 // If this array is popped, then this serves only to ensure we enact
7245 // all side-effects (like method calls) that are contained within
7246 // the array contents.
7247 //
7248 // We treat all sequences of non-splat elements as their
7249 // own arrays, followed by a newarray, and then continually
7250 // concat the arrays with the SplatNode nodes.
7251 const int max_new_array_size = 0x100;
7252 const unsigned int min_tmp_array_size = 0x40;
7253
7254 int new_array_size = 0;
7255 bool first_chunk = true;
7256
7257 // This is an optimization wherein we keep track of whether or not
7258 // the previous element was a static literal. If it was, then we do
7259 // not attempt to check if we have a subarray that can be optimized.
7260 // If it was not, then we do check.
7261 bool static_literal = false;
7262
7263 // Either create a new array, or push to the existing array.
7264#define FLUSH_CHUNK \
7265 if (new_array_size) { \
7266 if (first_chunk) PUSH_INSN1(ret, *location, newarray, INT2FIX(new_array_size)); \
7267 else PUSH_INSN1(ret, *location, pushtoarray, INT2FIX(new_array_size)); \
7268 first_chunk = false; \
7269 new_array_size = 0; \
7270 }
7271
7272 for (size_t index = 0; index < elements->size; index++) {
7273 const pm_node_t *element = elements->nodes[index];
7274
7275 if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) {
7276 FLUSH_CHUNK;
7277
7278 const pm_splat_node_t *splat_element = (const pm_splat_node_t *) element;
7279 if (splat_element->expression) {
7280 PM_COMPILE_NOT_POPPED(splat_element->expression);
7281 }
7282 else {
7283 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
7284 PUSH_GETLOCAL(ret, *location, index.index, index.level);
7285 }
7286
7287 if (first_chunk) {
7288 // If this is the first element of the array then we
7289 // need to splatarray the elements into the list.
7290 PUSH_INSN1(ret, *location, splatarray, Qtrue);
7291 first_chunk = false;
7292 }
7293 else {
7294 PUSH_INSN(ret, *location, concattoarray);
7295 }
7296
7297 static_literal = false;
7298 }
7299 else if (PM_NODE_TYPE_P(element, PM_KEYWORD_HASH_NODE)) {
7300 if (new_array_size == 0 && first_chunk) {
7301 PUSH_INSN1(ret, *location, newarray, INT2FIX(0));
7302 first_chunk = false;
7303 }
7304 else {
7305 FLUSH_CHUNK;
7306 }
7307
7308 // If we get here, then this is the last element of the
7309 // array/arguments, because it cannot be followed by
7310 // anything else without a syntax error. This looks like:
7311 //
7312 // [foo, bar, baz: qux]
7313 // ^^^^^^^^
7314 //
7315 // [foo, bar, **baz]
7316 // ^^^^^
7317 //
7318 const pm_keyword_hash_node_t *keyword_hash = (const pm_keyword_hash_node_t *) element;
7319 pm_compile_hash_elements(iseq, element, &keyword_hash->elements, 0, Qundef, false, ret, scope_node);
7320
7321 // This boolean controls the manner in which we push the
7322 // hash onto the array. If it's all keyword splats, then we
7323 // can use the very specialized pushtoarraykwsplat
7324 // instruction to check if it's empty before we push it.
7325 size_t splats = 0;
7326 while (splats < keyword_hash->elements.size && PM_NODE_TYPE_P(keyword_hash->elements.nodes[splats], PM_ASSOC_SPLAT_NODE)) splats++;
7327
7328 if (keyword_hash->elements.size == splats) {
7329 PUSH_INSN(ret, *location, pushtoarraykwsplat);
7330 }
7331 else {
7332 new_array_size++;
7333 }
7334 }
7335 else if (
7336 PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL) &&
7337 !PM_CONTAINER_P(element) &&
7338 !static_literal &&
7339 ((index + min_tmp_array_size) < elements->size)
7340 ) {
7341 // If we have a static literal, then there's the potential
7342 // to group a bunch of them together with a literal array
7343 // and then concat them together.
7344 size_t right_index = index + 1;
7345 while (
7346 right_index < elements->size &&
7347 PM_NODE_FLAG_P(elements->nodes[right_index], PM_NODE_FLAG_STATIC_LITERAL) &&
7348 !PM_CONTAINER_P(elements->nodes[right_index])
7349 ) right_index++;
7350
7351 size_t tmp_array_size = right_index - index;
7352 if (tmp_array_size >= min_tmp_array_size) {
7353 VALUE tmp_array = rb_ary_hidden_new(tmp_array_size);
7354
7355 // Create the temporary array.
7356 for (; tmp_array_size; tmp_array_size--)
7357 rb_ary_push(tmp_array, pm_static_literal_value(iseq, elements->nodes[index++], scope_node));
7358
7359 index--; // about to be incremented by for loop
7360 RB_OBJ_SET_FROZEN_SHAREABLE(tmp_array);
7361
7362 // Emit the optimized code.
7363 FLUSH_CHUNK;
7364 if (first_chunk) {
7365 PUSH_INSN1(ret, *location, duparray, tmp_array);
7366 first_chunk = false;
7367 }
7368 else {
7369 PUSH_INSN1(ret, *location, putobject, tmp_array);
7370 PUSH_INSN(ret, *location, concattoarray);
7371 }
7372 }
7373 else {
7374 PM_COMPILE_NOT_POPPED(element);
7375 if (++new_array_size >= max_new_array_size) FLUSH_CHUNK;
7376 static_literal = true;
7377 }
7378 } else {
7379 PM_COMPILE_NOT_POPPED(element);
7380 if (++new_array_size >= max_new_array_size) FLUSH_CHUNK;
7381 static_literal = false;
7382 }
7383 }
7384
7385 FLUSH_CHUNK;
7386 if (popped) PUSH_INSN(ret, *location, pop);
7387
7388#undef FLUSH_CHUNK
7389}
7390
7391static inline void
7392pm_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)
7393{
7394 unsigned long throw_flag = 0;
7395
7396 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
7397 /* while/until */
7398 LABEL *splabel = NEW_LABEL(0);
7399 PUSH_LABEL(ret, splabel);
7400 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
7401
7402 if (node->arguments != NULL) {
7403 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
7404 }
7405 else {
7406 PUSH_INSN(ret, *location, putnil);
7407 }
7408
7409 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
7410 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
7411 PUSH_ADJUST_RESTORE(ret, splabel);
7412 if (!popped) PUSH_INSN(ret, *location, putnil);
7413 }
7414 else {
7415 const rb_iseq_t *ip = iseq;
7416
7417 while (ip) {
7418 if (!ISEQ_COMPILE_DATA(ip)) {
7419 ip = 0;
7420 break;
7421 }
7422
7423 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
7424 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
7425 }
7426 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
7427 throw_flag = 0;
7428 }
7429 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
7430 COMPILE_ERROR(iseq, location->line, "Invalid break");
7431 return;
7432 }
7433 else {
7434 ip = ISEQ_BODY(ip)->parent_iseq;
7435 continue;
7436 }
7437
7438 /* escape from block */
7439 if (node->arguments != NULL) {
7440 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
7441 }
7442 else {
7443 PUSH_INSN(ret, *location, putnil);
7444 }
7445
7446 PUSH_INSN1(ret, *location, throw, INT2FIX(throw_flag | TAG_BREAK));
7447 if (popped) PUSH_INSN(ret, *location, pop);
7448
7449 return;
7450 }
7451
7452 COMPILE_ERROR(iseq, location->line, "Invalid break");
7453 }
7454}
7455
7456static inline void
7457pm_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)
7458{
7459 ID method_id = pm_constant_id_lookup(scope_node, node->name);
7460
7461 const pm_location_t *message_loc = &node->message_loc;
7462 if (message_loc->length == 0) message_loc = &node->base.location;
7463
7464 const pm_node_location_t location = PM_LOCATION_START_LOCATION(message_loc, node->base.node_id);
7465 const char *builtin_func;
7466
7467 if (UNLIKELY(iseq_has_builtin_function_table(iseq)) && (builtin_func = pm_iseq_builtin_function_name(scope_node, node->receiver, method_id)) != NULL) {
7468 pm_compile_builtin_function_call(iseq, ret, scope_node, node, &location, popped, ISEQ_COMPILE_DATA(iseq)->current_block, builtin_func);
7469 return;
7470 }
7471
7472 LABEL *start = NEW_LABEL(location.line);
7473 if (node->block) PUSH_LABEL(ret, start);
7474
7475 switch (method_id) {
7476 case idUMinus: {
7477 if (pm_opt_str_freeze_p(iseq, node)) {
7478 VALUE value = parse_static_literal_string(iseq, scope_node, node->receiver, &((const pm_string_node_t * ) node->receiver)->unescaped);
7479 const struct rb_callinfo *callinfo = new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE);
7480 PUSH_INSN2(ret, location, opt_str_uminus, value, callinfo);
7481 if (popped) PUSH_INSN(ret, location, pop);
7482 return;
7483 }
7484 break;
7485 }
7486 case idFreeze: {
7487 if (pm_opt_str_freeze_p(iseq, node)) {
7488 VALUE value = parse_static_literal_string(iseq, scope_node, node->receiver, &((const pm_string_node_t * ) node->receiver)->unescaped);
7489 const struct rb_callinfo *callinfo = new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE);
7490 PUSH_INSN2(ret, location, opt_str_freeze, value, callinfo);
7491 if (popped) PUSH_INSN(ret, location, pop);
7492 return;
7493 }
7494 break;
7495 }
7496 }
7497
7498 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && !popped) {
7499 PUSH_INSN(ret, location, putnil);
7500 }
7501
7502 if (node->receiver == NULL) {
7503 PUSH_INSN(ret, location, putself);
7504 }
7505 else {
7506 if ((method_id == idCall || method_id == idAREF || method_id == idYield || method_id == idEqq) && PM_NODE_TYPE_P(node->receiver, PM_LOCAL_VARIABLE_READ_NODE)) {
7507 const pm_local_variable_read_node_t *read_node_cast = (const pm_local_variable_read_node_t *) node->receiver;
7508 uint32_t node_id = node->receiver->node_id;
7509 int idx, level;
7510
7511 if (iseq_block_param_id_p(iseq, pm_constant_id_lookup(scope_node, read_node_cast->name), &idx, &level)) {
7512 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)));
7513 }
7514 else {
7515 PM_COMPILE_NOT_POPPED(node->receiver);
7516 }
7517 }
7518 else {
7519 PM_COMPILE_NOT_POPPED(node->receiver);
7520 }
7521 }
7522
7523 pm_compile_call(iseq, node, ret, popped, scope_node, method_id, start);
7524 return;
7525}
7526
7527static inline void
7528pm_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)
7529{
7530 int flag = 0;
7531
7532 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) {
7533 flag = VM_CALL_FCALL;
7534 }
7535
7536 PM_COMPILE_NOT_POPPED(node->receiver);
7537
7538 LABEL *safe_label = NULL;
7539 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
7540 safe_label = NEW_LABEL(location->line);
7541 PUSH_INSN(ret, *location, dup);
7542 PUSH_INSNL(ret, *location, branchnil, safe_label);
7543 }
7544
7545 PUSH_INSN(ret, *location, dup);
7546
7547 ID id_read_name = pm_constant_id_lookup(scope_node, node->read_name);
7548 PUSH_SEND_WITH_FLAG(ret, *location, id_read_name, INT2FIX(0), INT2FIX(flag));
7549
7550 PM_COMPILE_NOT_POPPED(node->value);
7551 ID id_operator = pm_constant_id_lookup(scope_node, node->binary_operator);
7552 PUSH_SEND(ret, *location, id_operator, INT2FIX(1));
7553
7554 if (!popped) {
7555 PUSH_INSN(ret, *location, swap);
7556 PUSH_INSN1(ret, *location, topn, INT2FIX(1));
7557 }
7558
7559 ID id_write_name = pm_constant_id_lookup(scope_node, node->write_name);
7560 PUSH_SEND_WITH_FLAG(ret, *location, id_write_name, INT2FIX(1), INT2FIX(flag));
7561
7562 if (safe_label != NULL && popped) PUSH_LABEL(ret, safe_label);
7563 PUSH_INSN(ret, *location, pop);
7564 if (safe_label != NULL && !popped) PUSH_LABEL(ret, safe_label);
7565}
7566
7583static VALUE
7584pm_compile_case_node_dispatch(rb_iseq_t *iseq, VALUE dispatch, const pm_node_t *node, LABEL *label, pm_scope_node_t *scope_node)
7585{
7586 VALUE key = Qundef;
7587 switch (PM_NODE_TYPE(node)) {
7588 case PM_FLOAT_NODE: {
7589 key = pm_static_literal_value(iseq, node, scope_node);
7590 double intptr;
7591
7592 if (modf(RFLOAT_VALUE(key), &intptr) == 0.0) {
7593 key = (FIXABLE(intptr) ? LONG2FIX((long) intptr) : rb_dbl2big(intptr));
7594 }
7595
7596 break;
7597 }
7598 case PM_FALSE_NODE:
7599 case PM_INTEGER_NODE:
7600 case PM_NIL_NODE:
7601 case PM_SOURCE_LINE_NODE:
7602 case PM_SYMBOL_NODE:
7603 case PM_TRUE_NODE:
7604 key = pm_static_literal_value(iseq, node, scope_node);
7605 break;
7606 case PM_STRING_NODE: {
7607 const pm_string_node_t *cast = (const pm_string_node_t *) node;
7608 key = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
7609 break;
7610 }
7611 default:
7612 return Qundef;
7613 }
7614
7615 cdhash_aset_if_missing(dispatch, key, (VALUE)label);
7616 return dispatch;
7617}
7618
7622static inline void
7623pm_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)
7624{
7625 const pm_node_location_t location = *node_location;
7626 const pm_node_list_t *conditions = &cast->conditions;
7627
7628 // This is the anchor that we will compile the conditions of the various
7629 // `when` nodes into. If a match is found, they will need to jump into
7630 // the body_seq anchor to the correct spot.
7631 DECL_ANCHOR(cond_seq);
7632
7633 // This is the anchor that we will compile the bodies of the various
7634 // `when` nodes into. We'll make sure that the clauses that are compiled
7635 // jump into the correct spots within this anchor.
7636 DECL_ANCHOR(body_seq);
7637
7638 // This is the label where all of the when clauses will jump to if they
7639 // have matched and are done executing their bodies.
7640 LABEL *end_label = NEW_LABEL(location.line);
7641
7642 // If we have a predicate on this case statement, then it's going to
7643 // compare all of the various when clauses to the predicate. If we
7644 // don't, then it's basically an if-elsif-else chain.
7645 if (cast->predicate == NULL) {
7646 // Establish branch coverage for the case node.
7647 VALUE branches = Qfalse;
7648 rb_code_location_t case_location = { 0 };
7649 int branch_id = 0;
7650
7651 if (PM_BRANCH_COVERAGE_P(iseq)) {
7652 case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
7653 branches = decl_branch_base(iseq, (int) cast->base.node_id, &case_location, "case");
7654 }
7655
7656 // Loop through each clauses in the case node and compile each of
7657 // the conditions within them into cond_seq. If they match, they
7658 // should jump into their respective bodies in body_seq.
7659 for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) {
7660 const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index];
7661 const pm_node_list_t *conditions = &clause->conditions;
7662
7663 int clause_lineno = pm_node_line_number_cached((const pm_node_t *) clause, scope_node);
7664 LABEL *label = NEW_LABEL(clause_lineno);
7665 PUSH_LABEL(body_seq, label);
7666
7667 // Establish branch coverage for the when clause.
7668 if (PM_BRANCH_COVERAGE_P(iseq)) {
7669 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));
7670 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches);
7671 }
7672
7673 if (clause->statements != NULL) {
7674 pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node);
7675 }
7676 else if (!popped) {
7677 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
7678 }
7679
7680 PUSH_INSNL(body_seq, location, jump, end_label);
7681
7682 // Compile each of the conditions for the when clause into the
7683 // cond_seq. Each one should have a unique condition and should
7684 // jump to the subsequent one if it doesn't match.
7685 for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) {
7686 const pm_node_t *condition = conditions->nodes[condition_index];
7687
7688 if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) {
7689 pm_node_location_t cond_location = PM_NODE_START_LOCATION(condition);
7690 PUSH_INSN(cond_seq, cond_location, putnil);
7691 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7692 PUSH_INSN1(cond_seq, cond_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
7693 PUSH_INSNL(cond_seq, cond_location, branchif, label);
7694 }
7695 else {
7696 LABEL *next_label = NEW_LABEL(pm_node_line_number_cached(condition, scope_node));
7697 pm_compile_branch_condition(iseq, cond_seq, condition, label, next_label, scope_node);
7698 PUSH_LABEL(cond_seq, next_label);
7699 }
7700 }
7701 }
7702
7703 // Establish branch coverage for the else clause (implicit or
7704 // explicit).
7705 if (PM_BRANCH_COVERAGE_P(iseq)) {
7706 rb_code_location_t branch_location;
7707
7708 if (cast->else_clause == NULL) {
7709 branch_location = case_location;
7710 } else if (cast->else_clause->statements == NULL) {
7711 branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->else_clause);
7712 } else {
7713 branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->else_clause->statements);
7714 }
7715
7716 add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
7717 }
7718
7719 // Compile the else clause if there is one.
7720 if (cast->else_clause != NULL) {
7721 pm_compile_node(iseq, (const pm_node_t *) cast->else_clause, cond_seq, popped, scope_node);
7722 }
7723 else if (!popped) {
7724 PUSH_SYNTHETIC_PUTNIL(cond_seq, iseq);
7725 }
7726
7727 // Finally, jump to the end label if none of the other conditions
7728 // have matched.
7729 PUSH_INSNL(cond_seq, location, jump, end_label);
7730 PUSH_SEQ(ret, cond_seq);
7731 }
7732 else {
7733 // Establish branch coverage for the case node.
7734 VALUE branches = Qfalse;
7735 rb_code_location_t case_location = { 0 };
7736 int branch_id = 0;
7737
7738 if (PM_BRANCH_COVERAGE_P(iseq)) {
7739 case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
7740 branches = decl_branch_base(iseq, (int) cast->base.node_id, &case_location, "case");
7741 }
7742
7743 // This is the label where everything will fall into if none of the
7744 // conditions matched.
7745 LABEL *else_label = NEW_LABEL(location.line);
7746
7747 // It's possible for us to speed up the case node by using a
7748 // dispatch hash. This is a hash that maps the conditions of the
7749 // various when clauses to the labels of their bodies. If we can
7750 // compile the conditions into a hash key, then we can use a hash
7751 // lookup to jump directly to the correct when clause body.
7752 VALUE dispatch = Qundef;
7753 if (ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
7754 dispatch = cdhash_new(0);
7755 }
7756
7757 // We're going to loop through each of the conditions in the case
7758 // node and compile each of their contents into both the cond_seq
7759 // and the body_seq. Each condition will use its own label to jump
7760 // from its conditions into its body.
7761 //
7762 // Note that none of the code in the loop below should be adding
7763 // anything to ret, as we're going to be laying out the entire case
7764 // node instructions later.
7765 for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) {
7766 const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index];
7767 pm_node_location_t clause_location = PM_NODE_START_LOCATION((const pm_node_t *) clause);
7768
7769 const pm_node_list_t *conditions = &clause->conditions;
7770 LABEL *label = NEW_LABEL(clause_location.line);
7771
7772 // Compile each of the conditions for the when clause into the
7773 // cond_seq. Each one should have a unique comparison that then
7774 // jumps into the body if it matches.
7775 for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) {
7776 const pm_node_t *condition = conditions->nodes[condition_index];
7777 const pm_node_location_t condition_location = PM_NODE_START_LOCATION(condition);
7778
7779 // If we haven't already abandoned the optimization, then
7780 // we're going to try to compile the condition into the
7781 // dispatch hash.
7782 if (dispatch != Qundef) {
7783 dispatch = pm_compile_case_node_dispatch(iseq, dispatch, condition, label, scope_node);
7784 }
7785
7786 if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) {
7787 PUSH_INSN(cond_seq, condition_location, dup);
7788 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7789 PUSH_INSN1(cond_seq, condition_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
7790 }
7791 else {
7792 if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
7793 const pm_string_node_t *string = (const pm_string_node_t *) condition;
7794 VALUE value = parse_static_literal_string(iseq, scope_node, condition, &string->unescaped);
7795 PUSH_INSN1(cond_seq, condition_location, putobject, value);
7796 }
7797 else {
7798 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7799 }
7800
7801 PUSH_INSN1(cond_seq, condition_location, topn, INT2FIX(1));
7802 PUSH_SEND_WITH_FLAG(cond_seq, condition_location, idEqq, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
7803 }
7804
7805 PUSH_INSNL(cond_seq, condition_location, branchif, label);
7806 }
7807
7808 // Now, add the label to the body and compile the body of the
7809 // when clause. This involves popping the predicate, compiling
7810 // the statements to be executed, and then compiling a jump to
7811 // the end of the case node.
7812 PUSH_LABEL(body_seq, label);
7813 PUSH_INSN(body_seq, clause_location, pop);
7814
7815 // Establish branch coverage for the when clause.
7816 if (PM_BRANCH_COVERAGE_P(iseq)) {
7817 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));
7818 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches);
7819 }
7820
7821 if (clause->statements != NULL) {
7822 pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node);
7823 }
7824 else if (!popped) {
7825 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
7826 }
7827
7828 PUSH_INSNL(body_seq, clause_location, jump, end_label);
7829 }
7830
7831 // Now that we have compiled the conditions and the bodies of the
7832 // various when clauses, we can compile the predicate, lay out the
7833 // conditions, compile the fallback subsequent if there is one, and
7834 // finally put in the bodies of the when clauses.
7835 PM_COMPILE_NOT_POPPED(cast->predicate);
7836
7837 // If we have a dispatch hash, then we'll use it here to create the
7838 // optimization.
7839 if (dispatch != Qundef) {
7840 PUSH_INSN(ret, location, dup);
7841 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.
7842 PUSH_INSN2(ret, location, opt_case_dispatch, dispatch, else_label);
7843 LABEL_REF(else_label);
7844 }
7845
7846 PUSH_SEQ(ret, cond_seq);
7847
7848 // Compile either the explicit else clause or an implicit else
7849 // clause.
7850 PUSH_LABEL(ret, else_label);
7851
7852 if (cast->else_clause != NULL) {
7853 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));
7854 PUSH_INSN(ret, else_location, pop);
7855
7856 // Establish branch coverage for the else clause.
7857 if (PM_BRANCH_COVERAGE_P(iseq)) {
7858 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));
7859 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
7860 }
7861
7862 PM_COMPILE((const pm_node_t *) cast->else_clause);
7863 PUSH_INSNL(ret, else_location, jump, end_label);
7864 }
7865 else {
7866 PUSH_INSN(ret, location, pop);
7867
7868 // Establish branch coverage for the implicit else clause.
7869 if (PM_BRANCH_COVERAGE_P(iseq)) {
7870 add_trace_branch_coverage(iseq, ret, &case_location, case_location.beg_pos.column, branch_id, "else", branches);
7871 }
7872
7873 if (!popped) PUSH_INSN(ret, location, putnil);
7874 PUSH_INSNL(ret, location, jump, end_label);
7875 }
7876 }
7877
7878 PUSH_SEQ(ret, body_seq);
7879 PUSH_LABEL(ret, end_label);
7880}
7881
7882static inline void
7883pm_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)
7884{
7885 // This is the anchor that we will compile the bodies of the various
7886 // `in` nodes into. We'll make sure that the patterns that are compiled
7887 // jump into the correct spots within this anchor.
7888 DECL_ANCHOR(body_seq);
7889
7890 // This is the anchor that we will compile the patterns of the various
7891 // `in` nodes into. If a match is found, they will need to jump into the
7892 // body_seq anchor to the correct spot.
7893 DECL_ANCHOR(cond_seq);
7894
7895 // This label is used to indicate the end of the entire node. It is
7896 // jumped to after the entire stack is cleaned up.
7897 LABEL *end_label = NEW_LABEL(location->line);
7898
7899 // This label is used as the fallback for the case match. If no match is
7900 // found, then we jump to this label. This is either an `else` clause or
7901 // an error handler.
7902 LABEL *else_label = NEW_LABEL(location->line);
7903
7904 // We're going to use this to uniquely identify each branch so that we
7905 // can track coverage information.
7906 rb_code_location_t case_location = { 0 };
7907 VALUE branches = Qfalse;
7908 int branch_id = 0;
7909
7910 if (PM_BRANCH_COVERAGE_P(iseq)) {
7911 case_location = pm_code_location(scope_node, (const pm_node_t *) node);
7912 branches = decl_branch_base(iseq, (int) node->base.node_id, &case_location, "case");
7913 }
7914
7915 // If there is only one pattern, then the behavior changes a bit. It
7916 // effectively gets treated as a match required node (this is how it is
7917 // represented in the other parser).
7918 bool in_single_pattern = node->else_clause == NULL && node->conditions.size == 1;
7919
7920 // First, we're going to push a bunch of stuff onto the stack that is
7921 // going to serve as our scratch space.
7922 if (in_single_pattern) {
7923 PUSH_INSN(ret, *location, putnil); // key error key
7924 PUSH_INSN(ret, *location, putnil); // key error matchee
7925 PUSH_INSN1(ret, *location, putobject, Qfalse); // key error?
7926 PUSH_INSN(ret, *location, putnil); // error string
7927 }
7928
7929 // Now we're going to compile the value to match against.
7930 PUSH_INSN(ret, *location, putnil); // deconstruct cache
7931 PM_COMPILE_NOT_POPPED(node->predicate);
7932
7933 // Next, we'll loop through every in clause and compile its body into
7934 // the body_seq anchor and its pattern into the cond_seq anchor. We'll
7935 // make sure the pattern knows how to jump correctly into the body if it
7936 // finds a match.
7937 for (size_t index = 0; index < node->conditions.size; index++) {
7938 const pm_node_t *condition = node->conditions.nodes[index];
7939 RUBY_ASSERT(PM_NODE_TYPE_P(condition, PM_IN_NODE));
7940
7941 const pm_in_node_t *in_node = (const pm_in_node_t *) condition;
7942 const pm_node_location_t in_location = PM_NODE_START_LOCATION(in_node);
7943 const pm_node_location_t pattern_location = PM_NODE_START_LOCATION(in_node->pattern);
7944
7945 if (branch_id) {
7946 PUSH_INSN(body_seq, in_location, putnil);
7947 }
7948
7949 LABEL *body_label = NEW_LABEL(in_location.line);
7950 PUSH_LABEL(body_seq, body_label);
7951 PUSH_INSN1(body_seq, in_location, adjuststack, INT2FIX(in_single_pattern ? 6 : 2));
7952
7953 // Establish branch coverage for the in clause.
7954 if (PM_BRANCH_COVERAGE_P(iseq)) {
7955 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));
7956 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "in", branches);
7957 }
7958
7959 if (in_node->statements != NULL) {
7960 PM_COMPILE_INTO_ANCHOR(body_seq, (const pm_node_t *) in_node->statements);
7961 }
7962 else if (!popped) {
7963 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
7964 }
7965
7966 PUSH_INSNL(body_seq, in_location, jump, end_label);
7967 LABEL *next_pattern_label = NEW_LABEL(pattern_location.line);
7968
7969 PUSH_INSN(cond_seq, pattern_location, dup);
7970 pm_compile_pattern(iseq, scope_node, in_node->pattern, cond_seq, body_label, next_pattern_label, in_single_pattern, true, 2);
7971 PUSH_LABEL(cond_seq, next_pattern_label);
7972 LABEL_UNREMOVABLE(next_pattern_label);
7973 }
7974
7975 if (node->else_clause != NULL) {
7976 // If we have an `else` clause, then this becomes our fallback (and
7977 // there is no need to compile in code to potentially raise an
7978 // error).
7979 const pm_else_node_t *else_node = node->else_clause;
7980
7981 PUSH_LABEL(cond_seq, else_label);
7982 PUSH_INSN(cond_seq, *location, pop);
7983 PUSH_INSN(cond_seq, *location, pop);
7984
7985 // Establish branch coverage for the else clause.
7986 if (PM_BRANCH_COVERAGE_P(iseq)) {
7987 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));
7988 add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
7989 }
7990
7991 PM_COMPILE_INTO_ANCHOR(cond_seq, (const pm_node_t *) else_node);
7992 PUSH_INSNL(cond_seq, *location, jump, end_label);
7993 PUSH_INSN(cond_seq, *location, putnil);
7994 if (popped) PUSH_INSN(cond_seq, *location, putnil);
7995 }
7996 else {
7997 // Otherwise, if we do not have an `else` clause, we will compile in
7998 // the code to handle raising an appropriate error.
7999 PUSH_LABEL(cond_seq, else_label);
8000
8001 // Establish branch coverage for the implicit else clause.
8002 add_trace_branch_coverage(iseq, cond_seq, &case_location, case_location.beg_pos.column, branch_id, "else", branches);
8003
8004 if (in_single_pattern) {
8005 pm_compile_pattern_error_handler(iseq, scope_node, (const pm_node_t *) node, cond_seq, end_label, popped);
8006 }
8007 else {
8008 PUSH_INSN1(cond_seq, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8009 PUSH_INSN1(cond_seq, *location, putobject, rb_eNoMatchingPatternError);
8010 PUSH_INSN1(cond_seq, *location, topn, INT2FIX(2));
8011 PUSH_SEND(cond_seq, *location, id_core_raise, INT2FIX(2));
8012
8013 PUSH_INSN1(cond_seq, *location, adjuststack, INT2FIX(3));
8014 if (!popped) PUSH_INSN(cond_seq, *location, putnil);
8015 PUSH_INSNL(cond_seq, *location, jump, end_label);
8016 PUSH_INSN1(cond_seq, *location, dupn, INT2FIX(1));
8017 if (popped) PUSH_INSN(cond_seq, *location, putnil);
8018 }
8019 }
8020
8021 // At the end of all of this compilation, we will add the code for the
8022 // conditions first, then the various bodies, then mark the end of the
8023 // entire sequence with the end label.
8024 PUSH_SEQ(ret, cond_seq);
8025 PUSH_SEQ(ret, body_seq);
8026 PUSH_LABEL(ret, end_label);
8027}
8028
8029static inline void
8030pm_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)
8031{
8032 const rb_iseq_t *block = NULL;
8033 const rb_iseq_t *previous_block = NULL;
8034 LABEL *retry_label = NULL;
8035 LABEL *retry_end_l = NULL;
8036
8037 if (node->block != NULL) {
8038 previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
8039 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
8040
8041 retry_label = NEW_LABEL(location->line);
8042 retry_end_l = NEW_LABEL(location->line);
8043
8044 PUSH_LABEL(ret, retry_label);
8045 }
8046 else {
8047 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
8048 }
8049
8050 PUSH_INSN(ret, *location, putself);
8051 int flag = VM_CALL_ZSUPER | VM_CALL_SUPER | VM_CALL_FCALL;
8052
8053 if (node->block != NULL) {
8054 pm_scope_node_t next_scope_node;
8055 pm_scope_node_init((const pm_node_t *) node->block, &next_scope_node, scope_node);
8056
8057 ISEQ_COMPILE_DATA(iseq)->current_block = block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location->line);
8058 pm_scope_node_destroy(&next_scope_node);
8059 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) block);
8060 }
8061
8062 DECL_ANCHOR(args);
8063
8064 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
8065 const rb_iseq_t *local_iseq = body->local_iseq;
8066 const struct rb_iseq_constant_body *const local_body = ISEQ_BODY(local_iseq);
8067
8068 int argc = 0;
8069 int depth = get_lvar_level(iseq);
8070
8071 if (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
8072 flag |= VM_CALL_FORWARDING;
8073 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_DOT3, 0);
8074 PUSH_GETLOCAL(ret, *location, mult_local.index, mult_local.level);
8075
8076 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, 0, flag, NULL, block != NULL);
8077 PUSH_INSN2(ret, *location, invokesuperforward, callinfo, block);
8078
8079 if (popped) PUSH_INSN(ret, *location, pop);
8080 if (node->block) {
8081 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
8082 }
8083 return;
8084 }
8085
8086 if (local_body->param.flags.has_lead) {
8087 /* required arguments */
8088 for (int i = 0; i < local_body->param.lead_num; i++) {
8089 int idx = local_body->local_table_size - i;
8090 PUSH_GETLOCAL(args, *location, idx, depth);
8091 }
8092 argc += local_body->param.lead_num;
8093 }
8094
8095 if (local_body->param.flags.has_opt) {
8096 /* optional arguments */
8097 for (int j = 0; j < local_body->param.opt_num; j++) {
8098 int idx = local_body->local_table_size - (argc + j);
8099 PUSH_GETLOCAL(args, *location, idx, depth);
8100 }
8101 argc += local_body->param.opt_num;
8102 }
8103
8104 if (local_body->param.flags.has_rest) {
8105 /* rest argument */
8106 int idx = local_body->local_table_size - local_body->param.rest_start;
8107 PUSH_GETLOCAL(args, *location, idx, depth);
8108 PUSH_INSN1(args, *location, splatarray, Qfalse);
8109
8110 argc = local_body->param.rest_start + 1;
8111 flag |= VM_CALL_ARGS_SPLAT;
8112 }
8113
8114 if (local_body->param.flags.has_post) {
8115 /* post arguments */
8116 int post_len = local_body->param.post_num;
8117 int post_start = local_body->param.post_start;
8118
8119 int j = 0;
8120 for (; j < post_len; j++) {
8121 int idx = local_body->local_table_size - (post_start + j);
8122 PUSH_GETLOCAL(args, *location, idx, depth);
8123 }
8124
8125 if (local_body->param.flags.has_rest) {
8126 // argc remains unchanged from rest branch
8127 PUSH_INSN1(args, *location, newarray, INT2FIX(j));
8128 PUSH_INSN(args, *location, concatarray);
8129 }
8130 else {
8131 argc = post_len + post_start;
8132 }
8133 }
8134
8135 const struct rb_iseq_param_keyword *const local_keyword = local_body->param.keyword;
8136 if (local_body->param.flags.has_kw) {
8137 int local_size = local_body->local_table_size;
8138 argc++;
8139
8140 PUSH_INSN1(args, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8141
8142 if (local_body->param.flags.has_kwrest) {
8143 int idx = local_body->local_table_size - local_keyword->rest_start;
8144 PUSH_GETLOCAL(args, *location, idx, depth);
8145 RUBY_ASSERT(local_keyword->num > 0);
8146 PUSH_SEND(args, *location, rb_intern("dup"), INT2FIX(0));
8147 }
8148 else {
8149 PUSH_INSN1(args, *location, newhash, INT2FIX(0));
8150 }
8151 int i = 0;
8152 for (; i < local_keyword->num; ++i) {
8153 ID id = local_keyword->table[i];
8154 int idx = local_size - get_local_var_idx(local_iseq, id);
8155
8156 {
8157 VALUE operand = ID2SYM(id);
8158 PUSH_INSN1(args, *location, putobject, operand);
8159 }
8160
8161 PUSH_GETLOCAL(args, *location, idx, depth);
8162 }
8163
8164 PUSH_SEND(args, *location, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
8165 flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT;
8166 }
8167 else if (local_body->param.flags.has_kwrest) {
8168 int idx = local_body->local_table_size - local_keyword->rest_start;
8169 PUSH_GETLOCAL(args, *location, idx, depth);
8170 argc++;
8171 flag |= VM_CALL_KW_SPLAT;
8172 }
8173
8174 PUSH_SEQ(ret, args);
8175
8176 {
8177 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flag, NULL, block != NULL);
8178 PUSH_INSN2(ret, *location, invokesuper, callinfo, block);
8179 }
8180
8181 if (node->block != NULL) {
8182 pm_compile_retry_end_label(iseq, ret, retry_end_l);
8183 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, block, retry_end_l);
8184 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
8185 }
8186
8187 if (popped) PUSH_INSN(ret, *location, pop);
8188}
8189
8190static inline void
8191pm_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)
8192{
8193 LABEL *matched_label = NEW_LABEL(location->line);
8194 LABEL *unmatched_label = NEW_LABEL(location->line);
8195 LABEL *done_label = NEW_LABEL(location->line);
8196
8197 // First, we're going to push a bunch of stuff onto the stack that is
8198 // going to serve as our scratch space.
8199 PUSH_INSN(ret, *location, putnil); // key error key
8200 PUSH_INSN(ret, *location, putnil); // key error matchee
8201 PUSH_INSN1(ret, *location, putobject, Qfalse); // key error?
8202 PUSH_INSN(ret, *location, putnil); // error string
8203 PUSH_INSN(ret, *location, putnil); // deconstruct cache
8204
8205 // Next we're going to compile the value expression such that it's on
8206 // the stack.
8207 PM_COMPILE_NOT_POPPED(node->value);
8208
8209 // Here we'll dup it so that it can be used for comparison, but also be
8210 // used for error handling.
8211 PUSH_INSN(ret, *location, dup);
8212
8213 // Next we'll compile the pattern. We indicate to the pm_compile_pattern
8214 // function that this is the only pattern that will be matched against
8215 // through the in_single_pattern parameter. We also indicate that the
8216 // value to compare against is 2 slots from the top of the stack (the
8217 // base_index parameter).
8218 pm_compile_pattern(iseq, scope_node, node->pattern, ret, matched_label, unmatched_label, true, true, 2);
8219
8220 // If the pattern did not match the value, then we're going to compile
8221 // in our error handler code. This will determine which error to raise
8222 // and raise it.
8223 PUSH_LABEL(ret, unmatched_label);
8224 pm_compile_pattern_error_handler(iseq, scope_node, (const pm_node_t *) node, ret, done_label, popped);
8225
8226 // If the pattern did match, we'll clean up the values we've pushed onto
8227 // the stack and then push nil onto the stack if it's not popped.
8228 PUSH_LABEL(ret, matched_label);
8229 PUSH_INSN1(ret, *location, adjuststack, INT2FIX(6));
8230 if (!popped) PUSH_INSN(ret, *location, putnil);
8231 PUSH_INSNL(ret, *location, jump, done_label);
8232
8233 PUSH_LABEL(ret, done_label);
8234}
8235
8236static inline void
8237pm_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)
8238{
8239 LABEL *fail_label = NEW_LABEL(location->line);
8240 LABEL *end_label = NEW_LABEL(location->line);
8241
8242 // First, we'll compile the call so that all of its instructions are
8243 // present. Then we'll compile all of the local variable targets.
8244 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->call);
8245
8246 // Now, check if the match was successful. If it was, then we'll
8247 // continue on and assign local variables. Otherwise we'll skip over the
8248 // assignment code.
8249 {
8250 VALUE operand = rb_id2sym(idBACKREF);
8251 PUSH_INSN1(ret, *location, getglobal, operand);
8252 }
8253
8254 PUSH_INSN(ret, *location, dup);
8255 PUSH_INSNL(ret, *location, branchunless, fail_label);
8256
8257 // If there's only a single local variable target, we can skip some of
8258 // the bookkeeping, so we'll put a special branch here.
8259 size_t targets_count = node->targets.size;
8260
8261 if (targets_count == 1) {
8262 const pm_node_t *target = node->targets.nodes[0];
8263 RUBY_ASSERT(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
8264
8265 const pm_local_variable_target_node_t *local_target = (const pm_local_variable_target_node_t *) target;
8266 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, local_target->name, local_target->depth);
8267
8268 {
8269 VALUE operand = rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name));
8270 PUSH_INSN1(ret, *location, putobject, operand);
8271 }
8272
8273 PUSH_SEND(ret, *location, idAREF, INT2FIX(1));
8274 PUSH_LABEL(ret, fail_label);
8275 PUSH_SETLOCAL(ret, *location, index.index, index.level);
8276 if (popped) PUSH_INSN(ret, *location, pop);
8277 return;
8278 }
8279
8280 DECL_ANCHOR(fail_anchor);
8281
8282 // Otherwise there is more than one local variable target, so we'll need
8283 // to do some bookkeeping.
8284 for (size_t targets_index = 0; targets_index < targets_count; targets_index++) {
8285 const pm_node_t *target = node->targets.nodes[targets_index];
8286 RUBY_ASSERT(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
8287
8288 const pm_local_variable_target_node_t *local_target = (const pm_local_variable_target_node_t *) target;
8289 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, local_target->name, local_target->depth);
8290
8291 if (((size_t) targets_index) < (targets_count - 1)) {
8292 PUSH_INSN(ret, *location, dup);
8293 }
8294
8295 {
8296 VALUE operand = rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name));
8297 PUSH_INSN1(ret, *location, putobject, operand);
8298 }
8299
8300 PUSH_SEND(ret, *location, idAREF, INT2FIX(1));
8301 PUSH_SETLOCAL(ret, *location, index.index, index.level);
8302
8303 PUSH_INSN(fail_anchor, *location, putnil);
8304 PUSH_SETLOCAL(fail_anchor, *location, index.index, index.level);
8305 }
8306
8307 // Since we matched successfully, now we'll jump to the end.
8308 PUSH_INSNL(ret, *location, jump, end_label);
8309
8310 // In the case that the match failed, we'll loop through each local
8311 // variable target and set all of them to `nil`.
8312 PUSH_LABEL(ret, fail_label);
8313 PUSH_INSN(ret, *location, pop);
8314 PUSH_SEQ(ret, fail_anchor);
8315
8316 // Finally, we can push the end label for either case.
8317 PUSH_LABEL(ret, end_label);
8318 if (popped) PUSH_INSN(ret, *location, pop);
8319}
8320
8321static inline void
8322pm_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)
8323{
8324 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
8325 LABEL *splabel = NEW_LABEL(0);
8326 PUSH_LABEL(ret, splabel);
8327
8328 if (node->arguments) {
8329 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8330 }
8331 else {
8332 PUSH_INSN(ret, *location, putnil);
8333 }
8334 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8335
8336 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
8337 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8338
8339 PUSH_ADJUST_RESTORE(ret, splabel);
8340 if (!popped) PUSH_INSN(ret, *location, putnil);
8341 }
8342 else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
8343 LABEL *splabel = NEW_LABEL(0);
8344
8345 PUSH_LABEL(ret, splabel);
8346 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->start_label);
8347
8348 if (node->arguments != NULL) {
8349 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8350 }
8351 else {
8352 PUSH_INSN(ret, *location, putnil);
8353 }
8354
8355 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8356 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
8357 PUSH_ADJUST_RESTORE(ret, splabel);
8358 splabel->unremovable = FALSE;
8359
8360 if (!popped) PUSH_INSN(ret, *location, putnil);
8361 }
8362 else {
8363 const rb_iseq_t *ip = iseq;
8364 unsigned long throw_flag = 0;
8365
8366 while (ip) {
8367 if (!ISEQ_COMPILE_DATA(ip)) {
8368 ip = 0;
8369 break;
8370 }
8371
8372 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
8373 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8374 /* while loop */
8375 break;
8376 }
8377 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8378 break;
8379 }
8380 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8381 COMPILE_ERROR(iseq, location->line, "Invalid next");
8382 return;
8383 }
8384
8385 ip = ISEQ_BODY(ip)->parent_iseq;
8386 }
8387
8388 if (ip != 0) {
8389 if (node->arguments) {
8390 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8391 }
8392 else {
8393 PUSH_INSN(ret, *location, putnil);
8394 }
8395
8396 PUSH_INSN1(ret, *location, throw, INT2FIX(throw_flag | TAG_NEXT));
8397 if (popped) PUSH_INSN(ret, *location, pop);
8398 }
8399 else {
8400 COMPILE_ERROR(iseq, location->line, "Invalid next");
8401 }
8402 }
8403}
8404
8405static inline void
8406pm_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)
8407{
8408 if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
8409 LABEL *splabel = NEW_LABEL(0);
8410
8411 PUSH_LABEL(ret, splabel);
8412 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
8413 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8414
8415 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
8416 PUSH_ADJUST_RESTORE(ret, splabel);
8417 if (!popped) PUSH_INSN(ret, *location, putnil);
8418 }
8419 else if (ISEQ_BODY(iseq)->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
8420 LABEL *splabel = NEW_LABEL(0);
8421
8422 PUSH_LABEL(ret, splabel);
8423 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8424 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->start_label);
8425
8426 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8427 PUSH_ADJUST_RESTORE(ret, splabel);
8428 if (!popped) PUSH_INSN(ret, *location, putnil);
8429 }
8430 else {
8431 const rb_iseq_t *ip = iseq;
8432
8433 while (ip) {
8434 if (!ISEQ_COMPILE_DATA(ip)) {
8435 ip = 0;
8436 break;
8437 }
8438
8439 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8440 break;
8441 }
8442 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8443 break;
8444 }
8445 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8446 COMPILE_ERROR(iseq, location->line, "Invalid redo");
8447 return;
8448 }
8449
8450 ip = ISEQ_BODY(ip)->parent_iseq;
8451 }
8452
8453 if (ip != 0) {
8454 PUSH_INSN(ret, *location, putnil);
8455 PUSH_INSN1(ret, *location, throw, INT2FIX(VM_THROW_NO_ESCAPE_FLAG | TAG_REDO));
8456 if (popped) PUSH_INSN(ret, *location, pop);
8457 }
8458 else {
8459 COMPILE_ERROR(iseq, location->line, "Invalid redo");
8460 }
8461 }
8462}
8463
8464static inline void
8465pm_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)
8466{
8467 iseq_set_exception_local_table(iseq);
8468
8469 // First, establish the labels that we need to be able to jump to within
8470 // this compilation block.
8471 LABEL *exception_match_label = NEW_LABEL(location->line);
8472 LABEL *rescue_end_label = NEW_LABEL(location->line);
8473
8474 // Next, compile each of the exceptions that we're going to be
8475 // handling. For each one, we'll add instructions to check if the
8476 // exception matches the raised one, and if it does then jump to the
8477 // exception_match_label label. Otherwise it will fall through to the
8478 // subsequent check. If there are no exceptions, we'll only check
8479 // StandardError.
8480 const pm_node_list_t *exceptions = &node->exceptions;
8481
8482 if (exceptions->size > 0) {
8483 for (size_t index = 0; index < exceptions->size; index++) {
8484 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8485 PM_COMPILE(exceptions->nodes[index]);
8486 int checkmatch_flags = VM_CHECKMATCH_TYPE_RESCUE;
8487 if (PM_NODE_TYPE_P(exceptions->nodes[index], PM_SPLAT_NODE)) {
8488 checkmatch_flags |= VM_CHECKMATCH_ARRAY;
8489 }
8490 PUSH_INSN1(ret, *location, checkmatch, INT2FIX(checkmatch_flags));
8491 PUSH_INSNL(ret, *location, branchif, exception_match_label);
8492 }
8493 }
8494 else {
8495 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8496 PUSH_INSN1(ret, *location, putobject, rb_eStandardError);
8497 PUSH_INSN1(ret, *location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
8498 PUSH_INSNL(ret, *location, branchif, exception_match_label);
8499 }
8500
8501 // If none of the exceptions that we are matching against matched, then
8502 // we'll jump straight to the rescue_end_label label.
8503 PUSH_INSNL(ret, *location, jump, rescue_end_label);
8504
8505 // Here we have the exception_match_label, which is where the
8506 // control-flow goes in the case that one of the exceptions matched.
8507 // Here we will compile the instructions to handle the exception.
8508 PUSH_LABEL(ret, exception_match_label);
8509 PUSH_TRACE(ret, RUBY_EVENT_RESCUE);
8510
8511 // If we have a reference to the exception, then we'll compile the write
8512 // into the instruction sequence. This can look quite different
8513 // depending on the kind of write being performed.
8514 if (node->reference) {
8515 DECL_ANCHOR(writes);
8516 DECL_ANCHOR(cleanup);
8517
8518 pm_compile_target_node(iseq, node->reference, ret, writes, cleanup, scope_node, NULL);
8519 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8520
8521 PUSH_SEQ(ret, writes);
8522 PUSH_SEQ(ret, cleanup);
8523 }
8524
8525 // If we have statements to execute, we'll compile them here. Otherwise
8526 // we'll push nil onto the stack.
8527 if (node->statements != NULL) {
8528 // We'll temporarily remove the end_label location from the iseq
8529 // when compiling the statements so that next/redo statements
8530 // inside the body will throw to the correct place instead of
8531 // jumping straight to the end of this iseq
8532 LABEL *prev_end = ISEQ_COMPILE_DATA(iseq)->end_label;
8533 ISEQ_COMPILE_DATA(iseq)->end_label = NULL;
8534
8535 PM_COMPILE((const pm_node_t *) node->statements);
8536
8537 // Now restore the end_label
8538 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end;
8539 }
8540 else {
8541 PUSH_INSN(ret, *location, putnil);
8542 }
8543
8544 PUSH_INSN(ret, *location, leave);
8545
8546 // Here we'll insert the rescue_end_label label, which is jumped to if
8547 // none of the exceptions matched. It will cause the control-flow to
8548 // either jump to the next rescue clause or it will fall through to the
8549 // subsequent instruction returning the raised error.
8550 PUSH_LABEL(ret, rescue_end_label);
8551 if (node->subsequent != NULL) {
8552 PM_COMPILE((const pm_node_t *) node->subsequent);
8553 }
8554 else {
8555 PUSH_GETLOCAL(ret, *location, 1, 0);
8556 }
8557}
8558
8559static inline void
8560pm_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)
8561{
8562 const pm_arguments_node_t *arguments = node->arguments;
8563 enum rb_iseq_type type = ISEQ_BODY(iseq)->type;
8564 LABEL *splabel = 0;
8565
8566 const rb_iseq_t *parent_iseq = iseq;
8567 enum rb_iseq_type parent_type = ISEQ_BODY(parent_iseq)->type;
8568 while (parent_type == ISEQ_TYPE_RESCUE || parent_type == ISEQ_TYPE_ENSURE) {
8569 if (!(parent_iseq = ISEQ_BODY(parent_iseq)->parent_iseq)) break;
8570 parent_type = ISEQ_BODY(parent_iseq)->type;
8571 }
8572
8573 switch (parent_type) {
8574 case ISEQ_TYPE_TOP:
8575 case ISEQ_TYPE_MAIN:
8576 if (arguments) {
8577 rb_warn("argument of top-level return is ignored");
8578 }
8579 if (parent_iseq == iseq) {
8580 type = ISEQ_TYPE_METHOD;
8581 }
8582 break;
8583 default:
8584 break;
8585 }
8586
8587 if (type == ISEQ_TYPE_METHOD) {
8588 splabel = NEW_LABEL(0);
8589 PUSH_LABEL(ret, splabel);
8590 PUSH_ADJUST(ret, *location, 0);
8591 }
8592
8593 if (arguments != NULL) {
8594 PM_COMPILE_NOT_POPPED((const pm_node_t *) arguments);
8595 }
8596 else {
8597 PUSH_INSN(ret, *location, putnil);
8598 }
8599
8600 if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
8601 pm_add_ensure_iseq(ret, iseq, 1, scope_node);
8602 PUSH_TRACE(ret, RUBY_EVENT_RETURN);
8603 PUSH_INSN(ret, *location, leave);
8604 PUSH_ADJUST_RESTORE(ret, splabel);
8605 if (!popped) PUSH_INSN(ret, *location, putnil);
8606 }
8607 else {
8608 PUSH_INSN1(ret, *location, throw, INT2FIX(TAG_RETURN));
8609 if (popped) PUSH_INSN(ret, *location, pop);
8610 }
8611}
8612
8613static inline void
8614pm_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)
8615{
8616 DECL_ANCHOR(args);
8617
8618 LABEL *retry_label = NEW_LABEL(location->line);
8619 LABEL *retry_end_l = NEW_LABEL(location->line);
8620
8621 const rb_iseq_t *previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
8622 const rb_iseq_t *current_block;
8623 ISEQ_COMPILE_DATA(iseq)->current_block = current_block = NULL;
8624
8625 PUSH_LABEL(ret, retry_label);
8626 PUSH_INSN(ret, *location, putself);
8627
8628 int flags = 0;
8629 struct rb_callinfo_kwarg *keywords = NULL;
8630 int argc = pm_setup_args(node->arguments, node->block, &flags, &keywords, iseq, ret, scope_node, location);
8631 bool is_forwardable = (node->arguments != NULL) && PM_NODE_FLAG_P(node->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING);
8632 flags |= VM_CALL_SUPER | VM_CALL_FCALL;
8633
8634 if (node->block && PM_NODE_TYPE_P(node->block, PM_BLOCK_NODE)) {
8635 pm_scope_node_t next_scope_node;
8636 pm_scope_node_init(node->block, &next_scope_node, scope_node);
8637
8638 ISEQ_COMPILE_DATA(iseq)->current_block = current_block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location->line);
8639 pm_scope_node_destroy(&next_scope_node);
8640 }
8641
8642 if (!node->block) {
8643 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
8644 }
8645
8646 if ((flags & VM_CALL_ARGS_BLOCKARG) && (flags & VM_CALL_KW_SPLAT) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
8647 PUSH_INSN(args, *location, splatkw);
8648 }
8649
8650 PUSH_SEQ(ret, args);
8651 if (is_forwardable && ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
8652 flags |= VM_CALL_FORWARDING;
8653
8654 {
8655 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, current_block != NULL);
8656 PUSH_INSN2(ret, *location, invokesuperforward, callinfo, current_block);
8657 }
8658 }
8659 else {
8660 {
8661 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, current_block != NULL);
8662 PUSH_INSN2(ret, *location, invokesuper, callinfo, current_block);
8663 }
8664
8665 }
8666
8667 pm_compile_retry_end_label(iseq, ret, retry_end_l);
8668
8669 if (popped) PUSH_INSN(ret, *location, pop);
8670 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
8671 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, current_block, retry_end_l);
8672}
8673
8674static inline void
8675pm_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)
8676{
8677 switch (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->type) {
8678 case ISEQ_TYPE_TOP:
8679 case ISEQ_TYPE_MAIN:
8680 case ISEQ_TYPE_CLASS:
8681 COMPILE_ERROR(iseq, location->line, "Invalid yield");
8682 return;
8683 default: /* valid */;
8684 }
8685
8686 int argc = 0;
8687 int flags = 0;
8688 struct rb_callinfo_kwarg *keywords = NULL;
8689
8690 if (node->arguments) {
8691 argc = pm_setup_args(node->arguments, NULL, &flags, &keywords, iseq, ret, scope_node, location);
8692 }
8693
8694 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, FALSE);
8695 PUSH_INSN1(ret, *location, invokeblock, callinfo);
8696
8697 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
8698 if (popped) PUSH_INSN(ret, *location, pop);
8699
8700 int level = 0;
8701 for (const rb_iseq_t *tmp_iseq = iseq; tmp_iseq != ISEQ_BODY(iseq)->local_iseq; level++) {
8702 tmp_iseq = ISEQ_BODY(tmp_iseq)->parent_iseq;
8703 }
8704
8705 if (level > 0) access_outer_variables(iseq, level, rb_intern("yield"), true);
8706}
8707
8718static void
8719pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8720{
8721 const pm_node_location_t location = PM_NODE_START_LOCATION(node);
8722 int lineno = (int) location.line;
8723
8724 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)) {
8725 // If this node is a begin node and it has empty statements and also
8726 // has a rescue clause, then the other parser considers it as
8727 // starting on the same line as the rescue, as opposed to the
8728 // location of the begin keyword. We replicate that behavior here.
8729 lineno = (int) PM_NODE_START_LINE_COLUMN(((const pm_begin_node_t *) node)->rescue_clause).line;
8730 }
8731
8732 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_NEWLINE) && ISEQ_COMPILE_DATA(iseq)->last_line != lineno) {
8733 // If this node has the newline flag set and it is on a new line
8734 // from the previous nodes that have been compiled for this ISEQ,
8735 // then we need to emit a newline event.
8736 int event = RUBY_EVENT_LINE;
8737
8738 ISEQ_COMPILE_DATA(iseq)->last_line = lineno;
8739 if (lineno > 0 && ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
8740 event |= RUBY_EVENT_COVERAGE_LINE;
8741 }
8742 PUSH_TRACE(ret, event);
8743 }
8744
8745 switch (PM_NODE_TYPE(node)) {
8746 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
8747 // alias $foo $bar
8748 // ^^^^^^^^^^^^^^^
8749 pm_compile_alias_global_variable_node(iseq, (const pm_alias_global_variable_node_t *) node, &location, ret, popped, scope_node);
8750 return;
8751 case PM_ALIAS_METHOD_NODE:
8752 // alias foo bar
8753 // ^^^^^^^^^^^^^
8754 pm_compile_alias_method_node(iseq, (const pm_alias_method_node_t *) node, &location, ret, popped, scope_node);
8755 return;
8756 case PM_AND_NODE:
8757 // a and b
8758 // ^^^^^^^
8759 pm_compile_and_node(iseq, (const pm_and_node_t *) node, &location, ret, popped, scope_node);
8760 return;
8761 case PM_ARGUMENTS_NODE: {
8762 // break foo
8763 // ^^^
8764 //
8765 // These are ArgumentsNodes that are not compiled directly by their
8766 // parent call nodes, used in the cases of NextNodes, ReturnNodes, and
8767 // BreakNodes. They can create an array like ArrayNode.
8768 const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node;
8769 const pm_node_list_t *elements = &cast->arguments;
8770
8771 if (elements->size == 1) {
8772 // If we are only returning a single element through one of the jump
8773 // nodes, then we will only compile that node directly.
8774 PM_COMPILE(elements->nodes[0]);
8775 }
8776 else {
8777 pm_compile_array_node(iseq, (const pm_node_t *) cast, elements, &location, ret, popped, scope_node);
8778 }
8779 return;
8780 }
8781 case PM_ARRAY_NODE: {
8782 // [foo, bar, baz]
8783 // ^^^^^^^^^^^^^^^
8784 const pm_array_node_t *cast = (const pm_array_node_t *) node;
8785 pm_compile_array_node(iseq, (const pm_node_t *) cast, &cast->elements, &location, ret, popped, scope_node);
8786 return;
8787 }
8788 case PM_ASSOC_NODE: {
8789 // { foo: 1 }
8790 // ^^^^^^
8791 //
8792 // foo(bar: 1)
8793 // ^^^^^^
8794 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node;
8795
8796 PM_COMPILE(cast->key);
8797 PM_COMPILE(cast->value);
8798
8799 return;
8800 }
8801 case PM_ASSOC_SPLAT_NODE: {
8802 // { **foo }
8803 // ^^^^^
8804 //
8805 // def foo(**); bar(**); end
8806 // ^^
8807 const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node;
8808
8809 if (cast->value != NULL) {
8810 PM_COMPILE(cast->value);
8811 }
8812 else if (!popped) {
8813 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_POW, 0);
8814 PUSH_GETLOCAL(ret, location, index.index, index.level);
8815 }
8816
8817 return;
8818 }
8819 case PM_BACK_REFERENCE_READ_NODE: {
8820 // $+
8821 // ^^
8822 if (!popped) {
8824 VALUE backref = pm_compile_back_reference_ref(scope_node, cast);
8825
8826 PUSH_INSN2(ret, location, getspecial, INT2FIX(1), backref);
8827 }
8828 return;
8829 }
8830 case PM_BEGIN_NODE: {
8831 // begin end
8832 // ^^^^^^^^^
8833 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
8834
8835 if (cast->ensure_clause) {
8836 // Compiling the ensure clause will compile the rescue clause (if
8837 // there is one), which will compile the begin statements.
8838 pm_compile_ensure(iseq, cast, &location, ret, popped, scope_node);
8839 }
8840 else if (cast->rescue_clause) {
8841 // Compiling rescue will compile begin statements (if applicable).
8842 pm_compile_rescue(iseq, cast, &location, ret, popped, scope_node);
8843 }
8844 else {
8845 // If there is neither ensure or rescue, the just compile the
8846 // statements.
8847 if (cast->statements != NULL) {
8848 PM_COMPILE((const pm_node_t *) cast->statements);
8849 }
8850 else if (!popped) {
8851 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
8852 }
8853 }
8854 return;
8855 }
8856 case PM_BLOCK_ARGUMENT_NODE: {
8857 // foo(&bar)
8858 // ^^^^
8859 const pm_block_argument_node_t *cast = (const pm_block_argument_node_t *) node;
8860
8861 if (cast->expression != NULL) {
8862 PM_COMPILE(cast->expression);
8863 }
8864 else {
8865 // If there's no expression, this must be block forwarding.
8866 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_AND, 0);
8867 PUSH_INSN2(ret, location, getblockparamproxy, INT2FIX(local_index.index + VM_ENV_DATA_SIZE - 1), INT2FIX(local_index.level));
8868 }
8869 return;
8870 }
8871 case PM_BREAK_NODE:
8872 // break
8873 // ^^^^^
8874 //
8875 // break foo
8876 // ^^^^^^^^^
8877 pm_compile_break_node(iseq, (const pm_break_node_t *) node, &location, ret, popped, scope_node);
8878 return;
8879 case PM_CALL_NODE:
8880 // foo
8881 // ^^^
8882 //
8883 // foo.bar
8884 // ^^^^^^^
8885 //
8886 // foo.bar() {}
8887 // ^^^^^^^^^^^^
8888 pm_compile_call_node(iseq, (const pm_call_node_t *) node, ret, popped, scope_node);
8889 return;
8890 case PM_CALL_AND_WRITE_NODE: {
8891 // foo.bar &&= baz
8892 // ^^^^^^^^^^^^^^^
8893 const pm_call_and_write_node_t *cast = (const pm_call_and_write_node_t *) node;
8894 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);
8895 return;
8896 }
8897 case PM_CALL_OR_WRITE_NODE: {
8898 // foo.bar ||= baz
8899 // ^^^^^^^^^^^^^^^
8900 const pm_call_or_write_node_t *cast = (const pm_call_or_write_node_t *) node;
8901 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);
8902 return;
8903 }
8904 case PM_CALL_OPERATOR_WRITE_NODE:
8905 // foo.bar += baz
8906 // ^^^^^^^^^^^^^^^
8907 //
8908 // Call operator writes occur when you have a call node on the left-hand
8909 // side of a write operator that is not `=`. As an example,
8910 // `foo.bar *= 1`. This breaks down to caching the receiver on the
8911 // stack and then performing three method calls, one to read the value,
8912 // one to compute the result, and one to write the result back to the
8913 // receiver.
8914 pm_compile_call_operator_write_node(iseq, (const pm_call_operator_write_node_t *) node, &location, ret, popped, scope_node);
8915 return;
8916 case PM_CASE_NODE:
8917 // case foo; when bar; end
8918 // ^^^^^^^^^^^^^^^^^^^^^^^
8919 pm_compile_case_node(iseq, (const pm_case_node_t *) node, &location, ret, popped, scope_node);
8920 return;
8921 case PM_CASE_MATCH_NODE:
8922 // case foo; in bar; end
8923 // ^^^^^^^^^^^^^^^^^^^^^
8924 //
8925 // If you use the `case` keyword to create a case match node, it will
8926 // match against all of the `in` clauses until it finds one that
8927 // matches. If it doesn't find one, it can optionally fall back to an
8928 // `else` clause. If none is present and a match wasn't found, it will
8929 // raise an appropriate error.
8930 pm_compile_case_match_node(iseq, (const pm_case_match_node_t *) node, &location, ret, popped, scope_node);
8931 return;
8932 case PM_CLASS_NODE: {
8933 // class Foo; end
8934 // ^^^^^^^^^^^^^^
8935 const pm_class_node_t *cast = (const pm_class_node_t *) node;
8936
8937 ID class_id = pm_constant_id_lookup(scope_node, cast->name);
8938 VALUE class_name = rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(class_id)));
8939
8940 pm_scope_node_t next_scope_node;
8941 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
8942
8943 const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(&next_scope_node, class_name, ISEQ_TYPE_CLASS, location.line);
8944 pm_scope_node_destroy(&next_scope_node);
8945
8946 // TODO: Once we merge constant path nodes correctly, fix this flag
8947 const int flags = VM_DEFINECLASS_TYPE_CLASS |
8948 (cast->superclass ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
8949 pm_compile_class_path(iseq, cast->constant_path, &location, ret, false, scope_node);
8950
8951 if (cast->superclass) {
8952 PM_COMPILE_NOT_POPPED(cast->superclass);
8953 }
8954 else {
8955 PUSH_INSN(ret, location, putnil);
8956 }
8957
8958 {
8959 VALUE operand = ID2SYM(class_id);
8960 PUSH_INSN3(ret, location, defineclass, operand, class_iseq, INT2FIX(flags));
8961 }
8962 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq);
8963
8964 if (popped) PUSH_INSN(ret, location, pop);
8965 return;
8966 }
8967 case PM_CLASS_VARIABLE_AND_WRITE_NODE: {
8968 // @@foo &&= bar
8969 // ^^^^^^^^^^^^^
8971 LABEL *end_label = NEW_LABEL(location.line);
8972
8973 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
8974 VALUE name = ID2SYM(name_id);
8975
8976 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
8977 if (!popped) PUSH_INSN(ret, location, dup);
8978
8979 PUSH_INSNL(ret, location, branchunless, end_label);
8980 if (!popped) PUSH_INSN(ret, location, pop);
8981
8982 PM_COMPILE_NOT_POPPED(cast->value);
8983 if (!popped) PUSH_INSN(ret, location, dup);
8984
8985 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
8986 PUSH_LABEL(ret, end_label);
8987
8988 return;
8989 }
8990 case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: {
8991 // @@foo += bar
8992 // ^^^^^^^^^^^^
8994
8995 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
8996 VALUE name = ID2SYM(name_id);
8997
8998 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
8999 PM_COMPILE_NOT_POPPED(cast->value);
9000
9001 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9002 int flags = VM_CALL_ARGS_SIMPLE;
9003 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
9004
9005 if (!popped) PUSH_INSN(ret, location, dup);
9006 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
9007
9008 return;
9009 }
9010 case PM_CLASS_VARIABLE_OR_WRITE_NODE: {
9011 // @@foo ||= bar
9012 // ^^^^^^^^^^^^^
9014 LABEL *end_label = NEW_LABEL(location.line);
9015 LABEL *start_label = NEW_LABEL(location.line);
9016
9017 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9018 VALUE name = ID2SYM(name_id);
9019
9020 PUSH_INSN(ret, location, putnil);
9021 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CVAR), name, Qtrue);
9022 PUSH_INSNL(ret, location, branchunless, start_label);
9023
9024 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
9025 if (!popped) PUSH_INSN(ret, location, dup);
9026
9027 PUSH_INSNL(ret, location, branchif, end_label);
9028 if (!popped) PUSH_INSN(ret, location, pop);
9029
9030 PUSH_LABEL(ret, start_label);
9031 PM_COMPILE_NOT_POPPED(cast->value);
9032 if (!popped) PUSH_INSN(ret, location, dup);
9033
9034 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
9035 PUSH_LABEL(ret, end_label);
9036
9037 return;
9038 }
9039 case PM_CLASS_VARIABLE_READ_NODE: {
9040 // @@foo
9041 // ^^^^^
9042 if (!popped) {
9044 ID name = pm_constant_id_lookup(scope_node, cast->name);
9045 PUSH_INSN2(ret, location, getclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
9046 }
9047 return;
9048 }
9049 case PM_CLASS_VARIABLE_WRITE_NODE: {
9050 // @@foo = 1
9051 // ^^^^^^^^^
9053 PM_COMPILE_NOT_POPPED(cast->value);
9054 if (!popped) PUSH_INSN(ret, location, dup);
9055
9056 ID name = pm_constant_id_lookup(scope_node, cast->name);
9057 PUSH_INSN2(ret, location, setclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
9058
9059 return;
9060 }
9061 case PM_CONSTANT_PATH_NODE: {
9062 // Foo::Bar
9063 // ^^^^^^^^
9064 VALUE parts;
9065
9066 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache && ((parts = pm_constant_path_parts(node, scope_node)) != Qnil)) {
9067 ISEQ_BODY(iseq)->ic_size++;
9068 RB_OBJ_SET_SHAREABLE(parts);
9069 PUSH_INSN1(ret, location, opt_getconstant_path, parts);
9070 }
9071 else {
9072 DECL_ANCHOR(prefix);
9073 DECL_ANCHOR(body);
9074
9075 pm_compile_constant_path(iseq, node, prefix, body, popped, scope_node);
9076 if (LIST_INSN_SIZE_ZERO(prefix)) {
9077 PUSH_INSN(ret, location, putnil);
9078 }
9079 else {
9080 PUSH_SEQ(ret, prefix);
9081 }
9082
9083 PUSH_SEQ(ret, body);
9084 }
9085
9086 if (popped) PUSH_INSN(ret, location, pop);
9087 return;
9088 }
9089 case PM_CONSTANT_PATH_AND_WRITE_NODE: {
9090 // Foo::Bar &&= baz
9091 // ^^^^^^^^^^^^^^^^
9093 pm_compile_constant_path_and_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9094 return;
9095 }
9096 case PM_CONSTANT_PATH_OR_WRITE_NODE: {
9097 // Foo::Bar ||= baz
9098 // ^^^^^^^^^^^^^^^^
9100 pm_compile_constant_path_or_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9101 return;
9102 }
9103 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: {
9104 // Foo::Bar += baz
9105 // ^^^^^^^^^^^^^^^
9107 pm_compile_constant_path_operator_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9108 return;
9109 }
9110 case PM_CONSTANT_PATH_WRITE_NODE: {
9111 // Foo::Bar = 1
9112 // ^^^^^^^^^^^^
9114 pm_compile_constant_path_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9115 return;
9116 }
9117 case PM_CONSTANT_READ_NODE: {
9118 // Foo
9119 // ^^^
9120 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
9121 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9122
9123 pm_compile_constant_read(iseq, name, &cast->base.location, location.node_id, ret, scope_node);
9124 if (popped) PUSH_INSN(ret, location, pop);
9125
9126 return;
9127 }
9128 case PM_CONSTANT_AND_WRITE_NODE: {
9129 // Foo &&= bar
9130 // ^^^^^^^^^^^
9132 pm_compile_constant_and_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9133 return;
9134 }
9135 case PM_CONSTANT_OR_WRITE_NODE: {
9136 // Foo ||= bar
9137 // ^^^^^^^^^^^
9138 const pm_constant_or_write_node_t *cast = (const pm_constant_or_write_node_t *) node;
9139 pm_compile_constant_or_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9140 return;
9141 }
9142 case PM_CONSTANT_OPERATOR_WRITE_NODE: {
9143 // Foo += bar
9144 // ^^^^^^^^^^
9146 pm_compile_constant_operator_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9147 return;
9148 }
9149 case PM_CONSTANT_WRITE_NODE: {
9150 // Foo = 1
9151 // ^^^^^^^
9152 const pm_constant_write_node_t *cast = (const pm_constant_write_node_t *) node;
9153 pm_compile_constant_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9154 return;
9155 }
9156 case PM_DEF_NODE: {
9157 // def foo; end
9158 // ^^^^^^^^^^^^
9159 //
9160 // def self.foo; end
9161 // ^^^^^^^^^^^^^^^^^
9162 const pm_def_node_t *cast = (const pm_def_node_t *) node;
9163 ID method_name = pm_constant_id_lookup(scope_node, cast->name);
9164
9165 pm_scope_node_t next_scope_node;
9166 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9167
9168 rb_iseq_t *method_iseq = NEW_ISEQ(&next_scope_node, rb_id2str(method_name), ISEQ_TYPE_METHOD, location.line);
9169 pm_scope_node_destroy(&next_scope_node);
9170
9171 if (cast->receiver) {
9172 PM_COMPILE_NOT_POPPED(cast->receiver);
9173 PUSH_INSN2(ret, location, definesmethod, ID2SYM(method_name), method_iseq);
9174 }
9175 else {
9176 PUSH_INSN2(ret, location, definemethod, ID2SYM(method_name), method_iseq);
9177 }
9178 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) method_iseq);
9179
9180 if (!popped) {
9181 PUSH_INSN1(ret, location, putobject, ID2SYM(method_name));
9182 }
9183
9184 return;
9185 }
9186 case PM_DEFINED_NODE: {
9187 // defined?(a)
9188 // ^^^^^^^^^^^
9189 const pm_defined_node_t *cast = (const pm_defined_node_t *) node;
9190 pm_compile_defined_expr(iseq, cast->value, &location, ret, popped, scope_node, false);
9191 return;
9192 }
9193 case PM_EMBEDDED_STATEMENTS_NODE: {
9194 // "foo #{bar}"
9195 // ^^^^^^
9197
9198 if (cast->statements != NULL) {
9199 PM_COMPILE((const pm_node_t *) (cast->statements));
9200 }
9201 else {
9202 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
9203 }
9204
9205 if (popped) PUSH_INSN(ret, location, pop);
9206 return;
9207 }
9208 case PM_EMBEDDED_VARIABLE_NODE: {
9209 // "foo #@bar"
9210 // ^^^^^
9211 const pm_embedded_variable_node_t *cast = (const pm_embedded_variable_node_t *) node;
9212 PM_COMPILE(cast->variable);
9213 return;
9214 }
9215 case PM_FALSE_NODE: {
9216 // false
9217 // ^^^^^
9218 if (!popped) {
9219 PUSH_INSN1(ret, location, putobject, Qfalse);
9220 }
9221 return;
9222 }
9223 case PM_ENSURE_NODE: {
9224 const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node;
9225
9226 if (cast->statements != NULL) {
9227 PM_COMPILE((const pm_node_t *) cast->statements);
9228 }
9229
9230 return;
9231 }
9232 case PM_ELSE_NODE: {
9233 // if foo then bar else baz end
9234 // ^^^^^^^^^^^^
9235 const pm_else_node_t *cast = (const pm_else_node_t *) node;
9236
9237 if (cast->statements != NULL) {
9238 PM_COMPILE((const pm_node_t *) cast->statements);
9239 }
9240 else if (!popped) {
9241 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
9242 }
9243
9244 return;
9245 }
9246 case PM_FLIP_FLOP_NODE: {
9247 // if foo .. bar; end
9248 // ^^^^^^^^^^
9249 const pm_flip_flop_node_t *cast = (const pm_flip_flop_node_t *) node;
9250
9251 LABEL *final_label = NEW_LABEL(location.line);
9252 LABEL *then_label = NEW_LABEL(location.line);
9253 LABEL *else_label = NEW_LABEL(location.line);
9254
9255 pm_compile_flip_flop(cast, else_label, then_label, iseq, location.line, ret, popped, scope_node);
9256
9257 PUSH_LABEL(ret, then_label);
9258 PUSH_INSN1(ret, location, putobject, Qtrue);
9259 PUSH_INSNL(ret, location, jump, final_label);
9260 PUSH_LABEL(ret, else_label);
9261 PUSH_INSN1(ret, location, putobject, Qfalse);
9262 PUSH_LABEL(ret, final_label);
9263
9264 return;
9265 }
9266 case PM_FLOAT_NODE: {
9267 // 1.0
9268 // ^^^
9269 if (!popped) {
9270 VALUE operand = parse_float((const pm_float_node_t *) node);
9271 PUSH_INSN1(ret, location, putobject, operand);
9272 }
9273 return;
9274 }
9275 case PM_FOR_NODE: {
9276 // for foo in bar do end
9277 // ^^^^^^^^^^^^^^^^^^^^^
9278 const pm_for_node_t *cast = (const pm_for_node_t *) node;
9279
9280 LABEL *retry_label = NEW_LABEL(location.line);
9281 LABEL *retry_end_l = NEW_LABEL(location.line);
9282
9283 // First, compile the collection that we're going to be iterating over.
9284 PUSH_LABEL(ret, retry_label);
9285 PM_COMPILE_NOT_POPPED(cast->collection);
9286
9287 // Next, create the new scope that is going to contain the block that
9288 // will be passed to the each method.
9289 pm_scope_node_t next_scope_node;
9290 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9291
9292 const rb_iseq_t *child_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location.line);
9293 pm_scope_node_destroy(&next_scope_node);
9294
9295 const rb_iseq_t *prev_block = ISEQ_COMPILE_DATA(iseq)->current_block;
9296 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
9297
9298 // Now, create the method call to each that will be used to iterate over
9299 // the collection, and pass the newly created iseq as the block.
9300 PUSH_SEND_WITH_BLOCK(ret, location, idEach, INT2FIX(0), child_iseq);
9301 pm_compile_retry_end_label(iseq, ret, retry_end_l);
9302
9303 if (popped) PUSH_INSN(ret, location, pop);
9304 ISEQ_COMPILE_DATA(iseq)->current_block = prev_block;
9305 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
9306 return;
9307 }
9308 case PM_FORWARDING_ARGUMENTS_NODE:
9309 rb_bug("Cannot compile a ForwardingArgumentsNode directly\n");
9310 return;
9311 case PM_FORWARDING_SUPER_NODE:
9312 // super
9313 // ^^^^^
9314 //
9315 // super {}
9316 // ^^^^^^^^
9317 pm_compile_forwarding_super_node(iseq, (const pm_forwarding_super_node_t *) node, &location, ret, popped, scope_node);
9318 return;
9319 case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: {
9320 // $foo &&= bar
9321 // ^^^^^^^^^^^^
9323 LABEL *end_label = NEW_LABEL(location.line);
9324
9325 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9326 PUSH_INSN1(ret, location, getglobal, name);
9327 if (!popped) PUSH_INSN(ret, location, dup);
9328
9329 PUSH_INSNL(ret, location, branchunless, end_label);
9330 if (!popped) PUSH_INSN(ret, location, pop);
9331
9332 PM_COMPILE_NOT_POPPED(cast->value);
9333 if (!popped) PUSH_INSN(ret, location, dup);
9334
9335 PUSH_INSN1(ret, location, setglobal, name);
9336 PUSH_LABEL(ret, end_label);
9337
9338 return;
9339 }
9340 case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: {
9341 // $foo += bar
9342 // ^^^^^^^^^^^
9344
9345 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9346 PUSH_INSN1(ret, location, getglobal, name);
9347 PM_COMPILE_NOT_POPPED(cast->value);
9348
9349 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9350 int flags = VM_CALL_ARGS_SIMPLE;
9351 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
9352
9353 if (!popped) PUSH_INSN(ret, location, dup);
9354 PUSH_INSN1(ret, location, setglobal, name);
9355
9356 return;
9357 }
9358 case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: {
9359 // $foo ||= bar
9360 // ^^^^^^^^^^^^
9362 LABEL *set_label = NEW_LABEL(location.line);
9363 LABEL *end_label = NEW_LABEL(location.line);
9364
9365 PUSH_INSN(ret, location, putnil);
9366 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9367
9368 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_GVAR), name, Qtrue);
9369 PUSH_INSNL(ret, location, branchunless, set_label);
9370
9371 PUSH_INSN1(ret, location, getglobal, name);
9372 if (!popped) PUSH_INSN(ret, location, dup);
9373
9374 PUSH_INSNL(ret, location, branchif, end_label);
9375 if (!popped) PUSH_INSN(ret, location, pop);
9376
9377 PUSH_LABEL(ret, set_label);
9378 PM_COMPILE_NOT_POPPED(cast->value);
9379 if (!popped) PUSH_INSN(ret, location, dup);
9380
9381 PUSH_INSN1(ret, location, setglobal, name);
9382 PUSH_LABEL(ret, end_label);
9383
9384 return;
9385 }
9386 case PM_GLOBAL_VARIABLE_READ_NODE: {
9387 // $foo
9388 // ^^^^
9390 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9391
9392 PUSH_INSN1(ret, location, getglobal, name);
9393 if (popped) PUSH_INSN(ret, location, pop);
9394
9395 return;
9396 }
9397 case PM_GLOBAL_VARIABLE_WRITE_NODE: {
9398 // $foo = 1
9399 // ^^^^^^^^
9401 PM_COMPILE_NOT_POPPED(cast->value);
9402 if (!popped) PUSH_INSN(ret, location, dup);
9403
9404 ID name = pm_constant_id_lookup(scope_node, cast->name);
9405 PUSH_INSN1(ret, location, setglobal, ID2SYM(name));
9406
9407 return;
9408 }
9409 case PM_HASH_NODE: {
9410 // {}
9411 // ^^
9412 //
9413 // If every node in the hash is static, then we can compile the entire
9414 // hash now instead of later.
9415 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9416 // We're only going to compile this node if it's not popped. If it
9417 // is popped, then we know we don't need to do anything since it's
9418 // statically known.
9419 if (!popped) {
9420 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
9421
9422 if (cast->elements.size == 0) {
9423 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
9424 }
9425 else {
9426 VALUE value = pm_static_literal_value(iseq, node, scope_node);
9427 PUSH_INSN1(ret, location, duphash, value);
9428 RB_OBJ_WRITTEN(iseq, Qundef, value);
9429 }
9430 }
9431 }
9432 else {
9433 // Here since we know there are possible side-effects inside the
9434 // hash contents, we're going to build it entirely at runtime. We'll
9435 // do this by pushing all of the key-value pairs onto the stack and
9436 // then combining them with newhash.
9437 //
9438 // If this hash is popped, then this serves only to ensure we enact
9439 // all side-effects (like method calls) that are contained within
9440 // the hash contents.
9441 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
9442 const pm_node_list_t *elements = &cast->elements;
9443
9444 if (popped) {
9445 // If this hash is popped, then we can iterate through each
9446 // element and compile it. The result of each compilation will
9447 // only include the side effects of the element itself.
9448 for (size_t index = 0; index < elements->size; index++) {
9449 PM_COMPILE_POPPED(elements->nodes[index]);
9450 }
9451 }
9452 else {
9453 pm_compile_hash_elements(iseq, node, elements, 0, Qundef, false, ret, scope_node);
9454 }
9455 }
9456
9457 return;
9458 }
9459 case PM_IF_NODE: {
9460 // if foo then bar end
9461 // ^^^^^^^^^^^^^^^^^^^
9462 //
9463 // bar if foo
9464 // ^^^^^^^^^^
9465 //
9466 // foo ? bar : baz
9467 // ^^^^^^^^^^^^^^^
9468 const pm_if_node_t *cast = (const pm_if_node_t *) node;
9469 pm_compile_conditional(iseq, &location, PM_IF_NODE, (const pm_node_t *) cast, cast->statements, cast->subsequent, cast->predicate, ret, popped, scope_node);
9470 return;
9471 }
9472 case PM_IMAGINARY_NODE: {
9473 // 1i
9474 // ^^
9475 if (!popped) {
9476 VALUE operand = parse_imaginary((const pm_imaginary_node_t *) node);
9477 PUSH_INSN1(ret, location, putobject, operand);
9478 }
9479 return;
9480 }
9481 case PM_IMPLICIT_NODE: {
9482 // Implicit nodes mark places in the syntax tree where explicit syntax
9483 // was omitted, but implied. For example,
9484 //
9485 // { foo: }
9486 //
9487 // In this case a method call/local variable read is implied by virtue
9488 // of the missing value. To compile these nodes, we simply compile the
9489 // value that is implied, which is helpfully supplied by the parser.
9490 const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node;
9491 PM_COMPILE(cast->value);
9492 return;
9493 }
9494 case PM_IN_NODE: {
9495 // In nodes are handled by the case match node directly, so we should
9496 // never end up hitting them through this path.
9497 rb_bug("Should not ever enter an in node directly");
9498 return;
9499 }
9500 case PM_INDEX_OPERATOR_WRITE_NODE: {
9501 // foo[bar] += baz
9502 // ^^^^^^^^^^^^^^^
9504 pm_compile_index_operator_write_node(iseq, cast, &location, ret, popped, scope_node);
9505 return;
9506 }
9507 case PM_INDEX_AND_WRITE_NODE: {
9508 // foo[bar] &&= baz
9509 // ^^^^^^^^^^^^^^^^
9510 const pm_index_and_write_node_t *cast = (const pm_index_and_write_node_t *) node;
9511 pm_compile_index_control_flow_write_node(iseq, node, cast->receiver, cast->arguments, cast->block, cast->value, &location, ret, popped, scope_node);
9512 return;
9513 }
9514 case PM_INDEX_OR_WRITE_NODE: {
9515 // foo[bar] ||= baz
9516 // ^^^^^^^^^^^^^^^^
9517 const pm_index_or_write_node_t *cast = (const pm_index_or_write_node_t *) node;
9518 pm_compile_index_control_flow_write_node(iseq, node, cast->receiver, cast->arguments, cast->block, cast->value, &location, ret, popped, scope_node);
9519 return;
9520 }
9521 case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: {
9522 // @foo &&= bar
9523 // ^^^^^^^^^^^^
9525 LABEL *end_label = NEW_LABEL(location.line);
9526
9527 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9528 VALUE name = ID2SYM(name_id);
9529
9530 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9531 if (!popped) PUSH_INSN(ret, location, dup);
9532
9533 PUSH_INSNL(ret, location, branchunless, end_label);
9534 if (!popped) PUSH_INSN(ret, location, pop);
9535
9536 PM_COMPILE_NOT_POPPED(cast->value);
9537 if (!popped) PUSH_INSN(ret, location, dup);
9538
9539 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9540 PUSH_LABEL(ret, end_label);
9541
9542 return;
9543 }
9544 case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: {
9545 // @foo += bar
9546 // ^^^^^^^^^^^
9548
9549 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9550 VALUE name = ID2SYM(name_id);
9551
9552 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9553 PM_COMPILE_NOT_POPPED(cast->value);
9554
9555 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9556 int flags = VM_CALL_ARGS_SIMPLE;
9557 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
9558
9559 if (!popped) PUSH_INSN(ret, location, dup);
9560 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9561
9562 return;
9563 }
9564 case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: {
9565 // @foo ||= bar
9566 // ^^^^^^^^^^^^
9568 LABEL *end_label = NEW_LABEL(location.line);
9569
9570 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9571 VALUE name = ID2SYM(name_id);
9572
9573 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9574 if (!popped) PUSH_INSN(ret, location, dup);
9575
9576 PUSH_INSNL(ret, location, branchif, end_label);
9577 if (!popped) PUSH_INSN(ret, location, pop);
9578
9579 PM_COMPILE_NOT_POPPED(cast->value);
9580 if (!popped) PUSH_INSN(ret, location, dup);
9581
9582 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9583 PUSH_LABEL(ret, end_label);
9584
9585 return;
9586 }
9587 case PM_INSTANCE_VARIABLE_READ_NODE: {
9588 // @foo
9589 // ^^^^
9590 if (!popped) {
9592 ID name = pm_constant_id_lookup(scope_node, cast->name);
9593 PUSH_INSN2(ret, location, getinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
9594 }
9595 return;
9596 }
9597 case PM_INSTANCE_VARIABLE_WRITE_NODE: {
9598 // @foo = 1
9599 // ^^^^^^^^
9601 PM_COMPILE_NOT_POPPED(cast->value);
9602 if (!popped) PUSH_INSN(ret, location, dup);
9603
9604 ID name = pm_constant_id_lookup(scope_node, cast->name);
9605 PUSH_INSN2(ret, location, setinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
9606
9607 return;
9608 }
9609 case PM_INTEGER_NODE: {
9610 // 1
9611 // ^
9612 if (!popped) {
9613 VALUE operand = parse_integer((const pm_integer_node_t *) node);
9614 PUSH_INSN1(ret, location, putobject, operand);
9615 }
9616 return;
9617 }
9618 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: {
9619 // if /foo #{bar}/ then end
9620 // ^^^^^^^^^^^^
9621 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9622 if (!popped) {
9623 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9624 PUSH_INSN1(ret, location, putobject, regexp);
9625 }
9626 }
9627 else {
9628 pm_compile_regexp_dynamic(iseq, node, &((const pm_interpolated_match_last_line_node_t *) node)->parts, &location, ret, popped, scope_node);
9629 }
9630
9631 PUSH_INSN1(ret, location, getglobal, rb_id2sym(idLASTLINE));
9632 PUSH_SEND(ret, location, idEqTilde, INT2NUM(1));
9633 if (popped) PUSH_INSN(ret, location, pop);
9634
9635 return;
9636 }
9637 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
9638 // /foo #{bar}/
9639 // ^^^^^^^^^^^^
9640 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) {
9641 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
9642 const rb_iseq_t *block_iseq = NULL;
9643 int ise_index = ISEQ_BODY(iseq)->ise_size++;
9644
9645 pm_scope_node_t next_scope_node;
9646 pm_scope_node_init(node, &next_scope_node, scope_node);
9647
9648 block_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, location.line);
9649 pm_scope_node_destroy(&next_scope_node);
9650
9651 ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
9652 PUSH_INSN2(ret, location, once, block_iseq, INT2FIX(ise_index));
9653 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
9654
9655 if (popped) PUSH_INSN(ret, location, pop);
9656 return;
9657 }
9658
9659 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9660 if (!popped) {
9661 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9662 PUSH_INSN1(ret, location, putobject, regexp);
9663 }
9664 }
9665 else {
9666 pm_compile_regexp_dynamic(iseq, node, &((const pm_interpolated_regular_expression_node_t *) node)->parts, &location, ret, popped, scope_node);
9667 if (popped) PUSH_INSN(ret, location, pop);
9668 }
9669
9670 return;
9671 }
9672 case PM_INTERPOLATED_STRING_NODE: {
9673 // "foo #{bar}"
9674 // ^^^^^^^^^^^^
9675 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9676 if (!popped) {
9677 VALUE string = pm_static_literal_value(iseq, node, scope_node);
9678
9679 if (PM_NODE_FLAG_P(node, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN)) {
9680 PUSH_INSN1(ret, location, putobject, string);
9681 }
9682 else if (PM_NODE_FLAG_P(node, PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) {
9683 PUSH_INSN1(ret, location, dupstring, string);
9684 }
9685 else {
9686 PUSH_INSN1(ret, location, dupchilledstring, string);
9687 }
9688 }
9689 }
9690 else {
9692 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));
9693 if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9694 if (popped) PUSH_INSN(ret, location, pop);
9695 }
9696
9697 return;
9698 }
9699 case PM_INTERPOLATED_SYMBOL_NODE: {
9700 // :"foo #{bar}"
9701 // ^^^^^^^^^^^^^
9703 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL, false, false);
9704
9705 if (length > 1) {
9706 PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9707 }
9708
9709 if (!popped) {
9710 PUSH_INSN(ret, location, intern);
9711 }
9712 else {
9713 PUSH_INSN(ret, location, pop);
9714 }
9715
9716 return;
9717 }
9718 case PM_INTERPOLATED_X_STRING_NODE: {
9719 // `foo #{bar}`
9720 // ^^^^^^^^^^^^
9722
9723 PUSH_INSN(ret, location, putself);
9724
9725 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, false, scope_node, NULL, NULL, false, false);
9726 if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9727
9728 PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
9729 if (popped) PUSH_INSN(ret, location, pop);
9730
9731 return;
9732 }
9733 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
9734 // -> { it }
9735 // ^^
9736 if (!popped) {
9737 pm_scope_node_t *current_scope_node = scope_node;
9738 int level = 0;
9739
9740 while (current_scope_node) {
9741 if (current_scope_node->parameters && PM_NODE_TYPE_P(current_scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
9742 PUSH_GETLOCAL(ret, location, current_scope_node->local_table_for_iseq_size, level);
9743 return;
9744 }
9745
9746 current_scope_node = current_scope_node->previous;
9747 level++;
9748 }
9749 rb_bug("Local `it` does not exist");
9750 }
9751
9752 return;
9753 }
9754 case PM_KEYWORD_HASH_NODE: {
9755 // foo(bar: baz)
9756 // ^^^^^^^^
9757 const pm_keyword_hash_node_t *cast = (const pm_keyword_hash_node_t *) node;
9758 const pm_node_list_t *elements = &cast->elements;
9759
9760 bool has_splat = false;
9761 for (size_t index = 0; index < elements->size; index++) {
9762 if (PM_NODE_TYPE_P(elements->nodes[index], PM_ASSOC_SPLAT_NODE)) {
9763 has_splat = true;
9764 break;
9765 }
9766 }
9767
9768 if (has_splat) {
9769 pm_compile_hash_elements(iseq, node, elements, 0, Qundef, false, ret, scope_node);
9770 }
9771 else {
9772 const pm_node_t *element;
9773 PM_NODE_LIST_FOREACH(elements, index, element) {
9774 PM_COMPILE(element);
9775 }
9776
9777 if (!popped) PUSH_INSN1(ret, location, newhash, INT2FIX(elements->size * 2));
9778 }
9779 return;
9780 }
9781 case PM_LAMBDA_NODE: {
9782 // -> {}
9783 // ^^^^^
9784 const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node;
9785
9786 pm_scope_node_t next_scope_node;
9787 pm_scope_node_init(node, &next_scope_node, scope_node);
9788
9789 int opening_lineno = pm_location_line_number_cached(&cast->opening_loc, scope_node);
9790 const rb_iseq_t *block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, opening_lineno);
9791 pm_scope_node_destroy(&next_scope_node);
9792
9793 VALUE argc = INT2FIX(0);
9794 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
9795 PUSH_CALL_WITH_BLOCK(ret, location, idLambda, argc, block);
9796 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) block);
9797
9798 if (popped) PUSH_INSN(ret, location, pop);
9799 return;
9800 }
9801 case PM_LOCAL_VARIABLE_AND_WRITE_NODE: {
9802 // foo &&= bar
9803 // ^^^^^^^^^^^
9805 LABEL *end_label = NEW_LABEL(location.line);
9806
9807 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9808 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9809 if (!popped) PUSH_INSN(ret, location, dup);
9810
9811 PUSH_INSNL(ret, location, branchunless, end_label);
9812 if (!popped) PUSH_INSN(ret, location, pop);
9813
9814 PM_COMPILE_NOT_POPPED(cast->value);
9815 if (!popped) PUSH_INSN(ret, location, dup);
9816
9817 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9818 PUSH_LABEL(ret, end_label);
9819
9820 return;
9821 }
9822 case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: {
9823 // foo += bar
9824 // ^^^^^^^^^^
9826
9827 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9828 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9829
9830 PM_COMPILE_NOT_POPPED(cast->value);
9831
9832 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9833 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
9834
9835 if (!popped) PUSH_INSN(ret, location, dup);
9836 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9837
9838 return;
9839 }
9840 case PM_LOCAL_VARIABLE_OR_WRITE_NODE: {
9841 // foo ||= bar
9842 // ^^^^^^^^^^^
9844
9845 LABEL *set_label = NEW_LABEL(location.line);
9846 LABEL *end_label = NEW_LABEL(location.line);
9847
9848 PUSH_INSN1(ret, location, putobject, Qtrue);
9849 PUSH_INSNL(ret, location, branchunless, set_label);
9850
9851 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9852 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9853 if (!popped) PUSH_INSN(ret, location, dup);
9854
9855 PUSH_INSNL(ret, location, branchif, end_label);
9856 if (!popped) PUSH_INSN(ret, location, pop);
9857
9858 PUSH_LABEL(ret, set_label);
9859 PM_COMPILE_NOT_POPPED(cast->value);
9860 if (!popped) PUSH_INSN(ret, location, dup);
9861
9862 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9863 PUSH_LABEL(ret, end_label);
9864
9865 return;
9866 }
9867 case PM_LOCAL_VARIABLE_READ_NODE: {
9868 // foo
9869 // ^^^
9870 if (!popped) {
9872 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9873 PUSH_GETLOCAL(ret, location, index.index, index.level);
9874 }
9875
9876 return;
9877 }
9878 case PM_LOCAL_VARIABLE_WRITE_NODE: {
9879 // foo = 1
9880 // ^^^^^^^
9882 PM_COMPILE_NOT_POPPED(cast->value);
9883 if (!popped) PUSH_INSN(ret, location, dup);
9884
9885 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9886 PUSH_SETLOCAL(ret, location, index.index, index.level);
9887 return;
9888 }
9889 case PM_MATCH_LAST_LINE_NODE: {
9890 // if /foo/ then end
9891 // ^^^^^
9892 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9893
9894 PUSH_INSN1(ret, location, putobject, regexp);
9895 PUSH_INSN2(ret, location, getspecial, INT2FIX(0), INT2FIX(0));
9896 PUSH_SEND(ret, location, idEqTilde, INT2NUM(1));
9897 if (popped) PUSH_INSN(ret, location, pop);
9898
9899 return;
9900 }
9901 case PM_MATCH_PREDICATE_NODE: {
9902 // foo in bar
9903 // ^^^^^^^^^^
9904 const pm_match_predicate_node_t *cast = (const pm_match_predicate_node_t *) node;
9905
9906 // First, allocate some stack space for the cached return value of any
9907 // calls to #deconstruct.
9908 PUSH_INSN(ret, location, putnil);
9909
9910 // Next, compile the expression that we're going to match against.
9911 PM_COMPILE_NOT_POPPED(cast->value);
9912 PUSH_INSN(ret, location, dup);
9913
9914 // Now compile the pattern that is going to be used to match against the
9915 // expression.
9916 LABEL *matched_label = NEW_LABEL(location.line);
9917 LABEL *unmatched_label = NEW_LABEL(location.line);
9918 LABEL *done_label = NEW_LABEL(location.line);
9919 pm_compile_pattern(iseq, scope_node, cast->pattern, ret, matched_label, unmatched_label, false, true, 2);
9920
9921 // If the pattern did not match, then compile the necessary instructions
9922 // to handle pushing false onto the stack, then jump to the end.
9923 PUSH_LABEL(ret, unmatched_label);
9924 PUSH_INSN(ret, location, pop);
9925 PUSH_INSN(ret, location, pop);
9926
9927 if (!popped) PUSH_INSN1(ret, location, putobject, Qfalse);
9928 PUSH_INSNL(ret, location, jump, done_label);
9929 PUSH_INSN(ret, location, putnil);
9930
9931 // If the pattern did match, then compile the necessary instructions to
9932 // handle pushing true onto the stack, then jump to the end.
9933 PUSH_LABEL(ret, matched_label);
9934 PUSH_INSN1(ret, location, adjuststack, INT2FIX(2));
9935 if (!popped) PUSH_INSN1(ret, location, putobject, Qtrue);
9936 PUSH_INSNL(ret, location, jump, done_label);
9937
9938 PUSH_LABEL(ret, done_label);
9939 return;
9940 }
9941 case PM_MATCH_REQUIRED_NODE:
9942 // foo => bar
9943 // ^^^^^^^^^^
9944 //
9945 // A match required node represents pattern matching against a single
9946 // pattern using the => operator. For example,
9947 //
9948 // foo => bar
9949 //
9950 // This is somewhat analogous to compiling a case match statement with a
9951 // single pattern. In both cases, if the pattern fails it should
9952 // immediately raise an error.
9953 pm_compile_match_required_node(iseq, (const pm_match_required_node_t *) node, &location, ret, popped, scope_node);
9954 return;
9955 case PM_MATCH_WRITE_NODE:
9956 // /(?<foo>foo)/ =~ bar
9957 // ^^^^^^^^^^^^^^^^^^^^
9958 //
9959 // Match write nodes are specialized call nodes that have a regular
9960 // expression with valid named capture groups on the left, the =~
9961 // operator, and some value on the right. The nodes themselves simply
9962 // wrap the call with the local variable targets that will be written
9963 // when the call is executed.
9964 pm_compile_match_write_node(iseq, (const pm_match_write_node_t *) node, &location, ret, popped, scope_node);
9965 return;
9966 case PM_ERROR_RECOVERY_NODE:
9967 rb_bug("A pm_error_recovery_node_t should not exist in prism's AST.");
9968 return;
9969 case PM_MODULE_NODE: {
9970 // module Foo; end
9971 // ^^^^^^^^^^^^^^^
9972 const pm_module_node_t *cast = (const pm_module_node_t *) node;
9973
9974 ID module_id = pm_constant_id_lookup(scope_node, cast->name);
9975 VALUE module_name = rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(module_id)));
9976
9977 pm_scope_node_t next_scope_node;
9978 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9979
9980 const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(&next_scope_node, module_name, ISEQ_TYPE_CLASS, location.line);
9981 pm_scope_node_destroy(&next_scope_node);
9982
9983 const int flags = VM_DEFINECLASS_TYPE_MODULE | pm_compile_class_path(iseq, cast->constant_path, &location, ret, false, scope_node);
9984 PUSH_INSN(ret, location, putnil);
9985 PUSH_INSN3(ret, location, defineclass, ID2SYM(module_id), module_iseq, INT2FIX(flags));
9986 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) module_iseq);
9987
9988 if (popped) PUSH_INSN(ret, location, pop);
9989 return;
9990 }
9991 case PM_REQUIRED_PARAMETER_NODE: {
9992 // def foo(bar); end
9993 // ^^^
9995 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, 0);
9996
9997 PUSH_SETLOCAL(ret, location, index.index, index.level);
9998 return;
9999 }
10000 case PM_MULTI_WRITE_NODE: {
10001 // foo, bar = baz
10002 // ^^^^^^^^^^^^^^
10003 //
10004 // A multi write node represents writing to multiple values using an =
10005 // operator. Importantly these nodes are only parsed when the left-hand
10006 // side of the operator has multiple targets. The right-hand side of the
10007 // operator having multiple targets represents an implicit array
10008 // instead.
10009 const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node;
10010
10011 DECL_ANCHOR(writes);
10012 DECL_ANCHOR(cleanup);
10013
10014 pm_multi_target_state_t state = { 0 };
10015 state.position = popped ? 0 : 1;
10016 pm_compile_multi_target_node(iseq, node, ret, writes, cleanup, scope_node, &state);
10017
10018 PM_COMPILE_NOT_POPPED(cast->value);
10019 if (!popped) PUSH_INSN(ret, location, dup);
10020
10021 PUSH_SEQ(ret, writes);
10022 if (!popped && state.stack_size >= 1) {
10023 // Make sure the value on the right-hand side of the = operator is
10024 // being returned before we pop the parent expressions.
10025 PUSH_INSN1(ret, location, setn, INT2FIX(state.stack_size));
10026 }
10027
10028 // Now, we need to go back and modify the topn instructions in order to
10029 // ensure they can correctly retrieve the parent expressions.
10030 pm_multi_target_state_update(&state);
10031
10032 PUSH_SEQ(ret, cleanup);
10033 return;
10034 }
10035 case PM_NEXT_NODE:
10036 // next
10037 // ^^^^
10038 //
10039 // next foo
10040 // ^^^^^^^^
10041 pm_compile_next_node(iseq, (const pm_next_node_t *) node, &location, ret, popped, scope_node);
10042 return;
10043 case PM_NIL_NODE: {
10044 // nil
10045 // ^^^
10046 if (!popped) {
10047 PUSH_INSN(ret, location, putnil);
10048 }
10049
10050 return;
10051 }
10052 case PM_NO_BLOCK_PARAMETER_NODE: {
10053 // def foo(&nil); end
10054 // ^^^^
10055 ISEQ_BODY(iseq)->param.flags.accepts_no_block = TRUE;
10056 return;
10057 }
10058 case PM_NO_KEYWORDS_PARAMETER_NODE: {
10059 // def foo(**nil); end
10060 // ^^^^^
10061 ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg = TRUE;
10062 return;
10063 }
10064 case PM_NUMBERED_REFERENCE_READ_NODE: {
10065 // $1
10066 // ^^
10067 if (!popped) {
10069
10070 if (cast->number != 0) {
10071 VALUE ref = pm_compile_numbered_reference_ref(cast);
10072 PUSH_INSN2(ret, location, getspecial, INT2FIX(1), ref);
10073 }
10074 else {
10075 PUSH_INSN(ret, location, putnil);
10076 }
10077 }
10078
10079 return;
10080 }
10081 case PM_OR_NODE: {
10082 // a or b
10083 // ^^^^^^
10084 const pm_or_node_t *cast = (const pm_or_node_t *) node;
10085
10086 LABEL *end_label = NEW_LABEL(location.line);
10087 PM_COMPILE_NOT_POPPED(cast->left);
10088
10089 if (!popped) PUSH_INSN(ret, location, dup);
10090 PUSH_INSNL(ret, location, branchif, end_label);
10091
10092 if (!popped) PUSH_INSN(ret, location, pop);
10093 PM_COMPILE(cast->right);
10094 PUSH_LABEL(ret, end_label);
10095
10096 return;
10097 }
10098 case PM_OPTIONAL_PARAMETER_NODE: {
10099 // def foo(bar = 1); end
10100 // ^^^^^^^
10102 PM_COMPILE_NOT_POPPED(cast->value);
10103
10104 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, 0);
10105 PUSH_SETLOCAL(ret, location, index.index, index.level);
10106
10107 return;
10108 }
10109 case PM_PARENTHESES_NODE: {
10110 // ()
10111 // ^^
10112 //
10113 // (1)
10114 // ^^^
10115 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
10116
10117 if (cast->body != NULL) {
10118 PM_COMPILE(cast->body);
10119 }
10120 else if (!popped) {
10121 PUSH_INSN(ret, location, putnil);
10122 }
10123
10124 return;
10125 }
10126 case PM_PRE_EXECUTION_NODE: {
10127 // BEGIN {}
10128 // ^^^^^^^^
10129 const pm_pre_execution_node_t *cast = (const pm_pre_execution_node_t *) node;
10130
10131 LINK_ANCHOR *outer_pre = scope_node->pre_execution_anchor;
10132 RUBY_ASSERT(outer_pre != NULL);
10133
10134 // BEGIN{} nodes can be nested, so here we're going to do the same thing
10135 // that we did for the top-level compilation where we create two
10136 // anchors and then join them in the correct order into the resulting
10137 // anchor.
10138 DECL_ANCHOR(inner_pre);
10139 scope_node->pre_execution_anchor = inner_pre;
10140
10141 DECL_ANCHOR(inner_body);
10142
10143 if (cast->statements != NULL) {
10144 const pm_node_list_t *body = &cast->statements->body;
10145
10146 for (size_t index = 0; index < body->size; index++) {
10147 pm_compile_node(iseq, body->nodes[index], inner_body, true, scope_node);
10148 }
10149 }
10150
10151 if (!popped) {
10152 PUSH_INSN(inner_body, location, putnil);
10153 }
10154
10155 // Now that everything has been compiled, join both anchors together
10156 // into the correct outer pre execution anchor, and reset the value so
10157 // that subsequent BEGIN{} nodes can be compiled correctly.
10158 PUSH_SEQ(outer_pre, inner_pre);
10159 PUSH_SEQ(outer_pre, inner_body);
10160 scope_node->pre_execution_anchor = outer_pre;
10161
10162 return;
10163 }
10164 case PM_POST_EXECUTION_NODE: {
10165 // END {}
10166 // ^^^^^^
10167 const rb_iseq_t *child_iseq;
10168 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
10169
10170 pm_scope_node_t next_scope_node;
10171 pm_scope_node_init(node, &next_scope_node, scope_node);
10172 child_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, lineno);
10173 pm_scope_node_destroy(&next_scope_node);
10174
10175 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
10176
10177 int is_index = ISEQ_BODY(iseq)->ise_size++;
10178 PUSH_INSN2(ret, location, once, child_iseq, INT2FIX(is_index));
10179 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) child_iseq);
10180 if (popped) PUSH_INSN(ret, location, pop);
10181
10182 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
10183
10184 return;
10185 }
10186 case PM_RANGE_NODE: {
10187 // 0..5
10188 // ^^^^
10189 const pm_range_node_t *cast = (const pm_range_node_t *) node;
10190 bool exclude_end = PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END);
10191
10192 if (pm_optimizable_range_item_p(cast->left) && pm_optimizable_range_item_p(cast->right)) {
10193 if (!popped) {
10194 const pm_node_t *left = cast->left;
10195 const pm_node_t *right = cast->right;
10196
10197 VALUE val = rb_range_new(
10198 (left && PM_NODE_TYPE_P(left, PM_INTEGER_NODE)) ? parse_integer((const pm_integer_node_t *) left) : Qnil,
10199 (right && PM_NODE_TYPE_P(right, PM_INTEGER_NODE)) ? parse_integer((const pm_integer_node_t *) right) : Qnil,
10200 exclude_end
10201 );
10202
10204 PUSH_INSN1(ret, location, putobject, val);
10205 }
10206 }
10207 else {
10208 if (cast->left != NULL) {
10209 PM_COMPILE(cast->left);
10210 }
10211 else if (!popped) {
10212 PUSH_INSN(ret, location, putnil);
10213 }
10214
10215 if (cast->right != NULL) {
10216 PM_COMPILE(cast->right);
10217 }
10218 else if (!popped) {
10219 PUSH_INSN(ret, location, putnil);
10220 }
10221
10222 if (!popped) {
10223 PUSH_INSN1(ret, location, newrange, INT2FIX(exclude_end ? 1 : 0));
10224 }
10225 }
10226 return;
10227 }
10228 case PM_RATIONAL_NODE: {
10229 // 1r
10230 // ^^
10231 if (!popped) {
10232 PUSH_INSN1(ret, location, putobject, parse_rational((const pm_rational_node_t *) node));
10233 }
10234 return;
10235 }
10236 case PM_REDO_NODE:
10237 // redo
10238 // ^^^^
10239 pm_compile_redo_node(iseq, &location, ret, popped, scope_node);
10240 return;
10241 case PM_REGULAR_EXPRESSION_NODE: {
10242 // /foo/
10243 // ^^^^^
10244 if (!popped) {
10245 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
10246 PUSH_INSN1(ret, location, putobject, regexp);
10247 }
10248 return;
10249 }
10250 case PM_RESCUE_NODE:
10251 // begin; rescue; end
10252 // ^^^^^^^
10253 pm_compile_rescue_node(iseq, (const pm_rescue_node_t *) node, &location, ret, popped, scope_node);
10254 return;
10255 case PM_RESCUE_MODIFIER_NODE: {
10256 // foo rescue bar
10257 // ^^^^^^^^^^^^^^
10258 const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node;
10259
10260 pm_scope_node_t rescue_scope_node;
10261 pm_scope_node_init((const pm_node_t *) cast, &rescue_scope_node, scope_node);
10262
10263 rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(
10264 &rescue_scope_node,
10265 rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label),
10266 ISEQ_TYPE_RESCUE,
10267 pm_node_line_number_cached(cast->rescue_expression, scope_node)
10268 );
10269
10270 pm_scope_node_destroy(&rescue_scope_node);
10271
10272 LABEL *lstart = NEW_LABEL(location.line);
10273 LABEL *lend = NEW_LABEL(location.line);
10274 LABEL *lcont = NEW_LABEL(location.line);
10275
10276 lstart->rescued = LABEL_RESCUE_BEG;
10277 lend->rescued = LABEL_RESCUE_END;
10278
10279 PUSH_LABEL(ret, lstart);
10280 PM_COMPILE_NOT_POPPED(cast->expression);
10281 PUSH_LABEL(ret, lend);
10282
10283 PUSH_INSN(ret, location, nop);
10284 PUSH_LABEL(ret, lcont);
10285 if (popped) PUSH_INSN(ret, location, pop);
10286
10287 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
10288 PUSH_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
10289 return;
10290 }
10291 case PM_RETURN_NODE:
10292 // return
10293 // ^^^^^^
10294 //
10295 // return 1
10296 // ^^^^^^^^
10297 pm_compile_return_node(iseq, (const pm_return_node_t *) node, &location, ret, popped, scope_node);
10298 return;
10299 case PM_RETRY_NODE: {
10300 // retry
10301 // ^^^^^
10302 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
10303 PUSH_INSN(ret, location, putnil);
10304 PUSH_INSN1(ret, location, throw, INT2FIX(TAG_RETRY));
10305 if (popped) PUSH_INSN(ret, location, pop);
10306 }
10307 else {
10308 COMPILE_ERROR(iseq, location.line, "Invalid retry");
10309 return;
10310 }
10311 return;
10312 }
10313 case PM_SCOPE_NODE:
10314 pm_compile_scope_node(iseq, (pm_scope_node_t *) node, &location, ret, popped);
10315 return;
10316 case PM_SELF_NODE: {
10317 // self
10318 // ^^^^
10319 if (!popped) {
10320 PUSH_INSN(ret, location, putself);
10321 }
10322 return;
10323 }
10324 case PM_SHAREABLE_CONSTANT_NODE: {
10325 // A value that is being written to a constant that is being marked as
10326 // shared depending on the current lexical context.
10328 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));
10329
10330 switch (PM_NODE_TYPE(cast->write)) {
10331 case PM_CONSTANT_WRITE_NODE:
10332 pm_compile_constant_write_node(iseq, (const pm_constant_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10333 break;
10334 case PM_CONSTANT_AND_WRITE_NODE:
10335 pm_compile_constant_and_write_node(iseq, (const pm_constant_and_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10336 break;
10337 case PM_CONSTANT_OR_WRITE_NODE:
10338 pm_compile_constant_or_write_node(iseq, (const pm_constant_or_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10339 break;
10340 case PM_CONSTANT_OPERATOR_WRITE_NODE:
10341 pm_compile_constant_operator_write_node(iseq, (const pm_constant_operator_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10342 break;
10343 case PM_CONSTANT_PATH_WRITE_NODE:
10344 pm_compile_constant_path_write_node(iseq, (const pm_constant_path_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10345 break;
10346 case PM_CONSTANT_PATH_AND_WRITE_NODE:
10347 pm_compile_constant_path_and_write_node(iseq, (const pm_constant_path_and_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10348 break;
10349 case PM_CONSTANT_PATH_OR_WRITE_NODE:
10350 pm_compile_constant_path_or_write_node(iseq, (const pm_constant_path_or_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10351 break;
10352 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE:
10353 pm_compile_constant_path_operator_write_node(iseq, (const pm_constant_path_operator_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10354 break;
10355 default:
10356 rb_bug("Unexpected node type for shareable constant write: %s", pm_node_type(PM_NODE_TYPE(cast->write)));
10357 break;
10358 }
10359
10360 return;
10361 }
10362 case PM_SINGLETON_CLASS_NODE: {
10363 // class << self; end
10364 // ^^^^^^^^^^^^^^^^^^
10365 const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node;
10366
10367 pm_scope_node_t next_scope_node;
10368 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
10369 const rb_iseq_t *child_iseq = NEW_ISEQ(&next_scope_node, rb_fstring_lit("singleton class"), ISEQ_TYPE_CLASS, location.line);
10370 pm_scope_node_destroy(&next_scope_node);
10371
10372 PM_COMPILE_NOT_POPPED(cast->expression);
10373 PUSH_INSN(ret, location, putnil);
10374
10375 ID singletonclass;
10376 CONST_ID(singletonclass, "singletonclass");
10377
10378 /* `class << self` in a class body and `class << Foo` (constant
10379 receiver) are stable. All other forms are potentially dynamic. */
10380 int sclass_flags = VM_DEFINECLASS_TYPE_SINGLETON_CLASS;
10381 if (!(PM_NODE_TYPE_P(cast->expression, PM_SELF_NODE) &&
10382 ISEQ_BODY(iseq)->type == ISEQ_TYPE_CLASS) &&
10383 !pm_cpath_const_p(cast->expression)) {
10384 sclass_flags |= VM_DEFINECLASS_FLAG_DYNAMIC_CREF;
10385 }
10386
10387 PUSH_INSN3(ret, location, defineclass, ID2SYM(singletonclass), child_iseq, INT2FIX(sclass_flags));
10388
10389 if (popped) PUSH_INSN(ret, location, pop);
10390 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) child_iseq);
10391
10392 return;
10393 }
10394 case PM_SOURCE_ENCODING_NODE: {
10395 // __ENCODING__
10396 // ^^^^^^^^^^^^
10397 if (!popped) {
10398 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10399 PUSH_INSN1(ret, location, putobject, value);
10400 }
10401 return;
10402 }
10403 case PM_SOURCE_FILE_NODE: {
10404 // __FILE__
10405 // ^^^^^^^^
10406 if (!popped) {
10407 const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node;
10408 VALUE string = pm_source_file_value(cast, scope_node);
10409
10410 if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FROZEN)) {
10411 PUSH_INSN1(ret, location, putobject, string);
10412 }
10413 else if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_MUTABLE)) {
10414 PUSH_INSN1(ret, location, dupstring, string);
10415 }
10416 else {
10417 PUSH_INSN1(ret, location, dupchilledstring, string);
10418 }
10419 }
10420 return;
10421 }
10422 case PM_SOURCE_LINE_NODE: {
10423 // __LINE__
10424 // ^^^^^^^^
10425 if (!popped) {
10426 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10427 PUSH_INSN1(ret, location, putobject, value);
10428 }
10429 return;
10430 }
10431 case PM_SPLAT_NODE: {
10432 // foo(*bar)
10433 // ^^^^
10434 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
10435 if (cast->expression) {
10436 PM_COMPILE(cast->expression);
10437 }
10438
10439 if (!popped) {
10440 PUSH_INSN1(ret, location, splatarray, Qtrue);
10441 }
10442 return;
10443 }
10444 case PM_STATEMENTS_NODE: {
10445 // A list of statements.
10446 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
10447 const pm_node_list_t *body = &cast->body;
10448
10449 if (body->size > 0) {
10450 for (size_t index = 0; index < body->size - 1; index++) {
10451 PM_COMPILE_POPPED(body->nodes[index]);
10452 }
10453 PM_COMPILE(body->nodes[body->size - 1]);
10454 }
10455 else {
10456 PUSH_INSN(ret, location, putnil);
10457 }
10458 return;
10459 }
10460 case PM_STRING_NODE: {
10461 // "foo"
10462 // ^^^^^
10463 if (!popped) {
10464 const pm_string_node_t *cast = (const pm_string_node_t *) node;
10465 VALUE value = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
10466
10467 if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_FROZEN)) {
10468 PUSH_INSN1(ret, location, putobject, value);
10469 }
10470 else if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_MUTABLE)) {
10471 PUSH_INSN1(ret, location, dupstring, value);
10472 }
10473 else {
10474 PUSH_INSN1(ret, location, dupchilledstring, value);
10475 }
10476 }
10477 return;
10478 }
10479 case PM_SUPER_NODE:
10480 // super()
10481 // super(foo)
10482 // super(...)
10483 pm_compile_super_node(iseq, (const pm_super_node_t *) node, &location, ret, popped, scope_node);
10484 return;
10485 case PM_SYMBOL_NODE: {
10486 // :foo
10487 // ^^^^
10488 if (!popped) {
10489 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10490 PUSH_INSN1(ret, location, putobject, value);
10491 }
10492 return;
10493 }
10494 case PM_TRUE_NODE: {
10495 // true
10496 // ^^^^
10497 if (!popped) {
10498 PUSH_INSN1(ret, location, putobject, Qtrue);
10499 }
10500 return;
10501 }
10502 case PM_UNDEF_NODE: {
10503 // undef foo
10504 // ^^^^^^^^^
10505 const pm_undef_node_t *cast = (const pm_undef_node_t *) node;
10506 const pm_node_list_t *names = &cast->names;
10507
10508 for (size_t index = 0; index < names->size; index++) {
10509 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10510 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
10511
10512 PM_COMPILE_NOT_POPPED(names->nodes[index]);
10513 PUSH_SEND(ret, location, id_core_undef_method, INT2NUM(2));
10514
10515 if (index < names->size - 1) {
10516 PUSH_INSN(ret, location, pop);
10517 }
10518 }
10519
10520 if (popped) PUSH_INSN(ret, location, pop);
10521 return;
10522 }
10523 case PM_UNLESS_NODE: {
10524 // unless foo; bar end
10525 // ^^^^^^^^^^^^^^^^^^^
10526 //
10527 // bar unless foo
10528 // ^^^^^^^^^^^^^^
10529 const pm_unless_node_t *cast = (const pm_unless_node_t *) node;
10530 const pm_statements_node_t *statements = NULL;
10531 if (cast->else_clause != NULL) {
10532 statements = ((const pm_else_node_t *) cast->else_clause)->statements;
10533 }
10534
10535 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);
10536 return;
10537 }
10538 case PM_UNTIL_NODE: {
10539 // until foo; bar end
10540 // ^^^^^^^^^^^^^^^^^
10541 //
10542 // bar until foo
10543 // ^^^^^^^^^^^^^
10544 const pm_until_node_t *cast = (const pm_until_node_t *) node;
10545 pm_compile_loop(iseq, &location, cast->base.flags, PM_UNTIL_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node);
10546 return;
10547 }
10548 case PM_WHILE_NODE: {
10549 // while foo; bar end
10550 // ^^^^^^^^^^^^^^^^^^
10551 //
10552 // bar while foo
10553 // ^^^^^^^^^^^^^
10554 const pm_while_node_t *cast = (const pm_while_node_t *) node;
10555 pm_compile_loop(iseq, &location, cast->base.flags, PM_WHILE_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node);
10556 return;
10557 }
10558 case PM_X_STRING_NODE: {
10559 // `foo`
10560 // ^^^^^
10561 const pm_x_string_node_t *cast = (const pm_x_string_node_t *) node;
10562 VALUE value = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
10563
10564 PUSH_INSN(ret, location, putself);
10565 PUSH_INSN1(ret, location, putobject, value);
10566 PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
10567 if (popped) PUSH_INSN(ret, location, pop);
10568
10569 return;
10570 }
10571 case PM_YIELD_NODE:
10572 // yield
10573 // ^^^^^
10574 //
10575 // yield 1
10576 // ^^^^^^^
10577 pm_compile_yield_node(iseq, (const pm_yield_node_t *) node, &location, ret, popped, scope_node);
10578 return;
10579 default:
10580 rb_raise(rb_eNotImpError, "node type %s not implemented", pm_node_type(PM_NODE_TYPE(node)));
10581 return;
10582 }
10583}
10584
10585#undef PM_CONTAINER_P
10586
10588static inline bool
10589pm_iseq_pre_execution_p(rb_iseq_t *iseq)
10590{
10591 switch (ISEQ_BODY(iseq)->type) {
10592 case ISEQ_TYPE_TOP:
10593 case ISEQ_TYPE_EVAL:
10594 case ISEQ_TYPE_MAIN:
10595 return true;
10596 default:
10597 return false;
10598 }
10599}
10600
10608VALUE
10609pm_iseq_compile_node(rb_iseq_t *iseq, pm_scope_node_t *node)
10610{
10611 DECL_ANCHOR(ret);
10612
10613 if (pm_iseq_pre_execution_p(iseq)) {
10614 // Because these ISEQs can have BEGIN{}, we're going to create two
10615 // anchors to compile them, a "pre" and a "body". We'll mark the "pre"
10616 // on the scope node so that when BEGIN{} is found, its contents will be
10617 // added to the "pre" anchor.
10618 DECL_ANCHOR(pre);
10619 node->pre_execution_anchor = pre;
10620
10621 // Now we'll compile the body as normal. We won't compile directly into
10622 // the "ret" anchor yet because we want to add the "pre" anchor to the
10623 // beginning of the "ret" anchor first.
10624 DECL_ANCHOR(body);
10625 pm_compile_node(iseq, (const pm_node_t *) node, body, false, node);
10626
10627 // Now we'll join both anchors together so that the content is in the
10628 // correct order.
10629 PUSH_SEQ(ret, pre);
10630 PUSH_SEQ(ret, body);
10631 }
10632 else {
10633 // In other circumstances, we can just compile the node directly into
10634 // the "ret" anchor.
10635 pm_compile_node(iseq, (const pm_node_t *) node, ret, false, node);
10636 }
10637
10638 CHECK(iseq_setup_insn(iseq, ret));
10639 return iseq_setup(iseq, ret);
10640}
10641
10642void
10643pm_parse_result_init(pm_parse_result_t *result)
10644{
10645 memset(result, 0, sizeof(pm_parse_result_t));
10646 result->arena = pm_arena_new();
10647 result->options = pm_options_new();
10648 pm_options_line_set(result->options, 1);
10649}
10650
10655void
10656pm_parse_result_free(pm_parse_result_t *result)
10657{
10658 if (result->parsed) {
10659 SIZED_FREE_N(result->node.constants, pm_parser_constants_size(result->node.parser));
10660 pm_scope_node_destroy(&result->node);
10661 }
10662
10663 if (result->parser) pm_parser_free(result->parser);
10664 pm_arena_free(result->arena);
10665 if (result->source) pm_source_free(result->source);
10666 pm_options_free(result->options);
10667}
10668
10673static VALUE
10674pm_parse_process_error(const pm_parse_result_t *result)
10675{
10676 const pm_parser_t *parser = result->parser;
10677 pm_buffer_t *buffer = pm_buffer_new();
10678 if (buffer == NULL) {
10679 return rb_exc_new_cstr(rb_eNoMemError, "failed to allocate memory");
10680 }
10681
10682 pm_errors_format_type_t format_type;
10683 if (rb_stderr_tty_p()) {
10684 const char *no_color = getenv("NO_COLOR");
10685 if (no_color == NULL || no_color[0] == '\0') {
10686 format_type = PM_ERRORS_FORMAT_COLOR;
10687 } else {
10688 format_type = PM_ERRORS_FORMAT_STYLE;
10689 }
10690 } else {
10691 format_type = PM_ERRORS_FORMAT_PLAIN;
10692 }
10693
10694 pm_error_level_t error_level = pm_errors_format(parser, buffer, format_type);
10695
10696 rb_encoding *message_encoding = (error_level == PM_ERROR_LEVEL_LOAD) ? rb_locale_encoding() : rb_enc_find(pm_parser_encoding_name(parser));
10697 VALUE message = rb_enc_str_new(pm_buffer_value(buffer), (long) pm_buffer_length(buffer), message_encoding);
10698 pm_buffer_free(buffer);
10699
10700 VALUE error = Qnil;
10701 switch (error_level) {
10702 case PM_ERROR_LEVEL_SYNTAX: {
10703 error = rb_exc_new_str(rb_eSyntaxError, message);
10704
10705 const pm_string_t *filepath = pm_parser_filepath(result->parser);
10706 rb_encoding *filepath_encoding = result->node.filepath_encoding != NULL ? result->node.filepath_encoding : rb_utf8_encoding();
10707
10708 VALUE path = rb_enc_str_new((const char *) pm_string_source(filepath), pm_string_length(filepath), filepath_encoding);
10709 rb_ivar_set(error, rb_intern_const("@path"), path);
10710 break;
10711 }
10713 error = rb_exc_new_str(rb_eArgError, message);
10714 break;
10716 error = rb_exc_new_str(rb_eLoadError, message);
10717 rb_ivar_set(error, rb_intern_const("@path"), Qnil);
10718 break;
10719 }
10720
10721 return error;
10722}
10723
10725typedef struct {
10726 ID *constants;
10727 rb_encoding *encoding;
10728 size_t index;
10730
10731static void
10732pm_intern_constants_callback(const pm_constant_t *constant, void *data)
10733{
10735 ctx->constants[ctx->index++] = rb_intern3((const char *) pm_constant_start(constant), pm_constant_length(constant), ctx->encoding);
10736}
10737
10739typedef struct {
10740 const pm_parser_t *parser;
10741 rb_encoding *encoding;
10742 const char *filepath;
10744
10745static void
10746pm_warning_emit_callback(const pm_diagnostic_t *diagnostic, void *data) {
10748 pm_location_t loc = pm_diagnostic_location(diagnostic);
10749 int line = pm_location_line_number(ctx->parser, &loc);
10750
10751 if (pm_diagnostic_warning_level(diagnostic) == PM_WARNING_LEVEL_VERBOSE) {
10752 rb_enc_compile_warning(ctx->encoding, ctx->filepath, line, "%s", pm_diagnostic_message(diagnostic));
10753 }
10754 else {
10755 rb_enc_compile_warn(ctx->encoding, ctx->filepath, line, "%s", pm_diagnostic_message(diagnostic));
10756 }
10757}
10758
10770static uint64_t
10771pm_source_hash(const pm_parser_t *parser)
10772{
10773 const uint8_t *start = pm_parser_start(parser);
10774 const uint8_t *end = pm_parser_end(parser);
10775 const pm_location_t *data_loc = pm_parser_data_loc(parser);
10776
10777 if (data_loc->length != 0) {
10778 const uint8_t *cursor = start + data_loc->start;
10779 while (cursor < end && *cursor != '\n') cursor++;
10780 if (cursor < end) cursor++;
10781 end = cursor;
10782 }
10783
10785 rb_source_hash_init(&state);
10786 rb_source_hash_update(&state, start, (size_t) (end - start));
10787 return rb_source_hash_finalize(&state);
10788}
10789
10790static VALUE
10791pm_parse_process(pm_parse_result_t *result, pm_node_t *node, VALUE *script_lines)
10792{
10793 pm_parser_t *parser = result->parser;
10794
10795 // First, set up the scope node so that the AST node is attached and can be
10796 // freed regardless of whether or we return an error.
10797 pm_scope_node_t *scope_node = &result->node;
10798 rb_encoding *filepath_encoding = scope_node->filepath_encoding;
10799 int coverage_enabled = scope_node->coverage_enabled;
10800
10801 pm_scope_node_init(node, scope_node, NULL);
10802 scope_node->filepath_encoding = filepath_encoding;
10803
10804 const char *encoding_name = pm_parser_encoding_name(parser);
10805 scope_node->encoding = rb_enc_find(encoding_name);
10806 if (!scope_node->encoding) rb_bug("Encoding not found %s!", encoding_name);
10807
10808 scope_node->coverage_enabled = coverage_enabled;
10809
10810 // If RubyVM.keep_script_lines is set to true, then we need to create that
10811 // array of script lines here.
10812 if (script_lines != NULL) {
10813 const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser);
10814 *script_lines = rb_ary_new_capa(line_offsets->size);
10815
10816 for (size_t index = 0; index < line_offsets->size; index++) {
10817 size_t offset = line_offsets->offsets[index];
10818 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);
10819 rb_ary_push(*script_lines, rb_enc_str_new((const char *) pm_parser_start(parser) + offset, length, scope_node->encoding));
10820 }
10821
10822 scope_node->script_lines = script_lines;
10823 }
10824
10825 // Emit all of the various warnings from the parse.
10826 pm_warning_emit_ctx_t warning_ctx = {
10827 .parser = parser,
10828 .encoding = scope_node->encoding,
10829 .filepath = (const char *) pm_string_source(pm_parser_filepath(parser))
10830 };
10831 pm_parser_warnings_each(parser, pm_warning_emit_callback, &warning_ctx);
10832
10833 // If there are errors, raise an appropriate error and free the result.
10834 if (pm_parser_errors_size(parser) > 0) {
10835 VALUE error = pm_parse_process_error(result);
10836
10837 // TODO: We need to set the backtrace.
10838 // rb_funcallv(error, rb_intern("set_backtrace"), 1, &path);
10839 return error;
10840 }
10841
10842 // Now set up the constant pool and intern all of the various constants into
10843 // their corresponding IDs.
10844 scope_node->parser = parser;
10845 scope_node->source_hash = pm_source_hash(parser);
10846 scope_node->options = result->options;
10847 scope_node->line_offsets = pm_parser_line_offsets(parser);
10848 scope_node->start_line = pm_parser_start_line(parser);
10849 size_t constants_size = pm_parser_constants_size(parser);
10850 scope_node->constants = constants_size ? xmalloc(constants_size * sizeof(ID)) : NULL;
10851
10852 pm_intern_constants_ctx_t intern_ctx = { .constants = scope_node->constants, .encoding = scope_node->encoding, .index = 0 };
10853 pm_parser_constants_each(parser, pm_intern_constants_callback, &intern_ctx);
10854
10855 // If we got here, this is a success and we can return Qnil to indicate that
10856 // no error should be raised.
10857 result->parsed = true;
10858 return Qnil;
10859}
10860
10865static void
10866pm_options_frozen_string_literal_init(pm_options_t *options)
10867{
10868 int frozen_string_literal = rb_iseq_opt_frozen_string_literal();
10869
10870 switch (frozen_string_literal) {
10871 case ISEQ_FROZEN_STRING_LITERAL_UNSET:
10872 break;
10873 case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
10874 pm_options_frozen_string_literal_set(options, false);
10875 break;
10876 case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
10877 pm_options_frozen_string_literal_set(options, true);
10878 break;
10879 default:
10880 rb_bug("pm_options_frozen_string_literal_init: invalid frozen_string_literal=%d", frozen_string_literal);
10881 break;
10882 }
10883}
10884
10889static inline VALUE
10890pm_parse_file_script_lines(const pm_scope_node_t *scope_node, const pm_parser_t *parser)
10891{
10892 const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser);
10893 const char *start = (const char *) pm_parser_start(parser);
10894 const char *end = (const char *) pm_parser_end(parser);
10895
10896 // If we end exactly on a newline, then there's no need to push on a final
10897 // segment. If we don't, then we need to push on the last offset up to the
10898 // end of the string.
10899 size_t last_offset = line_offsets->offsets[line_offsets->size - 1];
10900 bool last_push = start + last_offset != end;
10901
10902 // Create the ruby strings that represent the lines of the source.
10903 VALUE lines = rb_ary_new_capa(line_offsets->size - (last_push ? 0 : 1));
10904
10905 for (size_t index = 0; index < line_offsets->size - 1; index++) {
10906 size_t offset = line_offsets->offsets[index];
10907 size_t length = line_offsets->offsets[index + 1] - offset;
10908
10909 rb_ary_push(lines, rb_enc_str_new(start + offset, length, scope_node->encoding));
10910 }
10911
10912 // Push on the last line if we need to.
10913 if (last_push) {
10914 rb_ary_push(lines, rb_enc_str_new(start + last_offset, end - (start + last_offset), scope_node->encoding));
10915 }
10916
10917 return lines;
10918}
10919
10921 VALUE path;
10922 VALUE io;
10923 int open_mode;
10924 int fd;
10925};
10926
10927static VALUE
10928close_file(VALUE args)
10929{
10930 struct load_from_fd_args *arg = (void *)args;
10931 if (arg->fd != -1) {
10932 close(arg->fd);
10933 }
10934 else if (!NIL_P(arg->io)) {
10935 rb_io_close(arg->io);
10936 }
10937 return Qnil;
10938}
10939
10940static VALUE
10941load_content(VALUE args)
10942{
10943 struct load_from_fd_args *arg = (void *)args;
10944 VALUE io = rb_io_fdopen(arg->fd, arg->open_mode, RSTRING_PTR(arg->path));
10945 arg->io = io;
10946 arg->fd = -1;
10948 return rb_funcall(io, rb_intern("read"), 0);
10949}
10950
10955VALUE
10956pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error)
10957{
10958 pm_source_init_result_t init_result;
10959 result->source = pm_source_mapped_new(RSTRING_PTR(filepath), O_RDONLY | O_NONBLOCK, &init_result);
10960
10961 if (init_result == PM_SOURCE_INIT_SUCCESS) {
10962 pm_options_frozen_string_literal_init(result->options);
10963 return Qnil;
10964 }
10965
10966 int err;
10967
10968 // For non-regular files (pipes, character devices), we need to read
10969 // through Ruby IO to properly release the GVL while waiting for data.
10970 if (init_result == PM_SOURCE_INIT_ERROR_NON_REGULAR) {
10971 struct load_from_fd_args args = {
10972 .path = filepath,
10973 .open_mode = O_RDONLY | O_NONBLOCK,
10974 .fd = rb_cloexec_open(RSTRING_PTR(filepath), args.open_mode, 0),
10975 .io = Qnil,
10976 };
10977 if (args.fd == -1) goto error_generic;
10978 VALUE contents = rb_ensure(load_content, (VALUE)&args, close_file, (VALUE)&args);
10979
10980 if (!RB_TYPE_P(contents, T_STRING)) goto error_generic;
10981
10982 long len = RSTRING_LEN(contents);
10983 if (len < 0) goto error_generic;
10984
10985 size_t length = (size_t) len;
10986 uint8_t *source_data = xmalloc(length);
10987 memcpy(source_data, RSTRING_PTR(contents), length);
10988 result->source = pm_source_owned_new(source_data, length);
10989
10990 pm_options_frozen_string_literal_init(result->options);
10991 return Qnil;
10992 }
10993
10994 if (init_result == PM_SOURCE_INIT_ERROR_DIRECTORY) {
10995 err = EISDIR;
10996 } else {
10997error_generic:
10998#ifdef _WIN32
10999 err = rb_w32_map_errno(GetLastError());
11000#else
11001 err = errno;
11002#endif
11003 }
11004
11005 VALUE error;
11006 if (load_error) {
11007 VALUE message = rb_str_buf_new_cstr(strerror(err));
11008 rb_str_cat2(message, " -- ");
11009 rb_str_append(message, filepath);
11010
11011 error = rb_exc_new3(rb_eLoadError, message);
11012 rb_ivar_set(error, rb_intern_const("@path"), filepath);
11013 } else {
11014 error = rb_syserr_new_str(err, filepath);
11015 }
11016
11017 return error;
11018}
11019
11026VALUE
11027pm_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines)
11028{
11029 result->node.filepath_encoding = rb_enc_get(filepath);
11030 pm_options_filepath_set(result->options, RSTRING_PTR(filepath));
11031 RB_GC_GUARD(filepath);
11032
11033 pm_options_version_for_current_ruby_set(result->options);
11034
11035 result->parser = pm_parser_new(result->arena, pm_source_source(result->source), pm_source_length(result->source), result->options);
11036 pm_node_t *node = pm_parse(result->parser);
11037
11038 VALUE error = pm_parse_process(result, node, script_lines);
11039
11040 // If we're parsing a filepath, then we need to potentially support the
11041 // SCRIPT_LINES__ constant, which can be a hash that has an array of lines
11042 // of every read file.
11043 ID id_script_lines = rb_intern("SCRIPT_LINES__");
11044
11045 if (rb_const_defined_at(rb_cObject, id_script_lines)) {
11046 VALUE constant_script_lines = rb_const_get_at(rb_cObject, id_script_lines);
11047
11048 if (RB_TYPE_P(constant_script_lines, T_HASH)) {
11049 rb_hash_aset(constant_script_lines, filepath, pm_parse_file_script_lines(&result->node, result->parser));
11050 }
11051 }
11052
11053 return error;
11054}
11055
11060VALUE
11061pm_load_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines)
11062{
11063 VALUE error = pm_load_file(result, filepath, false);
11064 if (NIL_P(error)) {
11065 error = pm_parse_file(result, filepath, script_lines);
11066 }
11067
11068 return error;
11069}
11070
11077VALUE
11078pm_parse_string(pm_parse_result_t *result, VALUE source, VALUE filepath, VALUE *script_lines)
11079{
11080 rb_encoding *encoding = rb_enc_get(source);
11081 if (!rb_enc_asciicompat(encoding)) {
11082 return rb_exc_new_cstr(rb_eArgError, "invalid source encoding");
11083 }
11084
11085 pm_options_frozen_string_literal_init(result->options);
11086 result->source = pm_source_constant_new((const uint8_t *) RSTRING_PTR(source), (size_t) RSTRING_LEN(source));
11087 pm_options_encoding_set(result->options, rb_enc_name(encoding));
11088
11089 result->node.filepath_encoding = rb_enc_get(filepath);
11090 pm_options_filepath_set(result->options, RSTRING_PTR(filepath));
11091
11092 pm_options_version_for_current_ruby_set(result->options);
11093
11094 result->parser = pm_parser_new(result->arena, pm_source_source(result->source), pm_source_length(result->source), result->options);
11095 pm_node_t *node = pm_parse(result->parser);
11096
11097 VALUE error = pm_parse_process(result, node, script_lines);
11098
11099 RB_GC_GUARD(source);
11100 RB_GC_GUARD(filepath);
11101 return error;
11102}
11103
11104typedef struct {
11105 uint32_t node_id;
11106 const pm_node_t *node;
11108
11109static bool
11110pm_node_find(const pm_node_t *node, void *data)
11111{
11112 pm_node_find_context_t *context = data;
11113
11114 if (context->node == NULL && node->node_id == context->node_id) {
11115 context->node = node;
11116 return false;
11117 }
11118
11119 return context->node == NULL;
11120}
11121
11122bool
11123pm_node_source_location(VALUE source, VALUE filepath, int start_line,
11124 int node_id, rb_code_location_t *location)
11125{
11126 pm_parse_result_t result;
11127 pm_parse_result_init(&result);
11128
11129 pm_options_line_set(result.options, start_line);
11130 VALUE error = pm_parse_string(&result, source, filepath, NULL);
11131
11132 if (!NIL_P(error)) {
11133 pm_parse_result_free(&result);
11134 rb_exc_raise(error);
11135 }
11136
11137 pm_node_find_context_t context = {
11138 .node_id = (uint32_t) node_id,
11139 .node = NULL
11140 };
11141 pm_visit_node(result.node.ast_node, pm_node_find, &context);
11142
11143 bool found = context.node != NULL;
11144 if (found) {
11145 *location = pm_code_location(&result.node, context.node);
11146 }
11147
11148 RB_GC_GUARD(source);
11149 RB_GC_GUARD(filepath);
11150
11151 pm_parse_result_free(&result);
11152 return found;
11153}
11154
11155VALUE rb_io_gets_limit_internal(VALUE io, long limit);
11156
11164static int
11165pm_parse_stdin_eof(void *stream)
11166{
11167 return RTEST(rb_io_eof((VALUE) stream));
11168}
11169
11178#define MAX_ENC_LEN 6
11179
11185static char *
11186pm_parse_stdin_fgets(char *string, int size, void *stream)
11187{
11188 RUBY_ASSERT(size > MAX_ENC_LEN);
11189
11190 /*
11191 * Request fewer bytes than the buffer can hold. Ruby's line reader may
11192 * return more bytes than requested when the limit falls in the middle of a
11193 * multi-byte character, and the reserved headroom guarantees the result
11194 * still fits.
11195 */
11196 VALUE line = rb_io_gets_limit_internal((VALUE) stream, size - MAX_ENC_LEN);
11197 if (NIL_P(line)) {
11198 return NULL;
11199 }
11200
11201 const char *cstr = RSTRING_PTR(line);
11202 long length = RSTRING_LEN(line);
11203
11204 /*
11205 * Defensively clamp the copy. The line reader relaxes the limit to avoid
11206 * splitting a multi-byte character, so the returned string can be longer
11207 * than requested; we must never write past the caller's buffer. One byte
11208 * is reserved for the NUL terminator.
11209 */
11210 if (length > (long) (size - 1)) {
11211 length = (long) (size - 1);
11212 }
11213
11214 memcpy(string, cstr, (size_t) length);
11215 string[length] = '\0';
11216
11217 return string;
11218}
11219
11220#undef MAX_ENC_LEN
11221
11222// We need access to this function when we're done parsing stdin.
11223void rb_reset_argf_lineno(long n);
11224
11230VALUE
11231pm_parse_stdin(pm_parse_result_t *result)
11232{
11233 pm_options_frozen_string_literal_init(result->options);
11234
11235 result->source = pm_source_stream_new((void *) rb_stdin, pm_parse_stdin_fgets, pm_parse_stdin_eof);
11236 pm_node_t *node = pm_parse_stream(&result->parser, result->arena, result->source, result->options);
11237
11238 // When we're done parsing, we reset $. because we don't want the fact that
11239 // we went through an IO object to be visible to the user.
11240 rb_reset_argf_lineno(0);
11241
11242 return pm_parse_process(result, node, NULL);
11243}
11244
11245#define PM_VERSION_FOR_RELEASE(major, minor) PM_VERSION_FOR_RELEASE_IMPL(major, minor)
11246#define PM_VERSION_FOR_RELEASE_IMPL(major, minor) #major "." #minor
11247
11248void pm_options_version_for_current_ruby_set(pm_options_t *options) {
11249 const char *version = PM_VERSION_FOR_RELEASE(RUBY_API_VERSION_MAJOR, RUBY_API_VERSION_MINOR);
11250 pm_options_version_set(options, version, strlen(version));
11251}
11252
11253#undef NEW_ISEQ
11254#define NEW_ISEQ OLD_ISEQ
11255
11256#undef NEW_CHILD_ISEQ
11257#define NEW_CHILD_ISEQ OLD_CHILD_ISEQ
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
uint32_t pm_constant_id_t
A constant id is a unique identifier for a constant in the constant pool.
@ PM_WARNING_LEVEL_VERBOSE
For warnings which should be emitted if $VERBOSE == true.
Definition diagnostic.h:42
pm_error_level_t
The levels of errors generated during parsing.
Definition diagnostic.h:23
@ PM_ERROR_LEVEL_ARGUMENT
For errors that should raise an argument error.
Definition diagnostic.h:28
@ PM_ERROR_LEVEL_LOAD
For errors that should raise a load error.
Definition diagnostic.h:31
@ PM_ERROR_LEVEL_SYNTAX
For errors that should raise a syntax error.
Definition diagnostic.h:25
pm_errors_format_type_t
The type of formatting to use when formatting errors.
@ PM_ERRORS_FORMAT_PLAIN
Format errors in a plain format with no colors or styles.
@ PM_ERRORS_FORMAT_COLOR
Format errors in a color format with bold and colors.
@ PM_ERRORS_FORMAT_STYLE
Format errors in a style format with bold only.
#define RUBY_EVENT_END
Encountered an end of a class clause.
Definition event.h:40
#define RUBY_EVENT_B_RETURN
Encountered a next statement.
Definition event.h:56
#define RUBY_EVENT_CLASS
Encountered a new class.
Definition event.h:39
#define RUBY_EVENT_LINE
Encountered a new line.
Definition event.h:38
#define RUBY_EVENT_RETURN
Encountered a return statement.
Definition event.h:42
#define RUBY_EVENT_B_CALL
Encountered an yield statement.
Definition event.h:55
#define RUBY_EVENT_CALL
A method, written in Ruby, is called.
Definition event.h:41
#define RUBY_EVENT_RESCUE
Encountered a rescue statement.
Definition event.h:61
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1676
#define ALLOCV
Old name of RB_ALLOCV.
Definition memory.h:404
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:400
#define RFLOAT_VALUE
Old name of rb_float_value.
Definition double.h:28
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define rb_str_cat2
Old name of rb_str_cat_cstr.
Definition string.h:1684
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
#define ULONG2NUM
Old name of RB_ULONG2NUM.
Definition long.h:60
#define FIXABLE
Old name of RB_FIXABLE.
Definition fixnum.h:25
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define ZALLOC_N
Old name of RB_ZALLOC_N.
Definition memory.h:401
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
#define ALLOC_N
Old name of RB_ALLOC_N.
Definition memory.h:399
#define rb_exc_new3
Old name of rb_exc_new_str.
Definition error.h:38
#define FLONUM_P
Old name of RB_FLONUM_P.
#define Qtrue
Old name of RUBY_Qtrue.
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define NIL_P
Old name of RB_NIL_P.
#define DBL2NUM
Old name of rb_float_new.
Definition double.h:29
#define NUM2LONG
Old name of RB_NUM2LONG.
Definition long.h:51
#define UINT2NUM
Old name of RB_UINT2NUM.
Definition int.h:46
#define CONST_ID
Old name of RUBY_CONST_ID.
Definition symbol.h:47
#define ruby_debug
This variable controls whether the interpreter is in debug mode.
Definition error.h:487
VALUE rb_eNotImpError
NotImplementedError exception.
Definition error.c:1473
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:678
VALUE rb_eStandardError
StandardError exception.
Definition error.c:1460
VALUE rb_syserr_new_str(int n, VALUE arg)
Identical to rb_syserr_new(), except it takes the message in Ruby's String instead of C's.
Definition error.c:4068
VALUE rb_eNoMemError
NoMemoryError exception.
Definition error.c:1474
VALUE rb_eLoadError
LoadError exception.
Definition error.c:1481
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1463
VALUE rb_eNoMatchingPatternError
NoMatchingPatternError exception.
Definition error.c:1476
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:468
VALUE rb_eNoMatchingPatternKeyError
NoMatchingPatternKeyError exception.
Definition error.c:1477
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:1514
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition eval.c:1192
VALUE rb_eSyntaxError
SyntaxError exception.
Definition error.c:1480
VALUE rb_obj_reveal(VALUE obj, VALUE klass)
Make a hidden object visible again.
Definition object.c:103
VALUE rb_cArray
Array class.
VALUE rb_cObject
Object class.
Definition object.c:60
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:94
VALUE rb_stdin
STDIN constant.
Definition io.c:207
VALUE rb_cHash
Hash class.
Definition hash.c:123
VALUE rb_obj_freeze(VALUE obj)
Same as RB_OBJ_FREEZE(), but returns the given object.
Definition object.c:1308
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:481
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:469
VALUE rb_enc_interned_str(const char *ptr, long len, rb_encoding *enc)
Identical to rb_enc_str_new(), except it returns a "f"string.
Definition string.c:14184
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1123
VALUE rb_ary_cat(VALUE ary, const VALUE *train, long len)
Destructively appends multiple elements at the end of the array.
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_new_capa(long capa)
Identical to rb_ary_new(), except it additionally specifies how many rooms of objects it should alloc...
VALUE rb_ary_hidden_new(long capa)
Allocates a hidden (no class) empty array.
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
VALUE rb_ary_join(VALUE ary, VALUE sep)
Recursively stringises the elements of the passed array, flattens that result, then joins the sequenc...
void rb_ary_store(VALUE ary, long key, VALUE val)
Destructively stores the passed value to the passed array's passed index.
#define INTEGER_PACK_NATIVE_BYTE_ORDER
Means either INTEGER_PACK_MSBYTE_FIRST or INTEGER_PACK_LSBYTE_FIRST, depending on the host processor'...
Definition bignum.h:550
#define INTEGER_PACK_LSWORD_FIRST
Stores/interprets the least significant word as the first word.
Definition bignum.h:532
VALUE rb_io_fdopen(int fd, int flags, const char *path)
Creates an IO instance whose backend is the given file descriptor.
Definition io.c:9477
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Opens a file that closes on exec.
Definition io.c:367
VALUE rb_io_eof(VALUE io)
Queries if the passed IO is at the end of file.
Definition io.c:2794
VALUE rb_io_close(VALUE io)
Closes the IO.
Definition io.c:5882
VALUE rb_range_new(VALUE beg, VALUE end, int excl)
Creates a new Range.
Definition range.c:77
VALUE rb_rational_new(VALUE num, VALUE den)
Constructs a Rational, with reduction.
Definition rational.c:2006
VALUE rb_str_append(VALUE dst, VALUE src)
Identical to rb_str_buf_append(), except it converts the right hand side before concatenating.
Definition string.c:3898
VALUE rb_str_tmp_new(long len)
Allocates a "temporary" string.
Definition string.c:1783
#define rb_exc_new_cstr(exc, str)
Identical to rb_exc_new(), except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1671
#define rb_str_buf_new_cstr(str)
Identical to rb_str_new_cstr, except done differently.
Definition string.h:1640
VALUE rb_str_concat(VALUE dst, VALUE src)
Identical to rb_str_append(), except it also accepts an integer as a codepoint.
Definition string.c:4135
VALUE rb_str_freeze(VALUE str)
This is the implementation of String#freeze.
Definition string.c:3376
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1515
VALUE rb_obj_as_string(VALUE obj)
Try converting an object to its stringised representation using its to_s method, if any.
Definition string.c:1887
VALUE rb_ivar_set(VALUE obj, ID name, VALUE val)
Identical to rb_iv_set(), except it accepts the name as an ID instead of a C string.
Definition variable.c:2131
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:3501
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:3834
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:285
VALUE rb_id2sym(ID id)
Allocates an instance of rb_cSymbol that has the given id.
Definition symbol.c:1129
@ RUBY_IO_READABLE
IO::READABLE
Definition io.h:97
VALUE rb_io_wait(VALUE io, VALUE events, VALUE timeout)
Blocks until the passed IO is ready for the passed events.
Definition io.c:1544
int len
Length of the buffer.
Definition io.h:8
#define RB_OBJ_SET_SHAREABLE(obj)
Wrapper of rb_obj_set_shareable().
Definition ractor.h:290
#define RB_OBJ_SHAREABLE_P(obj)
Queries if the passed object has previously classified as shareable or not.
Definition ractor.h:255
static VALUE RB_OBJ_SET_FROZEN_SHAREABLE(VALUE obj)
Freezes and marks the object as shareable.
Definition ractor.h:301
VALUE rb_ractor_make_shareable(VALUE obj)
Destructively transforms the passed object so that multiple Ractors can share it.
Definition ractor.c:1933
#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:23183
PRISM_EXPORTED_FUNCTION void pm_parser_free(pm_parser_t *parser) PRISM_NONNULL(1)
Free both the memory held by the given parser and the parser itself.
Definition prism.c:23216
PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse(pm_parser_t *parser) PRISM_NONNULL(1)
Initiate the parser with the given parser.
Definition prism.c:23387
#define PM_NODE_LIST_FOREACH(list, index, node)
Loop through each node in the node list, writing each node to the given pm_node_t pointer.
Definition node.h:18
The main header file for the prism parser.
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:50
#define RARRAY_AREF(a, i)
Definition rarray.h:402
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
Definition rarray.h:51
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
pm_source_init_result_t
Represents the result of initializing a source from a file.
Definition source.h:46
@ PM_SOURCE_INIT_SUCCESS
Indicates that the source was successfully initialized.
Definition source.h:48
@ PM_SOURCE_INIT_ERROR_NON_REGULAR
Indicates that the file is not a regular file (e.g.
Definition source.h:65
@ PM_SOURCE_INIT_ERROR_DIRECTORY
Indicates that the file that was attempted to be opened was a directory.
Definition source.h:59
#define RTEST
This is an old name of RB_TEST.
AliasGlobalVariableNode.
Definition ast.h:1156
PM_NODE_ALIGNAS struct pm_node * new_name
AliasGlobalVariableNode::new_name.
Definition ast.h:1168
PM_NODE_ALIGNAS struct pm_node * old_name
AliasGlobalVariableNode::old_name.
Definition ast.h:1178
AliasMethodNode.
Definition ast.h:1203
PM_NODE_ALIGNAS struct pm_node * old_name
AliasMethodNode::old_name.
Definition ast.h:1237
PM_NODE_ALIGNAS struct pm_node * new_name
AliasMethodNode::new_name.
Definition ast.h:1221
AlternationPatternNode.
Definition ast.h:1262
PM_NODE_ALIGNAS struct pm_node * left
AlternationPatternNode::left.
Definition ast.h:1274
PM_NODE_ALIGNAS struct pm_node * right
AlternationPatternNode::right.
Definition ast.h:1284
AndNode.
Definition ast.h:1309
PM_NODE_ALIGNAS struct pm_node * left
AndNode::left.
Definition ast.h:1324
PM_NODE_ALIGNAS struct pm_node * right
AndNode::right.
Definition ast.h:1337
ArgumentsNode.
Definition ast.h:1369
pm_node_t base
The embedded base node.
Definition ast.h:1371
struct pm_node_list arguments
ArgumentsNode::arguments.
Definition ast.h:1381
ArrayNode.
Definition ast.h:1399
struct pm_node_list elements
ArrayNode::elements.
Definition ast.h:1408
ArrayPatternNode.
Definition ast.h:1459
struct pm_node_list requireds
ArrayPatternNode::requireds.
Definition ast.h:1487
PM_NODE_ALIGNAS struct pm_node * rest
ArrayPatternNode::rest.
Definition ast.h:1497
PM_NODE_ALIGNAS struct pm_node * constant
ArrayPatternNode::constant.
Definition ast.h:1477
struct pm_node_list posts
ArrayPatternNode::posts.
Definition ast.h:1507
AssocNode.
Definition ast.h:1542
PM_NODE_ALIGNAS struct pm_node * value
AssocNode::value.
Definition ast.h:1573
PM_NODE_ALIGNAS struct pm_node * key
AssocNode::key.
Definition ast.h:1560
AssocSplatNode.
Definition ast.h:1598
PM_NODE_ALIGNAS struct pm_node * value
AssocSplatNode::value.
Definition ast.h:1610
BackReferenceReadNode.
Definition ast.h:1635
pm_node_t base
The embedded base node.
Definition ast.h:1637
BeginNode.
Definition ast.h:1665
PM_NODE_ALIGNAS struct pm_else_node * else_clause
BeginNode::else_clause.
Definition ast.h:1707
PM_NODE_ALIGNAS struct pm_ensure_node * ensure_clause
BeginNode::ensure_clause.
Definition ast.h:1717
PM_NODE_ALIGNAS struct pm_statements_node * statements
BeginNode::statements.
Definition ast.h:1687
PM_NODE_ALIGNAS struct pm_rescue_node * rescue_clause
BeginNode::rescue_clause.
Definition ast.h:1697
BlockArgumentNode.
Definition ast.h:1742
PM_NODE_ALIGNAS struct pm_node * expression
BlockArgumentNode::expression.
Definition ast.h:1754
BlockLocalVariableNode.
Definition ast.h:1782
BlockNode.
Definition ast.h:1809
PM_NODE_ALIGNAS struct pm_node * parameters
BlockNode::parameters.
Definition ast.h:1835
pm_constant_id_list_t locals
BlockNode::locals.
Definition ast.h:1821
PM_NODE_ALIGNAS struct pm_node * body
BlockNode::body.
Definition ast.h:1845
BlockParameterNode.
Definition ast.h:1884
BlockParametersNode.
Definition ast.h:1937
BreakNode.
Definition ast.h:2010
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
BreakNode::arguments.
Definition ast.h:2022
CallAndWriteNode.
Definition ast.h:2053
PM_NODE_ALIGNAS struct pm_node * receiver
CallAndWriteNode::receiver.
Definition ast.h:2065
PM_NODE_ALIGNAS struct pm_node * value
CallAndWriteNode::value.
Definition ast.h:2125
pm_constant_id_t read_name
CallAndWriteNode::read_name.
Definition ast.h:2095
pm_constant_id_t write_name
CallAndWriteNode::write_name.
Definition ast.h:2105
CallNode.
Definition ast.h:2161
pm_location_t closing_loc
CallNode::closing_loc.
Definition ast.h:2242
pm_constant_id_t name
CallNode::name.
Definition ast.h:2202
pm_node_t base
The embedded base node.
Definition ast.h:2163
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
CallNode::arguments.
Definition ast.h:2232
pm_location_t message_loc
CallNode::message_loc.
Definition ast.h:2212
PM_NODE_ALIGNAS struct pm_node * block
CallNode::block.
Definition ast.h:2265
PM_NODE_ALIGNAS struct pm_node * receiver
CallNode::receiver.
Definition ast.h:2179
CallOperatorWriteNode.
Definition ast.h:2286
pm_constant_id_t read_name
CallOperatorWriteNode::read_name.
Definition ast.h:2328
pm_constant_id_t binary_operator
CallOperatorWriteNode::binary_operator.
Definition ast.h:2348
pm_constant_id_t write_name
CallOperatorWriteNode::write_name.
Definition ast.h:2338
PM_NODE_ALIGNAS struct pm_node * receiver
CallOperatorWriteNode::receiver.
Definition ast.h:2298
PM_NODE_ALIGNAS struct pm_node * value
CallOperatorWriteNode::value.
Definition ast.h:2368
CallOrWriteNode.
Definition ast.h:2389
PM_NODE_ALIGNAS struct pm_node * receiver
CallOrWriteNode::receiver.
Definition ast.h:2401
PM_NODE_ALIGNAS struct pm_node * value
CallOrWriteNode::value.
Definition ast.h:2461
pm_constant_id_t write_name
CallOrWriteNode::write_name.
Definition ast.h:2441
pm_constant_id_t read_name
CallOrWriteNode::read_name.
Definition ast.h:2431
CallTargetNode.
Definition ast.h:2490
pm_constant_id_t name
CallTargetNode::name.
Definition ast.h:2522
PM_NODE_ALIGNAS struct pm_node * receiver
CallTargetNode::receiver.
Definition ast.h:2502
CapturePatternNode.
Definition ast.h:2547
PM_NODE_ALIGNAS struct pm_node * value
CapturePatternNode::value.
Definition ast.h:2559
PM_NODE_ALIGNAS struct pm_local_variable_target_node * target
CapturePatternNode::target.
Definition ast.h:2569
CaseMatchNode.
Definition ast.h:2596
struct pm_node_list conditions
CaseMatchNode::conditions.
Definition ast.h:2618
pm_node_t base
The embedded base node.
Definition ast.h:2598
PM_NODE_ALIGNAS struct pm_node * predicate
CaseMatchNode::predicate.
Definition ast.h:2608
PM_NODE_ALIGNAS struct pm_else_node * else_clause
CaseMatchNode::else_clause.
Definition ast.h:2628
CaseNode.
Definition ast.h:2665
PM_NODE_ALIGNAS struct pm_else_node * else_clause
CaseNode::else_clause.
Definition ast.h:2697
struct pm_node_list conditions
CaseNode::conditions.
Definition ast.h:2687
PM_NODE_ALIGNAS struct pm_node * predicate
CaseNode::predicate.
Definition ast.h:2677
pm_node_t base
The embedded base node.
Definition ast.h:2667
ClassNode.
Definition ast.h:2732
pm_constant_id_list_t locals
ClassNode::locals.
Definition ast.h:2739
PM_NODE_ALIGNAS struct pm_node * superclass
ClassNode::superclass.
Definition ast.h:2774
pm_constant_id_t name
ClassNode::name.
Definition ast.h:2803
PM_NODE_ALIGNAS struct pm_node * constant_path
ClassNode::constant_path.
Definition ast.h:2754
PM_NODE_ALIGNAS struct pm_node * body
ClassNode::body.
Definition ast.h:2784
ClassVariableAndWriteNode.
Definition ast.h:2818
PM_NODE_ALIGNAS struct pm_node * value
ClassVariableAndWriteNode::value.
Definition ast.h:2860
pm_constant_id_t name
ClassVariableAndWriteNode::name.
Definition ast.h:2830
ClassVariableOperatorWriteNode.
Definition ast.h:2875
pm_constant_id_t name
ClassVariableOperatorWriteNode::name.
Definition ast.h:2882
PM_NODE_ALIGNAS struct pm_node * value
ClassVariableOperatorWriteNode::value.
Definition ast.h:2897
pm_constant_id_t binary_operator
ClassVariableOperatorWriteNode::binary_operator.
Definition ast.h:2902
ClassVariableOrWriteNode.
Definition ast.h:2917
PM_NODE_ALIGNAS struct pm_node * value
ClassVariableOrWriteNode::value.
Definition ast.h:2939
pm_constant_id_t name
ClassVariableOrWriteNode::name.
Definition ast.h:2924
ClassVariableReadNode.
Definition ast.h:2954
pm_constant_id_t name
ClassVariableReadNode::name.
Definition ast.h:2967
ClassVariableTargetNode.
Definition ast.h:2982
pm_constant_id_t name
ClassVariableTargetNode::name.
Definition ast.h:2989
ClassVariableWriteNode.
Definition ast.h:3004
PM_NODE_ALIGNAS struct pm_node * value
ClassVariableWriteNode::value.
Definition ast.h:3040
pm_constant_id_t name
ClassVariableWriteNode::name.
Definition ast.h:3017
ConstantAndWriteNode.
Definition ast.h:3065
PM_NODE_ALIGNAS struct pm_node * value
ConstantAndWriteNode::value.
Definition ast.h:3087
pm_location_t name_loc
ConstantAndWriteNode::name_loc.
Definition ast.h:3077
pm_constant_id_t name
ConstantAndWriteNode::name.
Definition ast.h:3072
A list of constant IDs.
size_t size
The number of constant ids in the list.
size_t capacity
The number of constant ids that have been allocated in the list.
pm_constant_id_t * ids
The constant ids in the list.
ConstantOperatorWriteNode.
Definition ast.h:3102
pm_constant_id_t name
ConstantOperatorWriteNode::name.
Definition ast.h:3109
pm_location_t name_loc
ConstantOperatorWriteNode::name_loc.
Definition ast.h:3114
pm_constant_id_t binary_operator
ConstantOperatorWriteNode::binary_operator.
Definition ast.h:3129
PM_NODE_ALIGNAS struct pm_node * value
ConstantOperatorWriteNode::value.
Definition ast.h:3124
ConstantOrWriteNode.
Definition ast.h:3144
PM_NODE_ALIGNAS struct pm_node * value
ConstantOrWriteNode::value.
Definition ast.h:3166
pm_location_t name_loc
ConstantOrWriteNode::name_loc.
Definition ast.h:3156
pm_constant_id_t name
ConstantOrWriteNode::name.
Definition ast.h:3151
ConstantPathAndWriteNode.
Definition ast.h:3181
PM_NODE_ALIGNAS struct pm_constant_path_node * target
ConstantPathAndWriteNode::target.
Definition ast.h:3188
PM_NODE_ALIGNAS struct pm_node * value
ConstantPathAndWriteNode::value.
Definition ast.h:3198
ConstantPathNode.
Definition ast.h:3213
PM_NODE_ALIGNAS struct pm_node * parent
ConstantPathNode::parent.
Definition ast.h:3231
pm_constant_id_t name
ConstantPathNode::name.
Definition ast.h:3238
ConstantPathOperatorWriteNode.
Definition ast.h:3279
PM_NODE_ALIGNAS struct pm_constant_path_node * target
ConstantPathOperatorWriteNode::target.
Definition ast.h:3286
pm_constant_id_t binary_operator
ConstantPathOperatorWriteNode::binary_operator.
Definition ast.h:3301
PM_NODE_ALIGNAS struct pm_node * value
ConstantPathOperatorWriteNode::value.
Definition ast.h:3296
ConstantPathOrWriteNode.
Definition ast.h:3316
PM_NODE_ALIGNAS struct pm_node * value
ConstantPathOrWriteNode::value.
Definition ast.h:3333
PM_NODE_ALIGNAS struct pm_constant_path_node * target
ConstantPathOrWriteNode::target.
Definition ast.h:3323
ConstantPathTargetNode.
Definition ast.h:3348
pm_constant_id_t name
ConstantPathTargetNode::name.
Definition ast.h:3360
PM_NODE_ALIGNAS struct pm_node * parent
ConstantPathTargetNode::parent.
Definition ast.h:3355
ConstantPathWriteNode.
Definition ast.h:3391
PM_NODE_ALIGNAS struct pm_node * value
ConstantPathWriteNode::value.
Definition ast.h:3426
PM_NODE_ALIGNAS struct pm_constant_path_node * target
ConstantPathWriteNode::target.
Definition ast.h:3406
ConstantReadNode.
Definition ast.h:3441
pm_node_t base
The embedded base node.
Definition ast.h:3443
pm_constant_id_t name
ConstantReadNode::name.
Definition ast.h:3454
ConstantTargetNode.
Definition ast.h:3469
pm_constant_id_t name
ConstantTargetNode::name.
Definition ast.h:3476
ConstantWriteNode.
Definition ast.h:3491
PM_NODE_ALIGNAS struct pm_node * value
ConstantWriteNode::value.
Definition ast.h:3527
pm_constant_id_t name
ConstantWriteNode::name.
Definition ast.h:3504
DefNode.
Definition ast.h:3553
pm_constant_id_t name
DefNode::name.
Definition ast.h:3560
PM_NODE_ALIGNAS struct pm_parameters_node * parameters
DefNode::parameters.
Definition ast.h:3575
pm_node_t base
The embedded base node.
Definition ast.h:3555
PM_NODE_ALIGNAS struct pm_node * receiver
DefNode::receiver.
Definition ast.h:3570
PM_NODE_ALIGNAS struct pm_node * body
DefNode::body.
Definition ast.h:3580
pm_constant_id_list_t locals
DefNode::locals.
Definition ast.h:3585
DefinedNode.
Definition ast.h:3630
PM_NODE_ALIGNAS struct pm_node * value
DefinedNode::value.
Definition ast.h:3642
ElseNode.
Definition ast.h:3667
PM_NODE_ALIGNAS struct pm_statements_node * statements
ElseNode::statements.
Definition ast.h:3679
EmbeddedStatementsNode.
Definition ast.h:3699
PM_NODE_ALIGNAS struct pm_statements_node * statements
EmbeddedStatementsNode::statements.
Definition ast.h:3711
EmbeddedVariableNode.
Definition ast.h:3731
PM_NODE_ALIGNAS struct pm_node * variable
EmbeddedVariableNode::variable.
Definition ast.h:3743
EnsureNode.
Definition ast.h:3762
PM_NODE_ALIGNAS struct pm_statements_node * statements
EnsureNode::statements.
Definition ast.h:3774
FindPatternNode.
Definition ast.h:3841
PM_NODE_ALIGNAS struct pm_splat_node * left
FindPatternNode::left.
Definition ast.h:3866
PM_NODE_ALIGNAS struct pm_splat_node * right
FindPatternNode::right.
Definition ast.h:3892
PM_NODE_ALIGNAS struct pm_node * constant
FindPatternNode::constant.
Definition ast.h:3853
struct pm_node_list requireds
FindPatternNode::requireds.
Definition ast.h:3879
FlipFlopNode.
Definition ast.h:3936
PM_NODE_ALIGNAS struct pm_node * left
FlipFlopNode::left.
Definition ast.h:3943
pm_node_t base
The embedded base node.
Definition ast.h:3938
PM_NODE_ALIGNAS struct pm_node * right
FlipFlopNode::right.
Definition ast.h:3948
FloatNode.
Definition ast.h:3968
double value
FloatNode::value.
Definition ast.h:3977
ForNode.
Definition ast.h:3992
PM_NODE_ALIGNAS struct pm_node * collection
ForNode::collection.
Definition ast.h:4014
PM_NODE_ALIGNAS struct pm_statements_node * statements
ForNode::statements.
Definition ast.h:4026
ForwardingSuperNode.
Definition ast.h:4123
PM_NODE_ALIGNAS struct pm_block_node * block
ForwardingSuperNode::block.
Definition ast.h:4143
GlobalVariableAndWriteNode.
Definition ast.h:4158
PM_NODE_ALIGNAS struct pm_node * value
GlobalVariableAndWriteNode::value.
Definition ast.h:4180
pm_constant_id_t name
GlobalVariableAndWriteNode::name.
Definition ast.h:4165
GlobalVariableOperatorWriteNode.
Definition ast.h:4195
PM_NODE_ALIGNAS struct pm_node * value
GlobalVariableOperatorWriteNode::value.
Definition ast.h:4217
pm_constant_id_t name
GlobalVariableOperatorWriteNode::name.
Definition ast.h:4202
pm_constant_id_t binary_operator
GlobalVariableOperatorWriteNode::binary_operator.
Definition ast.h:4222
GlobalVariableOrWriteNode.
Definition ast.h:4237
pm_constant_id_t name
GlobalVariableOrWriteNode::name.
Definition ast.h:4244
PM_NODE_ALIGNAS struct pm_node * value
GlobalVariableOrWriteNode::value.
Definition ast.h:4259
GlobalVariableReadNode.
Definition ast.h:4274
pm_constant_id_t name
GlobalVariableReadNode::name.
Definition ast.h:4287
GlobalVariableTargetNode.
Definition ast.h:4302
pm_constant_id_t name
GlobalVariableTargetNode::name.
Definition ast.h:4309
GlobalVariableWriteNode.
Definition ast.h:4324
pm_constant_id_t name
GlobalVariableWriteNode::name.
Definition ast.h:4337
PM_NODE_ALIGNAS struct pm_node * value
GlobalVariableWriteNode::value.
Definition ast.h:4360
HashNode.
Definition ast.h:4385
struct pm_node_list elements
HashNode::elements.
Definition ast.h:4410
HashPatternNode.
Definition ast.h:4444
PM_NODE_ALIGNAS struct pm_node * constant
HashPatternNode::constant.
Definition ast.h:4459
struct pm_node_list elements
HashPatternNode::elements.
Definition ast.h:4469
PM_NODE_ALIGNAS struct pm_node * rest
HashPatternNode::rest.
Definition ast.h:4485
IfNode.
Definition ast.h:4532
PM_NODE_ALIGNAS struct pm_node * predicate
IfNode::predicate.
Definition ast.h:4564
PM_NODE_ALIGNAS struct pm_statements_node * statements
IfNode::statements.
Definition ast.h:4591
ImaginaryNode.
Definition ast.h:4637
PM_NODE_ALIGNAS struct pm_node * numeric
ImaginaryNode::numeric.
Definition ast.h:4644
ImplicitNode.
Definition ast.h:4665
PM_NODE_ALIGNAS struct pm_node * value
ImplicitNode::value.
Definition ast.h:4672
InNode.
Definition ast.h:4713
PM_NODE_ALIGNAS struct pm_node * pattern
InNode::pattern.
Definition ast.h:4720
PM_NODE_ALIGNAS struct pm_statements_node * statements
InNode::statements.
Definition ast.h:4725
IndexAndWriteNode.
Definition ast.h:4756
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
IndexAndWriteNode::arguments.
Definition ast.h:4778
PM_NODE_ALIGNAS struct pm_block_argument_node * block
IndexAndWriteNode::block.
Definition ast.h:4788
PM_NODE_ALIGNAS struct pm_node * receiver
IndexAndWriteNode::receiver.
Definition ast.h:4763
PM_NODE_ALIGNAS struct pm_node * value
IndexAndWriteNode::value.
Definition ast.h:4798
A direct-indexed lookup table mapping constant IDs to local variable indices.
int capacity
Total number of slots (constants_size + PM_INDEX_LOOKUP_SPECIALS).
bool owned
Whether the values array is heap-allocated and needs explicit free.
int * values
Array of local indices, indexed by constant_id.
IndexOperatorWriteNode.
Definition ast.h:4819
PM_NODE_ALIGNAS struct pm_block_argument_node * block
IndexOperatorWriteNode::block.
Definition ast.h:4851
PM_NODE_ALIGNAS struct pm_node * receiver
IndexOperatorWriteNode::receiver.
Definition ast.h:4826
PM_NODE_ALIGNAS struct pm_node * value
IndexOperatorWriteNode::value.
Definition ast.h:4866
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
IndexOperatorWriteNode::arguments.
Definition ast.h:4841
pm_constant_id_t binary_operator
IndexOperatorWriteNode::binary_operator.
Definition ast.h:4856
IndexOrWriteNode.
Definition ast.h:4887
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
IndexOrWriteNode::arguments.
Definition ast.h:4909
PM_NODE_ALIGNAS struct pm_node * value
IndexOrWriteNode::value.
Definition ast.h:4929
PM_NODE_ALIGNAS struct pm_node * receiver
IndexOrWriteNode::receiver.
Definition ast.h:4894
PM_NODE_ALIGNAS struct pm_block_argument_node * block
IndexOrWriteNode::block.
Definition ast.h:4919
IndexTargetNode.
Definition ast.h:4958
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
IndexTargetNode::arguments.
Definition ast.h:4975
PM_NODE_ALIGNAS struct pm_block_argument_node * block
IndexTargetNode::block.
Definition ast.h:4985
PM_NODE_ALIGNAS struct pm_node * receiver
IndexTargetNode::receiver.
Definition ast.h:4965
InstanceVariableAndWriteNode.
Definition ast.h:5000
PM_NODE_ALIGNAS struct pm_node * value
InstanceVariableAndWriteNode::value.
Definition ast.h:5022
pm_constant_id_t name
InstanceVariableAndWriteNode::name.
Definition ast.h:5007
InstanceVariableOperatorWriteNode.
Definition ast.h:5037
pm_constant_id_t binary_operator
InstanceVariableOperatorWriteNode::binary_operator.
Definition ast.h:5064
PM_NODE_ALIGNAS struct pm_node * value
InstanceVariableOperatorWriteNode::value.
Definition ast.h:5059
pm_constant_id_t name
InstanceVariableOperatorWriteNode::name.
Definition ast.h:5044
InstanceVariableOrWriteNode.
Definition ast.h:5079
pm_constant_id_t name
InstanceVariableOrWriteNode::name.
Definition ast.h:5086
PM_NODE_ALIGNAS struct pm_node * value
InstanceVariableOrWriteNode::value.
Definition ast.h:5101
InstanceVariableReadNode.
Definition ast.h:5116
pm_constant_id_t name
InstanceVariableReadNode::name.
Definition ast.h:5129
InstanceVariableTargetNode.
Definition ast.h:5144
pm_constant_id_t name
InstanceVariableTargetNode::name.
Definition ast.h:5151
InstanceVariableWriteNode.
Definition ast.h:5166
PM_NODE_ALIGNAS struct pm_node * value
InstanceVariableWriteNode::value.
Definition ast.h:5202
pm_constant_id_t name
InstanceVariableWriteNode::name.
Definition ast.h:5179
IntegerNode.
Definition ast.h:5233
pm_integer_t value
IntegerNode::value.
Definition ast.h:5242
A structure represents an arbitrary-sized integer.
Definition integer.h:16
size_t length
The number of allocated values.
Definition integer.h:21
uint32_t value
Embedded value for small integer.
Definition integer.h:32
uint32_t * values
List of 32-bit integers.
Definition integer.h:26
bool negative
Whether or not the integer is negative.
Definition integer.h:38
Context for interning constants via callback.
InterpolatedMatchLastLineNode.
Definition ast.h:5270
InterpolatedRegularExpressionNode.
Definition ast.h:5315
InterpolatedStringNode.
Definition ast.h:5351
struct pm_node_list parts
InterpolatedStringNode::parts.
Definition ast.h:5363
InterpolatedSymbolNode.
Definition ast.h:5383
struct pm_node_list parts
InterpolatedSymbolNode::parts.
Definition ast.h:5395
InterpolatedXStringNode.
Definition ast.h:5415
struct pm_node_list parts
InterpolatedXStringNode::parts.
Definition ast.h:5427
KeywordHashNode.
Definition ast.h:5484
struct pm_node_list elements
KeywordHashNode::elements.
Definition ast.h:5491
KeywordRestParameterNode.
Definition ast.h:5510
LambdaNode.
Definition ast.h:5542
pm_location_t opening_loc
LambdaNode::opening_loc.
Definition ast.h:5559
pm_constant_id_list_t locals
LambdaNode::locals.
Definition ast.h:5549
PM_NODE_ALIGNAS struct pm_node * body
LambdaNode::body.
Definition ast.h:5574
PM_NODE_ALIGNAS struct pm_node * parameters
LambdaNode::parameters.
Definition ast.h:5569
A line and column in a string.
uint32_t column
The column in bytes.
int32_t line
The line number.
A list of offsets of the start of lines in a string.
uint32_t * offsets
The list of offsets.
size_t size
The number of offsets in the list.
the getlocal and setlocal instructions require two parameters.
LocalVariableAndWriteNode.
Definition ast.h:5589
pm_constant_id_t name
LocalVariableAndWriteNode::name.
Definition ast.h:5611
uint32_t depth
LocalVariableAndWriteNode::depth.
Definition ast.h:5616
PM_NODE_ALIGNAS struct pm_node * value
LocalVariableAndWriteNode::value.
Definition ast.h:5606
LocalVariableOperatorWriteNode.
Definition ast.h:5631
uint32_t depth
LocalVariableOperatorWriteNode::depth.
Definition ast.h:5663
pm_constant_id_t binary_operator
LocalVariableOperatorWriteNode::binary_operator.
Definition ast.h:5658
PM_NODE_ALIGNAS struct pm_node * value
LocalVariableOperatorWriteNode::value.
Definition ast.h:5648
pm_constant_id_t name
LocalVariableOperatorWriteNode::name.
Definition ast.h:5653
LocalVariableOrWriteNode.
Definition ast.h:5678
PM_NODE_ALIGNAS struct pm_node * value
LocalVariableOrWriteNode::value.
Definition ast.h:5695
uint32_t depth
LocalVariableOrWriteNode::depth.
Definition ast.h:5705
pm_constant_id_t name
LocalVariableOrWriteNode::name.
Definition ast.h:5700
LocalVariableReadNode.
Definition ast.h:5720
uint32_t depth
LocalVariableReadNode::depth.
Definition ast.h:5750
pm_constant_id_t name
LocalVariableReadNode::name.
Definition ast.h:5737
LocalVariableTargetNode.
Definition ast.h:5768
uint32_t depth
LocalVariableTargetNode::depth.
Definition ast.h:5780
pm_constant_id_t name
LocalVariableTargetNode::name.
Definition ast.h:5775
LocalVariableWriteNode.
Definition ast.h:5795
PM_NODE_ALIGNAS struct pm_node * value
LocalVariableWriteNode::value.
Definition ast.h:5848
uint32_t depth
LocalVariableWriteNode::depth.
Definition ast.h:5821
pm_constant_id_t name
LocalVariableWriteNode::name.
Definition ast.h:5808
This struct represents a slice in the source code, defined by an offset and a length.
Definition ast.h:572
uint32_t start
The offset of the location from the start of the source.
Definition ast.h:574
uint32_t length
The length of the location.
Definition ast.h:577
MatchLastLineNode.
Definition ast.h:5886
MatchPredicateNode.
Definition ast.h:5923
PM_NODE_ALIGNAS struct pm_node * pattern
MatchPredicateNode::pattern.
Definition ast.h:5935
PM_NODE_ALIGNAS struct pm_node * value
MatchPredicateNode::value.
Definition ast.h:5930
MatchRequiredNode.
Definition ast.h:5955
PM_NODE_ALIGNAS struct pm_node * pattern
MatchRequiredNode::pattern.
Definition ast.h:6016
PM_NODE_ALIGNAS struct pm_node * value
MatchRequiredNode::value.
Definition ast.h:5967
MatchWriteNode.
Definition ast.h:6041
PM_NODE_ALIGNAS struct pm_call_node * call
MatchWriteNode::call.
Definition ast.h:6048
struct pm_node_list targets
MatchWriteNode::targets.
Definition ast.h:6053
ModuleNode.
Definition ast.h:6068
PM_NODE_ALIGNAS struct pm_node * constant_path
ModuleNode::constant_path.
Definition ast.h:6085
PM_NODE_ALIGNAS struct pm_node * body
ModuleNode::body.
Definition ast.h:6090
pm_constant_id_list_t locals
ModuleNode::locals.
Definition ast.h:6075
pm_constant_id_t name
ModuleNode::name.
Definition ast.h:6100
MultiTargetNode.
Definition ast.h:6120
PM_NODE_ALIGNAS struct pm_node * rest
MultiTargetNode::rest.
Definition ast.h:6157
struct pm_node_list lefts
MultiTargetNode::lefts.
Definition ast.h:6137
struct pm_node_list rights
MultiTargetNode::rights.
Definition ast.h:6167
This is a node in the multi target state linked list.
As we're compiling a multi target, we need to track additional information whenever there is a parent...
MultiWriteNode.
Definition ast.h:6202
PM_NODE_ALIGNAS struct pm_node * rest
MultiWriteNode::rest.
Definition ast.h:6239
struct pm_node_list rights
MultiWriteNode::rights.
Definition ast.h:6249
PM_NODE_ALIGNAS struct pm_node * value
MultiWriteNode::value.
Definition ast.h:6289
struct pm_node_list lefts
MultiWriteNode::lefts.
Definition ast.h:6219
NextNode.
Definition ast.h:6304
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
NextNode::arguments.
Definition ast.h:6311
A list of nodes in the source, most often used for lists of children.
Definition ast.h:585
size_t size
The number of nodes in the list.
Definition ast.h:587
struct pm_node ** nodes
The nodes in the list.
Definition ast.h:593
This compiler defines its own concept of the location of a node.
int32_t line
This is the line number of a node.
uint32_t node_id
This is a unique identifier for the node.
This is the base structure that represents a node in the syntax tree.
Definition ast.h:1083
pm_node_type_t type
This represents the type of the node.
Definition ast.h:1088
uint32_t node_id
The unique identifier for this node, which is deterministic based on the source.
Definition ast.h:1100
pm_node_flags_t flags
This represents any flags on the node.
Definition ast.h:1094
pm_location_t location
This is the location of the node in the source.
Definition ast.h:1106
NumberedParametersNode.
Definition ast.h:6404
NumberedReferenceReadNode.
Definition ast.h:6426
uint32_t number
NumberedReferenceReadNode::number.
Definition ast.h:6441
OptionalKeywordParameterNode.
Definition ast.h:6460
pm_constant_id_t name
OptionalKeywordParameterNode::name.
Definition ast.h:6467
PM_NODE_ALIGNAS struct pm_node * value
OptionalKeywordParameterNode::value.
Definition ast.h:6477
OptionalParameterNode.
Definition ast.h:6496
pm_constant_id_t name
OptionalParameterNode::name.
Definition ast.h:6503
PM_NODE_ALIGNAS struct pm_node * value
OptionalParameterNode::value.
Definition ast.h:6518
OrNode.
Definition ast.h:6533
PM_NODE_ALIGNAS struct pm_node * right
OrNode::right.
Definition ast.h:6561
PM_NODE_ALIGNAS struct pm_node * left
OrNode::left.
Definition ast.h:6548
ParametersNode.
Definition ast.h:6587
PM_NODE_ALIGNAS struct pm_node * block
ParametersNode::block.
Definition ast.h:6624
struct pm_node_list requireds
ParametersNode::requireds.
Definition ast.h:6594
struct pm_node_list optionals
ParametersNode::optionals.
Definition ast.h:6599
struct pm_node_list posts
ParametersNode::posts.
Definition ast.h:6609
pm_node_t base
The embedded base node.
Definition ast.h:6589
PM_NODE_ALIGNAS struct pm_node * rest
ParametersNode::rest.
Definition ast.h:6604
struct pm_node_list keywords
ParametersNode::keywords.
Definition ast.h:6614
PM_NODE_ALIGNAS struct pm_node * keyword_rest
ParametersNode::keyword_rest.
Definition ast.h:6619
ParenthesesNode.
Definition ast.h:6642
PM_NODE_ALIGNAS struct pm_node * body
ParenthesesNode::body.
Definition ast.h:6649
bool parsed
Whether or not this parse result has performed its parsing yet.
pm_parser_t * parser
The parser that will do the actual parsing.
pm_source_t * source
The source backing the parse (file, string, or stream).
pm_scope_node_t node
The resulting scope node that will hold the generated AST.
pm_options_t * options
The options that will be passed to the parser.
pm_arena_t * arena
The arena allocator for AST-lifetime memory.
PinnedExpressionNode.
Definition ast.h:6674
PinnedVariableNode.
Definition ast.h:6731
PM_NODE_ALIGNAS struct pm_node * variable
PinnedVariableNode::variable.
Definition ast.h:6743
PostExecutionNode.
Definition ast.h:6768
PM_NODE_ALIGNAS struct pm_statements_node * statements
PostExecutionNode::statements.
Definition ast.h:6775
PreExecutionNode.
Definition ast.h:6805
PM_NODE_ALIGNAS struct pm_statements_node * statements
PreExecutionNode::statements.
Definition ast.h:6812
ProgramNode.
Definition ast.h:6839
PM_NODE_ALIGNAS struct pm_statements_node * statements
ProgramNode::statements.
Definition ast.h:6851
RangeNode.
Definition ast.h:6872
PM_NODE_ALIGNAS struct pm_node * right
RangeNode::right.
Definition ast.h:6901
PM_NODE_ALIGNAS struct pm_node * left
RangeNode::left.
Definition ast.h:6887
RationalNode.
Definition ast.h:6929
pm_integer_t denominator
RationalNode::denominator.
Definition ast.h:6949
pm_integer_t numerator
RationalNode::numerator.
Definition ast.h:6940
RegularExpressionNode.
Definition ast.h:6994
RequiredKeywordParameterNode.
Definition ast.h:7035
RequiredParameterNode.
Definition ast.h:7066
pm_constant_id_t name
RequiredParameterNode::name.
Definition ast.h:7073
RescueModifierNode.
Definition ast.h:7088
PM_NODE_ALIGNAS struct pm_node * expression
RescueModifierNode::expression.
Definition ast.h:7095
PM_NODE_ALIGNAS struct pm_node * rescue_expression
RescueModifierNode::rescue_expression.
Definition ast.h:7105
RescueNode.
Definition ast.h:7125
PM_NODE_ALIGNAS struct pm_statements_node * statements
RescueNode::statements.
Definition ast.h:7157
PM_NODE_ALIGNAS struct pm_rescue_node * subsequent
RescueNode::subsequent.
Definition ast.h:7162
PM_NODE_ALIGNAS struct pm_node * reference
RescueNode::reference.
Definition ast.h:7147
struct pm_node_list exceptions
RescueNode::exceptions.
Definition ast.h:7137
RestParameterNode.
Definition ast.h:7181
ReturnNode.
Definition ast.h:7230
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
ReturnNode::arguments.
Definition ast.h:7242
uint64_t source_hash
The source hash of the parsed source, propagated to every iseq.
rb_encoding * filepath_encoding
This is the encoding of the actual filepath object that will be used when a FILE node is compiled or ...
pm_index_lookup_table_t index_lookup_table
A flat lookup table mapping constant IDs (or special IDs) to local variable indices.
size_t last_line
Cached line hint for line offset list lookups.
struct iseq_link_anchor * pre_execution_anchor
This will only be set on the top-level scope node.
VALUE * script_lines
This is a pointer to the list of script lines for the ISEQs that will be associated with this scope n...
ShareableConstantNode.
Definition ast.h:7280
PM_NODE_ALIGNAS struct pm_node * write
ShareableConstantNode::write.
Definition ast.h:7289
pm_node_t base
The embedded base node.
Definition ast.h:7282
SingletonClassNode.
Definition ast.h:7304
PM_NODE_ALIGNAS struct pm_node * body
SingletonClassNode::body.
Definition ast.h:7331
pm_constant_id_list_t locals
SingletonClassNode::locals.
Definition ast.h:7311
PM_NODE_ALIGNAS struct pm_node * expression
SingletonClassNode::expression.
Definition ast.h:7326
SourceFileNode.
Definition ast.h:7374
pm_string_t filepath
SourceFileNode::filepath.
Definition ast.h:7383
SplatNode.
Definition ast.h:7415
PM_NODE_ALIGNAS struct pm_node * expression
SplatNode::expression.
Definition ast.h:7427
StatementsNode.
Definition ast.h:7442
struct pm_node_list body
StatementsNode::body.
Definition ast.h:7449
pm_node_t base
The embedded base node.
Definition ast.h:7444
StringNode.
Definition ast.h:7476
pm_string_t unescaped
StringNode::unescaped.
Definition ast.h:7498
A generic string type that can have various ownership semantics.
Definition stringy.h:18
SuperNode.
Definition ast.h:7518
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
SuperNode::arguments.
Definition ast.h:7537
PM_NODE_ALIGNAS struct pm_node * block
SuperNode::block.
Definition ast.h:7547
SymbolNode.
Definition ast.h:7570
pm_string_t unescaped
SymbolNode::unescaped.
Definition ast.h:7592
pm_node_t base
The embedded base node.
Definition ast.h:7572
UndefNode.
Definition ast.h:7624
struct pm_node_list names
UndefNode::names.
Definition ast.h:7631
UnlessNode.
Definition ast.h:7654
PM_NODE_ALIGNAS struct pm_node * predicate
UnlessNode::predicate.
Definition ast.h:7682
PM_NODE_ALIGNAS struct pm_statements_node * statements
UnlessNode::statements.
Definition ast.h:7703
PM_NODE_ALIGNAS struct pm_else_node * else_clause
UnlessNode::else_clause.
Definition ast.h:7713
UntilNode.
Definition ast.h:7744
pm_node_t base
The embedded base node.
Definition ast.h:7746
Context for emitting warnings via callback.
WhenNode.
Definition ast.h:7788
WhileNode.
Definition ast.h:7831
pm_node_t base
The embedded base node.
Definition ast.h:7833
XStringNode.
Definition ast.h:7877
pm_string_t unescaped
XStringNode::unescaped.
Definition ast.h:7899
YieldNode.
Definition ast.h:7914
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
YieldNode::arguments.
Definition ast.h:7931
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376