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