Ruby: prism_compile.c Source File

Ruby 3.4.8p72 (2025-12-17 revision 995b59f66677d44767ce9faac6957e5543617ff9)

prism_compile.c
1#include "prism.h"
2#include "version.h"
3
9 typedef struct {
11 int32_t line;
12
14 uint32_t node_id;
15} pm_node_location_t;
16
17/******************************************************************************/
18/* These macros operate on pm_node_location_t structs as opposed to NODE*s. */
19/******************************************************************************/
20
21#define PUSH_ADJUST(seq, location, label) \
22 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), (int) (location).line))
23
24#define PUSH_ADJUST_RESTORE(seq, label) \
25 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), -1))
26
27#define PUSH_INSN(seq, location, insn) \
28 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 0))
29
30#define PUSH_INSN1(seq, location, insn, op1) \
31 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 1, (VALUE)(op1)))
32
33#define PUSH_INSN2(seq, location, insn, op1, op2) \
34 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).node_id, BIN(insn), 2, (VALUE)(op1), (VALUE)(op2)))
35
36#define PUSH_INSN3(seq, location, insn, op1, op2, op3) \
37 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)))
38
39#define PUSH_INSNL(seq, location, insn, label) \
40 (PUSH_INSN1(seq, location, insn, label), LABEL_REF(label))
41
42#define PUSH_LABEL(seq, label) \
43 ADD_ELEM((seq), (LINK_ELEMENT *) (label))
44
45#define PUSH_SEND_R(seq, location, id, argc, block, flag, keywords) \
46 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (int) (location).line, (int) (location).node_id, (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords)))
47
48#define PUSH_SEND(seq, location, id, argc) \
49 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)INT2FIX(0), NULL)
50
51#define PUSH_SEND_WITH_FLAG(seq, location, id, argc, flag) \
52 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)(flag), NULL)
53
54#define PUSH_SEND_WITH_BLOCK(seq, location, id, argc, block) \
55 PUSH_SEND_R((seq), location, (id), (argc), (block), (VALUE)INT2FIX(0), NULL)
56
57#define PUSH_CALL(seq, location, id, argc) \
58 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
59
60#define PUSH_CALL_WITH_BLOCK(seq, location, id, argc, block) \
61 PUSH_SEND_R((seq), location, (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
62
63#define PUSH_TRACE(seq, event) \
64 ADD_ELEM((seq), (LINK_ELEMENT *) new_trace_body(iseq, (event), 0))
65
66#define PUSH_CATCH_ENTRY(type, ls, le, iseqv, lc) \
67 ADD_CATCH_ENTRY((type), (ls), (le), (iseqv), (lc))
68
69#define PUSH_SEQ(seq1, seq2) \
70 APPEND_LIST((seq1), (seq2))
71
72#define PUSH_SYNTHETIC_PUTNIL(seq, iseq) \
73 do { \
74 int lineno = ISEQ_COMPILE_DATA(iseq)->last_line; \
75 if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq)); \
76 ADD_SYNTHETIC_INSN(seq, lineno, -1, putnil); \
77 } while (0)
78
79/******************************************************************************/
80/* These functions compile getlocal/setlocal instructions but operate on */
81/* prism locations instead of NODEs. */
82/******************************************************************************/
83
84static void
85pm_iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int node_id, int idx, int level)
86{
87 if (iseq_local_block_param_p(iseq, idx, level)) {
88 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(getblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
89 }
90 else {
91 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(getlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
92 }
93 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qfalse);
94}
95
96static void
97pm_iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int node_id, int idx, int level)
98{
99 if (iseq_local_block_param_p(iseq, idx, level)) {
100 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(setblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
101 }
102 else {
103 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line, node_id, BIN(setlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
104 }
105 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue);
106}
107
108#define PUSH_GETLOCAL(seq, location, idx, level) \
109 pm_iseq_add_getlocal(iseq, (seq), (int) (location).line, (int) (location).node_id, (idx), (level))
110
111#define PUSH_SETLOCAL(seq, location, idx, level) \
112 pm_iseq_add_setlocal(iseq, (seq), (int) (location).line, (int) (location).node_id, (idx), (level))
113
114/******************************************************************************/
115/* These are helper macros for the compiler. */
116/******************************************************************************/
117
118#define OLD_ISEQ NEW_ISEQ
119#undef NEW_ISEQ
120
121#define NEW_ISEQ(node, name, type, line_no) \
122 pm_new_child_iseq(iseq, (node), rb_fstring(name), 0, (type), (line_no))
123
124#define OLD_CHILD_ISEQ NEW_CHILD_ISEQ
125#undef NEW_CHILD_ISEQ
126
127#define NEW_CHILD_ISEQ(node, name, type, line_no) \
128 pm_new_child_iseq(iseq, (node), rb_fstring(name), iseq, (type), (line_no))
129
130#define PM_COMPILE(node) \
131 pm_compile_node(iseq, (node), ret, popped, scope_node)
132
133#define PM_COMPILE_INTO_ANCHOR(_ret, node) \
134 pm_compile_node(iseq, (node), _ret, popped, scope_node)
135
136#define PM_COMPILE_POPPED(node) \
137 pm_compile_node(iseq, (node), ret, true, scope_node)
138
139#define PM_COMPILE_NOT_POPPED(node) \
140 pm_compile_node(iseq, (node), ret, false, scope_node)
141
142#define PM_NODE_START_LOCATION(parser, node) \
143 ((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 })
144
145#define PM_NODE_END_LOCATION(parser, node) \
146 ((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 })
147
148#define PM_LOCATION_START_LOCATION(parser, location, id) \
149 ((pm_node_location_t) { .line = pm_newline_list_line(&(parser)->newline_list, (location)->start, (parser)->start_line), .node_id = id })
150
151#define PM_NODE_START_LINE_COLUMN(parser, node) \
152 pm_newline_list_line_column(&(parser)->newline_list, ((const pm_node_t *) (node))->location.start, (parser)->start_line)
153
154#define PM_NODE_END_LINE_COLUMN(parser, node) \
155 pm_newline_list_line_column(&(parser)->newline_list, ((const pm_node_t *) (node))->location.end, (parser)->start_line)
156
157#define PM_LOCATION_START_LINE_COLUMN(parser, location) \
158 pm_newline_list_line_column(&(parser)->newline_list, (location)->start, (parser)->start_line)
159
160static int
161pm_node_line_number(const pm_parser_t *parser, const pm_node_t *node)
162{
163 return (int) pm_newline_list_line(&parser->newline_list, node->location.start, parser->start_line);
164}
165
166static int
167pm_location_line_number(const pm_parser_t *parser, const pm_location_t *location) {
168 return (int) pm_newline_list_line(&parser->newline_list, location->start, parser->start_line);
169}
170
174static VALUE
175parse_integer_value(const pm_integer_t *integer)
176{
177 VALUE result;
178
179 if (integer->values == NULL) {
180 result = UINT2NUM(integer->value);
181 }
182 else {
183 VALUE string = rb_str_new(NULL, integer->length * 8);
184 unsigned char *bytes = (unsigned char *) RSTRING_PTR(string);
185
186 size_t offset = integer->length * 8;
187 for (size_t value_index = 0; value_index < integer->length; value_index++) {
188 uint32_t value = integer->values[value_index];
189
190 for (int index = 0; index < 8; index++) {
191 int byte = (value >> (4 * index)) & 0xf;
192 bytes[--offset] = byte < 10 ? byte + '0' : byte - 10 + 'a';
193 }
194 }
195
196 result = rb_funcall(string, rb_intern("to_i"), 1, UINT2NUM(16));
197 }
198
199 if (integer->negative) {
200 result = rb_funcall(result, rb_intern("-@"), 0);
201 }
202
203 return result;
204}
205
209static inline VALUE
210parse_integer(const pm_integer_node_t *node)
211{
212 return parse_integer_value(&node->value);
213}
214
218static VALUE
219parse_float(const pm_float_node_t *node)
220{
221 return DBL2NUM(node->value);
222}
223
230static VALUE
231parse_rational(const pm_rational_node_t *node)
232{
233 VALUE numerator = parse_integer_value(&node->numerator);
234 VALUE denominator = parse_integer_value(&node->denominator);
235 return rb_rational_new(numerator, denominator);
236}
237
244static VALUE
245parse_imaginary(const pm_imaginary_node_t *node)
246{
247 VALUE imaginary_part;
248 switch (PM_NODE_TYPE(node->numeric)) {
249 case PM_FLOAT_NODE: {
250 imaginary_part = parse_float((const pm_float_node_t *) node->numeric);
251 break;
252 }
253 case PM_INTEGER_NODE: {
254 imaginary_part = parse_integer((const pm_integer_node_t *) node->numeric);
255 break;
256 }
257 case PM_RATIONAL_NODE: {
258 imaginary_part = parse_rational((const pm_rational_node_t *) node->numeric);
259 break;
260 }
261 default:
262 rb_bug("Unexpected numeric type on imaginary number %s\n", pm_node_type_to_str(PM_NODE_TYPE(node->numeric)));
263 }
264
265 return rb_complex_raw(INT2FIX(0), imaginary_part);
266}
267
268static inline VALUE
269parse_string(const pm_scope_node_t *scope_node, const pm_string_t *string)
270{
271 return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), scope_node->encoding);
272}
273
279static inline VALUE
280parse_string_encoded(const pm_node_t *node, const pm_string_t *string, rb_encoding *default_encoding)
281{
282 rb_encoding *encoding;
283
284 if (node->flags & PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING) {
285 encoding = rb_ascii8bit_encoding();
286 }
287 else if (node->flags & PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING) {
288 encoding = rb_utf8_encoding();
289 }
290 else {
291 encoding = default_encoding;
292 }
293
294 return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), encoding);
295}
296
297static inline VALUE
298parse_static_literal_string(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *string)
299{
300 rb_encoding *encoding;
301
302 if (node->flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) {
303 encoding = rb_ascii8bit_encoding();
304 }
305 else if (node->flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) {
306 encoding = rb_utf8_encoding();
307 }
308 else {
309 encoding = scope_node->encoding;
310 }
311
312 VALUE value = rb_enc_literal_str((const char *) pm_string_source(string), pm_string_length(string), encoding);
313 rb_enc_str_coderange(value);
314
315 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
316 int line_number = pm_node_line_number(scope_node->parser, node);
317 value = rb_str_with_debug_created_info(value, rb_iseq_path(iseq), line_number);
318 }
319
320 return value;
321}
322
323static inline ID
324parse_string_symbol(const pm_scope_node_t *scope_node, const pm_symbol_node_t *symbol)
325{
326 rb_encoding *encoding;
327 if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING) {
328 encoding = rb_utf8_encoding();
329 }
330 else if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING) {
331 encoding = rb_ascii8bit_encoding();
332 }
333 else if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING) {
334 encoding = rb_usascii_encoding();
335 }
336 else {
337 encoding = scope_node->encoding;
338 }
339
340 return rb_intern3((const char *) pm_string_source(&symbol->unescaped), pm_string_length(&symbol->unescaped), encoding);
341}
342
343static int
344pm_optimizable_range_item_p(const pm_node_t *node)
345{
346 return (!node || PM_NODE_TYPE_P(node, PM_INTEGER_NODE) || PM_NODE_TYPE_P(node, PM_NIL_NODE));
347}
348
350static VALUE
351parse_regexp_error(rb_iseq_t *iseq, int32_t line_number, const char *fmt, ...)
352{
353 va_list args;
354 va_start(args, fmt);
355 VALUE error = rb_syntax_error_append(Qnil, rb_iseq_path(iseq), line_number, -1, NULL, "%" PRIsVALUE, args);
356 va_end(args);
357 rb_exc_raise(error);
358}
359
360static VALUE
361parse_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)
362{
363 // If we were passed an explicit regexp encoding, then we need to double
364 // check that it's okay here for this fragment of the string.
365 rb_encoding *encoding;
366
367 if (explicit_regexp_encoding != NULL) {
368 encoding = explicit_regexp_encoding;
369 }
370 else if (node->flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) {
371 encoding = rb_ascii8bit_encoding();
372 }
373 else if (node->flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) {
374 encoding = rb_utf8_encoding();
375 }
376 else {
377 encoding = implicit_regexp_encoding;
378 }
379
380 VALUE string = rb_enc_str_new((const char *) pm_string_source(unescaped), pm_string_length(unescaped), encoding);
381 VALUE error = rb_reg_check_preprocess(string);
382
383 if (error != Qnil) parse_regexp_error(iseq, pm_node_line_number(scope_node->parser, node), "%" PRIsVALUE, rb_obj_as_string(error));
384 return string;
385}
386
387static VALUE
388pm_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)
389{
390 VALUE current = Qnil;
391
392 for (size_t index = 0; index < nodes->size; index++) {
393 const pm_node_t *part = nodes->nodes[index];
394 VALUE string;
395
396 switch (PM_NODE_TYPE(part)) {
397 case PM_STRING_NODE:
398 if (implicit_regexp_encoding != NULL) {
399 if (top) {
400 string = parse_regexp_string_part(iseq, scope_node, part, &((const pm_string_node_t *) part)->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
401 }
402 else {
403 string = parse_string_encoded(part, &((const pm_string_node_t *) part)->unescaped, scope_node->encoding);
404 VALUE error = rb_reg_check_preprocess(string);
405 if (error != Qnil) parse_regexp_error(iseq, pm_node_line_number(scope_node->parser, part), "%" PRIsVALUE, rb_obj_as_string(error));
406 }
407 }
408 else {
409 string = parse_string_encoded(part, &((const pm_string_node_t *) part)->unescaped, scope_node->encoding);
410 }
411 break;
412 case PM_INTERPOLATED_STRING_NODE:
413 string = pm_static_literal_concat(iseq, &((const pm_interpolated_string_node_t *) part)->parts, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
414 break;
415 case PM_EMBEDDED_STATEMENTS_NODE: {
416 const pm_embedded_statements_node_t *cast = (const pm_embedded_statements_node_t *) part;
417 string = pm_static_literal_concat(iseq, &cast->statements->body, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
418 break;
419 }
420 default:
421 RUBY_ASSERT(false && "unexpected node type in pm_static_literal_concat");
422 return Qnil;
423 }
424
425 if (current != Qnil) {
426 current = rb_str_concat(current, string);
427 }
428 else {
429 current = string;
430 }
431 }
432
433 return top ? rb_fstring(current) : current;
434}
435
436#define RE_OPTION_ENCODING_SHIFT 8
437#define RE_OPTION_ENCODING(encoding) (((encoding) & 0xFF) << RE_OPTION_ENCODING_SHIFT)
438#define ARG_ENCODING_NONE 32
439#define ARG_ENCODING_FIXED 16
440#define ENC_ASCII8BIT 1
441#define ENC_EUC_JP 2
442#define ENC_Windows_31J 3
443#define ENC_UTF8 4
444
449static int
450parse_regexp_flags(const pm_node_t *node)
451{
452 int flags = 0;
453
454 // Check "no encoding" first so that flags don't get clobbered
455 // We're calling `rb_char_to_option_kcode` in this case so that
456 // we don't need to have access to `ARG_ENCODING_NONE`
457 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) {
458 flags |= ARG_ENCODING_NONE;
459 }
460
461 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) {
462 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_EUC_JP));
463 }
464
465 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) {
466 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_Windows_31J));
467 }
468
469 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) {
470 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_UTF8));
471 }
472
473 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) {
474 flags |= ONIG_OPTION_IGNORECASE;
475 }
476
477 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) {
478 flags |= ONIG_OPTION_MULTILINE;
479 }
480
481 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) {
482 flags |= ONIG_OPTION_EXTEND;
483 }
484
485 return flags;
486}
487
488#undef RE_OPTION_ENCODING_SHIFT
489#undef RE_OPTION_ENCODING
490#undef ARG_ENCODING_FIXED
491#undef ARG_ENCODING_NONE
492#undef ENC_ASCII8BIT
493#undef ENC_EUC_JP
494#undef ENC_Windows_31J
495#undef ENC_UTF8
496
497static rb_encoding *
498parse_regexp_encoding(const pm_scope_node_t *scope_node, const pm_node_t *node)
499{
500 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING) || PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) {
501 return rb_ascii8bit_encoding();
502 }
503 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) {
504 return rb_utf8_encoding();
505 }
506 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) {
507 return rb_enc_get_from_index(ENCINDEX_EUC_JP);
508 }
509 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) {
510 return rb_enc_get_from_index(ENCINDEX_Windows_31J);
511 }
512 else {
513 return NULL;
514 }
515}
516
517static VALUE
518parse_regexp(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, VALUE string)
519{
520 VALUE errinfo = rb_errinfo();
521
522 int32_t line_number = pm_node_line_number(scope_node->parser, node);
523 VALUE regexp = rb_reg_compile(string, parse_regexp_flags(node), (const char *) pm_string_source(&scope_node->parser->filepath), line_number);
524
525 if (NIL_P(regexp)) {
526 VALUE message = rb_attr_get(rb_errinfo(), idMesg);
527 rb_set_errinfo(errinfo);
528
529 parse_regexp_error(iseq, line_number, "%" PRIsVALUE, message);
530 return Qnil;
531 }
532
533 rb_obj_freeze(regexp);
534 return regexp;
535}
536
537static inline VALUE
538parse_regexp_literal(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *unescaped)
539{
540 rb_encoding *regexp_encoding = parse_regexp_encoding(scope_node, node);
541 if (regexp_encoding == NULL) regexp_encoding = scope_node->encoding;
542
543 VALUE string = rb_enc_str_new((const char *) pm_string_source(unescaped), pm_string_length(unescaped), regexp_encoding);
544 return parse_regexp(iseq, scope_node, node, string);
545}
546
547static inline VALUE
548parse_regexp_concat(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_node_list_t *parts)
549{
550 rb_encoding *explicit_regexp_encoding = parse_regexp_encoding(scope_node, node);
551 rb_encoding *implicit_regexp_encoding = explicit_regexp_encoding != NULL ? explicit_regexp_encoding : scope_node->encoding;
552
553 VALUE string = pm_static_literal_concat(iseq, parts, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
554 return parse_regexp(iseq, scope_node, node, string);
555}
556
557static 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);
558
559static int
560pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, rb_encoding *implicit_regexp_encoding, rb_encoding *explicit_regexp_encoding, bool mutable_result, bool frozen_result)
561{
562 int stack_size = 0;
563 size_t parts_size = parts->size;
564 bool interpolated = false;
565
566 if (parts_size > 0) {
567 VALUE current_string = Qnil;
568 pm_node_location_t current_location = *node_location;
569
570 for (size_t index = 0; index < parts_size; index++) {
571 const pm_node_t *part = parts->nodes[index];
572
573 if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
574 const pm_string_node_t *string_node = (const pm_string_node_t *) part;
575 VALUE string_value;
576
577 if (implicit_regexp_encoding == NULL) {
578 string_value = parse_string_encoded(part, &string_node->unescaped, scope_node->encoding);
579 }
580 else {
581 string_value = parse_regexp_string_part(iseq, scope_node, (const pm_node_t *) string_node, &string_node->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
582 }
583
584 if (RTEST(current_string)) {
585 current_string = rb_str_concat(current_string, string_value);
586 }
587 else {
588 current_string = string_value;
589 if (index != 0) current_location = PM_NODE_END_LOCATION(scope_node->parser, part);
590 }
591 }
592 else {
593 interpolated = true;
594
595 if (
596 PM_NODE_TYPE_P(part, PM_EMBEDDED_STATEMENTS_NODE) &&
597 ((const pm_embedded_statements_node_t *) part)->statements != NULL &&
598 ((const pm_embedded_statements_node_t *) part)->statements->body.size == 1 &&
599 PM_NODE_TYPE_P(((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0], PM_STRING_NODE)
600 ) {
601 const pm_string_node_t *string_node = (const pm_string_node_t *) ((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0];
602 VALUE string_value;
603
604 if (implicit_regexp_encoding == NULL) {
605 string_value = parse_string_encoded(part, &string_node->unescaped, scope_node->encoding);
606 }
607 else {
608 string_value = parse_regexp_string_part(iseq, scope_node, (const pm_node_t *) string_node, &string_node->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
609 }
610
611 if (RTEST(current_string)) {
612 current_string = rb_str_concat(current_string, string_value);
613 }
614 else {
615 current_string = string_value;
616 current_location = PM_NODE_START_LOCATION(scope_node->parser, part);
617 }
618 }
619 else {
620 if (!RTEST(current_string)) {
621 rb_encoding *encoding;
622
623 if (implicit_regexp_encoding != NULL) {
624 if (explicit_regexp_encoding != NULL) {
625 encoding = explicit_regexp_encoding;
626 }
627 else if (scope_node->parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
628 encoding = rb_ascii8bit_encoding();
629 }
630 else {
631 encoding = implicit_regexp_encoding;
632 }
633 }
634 else {
635 encoding = scope_node->encoding;
636 }
637
638 if (parts_size == 1) {
639 current_string = rb_enc_str_new(NULL, 0, encoding);
640 }
641 }
642
643 if (RTEST(current_string)) {
644 VALUE operand = rb_fstring(current_string);
645 PUSH_INSN1(ret, current_location, putobject, operand);
646 stack_size++;
647 }
648
649 PM_COMPILE_NOT_POPPED(part);
650
651 const pm_node_location_t current_location = PM_NODE_START_LOCATION(scope_node->parser, part);
652 PUSH_INSN(ret, current_location, dup);
653
654 {
655 const struct rb_callinfo *callinfo = new_callinfo(iseq, idTo_s, 0, VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE, NULL, FALSE);
656 PUSH_INSN1(ret, current_location, objtostring, callinfo);
657 }
658
659 PUSH_INSN(ret, current_location, anytostring);
660
661 current_string = Qnil;
662 stack_size++;
663 }
664 }
665 }
666
667 if (RTEST(current_string)) {
668 current_string = rb_fstring(current_string);
669
670 if (stack_size == 0) {
671 if (frozen_result) {
672 PUSH_INSN1(ret, current_location, putobject, current_string);
673 } else if (mutable_result || interpolated) {
674 PUSH_INSN1(ret, current_location, putstring, current_string);
675 } else {
676 PUSH_INSN1(ret, current_location, putchilledstring, current_string);
677 }
678 } else {
679 PUSH_INSN1(ret, current_location, putobject, current_string);
680 }
681
682 current_string = Qnil;
683 stack_size++;
684 }
685 }
686 else {
687 PUSH_INSN(ret, *node_location, putnil);
688 }
689
690 return stack_size;
691}
692
693static void
694pm_compile_regexp_dynamic(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *parts, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
695{
696 rb_encoding *explicit_regexp_encoding = parse_regexp_encoding(scope_node, node);
697 rb_encoding *implicit_regexp_encoding = explicit_regexp_encoding != NULL ? explicit_regexp_encoding : scope_node->encoding;
698
699 int length = pm_interpolated_node_compile(iseq, parts, node_location, ret, popped, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false, false);
700 PUSH_INSN2(ret, *node_location, toregexp, INT2FIX(parse_regexp_flags(node) & 0xFF), INT2FIX(length));
701}
702
703static VALUE
704pm_source_file_value(const pm_source_file_node_t *node, const pm_scope_node_t *scope_node)
705{
706 const pm_string_t *filepath = &node->filepath;
707 size_t length = pm_string_length(filepath);
708
709 if (length > 0) {
710 rb_encoding *filepath_encoding = scope_node->filepath_encoding != NULL ? scope_node->filepath_encoding : rb_utf8_encoding();
711 return rb_enc_interned_str((const char *) pm_string_source(filepath), length, filepath_encoding);
712 }
713 else {
714 return rb_fstring_lit("<compiled>");
715 }
716}
717
722static VALUE
723pm_static_literal_string(rb_iseq_t *iseq, VALUE string, int line_number)
724{
725 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
726 return rb_str_with_debug_created_info(string, rb_iseq_path(iseq), line_number);
727 }
728 else {
729 return rb_fstring(string);
730 }
731}
732
738static VALUE
739pm_static_literal_value(rb_iseq_t *iseq, const pm_node_t *node, const pm_scope_node_t *scope_node)
740{
741 // Every node that comes into this function should already be marked as
742 // static literal. If it's not, then we have a bug somewhere.
743 RUBY_ASSERT(PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL));
744
745 switch (PM_NODE_TYPE(node)) {
746 case PM_ARRAY_NODE: {
747 const pm_array_node_t *cast = (const pm_array_node_t *) node;
748 const pm_node_list_t *elements = &cast->elements;
749
750 VALUE value = rb_ary_hidden_new(elements->size);
751 for (size_t index = 0; index < elements->size; index++) {
752 rb_ary_push(value, pm_static_literal_value(iseq, elements->nodes[index], scope_node));
753 }
754
755 OBJ_FREEZE(value);
756 return value;
757 }
758 case PM_FALSE_NODE:
759 return Qfalse;
760 case PM_FLOAT_NODE:
761 return parse_float((const pm_float_node_t *) node);
762 case PM_HASH_NODE: {
763 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
764 const pm_node_list_t *elements = &cast->elements;
765
766 VALUE array = rb_ary_hidden_new(elements->size * 2);
767 for (size_t index = 0; index < elements->size; index++) {
768 RUBY_ASSERT(PM_NODE_TYPE_P(elements->nodes[index], PM_ASSOC_NODE));
769 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) elements->nodes[index];
770 VALUE pair[2] = { pm_static_literal_value(iseq, cast->key, scope_node), pm_static_literal_value(iseq, cast->value, scope_node) };
771 rb_ary_cat(array, pair, 2);
772 }
773
774 VALUE value = rb_hash_new_with_size(elements->size);
775 rb_hash_bulk_insert(RARRAY_LEN(array), RARRAY_CONST_PTR(array), value);
776
777 value = rb_obj_hide(value);
778 OBJ_FREEZE(value);
779 return value;
780 }
781 case PM_IMAGINARY_NODE:
782 return parse_imaginary((const pm_imaginary_node_t *) node);
783 case PM_INTEGER_NODE:
784 return parse_integer((const pm_integer_node_t *) node);
785 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: {
786 const pm_interpolated_match_last_line_node_t *cast = (const pm_interpolated_match_last_line_node_t *) node;
787 return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts);
788 }
789 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
790 const pm_interpolated_regular_expression_node_t *cast = (const pm_interpolated_regular_expression_node_t *) node;
791 return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts);
792 }
793 case PM_INTERPOLATED_STRING_NODE: {
794 VALUE string = pm_static_literal_concat(iseq, &((const pm_interpolated_string_node_t *) node)->parts, scope_node, NULL, NULL, false);
795 int line_number = pm_node_line_number(scope_node->parser, node);
796 return pm_static_literal_string(iseq, string, line_number);
797 }
798 case PM_INTERPOLATED_SYMBOL_NODE: {
799 const pm_interpolated_symbol_node_t *cast = (const pm_interpolated_symbol_node_t *) node;
800 VALUE string = pm_static_literal_concat(iseq, &cast->parts, scope_node, NULL, NULL, true);
801
802 return ID2SYM(rb_intern_str(string));
803 }
804 case PM_MATCH_LAST_LINE_NODE: {
805 const pm_match_last_line_node_t *cast = (const pm_match_last_line_node_t *) node;
806 return parse_regexp_literal(iseq, scope_node, (const pm_node_t *) cast, &cast->unescaped);
807 }
808 case PM_NIL_NODE:
809 return Qnil;
810 case PM_RATIONAL_NODE:
811 return parse_rational((const pm_rational_node_t *) node);
812 case PM_REGULAR_EXPRESSION_NODE: {
813 const pm_regular_expression_node_t *cast = (const pm_regular_expression_node_t *) node;
814 return parse_regexp_literal(iseq, scope_node, (const pm_node_t *) cast, &cast->unescaped);
815 }
816 case PM_SOURCE_ENCODING_NODE:
817 return rb_enc_from_encoding(scope_node->encoding);
818 case PM_SOURCE_FILE_NODE: {
819 const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node;
820 return pm_source_file_value(cast, scope_node);
821 }
822 case PM_SOURCE_LINE_NODE:
823 return INT2FIX(pm_node_line_number(scope_node->parser, node));
824 case PM_STRING_NODE: {
825 const pm_string_node_t *cast = (const pm_string_node_t *) node;
826 return parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
827 }
828 case PM_SYMBOL_NODE:
829 return ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) node));
830 case PM_TRUE_NODE:
831 return Qtrue;
832 default:
833 rb_bug("Don't have a literal value for node type %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
834 return Qfalse;
835 }
836}
837
841static rb_code_location_t
842pm_code_location(const pm_scope_node_t *scope_node, const pm_node_t *node)
843{
844 const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
845 const pm_line_column_t end_location = PM_NODE_END_LINE_COLUMN(scope_node->parser, node);
846
847 return (rb_code_location_t) {
848 .beg_pos = { .lineno = start_location.line, .column = start_location.column },
849 .end_pos = { .lineno = end_location.line, .column = end_location.column }
850 };
851}
852
858#define PM_BRANCH_COVERAGE_P(iseq) (ISEQ_COVERAGE(iseq) && ISEQ_BRANCH_COVERAGE(iseq))
859
860static void
861pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond,
862 LABEL *then_label, LABEL *else_label, bool popped, pm_scope_node_t *scope_node);
863
864static void
865pm_compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_node_t *cond, LABEL *then_label, LABEL *else_label, bool popped, pm_scope_node_t *scope_node)
866{
867 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, cond);
868
869 DECL_ANCHOR(seq);
870
871 LABEL *label = NEW_LABEL(location.line);
872 if (!then_label) then_label = label;
873 else if (!else_label) else_label = label;
874
875 pm_compile_branch_condition(iseq, seq, cond, then_label, else_label, popped, scope_node);
876
877 if (LIST_INSN_SIZE_ONE(seq)) {
878 INSN *insn = (INSN *) ELEM_FIRST_INSN(FIRST_ELEMENT(seq));
879 if (insn->insn_id == BIN(jump) && (LABEL *)(insn->operands[0]) == label) return;
880 }
881
882 if (!label->refcnt) {
883 if (popped) PUSH_INSN(ret, location, putnil);
884 }
885 else {
886 PUSH_LABEL(seq, label);
887 }
888
889 PUSH_SEQ(ret, seq);
890 return;
891}
892
893static void
894pm_compile_flip_flop_bound(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
895{
896 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
897
898 if (PM_NODE_TYPE_P(node, PM_INTEGER_NODE)) {
899 PM_COMPILE_NOT_POPPED(node);
900
901 VALUE operand = ID2SYM(rb_intern("$."));
902 PUSH_INSN1(ret, location, getglobal, operand);
903
904 PUSH_SEND(ret, location, idEq, INT2FIX(1));
905 if (popped) PUSH_INSN(ret, location, pop);
906 }
907 else {
908 PM_COMPILE(node);
909 }
910}
911
912static void
913pm_compile_flip_flop(const pm_flip_flop_node_t *flip_flop_node, LABEL *else_label, LABEL *then_label, rb_iseq_t *iseq, const int lineno, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
914{
915 const pm_node_location_t location = { .line = lineno, .node_id = -1 };
916 LABEL *lend = NEW_LABEL(location.line);
917
918 int again = !(flip_flop_node->base.flags & PM_RANGE_FLAGS_EXCLUDE_END);
919
920 rb_num_t count = ISEQ_FLIP_CNT_INCREMENT(ISEQ_BODY(iseq)->local_iseq) + VM_SVAR_FLIPFLOP_START;
921 VALUE key = INT2FIX(count);
922
923 PUSH_INSN2(ret, location, getspecial, key, INT2FIX(0));
924 PUSH_INSNL(ret, location, branchif, lend);
925
926 if (flip_flop_node->left) {
927 pm_compile_flip_flop_bound(iseq, flip_flop_node->left, ret, popped, scope_node);
928 }
929 else {
930 PUSH_INSN(ret, location, putnil);
931 }
932
933 PUSH_INSNL(ret, location, branchunless, else_label);
934 PUSH_INSN1(ret, location, putobject, Qtrue);
935 PUSH_INSN1(ret, location, setspecial, key);
936 if (!again) {
937 PUSH_INSNL(ret, location, jump, then_label);
938 }
939
940 PUSH_LABEL(ret, lend);
941 if (flip_flop_node->right) {
942 pm_compile_flip_flop_bound(iseq, flip_flop_node->right, ret, popped, scope_node);
943 }
944 else {
945 PUSH_INSN(ret, location, putnil);
946 }
947
948 PUSH_INSNL(ret, location, branchunless, then_label);
949 PUSH_INSN1(ret, location, putobject, Qfalse);
950 PUSH_INSN1(ret, location, setspecial, key);
951 PUSH_INSNL(ret, location, jump, then_label);
952}
953
954static void pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_location_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition);
955
956static void
957pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond, LABEL *then_label, LABEL *else_label, bool popped, pm_scope_node_t *scope_node)
958{
959 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, cond);
960
961again:
962 switch (PM_NODE_TYPE(cond)) {
963 case PM_AND_NODE: {
964 const pm_and_node_t *cast = (const pm_and_node_t *) cond;
965 pm_compile_logical(iseq, ret, cast->left, NULL, else_label, popped, scope_node);
966
967 cond = cast->right;
968 goto again;
969 }
970 case PM_OR_NODE: {
971 const pm_or_node_t *cast = (const pm_or_node_t *) cond;
972 pm_compile_logical(iseq, ret, cast->left, then_label, NULL, popped, scope_node);
973
974 cond = cast->right;
975 goto again;
976 }
977 case PM_FALSE_NODE:
978 case PM_NIL_NODE:
979 PUSH_INSNL(ret, location, jump, else_label);
980 return;
981 case PM_FLOAT_NODE:
982 case PM_IMAGINARY_NODE:
983 case PM_INTEGER_NODE:
984 case PM_LAMBDA_NODE:
985 case PM_RATIONAL_NODE:
986 case PM_REGULAR_EXPRESSION_NODE:
987 case PM_STRING_NODE:
988 case PM_SYMBOL_NODE:
989 case PM_TRUE_NODE:
990 PUSH_INSNL(ret, location, jump, then_label);
991 return;
992 case PM_FLIP_FLOP_NODE:
993 pm_compile_flip_flop((const pm_flip_flop_node_t *) cond, else_label, then_label, iseq, location.line, ret, popped, scope_node);
994 return;
995 case PM_DEFINED_NODE: {
996 const pm_defined_node_t *cast = (const pm_defined_node_t *) cond;
997 pm_compile_defined_expr(iseq, cast->value, &location, ret, popped, scope_node, true);
998 break;
999 }
1000 default: {
1001 DECL_ANCHOR(cond_seq);
1002 pm_compile_node(iseq, cond, cond_seq, false, scope_node);
1003
1004 if (LIST_INSN_SIZE_ONE(cond_seq)) {
1005 INSN *insn = (INSN *) ELEM_FIRST_INSN(FIRST_ELEMENT(cond_seq));
1006
1007 if (insn->insn_id == BIN(putobject)) {
1008 if (RTEST(insn->operands[0])) {
1009 PUSH_INSNL(ret, location, jump, then_label);
1010 // maybe unreachable
1011 return;
1012 }
1013 else {
1014 PUSH_INSNL(ret, location, jump, else_label);
1015 return;
1016 }
1017 }
1018 }
1019
1020 PUSH_SEQ(ret, cond_seq);
1021 break;
1022 }
1023 }
1024
1025 PUSH_INSNL(ret, location, branchunless, else_label);
1026 PUSH_INSNL(ret, location, jump, then_label);
1027}
1028
1032static void
1033pm_compile_conditional(rb_iseq_t *iseq, const pm_node_location_t *node_location, pm_node_type_t type, const pm_node_t *node, const pm_statements_node_t *statements, const pm_node_t *subsequent, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1034{
1035 const pm_node_location_t location = *node_location;
1036 LABEL *then_label = NEW_LABEL(location.line);
1037 LABEL *else_label = NEW_LABEL(location.line);
1038 LABEL *end_label = NULL;
1039
1040 DECL_ANCHOR(cond_seq);
1041 pm_compile_branch_condition(iseq, cond_seq, predicate, then_label, else_label, false, scope_node);
1042 PUSH_SEQ(ret, cond_seq);
1043
1044 rb_code_location_t conditional_location = { 0 };
1045 VALUE branches = Qfalse;
1046
1047 if (then_label->refcnt && else_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) {
1048 conditional_location = pm_code_location(scope_node, node);
1049 branches = decl_branch_base(iseq, PTR2NUM(node), &conditional_location, type == PM_IF_NODE ? "if" : "unless");
1050 }
1051
1052 if (then_label->refcnt) {
1053 PUSH_LABEL(ret, then_label);
1054
1055 DECL_ANCHOR(then_seq);
1056
1057 if (statements != NULL) {
1058 pm_compile_node(iseq, (const pm_node_t *) statements, then_seq, popped, scope_node);
1059 }
1060 else if (!popped) {
1061 PUSH_SYNTHETIC_PUTNIL(then_seq, iseq);
1062 }
1063
1064 if (else_label->refcnt) {
1065 // Establish branch coverage for the then block.
1066 if (PM_BRANCH_COVERAGE_P(iseq)) {
1067 rb_code_location_t branch_location;
1068
1069 if (statements != NULL) {
1070 branch_location = pm_code_location(scope_node, (const pm_node_t *) statements);
1071 } else if (type == PM_IF_NODE) {
1072 pm_line_column_t predicate_end = PM_NODE_END_LINE_COLUMN(scope_node->parser, predicate);
1073 branch_location = (rb_code_location_t) {
1074 .beg_pos = { .lineno = predicate_end.line, .column = predicate_end.column },
1075 .end_pos = { .lineno = predicate_end.line, .column = predicate_end.column }
1076 };
1077 } else {
1078 branch_location = conditional_location;
1079 }
1080
1081 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, type == PM_IF_NODE ? "then" : "else", branches);
1082 }
1083
1084 end_label = NEW_LABEL(location.line);
1085 PUSH_INSNL(then_seq, location, jump, end_label);
1086 if (!popped) PUSH_INSN(then_seq, location, pop);
1087 }
1088
1089 PUSH_SEQ(ret, then_seq);
1090 }
1091
1092 if (else_label->refcnt) {
1093 PUSH_LABEL(ret, else_label);
1094
1095 DECL_ANCHOR(else_seq);
1096
1097 if (subsequent != NULL) {
1098 pm_compile_node(iseq, subsequent, else_seq, popped, scope_node);
1099 }
1100 else if (!popped) {
1101 PUSH_SYNTHETIC_PUTNIL(else_seq, iseq);
1102 }
1103
1104 // Establish branch coverage for the else block.
1105 if (then_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) {
1106 rb_code_location_t branch_location;
1107
1108 if (subsequent == NULL) {
1109 branch_location = conditional_location;
1110 } else if (PM_NODE_TYPE_P(subsequent, PM_ELSE_NODE)) {
1111 const pm_else_node_t *else_node = (const pm_else_node_t *) subsequent;
1112 branch_location = pm_code_location(scope_node, else_node->statements != NULL ? ((const pm_node_t *) else_node->statements) : (const pm_node_t *) else_node);
1113 } else {
1114 branch_location = pm_code_location(scope_node, (const pm_node_t *) subsequent);
1115 }
1116
1117 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 1, type == PM_IF_NODE ? "else" : "then", branches);
1118 }
1119
1120 PUSH_SEQ(ret, else_seq);
1121 }
1122
1123 if (end_label) {
1124 PUSH_LABEL(ret, end_label);
1125 }
1126
1127 return;
1128}
1129
1133static void
1134pm_compile_loop(rb_iseq_t *iseq, const pm_node_location_t *node_location, pm_node_flags_t flags, enum pm_node_type type, const pm_node_t *node, const pm_statements_node_t *statements, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1135{
1136 const pm_node_location_t location = *node_location;
1137
1138 LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
1139 LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
1140 LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
1141
1142 LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(location.line); /* next */
1143 LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(location.line); /* redo */
1144 LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(location.line); /* break */
1145 LABEL *end_label = NEW_LABEL(location.line);
1146 LABEL *adjust_label = NEW_LABEL(location.line);
1147
1148 LABEL *next_catch_label = NEW_LABEL(location.line);
1149 LABEL *tmp_label = NULL;
1150
1151 // We're pushing onto the ensure stack because breaks need to break out of
1152 // this loop and not break into the ensure statements within the same
1153 // lexical scope.
1154 struct iseq_compile_data_ensure_node_stack enl;
1155 push_ensure_entry(iseq, &enl, NULL, NULL);
1156
1157 // begin; end while true
1158 if (flags & PM_LOOP_FLAGS_BEGIN_MODIFIER) {
1159 tmp_label = NEW_LABEL(location.line);
1160 PUSH_INSNL(ret, location, jump, tmp_label);
1161 }
1162 else {
1163 // while true; end
1164 PUSH_INSNL(ret, location, jump, next_label);
1165 }
1166
1167 PUSH_LABEL(ret, adjust_label);
1168 PUSH_INSN(ret, location, putnil);
1169 PUSH_LABEL(ret, next_catch_label);
1170 PUSH_INSN(ret, location, pop);
1171 PUSH_INSNL(ret, location, jump, next_label);
1172 if (tmp_label) PUSH_LABEL(ret, tmp_label);
1173
1174 PUSH_LABEL(ret, redo_label);
1175
1176 // Establish branch coverage for the loop.
1177 if (PM_BRANCH_COVERAGE_P(iseq)) {
1178 rb_code_location_t loop_location = pm_code_location(scope_node, node);
1179 VALUE branches = decl_branch_base(iseq, PTR2NUM(node), &loop_location, type == PM_WHILE_NODE ? "while" : "until");
1180
1181 rb_code_location_t branch_location = statements != NULL ? pm_code_location(scope_node, (const pm_node_t *) statements) : loop_location;
1182 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, "body", branches);
1183 }
1184
1185 if (statements != NULL) PM_COMPILE_POPPED((const pm_node_t *) statements);
1186 PUSH_LABEL(ret, next_label);
1187
1188 if (type == PM_WHILE_NODE) {
1189 pm_compile_branch_condition(iseq, ret, predicate, redo_label, end_label, popped, scope_node);
1190 }
1191 else if (type == PM_UNTIL_NODE) {
1192 pm_compile_branch_condition(iseq, ret, predicate, end_label, redo_label, popped, scope_node);
1193 }
1194
1195 PUSH_LABEL(ret, end_label);
1196 PUSH_ADJUST_RESTORE(ret, adjust_label);
1197 PUSH_INSN(ret, location, putnil);
1198
1199 PUSH_LABEL(ret, break_label);
1200 if (popped) PUSH_INSN(ret, location, pop);
1201
1202 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL, break_label);
1203 PUSH_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL, next_catch_label);
1204 PUSH_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL, ISEQ_COMPILE_DATA(iseq)->redo_label);
1205
1206 ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
1207 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
1208 ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
1209 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->prev;
1210
1211 return;
1212}
1213
1214// This recurses through scopes and finds the local index at any scope level
1215// It also takes a pointer to depth, and increments depth appropriately
1216// according to the depth of the local.
1217static pm_local_index_t
1218pm_lookup_local_index(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, pm_constant_id_t constant_id, int start_depth)
1219{
1220 pm_local_index_t lindex = { 0 };
1221 st_data_t local_index;
1222
1223 int level;
1224 for (level = 0; level < start_depth; level++) {
1225 scope_node = scope_node->previous;
1226 }
1227
1228 while (!st_lookup(scope_node->index_lookup_table, constant_id, &local_index)) {
1229 level++;
1230
1231 if (scope_node->previous) {
1232 scope_node = scope_node->previous;
1233 }
1234 else {
1235 // We have recursed up all scope nodes
1236 // and have not found the local yet
1237 rb_bug("Local with constant_id %u does not exist", (unsigned int) constant_id);
1238 }
1239 }
1240
1241 lindex.level = level;
1242 lindex.index = scope_node->local_table_for_iseq_size - (int) local_index;
1243 return lindex;
1244}
1245
1246// This returns the CRuby ID which maps to the pm_constant_id_t
1247//
1248// Constant_ids in prism are indexes of the constants in prism's constant pool.
1249// We add a constants mapping on the scope_node which is a mapping from
1250// these constant_id indexes to the CRuby IDs that they represent.
1251// This helper method allows easy access to those IDs
1252static ID
1253pm_constant_id_lookup(const pm_scope_node_t *scope_node, pm_constant_id_t constant_id)
1254{
1255 if (constant_id < 1 || constant_id > scope_node->parser->constant_pool.size) {
1256 rb_bug("constant_id out of range: %u", (unsigned int)constant_id);
1257 }
1258 return scope_node->constants[constant_id - 1];
1259}
1260
1261static rb_iseq_t *
1262pm_new_child_iseq(rb_iseq_t *iseq, pm_scope_node_t *node, VALUE name, const rb_iseq_t *parent, enum rb_iseq_type type, int line_no)
1263{
1264 debugs("[new_child_iseq]> ---------------------------------------\n");
1265 int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
1266 int error_state;
1267 rb_iseq_t *ret_iseq = pm_iseq_new_with_opt(node, name,
1268 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
1269 line_no, parent,
1270 isolated_depth ? isolated_depth + 1 : 0,
1271 type, ISEQ_COMPILE_DATA(iseq)->option, &error_state);
1272
1273 if (error_state) {
1274 pm_scope_node_destroy(node);
1275 RUBY_ASSERT(ret_iseq == NULL);
1276 rb_jump_tag(error_state);
1277 }
1278 debugs("[new_child_iseq]< ---------------------------------------\n");
1279 return ret_iseq;
1280}
1281
1282static int
1283pm_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)
1284{
1285 if (PM_NODE_TYPE_P(node, PM_CONSTANT_PATH_NODE)) {
1286 const pm_node_t *parent = ((const pm_constant_path_node_t *) node)->parent;
1287
1288 if (parent) {
1289 /* Bar::Foo */
1290 PM_COMPILE(parent);
1291 return VM_DEFINECLASS_FLAG_SCOPED;
1292 }
1293 else {
1294 /* toplevel class ::Foo */
1295 PUSH_INSN1(ret, *node_location, putobject, rb_cObject);
1296 return VM_DEFINECLASS_FLAG_SCOPED;
1297 }
1298 }
1299 else {
1300 /* class at cbase Foo */
1301 PUSH_INSN1(ret, *node_location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
1302 return 0;
1303 }
1304}
1305
1310static void
1311pm_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)
1312{
1313 const pm_node_location_t location = *node_location;
1314 LABEL *lfin = NEW_LABEL(location.line);
1315 LABEL *lcfin = NEW_LABEL(location.line);
1316 LABEL *lskip = NULL;
1317
1318 int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
1319 ID id_read_name = pm_constant_id_lookup(scope_node, read_name);
1320
1321 PM_COMPILE_NOT_POPPED(receiver);
1322 if (safe_nav) {
1323 lskip = NEW_LABEL(location.line);
1324 PUSH_INSN(ret, location, dup);
1325 PUSH_INSNL(ret, location, branchnil, lskip);
1326 }
1327
1328 PUSH_INSN(ret, location, dup);
1329 PUSH_SEND_WITH_FLAG(ret, location, id_read_name, INT2FIX(0), INT2FIX(flag));
1330 if (!popped) PUSH_INSN(ret, location, dup);
1331
1332 if (and_node) {
1333 PUSH_INSNL(ret, location, branchunless, lcfin);
1334 }
1335 else {
1336 PUSH_INSNL(ret, location, branchif, lcfin);
1337 }
1338
1339 if (!popped) PUSH_INSN(ret, location, pop);
1340 PM_COMPILE_NOT_POPPED(value);
1341
1342 if (!popped) {
1343 PUSH_INSN(ret, location, swap);
1344 PUSH_INSN1(ret, location, topn, INT2FIX(1));
1345 }
1346
1347 ID id_write_name = pm_constant_id_lookup(scope_node, write_name);
1348 PUSH_SEND_WITH_FLAG(ret, location, id_write_name, INT2FIX(1), INT2FIX(flag));
1349 PUSH_INSNL(ret, location, jump, lfin);
1350
1351 PUSH_LABEL(ret, lcfin);
1352 if (!popped) PUSH_INSN(ret, location, swap);
1353
1354 PUSH_LABEL(ret, lfin);
1355
1356 if (lskip && popped) PUSH_LABEL(ret, lskip);
1357 PUSH_INSN(ret, location, pop);
1358 if (lskip && !popped) PUSH_LABEL(ret, lskip);
1359}
1360
1361static 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);
1362
1368static void
1369pm_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)
1370{
1371 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
1372
1373 // If this element is not popped, then we need to create the hash on the
1374 // stack. Neighboring plain assoc nodes should be grouped together (either
1375 // by newhash or hash merge). Double splat nodes should be merged using the
1376 // merge_kwd method call.
1377 const int max_stack_length = 0x100;
1378 const unsigned int min_tmp_hash_length = 0x800;
1379
1380 int stack_length = 0;
1381 bool first_chunk = true;
1382
1383 // This is an optimization wherein we keep track of whether or not the
1384 // previous element was a static literal. If it was, then we do not attempt
1385 // to check if we have a subhash that can be optimized. If it was not, then
1386 // we do check.
1387 bool static_literal = false;
1388
1389 DECL_ANCHOR(anchor);
1390
1391 // Convert pushed elements to a hash, and merge if needed.
1392#define FLUSH_CHUNK \
1393 if (stack_length) { \
1394 if (first_chunk) { \
1395 PUSH_SEQ(ret, anchor); \
1396 PUSH_INSN1(ret, location, newhash, INT2FIX(stack_length)); \
1397 first_chunk = false; \
1398 } \
1399 else { \
1400 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); \
1401 PUSH_INSN(ret, location, swap); \
1402 PUSH_SEQ(ret, anchor); \
1403 PUSH_SEND(ret, location, id_core_hash_merge_ptr, INT2FIX(stack_length + 1)); \
1404 } \
1405 INIT_ANCHOR(anchor); \
1406 stack_length = 0; \
1407 }
1408
1409 for (size_t index = 0; index < elements->size; index++) {
1410 const pm_node_t *element = elements->nodes[index];
1411
1412 switch (PM_NODE_TYPE(element)) {
1413 case PM_ASSOC_NODE: {
1414 // Pre-allocation check (this branch can be omitted).
1415 if (
1416 (shareability == 0) &&
1417 PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL) && (
1418 (!static_literal && ((index + min_tmp_hash_length) < elements->size)) ||
1419 (first_chunk && stack_length == 0)
1420 )
1421 ) {
1422 // Count the elements that are statically-known.
1423 size_t count = 1;
1424 while (index + count < elements->size && PM_NODE_FLAG_P(elements->nodes[index + count], PM_NODE_FLAG_STATIC_LITERAL)) count++;
1425
1426 if ((first_chunk && stack_length == 0) || count >= min_tmp_hash_length) {
1427 // The subsequence of elements in this hash is long enough
1428 // to merit its own hash.
1429 VALUE ary = rb_ary_hidden_new(count);
1430
1431 // Create a hidden hash.
1432 for (size_t tmp_end = index + count; index < tmp_end; index++) {
1433 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[index];
1434
1435 VALUE elem[2] = {
1436 pm_static_literal_value(iseq, assoc->key, scope_node),
1437 pm_static_literal_value(iseq, assoc->value, scope_node)
1438 };
1439
1440 rb_ary_cat(ary, elem, 2);
1441 }
1442 index --;
1443
1444 VALUE hash = rb_hash_new_with_size(RARRAY_LEN(ary) / 2);
1445 rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary), hash);
1446 hash = rb_obj_hide(hash);
1447 OBJ_FREEZE(hash);
1448
1449 // Emit optimized code.
1450 FLUSH_CHUNK;
1451 if (first_chunk) {
1452 PUSH_INSN1(ret, location, duphash, hash);
1453 first_chunk = false;
1454 }
1455 else {
1456 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1457 PUSH_INSN(ret, location, swap);
1458 PUSH_INSN1(ret, location, putobject, hash);
1459 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1460 }
1461
1462 break;
1463 }
1464 else {
1465 static_literal = true;
1466 }
1467 }
1468 else {
1469 static_literal = false;
1470 }
1471
1472 // If this is a plain assoc node, then we can compile it directly
1473 // and then add the total number of values on the stack.
1474 if (shareability == 0) {
1475 pm_compile_node(iseq, element, anchor, false, scope_node);
1476 }
1477 else {
1478 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
1479 pm_compile_shareable_constant_value(iseq, assoc->key, shareability, path, ret, scope_node, false);
1480 pm_compile_shareable_constant_value(iseq, assoc->value, shareability, path, ret, scope_node, false);
1481 }
1482
1483 if ((stack_length += 2) >= max_stack_length) FLUSH_CHUNK;
1484 break;
1485 }
1486 case PM_ASSOC_SPLAT_NODE: {
1487 FLUSH_CHUNK;
1488
1489 const pm_assoc_splat_node_t *assoc_splat = (const pm_assoc_splat_node_t *) element;
1490 bool empty_hash = assoc_splat->value != NULL && (
1491 (PM_NODE_TYPE_P(assoc_splat->value, PM_HASH_NODE) && ((const pm_hash_node_t *) assoc_splat->value)->elements.size == 0) ||
1492 PM_NODE_TYPE_P(assoc_splat->value, PM_NIL_NODE)
1493 );
1494
1495 bool first_element = first_chunk && stack_length == 0;
1496 bool last_element = index == elements->size - 1;
1497 bool only_element = first_element && last_element;
1498
1499 if (empty_hash) {
1500 if (only_element && argument) {
1501 // **{} appears at the only keyword argument in method call,
1502 // so it won't be modified.
1503 //
1504 // This is only done for method calls and not for literal
1505 // hashes, because literal hashes should always result in a
1506 // new hash.
1507 PUSH_INSN(ret, location, putnil);
1508 }
1509 else if (first_element) {
1510 // **{} appears as the first keyword argument, so it may be
1511 // modified. We need to create a fresh hash object.
1512 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
1513 }
1514 // Any empty keyword splats that are not the first can be
1515 // ignored since merging an empty hash into the existing hash is
1516 // the same as not merging it.
1517 }
1518 else {
1519 if (only_element && argument) {
1520 // ** is only keyword argument in the method call. Use it
1521 // directly. This will be not be flagged as mutable. This is
1522 // only done for method calls and not for literal hashes,
1523 // because literal hashes should always result in a new
1524 // hash.
1525 if (shareability == 0) {
1526 PM_COMPILE_NOT_POPPED(element);
1527 }
1528 else {
1529 pm_compile_shareable_constant_value(iseq, element, shareability, path, ret, scope_node, false);
1530 }
1531 }
1532 else {
1533 // There is more than one keyword argument, or this is not a
1534 // method call. In that case, we need to add an empty hash
1535 // (if first keyword), or merge the hash to the accumulated
1536 // hash (if not the first keyword).
1537 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1538
1539 if (first_element) {
1540 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
1541 }
1542 else {
1543 PUSH_INSN(ret, location, swap);
1544 }
1545
1546 if (shareability == 0) {
1547 PM_COMPILE_NOT_POPPED(element);
1548 }
1549 else {
1550 pm_compile_shareable_constant_value(iseq, element, shareability, path, ret, scope_node, false);
1551 }
1552
1553 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1554 }
1555 }
1556
1557 first_chunk = false;
1558 static_literal = false;
1559 break;
1560 }
1561 default:
1562 RUBY_ASSERT("Invalid node type for hash" && false);
1563 break;
1564 }
1565 }
1566
1567 FLUSH_CHUNK;
1568#undef FLUSH_CHUNK
1569}
1570
1571#define SPLATARRAY_FALSE 0
1572#define SPLATARRAY_TRUE 1
1573#define DUP_SINGLE_KW_SPLAT 2
1574
1575// This is details. Users should call pm_setup_args() instead.
1576static int
1577pm_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)
1578{
1579 const pm_node_location_t location = *node_location;
1580
1581 int orig_argc = 0;
1582 bool has_splat = false;
1583 bool has_keyword_splat = false;
1584
1585 if (arguments_node == NULL) {
1586 if (*flags & VM_CALL_FCALL) {
1587 *flags |= VM_CALL_VCALL;
1588 }
1589 }
1590 else {
1591 const pm_node_list_t *arguments = &arguments_node->arguments;
1592 has_keyword_splat = PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT);
1593
1594 // We count the number of elements post the splat node that are not keyword elements to
1595 // eventually pass as an argument to newarray
1596 int post_splat_counter = 0;
1597 const pm_node_t *argument;
1598
1599 PM_NODE_LIST_FOREACH(arguments, index, argument) {
1600 switch (PM_NODE_TYPE(argument)) {
1601 // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
1602 case PM_KEYWORD_HASH_NODE: {
1603 const pm_keyword_hash_node_t *keyword_arg = (const pm_keyword_hash_node_t *) argument;
1604 const pm_node_list_t *elements = &keyword_arg->elements;
1605
1606 if (has_keyword_splat || has_splat) {
1607 *flags |= VM_CALL_KW_SPLAT;
1608 has_keyword_splat = true;
1609
1610 if (elements->size > 1 || !(elements->size == 1 && PM_NODE_TYPE_P(elements->nodes[0], PM_ASSOC_SPLAT_NODE))) {
1611 // A new hash will be created for the keyword arguments
1612 // in this case, so mark the method as passing mutable
1613 // keyword splat.
1614 *flags |= VM_CALL_KW_SPLAT_MUT;
1615 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1616 }
1617 else if (*dup_rest & DUP_SINGLE_KW_SPLAT) {
1618 *flags |= VM_CALL_KW_SPLAT_MUT;
1619 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1620 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
1621 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1622 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1623 }
1624 else {
1625 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1626 }
1627 }
1628 else {
1629 // We need to first figure out if all elements of the
1630 // KeywordHashNode are AssocNodes with symbol keys.
1631 if (PM_NODE_FLAG_P(keyword_arg, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS)) {
1632 // If they are all symbol keys then we can pass them as
1633 // keyword arguments. The first thing we need to do is
1634 // deduplicate. We'll do this using the combination of a
1635 // Ruby hash and a Ruby array.
1636 VALUE stored_indices = rb_hash_new();
1637 VALUE keyword_indices = rb_ary_new_capa(elements->size);
1638
1639 size_t size = 0;
1640 for (size_t element_index = 0; element_index < elements->size; element_index++) {
1641 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1642
1643 // Retrieve the stored index from the hash for this
1644 // keyword.
1645 VALUE keyword = pm_static_literal_value(iseq, assoc->key, scope_node);
1646 VALUE stored_index = rb_hash_aref(stored_indices, keyword);
1647
1648 // If this keyword was already seen in the hash,
1649 // then mark the array at that index as false and
1650 // decrement the keyword size.
1651 if (!NIL_P(stored_index)) {
1652 rb_ary_store(keyword_indices, NUM2LONG(stored_index), Qfalse);
1653 size--;
1654 }
1655
1656 // Store (and possibly overwrite) the index for this
1657 // keyword in the hash, mark the array at that index
1658 // as true, and increment the keyword size.
1659 rb_hash_aset(stored_indices, keyword, ULONG2NUM(element_index));
1660 rb_ary_store(keyword_indices, (long) element_index, Qtrue);
1661 size++;
1662 }
1663
1664 *kw_arg = rb_xmalloc_mul_add(size, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
1665 *flags |= VM_CALL_KWARG;
1666
1667 VALUE *keywords = (*kw_arg)->keywords;
1668 (*kw_arg)->references = 0;
1669 (*kw_arg)->keyword_len = (int) size;
1670
1671 size_t keyword_index = 0;
1672 for (size_t element_index = 0; element_index < elements->size; element_index++) {
1673 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1674 bool popped = true;
1675
1676 if (rb_ary_entry(keyword_indices, (long) element_index) == Qtrue) {
1677 keywords[keyword_index++] = pm_static_literal_value(iseq, assoc->key, scope_node);
1678 popped = false;
1679 }
1680
1681 PM_COMPILE(assoc->value);
1682 }
1683
1684 RUBY_ASSERT(keyword_index == size);
1685 }
1686 else {
1687 // If they aren't all symbol keys then we need to
1688 // construct a new hash and pass that as an argument.
1689 orig_argc++;
1690 *flags |= VM_CALL_KW_SPLAT;
1691
1692 size_t size = elements->size;
1693 if (size > 1) {
1694 // A new hash will be created for the keyword
1695 // arguments in this case, so mark the method as
1696 // passing mutable keyword splat.
1697 *flags |= VM_CALL_KW_SPLAT_MUT;
1698 }
1699
1700 for (size_t element_index = 0; element_index < size; element_index++) {
1701 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1702 PM_COMPILE_NOT_POPPED(assoc->key);
1703 PM_COMPILE_NOT_POPPED(assoc->value);
1704 }
1705
1706 PUSH_INSN1(ret, location, newhash, INT2FIX(size * 2));
1707 }
1708 }
1709 break;
1710 }
1711 case PM_SPLAT_NODE: {
1712 *flags |= VM_CALL_ARGS_SPLAT;
1713 const pm_splat_node_t *splat_node = (const pm_splat_node_t *) argument;
1714
1715 if (splat_node->expression) {
1716 PM_COMPILE_NOT_POPPED(splat_node->expression);
1717 }
1718 else {
1719 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
1720 PUSH_GETLOCAL(ret, location, index.index, index.level);
1721 }
1722
1723 bool first_splat = !has_splat;
1724
1725 if (first_splat) {
1726 // If this is the first splat array seen and it's not the
1727 // last parameter, we want splatarray to dup it.
1728 //
1729 // foo(a, *b, c)
1730 // ^^
1731 if (index + 1 < arguments->size || has_regular_blockarg) {
1732 PUSH_INSN1(ret, location, splatarray, (*dup_rest & SPLATARRAY_TRUE) ? Qtrue : Qfalse);
1733 if (*dup_rest & SPLATARRAY_TRUE) *dup_rest &= ~SPLATARRAY_TRUE;
1734 }
1735 // If this is the first spalt array seen and it's the last
1736 // parameter, we don't want splatarray to dup it.
1737 //
1738 // foo(a, *b)
1739 // ^^
1740 else {
1741 PUSH_INSN1(ret, location, splatarray, Qfalse);
1742 }
1743 }
1744 else {
1745 // If this is not the first splat array seen and it is also
1746 // the last parameter, we don't want splatarray to dup it
1747 // and we need to concat the array.
1748 //
1749 // foo(a, *b, *c)
1750 // ^^
1751 PUSH_INSN(ret, location, concattoarray);
1752 }
1753
1754 has_splat = true;
1755 post_splat_counter = 0;
1756
1757 break;
1758 }
1759 case PM_FORWARDING_ARGUMENTS_NODE: { // not counted in argc return value
1760 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
1761
1762 if (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
1763 *flags |= VM_CALL_FORWARDING;
1764
1765 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_DOT3, 0);
1766 PUSH_GETLOCAL(ret, location, mult_local.index, mult_local.level);
1767
1768 break;
1769 }
1770
1771 orig_argc += 2;
1772
1773 *flags |= VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_SPLAT_MUT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KW_SPLAT;
1774
1775 // Forwarding arguments nodes are treated as foo(*, **, &)
1776 // So foo(...) equals foo(*, **, &) and as such the local
1777 // table for this method is known in advance
1778 //
1779 // Push the *
1780 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
1781 PUSH_GETLOCAL(ret, location, mult_local.index, mult_local.level);
1782 PUSH_INSN1(ret, location, splatarray, Qtrue);
1783
1784 // Push the **
1785 pm_local_index_t pow_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_POW, 0);
1786 PUSH_GETLOCAL(ret, location, pow_local.index, pow_local.level);
1787
1788 // Push the &
1789 pm_local_index_t and_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_AND, 0);
1790 PUSH_INSN2(ret, location, getblockparamproxy, INT2FIX(and_local.index + VM_ENV_DATA_SIZE - 1), INT2FIX(and_local.level));
1791 PUSH_INSN(ret, location, splatkw);
1792
1793 break;
1794 }
1795 default: {
1796 post_splat_counter++;
1797 PM_COMPILE_NOT_POPPED(argument);
1798
1799 // If we have a splat and we've seen a splat, we need to process
1800 // everything after the splat.
1801 if (has_splat) {
1802 // Stack items are turned into an array and concatenated in
1803 // the following cases:
1804 //
1805 // If the next node is a splat:
1806 //
1807 // foo(*a, b, *c)
1808 //
1809 // If the next node is a kwarg or kwarg splat:
1810 //
1811 // foo(*a, b, c: :d)
1812 // foo(*a, b, **c)
1813 //
1814 // If the next node is NULL (we have hit the end):
1815 //
1816 // foo(*a, b)
1817 if (index == arguments->size - 1) {
1818 RUBY_ASSERT(post_splat_counter > 0);
1819 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(post_splat_counter));
1820 }
1821 else {
1822 pm_node_t *next_arg = arguments->nodes[index + 1];
1823
1824 switch (PM_NODE_TYPE(next_arg)) {
1825 // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
1826 case PM_KEYWORD_HASH_NODE: {
1827 PUSH_INSN1(ret, location, newarray, INT2FIX(post_splat_counter));
1828 PUSH_INSN(ret, location, concatarray);
1829 break;
1830 }
1831 case PM_SPLAT_NODE: {
1832 PUSH_INSN1(ret, location, newarray, INT2FIX(post_splat_counter));
1833 PUSH_INSN(ret, location, concatarray);
1834 break;
1835 }
1836 default:
1837 break;
1838 }
1839 }
1840 }
1841 else {
1842 orig_argc++;
1843 }
1844 }
1845 }
1846 }
1847 }
1848
1849 if (has_splat) orig_argc++;
1850 if (has_keyword_splat) orig_argc++;
1851 return orig_argc;
1852}
1853
1858static inline bool
1859pm_setup_args_dup_rest_p(const pm_node_t *node)
1860{
1861 switch (PM_NODE_TYPE(node)) {
1862 case PM_BACK_REFERENCE_READ_NODE:
1863 case PM_CLASS_VARIABLE_READ_NODE:
1864 case PM_CONSTANT_READ_NODE:
1865 case PM_FALSE_NODE:
1866 case PM_FLOAT_NODE:
1867 case PM_GLOBAL_VARIABLE_READ_NODE:
1868 case PM_IMAGINARY_NODE:
1869 case PM_INSTANCE_VARIABLE_READ_NODE:
1870 case PM_INTEGER_NODE:
1871 case PM_LAMBDA_NODE:
1872 case PM_LOCAL_VARIABLE_READ_NODE:
1873 case PM_NIL_NODE:
1874 case PM_NUMBERED_REFERENCE_READ_NODE:
1875 case PM_RATIONAL_NODE:
1876 case PM_REGULAR_EXPRESSION_NODE:
1877 case PM_SELF_NODE:
1878 case PM_STRING_NODE:
1879 case PM_SYMBOL_NODE:
1880 case PM_TRUE_NODE:
1881 return false;
1882 case PM_CONSTANT_PATH_NODE: {
1883 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
1884 if (cast->parent != NULL) {
1885 return pm_setup_args_dup_rest_p(cast->parent);
1886 }
1887 return false;
1888 }
1889 case PM_IMPLICIT_NODE:
1890 return pm_setup_args_dup_rest_p(((const pm_implicit_node_t *) node)->value);
1891 default:
1892 return true;
1893 }
1894}
1895
1899static int
1900pm_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)
1901{
1902 int dup_rest = SPLATARRAY_TRUE;
1903
1904 const pm_node_list_t *arguments;
1905 size_t arguments_size;
1906
1907 // Calls like foo(1, *f, **hash) that use splat and kwsplat could be
1908 // eligible for eliding duping the rest array (dup_reset=false).
1909 if (
1910 arguments_node != NULL &&
1911 (arguments = &arguments_node->arguments, arguments_size = arguments->size) >= 2 &&
1912 PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT) &&
1913 !PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS) &&
1914 PM_NODE_TYPE_P(arguments->nodes[arguments_size - 1], PM_KEYWORD_HASH_NODE)
1915 ) {
1916 // Start by assuming that dup_rest=false, then check each element of the
1917 // hash to ensure we don't need to flip it back to true (in case one of
1918 // the elements could potentially mutate the array).
1919 dup_rest = SPLATARRAY_FALSE;
1920
1921 const pm_keyword_hash_node_t *keyword_hash = (const pm_keyword_hash_node_t *) arguments->nodes[arguments_size - 1];
1922 const pm_node_list_t *elements = &keyword_hash->elements;
1923
1924 for (size_t index = 0; dup_rest == SPLATARRAY_FALSE && index < elements->size; index++) {
1925 const pm_node_t *element = elements->nodes[index];
1926
1927 switch (PM_NODE_TYPE(element)) {
1928 case PM_ASSOC_NODE: {
1929 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
1930 if (pm_setup_args_dup_rest_p(assoc->key) || pm_setup_args_dup_rest_p(assoc->value)) dup_rest = SPLATARRAY_TRUE;
1931 break;
1932 }
1933 case PM_ASSOC_SPLAT_NODE: {
1934 const pm_assoc_splat_node_t *assoc = (const pm_assoc_splat_node_t *) element;
1935 if (assoc->value != NULL && pm_setup_args_dup_rest_p(assoc->value)) dup_rest = SPLATARRAY_TRUE;
1936 break;
1937 }
1938 default:
1939 break;
1940 }
1941 }
1942 }
1943
1944 int initial_dup_rest = dup_rest;
1945 int argc;
1946
1947 if (block && PM_NODE_TYPE_P(block, PM_BLOCK_ARGUMENT_NODE)) {
1948 // We compile the `&block_arg` expression first and stitch it later
1949 // since the nature of the expression influences whether splat should
1950 // duplicate the array.
1951 bool regular_block_arg = true;
1952 const pm_node_t *block_expr = ((const pm_block_argument_node_t *)block)->expression;
1953
1954 if (block_expr && pm_setup_args_dup_rest_p(block_expr)) {
1955 dup_rest = SPLATARRAY_TRUE | DUP_SINGLE_KW_SPLAT;
1956 initial_dup_rest = dup_rest;
1957 }
1958
1959 DECL_ANCHOR(block_arg);
1960 pm_compile_node(iseq, block, block_arg, false, scope_node);
1961
1962 *flags |= VM_CALL_ARGS_BLOCKARG;
1963
1964 if (LIST_INSN_SIZE_ONE(block_arg)) {
1965 LINK_ELEMENT *elem = FIRST_ELEMENT(block_arg);
1966 if (IS_INSN(elem)) {
1967 INSN *iobj = (INSN *) elem;
1968 if (iobj->insn_id == BIN(getblockparam)) {
1969 iobj->insn_id = BIN(getblockparamproxy);
1970 }
1971
1972 // Allow splat without duplication for simple one-instruction
1973 // block arguments like `&arg`. It is known that this
1974 // optimization can be too aggressive in some cases. See
1975 // [Bug #16504].
1976 regular_block_arg = false;
1977 }
1978 }
1979
1980 argc = pm_setup_args_core(arguments_node, block, flags, regular_block_arg, kw_arg, &dup_rest, iseq, ret, scope_node, node_location);
1981 PUSH_SEQ(ret, block_arg);
1982 }
1983 else {
1984 argc = pm_setup_args_core(arguments_node, block, flags, false, kw_arg, &dup_rest, iseq, ret, scope_node, node_location);
1985 }
1986
1987 // If the dup_rest flag was consumed while compiling the arguments (which
1988 // effectively means we found the splat node), then it would have changed
1989 // during the call to pm_setup_args_core. In this case, we want to add the
1990 // VM_CALL_ARGS_SPLAT_MUT flag.
1991 if (*flags & VM_CALL_ARGS_SPLAT && dup_rest != initial_dup_rest) {
1992 *flags |= VM_CALL_ARGS_SPLAT_MUT;
1993 }
1994
1995 return argc;
1996}
1997
2008static void
2009pm_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)
2010{
2011 const pm_node_location_t location = *node_location;
2012 if (!popped) PUSH_INSN(ret, location, putnil);
2013
2014 PM_COMPILE_NOT_POPPED(node->receiver);
2015
2016 int boff = (node->block == NULL ? 0 : 1);
2017 int flag = PM_NODE_TYPE_P(node->receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
2018 struct rb_callinfo_kwarg *keywords = NULL;
2019 int argc = pm_setup_args(node->arguments, (const pm_node_t *) node->block, &flag, &keywords, iseq, ret, scope_node, node_location);
2020
2021 if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
2022 if (boff) {
2023 PUSH_INSN(ret, location, splatkw);
2024 }
2025 else {
2026 PUSH_INSN(ret, location, dup);
2027 PUSH_INSN(ret, location, splatkw);
2028 PUSH_INSN(ret, location, pop);
2029 }
2030 }
2031
2032 int dup_argn = argc + 1 + boff;
2033 int keyword_len = 0;
2034
2035 if (keywords) {
2036 keyword_len = keywords->keyword_len;
2037 dup_argn += keyword_len;
2038 }
2039
2040 PUSH_INSN1(ret, location, dupn, INT2FIX(dup_argn));
2041 PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords);
2042 PM_COMPILE_NOT_POPPED(node->value);
2043
2044 ID id_operator = pm_constant_id_lookup(scope_node, node->binary_operator);
2045 PUSH_SEND(ret, location, id_operator, INT2FIX(1));
2046
2047 if (!popped) {
2048 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2049 }
2050 if (flag & VM_CALL_ARGS_SPLAT) {
2051 if (flag & VM_CALL_KW_SPLAT) {
2052 PUSH_INSN1(ret, location, topn, INT2FIX(2 + boff));
2053
2054 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2055 PUSH_INSN1(ret, location, splatarray, Qtrue);
2056 flag |= VM_CALL_ARGS_SPLAT_MUT;
2057 }
2058
2059 PUSH_INSN(ret, location, swap);
2060 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2061 PUSH_INSN1(ret, location, setn, INT2FIX(2 + boff));
2062 PUSH_INSN(ret, location, pop);
2063 }
2064 else {
2065 if (boff > 0) {
2066 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2067 PUSH_INSN(ret, location, swap);
2068 PUSH_INSN(ret, location, pop);
2069 }
2070 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2071 PUSH_INSN(ret, location, swap);
2072 PUSH_INSN1(ret, location, splatarray, Qtrue);
2073 PUSH_INSN(ret, location, swap);
2074 flag |= VM_CALL_ARGS_SPLAT_MUT;
2075 }
2076 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2077 if (boff > 0) {
2078 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2079 PUSH_INSN(ret, location, pop);
2080 PUSH_INSN(ret, location, pop);
2081 }
2082 }
2083
2084 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc), NULL, INT2FIX(flag), keywords);
2085 }
2086 else if (flag & VM_CALL_KW_SPLAT) {
2087 if (boff > 0) {
2088 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2089 PUSH_INSN(ret, location, swap);
2090 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2091 PUSH_INSN(ret, location, pop);
2092 }
2093 PUSH_INSN(ret, location, swap);
2094 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2095 }
2096 else if (keyword_len) {
2097 PUSH_INSN(ret, location, dup);
2098 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 2));
2099 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 1));
2100 PUSH_INSN(ret, location, pop);
2101 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2102 }
2103 else {
2104 if (boff > 0) {
2105 PUSH_INSN(ret, location, swap);
2106 }
2107 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2108 }
2109
2110 PUSH_INSN(ret, location, pop);
2111}
2112
2125static void
2126pm_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)
2127{
2128 const pm_node_location_t location = *node_location;
2129 if (!popped) PUSH_INSN(ret, location, putnil);
2130 PM_COMPILE_NOT_POPPED(receiver);
2131
2132 int boff = (block == NULL ? 0 : 1);
2133 int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
2134 struct rb_callinfo_kwarg *keywords = NULL;
2135 int argc = pm_setup_args(arguments, (const pm_node_t *) block, &flag, &keywords, iseq, ret, scope_node, node_location);
2136
2137 if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
2138 if (boff) {
2139 PUSH_INSN(ret, location, splatkw);
2140 }
2141 else {
2142 PUSH_INSN(ret, location, dup);
2143 PUSH_INSN(ret, location, splatkw);
2144 PUSH_INSN(ret, location, pop);
2145 }
2146 }
2147
2148 int dup_argn = argc + 1 + boff;
2149 int keyword_len = 0;
2150
2151 if (keywords) {
2152 keyword_len = keywords->keyword_len;
2153 dup_argn += keyword_len;
2154 }
2155
2156 PUSH_INSN1(ret, location, dupn, INT2FIX(dup_argn));
2157 PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords);
2158
2159 LABEL *label = NEW_LABEL(location.line);
2160 LABEL *lfin = NEW_LABEL(location.line);
2161
2162 PUSH_INSN(ret, location, dup);
2163 if (PM_NODE_TYPE_P(node, PM_INDEX_AND_WRITE_NODE)) {
2164 PUSH_INSNL(ret, location, branchunless, label);
2165 }
2166 else {
2167 PUSH_INSNL(ret, location, branchif, label);
2168 }
2169
2170 PUSH_INSN(ret, location, pop);
2171 PM_COMPILE_NOT_POPPED(value);
2172
2173 if (!popped) {
2174 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2175 }
2176
2177 if (flag & VM_CALL_ARGS_SPLAT) {
2178 if (flag & VM_CALL_KW_SPLAT) {
2179 PUSH_INSN1(ret, location, topn, INT2FIX(2 + boff));
2180 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2181 PUSH_INSN1(ret, location, splatarray, Qtrue);
2182 flag |= VM_CALL_ARGS_SPLAT_MUT;
2183 }
2184
2185 PUSH_INSN(ret, location, swap);
2186 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2187 PUSH_INSN1(ret, location, setn, INT2FIX(2 + boff));
2188 PUSH_INSN(ret, location, pop);
2189 }
2190 else {
2191 if (boff > 0) {
2192 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2193 PUSH_INSN(ret, location, swap);
2194 PUSH_INSN(ret, location, pop);
2195 }
2196 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2197 PUSH_INSN(ret, location, swap);
2198 PUSH_INSN1(ret, location, splatarray, Qtrue);
2199 PUSH_INSN(ret, location, swap);
2200 flag |= VM_CALL_ARGS_SPLAT_MUT;
2201 }
2202 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2203 if (boff > 0) {
2204 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2205 PUSH_INSN(ret, location, pop);
2206 PUSH_INSN(ret, location, pop);
2207 }
2208 }
2209
2210 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc), NULL, INT2FIX(flag), keywords);
2211 }
2212 else if (flag & VM_CALL_KW_SPLAT) {
2213 if (boff > 0) {
2214 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2215 PUSH_INSN(ret, location, swap);
2216 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2217 PUSH_INSN(ret, location, pop);
2218 }
2219
2220 PUSH_INSN(ret, location, swap);
2221 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2222 }
2223 else if (keyword_len) {
2224 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 1));
2225 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 0));
2226 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2227 }
2228 else {
2229 if (boff > 0) {
2230 PUSH_INSN(ret, location, swap);
2231 }
2232 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2233 }
2234
2235 PUSH_INSN(ret, location, pop);
2236 PUSH_INSNL(ret, location, jump, lfin);
2237 PUSH_LABEL(ret, label);
2238 if (!popped) {
2239 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2240 }
2241 PUSH_INSN1(ret, location, adjuststack, INT2FIX(dup_argn + 1));
2242 PUSH_LABEL(ret, lfin);
2243}
2244
2245// When we compile a pattern matching expression, we use the stack as a scratch
2246// space to store lots of different values (consider it like we have a pattern
2247// matching function and we need space for a bunch of different local
2248// variables). The "base index" refers to the index on the stack where we
2249// started compiling the pattern matching expression. These offsets from that
2250// base index indicate the location of the various locals we need.
2251#define PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE 0
2252#define PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING 1
2253#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P 2
2254#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE 3
2255#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY 4
2256
2257// A forward declaration because this is the recursive function that handles
2258// compiling a pattern. It can be reentered by nesting patterns, as in the case
2259// of arrays or hashes.
2260static 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);
2261
2266static int
2267pm_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)
2268{
2269 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
2270 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2271
2272 PUSH_INSN(ret, location, dup);
2273 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2274
2275 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2276 PUSH_INSN1(ret, location, putobject, message);
2277 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2278 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(2));
2279 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2280
2281 PUSH_INSN1(ret, location, putobject, Qfalse);
2282 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2283
2284 PUSH_INSN(ret, location, pop);
2285 PUSH_INSN(ret, location, pop);
2286 PUSH_LABEL(ret, match_succeeded_label);
2287
2288 return COMPILE_OK;
2289}
2290
2296static int
2297pm_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)
2298{
2299 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
2300 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2301
2302 PUSH_INSN(ret, location, dup);
2303 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2304
2305 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2306 PUSH_INSN1(ret, location, putobject, message);
2307 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2308 PUSH_INSN(ret, location, dup);
2309 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2310 PUSH_INSN1(ret, location, putobject, length);
2311 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(4));
2312 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2313
2314 PUSH_INSN1(ret, location, putobject, Qfalse);
2315 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2316
2317 PUSH_INSN(ret, location, pop);
2318 PUSH_INSN(ret, location, pop);
2319 PUSH_LABEL(ret, match_succeeded_label);
2320
2321 return COMPILE_OK;
2322}
2323
2329static int
2330pm_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)
2331{
2332 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
2333 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2334
2335 PUSH_INSN(ret, location, dup);
2336 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2337 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2338
2339 VALUE operand = rb_fstring_lit("%p === %p does not return true");
2340 PUSH_INSN1(ret, location, putobject, operand);
2341
2342 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2343 PUSH_INSN1(ret, location, topn, INT2FIX(5));
2344 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2345 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2346 PUSH_INSN1(ret, location, putobject, Qfalse);
2347 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2348 PUSH_INSN(ret, location, pop);
2349 PUSH_INSN(ret, location, pop);
2350
2351 PUSH_LABEL(ret, match_succeeded_label);
2352 PUSH_INSN1(ret, location, setn, INT2FIX(2));
2353 PUSH_INSN(ret, location, pop);
2354 PUSH_INSN(ret, location, pop);
2355
2356 return COMPILE_OK;
2357}
2358
2365static int
2366pm_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)
2367{
2368 LABEL *matched_label = NEW_LABEL(pm_node_line_number(scope_node->parser, node));
2369 CHECK(pm_compile_pattern(iseq, scope_node, node, ret, matched_label, unmatched_label, in_single_pattern, in_alternation_pattern, use_deconstructed_cache, base_index));
2370 PUSH_LABEL(ret, matched_label);
2371 return COMPILE_OK;
2372}
2373
2379static int
2380pm_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)
2381{
2382 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
2383
2384 if (use_deconstructed_cache) {
2385 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2386 PUSH_INSNL(ret, location, branchnil, deconstruct_label);
2387
2388 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2389 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2390
2391 PUSH_INSN(ret, location, pop);
2392 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE - 1));
2393 PUSH_INSNL(ret, location, jump, deconstructed_label);
2394 }
2395 else {
2396 PUSH_INSNL(ret, location, jump, deconstruct_label);
2397 }
2398
2399 PUSH_LABEL(ret, deconstruct_label);
2400 PUSH_INSN(ret, location, dup);
2401
2402 VALUE operand = ID2SYM(rb_intern("deconstruct"));
2403 PUSH_INSN1(ret, location, putobject, operand);
2404 PUSH_SEND(ret, location, idRespond_to, INT2FIX(1));
2405
2406 if (use_deconstructed_cache) {
2407 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE + 1));
2408 }
2409
2410 if (in_single_pattern) {
2411 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p does not respond to #deconstruct"), base_index + 1));
2412 }
2413
2414 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2415 PUSH_SEND(ret, location, rb_intern("deconstruct"), INT2FIX(0));
2416
2417 if (use_deconstructed_cache) {
2418 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2419 }
2420
2421 PUSH_INSN(ret, location, dup);
2422 PUSH_INSN1(ret, location, checktype, INT2FIX(T_ARRAY));
2423 PUSH_INSNL(ret, location, branchunless, type_error_label);
2424 PUSH_LABEL(ret, deconstructed_label);
2425
2426 return COMPILE_OK;
2427}
2428
2433static int
2434pm_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)
2435{
2436 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
2437
2438 PUSH_INSN(ret, location, dup);
2439 PM_COMPILE_NOT_POPPED(node);
2440
2441 if (in_single_pattern) {
2442 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
2443 }
2444 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
2445 if (in_single_pattern) {
2446 CHECK(pm_compile_pattern_eqq_error(iseq, scope_node, node, ret, base_index + 3));
2447 }
2448 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2449 return COMPILE_OK;
2450}
2451
2456static void
2457pm_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)
2458{
2459 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
2460 LABEL *key_error_label = NEW_LABEL(location.line);
2461 LABEL *cleanup_label = NEW_LABEL(location.line);
2462
2463 struct rb_callinfo_kwarg *kw_arg = rb_xmalloc_mul_add(2, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
2464 kw_arg->references = 0;
2465 kw_arg->keyword_len = 2;
2466 kw_arg->keywords[0] = ID2SYM(rb_intern("matchee"));
2467 kw_arg->keywords[1] = ID2SYM(rb_intern("key"));
2468
2469 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2470 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2471 PUSH_INSNL(ret, location, branchif, key_error_label);
2472
2473 PUSH_INSN1(ret, location, putobject, rb_eNoMatchingPatternError);
2474 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2475
2476 {
2477 VALUE operand = rb_fstring_lit("%p: %s");
2478 PUSH_INSN1(ret, location, putobject, operand);
2479 }
2480
2481 PUSH_INSN1(ret, location, topn, INT2FIX(4));
2482 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 6));
2483 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2484 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2485 PUSH_INSNL(ret, location, jump, cleanup_label);
2486
2487 PUSH_LABEL(ret, key_error_label);
2488 PUSH_INSN1(ret, location, putobject, rb_eNoMatchingPatternKeyError);
2489 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2490
2491 {
2492 VALUE operand = rb_fstring_lit("%p: %s");
2493 PUSH_INSN1(ret, location, putobject, operand);
2494 }
2495
2496 PUSH_INSN1(ret, location, topn, INT2FIX(4));
2497 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 6));
2498 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2499 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE + 4));
2500 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY + 5));
2501 PUSH_SEND_R(ret, location, rb_intern("new"), INT2FIX(1), NULL, INT2FIX(VM_CALL_KWARG), kw_arg);
2502 PUSH_SEND(ret, location, id_core_raise, INT2FIX(1));
2503 PUSH_LABEL(ret, cleanup_label);
2504
2505 PUSH_INSN1(ret, location, adjuststack, INT2FIX(7));
2506 if (!popped) PUSH_INSN(ret, location, putnil);
2507 PUSH_INSNL(ret, location, jump, done_label);
2508 PUSH_INSN1(ret, location, dupn, INT2FIX(5));
2509 if (popped) PUSH_INSN(ret, location, putnil);
2510}
2511
2515static int
2516pm_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)
2517{
2518 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
2519
2520 switch (PM_NODE_TYPE(node)) {
2521 case PM_ARRAY_PATTERN_NODE: {
2522 // Array patterns in pattern matching are triggered by using commas in
2523 // a pattern or wrapping it in braces. They are represented by a
2524 // ArrayPatternNode. This looks like:
2525 //
2526 // foo => [1, 2, 3]
2527 //
2528 // It can optionally have a splat in the middle of it, which can
2529 // optionally have a name attached.
2530 const pm_array_pattern_node_t *cast = (const pm_array_pattern_node_t *) node;
2531
2532 const size_t requireds_size = cast->requireds.size;
2533 const size_t posts_size = cast->posts.size;
2534 const size_t minimum_size = requireds_size + posts_size;
2535
2536 bool rest_named = false;
2537 bool use_rest_size = false;
2538
2539 if (cast->rest != NULL) {
2540 rest_named = (PM_NODE_TYPE_P(cast->rest, PM_SPLAT_NODE) && ((const pm_splat_node_t *) cast->rest)->expression != NULL);
2541 use_rest_size = (rest_named || (!rest_named && posts_size > 0));
2542 }
2543
2544 LABEL *match_failed_label = NEW_LABEL(location.line);
2545 LABEL *type_error_label = NEW_LABEL(location.line);
2546 LABEL *deconstruct_label = NEW_LABEL(location.line);
2547 LABEL *deconstructed_label = NEW_LABEL(location.line);
2548
2549 if (use_rest_size) {
2550 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2551 PUSH_INSN(ret, location, swap);
2552 base_index++;
2553 }
2554
2555 if (cast->constant != NULL) {
2556 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2557 }
2558
2559 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));
2560
2561 PUSH_INSN(ret, location, dup);
2562 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2563 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2564 PUSH_SEND(ret, location, cast->rest == NULL ? idEq : idGE, INT2FIX(1));
2565 if (in_single_pattern) {
2566 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+)");
2567 CHECK(pm_compile_pattern_length_error(iseq, scope_node, node, ret, message, INT2FIX(minimum_size), base_index + 1));
2568 }
2569 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2570
2571 for (size_t index = 0; index < requireds_size; index++) {
2572 const pm_node_t *required = cast->requireds.nodes[index];
2573 PUSH_INSN(ret, location, dup);
2574 PUSH_INSN1(ret, location, putobject, INT2FIX(index));
2575 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2576 CHECK(pm_compile_pattern_match(iseq, scope_node, required, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
2577 }
2578
2579 if (cast->rest != NULL) {
2580 if (rest_named) {
2581 PUSH_INSN(ret, location, dup);
2582 PUSH_INSN1(ret, location, putobject, INT2FIX(requireds_size));
2583 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2584 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2585 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2586 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2587 PUSH_INSN1(ret, location, setn, INT2FIX(4));
2588 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2589 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));
2590 }
2591 else if (posts_size > 0) {
2592 PUSH_INSN(ret, location, dup);
2593 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2594 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2595 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2596 PUSH_INSN1(ret, location, setn, INT2FIX(2));
2597 PUSH_INSN(ret, location, pop);
2598 }
2599 }
2600
2601 for (size_t index = 0; index < posts_size; index++) {
2602 const pm_node_t *post = cast->posts.nodes[index];
2603 PUSH_INSN(ret, location, dup);
2604
2605 PUSH_INSN1(ret, location, putobject, INT2FIX(requireds_size + index));
2606 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2607 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2608 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2609 CHECK(pm_compile_pattern_match(iseq, scope_node, post, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
2610 }
2611
2612 PUSH_INSN(ret, location, pop);
2613 if (use_rest_size) {
2614 PUSH_INSN(ret, location, pop);
2615 }
2616
2617 PUSH_INSNL(ret, location, jump, matched_label);
2618 PUSH_INSN(ret, location, putnil);
2619 if (use_rest_size) {
2620 PUSH_INSN(ret, location, putnil);
2621 }
2622
2623 PUSH_LABEL(ret, type_error_label);
2624 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2625 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2626
2627 {
2628 VALUE operand = rb_fstring_lit("deconstruct must return Array");
2629 PUSH_INSN1(ret, location, putobject, operand);
2630 }
2631
2632 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2633 PUSH_INSN(ret, location, pop);
2634
2635 PUSH_LABEL(ret, match_failed_label);
2636 PUSH_INSN(ret, location, pop);
2637 if (use_rest_size) {
2638 PUSH_INSN(ret, location, pop);
2639 }
2640
2641 PUSH_INSNL(ret, location, jump, unmatched_label);
2642 break;
2643 }
2644 case PM_FIND_PATTERN_NODE: {
2645 // Find patterns in pattern matching are triggered by using commas in
2646 // a pattern or wrapping it in braces and using a splat on both the left
2647 // and right side of the pattern. This looks like:
2648 //
2649 // foo => [*, 1, 2, 3, *]
2650 //
2651 // There can be any number of requireds in the middle. The splats on
2652 // both sides can optionally have names attached.
2653 const pm_find_pattern_node_t *cast = (const pm_find_pattern_node_t *) node;
2654 const size_t size = cast->requireds.size;
2655
2656 LABEL *match_failed_label = NEW_LABEL(location.line);
2657 LABEL *type_error_label = NEW_LABEL(location.line);
2658 LABEL *deconstruct_label = NEW_LABEL(location.line);
2659 LABEL *deconstructed_label = NEW_LABEL(location.line);
2660
2661 if (cast->constant) {
2662 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2663 }
2664
2665 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));
2666
2667 PUSH_INSN(ret, location, dup);
2668 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2669 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2670 PUSH_SEND(ret, location, idGE, INT2FIX(1));
2671 if (in_single_pattern) {
2672 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));
2673 }
2674 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2675
2676 {
2677 LABEL *while_begin_label = NEW_LABEL(location.line);
2678 LABEL *next_loop_label = NEW_LABEL(location.line);
2679 LABEL *find_succeeded_label = NEW_LABEL(location.line);
2680 LABEL *find_failed_label = NEW_LABEL(location.line);
2681
2682 PUSH_INSN(ret, location, dup);
2683 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2684
2685 PUSH_INSN(ret, location, dup);
2686 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2687 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2688 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2689 PUSH_LABEL(ret, while_begin_label);
2690
2691 PUSH_INSN(ret, location, dup);
2692 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2693 PUSH_SEND(ret, location, idLE, INT2FIX(1));
2694 PUSH_INSNL(ret, location, branchunless, find_failed_label);
2695
2696 for (size_t index = 0; index < size; index++) {
2697 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2698 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2699
2700 if (index != 0) {
2701 PUSH_INSN1(ret, location, putobject, INT2FIX(index));
2702 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2703 }
2704
2705 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2706 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));
2707 }
2708
2709 const pm_splat_node_t *left = cast->left;
2710
2711 if (left->expression != NULL) {
2712 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2713 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2714 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2715 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2716 CHECK(pm_compile_pattern_match(iseq, scope_node, left->expression, ret, find_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 4));
2717 }
2718
2719 RUBY_ASSERT(PM_NODE_TYPE_P(cast->right, PM_SPLAT_NODE));
2720 const pm_splat_node_t *right = (const pm_splat_node_t *) cast->right;
2721
2722 if (right->expression != NULL) {
2723 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2724 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2725 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2726 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2727 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2728 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2729 pm_compile_pattern_match(iseq, scope_node, right->expression, ret, find_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 4);
2730 }
2731
2732 PUSH_INSNL(ret, location, jump, find_succeeded_label);
2733
2734 PUSH_LABEL(ret, next_loop_label);
2735 PUSH_INSN1(ret, location, putobject, INT2FIX(1));
2736 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2737 PUSH_INSNL(ret, location, jump, while_begin_label);
2738
2739 PUSH_LABEL(ret, find_failed_label);
2740 PUSH_INSN1(ret, location, adjuststack, INT2FIX(3));
2741 if (in_single_pattern) {
2742 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2743
2744 {
2745 VALUE operand = rb_fstring_lit("%p does not match to find pattern");
2746 PUSH_INSN1(ret, location, putobject, operand);
2747 }
2748
2749 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2750 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(2));
2751 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2752
2753 PUSH_INSN1(ret, location, putobject, Qfalse);
2754 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2755
2756 PUSH_INSN(ret, location, pop);
2757 PUSH_INSN(ret, location, pop);
2758 }
2759 PUSH_INSNL(ret, location, jump, match_failed_label);
2760 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2761
2762 PUSH_LABEL(ret, find_succeeded_label);
2763 PUSH_INSN1(ret, location, adjuststack, INT2FIX(3));
2764 }
2765
2766 PUSH_INSN(ret, location, pop);
2767 PUSH_INSNL(ret, location, jump, matched_label);
2768 PUSH_INSN(ret, location, putnil);
2769
2770 PUSH_LABEL(ret, type_error_label);
2771 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2772 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2773
2774 {
2775 VALUE operand = rb_fstring_lit("deconstruct must return Array");
2776 PUSH_INSN1(ret, location, putobject, operand);
2777 }
2778
2779 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2780 PUSH_INSN(ret, location, pop);
2781
2782 PUSH_LABEL(ret, match_failed_label);
2783 PUSH_INSN(ret, location, pop);
2784 PUSH_INSNL(ret, location, jump, unmatched_label);
2785
2786 break;
2787 }
2788 case PM_HASH_PATTERN_NODE: {
2789 // Hash patterns in pattern matching are triggered by using labels and
2790 // values in a pattern or by using the ** operator. They are represented
2791 // by the HashPatternNode. This looks like:
2792 //
2793 // foo => { a: 1, b: 2, **bar }
2794 //
2795 // It can optionally have an assoc splat in the middle of it, which can
2796 // optionally have a name.
2797 const pm_hash_pattern_node_t *cast = (const pm_hash_pattern_node_t *) node;
2798
2799 // We don't consider it a "rest" parameter if it's a ** that is unnamed.
2800 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);
2801 bool has_keys = cast->elements.size > 0 || cast->rest != NULL;
2802
2803 LABEL *match_failed_label = NEW_LABEL(location.line);
2804 LABEL *type_error_label = NEW_LABEL(location.line);
2805 VALUE keys = Qnil;
2806
2807 if (has_keys && !has_rest) {
2808 keys = rb_ary_new_capa(cast->elements.size);
2809
2810 for (size_t index = 0; index < cast->elements.size; index++) {
2811 const pm_node_t *element = cast->elements.nodes[index];
2812 RUBY_ASSERT(PM_NODE_TYPE_P(element, PM_ASSOC_NODE));
2813
2814 const pm_node_t *key = ((const pm_assoc_node_t *) element)->key;
2815 RUBY_ASSERT(PM_NODE_TYPE_P(key, PM_SYMBOL_NODE));
2816
2817 VALUE symbol = ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) key));
2818 rb_ary_push(keys, symbol);
2819 }
2820 }
2821
2822 if (cast->constant) {
2823 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2824 }
2825
2826 PUSH_INSN(ret, location, dup);
2827
2828 {
2829 VALUE operand = ID2SYM(rb_intern("deconstruct_keys"));
2830 PUSH_INSN1(ret, location, putobject, operand);
2831 }
2832
2833 PUSH_SEND(ret, location, idRespond_to, INT2FIX(1));
2834 if (in_single_pattern) {
2835 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p does not respond to #deconstruct_keys"), base_index + 1));
2836 }
2837 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2838
2839 if (NIL_P(keys)) {
2840 PUSH_INSN(ret, location, putnil);
2841 }
2842 else {
2843 PUSH_INSN1(ret, location, duparray, keys);
2844 RB_OBJ_WRITTEN(iseq, Qundef, rb_obj_hide(keys));
2845 }
2846 PUSH_SEND(ret, location, rb_intern("deconstruct_keys"), INT2FIX(1));
2847
2848 PUSH_INSN(ret, location, dup);
2849 PUSH_INSN1(ret, location, checktype, INT2FIX(T_HASH));
2850 PUSH_INSNL(ret, location, branchunless, type_error_label);
2851
2852 if (has_rest) {
2853 PUSH_SEND(ret, location, rb_intern("dup"), INT2FIX(0));
2854 }
2855
2856 if (has_keys) {
2857 DECL_ANCHOR(match_values);
2858
2859 for (size_t index = 0; index < cast->elements.size; index++) {
2860 const pm_node_t *element = cast->elements.nodes[index];
2861 RUBY_ASSERT(PM_NODE_TYPE_P(element, PM_ASSOC_NODE));
2862
2863 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
2864 const pm_node_t *key = assoc->key;
2865 RUBY_ASSERT(PM_NODE_TYPE_P(key, PM_SYMBOL_NODE));
2866
2867 VALUE symbol = ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) key));
2868 PUSH_INSN(ret, location, dup);
2869 PUSH_INSN1(ret, location, putobject, symbol);
2870 PUSH_SEND(ret, location, rb_intern("key?"), INT2FIX(1));
2871
2872 if (in_single_pattern) {
2873 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2874
2875 PUSH_INSN(ret, location, dup);
2876 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2877
2878 {
2879 VALUE operand = rb_str_freeze(rb_sprintf("key not found: %+"PRIsVALUE, symbol));
2880 PUSH_INSN1(ret, location, putobject, operand);
2881 }
2882
2883 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 2));
2884 PUSH_INSN1(ret, location, putobject, Qtrue);
2885 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 3));
2886 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2887 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE + 4));
2888 PUSH_INSN1(ret, location, putobject, symbol);
2889 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY + 5));
2890
2891 PUSH_INSN1(ret, location, adjuststack, INT2FIX(4));
2892 PUSH_LABEL(ret, match_succeeded_label);
2893 }
2894
2895 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2896 PUSH_INSN(match_values, location, dup);
2897 PUSH_INSN1(match_values, location, putobject, symbol);
2898 PUSH_SEND(match_values, location, has_rest ? rb_intern("delete") : idAREF, INT2FIX(1));
2899
2900 const pm_node_t *value = assoc->value;
2901 if (PM_NODE_TYPE_P(value, PM_IMPLICIT_NODE)) {
2902 value = ((const pm_implicit_node_t *) value)->value;
2903 }
2904
2905 CHECK(pm_compile_pattern_match(iseq, scope_node, value, match_values, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
2906 }
2907
2908 PUSH_SEQ(ret, match_values);
2909 }
2910 else {
2911 PUSH_INSN(ret, location, dup);
2912 PUSH_SEND(ret, location, idEmptyP, INT2FIX(0));
2913 if (in_single_pattern) {
2914 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p is not empty"), base_index + 1));
2915 }
2916 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2917 }
2918
2919 if (has_rest) {
2920 switch (PM_NODE_TYPE(cast->rest)) {
2921 case PM_NO_KEYWORDS_PARAMETER_NODE: {
2922 PUSH_INSN(ret, location, dup);
2923 PUSH_SEND(ret, location, idEmptyP, INT2FIX(0));
2924 if (in_single_pattern) {
2925 pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("rest of %p is not empty"), base_index + 1);
2926 }
2927 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2928 break;
2929 }
2930 case PM_ASSOC_SPLAT_NODE: {
2931 const pm_assoc_splat_node_t *splat = (const pm_assoc_splat_node_t *) cast->rest;
2932 PUSH_INSN(ret, location, dup);
2933 pm_compile_pattern_match(iseq, scope_node, splat->value, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1);
2934 break;
2935 }
2936 default:
2937 rb_bug("unreachable");
2938 break;
2939 }
2940 }
2941
2942 PUSH_INSN(ret, location, pop);
2943 PUSH_INSNL(ret, location, jump, matched_label);
2944 PUSH_INSN(ret, location, putnil);
2945
2946 PUSH_LABEL(ret, type_error_label);
2947 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2948 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2949
2950 {
2951 VALUE operand = rb_fstring_lit("deconstruct_keys must return Hash");
2952 PUSH_INSN1(ret, location, putobject, operand);
2953 }
2954
2955 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2956 PUSH_INSN(ret, location, pop);
2957
2958 PUSH_LABEL(ret, match_failed_label);
2959 PUSH_INSN(ret, location, pop);
2960 PUSH_INSNL(ret, location, jump, unmatched_label);
2961 break;
2962 }
2963 case PM_CAPTURE_PATTERN_NODE: {
2964 // Capture patterns allow you to pattern match against an element in a
2965 // pattern and also capture the value into a local variable. This looks
2966 // like:
2967 //
2968 // [1] => [Integer => foo]
2969 //
2970 // In this case the `Integer => foo` will be represented by a
2971 // CapturePatternNode, which has both a value (the pattern to match
2972 // against) and a target (the place to write the variable into).
2973 const pm_capture_pattern_node_t *cast = (const pm_capture_pattern_node_t *) node;
2974
2975 LABEL *match_failed_label = NEW_LABEL(location.line);
2976
2977 PUSH_INSN(ret, location, dup);
2978 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));
2979 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));
2980 PUSH_INSN(ret, location, putnil);
2981
2982 PUSH_LABEL(ret, match_failed_label);
2983 PUSH_INSN(ret, location, pop);
2984 PUSH_INSNL(ret, location, jump, unmatched_label);
2985
2986 break;
2987 }
2988 case PM_LOCAL_VARIABLE_TARGET_NODE: {
2989 // Local variables can be targeted by placing identifiers in the place
2990 // of a pattern. For example, foo in bar. This results in the value
2991 // being matched being written to that local variable.
2992 const pm_local_variable_target_node_t *cast = (const pm_local_variable_target_node_t *) node;
2993 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
2994
2995 // If this local variable is being written from within an alternation
2996 // pattern, then it cannot actually be added to the local table since
2997 // it's ambiguous which value should be used. So instead we indicate
2998 // this with a compile error.
2999 if (in_alternation_pattern) {
3000 ID id = pm_constant_id_lookup(scope_node, cast->name);
3001 const char *name = rb_id2name(id);
3002
3003 if (name && strlen(name) > 0 && name[0] != '_') {
3004 COMPILE_ERROR(iseq, location.line, "illegal variable in alternative pattern (%"PRIsVALUE")", rb_id2str(id));
3005 return COMPILE_NG;
3006 }
3007 }
3008
3009 PUSH_SETLOCAL(ret, location, index.index, index.level);
3010 PUSH_INSNL(ret, location, jump, matched_label);
3011 break;
3012 }
3013 case PM_ALTERNATION_PATTERN_NODE: {
3014 // Alternation patterns allow you to specify multiple patterns in a
3015 // single expression using the | operator.
3016 const pm_alternation_pattern_node_t *cast = (const pm_alternation_pattern_node_t *) node;
3017
3018 LABEL *matched_left_label = NEW_LABEL(location.line);
3019 LABEL *unmatched_left_label = NEW_LABEL(location.line);
3020
3021 // First, we're going to attempt to match against the left pattern. If
3022 // that pattern matches, then we'll skip matching the right pattern.
3023 PUSH_INSN(ret, location, dup);
3024 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));
3025
3026 // If we get here, then we matched on the left pattern. In this case we
3027 // should pop out the duplicate value that we preemptively added to
3028 // match against the right pattern and then jump to the match label.
3029 PUSH_LABEL(ret, matched_left_label);
3030 PUSH_INSN(ret, location, pop);
3031 PUSH_INSNL(ret, location, jump, matched_label);
3032 PUSH_INSN(ret, location, putnil);
3033
3034 // If we get here, then we didn't match on the left pattern. In this
3035 // case we attempt to match against the right pattern.
3036 PUSH_LABEL(ret, unmatched_left_label);
3037 CHECK(pm_compile_pattern(iseq, scope_node, cast->right, ret, matched_label, unmatched_label, in_single_pattern, true, use_deconstructed_cache, base_index));
3038 break;
3039 }
3040 case PM_PARENTHESES_NODE:
3041 // Parentheses are allowed to wrap expressions in pattern matching and
3042 // they do nothing since they can only wrap individual expressions and
3043 // not groups. In this case we'll recurse back into this same function
3044 // with the body of the parentheses.
3045 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);
3046 case PM_PINNED_EXPRESSION_NODE:
3047 // Pinned expressions are a way to match against the value of an
3048 // expression that should be evaluated at runtime. This looks like:
3049 // foo in ^(bar). To compile these, we compile the expression as if it
3050 // were a literal value by falling through to the literal case.
3051 node = ((const pm_pinned_expression_node_t *) node)->expression;
3052 /* fallthrough */
3053 case PM_ARRAY_NODE:
3054 case PM_CLASS_VARIABLE_READ_NODE:
3055 case PM_CONSTANT_PATH_NODE:
3056 case PM_CONSTANT_READ_NODE:
3057 case PM_FALSE_NODE:
3058 case PM_FLOAT_NODE:
3059 case PM_GLOBAL_VARIABLE_READ_NODE:
3060 case PM_IMAGINARY_NODE:
3061 case PM_INSTANCE_VARIABLE_READ_NODE:
3062 case PM_IT_LOCAL_VARIABLE_READ_NODE:
3063 case PM_INTEGER_NODE:
3064 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
3065 case PM_INTERPOLATED_STRING_NODE:
3066 case PM_INTERPOLATED_SYMBOL_NODE:
3067 case PM_INTERPOLATED_X_STRING_NODE:
3068 case PM_LAMBDA_NODE:
3069 case PM_LOCAL_VARIABLE_READ_NODE:
3070 case PM_NIL_NODE:
3071 case PM_SOURCE_ENCODING_NODE:
3072 case PM_SOURCE_FILE_NODE:
3073 case PM_SOURCE_LINE_NODE:
3074 case PM_RANGE_NODE:
3075 case PM_RATIONAL_NODE:
3076 case PM_REGULAR_EXPRESSION_NODE:
3077 case PM_SELF_NODE:
3078 case PM_STRING_NODE:
3079 case PM_SYMBOL_NODE:
3080 case PM_TRUE_NODE:
3081 case PM_X_STRING_NODE: {
3082 // These nodes are all simple patterns, which means we'll use the
3083 // checkmatch instruction to match against them, which is effectively a
3084 // VM-level === operator.
3085 PM_COMPILE_NOT_POPPED(node);
3086 if (in_single_pattern) {
3087 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
3088 }
3089
3090 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
3091
3092 if (in_single_pattern) {
3093 pm_compile_pattern_eqq_error(iseq, scope_node, node, ret, base_index + 2);
3094 }
3095
3096 PUSH_INSNL(ret, location, branchif, matched_label);
3097 PUSH_INSNL(ret, location, jump, unmatched_label);
3098 break;
3099 }
3100 case PM_PINNED_VARIABLE_NODE: {
3101 // Pinned variables are a way to match against the value of a variable
3102 // without it looking like you're trying to write to the variable. This
3103 // looks like: foo in ^@bar. To compile these, we compile the variable
3104 // that they hold.
3105 const pm_pinned_variable_node_t *cast = (const pm_pinned_variable_node_t *) node;
3106 CHECK(pm_compile_pattern(iseq, scope_node, cast->variable, ret, matched_label, unmatched_label, in_single_pattern, in_alternation_pattern, true, base_index));
3107 break;
3108 }
3109 case PM_IF_NODE:
3110 case PM_UNLESS_NODE: {
3111 // If and unless nodes can show up here as guards on `in` clauses. This
3112 // looks like:
3113 //
3114 // case foo
3115 // in bar if baz?
3116 // qux
3117 // end
3118 //
3119 // Because we know they're in the modifier form and they can't have any
3120 // variation on this pattern, we compile them differently (more simply)
3121 // here than we would in the normal compilation path.
3122 const pm_node_t *predicate;
3123 const pm_node_t *statement;
3124
3125 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3126 const pm_if_node_t *cast = (const pm_if_node_t *) node;
3127 predicate = cast->predicate;
3128
3129 RUBY_ASSERT(cast->statements != NULL && cast->statements->body.size == 1);
3130 statement = cast->statements->body.nodes[0];
3131 }
3132 else {
3133 const pm_unless_node_t *cast = (const pm_unless_node_t *) node;
3134 predicate = cast->predicate;
3135
3136 RUBY_ASSERT(cast->statements != NULL && cast->statements->body.size == 1);
3137 statement = cast->statements->body.nodes[0];
3138 }
3139
3140 CHECK(pm_compile_pattern_match(iseq, scope_node, statement, ret, unmatched_label, in_single_pattern, in_alternation_pattern, use_deconstructed_cache, base_index));
3141 PM_COMPILE_NOT_POPPED(predicate);
3142
3143 if (in_single_pattern) {
3144 LABEL *match_succeeded_label = NEW_LABEL(location.line);
3145
3146 PUSH_INSN(ret, location, dup);
3147 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3148 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
3149 }
3150 else {
3151 PUSH_INSNL(ret, location, branchunless, match_succeeded_label);
3152 }
3153
3154 {
3155 VALUE operand = rb_fstring_lit("guard clause does not return true");
3156 PUSH_INSN1(ret, location, putobject, operand);
3157 }
3158
3159 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
3160 PUSH_INSN1(ret, location, putobject, Qfalse);
3161 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
3162
3163 PUSH_INSN(ret, location, pop);
3164 PUSH_INSN(ret, location, pop);
3165
3166 PUSH_LABEL(ret, match_succeeded_label);
3167 }
3168
3169 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3170 PUSH_INSNL(ret, location, branchunless, unmatched_label);
3171 }
3172 else {
3173 PUSH_INSNL(ret, location, branchif, unmatched_label);
3174 }
3175
3176 PUSH_INSNL(ret, location, jump, matched_label);
3177 break;
3178 }
3179 default:
3180 // If we get here, then we have a node type that should not be in this
3181 // position. This would be a bug in the parser, because a different node
3182 // type should never have been created in this position in the tree.
3183 rb_bug("Unexpected node type in pattern matching expression: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
3184 break;
3185 }
3186
3187 return COMPILE_OK;
3188}
3189
3190#undef PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE
3191#undef PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING
3192#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P
3193#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE
3194#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY
3195
3196// Generate a scope node from the given node.
3197void
3198pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_t *previous)
3199{
3200 // This is very important, otherwise the scope node could be seen as having
3201 // certain flags set that _should not_ be set.
3202 memset(scope, 0, sizeof(pm_scope_node_t));
3203
3204 scope->base.type = PM_SCOPE_NODE;
3205 scope->base.location.start = node->location.start;
3206 scope->base.location.end = node->location.end;
3207
3208 scope->previous = previous;
3209 scope->ast_node = (pm_node_t *) node;
3210
3211 if (previous) {
3212 scope->parser = previous->parser;
3213 scope->encoding = previous->encoding;
3214 scope->filepath_encoding = previous->filepath_encoding;
3215 scope->constants = previous->constants;
3216 scope->coverage_enabled = previous->coverage_enabled;
3217 scope->script_lines = previous->script_lines;
3218 }
3219
3220 switch (PM_NODE_TYPE(node)) {
3221 case PM_BLOCK_NODE: {
3222 const pm_block_node_t *cast = (const pm_block_node_t *) node;
3223 scope->body = cast->body;
3224 scope->locals = cast->locals;
3225 scope->parameters = cast->parameters;
3226 break;
3227 }
3228 case PM_CLASS_NODE: {
3229 const pm_class_node_t *cast = (const pm_class_node_t *) node;
3230 scope->body = cast->body;
3231 scope->locals = cast->locals;
3232 break;
3233 }
3234 case PM_DEF_NODE: {
3235 const pm_def_node_t *cast = (const pm_def_node_t *) node;
3236 scope->parameters = (pm_node_t *) cast->parameters;
3237 scope->body = cast->body;
3238 scope->locals = cast->locals;
3239 break;
3240 }
3241 case PM_ENSURE_NODE: {
3242 const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node;
3243 scope->body = (pm_node_t *) node;
3244
3245 if (cast->statements != NULL) {
3246 scope->base.location.start = cast->statements->base.location.start;
3247 scope->base.location.end = cast->statements->base.location.end;
3248 }
3249
3250 break;
3251 }
3252 case PM_FOR_NODE: {
3253 const pm_for_node_t *cast = (const pm_for_node_t *) node;
3254 scope->body = (pm_node_t *) cast->statements;
3255 break;
3256 }
3257 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
3258 RUBY_ASSERT(node->flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE);
3259 scope->body = (pm_node_t *) node;
3260 break;
3261 }
3262 case PM_LAMBDA_NODE: {
3263 const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node;
3264 scope->parameters = cast->parameters;
3265 scope->body = cast->body;
3266 scope->locals = cast->locals;
3267
3268 if (cast->parameters != NULL) {
3269 scope->base.location.start = cast->parameters->location.start;
3270 }
3271 else {
3272 scope->base.location.start = cast->operator_loc.end;
3273 }
3274 break;
3275 }
3276 case PM_MODULE_NODE: {
3277 const pm_module_node_t *cast = (const pm_module_node_t *) node;
3278 scope->body = cast->body;
3279 scope->locals = cast->locals;
3280 break;
3281 }
3282 case PM_POST_EXECUTION_NODE: {
3283 const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) node;
3284 scope->body = (pm_node_t *) cast->statements;
3285 break;
3286 }
3287 case PM_PROGRAM_NODE: {
3288 const pm_program_node_t *cast = (const pm_program_node_t *) node;
3289 scope->body = (pm_node_t *) cast->statements;
3290 scope->locals = cast->locals;
3291 break;
3292 }
3293 case PM_RESCUE_NODE: {
3294 const pm_rescue_node_t *cast = (const pm_rescue_node_t *) node;
3295 scope->body = (pm_node_t *) cast->statements;
3296 break;
3297 }
3298 case PM_RESCUE_MODIFIER_NODE: {
3299 const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node;
3300 scope->body = (pm_node_t *) cast->rescue_expression;
3301 break;
3302 }
3303 case PM_SINGLETON_CLASS_NODE: {
3304 const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node;
3305 scope->body = cast->body;
3306 scope->locals = cast->locals;
3307 break;
3308 }
3309 case PM_STATEMENTS_NODE: {
3310 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
3311 scope->body = (pm_node_t *) cast;
3312 break;
3313 }
3314 default:
3315 rb_bug("unreachable");
3316 break;
3317 }
3318}
3319
3320void
3321pm_scope_node_destroy(pm_scope_node_t *scope_node)
3322{
3323 if (scope_node->index_lookup_table) {
3324 st_free_table(scope_node->index_lookup_table);
3325 }
3326}
3327
3339static void
3340pm_compile_retry_end_label(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *retry_end_l)
3341{
3342 INSN *iobj;
3343 LINK_ELEMENT *last_elem = LAST_ELEMENT(ret);
3344 iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem);
3345 while (!IS_INSN_ID(iobj, send) && !IS_INSN_ID(iobj, invokesuper) && !IS_INSN_ID(iobj, sendforward) && !IS_INSN_ID(iobj, invokesuperforward)) {
3346 iobj = (INSN*) get_prev_insn(iobj);
3347 }
3348 ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l);
3349
3350 // LINK_ANCHOR has a pointer to the last element, but
3351 // ELEM_INSERT_NEXT does not update it even if we add an insn to the
3352 // last of LINK_ANCHOR. So this updates it manually.
3353 if (&iobj->link == LAST_ELEMENT(ret)) {
3354 ret->last = (LINK_ELEMENT*) retry_end_l;
3355 }
3356}
3357
3358static const char *
3359pm_iseq_builtin_function_name(const pm_scope_node_t *scope_node, const pm_node_t *receiver, ID method_id)
3360{
3361 const char *name = rb_id2name(method_id);
3362 static const char prefix[] = "__builtin_";
3363 const size_t prefix_len = sizeof(prefix) - 1;
3364
3365 if (receiver == NULL) {
3366 if (UNLIKELY(strncmp(prefix, name, prefix_len) == 0)) {
3367 // __builtin_foo
3368 return &name[prefix_len];
3369 }
3370 }
3371 else if (PM_NODE_TYPE_P(receiver, PM_CALL_NODE)) {
3372 if (PM_NODE_FLAG_P(receiver, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
3373 const pm_call_node_t *cast = (const pm_call_node_t *) receiver;
3374 if (pm_constant_id_lookup(scope_node, cast->name) == rb_intern_const("__builtin")) {
3375 // __builtin.foo
3376 return name;
3377 }
3378 }
3379 }
3380 else if (PM_NODE_TYPE_P(receiver, PM_CONSTANT_READ_NODE)) {
3381 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) receiver;
3382 if (pm_constant_id_lookup(scope_node, cast->name) == rb_intern_const("Primitive")) {
3383 // Primitive.foo
3384 return name;
3385 }
3386 }
3387
3388 return NULL;
3389}
3390
3391// Compile Primitive.attr! :leaf, ...
3392static int
3393pm_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)
3394{
3395 if (arguments == NULL) {
3396 COMPILE_ERROR(iseq, node_location->line, "attr!: no argument");
3397 return COMPILE_NG;
3398 }
3399
3400 const pm_node_t *argument;
3401 PM_NODE_LIST_FOREACH(&arguments->arguments, index, argument) {
3402 if (!PM_NODE_TYPE_P(argument, PM_SYMBOL_NODE)) {
3403 COMPILE_ERROR(iseq, node_location->line, "non symbol argument to attr!: %s", pm_node_type_to_str(PM_NODE_TYPE(argument)));
3404 return COMPILE_NG;
3405 }
3406
3407 VALUE symbol = pm_static_literal_value(iseq, argument, scope_node);
3408 VALUE string = rb_sym2str(symbol);
3409
3410 if (strcmp(RSTRING_PTR(string), "leaf") == 0) {
3411 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_LEAF;
3412 }
3413 else if (strcmp(RSTRING_PTR(string), "inline_block") == 0) {
3414 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_INLINE_BLOCK;
3415 }
3416 else if (strcmp(RSTRING_PTR(string), "use_block") == 0) {
3417 iseq_set_use_block(iseq);
3418 }
3419 else if (strcmp(RSTRING_PTR(string), "c_trace") == 0) {
3420 // Let the iseq act like a C method in backtraces
3421 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_C_TRACE;
3422 }
3423 else {
3424 COMPILE_ERROR(iseq, node_location->line, "unknown argument to attr!: %s", RSTRING_PTR(string));
3425 return COMPILE_NG;
3426 }
3427 }
3428
3429 return COMPILE_OK;
3430}
3431
3432static int
3433pm_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)
3434{
3435 if (arguments == NULL) {
3436 COMPILE_ERROR(iseq, node_location->line, "arg!: no argument");
3437 return COMPILE_NG;
3438 }
3439
3440 if (arguments->arguments.size != 1) {
3441 COMPILE_ERROR(iseq, node_location->line, "arg!: too many argument");
3442 return COMPILE_NG;
3443 }
3444
3445 const pm_node_t *argument = arguments->arguments.nodes[0];
3446 if (!PM_NODE_TYPE_P(argument, PM_SYMBOL_NODE)) {
3447 COMPILE_ERROR(iseq, node_location->line, "non symbol argument to arg!: %s", pm_node_type_to_str(PM_NODE_TYPE(argument)));
3448 return COMPILE_NG;
3449 }
3450
3451 if (!popped) {
3452 ID name = parse_string_symbol(scope_node, ((const pm_symbol_node_t *) argument));
3453 int index = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size - get_local_var_idx(iseq, name);
3454
3455 debugs("id: %s idx: %d\n", rb_id2name(name), index);
3456 PUSH_GETLOCAL(ret, *node_location, index, get_lvar_level(iseq));
3457 }
3458
3459 return COMPILE_OK;
3460}
3461
3462static int
3463pm_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)
3464{
3465 const pm_node_t *ast_node = scope_node->ast_node;
3466 if (!PM_NODE_TYPE_P(ast_node, PM_DEF_NODE)) {
3467 rb_bug("mandatory_only?: not in method definition");
3468 return COMPILE_NG;
3469 }
3470
3471 const pm_def_node_t *def_node = (const pm_def_node_t *) ast_node;
3472 const pm_parameters_node_t *parameters_node = def_node->parameters;
3473 if (parameters_node == NULL) {
3474 rb_bug("mandatory_only?: in method definition with no parameters");
3475 return COMPILE_NG;
3476 }
3477
3478 const pm_node_t *body_node = def_node->body;
3479 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)) {
3480 rb_bug("mandatory_only?: not in method definition with plain statements");
3481 return COMPILE_NG;
3482 }
3483
3484 const pm_if_node_t *if_node = (const pm_if_node_t *) ((const pm_statements_node_t *) body_node)->body.nodes[0];
3485 if (if_node->predicate != ((const pm_node_t *) call_node)) {
3486 rb_bug("mandatory_only?: can't find mandatory node");
3487 return COMPILE_NG;
3488 }
3489
3490 pm_parameters_node_t parameters = {
3491 .base = parameters_node->base,
3492 .requireds = parameters_node->requireds
3493 };
3494
3495 const pm_def_node_t def = {
3496 .base = def_node->base,
3497 .name = def_node->name,
3498 .receiver = def_node->receiver,
3499 .parameters = &parameters,
3500 .body = (pm_node_t *) if_node->statements,
3501 .locals = {
3502 .ids = def_node->locals.ids,
3503 .size = parameters_node->requireds.size,
3504 .capacity = def_node->locals.capacity
3505 }
3506 };
3507
3508 pm_scope_node_t next_scope_node;
3509 pm_scope_node_init(&def.base, &next_scope_node, scope_node);
3510
3511 int error_state;
3512 const rb_iseq_t *mandatory_only_iseq = pm_iseq_new_with_opt(
3513 &next_scope_node,
3514 rb_iseq_base_label(iseq),
3515 rb_iseq_path(iseq),
3516 rb_iseq_realpath(iseq),
3517 node_location->line,
3518 NULL,
3519 0,
3520 ISEQ_TYPE_METHOD,
3521 ISEQ_COMPILE_DATA(iseq)->option,
3522 &error_state
3523 );
3524 RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->mandatory_only_iseq, (VALUE)mandatory_only_iseq);
3525
3526 if (error_state) {
3527 RUBY_ASSERT(ISEQ_BODY(iseq)->mandatory_only_iseq == NULL);
3528 rb_jump_tag(error_state);
3529 }
3530
3531 pm_scope_node_destroy(&next_scope_node);
3532 return COMPILE_OK;
3533}
3534
3535static int
3536pm_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)
3537{
3538 const pm_arguments_node_t *arguments = call_node->arguments;
3539
3540 if (parent_block != NULL) {
3541 COMPILE_ERROR(iseq, node_location->line, "should not call builtins here.");
3542 return COMPILE_NG;
3543 }
3544
3545#define BUILTIN_INLINE_PREFIX "_bi"
3546 char inline_func[sizeof(BUILTIN_INLINE_PREFIX) + DECIMAL_SIZE_OF(int)];
3547 bool cconst = false;
3548retry:;
3549 const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
3550
3551 if (bf == NULL) {
3552 if (strcmp("cstmt!", builtin_func) == 0 || strcmp("cexpr!", builtin_func) == 0) {
3553 // ok
3554 }
3555 else if (strcmp("cconst!", builtin_func) == 0) {
3556 cconst = true;
3557 }
3558 else if (strcmp("cinit!", builtin_func) == 0) {
3559 // ignore
3560 return COMPILE_OK;
3561 }
3562 else if (strcmp("attr!", builtin_func) == 0) {
3563 return pm_compile_builtin_attr(iseq, scope_node, arguments, node_location);
3564 }
3565 else if (strcmp("arg!", builtin_func) == 0) {
3566 return pm_compile_builtin_arg(iseq, ret, scope_node, arguments, node_location, popped);
3567 }
3568 else if (strcmp("mandatory_only?", builtin_func) == 0) {
3569 if (popped) {
3570 rb_bug("mandatory_only? should be in if condition");
3571 }
3572 else if (!LIST_INSN_SIZE_ZERO(ret)) {
3573 rb_bug("mandatory_only? should be put on top");
3574 }
3575
3576 PUSH_INSN1(ret, *node_location, putobject, Qfalse);
3577 return pm_compile_builtin_mandatory_only_method(iseq, scope_node, call_node, node_location);
3578 }
3579 else if (1) {
3580 rb_bug("can't find builtin function:%s", builtin_func);
3581 }
3582 else {
3583 COMPILE_ERROR(iseq, node_location->line, "can't find builtin function:%s", builtin_func);
3584 return COMPILE_NG;
3585 }
3586
3587 int inline_index = node_location->line;
3588 snprintf(inline_func, sizeof(inline_func), BUILTIN_INLINE_PREFIX "%d", inline_index);
3589 builtin_func = inline_func;
3590 arguments = NULL;
3591 goto retry;
3592 }
3593
3594 if (cconst) {
3595 typedef VALUE(*builtin_func0)(void *, VALUE);
3596 VALUE const_val = (*(builtin_func0)(uintptr_t)bf->func_ptr)(NULL, Qnil);
3597 PUSH_INSN1(ret, *node_location, putobject, const_val);
3598 return COMPILE_OK;
3599 }
3600
3601 // fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
3602
3603 DECL_ANCHOR(args_seq);
3604
3605 int flags = 0;
3606 struct rb_callinfo_kwarg *keywords = NULL;
3607 int argc = pm_setup_args(arguments, call_node->block, &flags, &keywords, iseq, args_seq, scope_node, node_location);
3608
3609 if (argc != bf->argc) {
3610 COMPILE_ERROR(iseq, node_location->line, "argc is not match for builtin function:%s (expect %d but %d)", builtin_func, bf->argc, argc);
3611 return COMPILE_NG;
3612 }
3613
3614 unsigned int start_index;
3615 if (delegate_call_p(iseq, argc, args_seq, &start_index)) {
3616 PUSH_INSN2(ret, *node_location, opt_invokebuiltin_delegate, bf, INT2FIX(start_index));
3617 }
3618 else {
3619 PUSH_SEQ(ret, args_seq);
3620 PUSH_INSN1(ret, *node_location, invokebuiltin, bf);
3621 }
3622
3623 if (popped) PUSH_INSN(ret, *node_location, pop);
3624 return COMPILE_OK;
3625}
3626
3630static void
3631pm_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)
3632{
3633 const pm_location_t *message_loc = &call_node->message_loc;
3634 if (message_loc->start == NULL) message_loc = &call_node->base.location;
3635
3636 const pm_node_location_t location = PM_LOCATION_START_LOCATION(scope_node->parser, message_loc, call_node->base.node_id);
3637 LABEL *else_label = NEW_LABEL(location.line);
3638 LABEL *end_label = NEW_LABEL(location.line);
3639 LABEL *retry_end_l = NEW_LABEL(location.line);
3640
3641 VALUE branches = Qfalse;
3642 rb_code_location_t code_location = { 0 };
3643 int node_id = location.node_id;
3644
3645 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
3646 if (PM_BRANCH_COVERAGE_P(iseq)) {
3647 const uint8_t *cursors[3] = {
3648 call_node->closing_loc.end,
3649 call_node->arguments == NULL ? NULL : call_node->arguments->base.location.end,
3650 call_node->message_loc.end
3651 };
3652
3653 const uint8_t *end_cursor = cursors[0];
3654 end_cursor = (end_cursor == NULL || cursors[1] == NULL) ? cursors[1] : (end_cursor > cursors[1] ? end_cursor : cursors[1]);
3655 end_cursor = (end_cursor == NULL || cursors[2] == NULL) ? cursors[2] : (end_cursor > cursors[2] ? end_cursor : cursors[2]);
3656 if (!end_cursor) end_cursor = call_node->closing_loc.end;
3657
3658 const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, call_node);
3659 const pm_line_column_t end_location = pm_newline_list_line_column(&scope_node->parser->newline_list, end_cursor, scope_node->parser->start_line);
3660
3661 code_location = (rb_code_location_t) {
3662 .beg_pos = { .lineno = start_location.line, .column = start_location.column },
3663 .end_pos = { .lineno = end_location.line, .column = end_location.column }
3664 };
3665
3666 branches = decl_branch_base(iseq, PTR2NUM(call_node), &code_location, "&.");
3667 }
3668
3669 PUSH_INSN(ret, location, dup);
3670 PUSH_INSNL(ret, location, branchnil, else_label);
3671
3672 add_trace_branch_coverage(iseq, ret, &code_location, node_id, 0, "then", branches);
3673 }
3674
3675 int flags = 0;
3676 struct rb_callinfo_kwarg *kw_arg = NULL;
3677
3678 int orig_argc = pm_setup_args(call_node->arguments, call_node->block, &flags, &kw_arg, iseq, ret, scope_node, &location);
3679 const rb_iseq_t *previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
3680 const rb_iseq_t *block_iseq = NULL;
3681
3682 if (call_node->block != NULL && PM_NODE_TYPE_P(call_node->block, PM_BLOCK_NODE)) {
3683 // Scope associated with the block
3684 pm_scope_node_t next_scope_node;
3685 pm_scope_node_init(call_node->block, &next_scope_node, scope_node);
3686
3687 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));
3688 pm_scope_node_destroy(&next_scope_node);
3689 ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
3690 }
3691 else {
3692 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
3693 flags |= VM_CALL_VCALL;
3694 }
3695
3696 if (!flags) {
3697 flags |= VM_CALL_ARGS_SIMPLE;
3698 }
3699 }
3700
3701 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) {
3702 flags |= VM_CALL_FCALL;
3703 }
3704
3705 if (!popped && PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) {
3706 if (flags & VM_CALL_ARGS_BLOCKARG) {
3707 PUSH_INSN1(ret, location, topn, INT2FIX(1));
3708 if (flags & VM_CALL_ARGS_SPLAT) {
3709 PUSH_INSN1(ret, location, putobject, INT2FIX(-1));
3710 PUSH_SEND_WITH_FLAG(ret, location, idAREF, INT2FIX(1), INT2FIX(0));
3711 }
3712 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 3));
3713 PUSH_INSN(ret, location, pop);
3714 }
3715 else if (flags & VM_CALL_ARGS_SPLAT) {
3716 PUSH_INSN(ret, location, dup);
3717 PUSH_INSN1(ret, location, putobject, INT2FIX(-1));
3718 PUSH_SEND_WITH_FLAG(ret, location, idAREF, INT2FIX(1), INT2FIX(0));
3719 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 2));
3720 PUSH_INSN(ret, location, pop);
3721 }
3722 else {
3723 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 1));
3724 }
3725 }
3726
3727 if ((flags & VM_CALL_KW_SPLAT) && (flags & VM_CALL_ARGS_BLOCKARG) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
3728 PUSH_INSN(ret, location, splatkw);
3729 }
3730
3731 PUSH_SEND_R(ret, location, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg);
3732
3733 if (block_iseq && ISEQ_BODY(block_iseq)->catch_table) {
3734 pm_compile_retry_end_label(iseq, ret, retry_end_l);
3735 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, start, retry_end_l, block_iseq, retry_end_l);
3736 }
3737
3738 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
3739 PUSH_INSNL(ret, location, jump, end_label);
3740 PUSH_LABEL(ret, else_label);
3741 add_trace_branch_coverage(iseq, ret, &code_location, node_id, 1, "else", branches);
3742 PUSH_LABEL(ret, end_label);
3743 }
3744
3745 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && !popped) {
3746 PUSH_INSN(ret, location, pop);
3747 }
3748
3749 if (popped) PUSH_INSN(ret, location, pop);
3750 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
3751}
3752
3757static inline VALUE
3758pm_compile_back_reference_ref(const pm_back_reference_read_node_t *node)
3759{
3760 const char *type = (const char *) (node->base.location.start + 1);
3761
3762 // Since a back reference is `$<char>`, Ruby represents the ID as an
3763 // rb_intern on the value after the `$`.
3764 return INT2FIX(rb_intern2(type, 1)) << 1 | 1;
3765}
3766
3771static inline VALUE
3772pm_compile_numbered_reference_ref(const pm_numbered_reference_read_node_t *node)
3773{
3774 return INT2FIX(node->number << 1);
3775}
3776
3777static void
3778pm_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)
3779{
3780#define PUSH_VAL(type) (in_condition ? Qtrue : rb_iseq_defined_string(type))
3781
3782 // in_condition is the same as compile.c's needstr
3783 enum defined_type dtype = DEFINED_NOT_DEFINED;
3784 const pm_node_location_t location = *node_location;
3785
3786 switch (PM_NODE_TYPE(node)) {
3787/* DEFINED_NIL ****************************************************************/
3788 case PM_NIL_NODE:
3789 // defined?(nil)
3790 // ^^^
3791 dtype = DEFINED_NIL;
3792 break;
3793/* DEFINED_IVAR ***************************************************************/
3794 case PM_INSTANCE_VARIABLE_READ_NODE: {
3795 // defined?(@a)
3796 // ^^
3797 const pm_instance_variable_read_node_t *cast = (const pm_instance_variable_read_node_t *) node;
3798 ID name = pm_constant_id_lookup(scope_node, cast->name);
3799
3800 PUSH_INSN3(ret, location, definedivar, ID2SYM(name), get_ivar_ic_value(iseq, name), PUSH_VAL(DEFINED_IVAR));
3801
3802 return;
3803 }
3804/* DEFINED_LVAR ***************************************************************/
3805 case PM_LOCAL_VARIABLE_READ_NODE:
3806 // a = 1; defined?(a)
3807 // ^
3808 case PM_IT_LOCAL_VARIABLE_READ_NODE:
3809 // 1.then { defined?(it) }
3810 // ^^
3811 dtype = DEFINED_LVAR;
3812 break;
3813/* DEFINED_GVAR ***************************************************************/
3814 case PM_GLOBAL_VARIABLE_READ_NODE: {
3815 // defined?($a)
3816 // ^^
3817 const pm_global_variable_read_node_t *cast = (const pm_global_variable_read_node_t *) node;
3818 ID name = pm_constant_id_lookup(scope_node, cast->name);
3819
3820 PUSH_INSN(ret, location, putnil);
3821 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_GVAR), ID2SYM(name), PUSH_VAL(DEFINED_GVAR));
3822
3823 return;
3824 }
3825/* DEFINED_CVAR ***************************************************************/
3826 case PM_CLASS_VARIABLE_READ_NODE: {
3827 // defined?(@@a)
3828 // ^^^
3829 const pm_class_variable_read_node_t *cast = (const pm_class_variable_read_node_t *) node;
3830 ID name = pm_constant_id_lookup(scope_node, cast->name);
3831
3832 PUSH_INSN(ret, location, putnil);
3833 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CVAR), ID2SYM(name), PUSH_VAL(DEFINED_CVAR));
3834
3835 return;
3836 }
3837/* DEFINED_CONST **************************************************************/
3838 case PM_CONSTANT_READ_NODE: {
3839 // defined?(A)
3840 // ^
3841 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
3842 ID name = pm_constant_id_lookup(scope_node, cast->name);
3843
3844 PUSH_INSN(ret, location, putnil);
3845 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), ID2SYM(name), PUSH_VAL(DEFINED_CONST));
3846
3847 return;
3848 }
3849/* DEFINED_YIELD **************************************************************/
3850 case PM_YIELD_NODE:
3851 // defined?(yield)
3852 // ^^^^^
3853 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
3854
3855 PUSH_INSN(ret, location, putnil);
3856 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_YIELD), 0, PUSH_VAL(DEFINED_YIELD));
3857
3858 return;
3859/* DEFINED_ZSUPER *************************************************************/
3860 case PM_SUPER_NODE: {
3861 // defined?(super 1, 2)
3862 // ^^^^^^^^^^
3863 const pm_super_node_t *cast = (const pm_super_node_t *) node;
3864
3865 if (cast->block != NULL && !PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE)) {
3866 dtype = DEFINED_EXPR;
3867 break;
3868 }
3869
3870 PUSH_INSN(ret, location, putnil);
3871 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_ZSUPER), 0, PUSH_VAL(DEFINED_ZSUPER));
3872 return;
3873 }
3874 case PM_FORWARDING_SUPER_NODE: {
3875 // defined?(super)
3876 // ^^^^^
3877 const pm_forwarding_super_node_t *cast = (const pm_forwarding_super_node_t *) node;
3878
3879 if (cast->block != NULL) {
3880 dtype = DEFINED_EXPR;
3881 break;
3882 }
3883
3884 PUSH_INSN(ret, location, putnil);
3885 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_ZSUPER), 0, PUSH_VAL(DEFINED_ZSUPER));
3886 return;
3887 }
3888/* DEFINED_SELF ***************************************************************/
3889 case PM_SELF_NODE:
3890 // defined?(self)
3891 // ^^^^
3892 dtype = DEFINED_SELF;
3893 break;
3894/* DEFINED_TRUE ***************************************************************/
3895 case PM_TRUE_NODE:
3896 // defined?(true)
3897 // ^^^^
3898 dtype = DEFINED_TRUE;
3899 break;
3900/* DEFINED_FALSE **************************************************************/
3901 case PM_FALSE_NODE:
3902 // defined?(false)
3903 // ^^^^^
3904 dtype = DEFINED_FALSE;
3905 break;
3906/* DEFINED_ASGN ***************************************************************/
3907 case PM_CALL_AND_WRITE_NODE:
3908 // defined?(a.a &&= 1)
3909 // ^^^^^^^^^
3910 case PM_CALL_OPERATOR_WRITE_NODE:
3911 // defined?(a.a += 1)
3912 // ^^^^^^^^
3913 case PM_CALL_OR_WRITE_NODE:
3914 // defined?(a.a ||= 1)
3915 // ^^^^^^^^^
3916 case PM_CLASS_VARIABLE_AND_WRITE_NODE:
3917 // defined?(@@a &&= 1)
3918 // ^^^^^^^^^
3919 case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE:
3920 // defined?(@@a += 1)
3921 // ^^^^^^^^
3922 case PM_CLASS_VARIABLE_OR_WRITE_NODE:
3923 // defined?(@@a ||= 1)
3924 // ^^^^^^^^^
3925 case PM_CLASS_VARIABLE_WRITE_NODE:
3926 // defined?(@@a = 1)
3927 // ^^^^^^^
3928 case PM_CONSTANT_AND_WRITE_NODE:
3929 // defined?(A &&= 1)
3930 // ^^^^^^^
3931 case PM_CONSTANT_OPERATOR_WRITE_NODE:
3932 // defined?(A += 1)
3933 // ^^^^^^
3934 case PM_CONSTANT_OR_WRITE_NODE:
3935 // defined?(A ||= 1)
3936 // ^^^^^^^
3937 case PM_CONSTANT_PATH_AND_WRITE_NODE:
3938 // defined?(A::A &&= 1)
3939 // ^^^^^^^^^^
3940 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE:
3941 // defined?(A::A += 1)
3942 // ^^^^^^^^^
3943 case PM_CONSTANT_PATH_OR_WRITE_NODE:
3944 // defined?(A::A ||= 1)
3945 // ^^^^^^^^^^
3946 case PM_CONSTANT_PATH_WRITE_NODE:
3947 // defined?(A::A = 1)
3948 // ^^^^^^^^
3949 case PM_CONSTANT_WRITE_NODE:
3950 // defined?(A = 1)
3951 // ^^^^^
3952 case PM_GLOBAL_VARIABLE_AND_WRITE_NODE:
3953 // defined?($a &&= 1)
3954 // ^^^^^^^^
3955 case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE:
3956 // defined?($a += 1)
3957 // ^^^^^^^
3958 case PM_GLOBAL_VARIABLE_OR_WRITE_NODE:
3959 // defined?($a ||= 1)
3960 // ^^^^^^^^
3961 case PM_GLOBAL_VARIABLE_WRITE_NODE:
3962 // defined?($a = 1)
3963 // ^^^^^^
3964 case PM_INDEX_AND_WRITE_NODE:
3965 // defined?(a[1] &&= 1)
3966 // ^^^^^^^^^^
3967 case PM_INDEX_OPERATOR_WRITE_NODE:
3968 // defined?(a[1] += 1)
3969 // ^^^^^^^^^
3970 case PM_INDEX_OR_WRITE_NODE:
3971 // defined?(a[1] ||= 1)
3972 // ^^^^^^^^^^
3973 case PM_INSTANCE_VARIABLE_AND_WRITE_NODE:
3974 // defined?(@a &&= 1)
3975 // ^^^^^^^^
3976 case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE:
3977 // defined?(@a += 1)
3978 // ^^^^^^^
3979 case PM_INSTANCE_VARIABLE_OR_WRITE_NODE:
3980 // defined?(@a ||= 1)
3981 // ^^^^^^^^
3982 case PM_INSTANCE_VARIABLE_WRITE_NODE:
3983 // defined?(@a = 1)
3984 // ^^^^^^
3985 case PM_LOCAL_VARIABLE_AND_WRITE_NODE:
3986 // defined?(a &&= 1)
3987 // ^^^^^^^
3988 case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE:
3989 // defined?(a += 1)
3990 // ^^^^^^
3991 case PM_LOCAL_VARIABLE_OR_WRITE_NODE:
3992 // defined?(a ||= 1)
3993 // ^^^^^^^
3994 case PM_LOCAL_VARIABLE_WRITE_NODE:
3995 // defined?(a = 1)
3996 // ^^^^^
3997 case PM_MULTI_WRITE_NODE:
3998 // defined?((a, = 1))
3999 // ^^^^^^
4000 dtype = DEFINED_ASGN;
4001 break;
4002/* DEFINED_EXPR ***************************************************************/
4003 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
4004 // defined?((alias $a $b))
4005 // ^^^^^^^^^^^
4006 case PM_ALIAS_METHOD_NODE:
4007 // defined?((alias a b))
4008 // ^^^^^^^^^
4009 case PM_AND_NODE:
4010 // defined?(a and b)
4011 // ^^^^^^^
4012 case PM_BREAK_NODE:
4013 // defined?(break 1)
4014 // ^^^^^^^
4015 case PM_CASE_MATCH_NODE:
4016 // defined?(case 1; in 1; end)
4017 // ^^^^^^^^^^^^^^^^^
4018 case PM_CASE_NODE:
4019 // defined?(case 1; when 1; end)
4020 // ^^^^^^^^^^^^^^^^^^^
4021 case PM_CLASS_NODE:
4022 // defined?(class Foo; end)
4023 // ^^^^^^^^^^^^^^
4024 case PM_DEF_NODE:
4025 // defined?(def a() end)
4026 // ^^^^^^^^^^^
4027 case PM_DEFINED_NODE:
4028 // defined?(defined?(a))
4029 // ^^^^^^^^^^^
4030 case PM_FLIP_FLOP_NODE:
4031 // defined?(not (a .. b))
4032 // ^^^^^^
4033 case PM_FLOAT_NODE:
4034 // defined?(1.0)
4035 // ^^^
4036 case PM_FOR_NODE:
4037 // defined?(for a in 1 do end)
4038 // ^^^^^^^^^^^^^^^^^
4039 case PM_IF_NODE:
4040 // defined?(if a then end)
4041 // ^^^^^^^^^^^^^
4042 case PM_IMAGINARY_NODE:
4043 // defined?(1i)
4044 // ^^
4045 case PM_INTEGER_NODE:
4046 // defined?(1)
4047 // ^
4048 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE:
4049 // defined?(not /#{1}/)
4050 // ^^^^^^
4051 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
4052 // defined?(/#{1}/)
4053 // ^^^^^^
4054 case PM_INTERPOLATED_STRING_NODE:
4055 // defined?("#{1}")
4056 // ^^^^^^
4057 case PM_INTERPOLATED_SYMBOL_NODE:
4058 // defined?(:"#{1}")
4059 // ^^^^^^^
4060 case PM_INTERPOLATED_X_STRING_NODE:
4061 // defined?(`#{1}`)
4062 // ^^^^^^
4063 case PM_LAMBDA_NODE:
4064 // defined?(-> {})
4065 // ^^^^^
4066 case PM_MATCH_LAST_LINE_NODE:
4067 // defined?(not //)
4068 // ^^^^^^
4069 case PM_MATCH_PREDICATE_NODE:
4070 // defined?(1 in 1)
4071 // ^^^^^^
4072 case PM_MATCH_REQUIRED_NODE:
4073 // defined?(1 => 1)
4074 // ^^^^^^
4075 case PM_MATCH_WRITE_NODE:
4076 // defined?(/(?<a>)/ =~ "")
4077 // ^^^^^^^^^^^^^^
4078 case PM_MODULE_NODE:
4079 // defined?(module A end)
4080 // ^^^^^^^^^^^^
4081 case PM_NEXT_NODE:
4082 // defined?(next 1)
4083 // ^^^^^^
4084 case PM_OR_NODE:
4085 // defined?(a or b)
4086 // ^^^^^^
4087 case PM_POST_EXECUTION_NODE:
4088 // defined?((END {}))
4089 // ^^^^^^^^
4090 case PM_RANGE_NODE:
4091 // defined?(1..1)
4092 // ^^^^
4093 case PM_RATIONAL_NODE:
4094 // defined?(1r)
4095 // ^^
4096 case PM_REDO_NODE:
4097 // defined?(redo)
4098 // ^^^^
4099 case PM_REGULAR_EXPRESSION_NODE:
4100 // defined?(//)
4101 // ^^
4102 case PM_RESCUE_MODIFIER_NODE:
4103 // defined?(a rescue b)
4104 // ^^^^^^^^^^
4105 case PM_RETRY_NODE:
4106 // defined?(retry)
4107 // ^^^^^
4108 case PM_RETURN_NODE:
4109 // defined?(return)
4110 // ^^^^^^
4111 case PM_SINGLETON_CLASS_NODE:
4112 // defined?(class << self; end)
4113 // ^^^^^^^^^^^^^^^^^^
4114 case PM_SOURCE_ENCODING_NODE:
4115 // defined?(__ENCODING__)
4116 // ^^^^^^^^^^^^
4117 case PM_SOURCE_FILE_NODE:
4118 // defined?(__FILE__)
4119 // ^^^^^^^^
4120 case PM_SOURCE_LINE_NODE:
4121 // defined?(__LINE__)
4122 // ^^^^^^^^
4123 case PM_STRING_NODE:
4124 // defined?("")
4125 // ^^
4126 case PM_SYMBOL_NODE:
4127 // defined?(:a)
4128 // ^^
4129 case PM_UNDEF_NODE:
4130 // defined?((undef a))
4131 // ^^^^^^^
4132 case PM_UNLESS_NODE:
4133 // defined?(unless a then end)
4134 // ^^^^^^^^^^^^^^^^^
4135 case PM_UNTIL_NODE:
4136 // defined?(until a do end)
4137 // ^^^^^^^^^^^^^^
4138 case PM_WHILE_NODE:
4139 // defined?(while a do end)
4140 // ^^^^^^^^^^^^^^
4141 case PM_X_STRING_NODE:
4142 // defined?(``)
4143 // ^^
4144 dtype = DEFINED_EXPR;
4145 break;
4146/* DEFINED_REF ****************************************************************/
4147 case PM_BACK_REFERENCE_READ_NODE: {
4148 // defined?($+)
4149 // ^^
4150 const pm_back_reference_read_node_t *cast = (const pm_back_reference_read_node_t *) node;
4151 VALUE ref = pm_compile_back_reference_ref(cast);
4152
4153 PUSH_INSN(ret, location, putnil);
4154 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_REF), ref, PUSH_VAL(DEFINED_GVAR));
4155
4156 return;
4157 }
4158 case PM_NUMBERED_REFERENCE_READ_NODE: {
4159 // defined?($1)
4160 // ^^
4161 const pm_numbered_reference_read_node_t *cast = (const pm_numbered_reference_read_node_t *) node;
4162 VALUE ref = pm_compile_numbered_reference_ref(cast);
4163
4164 PUSH_INSN(ret, location, putnil);
4165 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_REF), ref, PUSH_VAL(DEFINED_GVAR));
4166
4167 return;
4168 }
4169/* DEFINED_CONST_FROM *********************************************************/
4170 case PM_CONSTANT_PATH_NODE: {
4171 // defined?(A::A)
4172 // ^^^^
4173 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
4174 ID name = pm_constant_id_lookup(scope_node, cast->name);
4175
4176 if (cast->parent != NULL) {
4177 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4178 pm_compile_defined_expr0(iseq, cast->parent, node_location, ret, popped, scope_node, true, lfinish, false);
4179
4180 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4181 PM_COMPILE(cast->parent);
4182 }
4183 else {
4184 PUSH_INSN1(ret, location, putobject, rb_cObject);
4185 }
4186
4187 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), ID2SYM(name), PUSH_VAL(DEFINED_CONST));
4188 return;
4189 }
4190/* Containers *****************************************************************/
4191 case PM_BEGIN_NODE: {
4192 // defined?(begin end)
4193 // ^^^^^^^^^
4194 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
4195
4196 if (cast->rescue_clause == NULL && cast->ensure_clause == NULL && cast->else_clause == NULL) {
4197 if (cast->statements == NULL) {
4198 // If we have empty statements, then we want to return "nil".
4199 dtype = DEFINED_NIL;
4200 }
4201 else if (cast->statements->body.size == 1) {
4202 // If we have a begin node that is wrapping a single statement
4203 // then we want to recurse down to that statement and compile
4204 // it.
4205 pm_compile_defined_expr0(iseq, cast->statements->body.nodes[0], node_location, ret, popped, scope_node, in_condition, lfinish, false);
4206 return;
4207 }
4208 else {
4209 // Otherwise, we have a begin wrapping multiple statements, in
4210 // which case this is defined as "expression".
4211 dtype = DEFINED_EXPR;
4212 }
4213 } else {
4214 // If we have any of the other clauses besides the main begin/end,
4215 // this is defined as "expression".
4216 dtype = DEFINED_EXPR;
4217 }
4218
4219 break;
4220 }
4221 case PM_PARENTHESES_NODE: {
4222 // defined?(())
4223 // ^^
4224 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
4225
4226 if (cast->body == NULL) {
4227 // If we have empty parentheses, then we want to return "nil".
4228 dtype = DEFINED_NIL;
4229 }
4230 else if (PM_NODE_TYPE_P(cast->body, PM_STATEMENTS_NODE) && ((const pm_statements_node_t *) cast->body)->body.size == 1) {
4231 // If we have a parentheses node that is wrapping a single statement
4232 // then we want to recurse down to that statement and compile it.
4233 pm_compile_defined_expr0(iseq, ((const pm_statements_node_t *) cast->body)->body.nodes[0], node_location, ret, popped, scope_node, in_condition, lfinish, explicit_receiver);
4234 return;
4235 }
4236 else {
4237 // Otherwise, we have parentheses wrapping multiple statements, in
4238 // which case this is defined as "expression".
4239 dtype = DEFINED_EXPR;
4240 }
4241
4242 break;
4243 }
4244 case PM_ARRAY_NODE: {
4245 // defined?([])
4246 // ^^
4247 const pm_array_node_t *cast = (const pm_array_node_t *) node;
4248
4249 if (cast->elements.size > 0 && !lfinish[1]) {
4250 lfinish[1] = NEW_LABEL(location.line);
4251 }
4252
4253 for (size_t index = 0; index < cast->elements.size; index++) {
4254 pm_compile_defined_expr0(iseq, cast->elements.nodes[index], node_location, ret, popped, scope_node, true, lfinish, false);
4255 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4256 }
4257
4258 dtype = DEFINED_EXPR;
4259 break;
4260 }
4261 case PM_HASH_NODE:
4262 // defined?({ a: 1 })
4263 // ^^^^^^^^
4264 case PM_KEYWORD_HASH_NODE: {
4265 // defined?(a(a: 1))
4266 // ^^^^
4267 const pm_node_list_t *elements;
4268
4269 if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) {
4270 elements = &((const pm_hash_node_t *) node)->elements;
4271 }
4272 else {
4273 elements = &((const pm_keyword_hash_node_t *) node)->elements;
4274 }
4275
4276 if (elements->size > 0 && !lfinish[1]) {
4277 lfinish[1] = NEW_LABEL(location.line);
4278 }
4279
4280 for (size_t index = 0; index < elements->size; index++) {
4281 pm_compile_defined_expr0(iseq, elements->nodes[index], node_location, ret, popped, scope_node, true, lfinish, false);
4282 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4283 }
4284
4285 dtype = DEFINED_EXPR;
4286 break;
4287 }
4288 case PM_ASSOC_NODE: {
4289 // defined?({ a: 1 })
4290 // ^^^^
4291 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node;
4292
4293 pm_compile_defined_expr0(iseq, cast->key, node_location, ret, popped, scope_node, true, lfinish, false);
4294 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4295 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, true, lfinish, false);
4296
4297 return;
4298 }
4299 case PM_ASSOC_SPLAT_NODE: {
4300 // defined?({ **a })
4301 // ^^^^
4302 const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node;
4303
4304 if (cast->value == NULL) {
4305 dtype = DEFINED_EXPR;
4306 break;
4307 }
4308
4309 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, true, lfinish, false);
4310 return;
4311 }
4312 case PM_IMPLICIT_NODE: {
4313 // defined?({ a: })
4314 // ^^
4315 const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node;
4316 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, in_condition, lfinish, explicit_receiver);
4317 return;
4318 }
4319 case PM_CALL_NODE: {
4320#define BLOCK_P(cast) ((cast)->block != NULL && PM_NODE_TYPE_P((cast)->block, PM_BLOCK_NODE))
4321
4322 // defined?(a(1, 2, 3))
4323 // ^^^^^^^^^^
4324 const pm_call_node_t *cast = ((const pm_call_node_t *) node);
4325
4326 if (cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE)) {
4327 dtype = DEFINED_EXPR;
4328 break;
4329 }
4330
4331 if (cast->receiver || cast->arguments || (cast->block && PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE))) {
4332 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4333 if (!lfinish[2]) lfinish[2] = NEW_LABEL(location.line);
4334 }
4335
4336 if (cast->arguments) {
4337 pm_compile_defined_expr0(iseq, (const pm_node_t *) cast->arguments, node_location, ret, popped, scope_node, true, lfinish, false);
4338 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4339 }
4340
4341 if (cast->block && PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE)) {
4342 pm_compile_defined_expr0(iseq, cast->block, node_location, ret, popped, scope_node, true, lfinish, false);
4343 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4344 }
4345
4346 if (cast->receiver) {
4347 if (PM_NODE_TYPE_P(cast->receiver, PM_CALL_NODE) && !BLOCK_P((const pm_call_node_t *) cast->receiver)) {
4348 // Special behavior here where we chain calls together. This is
4349 // the only path that sets explicit_receiver to true.
4350 pm_compile_defined_expr0(iseq, cast->receiver, node_location, ret, popped, scope_node, true, lfinish, true);
4351 PUSH_INSNL(ret, location, branchunless, lfinish[2]);
4352
4353 const pm_call_node_t *receiver = (const pm_call_node_t *) cast->receiver;
4354 ID method_id = pm_constant_id_lookup(scope_node, receiver->name);
4355
4356 pm_compile_call(iseq, receiver, ret, popped, scope_node, method_id, NULL);
4357 }
4358 else {
4359 pm_compile_defined_expr0(iseq, cast->receiver, node_location, ret, popped, scope_node, true, lfinish, false);
4360 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4361 PM_COMPILE(cast->receiver);
4362 }
4363
4364 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
4365
4366 if (explicit_receiver) PUSH_INSN(ret, location, dup);
4367 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_METHOD), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
4368 }
4369 else {
4370 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
4371
4372 PUSH_INSN(ret, location, putself);
4373 if (explicit_receiver) PUSH_INSN(ret, location, dup);
4374
4375 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_FUNC), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
4376 }
4377
4378 return;
4379 }
4380 case PM_ARGUMENTS_NODE: {
4381 // defined?(a(1, 2, 3))
4382 // ^^^^^^^
4383 const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node;
4384
4385 for (size_t index = 0; index < cast->arguments.size; index++) {
4386 pm_compile_defined_expr0(iseq, cast->arguments.nodes[index], node_location, ret, popped, scope_node, in_condition, lfinish, explicit_receiver);
4387
4388 if (!lfinish[1]) {
4389 lfinish[1] = NEW_LABEL(location.line);
4390 }
4391 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4392 }
4393
4394 dtype = DEFINED_TRUE;
4395 break;
4396 }
4397 case PM_BLOCK_ARGUMENT_NODE:
4398 // defined?(a(&b))
4399 // ^^
4400 dtype = DEFINED_EXPR;
4401 break;
4402 case PM_FORWARDING_ARGUMENTS_NODE:
4403 // def a(...) = defined?(a(...))
4404 // ^^^
4405 dtype = DEFINED_EXPR;
4406 break;
4407 case PM_SPLAT_NODE: {
4408 // def a(*) = defined?(a(*))
4409 // ^
4410 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
4411
4412 if (cast->expression == NULL) {
4413 dtype = DEFINED_EXPR;
4414 break;
4415 }
4416
4417 pm_compile_defined_expr0(iseq, cast->expression, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4418
4419 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4420 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4421
4422 dtype = DEFINED_EXPR;
4423 break;
4424 }
4425 case PM_SHAREABLE_CONSTANT_NODE:
4426 // # shareable_constant_value: literal
4427 // defined?(A = 1)
4428 // ^^^^^
4429 pm_compile_defined_expr0(iseq, ((const pm_shareable_constant_node_t *) node)->write, node_location, ret, popped, scope_node, in_condition, lfinish, explicit_receiver);
4430 return;
4431/* Unreachable (parameters) ***************************************************/
4432 case PM_BLOCK_LOCAL_VARIABLE_NODE:
4433 case PM_BLOCK_PARAMETER_NODE:
4434 case PM_BLOCK_PARAMETERS_NODE:
4435 case PM_FORWARDING_PARAMETER_NODE:
4436 case PM_IMPLICIT_REST_NODE:
4437 case PM_IT_PARAMETERS_NODE:
4438 case PM_PARAMETERS_NODE:
4439 case PM_KEYWORD_REST_PARAMETER_NODE:
4440 case PM_NO_KEYWORDS_PARAMETER_NODE:
4441 case PM_NUMBERED_PARAMETERS_NODE:
4442 case PM_OPTIONAL_KEYWORD_PARAMETER_NODE:
4443 case PM_OPTIONAL_PARAMETER_NODE:
4444 case PM_REQUIRED_KEYWORD_PARAMETER_NODE:
4445 case PM_REQUIRED_PARAMETER_NODE:
4446 case PM_REST_PARAMETER_NODE:
4447/* Unreachable (pattern matching) *********************************************/
4448 case PM_ALTERNATION_PATTERN_NODE:
4449 case PM_ARRAY_PATTERN_NODE:
4450 case PM_CAPTURE_PATTERN_NODE:
4451 case PM_FIND_PATTERN_NODE:
4452 case PM_HASH_PATTERN_NODE:
4453 case PM_PINNED_EXPRESSION_NODE:
4454 case PM_PINNED_VARIABLE_NODE:
4455/* Unreachable (indirect writes) **********************************************/
4456 case PM_CALL_TARGET_NODE:
4457 case PM_CLASS_VARIABLE_TARGET_NODE:
4458 case PM_CONSTANT_PATH_TARGET_NODE:
4459 case PM_CONSTANT_TARGET_NODE:
4460 case PM_GLOBAL_VARIABLE_TARGET_NODE:
4461 case PM_INDEX_TARGET_NODE:
4462 case PM_INSTANCE_VARIABLE_TARGET_NODE:
4463 case PM_LOCAL_VARIABLE_TARGET_NODE:
4464 case PM_MULTI_TARGET_NODE:
4465/* Unreachable (clauses) ******************************************************/
4466 case PM_ELSE_NODE:
4467 case PM_ENSURE_NODE:
4468 case PM_IN_NODE:
4469 case PM_RESCUE_NODE:
4470 case PM_WHEN_NODE:
4471/* Unreachable (miscellaneous) ************************************************/
4472 case PM_BLOCK_NODE:
4473 case PM_EMBEDDED_STATEMENTS_NODE:
4474 case PM_EMBEDDED_VARIABLE_NODE:
4475 case PM_MISSING_NODE:
4476 case PM_PRE_EXECUTION_NODE:
4477 case PM_PROGRAM_NODE:
4478 case PM_SCOPE_NODE:
4479 case PM_STATEMENTS_NODE:
4480 rb_bug("Unreachable node in defined?: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
4481 }
4482
4483 RUBY_ASSERT(dtype != DEFINED_NOT_DEFINED);
4484 PUSH_INSN1(ret, location, putobject, PUSH_VAL(dtype));
4485
4486#undef PUSH_VAL
4487}
4488
4489static void
4490pm_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, bool explicit_receiver)
4491{
4492 LINK_ELEMENT *lcur = ret->last;
4493 pm_compile_defined_expr0(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4494
4495 if (lfinish[1]) {
4496 LABEL *lstart = NEW_LABEL(node_location->line);
4497 LABEL *lend = NEW_LABEL(node_location->line);
4498
4499 struct rb_iseq_new_with_callback_callback_func *ifunc =
4500 rb_iseq_new_with_callback_new_callback(build_defined_rescue_iseq, NULL);
4501
4502 const rb_iseq_t *rescue = new_child_iseq_with_callback(
4503 iseq,
4504 ifunc,
4505 rb_str_concat(rb_str_new2("defined guard in "), ISEQ_BODY(iseq)->location.label),
4506 iseq,
4507 ISEQ_TYPE_RESCUE,
4508 0
4509 );
4510
4511 lstart->rescued = LABEL_RESCUE_BEG;
4512 lend->rescued = LABEL_RESCUE_END;
4513
4514 APPEND_LABEL(ret, lcur, lstart);
4515 PUSH_LABEL(ret, lend);
4516 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
4517 }
4518}
4519
4520static void
4521pm_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)
4522{
4523 LABEL *lfinish[3];
4524 LINK_ELEMENT *last = ret->last;
4525
4526 lfinish[0] = NEW_LABEL(node_location->line);
4527 lfinish[1] = 0;
4528 lfinish[2] = 0;
4529
4530 if (!popped) {
4531 pm_defined_expr(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4532 }
4533
4534 if (lfinish[1]) {
4535 ELEM_INSERT_NEXT(last, &new_insn_body(iseq, node_location->line, node_location->node_id, BIN(putnil), 0)->link);
4536 PUSH_INSN(ret, *node_location, swap);
4537
4538 if (lfinish[2]) PUSH_LABEL(ret, lfinish[2]);
4539 PUSH_INSN(ret, *node_location, pop);
4540 PUSH_LABEL(ret, lfinish[1]);
4541
4542 }
4543
4544 PUSH_LABEL(ret, lfinish[0]);
4545}
4546
4547// This is exactly the same as add_ensure_iseq, except it compiled
4548// the node as a Prism node, and not a CRuby node
4549static void
4550pm_add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return, pm_scope_node_t *scope_node)
4551{
4552 RUBY_ASSERT(can_add_ensure_iseq(iseq));
4553
4554 struct iseq_compile_data_ensure_node_stack *enlp =
4555 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
4556 struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
4557 DECL_ANCHOR(ensure);
4558
4559 while (enlp) {
4560 if (enlp->erange != NULL) {
4561 DECL_ANCHOR(ensure_part);
4562 LABEL *lstart = NEW_LABEL(0);
4563 LABEL *lend = NEW_LABEL(0);
4564
4565 add_ensure_range(iseq, enlp->erange, lstart, lend);
4566
4567 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
4568 PUSH_LABEL(ensure_part, lstart);
4569 bool popped = true;
4570 PM_COMPILE_INTO_ANCHOR(ensure_part, (const pm_node_t *) enlp->ensure_node);
4571 PUSH_LABEL(ensure_part, lend);
4572 PUSH_SEQ(ensure, ensure_part);
4573 }
4574 else {
4575 if (!is_return) {
4576 break;
4577 }
4578 }
4579 enlp = enlp->prev;
4580 }
4581 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = prev_enlp;
4582 PUSH_SEQ(ret, ensure);
4583}
4584
4585 struct pm_local_table_insert_ctx {
4586 pm_scope_node_t *scope_node;
4587 rb_ast_id_table_t *local_table_for_iseq;
4588 int local_index;
4589};
4590
4591static int
4592pm_local_table_insert_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
4593{
4594 if (!existing) {
4595 pm_constant_id_t constant_id = (pm_constant_id_t) *key;
4596 struct pm_local_table_insert_ctx * ctx = (struct pm_local_table_insert_ctx *) arg;
4597
4598 pm_scope_node_t *scope_node = ctx->scope_node;
4599 rb_ast_id_table_t *local_table_for_iseq = ctx->local_table_for_iseq;
4600 int local_index = ctx->local_index;
4601
4602 ID local = pm_constant_id_lookup(scope_node, constant_id);
4603 local_table_for_iseq->ids[local_index] = local;
4604
4605 *value = (st_data_t)local_index;
4606
4607 ctx->local_index++;
4608 }
4609
4610 return ST_CONTINUE;
4611}
4612
4618static void
4619pm_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)
4620{
4621 RUBY_ASSERT((constant_id & PM_SPECIAL_CONSTANT_FLAG) == 0);
4622
4623 ID local = pm_constant_id_lookup(scope_node, constant_id);
4624 local_table_for_iseq->ids[local_index] = local;
4625 st_insert(index_lookup_table, (st_data_t) constant_id, (st_data_t) local_index);
4626}
4627
4632static void
4633pm_insert_local_special(ID local_name, int local_index, st_table *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq)
4634{
4635 local_table_for_iseq->ids[local_index] = local_name;
4636 st_insert(index_lookup_table, (st_data_t) (local_name | PM_SPECIAL_CONSTANT_FLAG), (st_data_t) local_index);
4637}
4638
4645static int
4646pm_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)
4647{
4648 for (size_t index = 0; index < node->lefts.size; index++) {
4649 const pm_node_t *left = node->lefts.nodes[index];
4650
4651 if (PM_NODE_TYPE_P(left, PM_REQUIRED_PARAMETER_NODE)) {
4652 if (!PM_NODE_FLAG_P(left, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4653 pm_insert_local_index(((const pm_required_parameter_node_t *) left)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4654 local_index++;
4655 }
4656 }
4657 else {
4658 RUBY_ASSERT(PM_NODE_TYPE_P(left, PM_MULTI_TARGET_NODE));
4659 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);
4660 }
4661 }
4662
4663 if (node->rest != NULL && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE)) {
4664 const pm_splat_node_t *rest = (const pm_splat_node_t *) node->rest;
4665
4666 if (rest->expression != NULL) {
4667 RUBY_ASSERT(PM_NODE_TYPE_P(rest->expression, PM_REQUIRED_PARAMETER_NODE));
4668
4669 if (!PM_NODE_FLAG_P(rest->expression, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4670 pm_insert_local_index(((const pm_required_parameter_node_t *) rest->expression)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4671 local_index++;
4672 }
4673 }
4674 }
4675
4676 for (size_t index = 0; index < node->rights.size; index++) {
4677 const pm_node_t *right = node->rights.nodes[index];
4678
4679 if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) {
4680 if (!PM_NODE_FLAG_P(right, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4681 pm_insert_local_index(((const pm_required_parameter_node_t *) right)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4682 local_index++;
4683 }
4684 }
4685 else {
4686 RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE));
4687 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);
4688 }
4689 }
4690
4691 return local_index;
4692}
4693
4698static inline void
4699pm_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)
4700{
4701 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
4702 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, node->name, 0);
4703 PUSH_SETLOCAL(ret, location, index.index, index.level);
4704}
4705
4714static void
4715pm_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)
4716{
4717 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
4718 bool has_rest = (node->rest && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE) && (((const pm_splat_node_t *) node->rest)->expression) != NULL);
4719 bool has_rights = node->rights.size > 0;
4720
4721 int flag = (has_rest || has_rights) ? 1 : 0;
4722 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->lefts.size), INT2FIX(flag));
4723
4724 for (size_t index = 0; index < node->lefts.size; index++) {
4725 const pm_node_t *left = node->lefts.nodes[index];
4726
4727 if (PM_NODE_TYPE_P(left, PM_REQUIRED_PARAMETER_NODE)) {
4728 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) left, ret, scope_node);
4729 }
4730 else {
4731 RUBY_ASSERT(PM_NODE_TYPE_P(left, PM_MULTI_TARGET_NODE));
4732 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) left, ret, scope_node);
4733 }
4734 }
4735
4736 if (has_rest) {
4737 if (has_rights) {
4738 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->rights.size), INT2FIX(3));
4739 }
4740
4741 const pm_node_t *rest = ((const pm_splat_node_t *) node->rest)->expression;
4742 RUBY_ASSERT(PM_NODE_TYPE_P(rest, PM_REQUIRED_PARAMETER_NODE));
4743
4744 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) rest, ret, scope_node);
4745 }
4746
4747 if (has_rights) {
4748 if (!has_rest) {
4749 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->rights.size), INT2FIX(2));
4750 }
4751
4752 for (size_t index = 0; index < node->rights.size; index++) {
4753 const pm_node_t *right = node->rights.nodes[index];
4754
4755 if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) {
4756 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) right, ret, scope_node);
4757 }
4758 else {
4759 RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE));
4760 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) right, ret, scope_node);
4761 }
4762 }
4763 }
4764}
4765
4770 typedef struct pm_multi_target_state_node {
4771 // The pointer to the topn instruction that will need to be modified after
4772 // we know the total stack size of all of the targets.
4773 INSN *topn;
4774
4775 // The index of the stack from the base of the entire multi target at which
4776 // the parent expression is located.
4777 size_t stack_index;
4778
4779 // The number of slots in the stack that this node occupies.
4780 size_t stack_size;
4781
4782 // The position of the node in the list of targets.
4783 size_t position;
4784
4785 // A pointer to the next node in this linked list.
4786 struct pm_multi_target_state_node *next;
4787} pm_multi_target_state_node_t;
4788
4796 typedef struct {
4797 // The total number of slots in the stack that this multi target occupies.
4798 size_t stack_size;
4799
4800 // The position of the current node being compiled. This is forwarded to
4801 // nodes when they are allocated.
4802 size_t position;
4803
4804 // A pointer to the head of this linked list.
4805 pm_multi_target_state_node_t *head;
4806
4807 // A pointer to the tail of this linked list.
4808 pm_multi_target_state_node_t *tail;
4809} pm_multi_target_state_t;
4810
4814static void
4815pm_multi_target_state_push(pm_multi_target_state_t *state, INSN *topn, size_t stack_size)
4816{
4817 pm_multi_target_state_node_t *node = ALLOC(pm_multi_target_state_node_t);
4818 node->topn = topn;
4819 node->stack_index = state->stack_size + 1;
4820 node->stack_size = stack_size;
4821 node->position = state->position;
4822 node->next = NULL;
4823
4824 if (state->head == NULL) {
4825 state->head = node;
4826 state->tail = node;
4827 }
4828 else {
4829 state->tail->next = node;
4830 state->tail = node;
4831 }
4832
4833 state->stack_size += stack_size;
4834}
4835
4841static void
4842pm_multi_target_state_update(pm_multi_target_state_t *state)
4843{
4844 // If nothing was ever pushed onto the stack, then we don't need to do any
4845 // kind of updates.
4846 if (state->stack_size == 0) return;
4847
4848 pm_multi_target_state_node_t *current = state->head;
4849 pm_multi_target_state_node_t *previous;
4850
4851 while (current != NULL) {
4852 VALUE offset = INT2FIX(state->stack_size - current->stack_index + current->position);
4853 current->topn->operands[0] = offset;
4854
4855 // stack_size will be > 1 in the case that we compiled an index target
4856 // and it had arguments. In this case, we use multiple topn instructions
4857 // to grab up all of the arguments as well, so those offsets need to be
4858 // updated as well.
4859 if (current->stack_size > 1) {
4860 INSN *insn = current->topn;
4861
4862 for (size_t index = 1; index < current->stack_size; index += 1) {
4863 LINK_ELEMENT *element = get_next_insn(insn);
4864 RUBY_ASSERT(IS_INSN(element));
4865
4866 insn = (INSN *) element;
4867 RUBY_ASSERT(insn->insn_id == BIN(topn));
4868
4869 insn->operands[0] = offset;
4870 }
4871 }
4872
4873 previous = current;
4874 current = current->next;
4875
4876 xfree(previous);
4877 }
4878}
4879
4880static void
4881pm_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);
4882
4911static void
4912pm_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)
4913{
4914 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
4915
4916 switch (PM_NODE_TYPE(node)) {
4917 case PM_LOCAL_VARIABLE_TARGET_NODE: {
4918 // Local variable targets have no parent expression, so they only need
4919 // to compile the write.
4920 //
4921 // for i in []; end
4922 //
4923 const pm_local_variable_target_node_t *cast = (const pm_local_variable_target_node_t *) node;
4924 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
4925
4926 PUSH_SETLOCAL(writes, location, index.index, index.level);
4927 break;
4928 }
4929 case PM_CLASS_VARIABLE_TARGET_NODE: {
4930 // Class variable targets have no parent expression, so they only need
4931 // to compile the write.
4932 //
4933 // for @@i in []; end
4934 //
4935 const pm_class_variable_target_node_t *cast = (const pm_class_variable_target_node_t *) node;
4936 ID name = pm_constant_id_lookup(scope_node, cast->name);
4937
4938 VALUE operand = ID2SYM(name);
4939 PUSH_INSN2(writes, location, setclassvariable, operand, get_cvar_ic_value(iseq, name));
4940 break;
4941 }
4942 case PM_CONSTANT_TARGET_NODE: {
4943 // Constant targets have no parent expression, so they only need to
4944 // compile the write.
4945 //
4946 // for I in []; end
4947 //
4948 const pm_constant_target_node_t *cast = (const pm_constant_target_node_t *) node;
4949 ID name = pm_constant_id_lookup(scope_node, cast->name);
4950
4951 VALUE operand = ID2SYM(name);
4952 PUSH_INSN1(writes, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
4953 PUSH_INSN1(writes, location, setconstant, operand);
4954 break;
4955 }
4956 case PM_GLOBAL_VARIABLE_TARGET_NODE: {
4957 // Global variable targets have no parent expression, so they only need
4958 // to compile the write.
4959 //
4960 // for $i in []; end
4961 //
4962 const pm_global_variable_target_node_t *cast = (const pm_global_variable_target_node_t *) node;
4963 ID name = pm_constant_id_lookup(scope_node, cast->name);
4964
4965 VALUE operand = ID2SYM(name);
4966 PUSH_INSN1(writes, location, setglobal, operand);
4967 break;
4968 }
4969 case PM_INSTANCE_VARIABLE_TARGET_NODE: {
4970 // Instance variable targets have no parent expression, so they only
4971 // need to compile the write.
4972 //
4973 // for @i in []; end
4974 //
4975 const pm_instance_variable_target_node_t *cast = (const pm_instance_variable_target_node_t *) node;
4976 ID name = pm_constant_id_lookup(scope_node, cast->name);
4977
4978 VALUE operand = ID2SYM(name);
4979 PUSH_INSN2(writes, location, setinstancevariable, operand, get_ivar_ic_value(iseq, name));
4980 break;
4981 }
4982 case PM_CONSTANT_PATH_TARGET_NODE: {
4983 // Constant path targets have a parent expression that is the object
4984 // that owns the constant. This needs to be compiled first into the
4985 // parents sequence. If no parent is found, then it represents using the
4986 // unary :: operator to indicate a top-level constant. In that case we
4987 // need to push Object onto the stack.
4988 //
4989 // for I::J in []; end
4990 //
4991 const pm_constant_path_target_node_t *cast = (const pm_constant_path_target_node_t *) node;
4992 ID name = pm_constant_id_lookup(scope_node, cast->name);
4993
4994 if (cast->parent != NULL) {
4995 pm_compile_node(iseq, cast->parent, parents, false, scope_node);
4996 }
4997 else {
4998 PUSH_INSN1(parents, location, putobject, rb_cObject);
4999 }
5000
5001 if (state == NULL) {
5002 PUSH_INSN(writes, location, swap);
5003 }
5004 else {
5005 PUSH_INSN1(writes, location, topn, INT2FIX(1));
5006 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), 1);
5007 }
5008
5009 VALUE operand = ID2SYM(name);
5010 PUSH_INSN1(writes, location, setconstant, operand);
5011
5012 if (state != NULL) {
5013 PUSH_INSN(cleanup, location, pop);
5014 }
5015
5016 break;
5017 }
5018 case PM_CALL_TARGET_NODE: {
5019 // Call targets have a parent expression that is the receiver of the
5020 // method being called. This needs to be compiled first into the parents
5021 // sequence. These nodes cannot have arguments, so the method call is
5022 // compiled with a single argument which represents the value being
5023 // written.
5024 //
5025 // for i.j in []; end
5026 //
5027 const pm_call_target_node_t *cast = (const pm_call_target_node_t *) node;
5028 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
5029
5030 pm_compile_node(iseq, cast->receiver, parents, false, scope_node);
5031
5032 LABEL *safe_label = NULL;
5033 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
5034 safe_label = NEW_LABEL(location.line);
5035 PUSH_INSN(parents, location, dup);
5036 PUSH_INSNL(parents, location, branchnil, safe_label);
5037 }
5038
5039 if (state != NULL) {
5040 PUSH_INSN1(writes, location, topn, INT2FIX(1));
5041 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), 1);
5042 PUSH_INSN(writes, location, swap);
5043 }
5044
5045 int flags = VM_CALL_ARGS_SIMPLE;
5046 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) flags |= VM_CALL_FCALL;
5047
5048 PUSH_SEND_WITH_FLAG(writes, location, method_id, INT2FIX(1), INT2FIX(flags));
5049 if (safe_label != NULL && state == NULL) PUSH_LABEL(writes, safe_label);
5050 PUSH_INSN(writes, location, pop);
5051 if (safe_label != NULL && state != NULL) PUSH_LABEL(writes, safe_label);
5052
5053 if (state != NULL) {
5054 PUSH_INSN(cleanup, location, pop);
5055 }
5056
5057 break;
5058 }
5059 case PM_INDEX_TARGET_NODE: {
5060 // Index targets have a parent expression that is the receiver of the
5061 // method being called and any additional arguments that are being
5062 // passed along with the value being written. The receiver and arguments
5063 // both need to be on the stack. Note that this is even more complicated
5064 // by the fact that these nodes can hold a block using the unary &
5065 // operator.
5066 //
5067 // for i[:j] in []; end
5068 //
5069 const pm_index_target_node_t *cast = (const pm_index_target_node_t *) node;
5070
5071 pm_compile_node(iseq, cast->receiver, parents, false, scope_node);
5072
5073 int flags = 0;
5074 struct rb_callinfo_kwarg *kwargs = NULL;
5075 int argc = pm_setup_args(cast->arguments, (const pm_node_t *) cast->block, &flags, &kwargs, iseq, parents, scope_node, &location);
5076
5077 if (state != NULL) {
5078 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5079 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), argc + 1);
5080
5081 if (argc == 0) {
5082 PUSH_INSN(writes, location, swap);
5083 }
5084 else {
5085 for (int index = 0; index < argc; index++) {
5086 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5087 }
5088 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5089 }
5090 }
5091
5092 // The argc that we're going to pass to the send instruction is the
5093 // number of arguments + 1 for the value being written. If there's a
5094 // splat, then we need to insert newarray and concatarray instructions
5095 // after the arguments have been written.
5096 int ci_argc = argc + 1;
5097 if (flags & VM_CALL_ARGS_SPLAT) {
5098 ci_argc--;
5099 PUSH_INSN1(writes, location, newarray, INT2FIX(1));
5100 PUSH_INSN(writes, location, concatarray);
5101 }
5102
5103 PUSH_SEND_R(writes, location, idASET, INT2NUM(ci_argc), NULL, INT2FIX(flags), kwargs);
5104 PUSH_INSN(writes, location, pop);
5105
5106 if (state != NULL) {
5107 if (argc != 0) {
5108 PUSH_INSN(writes, location, pop);
5109 }
5110
5111 for (int index = 0; index < argc + 1; index++) {
5112 PUSH_INSN(cleanup, location, pop);
5113 }
5114 }
5115
5116 break;
5117 }
5118 case PM_MULTI_TARGET_NODE: {
5119 // Multi target nodes represent a set of writes to multiple variables.
5120 // The parent expressions are the combined set of the parent expressions
5121 // of its inner target nodes.
5122 //
5123 // for i, j in []; end
5124 //
5125 size_t before_position;
5126 if (state != NULL) {
5127 before_position = state->position;
5128 state->position--;
5129 }
5130
5131 pm_compile_multi_target_node(iseq, node, parents, writes, cleanup, scope_node, state);
5132 if (state != NULL) state->position = before_position;
5133
5134 break;
5135 }
5136 case PM_SPLAT_NODE: {
5137 // Splat nodes capture all values into an array. They can be used
5138 // as targets in assignments or for loops.
5139 //
5140 // for *x in []; end
5141 //
5142 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
5143
5144 if (cast->expression != NULL) {
5145 pm_compile_target_node(iseq, cast->expression, parents, writes, cleanup, scope_node, state);
5146 }
5147
5148 break;
5149 }
5150 default:
5151 rb_bug("Unexpected node type: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
5152 break;
5153 }
5154}
5155
5161static void
5162pm_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)
5163{
5164 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
5165 const pm_node_list_t *lefts;
5166 const pm_node_t *rest;
5167 const pm_node_list_t *rights;
5168
5169 switch (PM_NODE_TYPE(node)) {
5170 case PM_MULTI_TARGET_NODE: {
5171 const pm_multi_target_node_t *cast = (const pm_multi_target_node_t *) node;
5172 lefts = &cast->lefts;
5173 rest = cast->rest;
5174 rights = &cast->rights;
5175 break;
5176 }
5177 case PM_MULTI_WRITE_NODE: {
5178 const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node;
5179 lefts = &cast->lefts;
5180 rest = cast->rest;
5181 rights = &cast->rights;
5182 break;
5183 }
5184 default:
5185 rb_bug("Unsupported node %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
5186 break;
5187 }
5188
5189 bool has_rest = (rest != NULL) && PM_NODE_TYPE_P(rest, PM_SPLAT_NODE) && ((const pm_splat_node_t *) rest)->expression != NULL;
5190 bool has_posts = rights->size > 0;
5191
5192 // The first instruction in the writes sequence is going to spread the
5193 // top value of the stack onto the number of values that we're going to
5194 // write.
5195 PUSH_INSN2(writes, location, expandarray, INT2FIX(lefts->size), INT2FIX((has_rest || has_posts) ? 1 : 0));
5196
5197 // We need to keep track of some additional state information as we're
5198 // going through the targets because we will need to revisit them once
5199 // we know how many values are being pushed onto the stack.
5200 pm_multi_target_state_t target_state = { 0 };
5201 if (state == NULL) state = &target_state;
5202
5203 size_t base_position = state->position;
5204 size_t splat_position = (has_rest || has_posts) ? 1 : 0;
5205
5206 // Next, we'll iterate through all of the leading targets.
5207 for (size_t index = 0; index < lefts->size; index++) {
5208 const pm_node_t *target = lefts->nodes[index];
5209 state->position = lefts->size - index + splat_position + base_position;
5210 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5211 }
5212
5213 // Next, we'll compile the rest target if there is one.
5214 if (has_rest) {
5215 const pm_node_t *target = ((const pm_splat_node_t *) rest)->expression;
5216 state->position = 1 + rights->size + base_position;
5217
5218 if (has_posts) {
5219 PUSH_INSN2(writes, location, expandarray, INT2FIX(rights->size), INT2FIX(3));
5220 }
5221
5222 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5223 }
5224
5225 // Finally, we'll compile the trailing targets.
5226 if (has_posts) {
5227 if (!has_rest && rest != NULL) {
5228 PUSH_INSN2(writes, location, expandarray, INT2FIX(rights->size), INT2FIX(2));
5229 }
5230
5231 for (size_t index = 0; index < rights->size; index++) {
5232 const pm_node_t *target = rights->nodes[index];
5233 state->position = rights->size - index + base_position;
5234 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5235 }
5236 }
5237}
5238
5244static void
5245pm_compile_for_node_index(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
5246{
5247 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
5248
5249 switch (PM_NODE_TYPE(node)) {
5250 case PM_LOCAL_VARIABLE_TARGET_NODE: {
5251 // For local variables, all we have to do is retrieve the value and then
5252 // compile the index node.
5253 PUSH_GETLOCAL(ret, location, 1, 0);
5254 pm_compile_target_node(iseq, node, ret, ret, ret, scope_node, NULL);
5255 break;
5256 }
5257 case PM_CLASS_VARIABLE_TARGET_NODE:
5258 case PM_CONSTANT_TARGET_NODE:
5259 case PM_GLOBAL_VARIABLE_TARGET_NODE:
5260 case PM_INSTANCE_VARIABLE_TARGET_NODE:
5261 case PM_CONSTANT_PATH_TARGET_NODE:
5262 case PM_CALL_TARGET_NODE:
5263 case PM_INDEX_TARGET_NODE: {
5264 // For other targets, we need to potentially compile the parent or
5265 // owning expression of this target, then retrieve the value, expand it,
5266 // and then compile the necessary writes.
5267 DECL_ANCHOR(writes);
5268 DECL_ANCHOR(cleanup);
5269
5270 pm_multi_target_state_t state = { 0 };
5271 state.position = 1;
5272 pm_compile_target_node(iseq, node, ret, writes, cleanup, scope_node, &state);
5273
5274 PUSH_GETLOCAL(ret, location, 1, 0);
5275 PUSH_INSN2(ret, location, expandarray, INT2FIX(1), INT2FIX(0));
5276
5277 PUSH_SEQ(ret, writes);
5278 PUSH_SEQ(ret, cleanup);
5279
5280 pm_multi_target_state_update(&state);
5281 break;
5282 }
5283 case PM_SPLAT_NODE:
5284 case PM_MULTI_TARGET_NODE: {
5285 DECL_ANCHOR(writes);
5286 DECL_ANCHOR(cleanup);
5287
5288 pm_compile_target_node(iseq, node, ret, writes, cleanup, scope_node, NULL);
5289
5290 LABEL *not_single = NEW_LABEL(location.line);
5291 LABEL *not_ary = NEW_LABEL(location.line);
5292
5293 // When there are multiple targets, we'll do a bunch of work to convert
5294 // the value into an array before we expand it. Effectively we're trying
5295 // to accomplish:
5296 //
5297 // (args.length == 1 && Array.try_convert(args[0])) || args
5298 //
5299 PUSH_GETLOCAL(ret, location, 1, 0);
5300 PUSH_INSN(ret, location, dup);
5301 PUSH_CALL(ret, location, idLength, INT2FIX(0));
5302 PUSH_INSN1(ret, location, putobject, INT2FIX(1));
5303 PUSH_CALL(ret, location, idEq, INT2FIX(1));
5304 PUSH_INSNL(ret, location, branchunless, not_single);
5305 PUSH_INSN(ret, location, dup);
5306 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
5307 PUSH_CALL(ret, location, idAREF, INT2FIX(1));
5308 PUSH_INSN1(ret, location, putobject, rb_cArray);
5309 PUSH_INSN(ret, location, swap);
5310 PUSH_CALL(ret, location, rb_intern("try_convert"), INT2FIX(1));
5311 PUSH_INSN(ret, location, dup);
5312 PUSH_INSNL(ret, location, branchunless, not_ary);
5313 PUSH_INSN(ret, location, swap);
5314
5315 PUSH_LABEL(ret, not_ary);
5316 PUSH_INSN(ret, location, pop);
5317
5318 PUSH_LABEL(ret, not_single);
5319
5320 if (PM_NODE_TYPE_P(node, PM_SPLAT_NODE)) {
5321 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
5322 PUSH_INSN2(ret, location, expandarray, INT2FIX(0), INT2FIX(cast->expression == NULL ? 0 : 1));
5323 }
5324
5325 PUSH_SEQ(ret, writes);
5326 PUSH_SEQ(ret, cleanup);
5327 break;
5328 }
5329 default:
5330 rb_bug("Unexpected node type for index in for node: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
5331 break;
5332 }
5333}
5334
5335static void
5336pm_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)
5337{
5338 const pm_parser_t *parser = scope_node->parser;
5339
5340 LABEL *lstart = NEW_LABEL(node_location->line);
5341 LABEL *lend = NEW_LABEL(node_location->line);
5342 LABEL *lcont = NEW_LABEL(node_location->line);
5343
5344 pm_scope_node_t rescue_scope_node;
5345 pm_scope_node_init((const pm_node_t *) cast->rescue_clause, &rescue_scope_node, scope_node);
5346
5347 rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(
5348 &rescue_scope_node,
5349 rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label),
5350 ISEQ_TYPE_RESCUE,
5351 pm_node_line_number(parser, (const pm_node_t *) cast->rescue_clause)
5352 );
5353
5354 pm_scope_node_destroy(&rescue_scope_node);
5355
5356 lstart->rescued = LABEL_RESCUE_BEG;
5357 lend->rescued = LABEL_RESCUE_END;
5358 PUSH_LABEL(ret, lstart);
5359
5360 bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
5361 ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
5362
5363 if (cast->statements != NULL) {
5364 PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->statements);
5365 }
5366 else {
5367 const pm_node_location_t location = PM_NODE_START_LOCATION(parser, cast->rescue_clause);
5368 PUSH_INSN(ret, location, putnil);
5369 }
5370
5371 ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
5372 PUSH_LABEL(ret, lend);
5373
5374 if (cast->else_clause != NULL) {
5375 if (!popped) PUSH_INSN(ret, *node_location, pop);
5376 PM_COMPILE((const pm_node_t *) cast->else_clause);
5377 }
5378
5379 PUSH_INSN(ret, *node_location, nop);
5380 PUSH_LABEL(ret, lcont);
5381
5382 if (popped) PUSH_INSN(ret, *node_location, pop);
5383 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
5384 PUSH_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
5385}
5386
5387static void
5388pm_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)
5389{
5390 const pm_parser_t *parser = scope_node->parser;
5391 const pm_statements_node_t *statements = cast->ensure_clause->statements;
5392
5393 pm_node_location_t location;
5394 if (statements != NULL) {
5395 location = PM_NODE_START_LOCATION(parser, statements);
5396 }
5397 else {
5398 location = *node_location;
5399 }
5400
5401 LABEL *lstart = NEW_LABEL(location.line);
5402 LABEL *lend = NEW_LABEL(location.line);
5403 LABEL *lcont = NEW_LABEL(location.line);
5404
5405 struct ensure_range er;
5406 struct iseq_compile_data_ensure_node_stack enl;
5407 struct ensure_range *erange;
5408
5409 DECL_ANCHOR(ensr);
5410 if (statements != NULL) {
5411 pm_compile_node(iseq, (const pm_node_t *) statements, ensr, true, scope_node);
5412 }
5413
5414 LINK_ELEMENT *last = ensr->last;
5415 bool last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave);
5416
5417 er.begin = lstart;
5418 er.end = lend;
5419 er.next = 0;
5420 push_ensure_entry(iseq, &enl, &er, (void *) cast->ensure_clause);
5421
5422 PUSH_LABEL(ret, lstart);
5423 if (cast->rescue_clause != NULL) {
5424 pm_compile_rescue(iseq, cast, node_location, ret, popped | last_leave, scope_node);
5425 }
5426 else if (cast->statements != NULL) {
5427 pm_compile_node(iseq, (const pm_node_t *) cast->statements, ret, popped | last_leave, scope_node);
5428 }
5429 else if (!(popped | last_leave)) {
5430 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
5431 }
5432
5433 PUSH_LABEL(ret, lend);
5434 PUSH_SEQ(ret, ensr);
5435 if (!popped && last_leave) PUSH_INSN(ret, *node_location, putnil);
5436 PUSH_LABEL(ret, lcont);
5437 if (last_leave) PUSH_INSN(ret, *node_location, pop);
5438
5439 pm_scope_node_t next_scope_node;
5440 pm_scope_node_init((const pm_node_t *) cast->ensure_clause, &next_scope_node, scope_node);
5441
5442 rb_iseq_t *child_iseq = NEW_CHILD_ISEQ(
5443 &next_scope_node,
5444 rb_str_concat(rb_str_new2("ensure in "), ISEQ_BODY(iseq)->location.label),
5445 ISEQ_TYPE_ENSURE,
5446 location.line
5447 );
5448
5449 pm_scope_node_destroy(&next_scope_node);
5450
5451 erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
5452 if (lstart->link.next != &lend->link) {
5453 while (erange) {
5454 PUSH_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end, child_iseq, lcont);
5455 erange = erange->next;
5456 }
5457 }
5458 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
5459}
5460
5465static inline bool
5466pm_opt_str_freeze_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
5467{
5468 return (
5469 !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) &&
5470 node->receiver != NULL &&
5471 PM_NODE_TYPE_P(node->receiver, PM_STRING_NODE) &&
5472 node->arguments == NULL &&
5473 node->block == NULL &&
5474 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
5475 );
5476}
5477
5482static inline bool
5483pm_opt_aref_with_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
5484{
5485 return (
5486 !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) &&
5487 node->arguments != NULL &&
5488 PM_NODE_TYPE_P((const pm_node_t *) node->arguments, PM_ARGUMENTS_NODE) &&
5489 ((const pm_arguments_node_t *) node->arguments)->arguments.size == 1 &&
5490 PM_NODE_TYPE_P(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0], PM_STRING_NODE) &&
5491 node->block == NULL &&
5492 !PM_NODE_FLAG_P(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0], PM_STRING_FLAGS_FROZEN) &&
5493 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
5494 );
5495}
5496
5501static inline bool
5502pm_opt_aset_with_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
5503{
5504 return (
5505 !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) &&
5506 node->arguments != NULL &&
5507 PM_NODE_TYPE_P((const pm_node_t *) node->arguments, PM_ARGUMENTS_NODE) &&
5508 ((const pm_arguments_node_t *) node->arguments)->arguments.size == 2 &&
5509 PM_NODE_TYPE_P(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0], PM_STRING_NODE) &&
5510 node->block == NULL &&
5511 !PM_NODE_FLAG_P(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0], PM_STRING_FLAGS_FROZEN) &&
5512 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
5513 );
5514}
5515
5520static void
5521pm_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)
5522{
5523 const pm_node_location_t location = PM_LOCATION_START_LOCATION(scope_node->parser, name_loc, node_id);
5524
5525 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
5526 ISEQ_BODY(iseq)->ic_size++;
5527 VALUE segments = rb_ary_new_from_args(1, name);
5528 PUSH_INSN1(ret, location, opt_getconstant_path, segments);
5529 }
5530 else {
5531 PUSH_INSN(ret, location, putnil);
5532 PUSH_INSN1(ret, location, putobject, Qtrue);
5533 PUSH_INSN1(ret, location, getconstant, name);
5534 }
5535}
5536
5541static VALUE
5542pm_constant_path_parts(const pm_node_t *node, const pm_scope_node_t *scope_node)
5543{
5544 VALUE parts = rb_ary_new();
5545
5546 while (true) {
5547 switch (PM_NODE_TYPE(node)) {
5548 case PM_CONSTANT_READ_NODE: {
5549 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
5550 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5551
5552 rb_ary_unshift(parts, name);
5553 return parts;
5554 }
5555 case PM_CONSTANT_PATH_NODE: {
5556 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
5557 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5558
5559 rb_ary_unshift(parts, name);
5560 if (cast->parent == NULL) {
5561 rb_ary_unshift(parts, ID2SYM(idNULL));
5562 return parts;
5563 }
5564
5565 node = cast->parent;
5566 break;
5567 }
5568 default:
5569 return Qnil;
5570 }
5571 }
5572}
5573
5579static void
5580pm_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)
5581{
5582 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
5583
5584 switch (PM_NODE_TYPE(node)) {
5585 case PM_CONSTANT_READ_NODE: {
5586 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
5587 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5588
5589 PUSH_INSN1(body, location, putobject, Qtrue);
5590 PUSH_INSN1(body, location, getconstant, name);
5591 break;
5592 }
5593 case PM_CONSTANT_PATH_NODE: {
5594 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
5595 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5596
5597 if (cast->parent == NULL) {
5598 PUSH_INSN(body, location, pop);
5599 PUSH_INSN1(body, location, putobject, rb_cObject);
5600 PUSH_INSN1(body, location, putobject, Qtrue);
5601 PUSH_INSN1(body, location, getconstant, name);
5602 }
5603 else {
5604 pm_compile_constant_path(iseq, cast->parent, prefix, body, false, scope_node);
5605 PUSH_INSN1(body, location, putobject, Qfalse);
5606 PUSH_INSN1(body, location, getconstant, name);
5607 }
5608 break;
5609 }
5610 default:
5611 PM_COMPILE_INTO_ANCHOR(prefix, node);
5612 break;
5613 }
5614}
5615
5619static VALUE
5620pm_compile_shareable_constant_literal(rb_iseq_t *iseq, const pm_node_t *node, const pm_scope_node_t *scope_node)
5621{
5622 switch (PM_NODE_TYPE(node)) {
5623 case PM_TRUE_NODE:
5624 case PM_FALSE_NODE:
5625 case PM_NIL_NODE:
5626 case PM_SYMBOL_NODE:
5627 case PM_REGULAR_EXPRESSION_NODE:
5628 case PM_SOURCE_LINE_NODE:
5629 case PM_INTEGER_NODE:
5630 case PM_FLOAT_NODE:
5631 case PM_RATIONAL_NODE:
5632 case PM_IMAGINARY_NODE:
5633 case PM_SOURCE_ENCODING_NODE:
5634 return pm_static_literal_value(iseq, node, scope_node);
5635 case PM_STRING_NODE:
5636 return parse_static_literal_string(iseq, scope_node, node, &((const pm_string_node_t *) node)->unescaped);
5637 case PM_SOURCE_FILE_NODE:
5638 return pm_source_file_value((const pm_source_file_node_t *) node, scope_node);
5639 case PM_ARRAY_NODE: {
5640 const pm_array_node_t *cast = (const pm_array_node_t *) node;
5641 VALUE result = rb_ary_new_capa(cast->elements.size);
5642
5643 for (size_t index = 0; index < cast->elements.size; index++) {
5644 VALUE element = pm_compile_shareable_constant_literal(iseq, cast->elements.nodes[index], scope_node);
5645 if (element == Qundef) return Qundef;
5646
5647 rb_ary_push(result, element);
5648 }
5649
5650 return rb_ractor_make_shareable(result);
5651 }
5652 case PM_HASH_NODE: {
5653 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
5654 VALUE result = rb_hash_new_capa(cast->elements.size);
5655
5656 for (size_t index = 0; index < cast->elements.size; index++) {
5657 const pm_node_t *element = cast->elements.nodes[index];
5658 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) return Qundef;
5659
5660 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
5661
5662 VALUE key = pm_compile_shareable_constant_literal(iseq, assoc->key, scope_node);
5663 if (key == Qundef) return Qundef;
5664
5665 VALUE value = pm_compile_shareable_constant_literal(iseq, assoc->value, scope_node);
5666 if (value == Qundef) return Qundef;
5667
5668 rb_hash_aset(result, key, value);
5669 }
5670
5671 return rb_ractor_make_shareable(result);
5672 }
5673 default:
5674 return Qundef;
5675 }
5676}
5677
5682static void
5683pm_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)
5684{
5685 VALUE literal = pm_compile_shareable_constant_literal(iseq, node, scope_node);
5686 if (literal != Qundef) {
5687 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
5688 PUSH_INSN1(ret, location, putobject, literal);
5689 return;
5690 }
5691
5692 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
5693 switch (PM_NODE_TYPE(node)) {
5694 case PM_ARRAY_NODE: {
5695 const pm_array_node_t *cast = (const pm_array_node_t *) node;
5696
5697 if (top) {
5698 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5699 }
5700
5701 for (size_t index = 0; index < cast->elements.size; index++) {
5702 pm_compile_shareable_constant_value(iseq, cast->elements.nodes[index], shareability, path, ret, scope_node, false);
5703 }
5704
5705 PUSH_INSN1(ret, location, newarray, INT2FIX(cast->elements.size));
5706
5707 if (top) {
5708 ID method_id = (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) ? rb_intern("make_shareable_copy") : rb_intern("make_shareable");
5709 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5710 }
5711
5712 return;
5713 }
5714 case PM_HASH_NODE: {
5715 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
5716
5717 if (top) {
5718 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5719 }
5720
5721 pm_compile_hash_elements(iseq, (const pm_node_t *) cast, &cast->elements, shareability, path, false, ret, scope_node);
5722
5723 if (top) {
5724 ID method_id = (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) ? rb_intern("make_shareable_copy") : rb_intern("make_shareable");
5725 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5726 }
5727
5728 return;
5729 }
5730 default: {
5731 DECL_ANCHOR(value_seq);
5732
5733 pm_compile_node(iseq, node, value_seq, false, scope_node);
5734 if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) {
5735 PUSH_SEND_WITH_FLAG(value_seq, location, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE));
5736 }
5737
5738 if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL) {
5739 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5740 PUSH_SEQ(ret, value_seq);
5741 PUSH_INSN1(ret, location, putobject, path);
5742 PUSH_SEND_WITH_FLAG(ret, location, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE));
5743 }
5744 else if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) {
5745 if (top) PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5746 PUSH_SEQ(ret, value_seq);
5747 if (top) PUSH_SEND_WITH_FLAG(ret, location, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5748 }
5749 else if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING) {
5750 if (top) PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5751 PUSH_SEQ(ret, value_seq);
5752 if (top) PUSH_SEND_WITH_FLAG(ret, location, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5753 }
5754
5755 break;
5756 }
5757 }
5758}
5759
5764static void
5765pm_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)
5766{
5767 const pm_node_location_t location = *node_location;
5768 ID name_id = pm_constant_id_lookup(scope_node, node->name);
5769
5770 if (shareability != 0) {
5771 pm_compile_shareable_constant_value(iseq, node->value, shareability, rb_id2str(name_id), ret, scope_node, true);
5772 }
5773 else {
5774 PM_COMPILE_NOT_POPPED(node->value);
5775 }
5776
5777 if (!popped) PUSH_INSN(ret, location, dup);
5778 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5779
5780 VALUE operand = ID2SYM(name_id);
5781 PUSH_INSN1(ret, location, setconstant, operand);
5782}
5783
5788static void
5789pm_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)
5790{
5791 const pm_node_location_t location = *node_location;
5792
5793 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5794 LABEL *end_label = NEW_LABEL(location.line);
5795
5796 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5797 if (!popped) PUSH_INSN(ret, location, dup);
5798
5799 PUSH_INSNL(ret, location, branchunless, end_label);
5800 if (!popped) PUSH_INSN(ret, location, pop);
5801
5802 if (shareability != 0) {
5803 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5804 }
5805 else {
5806 PM_COMPILE_NOT_POPPED(node->value);
5807 }
5808
5809 if (!popped) PUSH_INSN(ret, location, dup);
5810 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5811 PUSH_INSN1(ret, location, setconstant, name);
5812 PUSH_LABEL(ret, end_label);
5813}
5814
5819static void
5820pm_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)
5821{
5822 const pm_node_location_t location = *node_location;
5823 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5824
5825 LABEL *set_label = NEW_LABEL(location.line);
5826 LABEL *end_label = NEW_LABEL(location.line);
5827
5828 PUSH_INSN(ret, location, putnil);
5829 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), name, Qtrue);
5830 PUSH_INSNL(ret, location, branchunless, set_label);
5831
5832 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5833 if (!popped) PUSH_INSN(ret, location, dup);
5834
5835 PUSH_INSNL(ret, location, branchif, end_label);
5836 if (!popped) PUSH_INSN(ret, location, pop);
5837 PUSH_LABEL(ret, set_label);
5838
5839 if (shareability != 0) {
5840 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5841 }
5842 else {
5843 PM_COMPILE_NOT_POPPED(node->value);
5844 }
5845
5846 if (!popped) PUSH_INSN(ret, location, dup);
5847 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5848 PUSH_INSN1(ret, location, setconstant, name);
5849 PUSH_LABEL(ret, end_label);
5850}
5851
5856static void
5857pm_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)
5858{
5859 const pm_node_location_t location = *node_location;
5860
5861 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5862 ID method_id = pm_constant_id_lookup(scope_node, node->binary_operator);
5863
5864 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5865
5866 if (shareability != 0) {
5867 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5868 }
5869 else {
5870 PM_COMPILE_NOT_POPPED(node->value);
5871 }
5872
5873 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5874 if (!popped) PUSH_INSN(ret, location, dup);
5875
5876 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5877 PUSH_INSN1(ret, location, setconstant, name);
5878}
5879
5884static VALUE
5885pm_constant_path_path(const pm_constant_path_node_t *node, const pm_scope_node_t *scope_node)
5886{
5887 VALUE parts = rb_ary_new();
5888 rb_ary_push(parts, rb_id2str(pm_constant_id_lookup(scope_node, node->name)));
5889
5890 const pm_node_t *current = node->parent;
5891 while (current != NULL && PM_NODE_TYPE_P(current, PM_CONSTANT_PATH_NODE)) {
5892 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) current;
5893 rb_ary_unshift(parts, rb_id2str(pm_constant_id_lookup(scope_node, cast->name)));
5894 current = cast->parent;
5895 }
5896
5897 if (current == NULL) {
5898 rb_ary_unshift(parts, rb_id2str(idNULL));
5899 }
5900 else if (PM_NODE_TYPE_P(current, PM_CONSTANT_READ_NODE)) {
5901 rb_ary_unshift(parts, rb_id2str(pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) current)->name)));
5902 }
5903 else {
5904 rb_ary_unshift(parts, rb_str_new_cstr("..."));
5905 }
5906
5907 return rb_ary_join(parts, rb_str_new_cstr("::"));
5908}
5909
5914static void
5915pm_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)
5916{
5917 const pm_node_location_t location = *node_location;
5918 const pm_constant_path_node_t *target = node->target;
5919 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
5920
5921 if (target->parent) {
5922 PM_COMPILE_NOT_POPPED((const pm_node_t *) target->parent);
5923 }
5924 else {
5925 PUSH_INSN1(ret, location, putobject, rb_cObject);
5926 }
5927
5928 if (shareability != 0) {
5929 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
5930 }
5931 else {
5932 PM_COMPILE_NOT_POPPED(node->value);
5933 }
5934
5935 if (!popped) {
5936 PUSH_INSN(ret, location, swap);
5937 PUSH_INSN1(ret, location, topn, INT2FIX(1));
5938 }
5939
5940 PUSH_INSN(ret, location, swap);
5941 PUSH_INSN1(ret, location, setconstant, name);
5942}
5943
5948static void
5949pm_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)
5950{
5951 const pm_node_location_t location = *node_location;
5952 const pm_constant_path_node_t *target = node->target;
5953
5954 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
5955 LABEL *lfin = NEW_LABEL(location.line);
5956
5957 if (target->parent) {
5958 PM_COMPILE_NOT_POPPED(target->parent);
5959 }
5960 else {
5961 PUSH_INSN1(ret, location, putobject, rb_cObject);
5962 }
5963
5964 PUSH_INSN(ret, location, dup);
5965 PUSH_INSN1(ret, location, putobject, Qtrue);
5966 PUSH_INSN1(ret, location, getconstant, name);
5967
5968 if (!popped) PUSH_INSN(ret, location, dup);
5969 PUSH_INSNL(ret, location, branchunless, lfin);
5970
5971 if (!popped) PUSH_INSN(ret, location, pop);
5972
5973 if (shareability != 0) {
5974 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
5975 }
5976 else {
5977 PM_COMPILE_NOT_POPPED(node->value);
5978 }
5979
5980 if (popped) {
5981 PUSH_INSN1(ret, location, topn, INT2FIX(1));
5982 }
5983 else {
5984 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
5985 PUSH_INSN(ret, location, swap);
5986 }
5987
5988 PUSH_INSN1(ret, location, setconstant, name);
5989 PUSH_LABEL(ret, lfin);
5990
5991 if (!popped) PUSH_INSN(ret, location, swap);
5992 PUSH_INSN(ret, location, pop);
5993}
5994
5999static void
6000pm_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)
6001{
6002 const pm_node_location_t location = *node_location;
6003 const pm_constant_path_node_t *target = node->target;
6004
6005 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6006 LABEL *lassign = NEW_LABEL(location.line);
6007 LABEL *lfin = NEW_LABEL(location.line);
6008
6009 if (target->parent) {
6010 PM_COMPILE_NOT_POPPED(target->parent);
6011 }
6012 else {
6013 PUSH_INSN1(ret, location, putobject, rb_cObject);
6014 }
6015
6016 PUSH_INSN(ret, location, dup);
6017 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), name, Qtrue);
6018 PUSH_INSNL(ret, location, branchunless, lassign);
6019
6020 PUSH_INSN(ret, location, dup);
6021 PUSH_INSN1(ret, location, putobject, Qtrue);
6022 PUSH_INSN1(ret, location, getconstant, name);
6023
6024 if (!popped) PUSH_INSN(ret, location, dup);
6025 PUSH_INSNL(ret, location, branchif, lfin);
6026
6027 if (!popped) PUSH_INSN(ret, location, pop);
6028 PUSH_LABEL(ret, lassign);
6029
6030 if (shareability != 0) {
6031 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6032 }
6033 else {
6034 PM_COMPILE_NOT_POPPED(node->value);
6035 }
6036
6037 if (popped) {
6038 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6039 }
6040 else {
6041 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
6042 PUSH_INSN(ret, location, swap);
6043 }
6044
6045 PUSH_INSN1(ret, location, setconstant, name);
6046 PUSH_LABEL(ret, lfin);
6047
6048 if (!popped) PUSH_INSN(ret, location, swap);
6049 PUSH_INSN(ret, location, pop);
6050}
6051
6056static void
6057pm_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)
6058{
6059 const pm_node_location_t location = *node_location;
6060 const pm_constant_path_node_t *target = node->target;
6061
6062 ID method_id = pm_constant_id_lookup(scope_node, node->binary_operator);
6063 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6064
6065 if (target->parent) {
6066 PM_COMPILE_NOT_POPPED(target->parent);
6067 }
6068 else {
6069 PUSH_INSN1(ret, location, putobject, rb_cObject);
6070 }
6071
6072 PUSH_INSN(ret, location, dup);
6073 PUSH_INSN1(ret, location, putobject, Qtrue);
6074 PUSH_INSN1(ret, location, getconstant, name);
6075
6076 if (shareability != 0) {
6077 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6078 }
6079 else {
6080 PM_COMPILE_NOT_POPPED(node->value);
6081 }
6082
6083 PUSH_CALL(ret, location, method_id, INT2FIX(1));
6084 PUSH_INSN(ret, location, swap);
6085
6086 if (!popped) {
6087 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6088 PUSH_INSN(ret, location, swap);
6089 }
6090
6091 PUSH_INSN1(ret, location, setconstant, name);
6092}
6093
6100#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))
6101
6106static inline void
6107pm_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)
6108{
6109 const pm_node_location_t location = *node_location;
6110 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
6111
6112 pm_constant_id_list_t *locals = &scope_node->locals;
6113 pm_parameters_node_t *parameters_node = NULL;
6114 pm_node_list_t *keywords_list = NULL;
6115 pm_node_list_t *optionals_list = NULL;
6116 pm_node_list_t *posts_list = NULL;
6117 pm_node_list_t *requireds_list = NULL;
6118 pm_node_list_t *block_locals = NULL;
6119 bool trailing_comma = false;
6120
6121 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE) || PM_NODE_TYPE_P(scope_node->ast_node, PM_MODULE_NODE)) {
6122 PUSH_TRACE(ret, RUBY_EVENT_CLASS);
6123 }
6124
6125 if (scope_node->parameters != NULL) {
6126 switch (PM_NODE_TYPE(scope_node->parameters)) {
6127 case PM_BLOCK_PARAMETERS_NODE: {
6128 pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) scope_node->parameters;
6129 parameters_node = cast->parameters;
6130 block_locals = &cast->locals;
6131
6132 if (parameters_node) {
6133 if (parameters_node->rest && PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE)) {
6134 trailing_comma = true;
6135 }
6136 }
6137 break;
6138 }
6139 case PM_PARAMETERS_NODE: {
6140 parameters_node = (pm_parameters_node_t *) scope_node->parameters;
6141 break;
6142 }
6143 case PM_NUMBERED_PARAMETERS_NODE: {
6144 uint32_t maximum = ((const pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
6145 body->param.lead_num = maximum;
6146 body->param.flags.ambiguous_param0 = maximum == 1;
6147 break;
6148 }
6149 case PM_IT_PARAMETERS_NODE:
6150 body->param.lead_num = 1;
6151 body->param.flags.ambiguous_param0 = true;
6152 break;
6153 default:
6154 rb_bug("Unexpected node type for parameters: %s", pm_node_type_to_str(PM_NODE_TYPE(scope_node->parameters)));
6155 }
6156 }
6157
6158 struct rb_iseq_param_keyword *keyword = NULL;
6159
6160 if (parameters_node) {
6161 optionals_list = &parameters_node->optionals;
6162 requireds_list = &parameters_node->requireds;
6163 keywords_list = &parameters_node->keywords;
6164 posts_list = &parameters_node->posts;
6165 }
6166 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))) {
6167 body->param.opt_num = 0;
6168 }
6169 else {
6170 body->param.lead_num = 0;
6171 body->param.opt_num = 0;
6172 }
6173
6174 //********STEP 1**********
6175 // Goal: calculate the table size for the locals, accounting for
6176 // hidden variables and multi target nodes
6177 size_t locals_size = locals->size;
6178
6179 // Index lookup table buffer size is only the number of the locals
6180 st_table *index_lookup_table = st_init_numtable();
6181
6182 int table_size = (int) locals_size;
6183
6184 // For nodes have a hidden iteration variable. We add that to the local
6185 // table size here.
6186 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) table_size++;
6187
6188 if (keywords_list && keywords_list->size) {
6189 table_size++;
6190 }
6191
6192 if (requireds_list) {
6193 for (size_t i = 0; i < requireds_list->size; i++) {
6194 // For each MultiTargetNode, we're going to have one
6195 // additional anonymous local not represented in the locals table
6196 // We want to account for this in our table size
6197 pm_node_t *required = requireds_list->nodes[i];
6198 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
6199 table_size++;
6200 }
6201 else if (PM_NODE_TYPE_P(required, PM_REQUIRED_PARAMETER_NODE)) {
6202 if (PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6203 table_size++;
6204 }
6205 }
6206 }
6207 }
6208
6209 // If we have the `it` implicit local variable, we need to account for
6210 // it in the local table size.
6211 if (scope_node->parameters != NULL && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
6212 table_size++;
6213 }
6214
6215 // Ensure there is enough room in the local table for any
6216 // parameters that have been repeated
6217 // ex: def underscore_parameters(_, _ = 1, _ = 2); _; end
6218 // ^^^^^^^^^^^^
6219 if (optionals_list && optionals_list->size) {
6220 for (size_t i = 0; i < optionals_list->size; i++) {
6221 pm_node_t * node = optionals_list->nodes[i];
6222 if (PM_NODE_FLAG_P(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6223 table_size++;
6224 }
6225 }
6226 }
6227
6228 // If we have an anonymous "rest" node, we'll need to increase the local
6229 // table size to take it in to account.
6230 // def m(foo, *, bar)
6231 // ^
6232 if (parameters_node) {
6233 if (parameters_node->rest) {
6234 if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
6235 if (!((const pm_rest_parameter_node_t *) parameters_node->rest)->name || PM_NODE_FLAG_P(parameters_node->rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6236 table_size++;
6237 }
6238 }
6239 }
6240
6241 // def foo(_, **_); _; end
6242 // ^^^
6243 if (parameters_node->keyword_rest) {
6244 // def foo(...); end
6245 // ^^^
6246 // When we have a `...` as the keyword_rest, it's a forwarding_parameter_node and
6247 // we need to leave space for 4 locals: *, **, &, ...
6248 if (PM_NODE_TYPE_P(parameters_node->keyword_rest, PM_FORWARDING_PARAMETER_NODE)) {
6249 // Only optimize specifically methods like this: `foo(...)`
6250 if (requireds_list->size == 0 && optionals_list->size == 0 && keywords_list->size == 0) {
6251 ISEQ_BODY(iseq)->param.flags.use_block = TRUE;
6252 ISEQ_BODY(iseq)->param.flags.forwardable = TRUE;
6253 table_size += 1;
6254 }
6255 else {
6256 table_size += 4;
6257 }
6258 }
6259 else {
6260 const pm_keyword_rest_parameter_node_t *kw_rest = (const pm_keyword_rest_parameter_node_t *) parameters_node->keyword_rest;
6261
6262 // If it's anonymous or repeated, then we need to allocate stack space
6263 if (!kw_rest->name || PM_NODE_FLAG_P(kw_rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6264 table_size++;
6265 }
6266 }
6267 }
6268 }
6269
6270 if (posts_list) {
6271 for (size_t i = 0; i < posts_list->size; i++) {
6272 // For each MultiTargetNode, we're going to have one
6273 // additional anonymous local not represented in the locals table
6274 // We want to account for this in our table size
6275 pm_node_t *required = posts_list->nodes[i];
6276 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE) || PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6277 table_size++;
6278 }
6279 }
6280 }
6281
6282 if (keywords_list && keywords_list->size) {
6283 for (size_t i = 0; i < keywords_list->size; i++) {
6284 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6285 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6286 table_size++;
6287 }
6288 }
6289 }
6290
6291 if (parameters_node && parameters_node->block) {
6292 const pm_block_parameter_node_t *block_node = (const pm_block_parameter_node_t *) parameters_node->block;
6293
6294 if (PM_NODE_FLAG_P(block_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER) || !block_node->name) {
6295 table_size++;
6296 }
6297 }
6298
6299 // We can create local_table_for_iseq with the correct size
6300 VALUE idtmp = 0;
6301 rb_ast_id_table_t *local_table_for_iseq = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
6302 local_table_for_iseq->size = table_size;
6303
6304 //********END OF STEP 1**********
6305
6306 //********STEP 2**********
6307 // Goal: populate iv index table as well as local table, keeping the
6308 // layout of the local table consistent with the layout of the
6309 // stack when calling the method
6310 //
6311 // Do a first pass on all of the parameters, setting their values in
6312 // the local_table_for_iseq, _except_ for Multis who get a hidden
6313 // variable in this step, and will get their names inserted in step 3
6314
6315 // local_index is a cursor that keeps track of the current
6316 // index into local_table_for_iseq. The local table is actually a list,
6317 // and the order of that list must match the order of the items pushed
6318 // on the stack. We need to take in to account things pushed on the
6319 // stack that _might not have a name_ (for example array destructuring).
6320 // This index helps us know which item we're dealing with and also give
6321 // those anonymous items temporary names (as below)
6322 int local_index = 0;
6323
6324 // Here we figure out local table indices and insert them in to the
6325 // index lookup table and local tables.
6326 //
6327 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6328 // ^^^^^^^^^^^^^
6329 if (requireds_list && requireds_list->size) {
6330 for (size_t i = 0; i < requireds_list->size; i++, local_index++) {
6331 ID local;
6332
6333 // For each MultiTargetNode, we're going to have one additional
6334 // anonymous local not represented in the locals table. We want
6335 // to account for this in our table size.
6336 pm_node_t *required = requireds_list->nodes[i];
6337
6338 switch (PM_NODE_TYPE(required)) {
6339 case PM_MULTI_TARGET_NODE: {
6340 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6341 // ^^^^^^^^^^
6342 local = rb_make_temporary_id(local_index);
6343 local_table_for_iseq->ids[local_index] = local;
6344 break;
6345 }
6346 case PM_REQUIRED_PARAMETER_NODE: {
6347 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6348 // ^
6349 const pm_required_parameter_node_t *param = (const pm_required_parameter_node_t *) required;
6350
6351 if (PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6352 ID local = pm_constant_id_lookup(scope_node, param->name);
6353 local_table_for_iseq->ids[local_index] = local;
6354 }
6355 else {
6356 pm_insert_local_index(param->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6357 }
6358
6359 break;
6360 }
6361 default:
6362 rb_bug("Unsupported node in requireds in parameters %s", pm_node_type_to_str(PM_NODE_TYPE(required)));
6363 }
6364 }
6365
6366 body->param.lead_num = (int) requireds_list->size;
6367 body->param.flags.has_lead = true;
6368 }
6369
6370 if (scope_node->parameters != NULL && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
6371 ID local = rb_make_temporary_id(local_index);
6372 local_table_for_iseq->ids[local_index++] = local;
6373 }
6374
6375 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6376 // ^^^^^
6377 if (optionals_list && optionals_list->size) {
6378 body->param.opt_num = (int) optionals_list->size;
6379 body->param.flags.has_opt = true;
6380
6381 for (size_t i = 0; i < optionals_list->size; i++, local_index++) {
6382 pm_node_t * node = optionals_list->nodes[i];
6383 pm_constant_id_t name = ((const pm_optional_parameter_node_t *) node)->name;
6384
6385 if (PM_NODE_FLAG_P(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6386 ID local = pm_constant_id_lookup(scope_node, name);
6387 local_table_for_iseq->ids[local_index] = local;
6388 }
6389 else {
6390 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6391 }
6392 }
6393 }
6394
6395 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6396 // ^^
6397 if (parameters_node && parameters_node->rest) {
6398 body->param.rest_start = local_index;
6399
6400 // If there's a trailing comma, we'll have an implicit rest node,
6401 // and we don't want it to impact the rest variables on param
6402 if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
6403 body->param.flags.has_rest = true;
6404 RUBY_ASSERT(body->param.rest_start != -1);
6405
6406 pm_constant_id_t name = ((const pm_rest_parameter_node_t *) parameters_node->rest)->name;
6407
6408 if (name) {
6409 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6410 // ^^
6411 if (PM_NODE_FLAG_P(parameters_node->rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6412 ID local = pm_constant_id_lookup(scope_node, name);
6413 local_table_for_iseq->ids[local_index] = local;
6414 }
6415 else {
6416 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6417 }
6418 }
6419 else {
6420 // def foo(a, (b, *c, d), e = 1, *, g, (h, *i, j), k:, l: 1, **m, &n)
6421 // ^
6422 body->param.flags.anon_rest = true;
6423 pm_insert_local_special(idMULT, local_index, index_lookup_table, local_table_for_iseq);
6424 }
6425
6426 local_index++;
6427 }
6428 }
6429
6430 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6431 // ^^^^^^^^^^^^^
6432 if (posts_list && posts_list->size) {
6433 body->param.post_num = (int) posts_list->size;
6434 body->param.post_start = local_index;
6435 body->param.flags.has_post = true;
6436
6437 for (size_t i = 0; i < posts_list->size; i++, local_index++) {
6438 ID local;
6439
6440 // For each MultiTargetNode, we're going to have one additional
6441 // anonymous local not represented in the locals table. We want
6442 // to account for this in our table size.
6443 const pm_node_t *post_node = posts_list->nodes[i];
6444
6445 switch (PM_NODE_TYPE(post_node)) {
6446 case PM_MULTI_TARGET_NODE: {
6447 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6448 // ^^^^^^^^^^
6449 local = rb_make_temporary_id(local_index);
6450 local_table_for_iseq->ids[local_index] = local;
6451 break;
6452 }
6453 case PM_REQUIRED_PARAMETER_NODE: {
6454 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6455 // ^
6456 const pm_required_parameter_node_t *param = (const pm_required_parameter_node_t *) post_node;
6457
6458 if (PM_NODE_FLAG_P(param, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6459 ID local = pm_constant_id_lookup(scope_node, param->name);
6460 local_table_for_iseq->ids[local_index] = local;
6461 }
6462 else {
6463 pm_insert_local_index(param->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6464 }
6465 break;
6466 }
6467 default:
6468 rb_bug("Unsupported node in posts in parameters %s", pm_node_type_to_str(PM_NODE_TYPE(post_node)));
6469 }
6470 }
6471 }
6472
6473 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6474 // ^^^^^^^^
6475 // Keywords create an internal variable on the parse tree
6476 if (keywords_list && keywords_list->size) {
6477 keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6478 keyword->num = (int) keywords_list->size;
6479
6480 const VALUE default_values = rb_ary_hidden_new(1);
6481 const VALUE complex_mark = rb_str_tmp_new(0);
6482
6483 for (size_t i = 0; i < keywords_list->size; i++) {
6484 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6485 pm_constant_id_t name;
6486
6487 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6488 // ^^
6489 if (PM_NODE_TYPE_P(keyword_parameter_node, PM_REQUIRED_KEYWORD_PARAMETER_NODE)) {
6490 name = ((const pm_required_keyword_parameter_node_t *) keyword_parameter_node)->name;
6491 keyword->required_num++;
6492 ID local = pm_constant_id_lookup(scope_node, name);
6493
6494 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6495 local_table_for_iseq->ids[local_index] = local;
6496 }
6497 else {
6498 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6499 }
6500 local_index++;
6501 }
6502 }
6503
6504 for (size_t i = 0; i < keywords_list->size; i++) {
6505 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6506 pm_constant_id_t name;
6507
6508 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6509 // ^^^^
6510 if (PM_NODE_TYPE_P(keyword_parameter_node, PM_OPTIONAL_KEYWORD_PARAMETER_NODE)) {
6511 const pm_optional_keyword_parameter_node_t *cast = ((const pm_optional_keyword_parameter_node_t *) keyword_parameter_node);
6512
6513 pm_node_t *value = cast->value;
6514 name = cast->name;
6515
6516 if (PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL) && !PM_CONTAINER_P(value)) {
6517 rb_ary_push(default_values, pm_static_literal_value(iseq, value, scope_node));
6518 }
6519 else {
6520 rb_ary_push(default_values, complex_mark);
6521 }
6522
6523 ID local = pm_constant_id_lookup(scope_node, name);
6524 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6525 local_table_for_iseq->ids[local_index] = local;
6526 }
6527 else {
6528 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6529 }
6530 local_index++;
6531 }
6532
6533 }
6534
6535 if (RARRAY_LEN(default_values)) {
6536 VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
6537
6538 for (int i = 0; i < RARRAY_LEN(default_values); i++) {
6539 VALUE dv = RARRAY_AREF(default_values, i);
6540 if (dv == complex_mark) dv = Qundef;
6541 RB_OBJ_WRITE(iseq, &dvs[i], dv);
6542 }
6543
6544 keyword->default_values = dvs;
6545 }
6546
6547 // Hidden local for keyword arguments
6548 keyword->bits_start = local_index;
6549 ID local = rb_make_temporary_id(local_index);
6550 local_table_for_iseq->ids[local_index] = local;
6551 local_index++;
6552
6553 body->param.keyword = keyword;
6554 body->param.flags.has_kw = true;
6555 }
6556
6557 if (body->type == ISEQ_TYPE_BLOCK && local_index == 1 && requireds_list && requireds_list->size == 1 && !trailing_comma) {
6558 body->param.flags.ambiguous_param0 = true;
6559 }
6560
6561 if (parameters_node) {
6562 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6563 // ^^^
6564 if (parameters_node->keyword_rest) {
6565 switch (PM_NODE_TYPE(parameters_node->keyword_rest)) {
6566 case PM_NO_KEYWORDS_PARAMETER_NODE: {
6567 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **nil, &n)
6568 // ^^^^^
6569 body->param.flags.accepts_no_kwarg = true;
6570 break;
6571 }
6572 case PM_KEYWORD_REST_PARAMETER_NODE: {
6573 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6574 // ^^^
6575 const pm_keyword_rest_parameter_node_t *kw_rest_node = (const pm_keyword_rest_parameter_node_t *) parameters_node->keyword_rest;
6576 if (!body->param.flags.has_kw) {
6577 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6578 }
6579
6580 keyword->rest_start = local_index;
6581 body->param.flags.has_kwrest = true;
6582
6583 pm_constant_id_t constant_id = kw_rest_node->name;
6584 if (constant_id) {
6585 if (PM_NODE_FLAG_P(kw_rest_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6586 ID local = pm_constant_id_lookup(scope_node, constant_id);
6587 local_table_for_iseq->ids[local_index] = local;
6588 }
6589 else {
6590 pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6591 }
6592 }
6593 else {
6594 body->param.flags.anon_kwrest = true;
6595 pm_insert_local_special(idPow, local_index, index_lookup_table, local_table_for_iseq);
6596 }
6597
6598 local_index++;
6599 break;
6600 }
6601 case PM_FORWARDING_PARAMETER_NODE: {
6602 // def foo(...)
6603 // ^^^
6604 if (!ISEQ_BODY(iseq)->param.flags.forwardable) {
6605 // Add the anonymous *
6606 body->param.rest_start = local_index;
6607 body->param.flags.has_rest = true;
6608 body->param.flags.anon_rest = true;
6609 pm_insert_local_special(idMULT, local_index++, index_lookup_table, local_table_for_iseq);
6610
6611 // Add the anonymous **
6612 RUBY_ASSERT(!body->param.flags.has_kw);
6613 body->param.flags.has_kw = false;
6614 body->param.flags.has_kwrest = true;
6615 body->param.flags.anon_kwrest = true;
6616 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6617 keyword->rest_start = local_index;
6618 pm_insert_local_special(idPow, local_index++, index_lookup_table, local_table_for_iseq);
6619
6620 // Add the anonymous &
6621 body->param.block_start = local_index;
6622 body->param.flags.has_block = true;
6623 pm_insert_local_special(idAnd, local_index++, index_lookup_table, local_table_for_iseq);
6624 }
6625
6626 // Add the ...
6627 pm_insert_local_special(idDot3, local_index++, index_lookup_table, local_table_for_iseq);
6628 break;
6629 }
6630 default:
6631 rb_bug("node type %s not expected as keyword_rest", pm_node_type_to_str(PM_NODE_TYPE(parameters_node->keyword_rest)));
6632 }
6633 }
6634
6635 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6636 // ^^
6637 if (parameters_node->block) {
6638 body->param.block_start = local_index;
6639 body->param.flags.has_block = true;
6640 iseq_set_use_block(iseq);
6641
6642 pm_constant_id_t name = ((const pm_block_parameter_node_t *) parameters_node->block)->name;
6643
6644 if (name) {
6645 if (PM_NODE_FLAG_P(parameters_node->block, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6646 ID local = pm_constant_id_lookup(scope_node, name);
6647 local_table_for_iseq->ids[local_index] = local;
6648 }
6649 else {
6650 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6651 }
6652 }
6653 else {
6654 pm_insert_local_special(idAnd, local_index, index_lookup_table, local_table_for_iseq);
6655 }
6656
6657 local_index++;
6658 }
6659 }
6660
6661 //********END OF STEP 2**********
6662 // The local table is now consistent with expected
6663 // stack layout
6664
6665 // If there's only one required element in the parameters
6666 // CRuby needs to recognize it as an ambiguous parameter
6667
6668 //********STEP 3**********
6669 // Goal: fill in the names of the parameters in MultiTargetNodes
6670 //
6671 // Go through requireds again to set the multis
6672
6673 if (requireds_list && requireds_list->size) {
6674 for (size_t i = 0; i < requireds_list->size; i++) {
6675 // For each MultiTargetNode, we're going to have one
6676 // additional anonymous local not represented in the locals table
6677 // We want to account for this in our table size
6678 const pm_node_t *required = requireds_list->nodes[i];
6679
6680 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
6681 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);
6682 }
6683 }
6684 }
6685
6686 // Go through posts again to set the multis
6687 if (posts_list && posts_list->size) {
6688 for (size_t i = 0; i < posts_list->size; i++) {
6689 // For each MultiTargetNode, we're going to have one
6690 // additional anonymous local not represented in the locals table
6691 // We want to account for this in our table size
6692 const pm_node_t *post = posts_list->nodes[i];
6693
6694 if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
6695 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);
6696 }
6697 }
6698 }
6699
6700 // Set any anonymous locals for the for node
6701 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
6702 if (PM_NODE_TYPE_P(((const pm_for_node_t *) scope_node->ast_node)->index, PM_LOCAL_VARIABLE_TARGET_NODE)) {
6703 body->param.lead_num++;
6704 }
6705 else {
6706 body->param.rest_start = local_index;
6707 body->param.flags.has_rest = true;
6708 }
6709
6710 ID local = rb_make_temporary_id(local_index);
6711 local_table_for_iseq->ids[local_index] = local;
6712 local_index++;
6713 }
6714
6715 // Fill in any NumberedParameters, if they exist
6716 if (scope_node->parameters && PM_NODE_TYPE_P(scope_node->parameters, PM_NUMBERED_PARAMETERS_NODE)) {
6717 int maximum = ((const pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
6718 RUBY_ASSERT(0 < maximum && maximum <= 9);
6719 for (int i = 0; i < maximum; i++, local_index++) {
6720 const uint8_t param_name[] = { '_', '1' + i };
6721 pm_constant_id_t constant_id = pm_constant_pool_find(&scope_node->parser->constant_pool, param_name, 2);
6722 RUBY_ASSERT(constant_id && "parser should fill in any gaps in numbered parameters");
6723 pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6724 }
6725 body->param.lead_num = maximum;
6726 body->param.flags.has_lead = true;
6727 }
6728
6729 //********END OF STEP 3**********
6730
6731 //********STEP 4**********
6732 // Goal: fill in the method body locals
6733 // To be explicit, these are the non-parameter locals
6734 // We fill in the block_locals, if they exist
6735 // lambda { |x; y| y }
6736 // ^
6737 if (block_locals && block_locals->size) {
6738 for (size_t i = 0; i < block_locals->size; i++, local_index++) {
6739 pm_constant_id_t constant_id = ((const pm_block_local_variable_node_t *) block_locals->nodes[i])->name;
6740 pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6741 }
6742 }
6743
6744 // Fill in any locals we missed
6745 if (scope_node->locals.size) {
6746 for (size_t i = 0; i < scope_node->locals.size; i++) {
6747 pm_constant_id_t constant_id = locals->ids[i];
6748 if (constant_id) {
6749 struct pm_local_table_insert_ctx ctx;
6750 ctx.scope_node = scope_node;
6751 ctx.local_table_for_iseq = local_table_for_iseq;
6752 ctx.local_index = local_index;
6753
6754 st_update(index_lookup_table, (st_data_t)constant_id, pm_local_table_insert_func, (st_data_t)&ctx);
6755
6756 local_index = ctx.local_index;
6757 }
6758 }
6759 }
6760
6761 //********END OF STEP 4**********
6762
6763 // We set the index_lookup_table on the scope node so we can
6764 // refer to the parameters correctly
6765 if (scope_node->index_lookup_table) {
6766 st_free_table(scope_node->index_lookup_table);
6767 }
6768 scope_node->index_lookup_table = index_lookup_table;
6769 iseq_calc_param_size(iseq);
6770
6771 if (ISEQ_BODY(iseq)->param.flags.forwardable) {
6772 // We're treating `...` as a parameter so that frame
6773 // pushing won't clobber it.
6774 ISEQ_BODY(iseq)->param.size += 1;
6775 }
6776
6777 // FIXME: args?
6778 iseq_set_local_table(iseq, local_table_for_iseq, 0);
6779 scope_node->local_table_for_iseq_size = local_table_for_iseq->size;
6780
6781 if (keyword != NULL) {
6782 size_t keyword_start_index = keyword->bits_start - keyword->num;
6783 keyword->table = (ID *)&ISEQ_BODY(iseq)->local_table[keyword_start_index];
6784 }
6785
6786 //********STEP 5************
6787 // Goal: compile anything that needed to be compiled
6788 if (optionals_list && optionals_list->size) {
6789 LABEL **opt_table = (LABEL **) ALLOC_N(VALUE, optionals_list->size + 1);
6790 LABEL *label;
6791
6792 // TODO: Should we make an api for NEW_LABEL where you can pass
6793 // a pointer to the label it should fill out? We already
6794 // have a list of labels allocated above so it seems wasteful
6795 // to do the copies.
6796 for (size_t i = 0; i < optionals_list->size; i++) {
6797 label = NEW_LABEL(location.line);
6798 opt_table[i] = label;
6799 PUSH_LABEL(ret, label);
6800 pm_node_t *optional_node = optionals_list->nodes[i];
6801 PM_COMPILE_NOT_POPPED(optional_node);
6802 }
6803
6804 // Set the last label
6805 label = NEW_LABEL(location.line);
6806 opt_table[optionals_list->size] = label;
6807 PUSH_LABEL(ret, label);
6808
6809 body->param.opt_table = (const VALUE *) opt_table;
6810 }
6811
6812 if (keywords_list && keywords_list->size) {
6813 size_t optional_index = 0;
6814 for (size_t i = 0; i < keywords_list->size; i++) {
6815 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6816 pm_constant_id_t name;
6817
6818 switch (PM_NODE_TYPE(keyword_parameter_node)) {
6819 case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: {
6820 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6821 // ^^^^
6822 const pm_optional_keyword_parameter_node_t *cast = ((const pm_optional_keyword_parameter_node_t *) keyword_parameter_node);
6823
6824 pm_node_t *value = cast->value;
6825 name = cast->name;
6826
6827 if (!PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL) || PM_CONTAINER_P(value)) {
6828 LABEL *end_label = NEW_LABEL(location.line);
6829
6830 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, name, 0);
6831 int kw_bits_idx = table_size - body->param.keyword->bits_start;
6832 PUSH_INSN2(ret, location, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(optional_index));
6833 PUSH_INSNL(ret, location, branchif, end_label);
6834 PM_COMPILE(value);
6835 PUSH_SETLOCAL(ret, location, index.index, index.level);
6836 PUSH_LABEL(ret, end_label);
6837 }
6838 optional_index++;
6839 break;
6840 }
6841 case PM_REQUIRED_KEYWORD_PARAMETER_NODE:
6842 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6843 // ^^
6844 break;
6845 default:
6846 rb_bug("Unexpected keyword parameter node type %s", pm_node_type_to_str(PM_NODE_TYPE(keyword_parameter_node)));
6847 }
6848 }
6849 }
6850
6851 if (requireds_list && requireds_list->size) {
6852 for (size_t i = 0; i < requireds_list->size; i++) {
6853 // For each MultiTargetNode, we're going to have one additional
6854 // anonymous local not represented in the locals table. We want
6855 // to account for this in our table size.
6856 const pm_node_t *required = requireds_list->nodes[i];
6857
6858 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
6859 PUSH_GETLOCAL(ret, location, table_size - (int)i, 0);
6860 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) required, ret, scope_node);
6861 }
6862 }
6863 }
6864
6865 if (posts_list && posts_list->size) {
6866 for (size_t i = 0; i < posts_list->size; i++) {
6867 // For each MultiTargetNode, we're going to have one additional
6868 // anonymous local not represented in the locals table. We want
6869 // to account for this in our table size.
6870 const pm_node_t *post = posts_list->nodes[i];
6871
6872 if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
6873 PUSH_GETLOCAL(ret, location, table_size - body->param.post_start - (int) i, 0);
6874 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) post, ret, scope_node);
6875 }
6876 }
6877 }
6878
6879 switch (body->type) {
6880 case ISEQ_TYPE_PLAIN: {
6881 RUBY_ASSERT(PM_NODE_TYPE_P(scope_node->ast_node, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE));
6882
6883 const pm_interpolated_regular_expression_node_t *cast = (const pm_interpolated_regular_expression_node_t *) scope_node->ast_node;
6884 pm_compile_regexp_dynamic(iseq, (const pm_node_t *) cast, &cast->parts, &location, ret, popped, scope_node);
6885
6886 break;
6887 }
6888 case ISEQ_TYPE_BLOCK: {
6889 LABEL *start = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(0);
6890 LABEL *end = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(0);
6891 const pm_node_location_t block_location = { .line = body->location.first_lineno, .node_id = scope_node->ast_node->node_id };
6892
6893 start->rescued = LABEL_RESCUE_BEG;
6894 end->rescued = LABEL_RESCUE_END;
6895
6896 // For nodes automatically assign the iteration variable to whatever
6897 // index variable. We need to handle that write here because it has
6898 // to happen in the context of the block. Note that this happens
6899 // before the B_CALL tracepoint event.
6900 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
6901 pm_compile_for_node_index(iseq, ((const pm_for_node_t *) scope_node->ast_node)->index, ret, scope_node);
6902 }
6903
6904 PUSH_TRACE(ret, RUBY_EVENT_B_CALL);
6905 PUSH_INSN(ret, block_location, nop);
6906 PUSH_LABEL(ret, start);
6907
6908 if (scope_node->body != NULL) {
6909 switch (PM_NODE_TYPE(scope_node->ast_node)) {
6910 case PM_POST_EXECUTION_NODE: {
6911 const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) scope_node->ast_node;
6912 PUSH_INSN1(ret, block_location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6913
6914 // We create another ScopeNode from the statements within the PostExecutionNode
6915 pm_scope_node_t next_scope_node;
6916 pm_scope_node_init((const pm_node_t *) cast->statements, &next_scope_node, scope_node);
6917
6918 const rb_iseq_t *block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(body->parent_iseq), ISEQ_TYPE_BLOCK, location.line);
6919 pm_scope_node_destroy(&next_scope_node);
6920
6921 PUSH_CALL_WITH_BLOCK(ret, block_location, id_core_set_postexe, INT2FIX(0), block);
6922 break;
6923 }
6924 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
6925 const pm_interpolated_regular_expression_node_t *cast = (const pm_interpolated_regular_expression_node_t *) scope_node->ast_node;
6926 pm_compile_regexp_dynamic(iseq, (const pm_node_t *) cast, &cast->parts, &location, ret, popped, scope_node);
6927 break;
6928 }
6929 default:
6930 pm_compile_node(iseq, scope_node->body, ret, popped, scope_node);
6931 break;
6932 }
6933 }
6934 else {
6935 PUSH_INSN(ret, block_location, putnil);
6936 }
6937
6938 PUSH_LABEL(ret, end);
6939 PUSH_TRACE(ret, RUBY_EVENT_B_RETURN);
6940 ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
6941
6942 /* wide range catch handler must put at last */
6943 PUSH_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, NULL, start);
6944 PUSH_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, NULL, end);
6945 break;
6946 }
6947 case ISEQ_TYPE_ENSURE: {
6948 const pm_node_location_t statements_location = (scope_node->body != NULL ? PM_NODE_START_LOCATION(scope_node->parser, scope_node->body) : location);
6949 iseq_set_exception_local_table(iseq);
6950
6951 if (scope_node->body != NULL) {
6952 PM_COMPILE_POPPED((const pm_node_t *) scope_node->body);
6953 }
6954
6955 PUSH_GETLOCAL(ret, statements_location, 1, 0);
6956 PUSH_INSN1(ret, statements_location, throw, INT2FIX(0));
6957 return;
6958 }
6959 case ISEQ_TYPE_METHOD: {
6960 ISEQ_COMPILE_DATA(iseq)->root_node = (const void *) scope_node->body;
6961 PUSH_TRACE(ret, RUBY_EVENT_CALL);
6962
6963 if (scope_node->body) {
6964 PM_COMPILE((const pm_node_t *) scope_node->body);
6965 }
6966 else {
6967 PUSH_INSN(ret, location, putnil);
6968 }
6969
6970 ISEQ_COMPILE_DATA(iseq)->root_node = (const void *) scope_node->body;
6971 PUSH_TRACE(ret, RUBY_EVENT_RETURN);
6972
6973 ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
6974 break;
6975 }
6976 case ISEQ_TYPE_RESCUE: {
6977 iseq_set_exception_local_table(iseq);
6978 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_RESCUE_MODIFIER_NODE)) {
6979 LABEL *lab = NEW_LABEL(location.line);
6980 LABEL *rescue_end = NEW_LABEL(location.line);
6981 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
6982 PUSH_INSN1(ret, location, putobject, rb_eStandardError);
6983 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
6984 PUSH_INSNL(ret, location, branchif, lab);
6985 PUSH_INSNL(ret, location, jump, rescue_end);
6986 PUSH_LABEL(ret, lab);
6987 PUSH_TRACE(ret, RUBY_EVENT_RESCUE);
6988 PM_COMPILE((const pm_node_t *) scope_node->body);
6989 PUSH_INSN(ret, location, leave);
6990 PUSH_LABEL(ret, rescue_end);
6991 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
6992 }
6993 else {
6994 PM_COMPILE((const pm_node_t *) scope_node->ast_node);
6995 }
6996 PUSH_INSN1(ret, location, throw, INT2FIX(0));
6997
6998 return;
6999 }
7000 default:
7001 if (scope_node->body) {
7002 PM_COMPILE((const pm_node_t *) scope_node->body);
7003 }
7004 else {
7005 PUSH_INSN(ret, location, putnil);
7006 }
7007 break;
7008 }
7009
7010 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE) || PM_NODE_TYPE_P(scope_node->ast_node, PM_MODULE_NODE)) {
7011 const pm_node_location_t end_location = PM_NODE_END_LOCATION(scope_node->parser, scope_node->ast_node);
7012 PUSH_TRACE(ret, RUBY_EVENT_END);
7013 ISEQ_COMPILE_DATA(iseq)->last_line = end_location.line;
7014 }
7015
7016 if (!PM_NODE_TYPE_P(scope_node->ast_node, PM_ENSURE_NODE)) {
7017 const pm_node_location_t location = { .line = ISEQ_COMPILE_DATA(iseq)->last_line, .node_id = scope_node->ast_node->node_id };
7018 PUSH_INSN(ret, location, leave);
7019 }
7020}
7021
7022static inline void
7023pm_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)
7024{
7025 // alias $foo $bar
7026 // ^^^^^^^^^^^^^^^
7027 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7028
7029 {
7030 const pm_location_t *name_loc = &node->new_name->location;
7031 VALUE operand = ID2SYM(rb_intern3((const char *) name_loc->start, name_loc->end - name_loc->start, scope_node->encoding));
7032 PUSH_INSN1(ret, *location, putobject, operand);
7033 }
7034
7035 {
7036 const pm_location_t *name_loc = &node->old_name->location;
7037 VALUE operand = ID2SYM(rb_intern3((const char *) name_loc->start, name_loc->end - name_loc->start, scope_node->encoding));
7038 PUSH_INSN1(ret, *location, putobject, operand);
7039 }
7040
7041 PUSH_SEND(ret, *location, id_core_set_variable_alias, INT2FIX(2));
7042 if (popped) PUSH_INSN(ret, *location, pop);
7043}
7044
7045static inline void
7046pm_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)
7047{
7048 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7049 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
7050 PM_COMPILE_NOT_POPPED(node->new_name);
7051 PM_COMPILE_NOT_POPPED(node->old_name);
7052
7053 PUSH_SEND(ret, *location, id_core_set_method_alias, INT2FIX(3));
7054 if (popped) PUSH_INSN(ret, *location, pop);
7055}
7056
7057static inline void
7058pm_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)
7059{
7060 LABEL *end_label = NEW_LABEL(location->line);
7061
7062 PM_COMPILE_NOT_POPPED(node->left);
7063 if (!popped) PUSH_INSN(ret, *location, dup);
7064 PUSH_INSNL(ret, *location, branchunless, end_label);
7065
7066 if (!popped) PUSH_INSN(ret, *location, pop);
7067 PM_COMPILE(node->right);
7068 PUSH_LABEL(ret, end_label);
7069}
7070
7071static inline void
7072pm_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)
7073{
7074 // If every node in the array is static, then we can compile the entire
7075 // array now instead of later.
7076 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
7077 // We're only going to compile this node if it's not popped. If it
7078 // is popped, then we know we don't need to do anything since it's
7079 // statically known.
7080 if (!popped) {
7081 if (elements->size) {
7082 VALUE value = pm_static_literal_value(iseq, node, scope_node);
7083 PUSH_INSN1(ret, *location, duparray, value);
7084 }
7085 else {
7086 PUSH_INSN1(ret, *location, newarray, INT2FIX(0));
7087 }
7088 }
7089 return;
7090 }
7091
7092 // Here since we know there are possible side-effects inside the
7093 // array contents, we're going to build it entirely at runtime.
7094 // We'll do this by pushing all of the elements onto the stack and
7095 // then combining them with newarray.
7096 //
7097 // If this array is popped, then this serves only to ensure we enact
7098 // all side-effects (like method calls) that are contained within
7099 // the array contents.
7100 //
7101 // We treat all sequences of non-splat elements as their
7102 // own arrays, followed by a newarray, and then continually
7103 // concat the arrays with the SplatNode nodes.
7104 const int max_new_array_size = 0x100;
7105 const unsigned int min_tmp_array_size = 0x40;
7106
7107 int new_array_size = 0;
7108 bool first_chunk = true;
7109
7110 // This is an optimization wherein we keep track of whether or not
7111 // the previous element was a static literal. If it was, then we do
7112 // not attempt to check if we have a subarray that can be optimized.
7113 // If it was not, then we do check.
7114 bool static_literal = false;
7115
7116 // Either create a new array, or push to the existing array.
7117#define FLUSH_CHUNK \
7118 if (new_array_size) { \
7119 if (first_chunk) PUSH_INSN1(ret, *location, newarray, INT2FIX(new_array_size)); \
7120 else PUSH_INSN1(ret, *location, pushtoarray, INT2FIX(new_array_size)); \
7121 first_chunk = false; \
7122 new_array_size = 0; \
7123 }
7124
7125 for (size_t index = 0; index < elements->size; index++) {
7126 const pm_node_t *element = elements->nodes[index];
7127
7128 if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) {
7129 FLUSH_CHUNK;
7130
7131 const pm_splat_node_t *splat_element = (const pm_splat_node_t *) element;
7132 if (splat_element->expression) {
7133 PM_COMPILE_NOT_POPPED(splat_element->expression);
7134 }
7135 else {
7136 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
7137 PUSH_GETLOCAL(ret, *location, index.index, index.level);
7138 }
7139
7140 if (first_chunk) {
7141 // If this is the first element of the array then we
7142 // need to splatarray the elements into the list.
7143 PUSH_INSN1(ret, *location, splatarray, Qtrue);
7144 first_chunk = false;
7145 }
7146 else {
7147 PUSH_INSN(ret, *location, concattoarray);
7148 }
7149
7150 static_literal = false;
7151 }
7152 else if (PM_NODE_TYPE_P(element, PM_KEYWORD_HASH_NODE)) {
7153 if (new_array_size == 0 && first_chunk) {
7154 PUSH_INSN1(ret, *location, newarray, INT2FIX(0));
7155 first_chunk = false;
7156 }
7157 else {
7158 FLUSH_CHUNK;
7159 }
7160
7161 // If we get here, then this is the last element of the
7162 // array/arguments, because it cannot be followed by
7163 // anything else without a syntax error. This looks like:
7164 //
7165 // [foo, bar, baz: qux]
7166 // ^^^^^^^^
7167 //
7168 // [foo, bar, **baz]
7169 // ^^^^^
7170 //
7171 const pm_keyword_hash_node_t *keyword_hash = (const pm_keyword_hash_node_t *) element;
7172 pm_compile_hash_elements(iseq, element, &keyword_hash->elements, 0, Qundef, false, ret, scope_node);
7173
7174 // This boolean controls the manner in which we push the
7175 // hash onto the array. If it's all keyword splats, then we
7176 // can use the very specialized pushtoarraykwsplat
7177 // instruction to check if it's empty before we push it.
7178 size_t splats = 0;
7179 while (splats < keyword_hash->elements.size && PM_NODE_TYPE_P(keyword_hash->elements.nodes[splats], PM_ASSOC_SPLAT_NODE)) splats++;
7180
7181 if (keyword_hash->elements.size == splats) {
7182 PUSH_INSN(ret, *location, pushtoarraykwsplat);
7183 }
7184 else {
7185 new_array_size++;
7186 }
7187 }
7188 else if (
7189 PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL) &&
7190 !PM_CONTAINER_P(element) &&
7191 !static_literal &&
7192 ((index + min_tmp_array_size) < elements->size)
7193 ) {
7194 // If we have a static literal, then there's the potential
7195 // to group a bunch of them together with a literal array
7196 // and then concat them together.
7197 size_t right_index = index + 1;
7198 while (
7199 right_index < elements->size &&
7200 PM_NODE_FLAG_P(elements->nodes[right_index], PM_NODE_FLAG_STATIC_LITERAL) &&
7201 !PM_CONTAINER_P(elements->nodes[right_index])
7202 ) right_index++;
7203
7204 size_t tmp_array_size = right_index - index;
7205 if (tmp_array_size >= min_tmp_array_size) {
7206 VALUE tmp_array = rb_ary_hidden_new(tmp_array_size);
7207
7208 // Create the temporary array.
7209 for (; tmp_array_size; tmp_array_size--)
7210 rb_ary_push(tmp_array, pm_static_literal_value(iseq, elements->nodes[index++], scope_node));
7211
7212 index--; // about to be incremented by for loop
7213 OBJ_FREEZE(tmp_array);
7214
7215 // Emit the optimized code.
7216 FLUSH_CHUNK;
7217 if (first_chunk) {
7218 PUSH_INSN1(ret, *location, duparray, tmp_array);
7219 first_chunk = false;
7220 }
7221 else {
7222 PUSH_INSN1(ret, *location, putobject, tmp_array);
7223 PUSH_INSN(ret, *location, concattoarray);
7224 }
7225 }
7226 else {
7227 PM_COMPILE_NOT_POPPED(element);
7228 if (++new_array_size >= max_new_array_size) FLUSH_CHUNK;
7229 static_literal = true;
7230 }
7231 } else {
7232 PM_COMPILE_NOT_POPPED(element);
7233 if (++new_array_size >= max_new_array_size) FLUSH_CHUNK;
7234 static_literal = false;
7235 }
7236 }
7237
7238 FLUSH_CHUNK;
7239 if (popped) PUSH_INSN(ret, *location, pop);
7240
7241#undef FLUSH_CHUNK
7242}
7243
7244static inline void
7245pm_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)
7246{
7247 unsigned long throw_flag = 0;
7248
7249 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
7250 /* while/until */
7251 LABEL *splabel = NEW_LABEL(0);
7252 PUSH_LABEL(ret, splabel);
7253 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
7254
7255 if (node->arguments != NULL) {
7256 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
7257 }
7258 else {
7259 PUSH_INSN(ret, *location, putnil);
7260 }
7261
7262 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
7263 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
7264 PUSH_ADJUST_RESTORE(ret, splabel);
7265 if (!popped) PUSH_INSN(ret, *location, putnil);
7266 }
7267 else {
7268 const rb_iseq_t *ip = iseq;
7269
7270 while (ip) {
7271 if (!ISEQ_COMPILE_DATA(ip)) {
7272 ip = 0;
7273 break;
7274 }
7275
7276 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
7277 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
7278 }
7279 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
7280 throw_flag = 0;
7281 }
7282 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
7283 COMPILE_ERROR(iseq, location->line, "Invalid break");
7284 return;
7285 }
7286 else {
7287 ip = ISEQ_BODY(ip)->parent_iseq;
7288 continue;
7289 }
7290
7291 /* escape from block */
7292 if (node->arguments != NULL) {
7293 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
7294 }
7295 else {
7296 PUSH_INSN(ret, *location, putnil);
7297 }
7298
7299 PUSH_INSN1(ret, *location, throw, INT2FIX(throw_flag | TAG_BREAK));
7300 if (popped) PUSH_INSN(ret, *location, pop);
7301
7302 return;
7303 }
7304
7305 COMPILE_ERROR(iseq, location->line, "Invalid break");
7306 }
7307}
7308
7309static inline void
7310pm_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)
7311{
7312 ID method_id = pm_constant_id_lookup(scope_node, node->name);
7313
7314 const pm_location_t *message_loc = &node->message_loc;
7315 if (message_loc->start == NULL) message_loc = &node->base.location;
7316
7317 const pm_node_location_t location = PM_LOCATION_START_LOCATION(scope_node->parser, message_loc, node->base.node_id);
7318 const char *builtin_func;
7319
7320 if (UNLIKELY(iseq_has_builtin_function_table(iseq)) && (builtin_func = pm_iseq_builtin_function_name(scope_node, node->receiver, method_id)) != NULL) {
7321 pm_compile_builtin_function_call(iseq, ret, scope_node, node, &location, popped, ISEQ_COMPILE_DATA(iseq)->current_block, builtin_func);
7322 return;
7323 }
7324
7325 LABEL *start = NEW_LABEL(location.line);
7326 if (node->block) PUSH_LABEL(ret, start);
7327
7328 switch (method_id) {
7329 case idUMinus: {
7330 if (pm_opt_str_freeze_p(iseq, node)) {
7331 VALUE value = parse_static_literal_string(iseq, scope_node, node->receiver, &((const pm_string_node_t * ) node->receiver)->unescaped);
7332 const struct rb_callinfo *callinfo = new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE);
7333 PUSH_INSN2(ret, location, opt_str_uminus, value, callinfo);
7334 if (popped) PUSH_INSN(ret, location, pop);
7335 return;
7336 }
7337 break;
7338 }
7339 case idFreeze: {
7340 if (pm_opt_str_freeze_p(iseq, node)) {
7341 VALUE value = parse_static_literal_string(iseq, scope_node, node->receiver, &((const pm_string_node_t * ) node->receiver)->unescaped);
7342 const struct rb_callinfo *callinfo = new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE);
7343 PUSH_INSN2(ret, location, opt_str_freeze, value, callinfo);
7344 if (popped) PUSH_INSN(ret, location, pop);
7345 return;
7346 }
7347 break;
7348 }
7349 case idAREF: {
7350 if (pm_opt_aref_with_p(iseq, node)) {
7351 const pm_string_node_t *string = (const pm_string_node_t *) ((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0];
7352 VALUE value = parse_static_literal_string(iseq, scope_node, (const pm_node_t *) string, &string->unescaped);
7353
7354 PM_COMPILE_NOT_POPPED(node->receiver);
7355
7356 const struct rb_callinfo *callinfo = new_callinfo(iseq, idAREF, 1, 0, NULL, FALSE);
7357 PUSH_INSN2(ret, location, opt_aref_with, value, callinfo);
7358
7359 if (popped) {
7360 PUSH_INSN(ret, location, pop);
7361 }
7362
7363 return;
7364 }
7365 break;
7366 }
7367 case idASET: {
7368 if (pm_opt_aset_with_p(iseq, node)) {
7369 const pm_string_node_t *string = (const pm_string_node_t *) ((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0];
7370 VALUE value = parse_static_literal_string(iseq, scope_node, (const pm_node_t *) string, &string->unescaped);
7371
7372 PM_COMPILE_NOT_POPPED(node->receiver);
7373 PM_COMPILE_NOT_POPPED(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[1]);
7374
7375 if (!popped) {
7376 PUSH_INSN(ret, location, swap);
7377 PUSH_INSN1(ret, location, topn, INT2FIX(1));
7378 }
7379
7380 const struct rb_callinfo *callinfo = new_callinfo(iseq, idASET, 2, 0, NULL, FALSE);
7381 PUSH_INSN2(ret, location, opt_aset_with, value, callinfo);
7382 PUSH_INSN(ret, location, pop);
7383 return;
7384 }
7385 break;
7386 }
7387 }
7388
7389 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && !popped) {
7390 PUSH_INSN(ret, location, putnil);
7391 }
7392
7393 if (node->receiver == NULL) {
7394 PUSH_INSN(ret, location, putself);
7395 }
7396 else {
7397 if (method_id == idCall && PM_NODE_TYPE_P(node->receiver, PM_LOCAL_VARIABLE_READ_NODE)) {
7398 const pm_local_variable_read_node_t *read_node_cast = (const pm_local_variable_read_node_t *) node->receiver;
7399 uint32_t node_id = node->receiver->node_id;
7400 int idx, level;
7401
7402 if (iseq_block_param_id_p(iseq, pm_constant_id_lookup(scope_node, read_node_cast->name), &idx, &level)) {
7403 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)));
7404 }
7405 else {
7406 PM_COMPILE_NOT_POPPED(node->receiver);
7407 }
7408 }
7409 else {
7410 PM_COMPILE_NOT_POPPED(node->receiver);
7411 }
7412 }
7413
7414 pm_compile_call(iseq, node, ret, popped, scope_node, method_id, start);
7415 return;
7416}
7417
7418static inline void
7419pm_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)
7420{
7421 int flag = 0;
7422
7423 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) {
7424 flag = VM_CALL_FCALL;
7425 }
7426
7427 PM_COMPILE_NOT_POPPED(node->receiver);
7428
7429 LABEL *safe_label = NULL;
7430 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
7431 safe_label = NEW_LABEL(location->line);
7432 PUSH_INSN(ret, *location, dup);
7433 PUSH_INSNL(ret, *location, branchnil, safe_label);
7434 }
7435
7436 PUSH_INSN(ret, *location, dup);
7437
7438 ID id_read_name = pm_constant_id_lookup(scope_node, node->read_name);
7439 PUSH_SEND_WITH_FLAG(ret, *location, id_read_name, INT2FIX(0), INT2FIX(flag));
7440
7441 PM_COMPILE_NOT_POPPED(node->value);
7442 ID id_operator = pm_constant_id_lookup(scope_node, node->binary_operator);
7443 PUSH_SEND(ret, *location, id_operator, INT2FIX(1));
7444
7445 if (!popped) {
7446 PUSH_INSN(ret, *location, swap);
7447 PUSH_INSN1(ret, *location, topn, INT2FIX(1));
7448 }
7449
7450 ID id_write_name = pm_constant_id_lookup(scope_node, node->write_name);
7451 PUSH_SEND_WITH_FLAG(ret, *location, id_write_name, INT2FIX(1), INT2FIX(flag));
7452
7453 if (safe_label != NULL && popped) PUSH_LABEL(ret, safe_label);
7454 PUSH_INSN(ret, *location, pop);
7455 if (safe_label != NULL && !popped) PUSH_LABEL(ret, safe_label);
7456}
7457
7474static VALUE
7475pm_compile_case_node_dispatch(rb_iseq_t *iseq, VALUE dispatch, const pm_node_t *node, LABEL *label, const pm_scope_node_t *scope_node)
7476{
7477 VALUE key = Qundef;
7478
7479 switch (PM_NODE_TYPE(node)) {
7480 case PM_FLOAT_NODE: {
7481 key = pm_static_literal_value(iseq, node, scope_node);
7482 double intptr;
7483
7484 if (modf(RFLOAT_VALUE(key), &intptr) == 0.0) {
7485 key = (FIXABLE(intptr) ? LONG2FIX((long) intptr) : rb_dbl2big(intptr));
7486 }
7487
7488 break;
7489 }
7490 case PM_FALSE_NODE:
7491 case PM_INTEGER_NODE:
7492 case PM_NIL_NODE:
7493 case PM_SOURCE_FILE_NODE:
7494 case PM_SOURCE_LINE_NODE:
7495 case PM_SYMBOL_NODE:
7496 case PM_TRUE_NODE:
7497 key = pm_static_literal_value(iseq, node, scope_node);
7498 break;
7499 case PM_STRING_NODE: {
7500 const pm_string_node_t *cast = (const pm_string_node_t *) node;
7501 key = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
7502 break;
7503 }
7504 default:
7505 return Qundef;
7506 }
7507
7508 if (NIL_P(rb_hash_lookup(dispatch, key))) {
7509 rb_hash_aset(dispatch, key, ((VALUE) label) | 1);
7510 }
7511
7512 return dispatch;
7513}
7514
7518static inline void
7519pm_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)
7520{
7521 const pm_parser_t *parser = scope_node->parser;
7522 const pm_node_location_t location = *node_location;
7523 const pm_node_list_t *conditions = &cast->conditions;
7524
7525 // This is the anchor that we will compile the conditions of the various
7526 // `when` nodes into. If a match is found, they will need to jump into
7527 // the body_seq anchor to the correct spot.
7528 DECL_ANCHOR(cond_seq);
7529
7530 // This is the anchor that we will compile the bodies of the various
7531 // `when` nodes into. We'll make sure that the clauses that are compiled
7532 // jump into the correct spots within this anchor.
7533 DECL_ANCHOR(body_seq);
7534
7535 // This is the label where all of the when clauses will jump to if they
7536 // have matched and are done executing their bodies.
7537 LABEL *end_label = NEW_LABEL(location.line);
7538
7539 // If we have a predicate on this case statement, then it's going to
7540 // compare all of the various when clauses to the predicate. If we
7541 // don't, then it's basically an if-elsif-else chain.
7542 if (cast->predicate == NULL) {
7543 // Establish branch coverage for the case node.
7544 VALUE branches = Qfalse;
7545 rb_code_location_t case_location = { 0 };
7546 int branch_id = 0;
7547
7548 if (PM_BRANCH_COVERAGE_P(iseq)) {
7549 case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
7550 branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case");
7551 }
7552
7553 // Loop through each clauses in the case node and compile each of
7554 // the conditions within them into cond_seq. If they match, they
7555 // should jump into their respective bodies in body_seq.
7556 for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) {
7557 const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index];
7558 const pm_node_list_t *conditions = &clause->conditions;
7559
7560 int clause_lineno = pm_node_line_number(parser, (const pm_node_t *) clause);
7561 LABEL *label = NEW_LABEL(clause_lineno);
7562 PUSH_LABEL(body_seq, label);
7563
7564 // Establish branch coverage for the when clause.
7565 if (PM_BRANCH_COVERAGE_P(iseq)) {
7566 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));
7567 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches);
7568 }
7569
7570 if (clause->statements != NULL) {
7571 pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node);
7572 }
7573 else if (!popped) {
7574 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
7575 }
7576
7577 PUSH_INSNL(body_seq, location, jump, end_label);
7578
7579 // Compile each of the conditions for the when clause into the
7580 // cond_seq. Each one should have a unique condition and should
7581 // jump to the subsequent one if it doesn't match.
7582 for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) {
7583 const pm_node_t *condition = conditions->nodes[condition_index];
7584
7585 if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) {
7586 pm_node_location_t cond_location = PM_NODE_START_LOCATION(parser, condition);
7587 PUSH_INSN(cond_seq, cond_location, putnil);
7588 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7589 PUSH_INSN1(cond_seq, cond_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
7590 PUSH_INSNL(cond_seq, cond_location, branchif, label);
7591 }
7592 else {
7593 LABEL *next_label = NEW_LABEL(pm_node_line_number(parser, condition));
7594 pm_compile_branch_condition(iseq, cond_seq, condition, label, next_label, false, scope_node);
7595 PUSH_LABEL(cond_seq, next_label);
7596 }
7597 }
7598 }
7599
7600 // Establish branch coverage for the else clause (implicit or
7601 // explicit).
7602 if (PM_BRANCH_COVERAGE_P(iseq)) {
7603 rb_code_location_t branch_location;
7604
7605 if (cast->else_clause == NULL) {
7606 branch_location = case_location;
7607 } else if (cast->else_clause->statements == NULL) {
7608 branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->else_clause);
7609 } else {
7610 branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->else_clause->statements);
7611 }
7612
7613 add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
7614 }
7615
7616 // Compile the else clause if there is one.
7617 if (cast->else_clause != NULL) {
7618 pm_compile_node(iseq, (const pm_node_t *) cast->else_clause, cond_seq, popped, scope_node);
7619 }
7620 else if (!popped) {
7621 PUSH_SYNTHETIC_PUTNIL(cond_seq, iseq);
7622 }
7623
7624 // Finally, jump to the end label if none of the other conditions
7625 // have matched.
7626 PUSH_INSNL(cond_seq, location, jump, end_label);
7627 PUSH_SEQ(ret, cond_seq);
7628 }
7629 else {
7630 // Establish branch coverage for the case node.
7631 VALUE branches = Qfalse;
7632 rb_code_location_t case_location = { 0 };
7633 int branch_id = 0;
7634
7635 if (PM_BRANCH_COVERAGE_P(iseq)) {
7636 case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
7637 branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case");
7638 }
7639
7640 // This is the label where everything will fall into if none of the
7641 // conditions matched.
7642 LABEL *else_label = NEW_LABEL(location.line);
7643
7644 // It's possible for us to speed up the case node by using a
7645 // dispatch hash. This is a hash that maps the conditions of the
7646 // various when clauses to the labels of their bodies. If we can
7647 // compile the conditions into a hash key, then we can use a hash
7648 // lookup to jump directly to the correct when clause body.
7649 VALUE dispatch = Qundef;
7650 if (ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
7651 dispatch = rb_hash_new();
7652 RHASH_TBL_RAW(dispatch)->type = &cdhash_type;
7653 }
7654
7655 // We're going to loop through each of the conditions in the case
7656 // node and compile each of their contents into both the cond_seq
7657 // and the body_seq. Each condition will use its own label to jump
7658 // from its conditions into its body.
7659 //
7660 // Note that none of the code in the loop below should be adding
7661 // anything to ret, as we're going to be laying out the entire case
7662 // node instructions later.
7663 for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) {
7664 const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index];
7665 pm_node_location_t clause_location = PM_NODE_START_LOCATION(parser, (const pm_node_t *) clause);
7666
7667 const pm_node_list_t *conditions = &clause->conditions;
7668 LABEL *label = NEW_LABEL(clause_location.line);
7669
7670 // Compile each of the conditions for the when clause into the
7671 // cond_seq. Each one should have a unique comparison that then
7672 // jumps into the body if it matches.
7673 for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) {
7674 const pm_node_t *condition = conditions->nodes[condition_index];
7675 const pm_node_location_t condition_location = PM_NODE_START_LOCATION(parser, condition);
7676
7677 // If we haven't already abandoned the optimization, then
7678 // we're going to try to compile the condition into the
7679 // dispatch hash.
7680 if (dispatch != Qundef) {
7681 dispatch = pm_compile_case_node_dispatch(iseq, dispatch, condition, label, scope_node);
7682 }
7683
7684 if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) {
7685 PUSH_INSN(cond_seq, condition_location, dup);
7686 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7687 PUSH_INSN1(cond_seq, condition_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
7688 }
7689 else {
7690 if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
7691 const pm_string_node_t *string = (const pm_string_node_t *) condition;
7692 VALUE value = parse_static_literal_string(iseq, scope_node, condition, &string->unescaped);
7693 PUSH_INSN1(cond_seq, condition_location, putobject, value);
7694 }
7695 else {
7696 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7697 }
7698
7699 PUSH_INSN1(cond_seq, condition_location, topn, INT2FIX(1));
7700 PUSH_SEND_WITH_FLAG(cond_seq, condition_location, idEqq, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
7701 }
7702
7703 PUSH_INSNL(cond_seq, condition_location, branchif, label);
7704 }
7705
7706 // Now, add the label to the body and compile the body of the
7707 // when clause. This involves popping the predicate, compiling
7708 // the statements to be executed, and then compiling a jump to
7709 // the end of the case node.
7710 PUSH_LABEL(body_seq, label);
7711 PUSH_INSN(body_seq, clause_location, pop);
7712
7713 // Establish branch coverage for the when clause.
7714 if (PM_BRANCH_COVERAGE_P(iseq)) {
7715 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));
7716 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches);
7717 }
7718
7719 if (clause->statements != NULL) {
7720 pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node);
7721 }
7722 else if (!popped) {
7723 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
7724 }
7725
7726 PUSH_INSNL(body_seq, clause_location, jump, end_label);
7727 }
7728
7729 // Now that we have compiled the conditions and the bodies of the
7730 // various when clauses, we can compile the predicate, lay out the
7731 // conditions, compile the fallback subsequent if there is one, and
7732 // finally put in the bodies of the when clauses.
7733 PM_COMPILE_NOT_POPPED(cast->predicate);
7734
7735 // If we have a dispatch hash, then we'll use it here to create the
7736 // optimization.
7737 if (dispatch != Qundef) {
7738 PUSH_INSN(ret, location, dup);
7739 PUSH_INSN2(ret, location, opt_case_dispatch, dispatch, else_label);
7740 LABEL_REF(else_label);
7741 }
7742
7743 PUSH_SEQ(ret, cond_seq);
7744
7745 // Compile either the explicit else clause or an implicit else
7746 // clause.
7747 PUSH_LABEL(ret, else_label);
7748
7749 if (cast->else_clause != NULL) {
7750 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));
7751 PUSH_INSN(ret, else_location, pop);
7752
7753 // Establish branch coverage for the else clause.
7754 if (PM_BRANCH_COVERAGE_P(iseq)) {
7755 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));
7756 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
7757 }
7758
7759 PM_COMPILE((const pm_node_t *) cast->else_clause);
7760 PUSH_INSNL(ret, else_location, jump, end_label);
7761 }
7762 else {
7763 PUSH_INSN(ret, location, pop);
7764
7765 // Establish branch coverage for the implicit else clause.
7766 if (PM_BRANCH_COVERAGE_P(iseq)) {
7767 add_trace_branch_coverage(iseq, ret, &case_location, case_location.beg_pos.column, branch_id, "else", branches);
7768 }
7769
7770 if (!popped) PUSH_INSN(ret, location, putnil);
7771 PUSH_INSNL(ret, location, jump, end_label);
7772 }
7773 }
7774
7775 PUSH_SEQ(ret, body_seq);
7776 PUSH_LABEL(ret, end_label);
7777}
7778
7779static inline void
7780pm_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)
7781{
7782 // This is the anchor that we will compile the bodies of the various
7783 // `in` nodes into. We'll make sure that the patterns that are compiled
7784 // jump into the correct spots within this anchor.
7785 DECL_ANCHOR(body_seq);
7786
7787 // This is the anchor that we will compile the patterns of the various
7788 // `in` nodes into. If a match is found, they will need to jump into the
7789 // body_seq anchor to the correct spot.
7790 DECL_ANCHOR(cond_seq);
7791
7792 // This label is used to indicate the end of the entire node. It is
7793 // jumped to after the entire stack is cleaned up.
7794 LABEL *end_label = NEW_LABEL(location->line);
7795
7796 // This label is used as the fallback for the case match. If no match is
7797 // found, then we jump to this label. This is either an `else` clause or
7798 // an error handler.
7799 LABEL *else_label = NEW_LABEL(location->line);
7800
7801 // We're going to use this to uniquely identify each branch so that we
7802 // can track coverage information.
7803 rb_code_location_t case_location = { 0 };
7804 VALUE branches = Qfalse;
7805 int branch_id = 0;
7806
7807 if (PM_BRANCH_COVERAGE_P(iseq)) {
7808 case_location = pm_code_location(scope_node, (const pm_node_t *) node);
7809 branches = decl_branch_base(iseq, PTR2NUM(node), &case_location, "case");
7810 }
7811
7812 // If there is only one pattern, then the behavior changes a bit. It
7813 // effectively gets treated as a match required node (this is how it is
7814 // represented in the other parser).
7815 bool in_single_pattern = node->else_clause == NULL && node->conditions.size == 1;
7816
7817 // First, we're going to push a bunch of stuff onto the stack that is
7818 // going to serve as our scratch space.
7819 if (in_single_pattern) {
7820 PUSH_INSN(ret, *location, putnil); // key error key
7821 PUSH_INSN(ret, *location, putnil); // key error matchee
7822 PUSH_INSN1(ret, *location, putobject, Qfalse); // key error?
7823 PUSH_INSN(ret, *location, putnil); // error string
7824 }
7825
7826 // Now we're going to compile the value to match against.
7827 PUSH_INSN(ret, *location, putnil); // deconstruct cache
7828 PM_COMPILE_NOT_POPPED(node->predicate);
7829
7830 // Next, we'll loop through every in clause and compile its body into
7831 // the body_seq anchor and its pattern into the cond_seq anchor. We'll
7832 // make sure the pattern knows how to jump correctly into the body if it
7833 // finds a match.
7834 for (size_t index = 0; index < node->conditions.size; index++) {
7835 const pm_node_t *condition = node->conditions.nodes[index];
7836 RUBY_ASSERT(PM_NODE_TYPE_P(condition, PM_IN_NODE));
7837
7838 const pm_in_node_t *in_node = (const pm_in_node_t *) condition;
7839 const pm_node_location_t in_location = PM_NODE_START_LOCATION(scope_node->parser, in_node);
7840 const pm_node_location_t pattern_location = PM_NODE_START_LOCATION(scope_node->parser, in_node->pattern);
7841
7842 if (branch_id) {
7843 PUSH_INSN(body_seq, in_location, putnil);
7844 }
7845
7846 LABEL *body_label = NEW_LABEL(in_location.line);
7847 PUSH_LABEL(body_seq, body_label);
7848 PUSH_INSN1(body_seq, in_location, adjuststack, INT2FIX(in_single_pattern ? 6 : 2));
7849
7850 // Establish branch coverage for the in clause.
7851 if (PM_BRANCH_COVERAGE_P(iseq)) {
7852 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));
7853 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "in", branches);
7854 }
7855
7856 if (in_node->statements != NULL) {
7857 PM_COMPILE_INTO_ANCHOR(body_seq, (const pm_node_t *) in_node->statements);
7858 }
7859 else if (!popped) {
7860 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
7861 }
7862
7863 PUSH_INSNL(body_seq, in_location, jump, end_label);
7864 LABEL *next_pattern_label = NEW_LABEL(pattern_location.line);
7865
7866 PUSH_INSN(cond_seq, pattern_location, dup);
7867 pm_compile_pattern(iseq, scope_node, in_node->pattern, cond_seq, body_label, next_pattern_label, in_single_pattern, false, true, 2);
7868 PUSH_LABEL(cond_seq, next_pattern_label);
7869 LABEL_UNREMOVABLE(next_pattern_label);
7870 }
7871
7872 if (node->else_clause != NULL) {
7873 // If we have an `else` clause, then this becomes our fallback (and
7874 // there is no need to compile in code to potentially raise an
7875 // error).
7876 const pm_else_node_t *else_node = node->else_clause;
7877
7878 PUSH_LABEL(cond_seq, else_label);
7879 PUSH_INSN(cond_seq, *location, pop);
7880 PUSH_INSN(cond_seq, *location, pop);
7881
7882 // Establish branch coverage for the else clause.
7883 if (PM_BRANCH_COVERAGE_P(iseq)) {
7884 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));
7885 add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
7886 }
7887
7888 PM_COMPILE_INTO_ANCHOR(cond_seq, (const pm_node_t *) else_node);
7889 PUSH_INSNL(cond_seq, *location, jump, end_label);
7890 PUSH_INSN(cond_seq, *location, putnil);
7891 if (popped) PUSH_INSN(cond_seq, *location, putnil);
7892 }
7893 else {
7894 // Otherwise, if we do not have an `else` clause, we will compile in
7895 // the code to handle raising an appropriate error.
7896 PUSH_LABEL(cond_seq, else_label);
7897
7898 // Establish branch coverage for the implicit else clause.
7899 add_trace_branch_coverage(iseq, cond_seq, &case_location, case_location.beg_pos.column, branch_id, "else", branches);
7900
7901 if (in_single_pattern) {
7902 pm_compile_pattern_error_handler(iseq, scope_node, (const pm_node_t *) node, cond_seq, end_label, popped);
7903 }
7904 else {
7905 PUSH_INSN1(cond_seq, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7906 PUSH_INSN1(cond_seq, *location, putobject, rb_eNoMatchingPatternError);
7907 PUSH_INSN1(cond_seq, *location, topn, INT2FIX(2));
7908 PUSH_SEND(cond_seq, *location, id_core_raise, INT2FIX(2));
7909
7910 PUSH_INSN1(cond_seq, *location, adjuststack, INT2FIX(3));
7911 if (!popped) PUSH_INSN(cond_seq, *location, putnil);
7912 PUSH_INSNL(cond_seq, *location, jump, end_label);
7913 PUSH_INSN1(cond_seq, *location, dupn, INT2FIX(1));
7914 if (popped) PUSH_INSN(cond_seq, *location, putnil);
7915 }
7916 }
7917
7918 // At the end of all of this compilation, we will add the code for the
7919 // conditions first, then the various bodies, then mark the end of the
7920 // entire sequence with the end label.
7921 PUSH_SEQ(ret, cond_seq);
7922 PUSH_SEQ(ret, body_seq);
7923 PUSH_LABEL(ret, end_label);
7924}
7925
7926static inline void
7927pm_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)
7928{
7929 const rb_iseq_t *block = NULL;
7930 const rb_iseq_t *previous_block = NULL;
7931 LABEL *retry_label = NULL;
7932 LABEL *retry_end_l = NULL;
7933
7934 if (node->block != NULL) {
7935 previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
7936 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
7937
7938 retry_label = NEW_LABEL(location->line);
7939 retry_end_l = NEW_LABEL(location->line);
7940
7941 PUSH_LABEL(ret, retry_label);
7942 }
7943 else {
7944 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
7945 }
7946
7947 PUSH_INSN(ret, *location, putself);
7948 int flag = VM_CALL_ZSUPER | VM_CALL_SUPER | VM_CALL_FCALL;
7949
7950 if (node->block != NULL) {
7951 pm_scope_node_t next_scope_node;
7952 pm_scope_node_init((const pm_node_t *) node->block, &next_scope_node, scope_node);
7953
7954 ISEQ_COMPILE_DATA(iseq)->current_block = block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location->line);
7955 pm_scope_node_destroy(&next_scope_node);
7956 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) block);
7957 }
7958
7959 DECL_ANCHOR(args);
7960
7961 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
7962 const rb_iseq_t *local_iseq = body->local_iseq;
7963 const struct rb_iseq_constant_body *const local_body = ISEQ_BODY(local_iseq);
7964
7965 int argc = 0;
7966 int depth = get_lvar_level(iseq);
7967
7968 if (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
7969 flag |= VM_CALL_FORWARDING;
7970 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_DOT3, 0);
7971 PUSH_GETLOCAL(ret, *location, mult_local.index, mult_local.level);
7972
7973 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, 0, flag, NULL, block != NULL);
7974 PUSH_INSN2(ret, *location, invokesuperforward, callinfo, block);
7975
7976 if (popped) PUSH_INSN(ret, *location, pop);
7977 if (node->block) {
7978 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
7979 }
7980 return;
7981 }
7982
7983 if (local_body->param.flags.has_lead) {
7984 /* required arguments */
7985 for (int i = 0; i < local_body->param.lead_num; i++) {
7986 int idx = local_body->local_table_size - i;
7987 PUSH_GETLOCAL(args, *location, idx, depth);
7988 }
7989 argc += local_body->param.lead_num;
7990 }
7991
7992 if (local_body->param.flags.has_opt) {
7993 /* optional arguments */
7994 for (int j = 0; j < local_body->param.opt_num; j++) {
7995 int idx = local_body->local_table_size - (argc + j);
7996 PUSH_GETLOCAL(args, *location, idx, depth);
7997 }
7998 argc += local_body->param.opt_num;
7999 }
8000
8001 if (local_body->param.flags.has_rest) {
8002 /* rest argument */
8003 int idx = local_body->local_table_size - local_body->param.rest_start;
8004 PUSH_GETLOCAL(args, *location, idx, depth);
8005 PUSH_INSN1(args, *location, splatarray, Qfalse);
8006
8007 argc = local_body->param.rest_start + 1;
8008 flag |= VM_CALL_ARGS_SPLAT;
8009 }
8010
8011 if (local_body->param.flags.has_post) {
8012 /* post arguments */
8013 int post_len = local_body->param.post_num;
8014 int post_start = local_body->param.post_start;
8015
8016 int j = 0;
8017 for (; j < post_len; j++) {
8018 int idx = local_body->local_table_size - (post_start + j);
8019 PUSH_GETLOCAL(args, *location, idx, depth);
8020 }
8021
8022 if (local_body->param.flags.has_rest) {
8023 // argc remains unchanged from rest branch
8024 PUSH_INSN1(args, *location, newarray, INT2FIX(j));
8025 PUSH_INSN(args, *location, concatarray);
8026 }
8027 else {
8028 argc = post_len + post_start;
8029 }
8030 }
8031
8032 const struct rb_iseq_param_keyword *const local_keyword = local_body->param.keyword;
8033 if (local_body->param.flags.has_kw) {
8034 int local_size = local_body->local_table_size;
8035 argc++;
8036
8037 PUSH_INSN1(args, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8038
8039 if (local_body->param.flags.has_kwrest) {
8040 int idx = local_body->local_table_size - local_keyword->rest_start;
8041 PUSH_GETLOCAL(args, *location, idx, depth);
8042 RUBY_ASSERT(local_keyword->num > 0);
8043 PUSH_SEND(args, *location, rb_intern("dup"), INT2FIX(0));
8044 }
8045 else {
8046 PUSH_INSN1(args, *location, newhash, INT2FIX(0));
8047 }
8048 int i = 0;
8049 for (; i < local_keyword->num; ++i) {
8050 ID id = local_keyword->table[i];
8051 int idx = local_size - get_local_var_idx(local_iseq, id);
8052
8053 {
8054 VALUE operand = ID2SYM(id);
8055 PUSH_INSN1(args, *location, putobject, operand);
8056 }
8057
8058 PUSH_GETLOCAL(args, *location, idx, depth);
8059 }
8060
8061 PUSH_SEND(args, *location, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
8062 flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT;
8063 }
8064 else if (local_body->param.flags.has_kwrest) {
8065 int idx = local_body->local_table_size - local_keyword->rest_start;
8066 PUSH_GETLOCAL(args, *location, idx, depth);
8067 argc++;
8068 flag |= VM_CALL_KW_SPLAT;
8069 }
8070
8071 PUSH_SEQ(ret, args);
8072
8073 {
8074 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flag, NULL, block != NULL);
8075 PUSH_INSN2(ret, *location, invokesuper, callinfo, block);
8076 }
8077
8078 if (node->block != NULL) {
8079 pm_compile_retry_end_label(iseq, ret, retry_end_l);
8080 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, block, retry_end_l);
8081 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
8082 }
8083
8084 if (popped) PUSH_INSN(ret, *location, pop);
8085}
8086
8087static inline void
8088pm_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)
8089{
8090 LABEL *matched_label = NEW_LABEL(location->line);
8091 LABEL *unmatched_label = NEW_LABEL(location->line);
8092 LABEL *done_label = NEW_LABEL(location->line);
8093
8094 // First, we're going to push a bunch of stuff onto the stack that is
8095 // going to serve as our scratch space.
8096 PUSH_INSN(ret, *location, putnil); // key error key
8097 PUSH_INSN(ret, *location, putnil); // key error matchee
8098 PUSH_INSN1(ret, *location, putobject, Qfalse); // key error?
8099 PUSH_INSN(ret, *location, putnil); // error string
8100 PUSH_INSN(ret, *location, putnil); // deconstruct cache
8101
8102 // Next we're going to compile the value expression such that it's on
8103 // the stack.
8104 PM_COMPILE_NOT_POPPED(node->value);
8105
8106 // Here we'll dup it so that it can be used for comparison, but also be
8107 // used for error handling.
8108 PUSH_INSN(ret, *location, dup);
8109
8110 // Next we'll compile the pattern. We indicate to the pm_compile_pattern
8111 // function that this is the only pattern that will be matched against
8112 // through the in_single_pattern parameter. We also indicate that the
8113 // value to compare against is 2 slots from the top of the stack (the
8114 // base_index parameter).
8115 pm_compile_pattern(iseq, scope_node, node->pattern, ret, matched_label, unmatched_label, true, false, true, 2);
8116
8117 // If the pattern did not match the value, then we're going to compile
8118 // in our error handler code. This will determine which error to raise
8119 // and raise it.
8120 PUSH_LABEL(ret, unmatched_label);
8121 pm_compile_pattern_error_handler(iseq, scope_node, (const pm_node_t *) node, ret, done_label, popped);
8122
8123 // If the pattern did match, we'll clean up the values we've pushed onto
8124 // the stack and then push nil onto the stack if it's not popped.
8125 PUSH_LABEL(ret, matched_label);
8126 PUSH_INSN1(ret, *location, adjuststack, INT2FIX(6));
8127 if (!popped) PUSH_INSN(ret, *location, putnil);
8128 PUSH_INSNL(ret, *location, jump, done_label);
8129
8130 PUSH_LABEL(ret, done_label);
8131}
8132
8133static inline void
8134pm_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)
8135{
8136 LABEL *fail_label = NEW_LABEL(location->line);
8137 LABEL *end_label = NEW_LABEL(location->line);
8138
8139 // First, we'll compile the call so that all of its instructions are
8140 // present. Then we'll compile all of the local variable targets.
8141 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->call);
8142
8143 // Now, check if the match was successful. If it was, then we'll
8144 // continue on and assign local variables. Otherwise we'll skip over the
8145 // assignment code.
8146 {
8147 VALUE operand = rb_id2sym(idBACKREF);
8148 PUSH_INSN1(ret, *location, getglobal, operand);
8149 }
8150
8151 PUSH_INSN(ret, *location, dup);
8152 PUSH_INSNL(ret, *location, branchunless, fail_label);
8153
8154 // If there's only a single local variable target, we can skip some of
8155 // the bookkeeping, so we'll put a special branch here.
8156 size_t targets_count = node->targets.size;
8157
8158 if (targets_count == 1) {
8159 const pm_node_t *target = node->targets.nodes[0];
8160 RUBY_ASSERT(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
8161
8162 const pm_local_variable_target_node_t *local_target = (const pm_local_variable_target_node_t *) target;
8163 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, local_target->name, local_target->depth);
8164
8165 {
8166 VALUE operand = rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name));
8167 PUSH_INSN1(ret, *location, putobject, operand);
8168 }
8169
8170 PUSH_SEND(ret, *location, idAREF, INT2FIX(1));
8171 PUSH_LABEL(ret, fail_label);
8172 PUSH_SETLOCAL(ret, *location, index.index, index.level);
8173 if (popped) PUSH_INSN(ret, *location, pop);
8174 return;
8175 }
8176
8177 DECL_ANCHOR(fail_anchor);
8178
8179 // Otherwise there is more than one local variable target, so we'll need
8180 // to do some bookkeeping.
8181 for (size_t targets_index = 0; targets_index < targets_count; targets_index++) {
8182 const pm_node_t *target = node->targets.nodes[targets_index];
8183 RUBY_ASSERT(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
8184
8185 const pm_local_variable_target_node_t *local_target = (const pm_local_variable_target_node_t *) target;
8186 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, local_target->name, local_target->depth);
8187
8188 if (((size_t) targets_index) < (targets_count - 1)) {
8189 PUSH_INSN(ret, *location, dup);
8190 }
8191
8192 {
8193 VALUE operand = rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name));
8194 PUSH_INSN1(ret, *location, putobject, operand);
8195 }
8196
8197 PUSH_SEND(ret, *location, idAREF, INT2FIX(1));
8198 PUSH_SETLOCAL(ret, *location, index.index, index.level);
8199
8200 PUSH_INSN(fail_anchor, *location, putnil);
8201 PUSH_SETLOCAL(fail_anchor, *location, index.index, index.level);
8202 }
8203
8204 // Since we matched successfully, now we'll jump to the end.
8205 PUSH_INSNL(ret, *location, jump, end_label);
8206
8207 // In the case that the match failed, we'll loop through each local
8208 // variable target and set all of them to `nil`.
8209 PUSH_LABEL(ret, fail_label);
8210 PUSH_INSN(ret, *location, pop);
8211 PUSH_SEQ(ret, fail_anchor);
8212
8213 // Finally, we can push the end label for either case.
8214 PUSH_LABEL(ret, end_label);
8215 if (popped) PUSH_INSN(ret, *location, pop);
8216}
8217
8218static inline void
8219pm_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)
8220{
8221 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
8222 LABEL *splabel = NEW_LABEL(0);
8223 PUSH_LABEL(ret, splabel);
8224
8225 if (node->arguments) {
8226 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8227 }
8228 else {
8229 PUSH_INSN(ret, *location, putnil);
8230 }
8231 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8232
8233 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
8234 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8235
8236 PUSH_ADJUST_RESTORE(ret, splabel);
8237 if (!popped) PUSH_INSN(ret, *location, putnil);
8238 }
8239 else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
8240 LABEL *splabel = NEW_LABEL(0);
8241
8242 PUSH_LABEL(ret, splabel);
8243 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->start_label);
8244
8245 if (node->arguments != NULL) {
8246 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8247 }
8248 else {
8249 PUSH_INSN(ret, *location, putnil);
8250 }
8251
8252 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8253 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
8254 PUSH_ADJUST_RESTORE(ret, splabel);
8255 splabel->unremovable = FALSE;
8256
8257 if (!popped) PUSH_INSN(ret, *location, putnil);
8258 }
8259 else {
8260 const rb_iseq_t *ip = iseq;
8261 unsigned long throw_flag = 0;
8262
8263 while (ip) {
8264 if (!ISEQ_COMPILE_DATA(ip)) {
8265 ip = 0;
8266 break;
8267 }
8268
8269 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
8270 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8271 /* while loop */
8272 break;
8273 }
8274 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8275 break;
8276 }
8277 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8278 COMPILE_ERROR(iseq, location->line, "Invalid next");
8279 return;
8280 }
8281
8282 ip = ISEQ_BODY(ip)->parent_iseq;
8283 }
8284
8285 if (ip != 0) {
8286 if (node->arguments) {
8287 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8288 }
8289 else {
8290 PUSH_INSN(ret, *location, putnil);
8291 }
8292
8293 PUSH_INSN1(ret, *location, throw, INT2FIX(throw_flag | TAG_NEXT));
8294 if (popped) PUSH_INSN(ret, *location, pop);
8295 }
8296 else {
8297 COMPILE_ERROR(iseq, location->line, "Invalid next");
8298 }
8299 }
8300}
8301
8302static inline void
8303pm_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)
8304{
8305 if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
8306 LABEL *splabel = NEW_LABEL(0);
8307
8308 PUSH_LABEL(ret, splabel);
8309 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
8310 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8311
8312 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
8313 PUSH_ADJUST_RESTORE(ret, splabel);
8314 if (!popped) PUSH_INSN(ret, *location, putnil);
8315 }
8316 else if (ISEQ_BODY(iseq)->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
8317 LABEL *splabel = NEW_LABEL(0);
8318
8319 PUSH_LABEL(ret, splabel);
8320 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8321 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->start_label);
8322
8323 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8324 PUSH_ADJUST_RESTORE(ret, splabel);
8325 if (!popped) PUSH_INSN(ret, *location, putnil);
8326 }
8327 else {
8328 const rb_iseq_t *ip = iseq;
8329
8330 while (ip) {
8331 if (!ISEQ_COMPILE_DATA(ip)) {
8332 ip = 0;
8333 break;
8334 }
8335
8336 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8337 break;
8338 }
8339 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8340 break;
8341 }
8342 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8343 COMPILE_ERROR(iseq, location->line, "Invalid redo");
8344 return;
8345 }
8346
8347 ip = ISEQ_BODY(ip)->parent_iseq;
8348 }
8349
8350 if (ip != 0) {
8351 PUSH_INSN(ret, *location, putnil);
8352 PUSH_INSN1(ret, *location, throw, INT2FIX(VM_THROW_NO_ESCAPE_FLAG | TAG_REDO));
8353 if (popped) PUSH_INSN(ret, *location, pop);
8354 }
8355 else {
8356 COMPILE_ERROR(iseq, location->line, "Invalid redo");
8357 }
8358 }
8359}
8360
8361static inline void
8362pm_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)
8363{
8364 iseq_set_exception_local_table(iseq);
8365
8366 // First, establish the labels that we need to be able to jump to within
8367 // this compilation block.
8368 LABEL *exception_match_label = NEW_LABEL(location->line);
8369 LABEL *rescue_end_label = NEW_LABEL(location->line);
8370
8371 // Next, compile each of the exceptions that we're going to be
8372 // handling. For each one, we'll add instructions to check if the
8373 // exception matches the raised one, and if it does then jump to the
8374 // exception_match_label label. Otherwise it will fall through to the
8375 // subsequent check. If there are no exceptions, we'll only check
8376 // StandardError.
8377 const pm_node_list_t *exceptions = &node->exceptions;
8378
8379 if (exceptions->size > 0) {
8380 for (size_t index = 0; index < exceptions->size; index++) {
8381 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8382 PM_COMPILE(exceptions->nodes[index]);
8383 int checkmatch_flags = VM_CHECKMATCH_TYPE_RESCUE;
8384 if (PM_NODE_TYPE_P(exceptions->nodes[index], PM_SPLAT_NODE)) {
8385 checkmatch_flags |= VM_CHECKMATCH_ARRAY;
8386 }
8387 PUSH_INSN1(ret, *location, checkmatch, INT2FIX(checkmatch_flags));
8388 PUSH_INSNL(ret, *location, branchif, exception_match_label);
8389 }
8390 }
8391 else {
8392 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8393 PUSH_INSN1(ret, *location, putobject, rb_eStandardError);
8394 PUSH_INSN1(ret, *location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
8395 PUSH_INSNL(ret, *location, branchif, exception_match_label);
8396 }
8397
8398 // If none of the exceptions that we are matching against matched, then
8399 // we'll jump straight to the rescue_end_label label.
8400 PUSH_INSNL(ret, *location, jump, rescue_end_label);
8401
8402 // Here we have the exception_match_label, which is where the
8403 // control-flow goes in the case that one of the exceptions matched.
8404 // Here we will compile the instructions to handle the exception.
8405 PUSH_LABEL(ret, exception_match_label);
8406 PUSH_TRACE(ret, RUBY_EVENT_RESCUE);
8407
8408 // If we have a reference to the exception, then we'll compile the write
8409 // into the instruction sequence. This can look quite different
8410 // depending on the kind of write being performed.
8411 if (node->reference) {
8412 DECL_ANCHOR(writes);
8413 DECL_ANCHOR(cleanup);
8414
8415 pm_compile_target_node(iseq, node->reference, ret, writes, cleanup, scope_node, NULL);
8416 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8417
8418 PUSH_SEQ(ret, writes);
8419 PUSH_SEQ(ret, cleanup);
8420 }
8421
8422 // If we have statements to execute, we'll compile them here. Otherwise
8423 // we'll push nil onto the stack.
8424 if (node->statements != NULL) {
8425 // We'll temporarily remove the end_label location from the iseq
8426 // when compiling the statements so that next/redo statements
8427 // inside the body will throw to the correct place instead of
8428 // jumping straight to the end of this iseq
8429 LABEL *prev_end = ISEQ_COMPILE_DATA(iseq)->end_label;
8430 ISEQ_COMPILE_DATA(iseq)->end_label = NULL;
8431
8432 PM_COMPILE((const pm_node_t *) node->statements);
8433
8434 // Now restore the end_label
8435 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end;
8436 }
8437 else {
8438 PUSH_INSN(ret, *location, putnil);
8439 }
8440
8441 PUSH_INSN(ret, *location, leave);
8442
8443 // Here we'll insert the rescue_end_label label, which is jumped to if
8444 // none of the exceptions matched. It will cause the control-flow to
8445 // either jump to the next rescue clause or it will fall through to the
8446 // subsequent instruction returning the raised error.
8447 PUSH_LABEL(ret, rescue_end_label);
8448 if (node->subsequent != NULL) {
8449 PM_COMPILE((const pm_node_t *) node->subsequent);
8450 }
8451 else {
8452 PUSH_GETLOCAL(ret, *location, 1, 0);
8453 }
8454}
8455
8456static inline void
8457pm_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)
8458{
8459 const pm_arguments_node_t *arguments = node->arguments;
8460 enum rb_iseq_type type = ISEQ_BODY(iseq)->type;
8461 LABEL *splabel = 0;
8462
8463 const rb_iseq_t *parent_iseq = iseq;
8464 enum rb_iseq_type parent_type = ISEQ_BODY(parent_iseq)->type;
8465 while (parent_type == ISEQ_TYPE_RESCUE || parent_type == ISEQ_TYPE_ENSURE) {
8466 if (!(parent_iseq = ISEQ_BODY(parent_iseq)->parent_iseq)) break;
8467 parent_type = ISEQ_BODY(parent_iseq)->type;
8468 }
8469
8470 switch (parent_type) {
8471 case ISEQ_TYPE_TOP:
8472 case ISEQ_TYPE_MAIN:
8473 if (arguments) {
8474 rb_warn("argument of top-level return is ignored");
8475 }
8476 if (parent_iseq == iseq) {
8477 type = ISEQ_TYPE_METHOD;
8478 }
8479 break;
8480 default:
8481 break;
8482 }
8483
8484 if (type == ISEQ_TYPE_METHOD) {
8485 splabel = NEW_LABEL(0);
8486 PUSH_LABEL(ret, splabel);
8487 PUSH_ADJUST(ret, *location, 0);
8488 }
8489
8490 if (arguments != NULL) {
8491 PM_COMPILE_NOT_POPPED((const pm_node_t *) arguments);
8492 }
8493 else {
8494 PUSH_INSN(ret, *location, putnil);
8495 }
8496
8497 if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
8498 pm_add_ensure_iseq(ret, iseq, 1, scope_node);
8499 PUSH_TRACE(ret, RUBY_EVENT_RETURN);
8500 PUSH_INSN(ret, *location, leave);
8501 PUSH_ADJUST_RESTORE(ret, splabel);
8502 if (!popped) PUSH_INSN(ret, *location, putnil);
8503 }
8504 else {
8505 PUSH_INSN1(ret, *location, throw, INT2FIX(TAG_RETURN));
8506 if (popped) PUSH_INSN(ret, *location, pop);
8507 }
8508}
8509
8510static inline void
8511pm_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)
8512{
8513 DECL_ANCHOR(args);
8514
8515 LABEL *retry_label = NEW_LABEL(location->line);
8516 LABEL *retry_end_l = NEW_LABEL(location->line);
8517
8518 const rb_iseq_t *previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
8519 const rb_iseq_t *current_block;
8520 ISEQ_COMPILE_DATA(iseq)->current_block = current_block = NULL;
8521
8522 PUSH_LABEL(ret, retry_label);
8523 PUSH_INSN(ret, *location, putself);
8524
8525 int flags = 0;
8526 struct rb_callinfo_kwarg *keywords = NULL;
8527 int argc = pm_setup_args(node->arguments, node->block, &flags, &keywords, iseq, ret, scope_node, location);
8528 bool is_forwardable = (node->arguments != NULL) && PM_NODE_FLAG_P(node->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING);
8529 flags |= VM_CALL_SUPER | VM_CALL_FCALL;
8530
8531 if (node->block && PM_NODE_TYPE_P(node->block, PM_BLOCK_NODE)) {
8532 pm_scope_node_t next_scope_node;
8533 pm_scope_node_init(node->block, &next_scope_node, scope_node);
8534
8535 ISEQ_COMPILE_DATA(iseq)->current_block = current_block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location->line);
8536 pm_scope_node_destroy(&next_scope_node);
8537 }
8538
8539 if (!node->block) {
8540 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
8541 }
8542
8543 if ((flags & VM_CALL_ARGS_BLOCKARG) && (flags & VM_CALL_KW_SPLAT) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
8544 PUSH_INSN(args, *location, splatkw);
8545 }
8546
8547 PUSH_SEQ(ret, args);
8548 if (is_forwardable && ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
8549 flags |= VM_CALL_FORWARDING;
8550
8551 {
8552 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, current_block != NULL);
8553 PUSH_INSN2(ret, *location, invokesuperforward, callinfo, current_block);
8554 }
8555 }
8556 else {
8557 {
8558 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, current_block != NULL);
8559 PUSH_INSN2(ret, *location, invokesuper, callinfo, current_block);
8560 }
8561
8562 }
8563
8564 pm_compile_retry_end_label(iseq, ret, retry_end_l);
8565
8566 if (popped) PUSH_INSN(ret, *location, pop);
8567 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
8568 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, current_block, retry_end_l);
8569}
8570
8571static inline void
8572pm_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)
8573{
8574 switch (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->type) {
8575 case ISEQ_TYPE_TOP:
8576 case ISEQ_TYPE_MAIN:
8577 case ISEQ_TYPE_CLASS:
8578 COMPILE_ERROR(iseq, location->line, "Invalid yield");
8579 return;
8580 default: /* valid */;
8581 }
8582
8583 int argc = 0;
8584 int flags = 0;
8585 struct rb_callinfo_kwarg *keywords = NULL;
8586
8587 if (node->arguments) {
8588 argc = pm_setup_args(node->arguments, NULL, &flags, &keywords, iseq, ret, scope_node, location);
8589 }
8590
8591 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, FALSE);
8592 PUSH_INSN1(ret, *location, invokeblock, callinfo);
8593
8594 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
8595 if (popped) PUSH_INSN(ret, *location, pop);
8596
8597 int level = 0;
8598 for (const rb_iseq_t *tmp_iseq = iseq; tmp_iseq != ISEQ_BODY(iseq)->local_iseq; level++) {
8599 tmp_iseq = ISEQ_BODY(tmp_iseq)->parent_iseq;
8600 }
8601
8602 if (level > 0) access_outer_variables(iseq, level, rb_intern("yield"), true);
8603}
8604
8615static void
8616pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8617{
8618 const pm_parser_t *parser = scope_node->parser;
8619 const pm_node_location_t location = PM_NODE_START_LOCATION(parser, node);
8620 int lineno = (int) location.line;
8621
8622 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)) {
8623 // If this node is a begin node and it has empty statements and also
8624 // has a rescue clause, then the other parser considers it as
8625 // starting on the same line as the rescue, as opposed to the
8626 // location of the begin keyword. We replicate that behavior here.
8627 lineno = (int) PM_NODE_START_LINE_COLUMN(parser, ((const pm_begin_node_t *) node)->rescue_clause).line;
8628 }
8629
8630 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_NEWLINE) && ISEQ_COMPILE_DATA(iseq)->last_line != lineno) {
8631 // If this node has the newline flag set and it is on a new line
8632 // from the previous nodes that have been compiled for this ISEQ,
8633 // then we need to emit a newline event.
8634 int event = RUBY_EVENT_LINE;
8635
8636 ISEQ_COMPILE_DATA(iseq)->last_line = lineno;
8637 if (lineno > 0 && ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
8638 event |= RUBY_EVENT_COVERAGE_LINE;
8639 }
8640 PUSH_TRACE(ret, event);
8641 }
8642
8643 switch (PM_NODE_TYPE(node)) {
8644 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
8645 // alias $foo $bar
8646 // ^^^^^^^^^^^^^^^
8647 pm_compile_alias_global_variable_node(iseq, (const pm_alias_global_variable_node_t *) node, &location, ret, popped, scope_node);
8648 return;
8649 case PM_ALIAS_METHOD_NODE:
8650 // alias foo bar
8651 // ^^^^^^^^^^^^^
8652 pm_compile_alias_method_node(iseq, (const pm_alias_method_node_t *) node, &location, ret, popped, scope_node);
8653 return;
8654 case PM_AND_NODE:
8655 // a and b
8656 // ^^^^^^^
8657 pm_compile_and_node(iseq, (const pm_and_node_t *) node, &location, ret, popped, scope_node);
8658 return;
8659 case PM_ARGUMENTS_NODE: {
8660 // break foo
8661 // ^^^
8662 //
8663 // These are ArgumentsNodes that are not compiled directly by their
8664 // parent call nodes, used in the cases of NextNodes, ReturnNodes, and
8665 // BreakNodes. They can create an array like ArrayNode.
8666 const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node;
8667 const pm_node_list_t *elements = &cast->arguments;
8668
8669 if (elements->size == 1) {
8670 // If we are only returning a single element through one of the jump
8671 // nodes, then we will only compile that node directly.
8672 PM_COMPILE(elements->nodes[0]);
8673 }
8674 else {
8675 pm_compile_array_node(iseq, (const pm_node_t *) cast, elements, &location, ret, popped, scope_node);
8676 }
8677 return;
8678 }
8679 case PM_ARRAY_NODE: {
8680 // [foo, bar, baz]
8681 // ^^^^^^^^^^^^^^^
8682 const pm_array_node_t *cast = (const pm_array_node_t *) node;
8683 pm_compile_array_node(iseq, (const pm_node_t *) cast, &cast->elements, &location, ret, popped, scope_node);
8684 return;
8685 }
8686 case PM_ASSOC_NODE: {
8687 // { foo: 1 }
8688 // ^^^^^^
8689 //
8690 // foo(bar: 1)
8691 // ^^^^^^
8692 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node;
8693
8694 PM_COMPILE(cast->key);
8695 PM_COMPILE(cast->value);
8696
8697 return;
8698 }
8699 case PM_ASSOC_SPLAT_NODE: {
8700 // { **foo }
8701 // ^^^^^
8702 //
8703 // def foo(**); bar(**); end
8704 // ^^
8705 const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node;
8706
8707 if (cast->value != NULL) {
8708 PM_COMPILE(cast->value);
8709 }
8710 else if (!popped) {
8711 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_POW, 0);
8712 PUSH_GETLOCAL(ret, location, index.index, index.level);
8713 }
8714
8715 return;
8716 }
8717 case PM_BACK_REFERENCE_READ_NODE: {
8718 // $+
8719 // ^^
8720 if (!popped) {
8721 const pm_back_reference_read_node_t *cast = (const pm_back_reference_read_node_t *) node;
8722 VALUE backref = pm_compile_back_reference_ref(cast);
8723
8724 PUSH_INSN2(ret, location, getspecial, INT2FIX(1), backref);
8725 }
8726 return;
8727 }
8728 case PM_BEGIN_NODE: {
8729 // begin end
8730 // ^^^^^^^^^
8731 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
8732
8733 if (cast->ensure_clause) {
8734 // Compiling the ensure clause will compile the rescue clause (if
8735 // there is one), which will compile the begin statements.
8736 pm_compile_ensure(iseq, cast, &location, ret, popped, scope_node);
8737 }
8738 else if (cast->rescue_clause) {
8739 // Compiling rescue will compile begin statements (if applicable).
8740 pm_compile_rescue(iseq, cast, &location, ret, popped, scope_node);
8741 }
8742 else {
8743 // If there is neither ensure or rescue, the just compile the
8744 // statements.
8745 if (cast->statements != NULL) {
8746 PM_COMPILE((const pm_node_t *) cast->statements);
8747 }
8748 else if (!popped) {
8749 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
8750 }
8751 }
8752 return;
8753 }
8754 case PM_BLOCK_ARGUMENT_NODE: {
8755 // foo(&bar)
8756 // ^^^^
8757 const pm_block_argument_node_t *cast = (const pm_block_argument_node_t *) node;
8758
8759 if (cast->expression != NULL) {
8760 PM_COMPILE(cast->expression);
8761 }
8762 else {
8763 // If there's no expression, this must be block forwarding.
8764 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_AND, 0);
8765 PUSH_INSN2(ret, location, getblockparamproxy, INT2FIX(local_index.index + VM_ENV_DATA_SIZE - 1), INT2FIX(local_index.level));
8766 }
8767 return;
8768 }
8769 case PM_BREAK_NODE:
8770 // break
8771 // ^^^^^
8772 //
8773 // break foo
8774 // ^^^^^^^^^
8775 pm_compile_break_node(iseq, (const pm_break_node_t *) node, &location, ret, popped, scope_node);
8776 return;
8777 case PM_CALL_NODE:
8778 // foo
8779 // ^^^
8780 //
8781 // foo.bar
8782 // ^^^^^^^
8783 //
8784 // foo.bar() {}
8785 // ^^^^^^^^^^^^
8786 pm_compile_call_node(iseq, (const pm_call_node_t *) node, ret, popped, scope_node);
8787 return;
8788 case PM_CALL_AND_WRITE_NODE: {
8789 // foo.bar &&= baz
8790 // ^^^^^^^^^^^^^^^
8791 const pm_call_and_write_node_t *cast = (const pm_call_and_write_node_t *) node;
8792 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);
8793 return;
8794 }
8795 case PM_CALL_OR_WRITE_NODE: {
8796 // foo.bar ||= baz
8797 // ^^^^^^^^^^^^^^^
8798 const pm_call_or_write_node_t *cast = (const pm_call_or_write_node_t *) node;
8799 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);
8800 return;
8801 }
8802 case PM_CALL_OPERATOR_WRITE_NODE:
8803 // foo.bar += baz
8804 // ^^^^^^^^^^^^^^^
8805 //
8806 // Call operator writes occur when you have a call node on the left-hand
8807 // side of a write operator that is not `=`. As an example,
8808 // `foo.bar *= 1`. This breaks down to caching the receiver on the
8809 // stack and then performing three method calls, one to read the value,
8810 // one to compute the result, and one to write the result back to the
8811 // receiver.
8812 pm_compile_call_operator_write_node(iseq, (const pm_call_operator_write_node_t *) node, &location, ret, popped, scope_node);
8813 return;
8814 case PM_CASE_NODE:
8815 // case foo; when bar; end
8816 // ^^^^^^^^^^^^^^^^^^^^^^^
8817 pm_compile_case_node(iseq, (const pm_case_node_t *) node, &location, ret, popped, scope_node);
8818 return;
8819 case PM_CASE_MATCH_NODE:
8820 // case foo; in bar; end
8821 // ^^^^^^^^^^^^^^^^^^^^^
8822 //
8823 // If you use the `case` keyword to create a case match node, it will
8824 // match against all of the `in` clauses until it finds one that
8825 // matches. If it doesn't find one, it can optionally fall back to an
8826 // `else` clause. If none is present and a match wasn't found, it will
8827 // raise an appropriate error.
8828 pm_compile_case_match_node(iseq, (const pm_case_match_node_t *) node, &location, ret, popped, scope_node);
8829 return;
8830 case PM_CLASS_NODE: {
8831 // class Foo; end
8832 // ^^^^^^^^^^^^^^
8833 const pm_class_node_t *cast = (const pm_class_node_t *) node;
8834
8835 ID class_id = pm_constant_id_lookup(scope_node, cast->name);
8836 VALUE class_name = rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(class_id)));
8837
8838 pm_scope_node_t next_scope_node;
8839 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
8840
8841 const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(&next_scope_node, class_name, ISEQ_TYPE_CLASS, location.line);
8842 pm_scope_node_destroy(&next_scope_node);
8843
8844 // TODO: Once we merge constant path nodes correctly, fix this flag
8845 const int flags = VM_DEFINECLASS_TYPE_CLASS |
8846 (cast->superclass ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
8847 pm_compile_class_path(iseq, cast->constant_path, &location, ret, false, scope_node);
8848
8849 if (cast->superclass) {
8850 PM_COMPILE_NOT_POPPED(cast->superclass);
8851 }
8852 else {
8853 PUSH_INSN(ret, location, putnil);
8854 }
8855
8856 {
8857 VALUE operand = ID2SYM(class_id);
8858 PUSH_INSN3(ret, location, defineclass, operand, class_iseq, INT2FIX(flags));
8859 }
8860 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq);
8861
8862 if (popped) PUSH_INSN(ret, location, pop);
8863 return;
8864 }
8865 case PM_CLASS_VARIABLE_AND_WRITE_NODE: {
8866 // @@foo &&= bar
8867 // ^^^^^^^^^^^^^
8868 const pm_class_variable_and_write_node_t *cast = (const pm_class_variable_and_write_node_t *) node;
8869 LABEL *end_label = NEW_LABEL(location.line);
8870
8871 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
8872 VALUE name = ID2SYM(name_id);
8873
8874 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
8875 if (!popped) PUSH_INSN(ret, location, dup);
8876
8877 PUSH_INSNL(ret, location, branchunless, end_label);
8878 if (!popped) PUSH_INSN(ret, location, pop);
8879
8880 PM_COMPILE_NOT_POPPED(cast->value);
8881 if (!popped) PUSH_INSN(ret, location, dup);
8882
8883 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
8884 PUSH_LABEL(ret, end_label);
8885
8886 return;
8887 }
8888 case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: {
8889 // @@foo += bar
8890 // ^^^^^^^^^^^^
8891 const pm_class_variable_operator_write_node_t *cast = (const pm_class_variable_operator_write_node_t *) node;
8892
8893 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
8894 VALUE name = ID2SYM(name_id);
8895
8896 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
8897 PM_COMPILE_NOT_POPPED(cast->value);
8898
8899 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
8900 int flags = VM_CALL_ARGS_SIMPLE;
8901 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
8902
8903 if (!popped) PUSH_INSN(ret, location, dup);
8904 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
8905
8906 return;
8907 }
8908 case PM_CLASS_VARIABLE_OR_WRITE_NODE: {
8909 // @@foo ||= bar
8910 // ^^^^^^^^^^^^^
8911 const pm_class_variable_or_write_node_t *cast = (const pm_class_variable_or_write_node_t *) node;
8912 LABEL *end_label = NEW_LABEL(location.line);
8913 LABEL *start_label = NEW_LABEL(location.line);
8914
8915 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
8916 VALUE name = ID2SYM(name_id);
8917
8918 PUSH_INSN(ret, location, putnil);
8919 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CVAR), name, Qtrue);
8920 PUSH_INSNL(ret, location, branchunless, start_label);
8921
8922 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
8923 if (!popped) PUSH_INSN(ret, location, dup);
8924
8925 PUSH_INSNL(ret, location, branchif, end_label);
8926 if (!popped) PUSH_INSN(ret, location, pop);
8927
8928 PUSH_LABEL(ret, start_label);
8929 PM_COMPILE_NOT_POPPED(cast->value);
8930 if (!popped) PUSH_INSN(ret, location, dup);
8931
8932 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
8933 PUSH_LABEL(ret, end_label);
8934
8935 return;
8936 }
8937 case PM_CLASS_VARIABLE_READ_NODE: {
8938 // @@foo
8939 // ^^^^^
8940 if (!popped) {
8941 const pm_class_variable_read_node_t *cast = (const pm_class_variable_read_node_t *) node;
8942 ID name = pm_constant_id_lookup(scope_node, cast->name);
8943 PUSH_INSN2(ret, location, getclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
8944 }
8945 return;
8946 }
8947 case PM_CLASS_VARIABLE_WRITE_NODE: {
8948 // @@foo = 1
8949 // ^^^^^^^^^
8950 const pm_class_variable_write_node_t *cast = (const pm_class_variable_write_node_t *) node;
8951 PM_COMPILE_NOT_POPPED(cast->value);
8952 if (!popped) PUSH_INSN(ret, location, dup);
8953
8954 ID name = pm_constant_id_lookup(scope_node, cast->name);
8955 PUSH_INSN2(ret, location, setclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
8956
8957 return;
8958 }
8959 case PM_CONSTANT_PATH_NODE: {
8960 // Foo::Bar
8961 // ^^^^^^^^
8962 VALUE parts;
8963
8964 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache && ((parts = pm_constant_path_parts(node, scope_node)) != Qnil)) {
8965 ISEQ_BODY(iseq)->ic_size++;
8966 PUSH_INSN1(ret, location, opt_getconstant_path, parts);
8967 }
8968 else {
8969 DECL_ANCHOR(prefix);
8970 DECL_ANCHOR(body);
8971
8972 pm_compile_constant_path(iseq, node, prefix, body, popped, scope_node);
8973 if (LIST_INSN_SIZE_ZERO(prefix)) {
8974 PUSH_INSN(ret, location, putnil);
8975 }
8976 else {
8977 PUSH_SEQ(ret, prefix);
8978 }
8979
8980 PUSH_SEQ(ret, body);
8981 }
8982
8983 if (popped) PUSH_INSN(ret, location, pop);
8984 return;
8985 }
8986 case PM_CONSTANT_PATH_AND_WRITE_NODE: {
8987 // Foo::Bar &&= baz
8988 // ^^^^^^^^^^^^^^^^
8989 const pm_constant_path_and_write_node_t *cast = (const pm_constant_path_and_write_node_t *) node;
8990 pm_compile_constant_path_and_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
8991 return;
8992 }
8993 case PM_CONSTANT_PATH_OR_WRITE_NODE: {
8994 // Foo::Bar ||= baz
8995 // ^^^^^^^^^^^^^^^^
8996 const pm_constant_path_or_write_node_t *cast = (const pm_constant_path_or_write_node_t *) node;
8997 pm_compile_constant_path_or_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
8998 return;
8999 }
9000 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: {
9001 // Foo::Bar += baz
9002 // ^^^^^^^^^^^^^^^
9003 const pm_constant_path_operator_write_node_t *cast = (const pm_constant_path_operator_write_node_t *) node;
9004 pm_compile_constant_path_operator_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9005 return;
9006 }
9007 case PM_CONSTANT_PATH_WRITE_NODE: {
9008 // Foo::Bar = 1
9009 // ^^^^^^^^^^^^
9010 const pm_constant_path_write_node_t *cast = (const pm_constant_path_write_node_t *) node;
9011 pm_compile_constant_path_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9012 return;
9013 }
9014 case PM_CONSTANT_READ_NODE: {
9015 // Foo
9016 // ^^^
9017 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
9018 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9019
9020 pm_compile_constant_read(iseq, name, &cast->base.location, location.node_id, ret, scope_node);
9021 if (popped) PUSH_INSN(ret, location, pop);
9022
9023 return;
9024 }
9025 case PM_CONSTANT_AND_WRITE_NODE: {
9026 // Foo &&= bar
9027 // ^^^^^^^^^^^
9028 const pm_constant_and_write_node_t *cast = (const pm_constant_and_write_node_t *) node;
9029 pm_compile_constant_and_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9030 return;
9031 }
9032 case PM_CONSTANT_OR_WRITE_NODE: {
9033 // Foo ||= bar
9034 // ^^^^^^^^^^^
9035 const pm_constant_or_write_node_t *cast = (const pm_constant_or_write_node_t *) node;
9036 pm_compile_constant_or_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9037 return;
9038 }
9039 case PM_CONSTANT_OPERATOR_WRITE_NODE: {
9040 // Foo += bar
9041 // ^^^^^^^^^^
9042 const pm_constant_operator_write_node_t *cast = (const pm_constant_operator_write_node_t *) node;
9043 pm_compile_constant_operator_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9044 return;
9045 }
9046 case PM_CONSTANT_WRITE_NODE: {
9047 // Foo = 1
9048 // ^^^^^^^
9049 const pm_constant_write_node_t *cast = (const pm_constant_write_node_t *) node;
9050 pm_compile_constant_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9051 return;
9052 }
9053 case PM_DEF_NODE: {
9054 // def foo; end
9055 // ^^^^^^^^^^^^
9056 //
9057 // def self.foo; end
9058 // ^^^^^^^^^^^^^^^^^
9059 const pm_def_node_t *cast = (const pm_def_node_t *) node;
9060 ID method_name = pm_constant_id_lookup(scope_node, cast->name);
9061
9062 pm_scope_node_t next_scope_node;
9063 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9064
9065 rb_iseq_t *method_iseq = NEW_ISEQ(&next_scope_node, rb_id2str(method_name), ISEQ_TYPE_METHOD, location.line);
9066 pm_scope_node_destroy(&next_scope_node);
9067
9068 if (cast->receiver) {
9069 PM_COMPILE_NOT_POPPED(cast->receiver);
9070 PUSH_INSN2(ret, location, definesmethod, ID2SYM(method_name), method_iseq);
9071 }
9072 else {
9073 PUSH_INSN2(ret, location, definemethod, ID2SYM(method_name), method_iseq);
9074 }
9075 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) method_iseq);
9076
9077 if (!popped) {
9078 PUSH_INSN1(ret, location, putobject, ID2SYM(method_name));
9079 }
9080
9081 return;
9082 }
9083 case PM_DEFINED_NODE: {
9084 // defined?(a)
9085 // ^^^^^^^^^^^
9086 const pm_defined_node_t *cast = (const pm_defined_node_t *) node;
9087 pm_compile_defined_expr(iseq, cast->value, &location, ret, popped, scope_node, false);
9088 return;
9089 }
9090 case PM_EMBEDDED_STATEMENTS_NODE: {
9091 // "foo #{bar}"
9092 // ^^^^^^
9093 const pm_embedded_statements_node_t *cast = (const pm_embedded_statements_node_t *) node;
9094
9095 if (cast->statements != NULL) {
9096 PM_COMPILE((const pm_node_t *) (cast->statements));
9097 }
9098 else {
9099 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
9100 }
9101
9102 if (popped) PUSH_INSN(ret, location, pop);
9103 return;
9104 }
9105 case PM_EMBEDDED_VARIABLE_NODE: {
9106 // "foo #@bar"
9107 // ^^^^^
9108 const pm_embedded_variable_node_t *cast = (const pm_embedded_variable_node_t *) node;
9109 PM_COMPILE(cast->variable);
9110 return;
9111 }
9112 case PM_FALSE_NODE: {
9113 // false
9114 // ^^^^^
9115 if (!popped) {
9116 PUSH_INSN1(ret, location, putobject, Qfalse);
9117 }
9118 return;
9119 }
9120 case PM_ENSURE_NODE: {
9121 const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node;
9122
9123 if (cast->statements != NULL) {
9124 PM_COMPILE((const pm_node_t *) cast->statements);
9125 }
9126
9127 return;
9128 }
9129 case PM_ELSE_NODE: {
9130 // if foo then bar else baz end
9131 // ^^^^^^^^^^^^
9132 const pm_else_node_t *cast = (const pm_else_node_t *) node;
9133
9134 if (cast->statements != NULL) {
9135 PM_COMPILE((const pm_node_t *) cast->statements);
9136 }
9137 else if (!popped) {
9138 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
9139 }
9140
9141 return;
9142 }
9143 case PM_FLIP_FLOP_NODE: {
9144 // if foo .. bar; end
9145 // ^^^^^^^^^^
9146 const pm_flip_flop_node_t *cast = (const pm_flip_flop_node_t *) node;
9147
9148 LABEL *final_label = NEW_LABEL(location.line);
9149 LABEL *then_label = NEW_LABEL(location.line);
9150 LABEL *else_label = NEW_LABEL(location.line);
9151
9152 pm_compile_flip_flop(cast, else_label, then_label, iseq, location.line, ret, popped, scope_node);
9153
9154 PUSH_LABEL(ret, then_label);
9155 PUSH_INSN1(ret, location, putobject, Qtrue);
9156 PUSH_INSNL(ret, location, jump, final_label);
9157 PUSH_LABEL(ret, else_label);
9158 PUSH_INSN1(ret, location, putobject, Qfalse);
9159 PUSH_LABEL(ret, final_label);
9160
9161 return;
9162 }
9163 case PM_FLOAT_NODE: {
9164 // 1.0
9165 // ^^^
9166 if (!popped) {
9167 VALUE operand = parse_float((const pm_float_node_t *) node);
9168 PUSH_INSN1(ret, location, putobject, operand);
9169 }
9170 return;
9171 }
9172 case PM_FOR_NODE: {
9173 // for foo in bar do end
9174 // ^^^^^^^^^^^^^^^^^^^^^
9175 const pm_for_node_t *cast = (const pm_for_node_t *) node;
9176
9177 LABEL *retry_label = NEW_LABEL(location.line);
9178 LABEL *retry_end_l = NEW_LABEL(location.line);
9179
9180 // First, compile the collection that we're going to be iterating over.
9181 PUSH_LABEL(ret, retry_label);
9182 PM_COMPILE_NOT_POPPED(cast->collection);
9183
9184 // Next, create the new scope that is going to contain the block that
9185 // will be passed to the each method.
9186 pm_scope_node_t next_scope_node;
9187 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9188
9189 const rb_iseq_t *child_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location.line);
9190 pm_scope_node_destroy(&next_scope_node);
9191
9192 const rb_iseq_t *prev_block = ISEQ_COMPILE_DATA(iseq)->current_block;
9193 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
9194
9195 // Now, create the method call to each that will be used to iterate over
9196 // the collection, and pass the newly created iseq as the block.
9197 PUSH_SEND_WITH_BLOCK(ret, location, idEach, INT2FIX(0), child_iseq);
9198 pm_compile_retry_end_label(iseq, ret, retry_end_l);
9199
9200 if (popped) PUSH_INSN(ret, location, pop);
9201 ISEQ_COMPILE_DATA(iseq)->current_block = prev_block;
9202 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
9203 return;
9204 }
9205 case PM_FORWARDING_ARGUMENTS_NODE:
9206 rb_bug("Cannot compile a ForwardingArgumentsNode directly\n");
9207 return;
9208 case PM_FORWARDING_SUPER_NODE:
9209 // super
9210 // ^^^^^
9211 //
9212 // super {}
9213 // ^^^^^^^^
9214 pm_compile_forwarding_super_node(iseq, (const pm_forwarding_super_node_t *) node, &location, ret, popped, scope_node);
9215 return;
9216 case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: {
9217 // $foo &&= bar
9218 // ^^^^^^^^^^^^
9219 const pm_global_variable_and_write_node_t *cast = (const pm_global_variable_and_write_node_t *) node;
9220 LABEL *end_label = NEW_LABEL(location.line);
9221
9222 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9223 PUSH_INSN1(ret, location, getglobal, name);
9224 if (!popped) PUSH_INSN(ret, location, dup);
9225
9226 PUSH_INSNL(ret, location, branchunless, end_label);
9227 if (!popped) PUSH_INSN(ret, location, pop);
9228
9229 PM_COMPILE_NOT_POPPED(cast->value);
9230 if (!popped) PUSH_INSN(ret, location, dup);
9231
9232 PUSH_INSN1(ret, location, setglobal, name);
9233 PUSH_LABEL(ret, end_label);
9234
9235 return;
9236 }
9237 case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: {
9238 // $foo += bar
9239 // ^^^^^^^^^^^
9240 const pm_global_variable_operator_write_node_t *cast = (const pm_global_variable_operator_write_node_t *) node;
9241
9242 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9243 PUSH_INSN1(ret, location, getglobal, name);
9244 PM_COMPILE_NOT_POPPED(cast->value);
9245
9246 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9247 int flags = VM_CALL_ARGS_SIMPLE;
9248 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
9249
9250 if (!popped) PUSH_INSN(ret, location, dup);
9251 PUSH_INSN1(ret, location, setglobal, name);
9252
9253 return;
9254 }
9255 case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: {
9256 // $foo ||= bar
9257 // ^^^^^^^^^^^^
9258 const pm_global_variable_or_write_node_t *cast = (const pm_global_variable_or_write_node_t *) node;
9259 LABEL *set_label = NEW_LABEL(location.line);
9260 LABEL *end_label = NEW_LABEL(location.line);
9261
9262 PUSH_INSN(ret, location, putnil);
9263 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9264
9265 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_GVAR), name, Qtrue);
9266 PUSH_INSNL(ret, location, branchunless, set_label);
9267
9268 PUSH_INSN1(ret, location, getglobal, name);
9269 if (!popped) PUSH_INSN(ret, location, dup);
9270
9271 PUSH_INSNL(ret, location, branchif, end_label);
9272 if (!popped) PUSH_INSN(ret, location, pop);
9273
9274 PUSH_LABEL(ret, set_label);
9275 PM_COMPILE_NOT_POPPED(cast->value);
9276 if (!popped) PUSH_INSN(ret, location, dup);
9277
9278 PUSH_INSN1(ret, location, setglobal, name);
9279 PUSH_LABEL(ret, end_label);
9280
9281 return;
9282 }
9283 case PM_GLOBAL_VARIABLE_READ_NODE: {
9284 // $foo
9285 // ^^^^
9286 const pm_global_variable_read_node_t *cast = (const pm_global_variable_read_node_t *) node;
9287 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9288
9289 PUSH_INSN1(ret, location, getglobal, name);
9290 if (popped) PUSH_INSN(ret, location, pop);
9291
9292 return;
9293 }
9294 case PM_GLOBAL_VARIABLE_WRITE_NODE: {
9295 // $foo = 1
9296 // ^^^^^^^^
9297 const pm_global_variable_write_node_t *cast = (const pm_global_variable_write_node_t *) node;
9298 PM_COMPILE_NOT_POPPED(cast->value);
9299 if (!popped) PUSH_INSN(ret, location, dup);
9300
9301 ID name = pm_constant_id_lookup(scope_node, cast->name);
9302 PUSH_INSN1(ret, location, setglobal, ID2SYM(name));
9303
9304 return;
9305 }
9306 case PM_HASH_NODE: {
9307 // {}
9308 // ^^
9309 //
9310 // If every node in the hash is static, then we can compile the entire
9311 // hash now instead of later.
9312 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9313 // We're only going to compile this node if it's not popped. If it
9314 // is popped, then we know we don't need to do anything since it's
9315 // statically known.
9316 if (!popped) {
9317 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
9318
9319 if (cast->elements.size == 0) {
9320 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
9321 }
9322 else {
9323 VALUE value = pm_static_literal_value(iseq, node, scope_node);
9324 PUSH_INSN1(ret, location, duphash, value);
9325 RB_OBJ_WRITTEN(iseq, Qundef, value);
9326 }
9327 }
9328 }
9329 else {
9330 // Here since we know there are possible side-effects inside the
9331 // hash contents, we're going to build it entirely at runtime. We'll
9332 // do this by pushing all of the key-value pairs onto the stack and
9333 // then combining them with newhash.
9334 //
9335 // If this hash is popped, then this serves only to ensure we enact
9336 // all side-effects (like method calls) that are contained within
9337 // the hash contents.
9338 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
9339 const pm_node_list_t *elements = &cast->elements;
9340
9341 if (popped) {
9342 // If this hash is popped, then we can iterate through each
9343 // element and compile it. The result of each compilation will
9344 // only include the side effects of the element itself.
9345 for (size_t index = 0; index < elements->size; index++) {
9346 PM_COMPILE_POPPED(elements->nodes[index]);
9347 }
9348 }
9349 else {
9350 pm_compile_hash_elements(iseq, node, elements, 0, Qundef, false, ret, scope_node);
9351 }
9352 }
9353
9354 return;
9355 }
9356 case PM_IF_NODE: {
9357 // if foo then bar end
9358 // ^^^^^^^^^^^^^^^^^^^
9359 //
9360 // bar if foo
9361 // ^^^^^^^^^^
9362 //
9363 // foo ? bar : baz
9364 // ^^^^^^^^^^^^^^^
9365 const pm_if_node_t *cast = (const pm_if_node_t *) node;
9366 pm_compile_conditional(iseq, &location, PM_IF_NODE, (const pm_node_t *) cast, cast->statements, cast->subsequent, cast->predicate, ret, popped, scope_node);
9367 return;
9368 }
9369 case PM_IMAGINARY_NODE: {
9370 // 1i
9371 // ^^
9372 if (!popped) {
9373 VALUE operand = parse_imaginary((const pm_imaginary_node_t *) node);
9374 PUSH_INSN1(ret, location, putobject, operand);
9375 }
9376 return;
9377 }
9378 case PM_IMPLICIT_NODE: {
9379 // Implicit nodes mark places in the syntax tree where explicit syntax
9380 // was omitted, but implied. For example,
9381 //
9382 // { foo: }
9383 //
9384 // In this case a method call/local variable read is implied by virtue
9385 // of the missing value. To compile these nodes, we simply compile the
9386 // value that is implied, which is helpfully supplied by the parser.
9387 const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node;
9388 PM_COMPILE(cast->value);
9389 return;
9390 }
9391 case PM_IN_NODE: {
9392 // In nodes are handled by the case match node directly, so we should
9393 // never end up hitting them through this path.
9394 rb_bug("Should not ever enter an in node directly");
9395 return;
9396 }
9397 case PM_INDEX_OPERATOR_WRITE_NODE: {
9398 // foo[bar] += baz
9399 // ^^^^^^^^^^^^^^^
9400 const pm_index_operator_write_node_t *cast = (const pm_index_operator_write_node_t *) node;
9401 pm_compile_index_operator_write_node(iseq, cast, &location, ret, popped, scope_node);
9402 return;
9403 }
9404 case PM_INDEX_AND_WRITE_NODE: {
9405 // foo[bar] &&= baz
9406 // ^^^^^^^^^^^^^^^^
9407 const pm_index_and_write_node_t *cast = (const pm_index_and_write_node_t *) node;
9408 pm_compile_index_control_flow_write_node(iseq, node, cast->receiver, cast->arguments, cast->block, cast->value, &location, ret, popped, scope_node);
9409 return;
9410 }
9411 case PM_INDEX_OR_WRITE_NODE: {
9412 // foo[bar] ||= baz
9413 // ^^^^^^^^^^^^^^^^
9414 const pm_index_or_write_node_t *cast = (const pm_index_or_write_node_t *) node;
9415 pm_compile_index_control_flow_write_node(iseq, node, cast->receiver, cast->arguments, cast->block, cast->value, &location, ret, popped, scope_node);
9416 return;
9417 }
9418 case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: {
9419 // @foo &&= bar
9420 // ^^^^^^^^^^^^
9421 const pm_instance_variable_and_write_node_t *cast = (const pm_instance_variable_and_write_node_t *) node;
9422 LABEL *end_label = NEW_LABEL(location.line);
9423
9424 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9425 VALUE name = ID2SYM(name_id);
9426
9427 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9428 if (!popped) PUSH_INSN(ret, location, dup);
9429
9430 PUSH_INSNL(ret, location, branchunless, end_label);
9431 if (!popped) PUSH_INSN(ret, location, pop);
9432
9433 PM_COMPILE_NOT_POPPED(cast->value);
9434 if (!popped) PUSH_INSN(ret, location, dup);
9435
9436 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9437 PUSH_LABEL(ret, end_label);
9438
9439 return;
9440 }
9441 case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: {
9442 // @foo += bar
9443 // ^^^^^^^^^^^
9444 const pm_instance_variable_operator_write_node_t *cast = (const pm_instance_variable_operator_write_node_t *) node;
9445
9446 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9447 VALUE name = ID2SYM(name_id);
9448
9449 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9450 PM_COMPILE_NOT_POPPED(cast->value);
9451
9452 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9453 int flags = VM_CALL_ARGS_SIMPLE;
9454 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
9455
9456 if (!popped) PUSH_INSN(ret, location, dup);
9457 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9458
9459 return;
9460 }
9461 case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: {
9462 // @foo ||= bar
9463 // ^^^^^^^^^^^^
9464 const pm_instance_variable_or_write_node_t *cast = (const pm_instance_variable_or_write_node_t *) node;
9465 LABEL *end_label = NEW_LABEL(location.line);
9466
9467 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9468 VALUE name = ID2SYM(name_id);
9469
9470 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9471 if (!popped) PUSH_INSN(ret, location, dup);
9472
9473 PUSH_INSNL(ret, location, branchif, end_label);
9474 if (!popped) PUSH_INSN(ret, location, pop);
9475
9476 PM_COMPILE_NOT_POPPED(cast->value);
9477 if (!popped) PUSH_INSN(ret, location, dup);
9478
9479 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9480 PUSH_LABEL(ret, end_label);
9481
9482 return;
9483 }
9484 case PM_INSTANCE_VARIABLE_READ_NODE: {
9485 // @foo
9486 // ^^^^
9487 if (!popped) {
9488 const pm_instance_variable_read_node_t *cast = (const pm_instance_variable_read_node_t *) node;
9489 ID name = pm_constant_id_lookup(scope_node, cast->name);
9490 PUSH_INSN2(ret, location, getinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
9491 }
9492 return;
9493 }
9494 case PM_INSTANCE_VARIABLE_WRITE_NODE: {
9495 // @foo = 1
9496 // ^^^^^^^^
9497 const pm_instance_variable_write_node_t *cast = (const pm_instance_variable_write_node_t *) node;
9498 PM_COMPILE_NOT_POPPED(cast->value);
9499 if (!popped) PUSH_INSN(ret, location, dup);
9500
9501 ID name = pm_constant_id_lookup(scope_node, cast->name);
9502 PUSH_INSN2(ret, location, setinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
9503
9504 return;
9505 }
9506 case PM_INTEGER_NODE: {
9507 // 1
9508 // ^
9509 if (!popped) {
9510 VALUE operand = parse_integer((const pm_integer_node_t *) node);
9511 PUSH_INSN1(ret, location, putobject, operand);
9512 }
9513 return;
9514 }
9515 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: {
9516 // if /foo #{bar}/ then end
9517 // ^^^^^^^^^^^^
9518 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9519 if (!popped) {
9520 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9521 PUSH_INSN1(ret, location, putobject, regexp);
9522 }
9523 }
9524 else {
9525 pm_compile_regexp_dynamic(iseq, node, &((const pm_interpolated_match_last_line_node_t *) node)->parts, &location, ret, popped, scope_node);
9526 }
9527
9528 PUSH_INSN1(ret, location, getglobal, rb_id2sym(idLASTLINE));
9529 PUSH_SEND(ret, location, idEqTilde, INT2NUM(1));
9530 if (popped) PUSH_INSN(ret, location, pop);
9531
9532 return;
9533 }
9534 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
9535 // /foo #{bar}/
9536 // ^^^^^^^^^^^^
9537 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) {
9538 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
9539 const rb_iseq_t *block_iseq = NULL;
9540 int ise_index = ISEQ_BODY(iseq)->ise_size++;
9541
9542 pm_scope_node_t next_scope_node;
9543 pm_scope_node_init(node, &next_scope_node, scope_node);
9544
9545 block_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, location.line);
9546 pm_scope_node_destroy(&next_scope_node);
9547
9548 ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
9549 PUSH_INSN2(ret, location, once, block_iseq, INT2FIX(ise_index));
9550 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
9551
9552 if (popped) PUSH_INSN(ret, location, pop);
9553 return;
9554 }
9555
9556 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9557 if (!popped) {
9558 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9559 PUSH_INSN1(ret, location, putobject, regexp);
9560 }
9561 }
9562 else {
9563 pm_compile_regexp_dynamic(iseq, node, &((const pm_interpolated_regular_expression_node_t *) node)->parts, &location, ret, popped, scope_node);
9564 if (popped) PUSH_INSN(ret, location, pop);
9565 }
9566
9567 return;
9568 }
9569 case PM_INTERPOLATED_STRING_NODE: {
9570 // "foo #{bar}"
9571 // ^^^^^^^^^^^^
9572 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9573 if (!popped) {
9574 VALUE string = pm_static_literal_value(iseq, node, scope_node);
9575
9576 if (PM_NODE_FLAG_P(node, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN)) {
9577 PUSH_INSN1(ret, location, putobject, string);
9578 }
9579 else if (PM_NODE_FLAG_P(node, PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) {
9580 PUSH_INSN1(ret, location, putstring, string);
9581 }
9582 else {
9583 PUSH_INSN1(ret, location, putchilledstring, string);
9584 }
9585 }
9586 }
9587 else {
9588 const pm_interpolated_string_node_t *cast = (const pm_interpolated_string_node_t *) node;
9589 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL, PM_NODE_FLAG_P(cast, PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE), PM_NODE_FLAG_P(cast, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN));
9590 if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9591 if (popped) PUSH_INSN(ret, location, pop);
9592 }
9593
9594 return;
9595 }
9596 case PM_INTERPOLATED_SYMBOL_NODE: {
9597 // :"foo #{bar}"
9598 // ^^^^^^^^^^^^^
9599 const pm_interpolated_symbol_node_t *cast = (const pm_interpolated_symbol_node_t *) node;
9600 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL, false, false);
9601
9602 if (length > 1) {
9603 PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9604 }
9605
9606 if (!popped) {
9607 PUSH_INSN(ret, location, intern);
9608 }
9609 else {
9610 PUSH_INSN(ret, location, pop);
9611 }
9612
9613 return;
9614 }
9615 case PM_INTERPOLATED_X_STRING_NODE: {
9616 // `foo #{bar}`
9617 // ^^^^^^^^^^^^
9618 const pm_interpolated_x_string_node_t *cast = (const pm_interpolated_x_string_node_t *) node;
9619
9620 PUSH_INSN(ret, location, putself);
9621
9622 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, false, scope_node, NULL, NULL, false, false);
9623 if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9624
9625 PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
9626 if (popped) PUSH_INSN(ret, location, pop);
9627
9628 return;
9629 }
9630 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
9631 // -> { it }
9632 // ^^
9633 if (!popped) {
9634 pm_scope_node_t *current_scope_node = scope_node;
9635 int level = 0;
9636
9637 while (current_scope_node) {
9638 if (current_scope_node->parameters && PM_NODE_TYPE_P(current_scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
9639 PUSH_GETLOCAL(ret, location, current_scope_node->local_table_for_iseq_size, level);
9640 return;
9641 }
9642
9643 current_scope_node = current_scope_node->previous;
9644 level++;
9645 }
9646 rb_bug("Local `it` does not exist");
9647 }
9648
9649 return;
9650 }
9651 case PM_KEYWORD_HASH_NODE: {
9652 // foo(bar: baz)
9653 // ^^^^^^^^
9654 const pm_keyword_hash_node_t *cast = (const pm_keyword_hash_node_t *) node;
9655 const pm_node_list_t *elements = &cast->elements;
9656
9657 const pm_node_t *element;
9658 PM_NODE_LIST_FOREACH(elements, index, element) {
9659 PM_COMPILE(element);
9660 }
9661
9662 if (!popped) PUSH_INSN1(ret, location, newhash, INT2FIX(elements->size * 2));
9663 return;
9664 }
9665 case PM_LAMBDA_NODE: {
9666 // -> {}
9667 // ^^^^^
9668 const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node;
9669
9670 pm_scope_node_t next_scope_node;
9671 pm_scope_node_init(node, &next_scope_node, scope_node);
9672
9673 int opening_lineno = pm_location_line_number(parser, &cast->opening_loc);
9674 const rb_iseq_t *block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, opening_lineno);
9675 pm_scope_node_destroy(&next_scope_node);
9676
9677 VALUE argc = INT2FIX(0);
9678 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
9679 PUSH_CALL_WITH_BLOCK(ret, location, idLambda, argc, block);
9680 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) block);
9681
9682 if (popped) PUSH_INSN(ret, location, pop);
9683 return;
9684 }
9685 case PM_LOCAL_VARIABLE_AND_WRITE_NODE: {
9686 // foo &&= bar
9687 // ^^^^^^^^^^^
9688 const pm_local_variable_and_write_node_t *cast = (const pm_local_variable_and_write_node_t *) node;
9689 LABEL *end_label = NEW_LABEL(location.line);
9690
9691 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9692 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9693 if (!popped) PUSH_INSN(ret, location, dup);
9694
9695 PUSH_INSNL(ret, location, branchunless, end_label);
9696 if (!popped) PUSH_INSN(ret, location, pop);
9697
9698 PM_COMPILE_NOT_POPPED(cast->value);
9699 if (!popped) PUSH_INSN(ret, location, dup);
9700
9701 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9702 PUSH_LABEL(ret, end_label);
9703
9704 return;
9705 }
9706 case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: {
9707 // foo += bar
9708 // ^^^^^^^^^^
9709 const pm_local_variable_operator_write_node_t *cast = (const pm_local_variable_operator_write_node_t *) node;
9710
9711 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9712 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9713
9714 PM_COMPILE_NOT_POPPED(cast->value);
9715
9716 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9717 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
9718
9719 if (!popped) PUSH_INSN(ret, location, dup);
9720 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9721
9722 return;
9723 }
9724 case PM_LOCAL_VARIABLE_OR_WRITE_NODE: {
9725 // foo ||= bar
9726 // ^^^^^^^^^^^
9727 const pm_local_variable_or_write_node_t *cast = (const pm_local_variable_or_write_node_t *) node;
9728
9729 LABEL *set_label = NEW_LABEL(location.line);
9730 LABEL *end_label = NEW_LABEL(location.line);
9731
9732 PUSH_INSN1(ret, location, putobject, Qtrue);
9733 PUSH_INSNL(ret, location, branchunless, set_label);
9734
9735 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9736 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9737 if (!popped) PUSH_INSN(ret, location, dup);
9738
9739 PUSH_INSNL(ret, location, branchif, end_label);
9740 if (!popped) PUSH_INSN(ret, location, pop);
9741
9742 PUSH_LABEL(ret, set_label);
9743 PM_COMPILE_NOT_POPPED(cast->value);
9744 if (!popped) PUSH_INSN(ret, location, dup);
9745
9746 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9747 PUSH_LABEL(ret, end_label);
9748
9749 return;
9750 }
9751 case PM_LOCAL_VARIABLE_READ_NODE: {
9752 // foo
9753 // ^^^
9754 if (!popped) {
9755 const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) node;
9756 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9757 PUSH_GETLOCAL(ret, location, index.index, index.level);
9758 }
9759
9760 return;
9761 }
9762 case PM_LOCAL_VARIABLE_WRITE_NODE: {
9763 // foo = 1
9764 // ^^^^^^^
9765 const pm_local_variable_write_node_t *cast = (const pm_local_variable_write_node_t *) node;
9766 PM_COMPILE_NOT_POPPED(cast->value);
9767 if (!popped) PUSH_INSN(ret, location, dup);
9768
9769 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9770 PUSH_SETLOCAL(ret, location, index.index, index.level);
9771 return;
9772 }
9773 case PM_MATCH_LAST_LINE_NODE: {
9774 // if /foo/ then end
9775 // ^^^^^
9776 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9777
9778 PUSH_INSN1(ret, location, putobject, regexp);
9779 PUSH_INSN2(ret, location, getspecial, INT2FIX(0), INT2FIX(0));
9780 PUSH_SEND(ret, location, idEqTilde, INT2NUM(1));
9781 if (popped) PUSH_INSN(ret, location, pop);
9782
9783 return;
9784 }
9785 case PM_MATCH_PREDICATE_NODE: {
9786 // foo in bar
9787 // ^^^^^^^^^^
9788 const pm_match_predicate_node_t *cast = (const pm_match_predicate_node_t *) node;
9789
9790 // First, allocate some stack space for the cached return value of any
9791 // calls to #deconstruct.
9792 PUSH_INSN(ret, location, putnil);
9793
9794 // Next, compile the expression that we're going to match against.
9795 PM_COMPILE_NOT_POPPED(cast->value);
9796 PUSH_INSN(ret, location, dup);
9797
9798 // Now compile the pattern that is going to be used to match against the
9799 // expression.
9800 LABEL *matched_label = NEW_LABEL(location.line);
9801 LABEL *unmatched_label = NEW_LABEL(location.line);
9802 LABEL *done_label = NEW_LABEL(location.line);
9803 pm_compile_pattern(iseq, scope_node, cast->pattern, ret, matched_label, unmatched_label, false, false, true, 2);
9804
9805 // If the pattern did not match, then compile the necessary instructions
9806 // to handle pushing false onto the stack, then jump to the end.
9807 PUSH_LABEL(ret, unmatched_label);
9808 PUSH_INSN(ret, location, pop);
9809 PUSH_INSN(ret, location, pop);
9810
9811 if (!popped) PUSH_INSN1(ret, location, putobject, Qfalse);
9812 PUSH_INSNL(ret, location, jump, done_label);
9813 PUSH_INSN(ret, location, putnil);
9814
9815 // If the pattern did match, then compile the necessary instructions to
9816 // handle pushing true onto the stack, then jump to the end.
9817 PUSH_LABEL(ret, matched_label);
9818 PUSH_INSN1(ret, location, adjuststack, INT2FIX(2));
9819 if (!popped) PUSH_INSN1(ret, location, putobject, Qtrue);
9820 PUSH_INSNL(ret, location, jump, done_label);
9821
9822 PUSH_LABEL(ret, done_label);
9823 return;
9824 }
9825 case PM_MATCH_REQUIRED_NODE:
9826 // foo => bar
9827 // ^^^^^^^^^^
9828 //
9829 // A match required node represents pattern matching against a single
9830 // pattern using the => operator. For example,
9831 //
9832 // foo => bar
9833 //
9834 // This is somewhat analogous to compiling a case match statement with a
9835 // single pattern. In both cases, if the pattern fails it should
9836 // immediately raise an error.
9837 pm_compile_match_required_node(iseq, (const pm_match_required_node_t *) node, &location, ret, popped, scope_node);
9838 return;
9839 case PM_MATCH_WRITE_NODE:
9840 // /(?<foo>foo)/ =~ bar
9841 // ^^^^^^^^^^^^^^^^^^^^
9842 //
9843 // Match write nodes are specialized call nodes that have a regular
9844 // expression with valid named capture groups on the left, the =~
9845 // operator, and some value on the right. The nodes themselves simply
9846 // wrap the call with the local variable targets that will be written
9847 // when the call is executed.
9848 pm_compile_match_write_node(iseq, (const pm_match_write_node_t *) node, &location, ret, popped, scope_node);
9849 return;
9850 case PM_MISSING_NODE:
9851 rb_bug("A pm_missing_node_t should not exist in prism's AST.");
9852 return;
9853 case PM_MODULE_NODE: {
9854 // module Foo; end
9855 // ^^^^^^^^^^^^^^^
9856 const pm_module_node_t *cast = (const pm_module_node_t *) node;
9857
9858 ID module_id = pm_constant_id_lookup(scope_node, cast->name);
9859 VALUE module_name = rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(module_id)));
9860
9861 pm_scope_node_t next_scope_node;
9862 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9863
9864 const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(&next_scope_node, module_name, ISEQ_TYPE_CLASS, location.line);
9865 pm_scope_node_destroy(&next_scope_node);
9866
9867 const int flags = VM_DEFINECLASS_TYPE_MODULE | pm_compile_class_path(iseq, cast->constant_path, &location, ret, false, scope_node);
9868 PUSH_INSN(ret, location, putnil);
9869 PUSH_INSN3(ret, location, defineclass, ID2SYM(module_id), module_iseq, INT2FIX(flags));
9870 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) module_iseq);
9871
9872 if (popped) PUSH_INSN(ret, location, pop);
9873 return;
9874 }
9875 case PM_REQUIRED_PARAMETER_NODE: {
9876 // def foo(bar); end
9877 // ^^^
9878 const pm_required_parameter_node_t *cast = (const pm_required_parameter_node_t *) node;
9879 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, 0);
9880
9881 PUSH_SETLOCAL(ret, location, index.index, index.level);
9882 return;
9883 }
9884 case PM_MULTI_WRITE_NODE: {
9885 // foo, bar = baz
9886 // ^^^^^^^^^^^^^^
9887 //
9888 // A multi write node represents writing to multiple values using an =
9889 // operator. Importantly these nodes are only parsed when the left-hand
9890 // side of the operator has multiple targets. The right-hand side of the
9891 // operator having multiple targets represents an implicit array
9892 // instead.
9893 const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node;
9894
9895 DECL_ANCHOR(writes);
9896 DECL_ANCHOR(cleanup);
9897
9898 pm_multi_target_state_t state = { 0 };
9899 state.position = popped ? 0 : 1;
9900 pm_compile_multi_target_node(iseq, node, ret, writes, cleanup, scope_node, &state);
9901
9902 PM_COMPILE_NOT_POPPED(cast->value);
9903 if (!popped) PUSH_INSN(ret, location, dup);
9904
9905 PUSH_SEQ(ret, writes);
9906 if (!popped && state.stack_size >= 1) {
9907 // Make sure the value on the right-hand side of the = operator is
9908 // being returned before we pop the parent expressions.
9909 PUSH_INSN1(ret, location, setn, INT2FIX(state.stack_size));
9910 }
9911
9912 // Now, we need to go back and modify the topn instructions in order to
9913 // ensure they can correctly retrieve the parent expressions.
9914 pm_multi_target_state_update(&state);
9915
9916 PUSH_SEQ(ret, cleanup);
9917 return;
9918 }
9919 case PM_NEXT_NODE:
9920 // next
9921 // ^^^^
9922 //
9923 // next foo
9924 // ^^^^^^^^
9925 pm_compile_next_node(iseq, (const pm_next_node_t *) node, &location, ret, popped, scope_node);
9926 return;
9927 case PM_NIL_NODE: {
9928 // nil
9929 // ^^^
9930 if (!popped) {
9931 PUSH_INSN(ret, location, putnil);
9932 }
9933
9934 return;
9935 }
9936 case PM_NO_KEYWORDS_PARAMETER_NODE: {
9937 // def foo(**nil); end
9938 // ^^^^^
9939 ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg = TRUE;
9940 return;
9941 }
9942 case PM_NUMBERED_REFERENCE_READ_NODE: {
9943 // $1
9944 // ^^
9945 if (!popped) {
9946 const pm_numbered_reference_read_node_t *cast = (const pm_numbered_reference_read_node_t *) node;
9947
9948 if (cast->number != 0) {
9949 VALUE ref = pm_compile_numbered_reference_ref(cast);
9950 PUSH_INSN2(ret, location, getspecial, INT2FIX(1), ref);
9951 }
9952 else {
9953 PUSH_INSN(ret, location, putnil);
9954 }
9955 }
9956
9957 return;
9958 }
9959 case PM_OR_NODE: {
9960 // a or b
9961 // ^^^^^^
9962 const pm_or_node_t *cast = (const pm_or_node_t *) node;
9963
9964 LABEL *end_label = NEW_LABEL(location.line);
9965 PM_COMPILE_NOT_POPPED(cast->left);
9966
9967 if (!popped) PUSH_INSN(ret, location, dup);
9968 PUSH_INSNL(ret, location, branchif, end_label);
9969
9970 if (!popped) PUSH_INSN(ret, location, pop);
9971 PM_COMPILE(cast->right);
9972 PUSH_LABEL(ret, end_label);
9973
9974 return;
9975 }
9976 case PM_OPTIONAL_PARAMETER_NODE: {
9977 // def foo(bar = 1); end
9978 // ^^^^^^^
9979 const pm_optional_parameter_node_t *cast = (const pm_optional_parameter_node_t *) node;
9980 PM_COMPILE_NOT_POPPED(cast->value);
9981
9982 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, 0);
9983 PUSH_SETLOCAL(ret, location, index.index, index.level);
9984
9985 return;
9986 }
9987 case PM_PARENTHESES_NODE: {
9988 // ()
9989 // ^^
9990 //
9991 // (1)
9992 // ^^^
9993 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
9994
9995 if (cast->body != NULL) {
9996 PM_COMPILE(cast->body);
9997 }
9998 else if (!popped) {
9999 PUSH_INSN(ret, location, putnil);
10000 }
10001
10002 return;
10003 }
10004 case PM_PRE_EXECUTION_NODE: {
10005 // BEGIN {}
10006 // ^^^^^^^^
10007 const pm_pre_execution_node_t *cast = (const pm_pre_execution_node_t *) node;
10008
10009 LINK_ANCHOR *outer_pre = scope_node->pre_execution_anchor;
10010 RUBY_ASSERT(outer_pre != NULL);
10011
10012 // BEGIN{} nodes can be nested, so here we're going to do the same thing
10013 // that we did for the top-level compilation where we create two
10014 // anchors and then join them in the correct order into the resulting
10015 // anchor.
10016 DECL_ANCHOR(inner_pre);
10017 scope_node->pre_execution_anchor = inner_pre;
10018
10019 DECL_ANCHOR(inner_body);
10020
10021 if (cast->statements != NULL) {
10022 const pm_node_list_t *body = &cast->statements->body;
10023
10024 for (size_t index = 0; index < body->size; index++) {
10025 pm_compile_node(iseq, body->nodes[index], inner_body, true, scope_node);
10026 }
10027 }
10028
10029 if (!popped) {
10030 PUSH_INSN(inner_body, location, putnil);
10031 }
10032
10033 // Now that everything has been compiled, join both anchors together
10034 // into the correct outer pre execution anchor, and reset the value so
10035 // that subsequent BEGIN{} nodes can be compiled correctly.
10036 PUSH_SEQ(outer_pre, inner_pre);
10037 PUSH_SEQ(outer_pre, inner_body);
10038 scope_node->pre_execution_anchor = outer_pre;
10039
10040 return;
10041 }
10042 case PM_POST_EXECUTION_NODE: {
10043 // END {}
10044 // ^^^^^^
10045 const rb_iseq_t *child_iseq;
10046 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
10047
10048 pm_scope_node_t next_scope_node;
10049 pm_scope_node_init(node, &next_scope_node, scope_node);
10050 child_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, lineno);
10051 pm_scope_node_destroy(&next_scope_node);
10052
10053 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
10054
10055 int is_index = ISEQ_BODY(iseq)->ise_size++;
10056 PUSH_INSN2(ret, location, once, child_iseq, INT2FIX(is_index));
10057 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) child_iseq);
10058 if (popped) PUSH_INSN(ret, location, pop);
10059
10060 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
10061
10062 return;
10063 }
10064 case PM_RANGE_NODE: {
10065 // 0..5
10066 // ^^^^
10067 const pm_range_node_t *cast = (const pm_range_node_t *) node;
10068 bool exclude_end = PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END);
10069
10070 if (pm_optimizable_range_item_p(cast->left) && pm_optimizable_range_item_p(cast->right)) {
10071 if (!popped) {
10072 const pm_node_t *left = cast->left;
10073 const pm_node_t *right = cast->right;
10074
10075 VALUE val = rb_range_new(
10076 (left && PM_NODE_TYPE_P(left, PM_INTEGER_NODE)) ? parse_integer((const pm_integer_node_t *) left) : Qnil,
10077 (right && PM_NODE_TYPE_P(right, PM_INTEGER_NODE)) ? parse_integer((const pm_integer_node_t *) right) : Qnil,
10078 exclude_end
10079 );
10080
10081 PUSH_INSN1(ret, location, putobject, val);
10082 }
10083 }
10084 else {
10085 if (cast->left != NULL) {
10086 PM_COMPILE(cast->left);
10087 }
10088 else if (!popped) {
10089 PUSH_INSN(ret, location, putnil);
10090 }
10091
10092 if (cast->right != NULL) {
10093 PM_COMPILE(cast->right);
10094 }
10095 else if (!popped) {
10096 PUSH_INSN(ret, location, putnil);
10097 }
10098
10099 if (!popped) {
10100 PUSH_INSN1(ret, location, newrange, INT2FIX(exclude_end ? 1 : 0));
10101 }
10102 }
10103 return;
10104 }
10105 case PM_RATIONAL_NODE: {
10106 // 1r
10107 // ^^
10108 if (!popped) {
10109 PUSH_INSN1(ret, location, putobject, parse_rational((const pm_rational_node_t *) node));
10110 }
10111 return;
10112 }
10113 case PM_REDO_NODE:
10114 // redo
10115 // ^^^^
10116 pm_compile_redo_node(iseq, &location, ret, popped, scope_node);
10117 return;
10118 case PM_REGULAR_EXPRESSION_NODE: {
10119 // /foo/
10120 // ^^^^^
10121 if (!popped) {
10122 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
10123 PUSH_INSN1(ret, location, putobject, regexp);
10124 }
10125 return;
10126 }
10127 case PM_RESCUE_NODE:
10128 // begin; rescue; end
10129 // ^^^^^^^
10130 pm_compile_rescue_node(iseq, (const pm_rescue_node_t *) node, &location, ret, popped, scope_node);
10131 return;
10132 case PM_RESCUE_MODIFIER_NODE: {
10133 // foo rescue bar
10134 // ^^^^^^^^^^^^^^
10135 const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node;
10136
10137 pm_scope_node_t rescue_scope_node;
10138 pm_scope_node_init((const pm_node_t *) cast, &rescue_scope_node, scope_node);
10139
10140 rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(
10141 &rescue_scope_node,
10142 rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label),
10143 ISEQ_TYPE_RESCUE,
10144 pm_node_line_number(parser, cast->rescue_expression)
10145 );
10146
10147 pm_scope_node_destroy(&rescue_scope_node);
10148
10149 LABEL *lstart = NEW_LABEL(location.line);
10150 LABEL *lend = NEW_LABEL(location.line);
10151 LABEL *lcont = NEW_LABEL(location.line);
10152
10153 lstart->rescued = LABEL_RESCUE_BEG;
10154 lend->rescued = LABEL_RESCUE_END;
10155
10156 PUSH_LABEL(ret, lstart);
10157 PM_COMPILE_NOT_POPPED(cast->expression);
10158 PUSH_LABEL(ret, lend);
10159
10160 PUSH_INSN(ret, location, nop);
10161 PUSH_LABEL(ret, lcont);
10162 if (popped) PUSH_INSN(ret, location, pop);
10163
10164 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
10165 PUSH_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
10166 return;
10167 }
10168 case PM_RETURN_NODE:
10169 // return
10170 // ^^^^^^
10171 //
10172 // return 1
10173 // ^^^^^^^^
10174 pm_compile_return_node(iseq, (const pm_return_node_t *) node, &location, ret, popped, scope_node);
10175 return;
10176 case PM_RETRY_NODE: {
10177 // retry
10178 // ^^^^^
10179 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
10180 PUSH_INSN(ret, location, putnil);
10181 PUSH_INSN1(ret, location, throw, INT2FIX(TAG_RETRY));
10182 if (popped) PUSH_INSN(ret, location, pop);
10183 }
10184 else {
10185 COMPILE_ERROR(iseq, location.line, "Invalid retry");
10186 return;
10187 }
10188 return;
10189 }
10190 case PM_SCOPE_NODE:
10191 pm_compile_scope_node(iseq, (pm_scope_node_t *) node, &location, ret, popped);
10192 return;
10193 case PM_SELF_NODE: {
10194 // self
10195 // ^^^^
10196 if (!popped) {
10197 PUSH_INSN(ret, location, putself);
10198 }
10199 return;
10200 }
10201 case PM_SHAREABLE_CONSTANT_NODE: {
10202 // A value that is being written to a constant that is being marked as
10203 // shared depending on the current lexical context.
10204 const pm_shareable_constant_node_t *cast = (const pm_shareable_constant_node_t *) node;
10205 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));
10206
10207 switch (PM_NODE_TYPE(cast->write)) {
10208 case PM_CONSTANT_WRITE_NODE:
10209 pm_compile_constant_write_node(iseq, (const pm_constant_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10210 break;
10211 case PM_CONSTANT_AND_WRITE_NODE:
10212 pm_compile_constant_and_write_node(iseq, (const pm_constant_and_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10213 break;
10214 case PM_CONSTANT_OR_WRITE_NODE:
10215 pm_compile_constant_or_write_node(iseq, (const pm_constant_or_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10216 break;
10217 case PM_CONSTANT_OPERATOR_WRITE_NODE:
10218 pm_compile_constant_operator_write_node(iseq, (const pm_constant_operator_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10219 break;
10220 case PM_CONSTANT_PATH_WRITE_NODE:
10221 pm_compile_constant_path_write_node(iseq, (const pm_constant_path_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10222 break;
10223 case PM_CONSTANT_PATH_AND_WRITE_NODE:
10224 pm_compile_constant_path_and_write_node(iseq, (const pm_constant_path_and_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10225 break;
10226 case PM_CONSTANT_PATH_OR_WRITE_NODE:
10227 pm_compile_constant_path_or_write_node(iseq, (const pm_constant_path_or_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10228 break;
10229 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE:
10230 pm_compile_constant_path_operator_write_node(iseq, (const pm_constant_path_operator_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10231 break;
10232 default:
10233 rb_bug("Unexpected node type for shareable constant write: %s", pm_node_type_to_str(PM_NODE_TYPE(cast->write)));
10234 break;
10235 }
10236
10237 return;
10238 }
10239 case PM_SINGLETON_CLASS_NODE: {
10240 // class << self; end
10241 // ^^^^^^^^^^^^^^^^^^
10242 const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node;
10243
10244 pm_scope_node_t next_scope_node;
10245 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
10246 const rb_iseq_t *child_iseq = NEW_ISEQ(&next_scope_node, rb_fstring_lit("singleton class"), ISEQ_TYPE_CLASS, location.line);
10247 pm_scope_node_destroy(&next_scope_node);
10248
10249 PM_COMPILE_NOT_POPPED(cast->expression);
10250 PUSH_INSN(ret, location, putnil);
10251
10252 ID singletonclass;
10253 CONST_ID(singletonclass, "singletonclass");
10254 PUSH_INSN3(ret, location, defineclass, ID2SYM(singletonclass), child_iseq, INT2FIX(VM_DEFINECLASS_TYPE_SINGLETON_CLASS));
10255
10256 if (popped) PUSH_INSN(ret, location, pop);
10257 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) child_iseq);
10258
10259 return;
10260 }
10261 case PM_SOURCE_ENCODING_NODE: {
10262 // __ENCODING__
10263 // ^^^^^^^^^^^^
10264 if (!popped) {
10265 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10266 PUSH_INSN1(ret, location, putobject, value);
10267 }
10268 return;
10269 }
10270 case PM_SOURCE_FILE_NODE: {
10271 // __FILE__
10272 // ^^^^^^^^
10273 if (!popped) {
10274 const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node;
10275 VALUE string = pm_source_file_value(cast, scope_node);
10276
10277 if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FROZEN)) {
10278 PUSH_INSN1(ret, location, putobject, string);
10279 }
10280 else if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_MUTABLE)) {
10281 PUSH_INSN1(ret, location, putstring, string);
10282 }
10283 else {
10284 PUSH_INSN1(ret, location, putchilledstring, string);
10285 }
10286 }
10287 return;
10288 }
10289 case PM_SOURCE_LINE_NODE: {
10290 // __LINE__
10291 // ^^^^^^^^
10292 if (!popped) {
10293 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10294 PUSH_INSN1(ret, location, putobject, value);
10295 }
10296 return;
10297 }
10298 case PM_SPLAT_NODE: {
10299 // foo(*bar)
10300 // ^^^^
10301 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
10302 if (cast->expression) {
10303 PM_COMPILE(cast->expression);
10304 }
10305
10306 if (!popped) {
10307 PUSH_INSN1(ret, location, splatarray, Qtrue);
10308 }
10309 return;
10310 }
10311 case PM_STATEMENTS_NODE: {
10312 // A list of statements.
10313 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
10314 const pm_node_list_t *body = &cast->body;
10315
10316 if (body->size > 0) {
10317 for (size_t index = 0; index < body->size - 1; index++) {
10318 PM_COMPILE_POPPED(body->nodes[index]);
10319 }
10320 PM_COMPILE(body->nodes[body->size - 1]);
10321 }
10322 else {
10323 PUSH_INSN(ret, location, putnil);
10324 }
10325 return;
10326 }
10327 case PM_STRING_NODE: {
10328 // "foo"
10329 // ^^^^^
10330 if (!popped) {
10331 const pm_string_node_t *cast = (const pm_string_node_t *) node;
10332 VALUE value = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
10333
10334 if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_FROZEN)) {
10335 PUSH_INSN1(ret, location, putobject, value);
10336 }
10337 else if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_MUTABLE)) {
10338 PUSH_INSN1(ret, location, putstring, value);
10339 }
10340 else {
10341 PUSH_INSN1(ret, location, putchilledstring, value);
10342 }
10343 }
10344 return;
10345 }
10346 case PM_SUPER_NODE:
10347 // super()
10348 // super(foo)
10349 // super(...)
10350 pm_compile_super_node(iseq, (const pm_super_node_t *) node, &location, ret, popped, scope_node);
10351 return;
10352 case PM_SYMBOL_NODE: {
10353 // :foo
10354 // ^^^^
10355 if (!popped) {
10356 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10357 PUSH_INSN1(ret, location, putobject, value);
10358 }
10359 return;
10360 }
10361 case PM_TRUE_NODE: {
10362 // true
10363 // ^^^^
10364 if (!popped) {
10365 PUSH_INSN1(ret, location, putobject, Qtrue);
10366 }
10367 return;
10368 }
10369 case PM_UNDEF_NODE: {
10370 // undef foo
10371 // ^^^^^^^^^
10372 const pm_undef_node_t *cast = (const pm_undef_node_t *) node;
10373 const pm_node_list_t *names = &cast->names;
10374
10375 for (size_t index = 0; index < names->size; index++) {
10376 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10377 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
10378
10379 PM_COMPILE_NOT_POPPED(names->nodes[index]);
10380 PUSH_SEND(ret, location, id_core_undef_method, INT2NUM(2));
10381
10382 if (index < names->size - 1) {
10383 PUSH_INSN(ret, location, pop);
10384 }
10385 }
10386
10387 if (popped) PUSH_INSN(ret, location, pop);
10388 return;
10389 }
10390 case PM_UNLESS_NODE: {
10391 // unless foo; bar end
10392 // ^^^^^^^^^^^^^^^^^^^
10393 //
10394 // bar unless foo
10395 // ^^^^^^^^^^^^^^
10396 const pm_unless_node_t *cast = (const pm_unless_node_t *) node;
10397 const pm_statements_node_t *statements = NULL;
10398 if (cast->else_clause != NULL) {
10399 statements = ((const pm_else_node_t *) cast->else_clause)->statements;
10400 }
10401
10402 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);
10403 return;
10404 }
10405 case PM_UNTIL_NODE: {
10406 // until foo; bar end
10407 // ^^^^^^^^^^^^^^^^^
10408 //
10409 // bar until foo
10410 // ^^^^^^^^^^^^^
10411 const pm_until_node_t *cast = (const pm_until_node_t *) node;
10412 pm_compile_loop(iseq, &location, cast->base.flags, PM_UNTIL_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node);
10413 return;
10414 }
10415 case PM_WHILE_NODE: {
10416 // while foo; bar end
10417 // ^^^^^^^^^^^^^^^^^^
10418 //
10419 // bar while foo
10420 // ^^^^^^^^^^^^^
10421 const pm_while_node_t *cast = (const pm_while_node_t *) node;
10422 pm_compile_loop(iseq, &location, cast->base.flags, PM_WHILE_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node);
10423 return;
10424 }
10425 case PM_X_STRING_NODE: {
10426 // `foo`
10427 // ^^^^^
10428 const pm_x_string_node_t *cast = (const pm_x_string_node_t *) node;
10429 VALUE value = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
10430
10431 PUSH_INSN(ret, location, putself);
10432 PUSH_INSN1(ret, location, putobject, value);
10433 PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
10434 if (popped) PUSH_INSN(ret, location, pop);
10435
10436 return;
10437 }
10438 case PM_YIELD_NODE:
10439 // yield
10440 // ^^^^^
10441 //
10442 // yield 1
10443 // ^^^^^^^
10444 pm_compile_yield_node(iseq, (const pm_yield_node_t *) node, &location, ret, popped, scope_node);
10445 return;
10446 default:
10447 rb_raise(rb_eNotImpError, "node type %s not implemented", pm_node_type_to_str(PM_NODE_TYPE(node)));
10448 return;
10449 }
10450}
10451
10452#undef PM_CONTAINER_P
10453
10455static inline bool
10456pm_iseq_pre_execution_p(rb_iseq_t *iseq)
10457{
10458 switch (ISEQ_BODY(iseq)->type) {
10459 case ISEQ_TYPE_TOP:
10460 case ISEQ_TYPE_EVAL:
10461 case ISEQ_TYPE_MAIN:
10462 return true;
10463 default:
10464 return false;
10465 }
10466}
10467
10475 VALUE
10476pm_iseq_compile_node(rb_iseq_t *iseq, pm_scope_node_t *node)
10477{
10478 DECL_ANCHOR(ret);
10479
10480 if (pm_iseq_pre_execution_p(iseq)) {
10481 // Because these ISEQs can have BEGIN{}, we're going to create two
10482 // anchors to compile them, a "pre" and a "body". We'll mark the "pre"
10483 // on the scope node so that when BEGIN{} is found, its contents will be
10484 // added to the "pre" anchor.
10485 DECL_ANCHOR(pre);
10486 node->pre_execution_anchor = pre;
10487
10488 // Now we'll compile the body as normal. We won't compile directly into
10489 // the "ret" anchor yet because we want to add the "pre" anchor to the
10490 // beginning of the "ret" anchor first.
10491 DECL_ANCHOR(body);
10492 pm_compile_node(iseq, (const pm_node_t *) node, body, false, node);
10493
10494 // Now we'll join both anchors together so that the content is in the
10495 // correct order.
10496 PUSH_SEQ(ret, pre);
10497 PUSH_SEQ(ret, body);
10498 }
10499 else {
10500 // In other circumstances, we can just compile the node directly into
10501 // the "ret" anchor.
10502 pm_compile_node(iseq, (const pm_node_t *) node, ret, false, node);
10503 }
10504
10505 CHECK(iseq_setup_insn(iseq, ret));
10506 return iseq_setup(iseq, ret);
10507}
10508
10513void
10514pm_parse_result_free(pm_parse_result_t *result)
10515{
10516 if (result->node.ast_node != NULL) {
10517 pm_node_destroy(&result->parser, result->node.ast_node);
10518 }
10519
10520 if (result->parsed) {
10521 xfree(result->node.constants);
10522 pm_scope_node_destroy(&result->node);
10523 }
10524
10525 pm_parser_free(&result->parser);
10526 pm_string_free(&result->input);
10527 pm_options_free(&result->options);
10528}
10529
10531 typedef struct {
10533 pm_diagnostic_t *error;
10534
10536 int32_t line;
10537
10539 uint32_t column_start;
10540
10542 uint32_t column_end;
10543} pm_parse_error_t;
10544
10546 typedef struct {
10548 const char *number_prefix;
10549
10551 const char *blank_prefix;
10552
10554 const char *divider;
10555
10557 size_t blank_prefix_length;
10558
10560 size_t divider_length;
10561} pm_parse_error_format_t;
10562
10563#define PM_COLOR_BOLD "\033[1m"
10564#define PM_COLOR_GRAY "\033[2m"
10565#define PM_COLOR_RED "\033[1;31m"
10566#define PM_COLOR_RESET "\033[m"
10567#define PM_ERROR_TRUNCATE 30
10568
10569static inline pm_parse_error_t *
10570pm_parse_errors_format_sort(const pm_parser_t *parser, const pm_list_t *error_list, const pm_newline_list_t *newline_list) {
10571 pm_parse_error_t *errors = xcalloc(error_list->size, sizeof(pm_parse_error_t));
10572 if (errors == NULL) return NULL;
10573
10574 int32_t start_line = parser->start_line;
10575 for (pm_diagnostic_t *error = (pm_diagnostic_t *) error_list->head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
10576 pm_line_column_t start = pm_newline_list_line_column(newline_list, error->location.start, start_line);
10577 pm_line_column_t end = pm_newline_list_line_column(newline_list, error->location.end, start_line);
10578
10579 // We're going to insert this error into the array in sorted order. We
10580 // do this by finding the first error that has a line number greater
10581 // than the current error and then inserting the current error before
10582 // that one.
10583 size_t index = 0;
10584 while (
10585 (index < error_list->size) &&
10586 (errors[index].error != NULL) &&
10587 (
10588 (errors[index].line < start.line) ||
10589 ((errors[index].line == start.line) && (errors[index].column_start < start.column))
10590 )
10591 ) index++;
10592
10593 // Now we're going to shift all of the errors after this one down one
10594 // index to make room for the new error.
10595 if (index + 1 < error_list->size) {
10596 memmove(&errors[index + 1], &errors[index], sizeof(pm_parse_error_t) * (error_list->size - index - 1));
10597 }
10598
10599 // Finally, we'll insert the error into the array.
10600 uint32_t column_end;
10601 if (start.line == end.line) {
10602 column_end = end.column;
10603 } else {
10604 column_end = (uint32_t) (newline_list->offsets[start.line - start_line + 1] - newline_list->offsets[start.line - start_line] - 1);
10605 }
10606
10607 // Ensure we have at least one column of error.
10608 if (start.column == column_end) column_end++;
10609
10610 errors[index] = (pm_parse_error_t) {
10611 .error = error,
10612 .line = start.line,
10613 .column_start = start.column,
10614 .column_end = column_end
10615 };
10616 }
10617
10618 return errors;
10619}
10620
10621/* Append a literal string to the buffer. */
10622#define pm_buffer_append_literal(buffer, str) pm_buffer_append_string(buffer, str, rb_strlen_lit(str))
10623
10624static inline void
10625pm_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) {
10626 int32_t line_delta = line - parser->start_line;
10627 assert(line_delta >= 0);
10628
10629 size_t index = (size_t) line_delta;
10630 assert(index < newline_list->size);
10631
10632 const uint8_t *start = &parser->start[newline_list->offsets[index]];
10633 const uint8_t *end;
10634
10635 if (index >= newline_list->size - 1) {
10636 end = parser->end;
10637 } else {
10638 end = &parser->start[newline_list->offsets[index + 1]];
10639 }
10640
10641 pm_buffer_append_format(buffer, number_prefix, line);
10642
10643 // Here we determine if we should truncate the end of the line.
10644 bool truncate_end = false;
10645 if ((column_end != 0) && ((end - (start + column_end)) >= PM_ERROR_TRUNCATE)) {
10646 end = start + column_end + PM_ERROR_TRUNCATE;
10647 truncate_end = true;
10648 }
10649
10650 // Here we determine if we should truncate the start of the line.
10651 if (column_start >= PM_ERROR_TRUNCATE) {
10652 pm_buffer_append_string(buffer, "... ", 4);
10653 start += column_start;
10654 }
10655
10656 pm_buffer_append_string(buffer, (const char *) start, (size_t) (end - start));
10657
10658 if (truncate_end) {
10659 pm_buffer_append_string(buffer, " ...\n", 5);
10660 } else if (end == parser->end && end[-1] != '\n') {
10661 pm_buffer_append_string(buffer, "\n", 1);
10662 }
10663}
10664
10668static void
10669pm_parse_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, pm_buffer_t *buffer, int highlight, bool inline_messages) {
10670 assert(error_list->size != 0);
10671
10672 // First, we're going to sort all of the errors by line number using an
10673 // insertion sort into a newly allocated array.
10674 const int32_t start_line = parser->start_line;
10675 const pm_newline_list_t *newline_list = &parser->newline_list;
10676
10677 pm_parse_error_t *errors = pm_parse_errors_format_sort(parser, error_list, newline_list);
10678 if (errors == NULL) return;
10679
10680 // Now we're going to determine how we're going to format line numbers and
10681 // blank lines based on the maximum number of digits in the line numbers
10682 // that are going to be displaid.
10683 pm_parse_error_format_t error_format;
10684 int32_t first_line_number = errors[0].line;
10685 int32_t last_line_number = errors[error_list->size - 1].line;
10686
10687 // If we have a maximum line number that is negative, then we're going to
10688 // use the absolute value for comparison but multiple by 10 to additionally
10689 // have a column for the negative sign.
10690 if (first_line_number < 0) first_line_number = (-first_line_number) * 10;
10691 if (last_line_number < 0) last_line_number = (-last_line_number) * 10;
10692 int32_t max_line_number = first_line_number > last_line_number ? first_line_number : last_line_number;
10693
10694 if (max_line_number < 10) {
10695 if (highlight > 0) {
10696 error_format = (pm_parse_error_format_t) {
10697 .number_prefix = PM_COLOR_GRAY "%1" PRIi32 " | " PM_COLOR_RESET,
10698 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10699 .divider = PM_COLOR_GRAY " ~~~~~" PM_COLOR_RESET "\n"
10700 };
10701 } else {
10702 error_format = (pm_parse_error_format_t) {
10703 .number_prefix = "%1" PRIi32 " | ",
10704 .blank_prefix = " | ",
10705 .divider = " ~~~~~\n"
10706 };
10707 }
10708 } else if (max_line_number < 100) {
10709 if (highlight > 0) {
10710 error_format = (pm_parse_error_format_t) {
10711 .number_prefix = PM_COLOR_GRAY "%2" PRIi32 " | " PM_COLOR_RESET,
10712 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10713 .divider = PM_COLOR_GRAY " ~~~~~~" PM_COLOR_RESET "\n"
10714 };
10715 } else {
10716 error_format = (pm_parse_error_format_t) {
10717 .number_prefix = "%2" PRIi32 " | ",
10718 .blank_prefix = " | ",
10719 .divider = " ~~~~~~\n"
10720 };
10721 }
10722 } else if (max_line_number < 1000) {
10723 if (highlight > 0) {
10724 error_format = (pm_parse_error_format_t) {
10725 .number_prefix = PM_COLOR_GRAY "%3" PRIi32 " | " PM_COLOR_RESET,
10726 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10727 .divider = PM_COLOR_GRAY " ~~~~~~~" PM_COLOR_RESET "\n"
10728 };
10729 } else {
10730 error_format = (pm_parse_error_format_t) {
10731 .number_prefix = "%3" PRIi32 " | ",
10732 .blank_prefix = " | ",
10733 .divider = " ~~~~~~~\n"
10734 };
10735 }
10736 } else if (max_line_number < 10000) {
10737 if (highlight > 0) {
10738 error_format = (pm_parse_error_format_t) {
10739 .number_prefix = PM_COLOR_GRAY "%4" PRIi32 " | " PM_COLOR_RESET,
10740 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10741 .divider = PM_COLOR_GRAY " ~~~~~~~~" PM_COLOR_RESET "\n"
10742 };
10743 } else {
10744 error_format = (pm_parse_error_format_t) {
10745 .number_prefix = "%4" PRIi32 " | ",
10746 .blank_prefix = " | ",
10747 .divider = " ~~~~~~~~\n"
10748 };
10749 }
10750 } else {
10751 if (highlight > 0) {
10752 error_format = (pm_parse_error_format_t) {
10753 .number_prefix = PM_COLOR_GRAY "%5" PRIi32 " | " PM_COLOR_RESET,
10754 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10755 .divider = PM_COLOR_GRAY " ~~~~~~~~" PM_COLOR_RESET "\n"
10756 };
10757 } else {
10758 error_format = (pm_parse_error_format_t) {
10759 .number_prefix = "%5" PRIi32 " | ",
10760 .blank_prefix = " | ",
10761 .divider = " ~~~~~~~~\n"
10762 };
10763 }
10764 }
10765
10766 error_format.blank_prefix_length = strlen(error_format.blank_prefix);
10767 error_format.divider_length = strlen(error_format.divider);
10768
10769 // Now we're going to iterate through every error in our error list and
10770 // display it. While we're iterating, we will display some padding lines of
10771 // the source before the error to give some context. We'll be careful not to
10772 // display the same line twice in case the errors are close enough in the
10773 // source.
10774 int32_t last_line = parser->start_line - 1;
10775 uint32_t last_column_start = 0;
10776 const pm_encoding_t *encoding = parser->encoding;
10777
10778 for (size_t index = 0; index < error_list->size; index++) {
10779 pm_parse_error_t *error = &errors[index];
10780
10781 // Here we determine how many lines of padding of the source to display,
10782 // based on the difference from the last line that was displaid.
10783 if (error->line - last_line > 1) {
10784 if (error->line - last_line > 2) {
10785 if ((index != 0) && (error->line - last_line > 3)) {
10786 pm_buffer_append_string(buffer, error_format.divider, error_format.divider_length);
10787 }
10788
10789 pm_buffer_append_string(buffer, " ", 2);
10790 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 2, 0, 0, buffer);
10791 }
10792
10793 pm_buffer_append_string(buffer, " ", 2);
10794 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 1, 0, 0, buffer);
10795 }
10796
10797 // If this is the first error or we're on a new line, then we'll display
10798 // the line that has the error in it.
10799 if ((index == 0) || (error->line != last_line)) {
10800 if (highlight > 1) {
10801 pm_buffer_append_literal(buffer, PM_COLOR_RED "> " PM_COLOR_RESET);
10802 } else if (highlight > 0) {
10803 pm_buffer_append_literal(buffer, PM_COLOR_BOLD "> " PM_COLOR_RESET);
10804 } else {
10805 pm_buffer_append_literal(buffer, "> ");
10806 }
10807
10808 last_column_start = error->column_start;
10809
10810 // Find the maximum column end of all the errors on this line.
10811 uint32_t column_end = error->column_end;
10812 for (size_t next_index = index + 1; next_index < error_list->size; next_index++) {
10813 if (errors[next_index].line != error->line) break;
10814 if (errors[next_index].column_end > column_end) column_end = errors[next_index].column_end;
10815 }
10816
10817 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, error->line, error->column_start, column_end, buffer);
10818 }
10819
10820 const uint8_t *start = &parser->start[newline_list->offsets[error->line - start_line]];
10821 if (start == parser->end) pm_buffer_append_byte(buffer, '\n');
10822
10823 // Now we'll display the actual error message. We'll do this by first
10824 // putting the prefix to the line, then a bunch of blank spaces
10825 // depending on the column, then as many carets as we need to display
10826 // the width of the error, then the error message itself.
10827 //
10828 // Note that this doesn't take into account the width of the actual
10829 // character when displaid in the terminal. For some east-asian
10830 // languages or emoji, this means it can be thrown off pretty badly. We
10831 // will need to solve this eventually.
10832 pm_buffer_append_string(buffer, " ", 2);
10833 pm_buffer_append_string(buffer, error_format.blank_prefix, error_format.blank_prefix_length);
10834
10835 size_t column = 0;
10836 if (last_column_start >= PM_ERROR_TRUNCATE) {
10837 pm_buffer_append_string(buffer, " ", 4);
10838 column = last_column_start;
10839 }
10840
10841 while (column < error->column_start) {
10842 pm_buffer_append_byte(buffer, ' ');
10843
10844 size_t char_width = encoding->char_width(start + column, parser->end - (start + column));
10845 column += (char_width == 0 ? 1 : char_width);
10846 }
10847
10848 if (highlight > 1) pm_buffer_append_literal(buffer, PM_COLOR_RED);
10849 else if (highlight > 0) pm_buffer_append_literal(buffer, PM_COLOR_BOLD);
10850 pm_buffer_append_byte(buffer, '^');
10851
10852 size_t char_width = encoding->char_width(start + column, parser->end - (start + column));
10853 column += (char_width == 0 ? 1 : char_width);
10854
10855 while (column < error->column_end) {
10856 pm_buffer_append_byte(buffer, '~');
10857
10858 size_t char_width = encoding->char_width(start + column, parser->end - (start + column));
10859 column += (char_width == 0 ? 1 : char_width);
10860 }
10861
10862 if (highlight > 0) pm_buffer_append_literal(buffer, PM_COLOR_RESET);
10863
10864 if (inline_messages) {
10865 pm_buffer_append_byte(buffer, ' ');
10866 assert(error->error != NULL);
10867
10868 const char *message = error->error->message;
10869 pm_buffer_append_string(buffer, message, strlen(message));
10870 }
10871
10872 pm_buffer_append_byte(buffer, '\n');
10873
10874 // Here we determine how many lines of padding to display after the
10875 // error, depending on where the next error is in source.
10876 last_line = error->line;
10877 int32_t next_line;
10878
10879 if (index == error_list->size - 1) {
10880 next_line = (((int32_t) newline_list->size) + parser->start_line);
10881
10882 // If the file ends with a newline, subtract one from our "next_line"
10883 // so that we don't output an extra line at the end of the file
10884 if ((parser->start + newline_list->offsets[newline_list->size - 1]) == parser->end) {
10885 next_line--;
10886 }
10887 }
10888 else {
10889 next_line = errors[index + 1].line;
10890 }
10891
10892 if (next_line - last_line > 1) {
10893 pm_buffer_append_string(buffer, " ", 2);
10894 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, 0, 0, buffer);
10895 }
10896
10897 if (next_line - last_line > 1) {
10898 pm_buffer_append_string(buffer, " ", 2);
10899 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, 0, 0, buffer);
10900 }
10901 }
10902
10903 // Finally, we'll free the array of errors that we allocated.
10904 xfree(errors);
10905}
10906
10907#undef PM_ERROR_TRUNCATE
10908#undef PM_COLOR_GRAY
10909#undef PM_COLOR_RED
10910#undef PM_COLOR_RESET
10911
10918static bool
10919pm_parse_process_error_utf8_p(const pm_parser_t *parser, const pm_location_t *location)
10920{
10921 const size_t start_line = pm_newline_list_line_column(&parser->newline_list, location->start, 1).line;
10922 const size_t end_line = pm_newline_list_line_column(&parser->newline_list, location->end, 1).line;
10923
10924 const uint8_t *start = parser->start + parser->newline_list.offsets[start_line - 1];
10925 const uint8_t *end = ((end_line == parser->newline_list.size) ? parser->end : (parser->start + parser->newline_list.offsets[end_line]));
10926 size_t width;
10927
10928 while (start < end) {
10929 if ((width = pm_encoding_utf_8_char_width(start, end - start)) == 0) return false;
10930 start += width;
10931 }
10932
10933 return true;
10934}
10935
10940static VALUE
10941pm_parse_process_error(const pm_parse_result_t *result)
10942{
10943 const pm_parser_t *parser = &result->parser;
10944 const pm_diagnostic_t *head = (const pm_diagnostic_t *) parser->error_list.head;
10945 bool valid_utf8 = true;
10946
10947 pm_buffer_t buffer = { 0 };
10948 const pm_string_t *filepath = &parser->filepath;
10949
10950 int highlight = rb_stderr_tty_p();
10951 if (highlight) {
10952 const char *no_color = getenv("NO_COLOR");
10953 highlight = (no_color == NULL || no_color[0] == '\0') ? 2 : 1;
10954 }
10955
10956 for (const pm_diagnostic_t *error = head; error != NULL; error = (const pm_diagnostic_t *) error->node.next) {
10957 switch (error->level) {
10958 case PM_ERROR_LEVEL_SYNTAX:
10959 // It is implicitly assumed that the error messages will be
10960 // encodeable as UTF-8. Because of this, we can't include source
10961 // examples that contain invalid byte sequences. So if any source
10962 // examples include invalid UTF-8 byte sequences, we will skip
10963 // showing source examples entirely.
10964 if (valid_utf8 && !pm_parse_process_error_utf8_p(parser, &error->location)) {
10965 valid_utf8 = false;
10966 }
10967 break;
10968 case PM_ERROR_LEVEL_ARGUMENT: {
10969 // Any errors with the level PM_ERROR_LEVEL_ARGUMENT take over as
10970 // the only argument that gets raised. This is to allow priority
10971 // messages that should be handled before anything else.
10972 int32_t line_number = (int32_t) pm_location_line_number(parser, &error->location);
10973
10974 pm_buffer_append_format(
10975 &buffer,
10976 "%.*s:%" PRIi32 ": %s",
10977 (int) pm_string_length(filepath),
10978 pm_string_source(filepath),
10979 line_number,
10980 error->message
10981 );
10982
10983 if (pm_parse_process_error_utf8_p(parser, &error->location)) {
10984 pm_buffer_append_byte(&buffer, '\n');
10985
10986 pm_list_node_t *list_node = (pm_list_node_t *) error;
10987 pm_list_t error_list = { .size = 1, .head = list_node, .tail = list_node };
10988
10989 pm_parse_errors_format(parser, &error_list, &buffer, highlight, false);
10990 }
10991
10992 VALUE value = rb_exc_new(rb_eArgError, pm_buffer_value(&buffer), pm_buffer_length(&buffer));
10993 pm_buffer_free(&buffer);
10994
10995 return value;
10996 }
10997 case PM_ERROR_LEVEL_LOAD: {
10998 // Load errors are much simpler, because they don't include any of
10999 // the source in them. We create the error directly from the
11000 // message.
11001 VALUE message = rb_enc_str_new_cstr(error->message, rb_locale_encoding());
11002 VALUE value = rb_exc_new3(rb_eLoadError, message);
11003 rb_ivar_set(value, rb_intern_const("@path"), Qnil);
11004 return value;
11005 }
11006 }
11007 }
11008
11009 pm_buffer_append_format(
11010 &buffer,
11011 "%.*s:%" PRIi32 ": syntax error%s found\n",
11012 (int) pm_string_length(filepath),
11013 pm_string_source(filepath),
11014 (int32_t) pm_location_line_number(parser, &head->location),
11015 (parser->error_list.size > 1) ? "s" : ""
11016 );
11017
11018 if (valid_utf8) {
11019 pm_parse_errors_format(parser, &parser->error_list, &buffer, highlight, true);
11020 }
11021 else {
11022 for (const pm_diagnostic_t *error = head; error != NULL; error = (const pm_diagnostic_t *) error->node.next) {
11023 if (error != head) pm_buffer_append_byte(&buffer, '\n');
11024 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);
11025 }
11026 }
11027
11028 VALUE message = rb_enc_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer), result->node.encoding);
11029 VALUE error = rb_exc_new_str(rb_eSyntaxError, message);
11030
11031 rb_encoding *filepath_encoding = result->node.filepath_encoding != NULL ? result->node.filepath_encoding : rb_utf8_encoding();
11032 VALUE path = rb_enc_str_new((const char *) pm_string_source(filepath), pm_string_length(filepath), filepath_encoding);
11033
11034 rb_ivar_set(error, rb_intern_const("@path"), path);
11035 pm_buffer_free(&buffer);
11036
11037 return error;
11038}
11039
11045static VALUE
11046pm_parse_process(pm_parse_result_t *result, pm_node_t *node, VALUE *script_lines)
11047{
11048 pm_parser_t *parser = &result->parser;
11049
11050 // First, set up the scope node so that the AST node is attached and can be
11051 // freed regardless of whether or we return an error.
11052 pm_scope_node_t *scope_node = &result->node;
11053 rb_encoding *filepath_encoding = scope_node->filepath_encoding;
11054 int coverage_enabled = scope_node->coverage_enabled;
11055
11056 pm_scope_node_init(node, scope_node, NULL);
11057 scope_node->filepath_encoding = filepath_encoding;
11058
11059 scope_node->encoding = rb_enc_find(parser->encoding->name);
11060 if (!scope_node->encoding) rb_bug("Encoding not found %s!", parser->encoding->name);
11061
11062 scope_node->coverage_enabled = coverage_enabled;
11063
11064 // If RubyVM.keep_script_lines is set to true, then we need to create that
11065 // array of script lines here.
11066 if (script_lines != NULL) {
11067 *script_lines = rb_ary_new_capa(parser->newline_list.size);
11068
11069 for (size_t index = 0; index < parser->newline_list.size; index++) {
11070 size_t offset = parser->newline_list.offsets[index];
11071 size_t length = index == parser->newline_list.size - 1 ? ((size_t) (parser->end - (parser->start + offset))) : (parser->newline_list.offsets[index + 1] - offset);
11072 rb_ary_push(*script_lines, rb_enc_str_new((const char *) parser->start + offset, length, scope_node->encoding));
11073 }
11074
11075 scope_node->script_lines = script_lines;
11076 }
11077
11078 // Emit all of the various warnings from the parse.
11079 const pm_diagnostic_t *warning;
11080 const char *warning_filepath = (const char *) pm_string_source(&parser->filepath);
11081
11082 for (warning = (const pm_diagnostic_t *) parser->warning_list.head; warning != NULL; warning = (const pm_diagnostic_t *) warning->node.next) {
11083 int line = pm_location_line_number(parser, &warning->location);
11084
11085 if (warning->level == PM_WARNING_LEVEL_VERBOSE) {
11086 rb_enc_compile_warning(scope_node->encoding, warning_filepath, line, "%s", warning->message);
11087 }
11088 else {
11089 rb_enc_compile_warn(scope_node->encoding, warning_filepath, line, "%s", warning->message);
11090 }
11091 }
11092
11093 // If there are errors, raise an appropriate error and free the result.
11094 if (parser->error_list.size > 0) {
11095 VALUE error = pm_parse_process_error(result);
11096
11097 // TODO: We need to set the backtrace.
11098 // rb_funcallv(error, rb_intern("set_backtrace"), 1, &path);
11099 return error;
11100 }
11101
11102 // Now set up the constant pool and intern all of the various constants into
11103 // their corresponding IDs.
11104 scope_node->parser = parser;
11105 scope_node->constants = xcalloc(parser->constant_pool.size, sizeof(ID));
11106
11107 for (uint32_t index = 0; index < parser->constant_pool.size; index++) {
11108 pm_constant_t *constant = &parser->constant_pool.constants[index];
11109 scope_node->constants[index] = rb_intern3((const char *) constant->start, constant->length, scope_node->encoding);
11110 }
11111
11112 scope_node->index_lookup_table = st_init_numtable();
11113 pm_constant_id_list_t *locals = &scope_node->locals;
11114 for (size_t index = 0; index < locals->size; index++) {
11115 st_insert(scope_node->index_lookup_table, locals->ids[index], index);
11116 }
11117
11118 // If we got here, this is a success and we can return Qnil to indicate that
11119 // no error should be raised.
11120 result->parsed = true;
11121 return Qnil;
11122}
11123
11128static void
11129pm_options_frozen_string_literal_init(pm_options_t *options)
11130{
11131 int frozen_string_literal = rb_iseq_opt_frozen_string_literal();
11132
11133 switch (frozen_string_literal) {
11134 case ISEQ_FROZEN_STRING_LITERAL_UNSET:
11135 break;
11136 case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
11137 pm_options_frozen_string_literal_set(options, false);
11138 break;
11139 case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
11140 pm_options_frozen_string_literal_set(options, true);
11141 break;
11142 default:
11143 rb_bug("pm_options_frozen_string_literal_init: invalid frozen_string_literal=%d", frozen_string_literal);
11144 break;
11145 }
11146}
11147
11152static inline VALUE
11153pm_parse_file_script_lines(const pm_scope_node_t *scope_node, const pm_parser_t *parser)
11154{
11155 const pm_newline_list_t *newline_list = &parser->newline_list;
11156 const char *start = (const char *) parser->start;
11157 const char *end = (const char *) parser->end;
11158
11159 // If we end exactly on a newline, then there's no need to push on a final
11160 // segment. If we don't, then we need to push on the last offset up to the
11161 // end of the string.
11162 size_t last_offset = newline_list->offsets[newline_list->size - 1];
11163 bool last_push = start + last_offset != end;
11164
11165 // Create the ruby strings that represent the lines of the source.
11166 VALUE lines = rb_ary_new_capa(newline_list->size - (last_push ? 0 : 1));
11167
11168 for (size_t index = 0; index < newline_list->size - 1; index++) {
11169 size_t offset = newline_list->offsets[index];
11170 size_t length = newline_list->offsets[index + 1] - offset;
11171
11172 rb_ary_push(lines, rb_enc_str_new(start + offset, length, scope_node->encoding));
11173 }
11174
11175 // Push on the last line if we need to.
11176 if (last_push) {
11177 rb_ary_push(lines, rb_enc_str_new(start + last_offset, end - (start + last_offset), scope_node->encoding));
11178 }
11179
11180 return lines;
11181}
11182
11183// This is essentially pm_string_mapped_init(), preferring to memory map the
11184// file, with additional handling for files that require blocking to properly
11185// read (e.g. pipes).
11186static pm_string_init_result_t
11187pm_read_file(pm_string_t *string, const char *filepath)
11188{
11189#ifdef _WIN32
11190 // Open the file for reading.
11191 int length = MultiByteToWideChar(CP_UTF8, 0, filepath, -1, NULL, 0);
11192 if (length == 0) return PM_STRING_INIT_ERROR_GENERIC;
11193
11194 WCHAR *wfilepath = xmalloc(sizeof(WCHAR) * ((size_t) length));
11195 if ((wfilepath == NULL) || (MultiByteToWideChar(CP_UTF8, 0, filepath, -1, wfilepath, length) == 0)) {
11196 xfree(wfilepath);
11197 return PM_STRING_INIT_ERROR_GENERIC;
11198 }
11199
11200 HANDLE file = CreateFileW(wfilepath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
11201 if (file == INVALID_HANDLE_VALUE) {
11202 pm_string_init_result_t result = PM_STRING_INIT_ERROR_GENERIC;
11203
11204 if (GetLastError() == ERROR_ACCESS_DENIED) {
11205 DWORD attributes = GetFileAttributesW(wfilepath);
11206 if ((attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
11207 result = PM_STRING_INIT_ERROR_DIRECTORY;
11208 }
11209 }
11210
11211 xfree(wfilepath);
11212 return result;
11213 }
11214
11215 // Get the file size.
11216 DWORD file_size = GetFileSize(file, NULL);
11217 if (file_size == INVALID_FILE_SIZE) {
11218 CloseHandle(file);
11219 xfree(wfilepath);
11220 return PM_STRING_INIT_ERROR_GENERIC;
11221 }
11222
11223 // If the file is empty, then we don't need to do anything else, we'll set
11224 // the source to a constant empty string and return.
11225 if (file_size == 0) {
11226 CloseHandle(file);
11227 xfree(wfilepath);
11228 const uint8_t source[] = "";
11229 *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
11230 return PM_STRING_INIT_SUCCESS;
11231 }
11232
11233 // Create a mapping of the file.
11234 HANDLE mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
11235 if (mapping == NULL) {
11236 CloseHandle(file);
11237 xfree(wfilepath);
11238 return PM_STRING_INIT_ERROR_GENERIC;
11239 }
11240
11241 // Map the file into memory.
11242 uint8_t *source = (uint8_t *) MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
11243 CloseHandle(mapping);
11244 CloseHandle(file);
11245 xfree(wfilepath);
11246
11247 if (source == NULL) {
11248 return PM_STRING_INIT_ERROR_GENERIC;
11249 }
11250
11251 *string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = (size_t) file_size };
11252 return PM_STRING_INIT_SUCCESS;
11253#elif defined(_POSIX_MAPPED_FILES)
11254 // Open the file for reading
11255 const int open_mode = O_RDONLY | O_NONBLOCK;
11256 int fd = open(filepath, open_mode);
11257 if (fd == -1) {
11258 return PM_STRING_INIT_ERROR_GENERIC;
11259 }
11260
11261 // Stat the file to get the file size
11262 struct stat sb;
11263 if (fstat(fd, &sb) == -1) {
11264 close(fd);
11265 return PM_STRING_INIT_ERROR_GENERIC;
11266 }
11267
11268 // Ensure it is a file and not a directory
11269 if (S_ISDIR(sb.st_mode)) {
11270 close(fd);
11271 return PM_STRING_INIT_ERROR_DIRECTORY;
11272 }
11273
11274 // We need to wait for data first before reading from pipes and character
11275 // devices. To not block the entire VM, we need to release the GVL while
11276 // reading. Use IO#read to do this and let the GC handle closing the FD.
11277 if (S_ISFIFO(sb.st_mode) || S_ISCHR(sb.st_mode)) {
11278 VALUE io = rb_io_fdopen((int) fd, open_mode, filepath);
11279 rb_io_wait(io, RB_INT2NUM(RUBY_IO_READABLE), Qnil);
11280 VALUE contents = rb_funcall(io, rb_intern("read"), 0);
11281
11282 if (!RB_TYPE_P(contents, T_STRING)) {
11283 return PM_STRING_INIT_ERROR_GENERIC;
11284 }
11285
11286 long len = RSTRING_LEN(contents);
11287 if (len < 0) {
11288 return PM_STRING_INIT_ERROR_GENERIC;
11289 }
11290
11291 size_t length = (size_t) len;
11292 uint8_t *source = malloc(length);
11293 memcpy(source, RSTRING_PTR(contents), length);
11294 *string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = length };
11295
11296 return PM_STRING_INIT_SUCCESS;
11297 }
11298
11299 // mmap the file descriptor to virtually get the contents
11300 size_t size = (size_t) sb.st_size;
11301 uint8_t *source = NULL;
11302
11303 if (size == 0) {
11304 close(fd);
11305 const uint8_t source[] = "";
11306 *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
11307 return PM_STRING_INIT_SUCCESS;
11308 }
11309
11310 source = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
11311 if (source == MAP_FAILED) {
11312 close(fd);
11313 return PM_STRING_INIT_ERROR_GENERIC;
11314 }
11315
11316 close(fd);
11317 *string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = size };
11318 return PM_STRING_INIT_SUCCESS;
11319#else
11320 return pm_string_file_init(string, filepath);
11321#endif
11322}
11323
11328 VALUE
11329pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error)
11330{
11331 pm_string_init_result_t init_result = pm_read_file(&result->input, RSTRING_PTR(filepath));
11332
11333 if (init_result == PM_STRING_INIT_SUCCESS) {
11334 pm_options_frozen_string_literal_init(&result->options);
11335 return Qnil;
11336 }
11337
11338 int err;
11339 if (init_result == PM_STRING_INIT_ERROR_DIRECTORY) {
11340 err = EISDIR;
11341 } else {
11342#ifdef _WIN32
11343 err = rb_w32_map_errno(GetLastError());
11344#else
11345 err = errno;
11346#endif
11347 }
11348
11349 VALUE error;
11350 if (load_error) {
11351 VALUE message = rb_str_buf_new_cstr(strerror(err));
11352 rb_str_cat2(message, " -- ");
11353 rb_str_append(message, filepath);
11354
11355 error = rb_exc_new3(rb_eLoadError, message);
11356 rb_ivar_set(error, rb_intern_const("@path"), filepath);
11357 } else {
11358 error = rb_syserr_new(err, RSTRING_PTR(filepath));
11359 RB_GC_GUARD(filepath);
11360 }
11361
11362 return error;
11363}
11364
11371 VALUE
11372pm_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines)
11373{
11374 result->node.filepath_encoding = rb_enc_get(filepath);
11375 pm_options_filepath_set(&result->options, RSTRING_PTR(filepath));
11376 RB_GC_GUARD(filepath);
11377
11378 pm_options_version_for_current_ruby_set(&result->options);
11379
11380 pm_parser_init(&result->parser, pm_string_source(&result->input), pm_string_length(&result->input), &result->options);
11381 pm_node_t *node = pm_parse(&result->parser);
11382
11383 VALUE error = pm_parse_process(result, node, script_lines);
11384
11385 // If we're parsing a filepath, then we need to potentially support the
11386 // SCRIPT_LINES__ constant, which can be a hash that has an array of lines
11387 // of every read file.
11388 ID id_script_lines = rb_intern("SCRIPT_LINES__");
11389
11390 if (rb_const_defined_at(rb_cObject, id_script_lines)) {
11391 VALUE constant_script_lines = rb_const_get_at(rb_cObject, id_script_lines);
11392
11393 if (RB_TYPE_P(constant_script_lines, T_HASH)) {
11394 rb_hash_aset(constant_script_lines, filepath, pm_parse_file_script_lines(&result->node, &result->parser));
11395 }
11396 }
11397
11398 return error;
11399}
11400
11405 VALUE
11406pm_load_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines)
11407{
11408 VALUE error = pm_load_file(result, filepath, false);
11409 if (NIL_P(error)) {
11410 error = pm_parse_file(result, filepath, script_lines);
11411 }
11412
11413 return error;
11414}
11415
11422 VALUE
11423pm_parse_string(pm_parse_result_t *result, VALUE source, VALUE filepath, VALUE *script_lines)
11424{
11425 rb_encoding *encoding = rb_enc_get(source);
11426 if (!rb_enc_asciicompat(encoding)) {
11427 return rb_exc_new_cstr(rb_eArgError, "invalid source encoding");
11428 }
11429
11430 pm_options_frozen_string_literal_init(&result->options);
11431 pm_string_constant_init(&result->input, RSTRING_PTR(source), RSTRING_LEN(source));
11432 pm_options_encoding_set(&result->options, rb_enc_name(encoding));
11433
11434 result->node.filepath_encoding = rb_enc_get(filepath);
11435 pm_options_filepath_set(&result->options, RSTRING_PTR(filepath));
11436 RB_GC_GUARD(filepath);
11437
11438 pm_options_version_for_current_ruby_set(&result->options);
11439
11440 pm_parser_init(&result->parser, pm_string_source(&result->input), pm_string_length(&result->input), &result->options);
11441 pm_node_t *node = pm_parse(&result->parser);
11442
11443 return pm_parse_process(result, node, script_lines);
11444}
11445
11446 struct rb_stdin_wrapper {
11447 VALUE rb_stdin;
11448 int eof_seen;
11449};
11450
11451static int
11452pm_parse_stdin_eof(void *stream)
11453{
11454 struct rb_stdin_wrapper * wrapped_stdin = (struct rb_stdin_wrapper *)stream;
11455 return wrapped_stdin->eof_seen;
11456}
11457
11461static char *
11462pm_parse_stdin_fgets(char *string, int size, void *stream)
11463{
11464 RUBY_ASSERT(size > 0);
11465
11466 struct rb_stdin_wrapper * wrapped_stdin = (struct rb_stdin_wrapper *)stream;
11467
11468 VALUE line = rb_funcall(wrapped_stdin->rb_stdin, rb_intern("gets"), 1, INT2FIX(size - 1));
11469 if (NIL_P(line)) {
11470 return NULL;
11471 }
11472
11473 const char *cstr = RSTRING_PTR(line);
11474 long length = RSTRING_LEN(line);
11475
11476 memcpy(string, cstr, length);
11477 string[length] = '\0';
11478
11479 // We're reading strings from stdin via gets. We'll assume that if the
11480 // string is smaller than the requested length, and doesn't end with a
11481 // newline, that we hit EOF.
11482 if (length < (size - 1) && string[length - 1] != '\n') {
11483 wrapped_stdin->eof_seen = 1;
11484 }
11485
11486 return string;
11487}
11488
11489// We need access to this function when we're done parsing stdin.
11490void rb_reset_argf_lineno(long n);
11491
11497 VALUE
11498pm_parse_stdin(pm_parse_result_t *result)
11499{
11500 pm_options_frozen_string_literal_init(&result->options);
11501
11502 struct rb_stdin_wrapper wrapped_stdin = {
11503 rb_stdin,
11504 0
11505 };
11506
11507 pm_buffer_t buffer;
11508 pm_node_t *node = pm_parse_stream(&result->parser, &buffer, (void *) &wrapped_stdin, pm_parse_stdin_fgets, pm_parse_stdin_eof, &result->options);
11509
11510 // Copy the allocated buffer contents into the input string so that it gets
11511 // freed. At this point we've handed over ownership, so we don't need to
11512 // free the buffer itself.
11513 pm_string_owned_init(&result->input, (uint8_t *) pm_buffer_value(&buffer), pm_buffer_length(&buffer));
11514
11515 // When we're done parsing, we reset $. because we don't want the fact that
11516 // we went through an IO object to be visible to the user.
11517 rb_reset_argf_lineno(0);
11518
11519 return pm_parse_process(result, node, NULL);
11520}
11521
11522#define PM_VERSION_FOR_RELEASE(major, minor) PM_VERSION_FOR_RELEASE_IMPL(major, minor)
11523#define PM_VERSION_FOR_RELEASE_IMPL(major, minor) PM_OPTIONS_VERSION_CRUBY_##major##_##minor
11524
11525void pm_options_version_for_current_ruby_set(pm_options_t *options) {
11526 options->version = PM_VERSION_FOR_RELEASE(RUBY_VERSION_MAJOR, RUBY_VERSION_MINOR);
11527}
11528
11529#undef NEW_ISEQ
11530#define NEW_ISEQ OLD_ISEQ
11531
11532#undef NEW_CHILD_ISEQ
11533#define NEW_CHILD_ISEQ OLD_CHILD_ISEQ
RUBY_ASSERT
#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
@ PM_WARNING_LEVEL_VERBOSE
For warnings which should be emitted if $VERBOSE == true.
Definition diagnostic.h:413
PM_ERROR_LEVEL_ARGUMENT
@ PM_ERROR_LEVEL_ARGUMENT
For errors that should raise an argument error.
Definition diagnostic.h:399
PM_ERROR_LEVEL_LOAD
@ PM_ERROR_LEVEL_LOAD
For errors that should raise a load error.
Definition diagnostic.h:402
PM_ERROR_LEVEL_SYNTAX
@ PM_ERROR_LEVEL_SYNTAX
For errors that should raise a syntax error.
Definition diagnostic.h:396
RUBY_EVENT_END
#define RUBY_EVENT_END
Encountered an end of a class clause.
Definition event.h:40
RUBY_EVENT_B_RETURN
#define RUBY_EVENT_B_RETURN
Encountered a next statement.
Definition event.h:56
RUBY_EVENT_CLASS
#define RUBY_EVENT_CLASS
Encountered a new class.
Definition event.h:39
RUBY_EVENT_LINE
#define RUBY_EVENT_LINE
Encountered a new line.
Definition event.h:38
RUBY_EVENT_RETURN
#define RUBY_EVENT_RETURN
Encountered a return statement.
Definition event.h:42
RUBY_EVENT_B_CALL
#define RUBY_EVENT_B_CALL
Encountered an yield statement.
Definition event.h:55
RUBY_EVENT_CALL
#define RUBY_EVENT_CALL
A method, written in Ruby, is called.
Definition event.h:41
RUBY_EVENT_RESCUE
#define RUBY_EVENT_RESCUE
Encountered a rescue statement.
Definition event.h:61
rb_str_new2
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1675
ALLOCV
#define ALLOCV
Old name of RB_ALLOCV.
Definition memory.h:404
ALLOC
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:400
RFLOAT_VALUE
#define RFLOAT_VALUE
Old name of rb_float_value.
Definition double.h:28
T_STRING
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
xfree
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
Qundef
#define Qundef
Old name of RUBY_Qundef.
Definition special_consts.h:62
INT2FIX
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
rb_str_cat2
#define rb_str_cat2
Old name of rb_str_cat_cstr.
Definition string.h:1683
ID2SYM
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
OBJ_FREEZE
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
Definition fl_type.h:135
ULONG2NUM
#define ULONG2NUM
Old name of RB_ULONG2NUM.
Definition long.h:60
FIXABLE
#define FIXABLE
Old name of RB_FIXABLE.
Definition fixnum.h:25
xmalloc
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
LONG2FIX
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
ZALLOC_N
#define ZALLOC_N
Old name of RB_ZALLOC_N.
Definition memory.h:401
T_HASH
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
ALLOC_N
#define ALLOC_N
Old name of RB_ALLOC_N.
Definition memory.h:399
rb_exc_new3
#define rb_exc_new3
Old name of rb_exc_new_str.
Definition error.h:38
Qtrue
#define Qtrue
Old name of RUBY_Qtrue.
Definition special_consts.h:61
INT2NUM
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
Qnil
#define Qnil
Old name of RUBY_Qnil.
Definition special_consts.h:60
Qfalse
#define Qfalse
Old name of RUBY_Qfalse.
Definition special_consts.h:59
T_ARRAY
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
NIL_P
#define NIL_P
Old name of RB_NIL_P.
Definition special_consts.h:55
DBL2NUM
#define DBL2NUM
Old name of rb_float_new.
Definition double.h:29
xcalloc
#define xcalloc
Old name of ruby_xcalloc.
Definition xmalloc.h:55
NUM2LONG
#define NUM2LONG
Old name of RB_NUM2LONG.
Definition long.h:51
UINT2NUM
#define UINT2NUM
Old name of RB_UINT2NUM.
Definition int.h:46
CONST_ID
#define CONST_ID
Old name of RUBY_CONST_ID.
Definition symbol.h:47
ruby_debug
#define ruby_debug
This variable controls whether the interpreter is in debug mode.
Definition error.h:486
rb_eNotImpError
VALUE rb_eNotImpError
NotImplementedError exception.
Definition error.c:1440
rb_eStandardError
VALUE rb_eStandardError
StandardError exception.
Definition error.c:1427
rb_eLoadError
VALUE rb_eLoadError
LoadError exception.
Definition error.c:1448
rb_eTypeError
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1430
rb_eNoMatchingPatternError
VALUE rb_eNoMatchingPatternError
NoMatchingPatternError exception.
Definition error.c:1443
rb_warn
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:466
rb_exc_new
VALUE rb_exc_new(VALUE etype, const char *ptr, long len)
Creates an instance of the passed exception class.
Definition error.c:1468
rb_eNoMatchingPatternKeyError
VALUE rb_eNoMatchingPatternKeyError
NoMatchingPatternKeyError exception.
Definition error.c:1444
rb_exc_new_str
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
rb_errinfo
VALUE rb_errinfo(void)
This is the same as $! in Ruby.
Definition eval.c:1941
rb_eSyntaxError
VALUE rb_eSyntaxError
SyntaxError exception.
Definition error.c:1447
rb_syserr_new
VALUE rb_syserr_new(int n, const char *mesg)
Creates an exception object that represents the given C errno.
Definition error.c:3863
rb_cArray
VALUE rb_cArray
Array class.
Definition array.c:40
rb_obj_hide
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:104
rb_stdin
VALUE rb_stdin
STDIN constant.
Definition io.c:201
rb_obj_freeze
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1260
RB_OBJ_WRITTEN
#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
RB_OBJ_WRITE
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:603
rb_utf8_encoding
rb_encoding * rb_utf8_encoding(void)
Queries the encoding that represents UTF-8.
Definition encoding.c:1475
rb_ascii8bit_encoding
rb_encoding * rb_ascii8bit_encoding(void)
Queries the encoding that represents ASCII-8BIT a.k.a.
Definition encoding.c:1463
rb_usascii_encoding
rb_encoding * rb_usascii_encoding(void)
Queries the encoding that represents US-ASCII.
Definition encoding.c:1487
rb_enc_str_coderange
int rb_enc_str_coderange(VALUE str)
Scans the passed string to collect its code range.
Definition string.c:905
rb_enc_interned_str
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:12528
rb_enc_str_new_cstr
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:1103
rb_funcall
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1099
rb_ary_new
VALUE rb_ary_new(void)
Allocates a new, empty array.
Definition array.c:741
rb_hash_new
VALUE rb_hash_new(void)
Creates a new, empty hash object.
Definition hash.c:1477
rb_io_fdopen
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:9343
rb_range_new
VALUE rb_range_new(VALUE beg, VALUE end, int excl)
Creates a new Range.
Definition range.c:68
rb_rational_new
VALUE rb_rational_new(VALUE num, VALUE den)
Constructs a Rational, with reduction.
Definition rational.c:1974
rb_str_append
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:3681
rb_str_tmp_new
VALUE rb_str_tmp_new(long len)
Allocates a "temporary" string.
Definition string.c:1676
rb_str_new
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1498
rb_exc_new_cstr
#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
rb_str_buf_new_cstr
#define rb_str_buf_new_cstr(str)
Identical to rb_str_new_cstr, except done differently.
Definition string.h:1639
rb_str_concat
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:3923
rb_str_freeze
VALUE rb_str_freeze(VALUE str)
This is the implementation of String#freeze.
Definition string.c:3181
rb_str_new_cstr
#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
rb_ivar_set
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:1939
rb_const_get_at
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:3237
rb_const_defined_at
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:3559
rb_intern_const
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:284
rb_id2sym
VALUE rb_id2sym(ID id)
Allocates an instance of rb_cSymbol that has the given id.
Definition symbol.c:952
rb_sym2str
VALUE rb_sym2str(VALUE symbol)
Obtain a frozen string representation of a symbol (not including the leading colon).
Definition symbol.c:971
RUBY_IO_READABLE
@ RUBY_IO_READABLE
IO::READABLE
Definition io.h:82
rb_io_wait
VALUE rb_io_wait(VALUE io, VALUE events, VALUE timeout)
Blocks until the passed IO is ready for the passed events.
Definition io.c:1453
len
int len
Length of the buffer.
Definition io.h:8
rb_ractor_make_shareable
VALUE rb_ractor_make_shareable(VALUE obj)
Destructively transforms the passed object so that multiple Ractors can share it.
Definition ractor.c:3118
DECIMAL_SIZE_OF
#define DECIMAL_SIZE_OF(expr)
An approximation of decimal representation size.
Definition util.h:48
RB_INT2NUM
#define RB_INT2NUM
Just another name of rb_int2num_inline.
Definition int.h:37
RB_GC_GUARD
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
ruby::backward::cxxanyargs::type
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition cxxanyargs.hpp:56
pm_options_t
struct pm_options pm_options_t
The options that can be passed to the parser.
pm_parser_t
struct pm_parser pm_parser_t
The parser used to parse Ruby source.
Definition parser.h:267
pm_constant_id_t
uint32_t pm_constant_id_t
A constant id is a unique identifier for a constant in the constant pool.
Definition pm_constant_pool.h:31
pm_list_node_t
struct pm_list_node pm_list_node_t
This struct represents an abstract linked list that provides common functionality.
pm_string_init_result_t
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
@ PM_STRING_INIT_SUCCESS
Indicates that the string was successfully initialized.
Definition pm_string.h:107
PM_STRING_INIT_ERROR_GENERIC
@ 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
@ PM_STRING_INIT_ERROR_DIRECTORY
Indicates that the file that was attempted to be opened was a directory.
Definition pm_string.h:116
PM_ENCODING_US_ASCII_ENTRY
#define PM_ENCODING_US_ASCII_ENTRY
This is the US-ASCII encoding.
Definition encoding.h:252
PM_NODE_LIST_FOREACH
#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
prism.h
The main header file for the prism parser.
RARRAY_LEN
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
RARRAY_AREF
#define RARRAY_AREF(a, i)
Definition rarray.h:403
RARRAY_CONST_PTR
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
Definition rarray.h:52
errno
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
RTEST
#define RTEST
This is an old name of RB_TEST.
Definition special_consts.h:51
default_encoding
Definition encoding.c:1542
ensure_range
Definition compile.c:115
iseq_compile_data_ensure_node_stack
Definition compile.c:121
pm_alias_global_variable_node::old_name
struct pm_node * old_name
AliasGlobalVariableNode::old_name.
Definition ast.h:1129
pm_alias_global_variable_node::new_name
struct pm_node * new_name
AliasGlobalVariableNode::new_name.
Definition ast.h:1119
pm_alias_method_node::old_name
struct pm_node * old_name
AliasMethodNode::old_name.
Definition ast.h:1189
pm_alias_method_node::new_name
struct pm_node * new_name
AliasMethodNode::new_name.
Definition ast.h:1173
pm_alternation_pattern_node::left
struct pm_node * left
AlternationPatternNode::left.
Definition ast.h:1227
pm_alternation_pattern_node::right
struct pm_node * right
AlternationPatternNode::right.
Definition ast.h:1237
pm_and_node::left
struct pm_node * left
AndNode::left.
Definition ast.h:1278
pm_and_node::right
struct pm_node * right
AndNode::right.
Definition ast.h:1291
pm_arguments_node::base
pm_node_t base
The embedded base node.
Definition ast.h:1325
pm_arguments_node::arguments
struct pm_node_list arguments
ArgumentsNode::arguments.
Definition ast.h:1336
pm_array_node::elements
struct pm_node_list elements
ArrayNode::elements.
Definition ast.h:1364
pm_array_pattern_node::requireds
struct pm_node_list requireds
ArrayPatternNode::requireds.
Definition ast.h:1444
pm_array_pattern_node::rest
struct pm_node * rest
ArrayPatternNode::rest.
Definition ast.h:1454
pm_array_pattern_node::constant
struct pm_node * constant
ArrayPatternNode::constant.
Definition ast.h:1434
pm_array_pattern_node::posts
struct pm_node_list posts
ArrayPatternNode::posts.
Definition ast.h:1464
pm_assoc_node::value
struct pm_node * value
AssocNode::value.
Definition ast.h:1531
pm_assoc_node::key
struct pm_node * key
AssocNode::key.
Definition ast.h:1518
pm_assoc_splat_node::value
struct pm_node * value
AssocSplatNode::value.
Definition ast.h:1569
pm_back_reference_read_node::base
pm_node_t base
The embedded base node.
Definition ast.h:1596
pm_begin_node::ensure_clause
struct pm_ensure_node * ensure_clause
BeginNode::ensure_clause.
Definition ast.h:1678
pm_begin_node::rescue_clause
struct pm_rescue_node * rescue_clause
BeginNode::rescue_clause.
Definition ast.h:1658
pm_begin_node::statements
struct pm_statements_node * statements
BeginNode::statements.
Definition ast.h:1648
pm_begin_node::else_clause
struct pm_else_node * else_clause
BeginNode::else_clause.
Definition ast.h:1668
pm_block_argument_node::expression
struct pm_node * expression
BlockArgumentNode::expression.
Definition ast.h:1716
pm_block_node::parameters
struct pm_node * parameters
BlockNode::parameters.
Definition ast.h:1799
pm_block_node::body
struct pm_node * body
BlockNode::body.
Definition ast.h:1809
pm_block_node::locals
pm_constant_id_list_t locals
BlockNode::locals.
Definition ast.h:1785
pm_break_node::arguments
struct pm_arguments_node * arguments
BreakNode::arguments.
Definition ast.h:1989
pm_buffer_t
A pm_buffer_t is a simple memory buffer that stores data in a contiguous block of memory.
Definition pm_buffer.h:22
pm_call_and_write_node::value
struct pm_node * value
CallAndWriteNode::value.
Definition ast.h:2093
pm_call_and_write_node::read_name
pm_constant_id_t read_name
CallAndWriteNode::read_name.
Definition ast.h:2063
pm_call_and_write_node::write_name
pm_constant_id_t write_name
CallAndWriteNode::write_name.
Definition ast.h:2073
pm_call_and_write_node::receiver
struct pm_node * receiver
CallAndWriteNode::receiver.
Definition ast.h:2033
pm_call_node::closing_loc
pm_location_t closing_loc
CallNode::closing_loc.
Definition ast.h:2210
pm_call_node::receiver
struct pm_node * receiver
CallNode::receiver.
Definition ast.h:2148
pm_call_node::name
pm_constant_id_t name
CallNode::name.
Definition ast.h:2171
pm_call_node::base
pm_node_t base
The embedded base node.
Definition ast.h:2131
pm_call_node::message_loc
pm_location_t message_loc
CallNode::message_loc.
Definition ast.h:2181
pm_call_node::arguments
struct pm_arguments_node * arguments
CallNode::arguments.
Definition ast.h:2200
pm_call_node::block
struct pm_node * block
CallNode::block.
Definition ast.h:2220
pm_call_operator_write_node::read_name
pm_constant_id_t read_name
CallOperatorWriteNode::read_name.
Definition ast.h:2284
pm_call_operator_write_node::binary_operator
pm_constant_id_t binary_operator
CallOperatorWriteNode::binary_operator.
Definition ast.h:2304
pm_call_operator_write_node::receiver
struct pm_node * receiver
CallOperatorWriteNode::receiver.
Definition ast.h:2254
pm_call_operator_write_node::write_name
pm_constant_id_t write_name
CallOperatorWriteNode::write_name.
Definition ast.h:2294
pm_call_operator_write_node::value
struct pm_node * value
CallOperatorWriteNode::value.
Definition ast.h:2324
pm_call_or_write_node::receiver
struct pm_node * receiver
CallOrWriteNode::receiver.
Definition ast.h:2358
pm_call_or_write_node::value
struct pm_node * value
CallOrWriteNode::value.
Definition ast.h:2418
pm_call_or_write_node::write_name
pm_constant_id_t write_name
CallOrWriteNode::write_name.
Definition ast.h:2398
pm_call_or_write_node::read_name
pm_constant_id_t read_name
CallOrWriteNode::read_name.
Definition ast.h:2388
pm_call_target_node::name
pm_constant_id_t name
CallTargetNode::name.
Definition ast.h:2480
pm_call_target_node::receiver
struct pm_node * receiver
CallTargetNode::receiver.
Definition ast.h:2460
pm_capture_pattern_node::target
struct pm_local_variable_target_node * target
CapturePatternNode::target.
Definition ast.h:2528
pm_capture_pattern_node::value
struct pm_node * value
CapturePatternNode::value.
Definition ast.h:2518
pm_case_match_node::conditions
struct pm_node_list conditions
CaseMatchNode::conditions.
Definition ast.h:2578
pm_case_match_node::else_clause
struct pm_else_node * else_clause
CaseMatchNode::else_clause.
Definition ast.h:2588
pm_case_match_node::predicate
struct pm_node * predicate
CaseMatchNode::predicate.
Definition ast.h:2568
pm_case_node::predicate
struct pm_node * predicate
CaseNode::predicate.
Definition ast.h:2638
pm_case_node::else_clause
struct pm_else_node * else_clause
CaseNode::else_clause.
Definition ast.h:2658
pm_case_node::conditions
struct pm_node_list conditions
CaseNode::conditions.
Definition ast.h:2648
pm_class_node::constant_path
struct pm_node * constant_path
ClassNode::constant_path.
Definition ast.h:2716
pm_class_node::locals
pm_constant_id_list_t locals
ClassNode::locals.
Definition ast.h:2701
pm_class_node::name
pm_constant_id_t name
ClassNode::name.
Definition ast.h:2766
pm_class_node::body
struct pm_node * body
ClassNode::body.
Definition ast.h:2747
pm_class_node::superclass
struct pm_node * superclass
ClassNode::superclass.
Definition ast.h:2736
pm_class_variable_and_write_node::value
struct pm_node * value
ClassVariableAndWriteNode::value.
Definition ast.h:2824
pm_class_variable_and_write_node::name
pm_constant_id_t name
ClassVariableAndWriteNode::name.
Definition ast.h:2794
pm_class_variable_operator_write_node::name
pm_constant_id_t name
ClassVariableOperatorWriteNode::name.
Definition ast.h:2847
pm_class_variable_operator_write_node::binary_operator
pm_constant_id_t binary_operator
ClassVariableOperatorWriteNode::binary_operator.
Definition ast.h:2867
pm_class_variable_operator_write_node::value
struct pm_node * value
ClassVariableOperatorWriteNode::value.
Definition ast.h:2862
pm_class_variable_or_write_node::name
pm_constant_id_t name
ClassVariableOrWriteNode::name.
Definition ast.h:2890
pm_class_variable_or_write_node::value
struct pm_node * value
ClassVariableOrWriteNode::value.
Definition ast.h:2905
pm_class_variable_read_node::name
pm_constant_id_t name
ClassVariableReadNode::name.
Definition ast.h:2934
pm_class_variable_target_node::name
pm_constant_id_t name
ClassVariableTargetNode::name.
Definition ast.h:2957
pm_class_variable_write_node::value
struct pm_node * value
ClassVariableWriteNode::value.
Definition ast.h:3009
pm_class_variable_write_node::name
pm_constant_id_t name
ClassVariableWriteNode::name.
Definition ast.h:2986
pm_constant_and_write_node::name_loc
pm_location_t name_loc
ConstantAndWriteNode::name_loc.
Definition ast.h:3047
pm_constant_and_write_node::name
pm_constant_id_t name
ConstantAndWriteNode::name.
Definition ast.h:3042
pm_constant_and_write_node::value
struct pm_node * value
ConstantAndWriteNode::value.
Definition ast.h:3057
pm_constant_id_list_t
A list of constant IDs.
Definition pm_constant_pool.h:36
pm_constant_id_list_t::size
size_t size
The number of constant ids in the list.
Definition pm_constant_pool.h:38
pm_constant_id_list_t::capacity
size_t capacity
The number of constant ids that have been allocated in the list.
Definition pm_constant_pool.h:41
pm_constant_id_list_t::ids
pm_constant_id_t * ids
The constant ids in the list.
Definition pm_constant_pool.h:44
pm_constant_operator_write_node::name
pm_constant_id_t name
ConstantOperatorWriteNode::name.
Definition ast.h:3080
pm_constant_operator_write_node::name_loc
pm_location_t name_loc
ConstantOperatorWriteNode::name_loc.
Definition ast.h:3085
pm_constant_operator_write_node::binary_operator
pm_constant_id_t binary_operator
ConstantOperatorWriteNode::binary_operator.
Definition ast.h:3100
pm_constant_operator_write_node::value
struct pm_node * value
ConstantOperatorWriteNode::value.
Definition ast.h:3095
pm_constant_or_write_node::name_loc
pm_location_t name_loc
ConstantOrWriteNode::name_loc.
Definition ast.h:3128
pm_constant_or_write_node::name
pm_constant_id_t name
ConstantOrWriteNode::name.
Definition ast.h:3123
pm_constant_or_write_node::value
struct pm_node * value
ConstantOrWriteNode::value.
Definition ast.h:3138
pm_constant_path_and_write_node::target
struct pm_constant_path_node * target
ConstantPathAndWriteNode::target.
Definition ast.h:3161
pm_constant_path_and_write_node::value
struct pm_node * value
ConstantPathAndWriteNode::value.
Definition ast.h:3171
pm_constant_path_node::name
pm_constant_id_t name
ConstantPathNode::name.
Definition ast.h:3212
pm_constant_path_node::parent
struct pm_node * parent
ConstantPathNode::parent.
Definition ast.h:3205
pm_constant_path_operator_write_node::target
struct pm_constant_path_node * target
ConstantPathOperatorWriteNode::target.
Definition ast.h:3261
pm_constant_path_operator_write_node::value
struct pm_node * value
ConstantPathOperatorWriteNode::value.
Definition ast.h:3271
pm_constant_path_operator_write_node::binary_operator
pm_constant_id_t binary_operator
ConstantPathOperatorWriteNode::binary_operator.
Definition ast.h:3276
pm_constant_path_or_write_node::value
struct pm_node * value
ConstantPathOrWriteNode::value.
Definition ast.h:3309
pm_constant_path_or_write_node::target
struct pm_constant_path_node * target
ConstantPathOrWriteNode::target.
Definition ast.h:3299
pm_constant_path_target_node::parent
struct pm_node * parent
ConstantPathTargetNode::parent.
Definition ast.h:3332
pm_constant_path_target_node::name
pm_constant_id_t name
ConstantPathTargetNode::name.
Definition ast.h:3337
pm_constant_path_write_node::target
struct pm_constant_path_node * target
ConstantPathWriteNode::target.
Definition ast.h:3384
pm_constant_path_write_node::value
struct pm_node * value
ConstantPathWriteNode::value.
Definition ast.h:3404
pm_constant_pool_t::size
uint32_t size
The number of buckets in the hash map.
Definition pm_constant_pool.h:142
pm_constant_pool_t::constants
pm_constant_t * constants
The constants that are stored in the buckets.
Definition pm_constant_pool.h:139
pm_constant_read_node::base
pm_node_t base
The embedded base node.
Definition ast.h:3421
pm_constant_read_node::name
pm_constant_id_t name
ConstantReadNode::name.
Definition ast.h:3433
pm_constant_t
A constant in the pool which effectively stores a string.
Definition pm_constant_pool.h:125
pm_constant_t::length
size_t length
The length of the string.
Definition pm_constant_pool.h:130
pm_constant_t::start
const uint8_t * start
A pointer to the start of the string.
Definition pm_constant_pool.h:127
pm_constant_target_node::name
pm_constant_id_t name
ConstantTargetNode::name.
Definition ast.h:3456
pm_constant_write_node::value
struct pm_node * value
ConstantWriteNode::value.
Definition ast.h:3508
pm_constant_write_node::name
pm_constant_id_t name
ConstantWriteNode::name.
Definition ast.h:3485
pm_def_node::parameters
struct pm_parameters_node * parameters
DefNode::parameters.
Definition ast.h:3557
pm_def_node::name
pm_constant_id_t name
DefNode::name.
Definition ast.h:3542
pm_def_node::body
struct pm_node * body
DefNode::body.
Definition ast.h:3562
pm_def_node::receiver
struct pm_node * receiver
DefNode::receiver.
Definition ast.h:3552
pm_def_node::base
pm_node_t base
The embedded base node.
Definition ast.h:3536
pm_def_node::locals
pm_constant_id_list_t locals
DefNode::locals.
Definition ast.h:3567
pm_defined_node::value
struct pm_node * value
DefinedNode::value.
Definition ast.h:3625
pm_diagnostic_t
This struct represents a diagnostic generated during parsing.
Definition diagnostic.h:364
pm_diagnostic_t::location
pm_location_t location
The location of the diagnostic in the source.
Definition diagnostic.h:369
pm_diagnostic_t::message
const char * message
The message associated with the diagnostic.
Definition diagnostic.h:375
pm_diagnostic_t::node
pm_list_node_t node
The embedded base node.
Definition diagnostic.h:366
pm_diagnostic_t::level
uint8_t level
The level of the diagnostic, see pm_error_level_t and pm_warning_level_t for possible values.
Definition diagnostic.h:388
pm_else_node::statements
struct pm_statements_node * statements
ElseNode::statements.
Definition ast.h:3663
pm_embedded_statements_node::statements
struct pm_statements_node * statements
EmbeddedStatementsNode::statements.
Definition ast.h:3696
pm_embedded_variable_node::variable
struct pm_node * variable
EmbeddedVariableNode::variable.
Definition ast.h:3729
pm_encoding_t
This struct defines the functions necessary to implement the encoding interface so we can determine h...
Definition encoding.h:23
pm_encoding_t::char_width
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
pm_encoding_t::name
const char * name
The name of the encoding.
Definition encoding.h:56
pm_ensure_node::statements
struct pm_statements_node * statements
EnsureNode::statements.
Definition ast.h:3761
pm_find_pattern_node::constant
struct pm_node * constant
FindPatternNode::constant.
Definition ast.h:3821
pm_find_pattern_node::right
struct pm_node * right
FindPatternNode::right.
Definition ast.h:3860
pm_find_pattern_node::requireds
struct pm_node_list requireds
FindPatternNode::requireds.
Definition ast.h:3847
pm_find_pattern_node::left
struct pm_splat_node * left
FindPatternNode::left.
Definition ast.h:3834
pm_flip_flop_node::base
pm_node_t base
The embedded base node.
Definition ast.h:3906
pm_flip_flop_node::left
struct pm_node * left
FlipFlopNode::left.
Definition ast.h:3912
pm_flip_flop_node::right
struct pm_node * right
FlipFlopNode::right.
Definition ast.h:3917
pm_float_node::value
double value
FloatNode::value.
Definition ast.h:3947
pm_for_node::statements
struct pm_statements_node * statements
ForNode::statements.
Definition ast.h:3997
pm_for_node::collection
struct pm_node * collection
ForNode::collection.
Definition ast.h:3985
pm_forwarding_super_node::block
struct pm_block_node * block
ForwardingSuperNode::block.
Definition ast.h:4099
pm_global_variable_and_write_node::value
struct pm_node * value
GlobalVariableAndWriteNode::value.
Definition ast.h:4137
pm_global_variable_and_write_node::name
pm_constant_id_t name
GlobalVariableAndWriteNode::name.
Definition ast.h:4122
pm_global_variable_operator_write_node::name
pm_constant_id_t name
GlobalVariableOperatorWriteNode::name.
Definition ast.h:4160
pm_global_variable_operator_write_node::binary_operator
pm_constant_id_t binary_operator
GlobalVariableOperatorWriteNode::binary_operator.
Definition ast.h:4180
pm_global_variable_operator_write_node::value
struct pm_node * value
GlobalVariableOperatorWriteNode::value.
Definition ast.h:4175
pm_global_variable_or_write_node::name
pm_constant_id_t name
GlobalVariableOrWriteNode::name.
Definition ast.h:4203
pm_global_variable_or_write_node::value
struct pm_node * value
GlobalVariableOrWriteNode::value.
Definition ast.h:4218
pm_global_variable_read_node::name
pm_constant_id_t name
GlobalVariableReadNode::name.
Definition ast.h:4247
pm_global_variable_target_node::name
pm_constant_id_t name
GlobalVariableTargetNode::name.
Definition ast.h:4270
pm_global_variable_write_node::value
struct pm_node * value
GlobalVariableWriteNode::value.
Definition ast.h:4322
pm_global_variable_write_node::name
pm_constant_id_t name
GlobalVariableWriteNode::name.
Definition ast.h:4299
pm_hash_node::elements
struct pm_node_list elements
HashNode::elements.
Definition ast.h:4373
pm_hash_pattern_node::elements
struct pm_node_list elements
HashPatternNode::elements.
Definition ast.h:4433
pm_hash_pattern_node::rest
struct pm_node * rest
HashPatternNode::rest.
Definition ast.h:4449
pm_hash_pattern_node::constant
struct pm_node * constant
HashPatternNode::constant.
Definition ast.h:4423
pm_if_node::predicate
struct pm_node * predicate
IfNode::predicate.
Definition ast.h:4529
pm_if_node::statements
struct pm_statements_node * statements
IfNode::statements.
Definition ast.h:4556
pm_imaginary_node::numeric
struct pm_node * numeric
ImaginaryNode::numeric.
Definition ast.h:4610
pm_implicit_node::value
struct pm_node * value
ImplicitNode::value.
Definition ast.h:4639
pm_in_node::statements
struct pm_statements_node * statements
InNode::statements.
Definition ast.h:4694
pm_in_node::pattern
struct pm_node * pattern
InNode::pattern.
Definition ast.h:4689
pm_index_and_write_node::arguments
struct pm_arguments_node * arguments
IndexAndWriteNode::arguments.
Definition ast.h:4748
pm_index_and_write_node::receiver
struct pm_node * receiver
IndexAndWriteNode::receiver.
Definition ast.h:4733
pm_index_and_write_node::block
struct pm_block_argument_node * block
IndexAndWriteNode::block.
Definition ast.h:4758
pm_index_and_write_node::value
struct pm_node * value
IndexAndWriteNode::value.
Definition ast.h:4768
pm_index_operator_write_node::block
struct pm_block_argument_node * block
IndexOperatorWriteNode::block.
Definition ast.h:4822
pm_index_operator_write_node::value
struct pm_node * value
IndexOperatorWriteNode::value.
Definition ast.h:4837
pm_index_operator_write_node::arguments
struct pm_arguments_node * arguments
IndexOperatorWriteNode::arguments.
Definition ast.h:4812
pm_index_operator_write_node::binary_operator
pm_constant_id_t binary_operator
IndexOperatorWriteNode::binary_operator.
Definition ast.h:4827
pm_index_operator_write_node::receiver
struct pm_node * receiver
IndexOperatorWriteNode::receiver.
Definition ast.h:4797
pm_index_or_write_node::block
struct pm_block_argument_node * block
IndexOrWriteNode::block.
Definition ast.h:4891
pm_index_or_write_node::receiver
struct pm_node * receiver
IndexOrWriteNode::receiver.
Definition ast.h:4866
pm_index_or_write_node::value
struct pm_node * value
IndexOrWriteNode::value.
Definition ast.h:4901
pm_index_or_write_node::arguments
struct pm_arguments_node * arguments
IndexOrWriteNode::arguments.
Definition ast.h:4881
pm_index_target_node::receiver
struct pm_node * receiver
IndexTargetNode::receiver.
Definition ast.h:4938
pm_index_target_node::arguments
struct pm_arguments_node * arguments
IndexTargetNode::arguments.
Definition ast.h:4948
pm_index_target_node::block
struct pm_block_argument_node * block
IndexTargetNode::block.
Definition ast.h:4958
pm_instance_variable_and_write_node::value
struct pm_node * value
InstanceVariableAndWriteNode::value.
Definition ast.h:4996
pm_instance_variable_and_write_node::name
pm_constant_id_t name
InstanceVariableAndWriteNode::name.
Definition ast.h:4981
pm_instance_variable_operator_write_node::value
struct pm_node * value
InstanceVariableOperatorWriteNode::value.
Definition ast.h:5034
pm_instance_variable_operator_write_node::binary_operator
pm_constant_id_t binary_operator
InstanceVariableOperatorWriteNode::binary_operator.
Definition ast.h:5039
pm_instance_variable_operator_write_node::name
pm_constant_id_t name
InstanceVariableOperatorWriteNode::name.
Definition ast.h:5019
pm_instance_variable_or_write_node::value
struct pm_node * value
InstanceVariableOrWriteNode::value.
Definition ast.h:5077
pm_instance_variable_or_write_node::name
pm_constant_id_t name
InstanceVariableOrWriteNode::name.
Definition ast.h:5062
pm_instance_variable_read_node::name
pm_constant_id_t name
InstanceVariableReadNode::name.
Definition ast.h:5106
pm_instance_variable_target_node::name
pm_constant_id_t name
InstanceVariableTargetNode::name.
Definition ast.h:5129
pm_instance_variable_write_node::name
pm_constant_id_t name
InstanceVariableWriteNode::name.
Definition ast.h:5158
pm_instance_variable_write_node::value
struct pm_node * value
InstanceVariableWriteNode::value.
Definition ast.h:5181
pm_integer_node::value
pm_integer_t value
IntegerNode::value.
Definition ast.h:5222
pm_integer_t
A structure represents an arbitrary-sized integer.
Definition pm_integer.h:20
pm_integer_t::length
size_t length
The number of allocated values.
Definition pm_integer.h:25
pm_integer_t::value
uint32_t value
Embedded value for small integer.
Definition pm_integer.h:36
pm_integer_t::values
uint32_t * values
List of 32-bit integers.
Definition pm_integer.h:30
pm_integer_t::negative
bool negative
Whether or not the integer is negative.
Definition pm_integer.h:42
pm_interpolated_string_node::parts
struct pm_node_list parts
InterpolatedStringNode::parts.
Definition ast.h:5346
pm_interpolated_symbol_node::parts
struct pm_node_list parts
InterpolatedSymbolNode::parts.
Definition ast.h:5379
pm_interpolated_x_string_node::parts
struct pm_node_list parts
InterpolatedXStringNode::parts.
Definition ast.h:5412
pm_keyword_hash_node::elements
struct pm_node_list elements
KeywordHashNode::elements.
Definition ast.h:5479
pm_lambda_node::body
struct pm_node * body
LambdaNode::body.
Definition ast.h:5564
pm_lambda_node::opening_loc
pm_location_t opening_loc
LambdaNode::opening_loc.
Definition ast.h:5549
pm_lambda_node::parameters
struct pm_node * parameters
LambdaNode::parameters.
Definition ast.h:5559
pm_lambda_node::operator_loc
pm_location_t operator_loc
LambdaNode::operator_loc.
Definition ast.h:5544
pm_lambda_node::locals
pm_constant_id_list_t locals
LambdaNode::locals.
Definition ast.h:5539
pm_line_column_t
A line and column in a string.
Definition pm_newline_list.h:45
pm_line_column_t::column
uint32_t column
The column number.
Definition pm_newline_list.h:50
pm_line_column_t::line
int32_t line
The line number.
Definition pm_newline_list.h:47
pm_list_node::next
struct pm_list_node * next
A pointer to the next node in the list.
Definition pm_list.h:48
pm_list_t
This represents the overall linked list.
Definition pm_list.h:55
pm_list_t::head
pm_list_node_t * head
A pointer to the head of the list.
Definition pm_list.h:60
pm_list_t::size
size_t size
The size of the list.
Definition pm_list.h:57
pm_local_table_insert_ctx
Definition prism_compile.c:4585
pm_local_variable_and_write_node::name
pm_constant_id_t name
LocalVariableAndWriteNode::name.
Definition ast.h:5602
pm_local_variable_and_write_node::depth
uint32_t depth
LocalVariableAndWriteNode::depth.
Definition ast.h:5607
pm_local_variable_and_write_node::value
struct pm_node * value
LocalVariableAndWriteNode::value.
Definition ast.h:5597
pm_local_variable_operator_write_node::depth
uint32_t depth
LocalVariableOperatorWriteNode::depth.
Definition ast.h:5655
pm_local_variable_operator_write_node::binary_operator
pm_constant_id_t binary_operator
LocalVariableOperatorWriteNode::binary_operator.
Definition ast.h:5650
pm_local_variable_operator_write_node::value
struct pm_node * value
LocalVariableOperatorWriteNode::value.
Definition ast.h:5640
pm_local_variable_operator_write_node::name
pm_constant_id_t name
LocalVariableOperatorWriteNode::name.
Definition ast.h:5645
pm_local_variable_or_write_node::depth
uint32_t depth
LocalVariableOrWriteNode::depth.
Definition ast.h:5698
pm_local_variable_or_write_node::value
struct pm_node * value
LocalVariableOrWriteNode::value.
Definition ast.h:5688
pm_local_variable_or_write_node::name
pm_constant_id_t name
LocalVariableOrWriteNode::name.
Definition ast.h:5693
pm_local_variable_read_node::depth
uint32_t depth
LocalVariableReadNode::depth.
Definition ast.h:5744
pm_local_variable_read_node::name
pm_constant_id_t name
LocalVariableReadNode::name.
Definition ast.h:5731
pm_local_variable_target_node::depth
uint32_t depth
LocalVariableTargetNode::depth.
Definition ast.h:5775
pm_local_variable_target_node::name
pm_constant_id_t name
LocalVariableTargetNode::name.
Definition ast.h:5770
pm_local_variable_write_node::value
struct pm_node * value
LocalVariableWriteNode::value.
Definition ast.h:5844
pm_local_variable_write_node::depth
uint32_t depth
LocalVariableWriteNode::depth.
Definition ast.h:5817
pm_local_variable_write_node::name
pm_constant_id_t name
LocalVariableWriteNode::name.
Definition ast.h:5804
pm_location_t
This represents a range of bytes in the source string to which a node or token corresponds.
Definition ast.h:544
pm_location_t::start
const uint8_t * start
A pointer to the start location of the range in the source.
Definition ast.h:546
pm_location_t::end
const uint8_t * end
A pointer to the end location of the range in the source.
Definition ast.h:549
pm_match_predicate_node::pattern
struct pm_node * pattern
MatchPredicateNode::pattern.
Definition ast.h:5933
pm_match_predicate_node::value
struct pm_node * value
MatchPredicateNode::value.
Definition ast.h:5928
pm_match_required_node::value
struct pm_node * value
MatchRequiredNode::value.
Definition ast.h:5966
pm_match_required_node::pattern
struct pm_node * pattern
MatchRequiredNode::pattern.
Definition ast.h:6015
pm_match_write_node::targets
struct pm_node_list targets
MatchWriteNode::targets.
Definition ast.h:6053
pm_match_write_node::call
struct pm_call_node * call
MatchWriteNode::call.
Definition ast.h:6048
pm_module_node::constant_path
struct pm_node * constant_path
ModuleNode::constant_path.
Definition ast.h:6101
pm_module_node::body
struct pm_node * body
ModuleNode::body.
Definition ast.h:6106
pm_module_node::locals
pm_constant_id_list_t locals
ModuleNode::locals.
Definition ast.h:6091
pm_module_node::name
pm_constant_id_t name
ModuleNode::name.
Definition ast.h:6116
pm_multi_target_node::lefts
struct pm_node_list lefts
MultiTargetNode::lefts.
Definition ast.h:6154
pm_multi_target_node::rest
struct pm_node * rest
MultiTargetNode::rest.
Definition ast.h:6174
pm_multi_target_node::rights
struct pm_node_list rights
MultiTargetNode::rights.
Definition ast.h:6184
pm_multi_target_state_node
This is a node in the multi target state linked list.
Definition prism_compile.c:4770
pm_multi_target_state_t
As we're compiling a multi target, we need to track additional information whenever there is a parent...
Definition prism_compile.c:4796
pm_multi_write_node::value
struct pm_node * value
MultiWriteNode::value.
Definition ast.h:6307
pm_multi_write_node::rest
struct pm_node * rest
MultiWriteNode::rest.
Definition ast.h:6257
pm_multi_write_node::rights
struct pm_node_list rights
MultiWriteNode::rights.
Definition ast.h:6267
pm_multi_write_node::lefts
struct pm_node_list lefts
MultiWriteNode::lefts.
Definition ast.h:6237
pm_newline_list_t
A list of offsets of newlines in a string.
Definition pm_newline_list.h:28
pm_newline_list_t::start
const uint8_t * start
A pointer to the start of the source string.
Definition pm_newline_list.h:30
pm_newline_list_t::offsets
size_t * offsets
The list of offsets.
Definition pm_newline_list.h:39
pm_newline_list_t::size
size_t size
The number of offsets in the list.
Definition pm_newline_list.h:33
pm_next_node::arguments
struct pm_arguments_node * arguments
NextNode::arguments.
Definition ast.h:6330
pm_node_list::size
size_t size
The number of nodes in the list.
Definition ast.h:559
pm_node_list::nodes
struct pm_node ** nodes
The nodes in the list.
Definition ast.h:565
pm_node_location_t
This compiler defines its own concept of the location of a node.
Definition prism_compile.c:9
pm_node_location_t::line
int32_t line
This is the line number of a node.
Definition prism_compile.c:11
pm_node_location_t::node_id
uint32_t node_id
This is a unique identifier for the node.
Definition prism_compile.c:14
pm_node::type
pm_node_type_t type
This represents the type of the node.
Definition ast.h:1073
pm_node::node_id
uint32_t node_id
The unique identifier for this node, which is deterministic based on the source.
Definition ast.h:1085
pm_node::flags
pm_node_flags_t flags
This represents any flags on the node.
Definition ast.h:1079
pm_node::location
pm_location_t location
This is the location of the node in the source.
Definition ast.h:1091
pm_numbered_reference_read_node::number
uint32_t number
NumberedReferenceReadNode::number.
Definition ast.h:6436
pm_optional_keyword_parameter_node::name
pm_constant_id_t name
OptionalKeywordParameterNode::name.
Definition ast.h:6463
pm_optional_keyword_parameter_node::value
struct pm_node * value
OptionalKeywordParameterNode::value.
Definition ast.h:6473
pm_optional_parameter_node::value
struct pm_node * value
OptionalParameterNode::value.
Definition ast.h:6515
pm_optional_parameter_node::name
pm_constant_id_t name
OptionalParameterNode::name.
Definition ast.h:6500
pm_options::version
pm_options_version_t version
The version of prism that we should be parsing with.
Definition options.h:150
pm_or_node::left
struct pm_node * left
OrNode::left.
Definition ast.h:6546
pm_or_node::right
struct pm_node * right
OrNode::right.
Definition ast.h:6559
pm_parameters_node::rest
struct pm_node * rest
ParametersNode::rest.
Definition ast.h:6603
pm_parameters_node::requireds
struct pm_node_list requireds
ParametersNode::requireds.
Definition ast.h:6593
pm_parameters_node::block
struct pm_block_parameter_node * block
ParametersNode::block.
Definition ast.h:6623
pm_parameters_node::optionals
struct pm_node_list optionals
ParametersNode::optionals.
Definition ast.h:6598
pm_parameters_node::posts
struct pm_node_list posts
ParametersNode::posts.
Definition ast.h:6608
pm_parameters_node::base
pm_node_t base
The embedded base node.
Definition ast.h:6587
pm_parameters_node::keyword_rest
struct pm_node * keyword_rest
ParametersNode::keyword_rest.
Definition ast.h:6618
pm_parameters_node::keywords
struct pm_node_list keywords
ParametersNode::keywords.
Definition ast.h:6613
pm_parentheses_node::body
struct pm_node * body
ParenthesesNode::body.
Definition ast.h:6649
pm_parse_error_format_t
The format that will be used to format the errors into the output.
Definition prism_compile.c:10546
pm_parse_error_format_t::blank_prefix_length
size_t blank_prefix_length
The length of the blank prefix.
Definition prism_compile.c:10557
pm_parse_error_format_t::blank_prefix
const char * blank_prefix
The prefix that will be used for blank lines.
Definition prism_compile.c:10551
pm_parse_error_format_t::divider_length
size_t divider_length
The length of the divider.
Definition prism_compile.c:10560
pm_parse_error_format_t::number_prefix
const char * number_prefix
The prefix that will be used for line numbers.
Definition prism_compile.c:10548
pm_parse_error_format_t::divider
const char * divider
The divider that will be used between sections of source code.
Definition prism_compile.c:10554
pm_parse_error_t
An error that is going to be formatted into the output.
Definition prism_compile.c:10531
pm_parse_error_t::error
pm_diagnostic_t * error
A pointer to the diagnostic that was generated during parsing.
Definition prism_compile.c:10533
pm_parse_error_t::column_end
uint32_t column_end
The column end of the diagnostic message.
Definition prism_compile.c:10542
pm_parse_error_t::line
int32_t line
The start line of the diagnostic message.
Definition prism_compile.c:10536
pm_parse_error_t::column_start
uint32_t column_start
The column start of the diagnostic message.
Definition prism_compile.c:10539
pm_parse_result_t
Definition prism_compile.h:69
pm_parse_result_t::parsed
bool parsed
Whether or not this parse result has performed its parsing yet.
Definition prism_compile.h:83
pm_parse_result_t::node
pm_scope_node_t node
The resulting scope node that will hold the generated AST.
Definition prism_compile.h:80
pm_parse_result_t::input
pm_string_t input
The input that represents the source to be parsed.
Definition prism_compile.h:77
pm_parse_result_t::parser
pm_parser_t parser
The parser that will do the actual parsing.
Definition prism_compile.h:71
pm_parse_result_t::options
pm_options_t options
The options that will be passed to the parser.
Definition prism_compile.h:74
pm_parser::encoding
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
pm_parser::end
const uint8_t * end
The pointer to the end of the source.
Definition parser.h:694
pm_parser::constant_pool
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
pm_parser::start
const uint8_t * start
The pointer to the start of the source.
Definition parser.h:691
pm_parser::error_list
pm_list_t error_list
The list of errors that have been found while parsing.
Definition parser.h:734
pm_parser::warning_list
pm_list_t warning_list
The list of warnings that have been found while parsing.
Definition parser.h:731
pm_parser::start_line
int32_t start_line
The line number at the start of the parse.
Definition parser.h:809
pm_parser::filepath
pm_string_t filepath
This is the path of the file being parsed.
Definition parser.h:780
pm_parser::newline_list
pm_newline_list_t newline_list
This is the list of newline offsets in the source file.
Definition parser.h:789
pm_pinned_variable_node::variable
struct pm_node * variable
PinnedVariableNode::variable.
Definition ast.h:6745
pm_post_execution_node::statements
struct pm_statements_node * statements
PostExecutionNode::statements.
Definition ast.h:6778
pm_pre_execution_node::statements
struct pm_statements_node * statements
PreExecutionNode::statements.
Definition ast.h:6816
pm_program_node::statements
struct pm_statements_node * statements
ProgramNode::statements.
Definition ast.h:6856
pm_range_node::right
struct pm_node * right
RangeNode::right.
Definition ast.h:6907
pm_range_node::left
struct pm_node * left
RangeNode::left.
Definition ast.h:6893
pm_rational_node::denominator
pm_integer_t denominator
RationalNode::denominator.
Definition ast.h:6956
pm_rational_node::numerator
pm_integer_t numerator
RationalNode::numerator.
Definition ast.h:6947
pm_required_parameter_node::name
pm_constant_id_t name
RequiredParameterNode::name.
Definition ast.h:7084
pm_rescue_modifier_node::rescue_expression
struct pm_node * rescue_expression
RescueModifierNode::rescue_expression.
Definition ast.h:7117
pm_rescue_modifier_node::expression
struct pm_node * expression
RescueModifierNode::expression.
Definition ast.h:7107
pm_rescue_node::subsequent
struct pm_rescue_node * subsequent
RescueNode::subsequent.
Definition ast.h:7175
pm_rescue_node::reference
struct pm_node * reference
RescueNode::reference.
Definition ast.h:7160
pm_rescue_node::exceptions
struct pm_node_list exceptions
RescueNode::exceptions.
Definition ast.h:7150
pm_rescue_node::statements
struct pm_statements_node * statements
RescueNode::statements.
Definition ast.h:7170
pm_return_node::arguments
struct pm_arguments_node * arguments
ReturnNode::arguments.
Definition ast.h:7258
pm_scope_node::filepath_encoding
rb_encoding * filepath_encoding
This is the encoding of the actual filepath object that will be used when a FILE node is compiled or ...
Definition prism_compile.h:47
pm_scope_node::pre_execution_anchor
struct iseq_link_anchor * pre_execution_anchor
This will only be set on the top-level scope node.
Definition prism_compile.h:63
pm_scope_node::script_lines
VALUE * script_lines
This is a pointer to the list of script lines for the ISEQs that will be associated with this scope n...
Definition prism_compile.h:40
pm_shareable_constant_node::write
struct pm_node * write
ShareableConstantNode::write.
Definition ast.h:7307
pm_shareable_constant_node::base
pm_node_t base
The embedded base node.
Definition ast.h:7299
pm_singleton_class_node::locals
pm_constant_id_list_t locals
SingletonClassNode::locals.
Definition ast.h:7330
pm_singleton_class_node::expression
struct pm_node * expression
SingletonClassNode::expression.
Definition ast.h:7345
pm_singleton_class_node::body
struct pm_node * body
SingletonClassNode::body.
Definition ast.h:7350
pm_source_file_node::filepath
pm_string_t filepath
SourceFileNode::filepath.
Definition ast.h:7404
pm_splat_node::expression
struct pm_node * expression
SplatNode::expression.
Definition ast.h:7450
pm_statements_node::body
struct pm_node_list body
StatementsNode::body.
Definition ast.h:7473
pm_statements_node::base
pm_node_t base
The embedded base node.
Definition ast.h:7467
pm_string_node::unescaped
pm_string_t unescaped
StringNode::unescaped.
Definition ast.h:7523
pm_string_t
A generic string type that can have various ownership semantics.
Definition pm_string.h:33
pm_super_node::arguments
struct pm_arguments_node * arguments
SuperNode::arguments.
Definition ast.h:7559
pm_super_node::block
struct pm_node * block
SuperNode::block.
Definition ast.h:7569
pm_symbol_node::unescaped
pm_string_t unescaped
SymbolNode::unescaped.
Definition ast.h:7615
pm_symbol_node::base
pm_node_t base
The embedded base node.
Definition ast.h:7594
pm_undef_node::names
struct pm_node_list names
UndefNode::names.
Definition ast.h:7656
pm_unless_node::statements
struct pm_statements_node * statements
UnlessNode::statements.
Definition ast.h:7729
pm_unless_node::predicate
struct pm_node * predicate
UnlessNode::predicate.
Definition ast.h:7708
pm_unless_node::else_clause
struct pm_else_node * else_clause
UnlessNode::else_clause.
Definition ast.h:7739
pm_until_node::base
pm_node_t base
The embedded base node.
Definition ast.h:7772
pm_while_node::base
pm_node_t base
The embedded base node.
Definition ast.h:7861
pm_x_string_node::unescaped
pm_string_t unescaped
XStringNode::unescaped.
Definition ast.h:7929
pm_yield_node::arguments
struct pm_arguments_node * arguments
YieldNode::arguments.
Definition ast.h:7962
rb_builtin_function
Definition builtin.h:6
rb_callinfo_kwarg
Definition vm_callinfo.h:48
rb_callinfo
Definition vm_callinfo.h:65
rb_iseq_constant_body
Definition vm_core.h:404
rb_iseq_constant_body::param
struct rb_iseq_constant_body::@000024342312237062266020177166377106262102236123 param
parameter information
rb_iseq_new_with_callback_callback_func
Definition vm_core.h:1233
rb_stdin_wrapper
Definition prism_compile.c:11446
ID
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
VALUE
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
RB_TYPE_P
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