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