Ruby 3.5.0dev (2025-10-16 revision b052d706a53e764786093d6d1cade5a9adebbb7b)
prism_compile.c (b052d706a53e764786093d6d1cade5a9adebbb7b)
1#include "prism.h"
2#include "ruby/version.h"
3
9typedef struct {
11 int32_t line;
12
14 uint32_t node_id;
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 update_lvar_state(iseq, level, idx);
106 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue);
107}
108
109#define PUSH_GETLOCAL(seq, location, idx, level) \
110 pm_iseq_add_getlocal(iseq, (seq), (int) (location).line, (int) (location).node_id, (idx), (level))
111
112#define PUSH_SETLOCAL(seq, location, idx, level) \
113 pm_iseq_add_setlocal(iseq, (seq), (int) (location).line, (int) (location).node_id, (idx), (level))
114
115/******************************************************************************/
116/* These are helper macros for the compiler. */
117/******************************************************************************/
118
119#define OLD_ISEQ NEW_ISEQ
120#undef NEW_ISEQ
121
122#define NEW_ISEQ(node, name, type, line_no) \
123 pm_new_child_iseq(iseq, (node), rb_fstring(name), 0, (type), (line_no))
124
125#define OLD_CHILD_ISEQ NEW_CHILD_ISEQ
126#undef NEW_CHILD_ISEQ
127
128#define NEW_CHILD_ISEQ(node, name, type, line_no) \
129 pm_new_child_iseq(iseq, (node), rb_fstring(name), iseq, (type), (line_no))
130
131#define PM_COMPILE(node) \
132 pm_compile_node(iseq, (node), ret, popped, scope_node)
133
134#define PM_COMPILE_INTO_ANCHOR(_ret, node) \
135 pm_compile_node(iseq, (node), _ret, popped, scope_node)
136
137#define PM_COMPILE_POPPED(node) \
138 pm_compile_node(iseq, (node), ret, true, scope_node)
139
140#define PM_COMPILE_NOT_POPPED(node) \
141 pm_compile_node(iseq, (node), ret, false, scope_node)
142
143#define PM_NODE_START_LOCATION(parser, node) \
144 ((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 })
145
146#define PM_NODE_END_LOCATION(parser, node) \
147 ((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 })
148
149#define PM_LOCATION_START_LOCATION(parser, location, id) \
150 ((pm_node_location_t) { .line = pm_newline_list_line(&(parser)->newline_list, (location)->start, (parser)->start_line), .node_id = id })
151
152#define PM_NODE_START_LINE_COLUMN(parser, node) \
153 pm_newline_list_line_column(&(parser)->newline_list, ((const pm_node_t *) (node))->location.start, (parser)->start_line)
154
155#define PM_NODE_END_LINE_COLUMN(parser, node) \
156 pm_newline_list_line_column(&(parser)->newline_list, ((const pm_node_t *) (node))->location.end, (parser)->start_line)
157
158#define PM_LOCATION_START_LINE_COLUMN(parser, location) \
159 pm_newline_list_line_column(&(parser)->newline_list, (location)->start, (parser)->start_line)
160
161static int
162pm_node_line_number(const pm_parser_t *parser, const pm_node_t *node)
163{
164 return (int) pm_newline_list_line(&parser->newline_list, node->location.start, parser->start_line);
165}
166
167static int
168pm_location_line_number(const pm_parser_t *parser, const pm_location_t *location) {
169 return (int) pm_newline_list_line(&parser->newline_list, location->start, parser->start_line);
170}
171
175static VALUE
176parse_integer_value(const pm_integer_t *integer)
177{
178 VALUE result;
179
180 if (integer->values == NULL) {
181 result = UINT2NUM(integer->value);
182 }
183 else {
184 VALUE string = rb_str_new(NULL, integer->length * 8);
185 unsigned char *bytes = (unsigned char *) RSTRING_PTR(string);
186
187 size_t offset = integer->length * 8;
188 for (size_t value_index = 0; value_index < integer->length; value_index++) {
189 uint32_t value = integer->values[value_index];
190
191 for (int index = 0; index < 8; index++) {
192 int byte = (value >> (4 * index)) & 0xf;
193 bytes[--offset] = byte < 10 ? byte + '0' : byte - 10 + 'a';
194 }
195 }
196
197 result = rb_funcall(string, rb_intern("to_i"), 1, UINT2NUM(16));
198 }
199
200 if (integer->negative) {
201 result = rb_funcall(result, rb_intern("-@"), 0);
202 }
203
204 return result;
205}
206
210static inline VALUE
211parse_integer(const pm_integer_node_t *node)
212{
213 return parse_integer_value(&node->value);
214}
215
219static VALUE
220parse_float(const pm_float_node_t *node)
221{
222 return DBL2NUM(node->value);
223}
224
231static VALUE
232parse_rational(const pm_rational_node_t *node)
233{
234 VALUE numerator = parse_integer_value(&node->numerator);
235 VALUE denominator = parse_integer_value(&node->denominator);
236 return rb_rational_new(numerator, denominator);
237}
238
245static VALUE
246parse_imaginary(const pm_imaginary_node_t *node)
247{
248 VALUE imaginary_part;
249 switch (PM_NODE_TYPE(node->numeric)) {
250 case PM_FLOAT_NODE: {
251 imaginary_part = parse_float((const pm_float_node_t *) node->numeric);
252 break;
253 }
254 case PM_INTEGER_NODE: {
255 imaginary_part = parse_integer((const pm_integer_node_t *) node->numeric);
256 break;
257 }
258 case PM_RATIONAL_NODE: {
259 imaginary_part = parse_rational((const pm_rational_node_t *) node->numeric);
260 break;
261 }
262 default:
263 rb_bug("Unexpected numeric type on imaginary number %s\n", pm_node_type_to_str(PM_NODE_TYPE(node->numeric)));
264 }
265
266 return rb_complex_raw(INT2FIX(0), imaginary_part);
267}
268
269static inline VALUE
270parse_string(const pm_scope_node_t *scope_node, const pm_string_t *string)
271{
272 return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), scope_node->encoding);
273}
274
280static inline VALUE
281parse_string_encoded(const pm_node_t *node, const pm_string_t *string, rb_encoding *default_encoding)
282{
283 rb_encoding *encoding;
284
285 if (node->flags & PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING) {
286 encoding = rb_ascii8bit_encoding();
287 }
288 else if (node->flags & PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING) {
289 encoding = rb_utf8_encoding();
290 }
291 else {
292 encoding = default_encoding;
293 }
294
295 return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), encoding);
296}
297
298static inline VALUE
299parse_static_literal_string(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *string)
300{
301 rb_encoding *encoding;
302
303 if (node->flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) {
304 encoding = rb_ascii8bit_encoding();
305 }
306 else if (node->flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) {
307 encoding = rb_utf8_encoding();
308 }
309 else {
310 encoding = scope_node->encoding;
311 }
312
313 VALUE value = rb_enc_literal_str((const char *) pm_string_source(string), pm_string_length(string), encoding);
315
316 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
317 int line_number = pm_node_line_number(scope_node->parser, node);
318 value = rb_str_with_debug_created_info(value, rb_iseq_path(iseq), line_number);
319 }
320
321 return value;
322}
323
324static inline ID
325parse_string_symbol(const pm_scope_node_t *scope_node, const pm_symbol_node_t *symbol)
326{
327 rb_encoding *encoding;
328 if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING) {
329 encoding = rb_utf8_encoding();
330 }
331 else if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING) {
332 encoding = rb_ascii8bit_encoding();
333 }
334 else if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING) {
335 encoding = rb_usascii_encoding();
336 }
337 else {
338 encoding = scope_node->encoding;
339 }
340
341 return rb_intern3((const char *) pm_string_source(&symbol->unescaped), pm_string_length(&symbol->unescaped), encoding);
342}
343
344static int
345pm_optimizable_range_item_p(const pm_node_t *node)
346{
347 return (!node || PM_NODE_TYPE_P(node, PM_INTEGER_NODE) || PM_NODE_TYPE_P(node, PM_NIL_NODE));
348}
349
351static VALUE
352parse_regexp_error(rb_iseq_t *iseq, int32_t line_number, const char *fmt, ...)
353{
354 va_list args;
355 va_start(args, fmt);
356 VALUE error = rb_syntax_error_append(Qnil, rb_iseq_path(iseq), line_number, -1, NULL, "%" PRIsVALUE, args);
357 va_end(args);
358 rb_exc_raise(error);
359}
360
361static VALUE
362parse_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)
363{
364 // If we were passed an explicit regexp encoding, then we need to double
365 // check that it's okay here for this fragment of the string.
366 rb_encoding *encoding;
367
368 if (explicit_regexp_encoding != NULL) {
369 encoding = explicit_regexp_encoding;
370 }
371 else if (node->flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) {
372 encoding = rb_ascii8bit_encoding();
373 }
374 else if (node->flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) {
375 encoding = rb_utf8_encoding();
376 }
377 else {
378 encoding = implicit_regexp_encoding;
379 }
380
381 VALUE string = rb_enc_str_new((const char *) pm_string_source(unescaped), pm_string_length(unescaped), encoding);
382 VALUE error = rb_reg_check_preprocess(string);
383
384 if (error != Qnil) parse_regexp_error(iseq, pm_node_line_number(scope_node->parser, node), "%" PRIsVALUE, rb_obj_as_string(error));
385 return string;
386}
387
388static VALUE
389pm_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)
390{
391 VALUE current = Qnil;
392
393 for (size_t index = 0; index < nodes->size; index++) {
394 const pm_node_t *part = nodes->nodes[index];
395 VALUE string;
396
397 switch (PM_NODE_TYPE(part)) {
398 case PM_STRING_NODE:
399 if (implicit_regexp_encoding != NULL) {
400 if (top) {
401 string = parse_regexp_string_part(iseq, scope_node, part, &((const pm_string_node_t *) part)->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
402 }
403 else {
404 string = parse_string_encoded(part, &((const pm_string_node_t *) part)->unescaped, scope_node->encoding);
405 VALUE error = rb_reg_check_preprocess(string);
406 if (error != Qnil) parse_regexp_error(iseq, pm_node_line_number(scope_node->parser, part), "%" PRIsVALUE, rb_obj_as_string(error));
407 }
408 }
409 else {
410 string = parse_string_encoded(part, &((const pm_string_node_t *) part)->unescaped, scope_node->encoding);
411 }
412 break;
413 case PM_INTERPOLATED_STRING_NODE:
414 string = pm_static_literal_concat(iseq, &((const pm_interpolated_string_node_t *) part)->parts, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
415 break;
416 case PM_EMBEDDED_STATEMENTS_NODE: {
418 string = pm_static_literal_concat(iseq, &cast->statements->body, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
419 break;
420 }
421 default:
422 RUBY_ASSERT(false && "unexpected node type in pm_static_literal_concat");
423 return Qnil;
424 }
425
426 if (current != Qnil) {
427 current = rb_str_concat(current, string);
428 }
429 else {
430 current = string;
431 }
432 }
433
434 return top ? rb_fstring(current) : current;
435}
436
437#define RE_OPTION_ENCODING_SHIFT 8
438#define RE_OPTION_ENCODING(encoding) (((encoding) & 0xFF) << RE_OPTION_ENCODING_SHIFT)
439#define ARG_ENCODING_NONE 32
440#define ARG_ENCODING_FIXED 16
441#define ENC_ASCII8BIT 1
442#define ENC_EUC_JP 2
443#define ENC_Windows_31J 3
444#define ENC_UTF8 4
445
450static int
451parse_regexp_flags(const pm_node_t *node)
452{
453 int flags = 0;
454
455 // Check "no encoding" first so that flags don't get clobbered
456 // We're calling `rb_char_to_option_kcode` in this case so that
457 // we don't need to have access to `ARG_ENCODING_NONE`
458 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) {
459 flags |= ARG_ENCODING_NONE;
460 }
461
462 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) {
463 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_EUC_JP));
464 }
465
466 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) {
467 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_Windows_31J));
468 }
469
470 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) {
471 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_UTF8));
472 }
473
474 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) {
475 flags |= ONIG_OPTION_IGNORECASE;
476 }
477
478 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) {
479 flags |= ONIG_OPTION_MULTILINE;
480 }
481
482 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) {
483 flags |= ONIG_OPTION_EXTEND;
484 }
485
486 return flags;
487}
488
489#undef RE_OPTION_ENCODING_SHIFT
490#undef RE_OPTION_ENCODING
491#undef ARG_ENCODING_FIXED
492#undef ARG_ENCODING_NONE
493#undef ENC_ASCII8BIT
494#undef ENC_EUC_JP
495#undef ENC_Windows_31J
496#undef ENC_UTF8
497
498static rb_encoding *
499parse_regexp_encoding(const pm_scope_node_t *scope_node, const pm_node_t *node)
500{
501 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING) || PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) {
502 return rb_ascii8bit_encoding();
503 }
504 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) {
505 return rb_utf8_encoding();
506 }
507 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) {
508 return rb_enc_get_from_index(ENCINDEX_EUC_JP);
509 }
510 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) {
511 return rb_enc_get_from_index(ENCINDEX_Windows_31J);
512 }
513 else {
514 return NULL;
515 }
516}
517
518static VALUE
519parse_regexp(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, VALUE string)
520{
521 VALUE errinfo = rb_errinfo();
522
523 int32_t line_number = pm_node_line_number(scope_node->parser, node);
524 VALUE regexp = rb_reg_compile(string, parse_regexp_flags(node), (const char *) pm_string_source(&scope_node->parser->filepath), line_number);
525
526 if (NIL_P(regexp)) {
527 VALUE message = rb_attr_get(rb_errinfo(), idMesg);
528 rb_set_errinfo(errinfo);
529
530 parse_regexp_error(iseq, line_number, "%" PRIsVALUE, message);
531 return Qnil;
532 }
533
534 rb_obj_freeze(regexp);
535 return regexp;
536}
537
538static inline VALUE
539parse_regexp_literal(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *unescaped)
540{
541 rb_encoding *regexp_encoding = parse_regexp_encoding(scope_node, node);
542 if (regexp_encoding == NULL) regexp_encoding = scope_node->encoding;
543
544 VALUE string = rb_enc_str_new((const char *) pm_string_source(unescaped), pm_string_length(unescaped), regexp_encoding);
545 return parse_regexp(iseq, scope_node, node, string);
546}
547
548static inline VALUE
549parse_regexp_concat(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_node_list_t *parts)
550{
551 rb_encoding *explicit_regexp_encoding = parse_regexp_encoding(scope_node, node);
552 rb_encoding *implicit_regexp_encoding = explicit_regexp_encoding != NULL ? explicit_regexp_encoding : scope_node->encoding;
553
554 VALUE string = pm_static_literal_concat(iseq, parts, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
555 return parse_regexp(iseq, scope_node, node, string);
556}
557
558static 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);
559
560static int
561pm_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)
562{
563 int stack_size = 0;
564 size_t parts_size = parts->size;
565 bool interpolated = false;
566
567 if (parts_size > 0) {
568 VALUE current_string = Qnil;
569 pm_node_location_t current_location = *node_location;
570
571 for (size_t index = 0; index < parts_size; index++) {
572 const pm_node_t *part = parts->nodes[index];
573
574 if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
575 const pm_string_node_t *string_node = (const pm_string_node_t *) part;
576 VALUE string_value;
577
578 if (implicit_regexp_encoding == NULL) {
579 string_value = parse_string_encoded(part, &string_node->unescaped, scope_node->encoding);
580 }
581 else {
582 string_value = parse_regexp_string_part(iseq, scope_node, (const pm_node_t *) string_node, &string_node->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
583 }
584
585 if (RTEST(current_string)) {
586 current_string = rb_str_concat(current_string, string_value);
587 }
588 else {
589 current_string = string_value;
590 if (index != 0) current_location = PM_NODE_END_LOCATION(scope_node->parser, part);
591 }
592 }
593 else {
594 interpolated = true;
595
596 if (
597 PM_NODE_TYPE_P(part, PM_EMBEDDED_STATEMENTS_NODE) &&
598 ((const pm_embedded_statements_node_t *) part)->statements != NULL &&
599 ((const pm_embedded_statements_node_t *) part)->statements->body.size == 1 &&
600 PM_NODE_TYPE_P(((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0], PM_STRING_NODE)
601 ) {
602 const pm_string_node_t *string_node = (const pm_string_node_t *) ((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0];
603 VALUE string_value;
604
605 if (implicit_regexp_encoding == NULL) {
606 string_value = parse_string_encoded(part, &string_node->unescaped, scope_node->encoding);
607 }
608 else {
609 string_value = parse_regexp_string_part(iseq, scope_node, (const pm_node_t *) string_node, &string_node->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
610 }
611
612 if (RTEST(current_string)) {
613 current_string = rb_str_concat(current_string, string_value);
614 }
615 else {
616 current_string = string_value;
617 current_location = PM_NODE_START_LOCATION(scope_node->parser, part);
618 }
619 }
620 else {
621 if (!RTEST(current_string)) {
622 rb_encoding *encoding;
623
624 if (implicit_regexp_encoding != NULL) {
625 if (explicit_regexp_encoding != NULL) {
626 encoding = explicit_regexp_encoding;
627 }
628 else if (scope_node->parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
629 encoding = rb_ascii8bit_encoding();
630 }
631 else {
632 encoding = implicit_regexp_encoding;
633 }
634 }
635 else {
636 encoding = scope_node->encoding;
637 }
638
639 if (parts_size == 1) {
640 current_string = rb_enc_str_new(NULL, 0, encoding);
641 }
642 }
643
644 if (RTEST(current_string)) {
645 VALUE operand = rb_fstring(current_string);
646 PUSH_INSN1(ret, current_location, putobject, operand);
647 stack_size++;
648 }
649
650 PM_COMPILE_NOT_POPPED(part);
651
652 const pm_node_location_t current_location = PM_NODE_START_LOCATION(scope_node->parser, part);
653 PUSH_INSN(ret, current_location, dup);
654
655 {
656 const struct rb_callinfo *callinfo = new_callinfo(iseq, idTo_s, 0, VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE, NULL, FALSE);
657 PUSH_INSN1(ret, current_location, objtostring, callinfo);
658 }
659
660 PUSH_INSN(ret, current_location, anytostring);
661
662 current_string = Qnil;
663 stack_size++;
664 }
665 }
666 }
667
668 if (RTEST(current_string)) {
669 current_string = rb_fstring(current_string);
670
671 if (stack_size == 0) {
672 if (frozen_result) {
673 PUSH_INSN1(ret, current_location, putobject, current_string);
674 } else if (mutable_result || interpolated) {
675 PUSH_INSN1(ret, current_location, putstring, current_string);
676 } else {
677 PUSH_INSN1(ret, current_location, putchilledstring, current_string);
678 }
679 } else {
680 PUSH_INSN1(ret, current_location, putobject, current_string);
681 }
682
683 current_string = Qnil;
684 stack_size++;
685 }
686 }
687 else {
688 PUSH_INSN(ret, *node_location, putnil);
689 }
690
691 return stack_size;
692}
693
694static void
695pm_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)
696{
697 rb_encoding *explicit_regexp_encoding = parse_regexp_encoding(scope_node, node);
698 rb_encoding *implicit_regexp_encoding = explicit_regexp_encoding != NULL ? explicit_regexp_encoding : scope_node->encoding;
699
700 int length = pm_interpolated_node_compile(iseq, parts, node_location, ret, popped, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false, false);
701 PUSH_INSN2(ret, *node_location, toregexp, INT2FIX(parse_regexp_flags(node) & 0xFF), INT2FIX(length));
702}
703
704static VALUE
705pm_source_file_value(const pm_source_file_node_t *node, const pm_scope_node_t *scope_node)
706{
707 const pm_string_t *filepath = &node->filepath;
708 size_t length = pm_string_length(filepath);
709
710 if (length > 0) {
711 rb_encoding *filepath_encoding = scope_node->filepath_encoding != NULL ? scope_node->filepath_encoding : rb_utf8_encoding();
712 return rb_enc_interned_str((const char *) pm_string_source(filepath), length, filepath_encoding);
713 }
714 else {
715 return rb_fstring_lit("<compiled>");
716 }
717}
718
723static VALUE
724pm_static_literal_string(rb_iseq_t *iseq, VALUE string, int line_number)
725{
726 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
727 return rb_str_with_debug_created_info(string, rb_iseq_path(iseq), line_number);
728 }
729 else {
730 return rb_fstring(string);
731 }
732}
733
739static VALUE
740pm_static_literal_value(rb_iseq_t *iseq, const pm_node_t *node, const pm_scope_node_t *scope_node)
741{
742 // Every node that comes into this function should already be marked as
743 // static literal. If it's not, then we have a bug somewhere.
744 RUBY_ASSERT(PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL));
745
746 switch (PM_NODE_TYPE(node)) {
747 case PM_ARRAY_NODE: {
748 const pm_array_node_t *cast = (const pm_array_node_t *) node;
749 const pm_node_list_t *elements = &cast->elements;
750
751 VALUE value = rb_ary_hidden_new(elements->size);
752 for (size_t index = 0; index < elements->size; index++) {
753 rb_ary_push(value, pm_static_literal_value(iseq, elements->nodes[index], scope_node));
754 }
755
756 OBJ_FREEZE(value);
757 return value;
758 }
759 case PM_FALSE_NODE:
760 return Qfalse;
761 case PM_FLOAT_NODE:
762 return parse_float((const pm_float_node_t *) node);
763 case PM_HASH_NODE: {
764 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
765 const pm_node_list_t *elements = &cast->elements;
766
767 VALUE array = rb_ary_hidden_new(elements->size * 2);
768 for (size_t index = 0; index < elements->size; index++) {
769 RUBY_ASSERT(PM_NODE_TYPE_P(elements->nodes[index], PM_ASSOC_NODE));
770 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) elements->nodes[index];
771 VALUE pair[2] = { pm_static_literal_value(iseq, cast->key, scope_node), pm_static_literal_value(iseq, cast->value, scope_node) };
772 rb_ary_cat(array, pair, 2);
773 }
774
775 VALUE value = rb_hash_new_with_size(elements->size);
776 rb_hash_bulk_insert(RARRAY_LEN(array), RARRAY_CONST_PTR(array), value);
777
778 value = rb_obj_hide(value);
779 OBJ_FREEZE(value);
780 return value;
781 }
782 case PM_IMAGINARY_NODE:
783 return parse_imaginary((const pm_imaginary_node_t *) node);
784 case PM_INTEGER_NODE:
785 return parse_integer((const pm_integer_node_t *) node);
786 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: {
788 return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts);
789 }
790 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
792 return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts);
793 }
794 case PM_INTERPOLATED_STRING_NODE: {
795 VALUE string = pm_static_literal_concat(iseq, &((const pm_interpolated_string_node_t *) node)->parts, scope_node, NULL, NULL, false);
796 int line_number = pm_node_line_number(scope_node->parser, node);
797 return pm_static_literal_string(iseq, string, line_number);
798 }
799 case PM_INTERPOLATED_SYMBOL_NODE: {
801 VALUE string = pm_static_literal_concat(iseq, &cast->parts, scope_node, NULL, NULL, true);
802
803 return ID2SYM(rb_intern_str(string));
804 }
805 case PM_MATCH_LAST_LINE_NODE: {
806 const pm_match_last_line_node_t *cast = (const pm_match_last_line_node_t *) node;
807 return parse_regexp_literal(iseq, scope_node, (const pm_node_t *) cast, &cast->unescaped);
808 }
809 case PM_NIL_NODE:
810 return Qnil;
811 case PM_RATIONAL_NODE:
812 return parse_rational((const pm_rational_node_t *) node);
813 case PM_REGULAR_EXPRESSION_NODE: {
815 return parse_regexp_literal(iseq, scope_node, (const pm_node_t *) cast, &cast->unescaped);
816 }
817 case PM_SOURCE_ENCODING_NODE:
818 return rb_enc_from_encoding(scope_node->encoding);
819 case PM_SOURCE_FILE_NODE: {
820 const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node;
821 return pm_source_file_value(cast, scope_node);
822 }
823 case PM_SOURCE_LINE_NODE:
824 return INT2FIX(pm_node_line_number(scope_node->parser, node));
825 case PM_STRING_NODE: {
826 const pm_string_node_t *cast = (const pm_string_node_t *) node;
827 return parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
828 }
829 case PM_SYMBOL_NODE:
830 return ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) node));
831 case PM_TRUE_NODE:
832 return Qtrue;
833 default:
834 rb_bug("Don't have a literal value for node type %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
835 return Qfalse;
836 }
837}
838
843pm_code_location(const pm_scope_node_t *scope_node, const pm_node_t *node)
844{
845 const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
846 const pm_line_column_t end_location = PM_NODE_END_LINE_COLUMN(scope_node->parser, node);
847
848 return (rb_code_location_t) {
849 .beg_pos = { .lineno = start_location.line, .column = start_location.column },
850 .end_pos = { .lineno = end_location.line, .column = end_location.column }
851 };
852}
853
859#define PM_BRANCH_COVERAGE_P(iseq) (ISEQ_COVERAGE(iseq) && ISEQ_BRANCH_COVERAGE(iseq))
860
861static void
862pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond,
863 LABEL *then_label, LABEL *else_label, bool popped, pm_scope_node_t *scope_node);
864
865static void
866pm_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)
867{
868 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, cond);
869
870 DECL_ANCHOR(seq);
871
872 LABEL *label = NEW_LABEL(location.line);
873 if (!then_label) then_label = label;
874 else if (!else_label) else_label = label;
875
876 pm_compile_branch_condition(iseq, seq, cond, then_label, else_label, popped, scope_node);
877
878 if (LIST_INSN_SIZE_ONE(seq)) {
879 INSN *insn = (INSN *) ELEM_FIRST_INSN(FIRST_ELEMENT(seq));
880 if (insn->insn_id == BIN(jump) && (LABEL *)(insn->operands[0]) == label) return;
881 }
882
883 if (!label->refcnt) {
884 if (popped) PUSH_INSN(ret, location, putnil);
885 }
886 else {
887 PUSH_LABEL(seq, label);
888 }
889
890 PUSH_SEQ(ret, seq);
891 return;
892}
893
894static void
895pm_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)
896{
897 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
898
899 if (PM_NODE_TYPE_P(node, PM_INTEGER_NODE)) {
900 PM_COMPILE_NOT_POPPED(node);
901
902 VALUE operand = ID2SYM(rb_intern("$."));
903 PUSH_INSN1(ret, location, getglobal, operand);
904
905 PUSH_SEND(ret, location, idEq, INT2FIX(1));
906 if (popped) PUSH_INSN(ret, location, pop);
907 }
908 else {
909 PM_COMPILE(node);
910 }
911}
912
913static void
914pm_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)
915{
916 const pm_node_location_t location = { .line = lineno, .node_id = -1 };
917 LABEL *lend = NEW_LABEL(location.line);
918
919 int again = !(flip_flop_node->base.flags & PM_RANGE_FLAGS_EXCLUDE_END);
920
921 rb_num_t count = ISEQ_FLIP_CNT_INCREMENT(ISEQ_BODY(iseq)->local_iseq) + VM_SVAR_FLIPFLOP_START;
922 VALUE key = INT2FIX(count);
923
924 PUSH_INSN2(ret, location, getspecial, key, INT2FIX(0));
925 PUSH_INSNL(ret, location, branchif, lend);
926
927 if (flip_flop_node->left) {
928 pm_compile_flip_flop_bound(iseq, flip_flop_node->left, ret, popped, scope_node);
929 }
930 else {
931 PUSH_INSN(ret, location, putnil);
932 }
933
934 PUSH_INSNL(ret, location, branchunless, else_label);
935 PUSH_INSN1(ret, location, putobject, Qtrue);
936 PUSH_INSN1(ret, location, setspecial, key);
937 if (!again) {
938 PUSH_INSNL(ret, location, jump, then_label);
939 }
940
941 PUSH_LABEL(ret, lend);
942 if (flip_flop_node->right) {
943 pm_compile_flip_flop_bound(iseq, flip_flop_node->right, ret, popped, scope_node);
944 }
945 else {
946 PUSH_INSN(ret, location, putnil);
947 }
948
949 PUSH_INSNL(ret, location, branchunless, then_label);
950 PUSH_INSN1(ret, location, putobject, Qfalse);
951 PUSH_INSN1(ret, location, setspecial, key);
952 PUSH_INSNL(ret, location, jump, then_label);
953}
954
955static 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);
956
957static void
958pm_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)
959{
960 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, cond);
961
962again:
963 switch (PM_NODE_TYPE(cond)) {
964 case PM_AND_NODE: {
965 const pm_and_node_t *cast = (const pm_and_node_t *) cond;
966 pm_compile_logical(iseq, ret, cast->left, NULL, else_label, popped, scope_node);
967
968 cond = cast->right;
969 goto again;
970 }
971 case PM_OR_NODE: {
972 const pm_or_node_t *cast = (const pm_or_node_t *) cond;
973 pm_compile_logical(iseq, ret, cast->left, then_label, NULL, popped, scope_node);
974
975 cond = cast->right;
976 goto again;
977 }
978 case PM_FALSE_NODE:
979 case PM_NIL_NODE:
980 PUSH_INSNL(ret, location, jump, else_label);
981 return;
982 case PM_FLOAT_NODE:
983 case PM_IMAGINARY_NODE:
984 case PM_INTEGER_NODE:
985 case PM_LAMBDA_NODE:
986 case PM_RATIONAL_NODE:
987 case PM_REGULAR_EXPRESSION_NODE:
988 case PM_STRING_NODE:
989 case PM_SYMBOL_NODE:
990 case PM_TRUE_NODE:
991 PUSH_INSNL(ret, location, jump, then_label);
992 return;
993 case PM_FLIP_FLOP_NODE:
994 pm_compile_flip_flop((const pm_flip_flop_node_t *) cond, else_label, then_label, iseq, location.line, ret, popped, scope_node);
995 return;
996 case PM_DEFINED_NODE: {
997 const pm_defined_node_t *cast = (const pm_defined_node_t *) cond;
998 pm_compile_defined_expr(iseq, cast->value, &location, ret, popped, scope_node, true);
999 break;
1000 }
1001 default: {
1002 DECL_ANCHOR(cond_seq);
1003 pm_compile_node(iseq, cond, cond_seq, false, scope_node);
1004
1005 if (LIST_INSN_SIZE_ONE(cond_seq)) {
1006 INSN *insn = (INSN *) ELEM_FIRST_INSN(FIRST_ELEMENT(cond_seq));
1007
1008 if (insn->insn_id == BIN(putobject)) {
1009 if (RTEST(insn->operands[0])) {
1010 PUSH_INSNL(ret, location, jump, then_label);
1011 // maybe unreachable
1012 return;
1013 }
1014 else {
1015 PUSH_INSNL(ret, location, jump, else_label);
1016 return;
1017 }
1018 }
1019 }
1020
1021 PUSH_SEQ(ret, cond_seq);
1022 break;
1023 }
1024 }
1025
1026 PUSH_INSNL(ret, location, branchunless, else_label);
1027 PUSH_INSNL(ret, location, jump, then_label);
1028}
1029
1033static void
1034pm_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)
1035{
1036 const pm_node_location_t location = *node_location;
1037 LABEL *then_label = NEW_LABEL(location.line);
1038 LABEL *else_label = NEW_LABEL(location.line);
1039 LABEL *end_label = NULL;
1040
1041 DECL_ANCHOR(cond_seq);
1042 pm_compile_branch_condition(iseq, cond_seq, predicate, then_label, else_label, false, scope_node);
1043 PUSH_SEQ(ret, cond_seq);
1044
1045 rb_code_location_t conditional_location = { 0 };
1046 VALUE branches = Qfalse;
1047
1048 if (then_label->refcnt && else_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) {
1049 conditional_location = pm_code_location(scope_node, node);
1050 branches = decl_branch_base(iseq, PTR2NUM(node), &conditional_location, type == PM_IF_NODE ? "if" : "unless");
1051 }
1052
1053 if (then_label->refcnt) {
1054 PUSH_LABEL(ret, then_label);
1055
1056 DECL_ANCHOR(then_seq);
1057
1058 if (statements != NULL) {
1059 pm_compile_node(iseq, (const pm_node_t *) statements, then_seq, popped, scope_node);
1060 }
1061 else if (!popped) {
1062 PUSH_SYNTHETIC_PUTNIL(then_seq, iseq);
1063 }
1064
1065 if (else_label->refcnt) {
1066 // Establish branch coverage for the then block.
1067 if (PM_BRANCH_COVERAGE_P(iseq)) {
1068 rb_code_location_t branch_location;
1069
1070 if (statements != NULL) {
1071 branch_location = pm_code_location(scope_node, (const pm_node_t *) statements);
1072 } else if (type == PM_IF_NODE) {
1073 pm_line_column_t predicate_end = PM_NODE_END_LINE_COLUMN(scope_node->parser, predicate);
1074 branch_location = (rb_code_location_t) {
1075 .beg_pos = { .lineno = predicate_end.line, .column = predicate_end.column },
1076 .end_pos = { .lineno = predicate_end.line, .column = predicate_end.column }
1077 };
1078 } else {
1079 branch_location = conditional_location;
1080 }
1081
1082 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, type == PM_IF_NODE ? "then" : "else", branches);
1083 }
1084
1085 end_label = NEW_LABEL(location.line);
1086 PUSH_INSNL(then_seq, location, jump, end_label);
1087 if (!popped) PUSH_INSN(then_seq, location, pop);
1088 }
1089
1090 PUSH_SEQ(ret, then_seq);
1091 }
1092
1093 if (else_label->refcnt) {
1094 PUSH_LABEL(ret, else_label);
1095
1096 DECL_ANCHOR(else_seq);
1097
1098 if (subsequent != NULL) {
1099 pm_compile_node(iseq, subsequent, else_seq, popped, scope_node);
1100 }
1101 else if (!popped) {
1102 PUSH_SYNTHETIC_PUTNIL(else_seq, iseq);
1103 }
1104
1105 // Establish branch coverage for the else block.
1106 if (then_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) {
1107 rb_code_location_t branch_location;
1108
1109 if (subsequent == NULL) {
1110 branch_location = conditional_location;
1111 } else if (PM_NODE_TYPE_P(subsequent, PM_ELSE_NODE)) {
1112 const pm_else_node_t *else_node = (const pm_else_node_t *) subsequent;
1113 branch_location = pm_code_location(scope_node, else_node->statements != NULL ? ((const pm_node_t *) else_node->statements) : (const pm_node_t *) else_node);
1114 } else {
1115 branch_location = pm_code_location(scope_node, (const pm_node_t *) subsequent);
1116 }
1117
1118 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 1, type == PM_IF_NODE ? "else" : "then", branches);
1119 }
1120
1121 PUSH_SEQ(ret, else_seq);
1122 }
1123
1124 if (end_label) {
1125 PUSH_LABEL(ret, end_label);
1126 }
1127
1128 return;
1129}
1130
1134static void
1135pm_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)
1136{
1137 const pm_node_location_t location = *node_location;
1138
1139 LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
1140 LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
1141 LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
1142
1143 LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(location.line); /* next */
1144 LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(location.line); /* redo */
1145 LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(location.line); /* break */
1146 LABEL *end_label = NEW_LABEL(location.line);
1147 LABEL *adjust_label = NEW_LABEL(location.line);
1148
1149 LABEL *next_catch_label = NEW_LABEL(location.line);
1150 LABEL *tmp_label = NULL;
1151
1152 // We're pushing onto the ensure stack because breaks need to break out of
1153 // this loop and not break into the ensure statements within the same
1154 // lexical scope.
1156 push_ensure_entry(iseq, &enl, NULL, NULL);
1157
1158 // begin; end while true
1159 if (flags & PM_LOOP_FLAGS_BEGIN_MODIFIER) {
1160 tmp_label = NEW_LABEL(location.line);
1161 PUSH_INSNL(ret, location, jump, tmp_label);
1162 }
1163 else {
1164 // while true; end
1165 PUSH_INSNL(ret, location, jump, next_label);
1166 }
1167
1168 PUSH_LABEL(ret, adjust_label);
1169 PUSH_INSN(ret, location, putnil);
1170 PUSH_LABEL(ret, next_catch_label);
1171 PUSH_INSN(ret, location, pop);
1172 PUSH_INSNL(ret, location, jump, next_label);
1173 if (tmp_label) PUSH_LABEL(ret, tmp_label);
1174
1175 PUSH_LABEL(ret, redo_label);
1176
1177 // Establish branch coverage for the loop.
1178 if (PM_BRANCH_COVERAGE_P(iseq)) {
1179 rb_code_location_t loop_location = pm_code_location(scope_node, node);
1180 VALUE branches = decl_branch_base(iseq, PTR2NUM(node), &loop_location, type == PM_WHILE_NODE ? "while" : "until");
1181
1182 rb_code_location_t branch_location = statements != NULL ? pm_code_location(scope_node, (const pm_node_t *) statements) : loop_location;
1183 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, "body", branches);
1184 }
1185
1186 if (statements != NULL) PM_COMPILE_POPPED((const pm_node_t *) statements);
1187 PUSH_LABEL(ret, next_label);
1188
1189 if (type == PM_WHILE_NODE) {
1190 pm_compile_branch_condition(iseq, ret, predicate, redo_label, end_label, popped, scope_node);
1191 }
1192 else if (type == PM_UNTIL_NODE) {
1193 pm_compile_branch_condition(iseq, ret, predicate, end_label, redo_label, popped, scope_node);
1194 }
1195
1196 PUSH_LABEL(ret, end_label);
1197 PUSH_ADJUST_RESTORE(ret, adjust_label);
1198 PUSH_INSN(ret, location, putnil);
1199
1200 PUSH_LABEL(ret, break_label);
1201 if (popped) PUSH_INSN(ret, location, pop);
1202
1203 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL, break_label);
1204 PUSH_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL, next_catch_label);
1205 PUSH_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL, ISEQ_COMPILE_DATA(iseq)->redo_label);
1206
1207 ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
1208 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
1209 ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
1210 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->prev;
1211
1212 return;
1213}
1214
1215// This recurses through scopes and finds the local index at any scope level
1216// It also takes a pointer to depth, and increments depth appropriately
1217// according to the depth of the local.
1218static pm_local_index_t
1219pm_lookup_local_index(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, pm_constant_id_t constant_id, int start_depth)
1220{
1221 pm_local_index_t lindex = { 0 };
1222 st_data_t local_index;
1223
1224 int level;
1225 for (level = 0; level < start_depth; level++) {
1226 scope_node = scope_node->previous;
1227 }
1228
1229 while (!st_lookup(scope_node->index_lookup_table, constant_id, &local_index)) {
1230 level++;
1231
1232 if (scope_node->previous) {
1233 scope_node = scope_node->previous;
1234 }
1235 else {
1236 // We have recursed up all scope nodes
1237 // and have not found the local yet
1238 rb_bug("Local with constant_id %u does not exist", (unsigned int) constant_id);
1239 }
1240 }
1241
1242 lindex.level = level;
1243 lindex.index = scope_node->local_table_for_iseq_size - (int) local_index;
1244 return lindex;
1245}
1246
1247// This returns the CRuby ID which maps to the pm_constant_id_t
1248//
1249// Constant_ids in prism are indexes of the constants in prism's constant pool.
1250// We add a constants mapping on the scope_node which is a mapping from
1251// these constant_id indexes to the CRuby IDs that they represent.
1252// This helper method allows easy access to those IDs
1253static ID
1254pm_constant_id_lookup(const pm_scope_node_t *scope_node, pm_constant_id_t constant_id)
1255{
1256 if (constant_id < 1 || constant_id > scope_node->parser->constant_pool.size) {
1257 rb_bug("constant_id out of range: %u", (unsigned int)constant_id);
1258 }
1259 return scope_node->constants[constant_id - 1];
1260}
1261
1262static rb_iseq_t *
1263pm_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)
1264{
1265 debugs("[new_child_iseq]> ---------------------------------------\n");
1266 int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
1267 int error_state;
1268 rb_iseq_t *ret_iseq = pm_iseq_new_with_opt(node, name,
1269 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
1270 line_no, parent,
1271 isolated_depth ? isolated_depth + 1 : 0,
1272 type, ISEQ_COMPILE_DATA(iseq)->option, &error_state);
1273
1274 if (error_state) {
1275 pm_scope_node_destroy(node);
1276 RUBY_ASSERT(ret_iseq == NULL);
1277 rb_jump_tag(error_state);
1278 }
1279 debugs("[new_child_iseq]< ---------------------------------------\n");
1280 return ret_iseq;
1281}
1282
1283static int
1284pm_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)
1285{
1286 if (PM_NODE_TYPE_P(node, PM_CONSTANT_PATH_NODE)) {
1287 const pm_node_t *parent = ((const pm_constant_path_node_t *) node)->parent;
1288
1289 if (parent) {
1290 /* Bar::Foo */
1291 PM_COMPILE(parent);
1292 return VM_DEFINECLASS_FLAG_SCOPED;
1293 }
1294 else {
1295 /* toplevel class ::Foo */
1296 PUSH_INSN1(ret, *node_location, putobject, rb_cObject);
1297 return VM_DEFINECLASS_FLAG_SCOPED;
1298 }
1299 }
1300 else {
1301 /* class at cbase Foo */
1302 PUSH_INSN1(ret, *node_location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
1303 return 0;
1304 }
1305}
1306
1311static void
1312pm_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)
1313{
1314 const pm_node_location_t location = *node_location;
1315 LABEL *lfin = NEW_LABEL(location.line);
1316 LABEL *lcfin = NEW_LABEL(location.line);
1317 LABEL *lskip = NULL;
1318
1319 int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
1320 ID id_read_name = pm_constant_id_lookup(scope_node, read_name);
1321
1322 PM_COMPILE_NOT_POPPED(receiver);
1323 if (safe_nav) {
1324 lskip = NEW_LABEL(location.line);
1325 PUSH_INSN(ret, location, dup);
1326 PUSH_INSNL(ret, location, branchnil, lskip);
1327 }
1328
1329 PUSH_INSN(ret, location, dup);
1330 PUSH_SEND_WITH_FLAG(ret, location, id_read_name, INT2FIX(0), INT2FIX(flag));
1331 if (!popped) PUSH_INSN(ret, location, dup);
1332
1333 if (and_node) {
1334 PUSH_INSNL(ret, location, branchunless, lcfin);
1335 }
1336 else {
1337 PUSH_INSNL(ret, location, branchif, lcfin);
1338 }
1339
1340 if (!popped) PUSH_INSN(ret, location, pop);
1341 PM_COMPILE_NOT_POPPED(value);
1342
1343 if (!popped) {
1344 PUSH_INSN(ret, location, swap);
1345 PUSH_INSN1(ret, location, topn, INT2FIX(1));
1346 }
1347
1348 ID id_write_name = pm_constant_id_lookup(scope_node, write_name);
1349 PUSH_SEND_WITH_FLAG(ret, location, id_write_name, INT2FIX(1), INT2FIX(flag));
1350 PUSH_INSNL(ret, location, jump, lfin);
1351
1352 PUSH_LABEL(ret, lcfin);
1353 if (!popped) PUSH_INSN(ret, location, swap);
1354
1355 PUSH_LABEL(ret, lfin);
1356
1357 if (lskip && popped) PUSH_LABEL(ret, lskip);
1358 PUSH_INSN(ret, location, pop);
1359 if (lskip && !popped) PUSH_LABEL(ret, lskip);
1360}
1361
1362static 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);
1363
1369static void
1370pm_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)
1371{
1372 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
1373
1374 // If this element is not popped, then we need to create the hash on the
1375 // stack. Neighboring plain assoc nodes should be grouped together (either
1376 // by newhash or hash merge). Double splat nodes should be merged using the
1377 // merge_kwd method call.
1378 const int max_stack_length = 0x100;
1379 const unsigned int min_tmp_hash_length = 0x800;
1380
1381 int stack_length = 0;
1382 bool first_chunk = true;
1383
1384 // This is an optimization wherein we keep track of whether or not the
1385 // previous element was a static literal. If it was, then we do not attempt
1386 // to check if we have a subhash that can be optimized. If it was not, then
1387 // we do check.
1388 bool static_literal = false;
1389
1390 DECL_ANCHOR(anchor);
1391
1392 // Convert pushed elements to a hash, and merge if needed.
1393#define FLUSH_CHUNK \
1394 if (stack_length) { \
1395 if (first_chunk) { \
1396 PUSH_SEQ(ret, anchor); \
1397 PUSH_INSN1(ret, location, newhash, INT2FIX(stack_length)); \
1398 first_chunk = false; \
1399 } \
1400 else { \
1401 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); \
1402 PUSH_INSN(ret, location, swap); \
1403 PUSH_SEQ(ret, anchor); \
1404 PUSH_SEND(ret, location, id_core_hash_merge_ptr, INT2FIX(stack_length + 1)); \
1405 } \
1406 INIT_ANCHOR(anchor); \
1407 stack_length = 0; \
1408 }
1409
1410 for (size_t index = 0; index < elements->size; index++) {
1411 const pm_node_t *element = elements->nodes[index];
1412
1413 switch (PM_NODE_TYPE(element)) {
1414 case PM_ASSOC_NODE: {
1415 // Pre-allocation check (this branch can be omitted).
1416 if (
1417 (shareability == 0) &&
1418 PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL) && (
1419 (!static_literal && ((index + min_tmp_hash_length) < elements->size)) ||
1420 (first_chunk && stack_length == 0)
1421 )
1422 ) {
1423 // Count the elements that are statically-known.
1424 size_t count = 1;
1425 while (index + count < elements->size && PM_NODE_FLAG_P(elements->nodes[index + count], PM_NODE_FLAG_STATIC_LITERAL)) count++;
1426
1427 if ((first_chunk && stack_length == 0) || count >= min_tmp_hash_length) {
1428 // The subsequence of elements in this hash is long enough
1429 // to merit its own hash.
1430 VALUE ary = rb_ary_hidden_new(count);
1431
1432 // Create a hidden hash.
1433 for (size_t tmp_end = index + count; index < tmp_end; index++) {
1434 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[index];
1435
1436 VALUE elem[2] = {
1437 pm_static_literal_value(iseq, assoc->key, scope_node),
1438 pm_static_literal_value(iseq, assoc->value, scope_node)
1439 };
1440
1441 rb_ary_cat(ary, elem, 2);
1442 }
1443 index --;
1444
1445 VALUE hash = rb_hash_new_with_size(RARRAY_LEN(ary) / 2);
1446 rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary), hash);
1447 hash = rb_obj_hide(hash);
1448 OBJ_FREEZE(hash);
1449
1450 // Emit optimized code.
1451 FLUSH_CHUNK;
1452 if (first_chunk) {
1453 PUSH_INSN1(ret, location, duphash, hash);
1454 first_chunk = false;
1455 }
1456 else {
1457 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1458 PUSH_INSN(ret, location, swap);
1459 PUSH_INSN1(ret, location, putobject, hash);
1460 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1461 }
1462
1463 break;
1464 }
1465 else {
1466 static_literal = true;
1467 }
1468 }
1469 else {
1470 static_literal = false;
1471 }
1472
1473 // If this is a plain assoc node, then we can compile it directly
1474 // and then add the total number of values on the stack.
1475 if (shareability == 0) {
1476 pm_compile_node(iseq, element, anchor, false, scope_node);
1477 }
1478 else {
1479 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
1480 pm_compile_shareable_constant_value(iseq, assoc->key, shareability, path, ret, scope_node, false);
1481 pm_compile_shareable_constant_value(iseq, assoc->value, shareability, path, ret, scope_node, false);
1482 }
1483
1484 if ((stack_length += 2) >= max_stack_length) FLUSH_CHUNK;
1485 break;
1486 }
1487 case PM_ASSOC_SPLAT_NODE: {
1488 FLUSH_CHUNK;
1489
1490 const pm_assoc_splat_node_t *assoc_splat = (const pm_assoc_splat_node_t *) element;
1491 bool empty_hash = assoc_splat->value != NULL && (
1492 (PM_NODE_TYPE_P(assoc_splat->value, PM_HASH_NODE) && ((const pm_hash_node_t *) assoc_splat->value)->elements.size == 0) ||
1493 PM_NODE_TYPE_P(assoc_splat->value, PM_NIL_NODE)
1494 );
1495
1496 bool first_element = first_chunk && stack_length == 0;
1497 bool last_element = index == elements->size - 1;
1498 bool only_element = first_element && last_element;
1499
1500 if (empty_hash) {
1501 if (only_element && argument) {
1502 // **{} appears at the only keyword argument in method call,
1503 // so it won't be modified.
1504 //
1505 // This is only done for method calls and not for literal
1506 // hashes, because literal hashes should always result in a
1507 // new hash.
1508 PUSH_INSN(ret, location, putnil);
1509 }
1510 else if (first_element) {
1511 // **{} appears as the first keyword argument, so it may be
1512 // modified. We need to create a fresh hash object.
1513 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
1514 }
1515 // Any empty keyword splats that are not the first can be
1516 // ignored since merging an empty hash into the existing hash is
1517 // the same as not merging it.
1518 }
1519 else {
1520 if (only_element && argument) {
1521 // ** is only keyword argument in the method call. Use it
1522 // directly. This will be not be flagged as mutable. This is
1523 // only done for method calls and not for literal hashes,
1524 // because literal hashes should always result in a new
1525 // hash.
1526 if (shareability == 0) {
1527 PM_COMPILE_NOT_POPPED(element);
1528 }
1529 else {
1530 pm_compile_shareable_constant_value(iseq, element, shareability, path, ret, scope_node, false);
1531 }
1532 }
1533 else {
1534 // There is more than one keyword argument, or this is not a
1535 // method call. In that case, we need to add an empty hash
1536 // (if first keyword), or merge the hash to the accumulated
1537 // hash (if not the first keyword).
1538 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1539
1540 if (first_element) {
1541 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
1542 }
1543 else {
1544 PUSH_INSN(ret, location, swap);
1545 }
1546
1547 if (shareability == 0) {
1548 PM_COMPILE_NOT_POPPED(element);
1549 }
1550 else {
1551 pm_compile_shareable_constant_value(iseq, element, shareability, path, ret, scope_node, false);
1552 }
1553
1554 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1555 }
1556 }
1557
1558 first_chunk = false;
1559 static_literal = false;
1560 break;
1561 }
1562 default:
1563 RUBY_ASSERT("Invalid node type for hash" && false);
1564 break;
1565 }
1566 }
1567
1568 FLUSH_CHUNK;
1569#undef FLUSH_CHUNK
1570}
1571
1572#define SPLATARRAY_FALSE 0
1573#define SPLATARRAY_TRUE 1
1574#define DUP_SINGLE_KW_SPLAT 2
1575
1576// This is details. Users should call pm_setup_args() instead.
1577static int
1578pm_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)
1579{
1580 const pm_node_location_t location = *node_location;
1581
1582 int orig_argc = 0;
1583 bool has_splat = false;
1584 bool has_keyword_splat = false;
1585
1586 if (arguments_node == NULL) {
1587 if (*flags & VM_CALL_FCALL) {
1588 *flags |= VM_CALL_VCALL;
1589 }
1590 }
1591 else {
1592 const pm_node_list_t *arguments = &arguments_node->arguments;
1593 has_keyword_splat = PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT);
1594
1595 // We count the number of elements post the splat node that are not keyword elements to
1596 // eventually pass as an argument to newarray
1597 int post_splat_counter = 0;
1598 const pm_node_t *argument;
1599
1600 PM_NODE_LIST_FOREACH(arguments, index, argument) {
1601 switch (PM_NODE_TYPE(argument)) {
1602 // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
1603 case PM_KEYWORD_HASH_NODE: {
1604 const pm_keyword_hash_node_t *keyword_arg = (const pm_keyword_hash_node_t *) argument;
1605 const pm_node_list_t *elements = &keyword_arg->elements;
1606
1607 if (has_keyword_splat || has_splat) {
1608 *flags |= VM_CALL_KW_SPLAT;
1609 has_keyword_splat = true;
1610
1611 if (elements->size > 1 || !(elements->size == 1 && PM_NODE_TYPE_P(elements->nodes[0], PM_ASSOC_SPLAT_NODE))) {
1612 // A new hash will be created for the keyword arguments
1613 // in this case, so mark the method as passing mutable
1614 // keyword splat.
1615 *flags |= VM_CALL_KW_SPLAT_MUT;
1616 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1617 }
1618 else if (*dup_rest & DUP_SINGLE_KW_SPLAT) {
1619 *flags |= VM_CALL_KW_SPLAT_MUT;
1620 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1621 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
1622 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1623 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1624 }
1625 else {
1626 pm_compile_hash_elements(iseq, argument, elements, 0, Qundef, true, ret, scope_node);
1627 }
1628 }
1629 else {
1630 // We need to first figure out if all elements of the
1631 // KeywordHashNode are AssocNodes with symbol keys.
1632 if (PM_NODE_FLAG_P(keyword_arg, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS)) {
1633 // If they are all symbol keys then we can pass them as
1634 // keyword arguments. The first thing we need to do is
1635 // deduplicate. We'll do this using the combination of a
1636 // Ruby hash and a Ruby array.
1637 VALUE stored_indices = rb_hash_new();
1638 VALUE keyword_indices = rb_ary_new_capa(elements->size);
1639
1640 size_t size = 0;
1641 for (size_t element_index = 0; element_index < elements->size; element_index++) {
1642 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1643
1644 // Retrieve the stored index from the hash for this
1645 // keyword.
1646 VALUE keyword = pm_static_literal_value(iseq, assoc->key, scope_node);
1647 VALUE stored_index = rb_hash_aref(stored_indices, keyword);
1648
1649 // If this keyword was already seen in the hash,
1650 // then mark the array at that index as false and
1651 // decrement the keyword size.
1652 if (!NIL_P(stored_index)) {
1653 rb_ary_store(keyword_indices, NUM2LONG(stored_index), Qfalse);
1654 size--;
1655 }
1656
1657 // Store (and possibly overwrite) the index for this
1658 // keyword in the hash, mark the array at that index
1659 // as true, and increment the keyword size.
1660 rb_hash_aset(stored_indices, keyword, ULONG2NUM(element_index));
1661 rb_ary_store(keyword_indices, (long) element_index, Qtrue);
1662 size++;
1663 }
1664
1665 *kw_arg = rb_xmalloc_mul_add(size, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
1666 *flags |= VM_CALL_KWARG;
1667
1668 VALUE *keywords = (*kw_arg)->keywords;
1669 (*kw_arg)->references = 0;
1670 (*kw_arg)->keyword_len = (int) size;
1671
1672 size_t keyword_index = 0;
1673 for (size_t element_index = 0; element_index < elements->size; element_index++) {
1674 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1675 bool popped = true;
1676
1677 if (rb_ary_entry(keyword_indices, (long) element_index) == Qtrue) {
1678 keywords[keyword_index++] = pm_static_literal_value(iseq, assoc->key, scope_node);
1679 popped = false;
1680 }
1681
1682 PM_COMPILE(assoc->value);
1683 }
1684
1685 RUBY_ASSERT(keyword_index == size);
1686 }
1687 else {
1688 // If they aren't all symbol keys then we need to
1689 // construct a new hash and pass that as an argument.
1690 orig_argc++;
1691 *flags |= VM_CALL_KW_SPLAT;
1692
1693 size_t size = elements->size;
1694 if (size > 1) {
1695 // A new hash will be created for the keyword
1696 // arguments in this case, so mark the method as
1697 // passing mutable keyword splat.
1698 *flags |= VM_CALL_KW_SPLAT_MUT;
1699 }
1700
1701 for (size_t element_index = 0; element_index < size; element_index++) {
1702 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1703 PM_COMPILE_NOT_POPPED(assoc->key);
1704 PM_COMPILE_NOT_POPPED(assoc->value);
1705 }
1706
1707 PUSH_INSN1(ret, location, newhash, INT2FIX(size * 2));
1708 }
1709 }
1710 break;
1711 }
1712 case PM_SPLAT_NODE: {
1713 *flags |= VM_CALL_ARGS_SPLAT;
1714 const pm_splat_node_t *splat_node = (const pm_splat_node_t *) argument;
1715
1716 if (splat_node->expression) {
1717 PM_COMPILE_NOT_POPPED(splat_node->expression);
1718 }
1719 else {
1720 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
1721 PUSH_GETLOCAL(ret, location, index.index, index.level);
1722 }
1723
1724 bool first_splat = !has_splat;
1725
1726 if (first_splat) {
1727 // If this is the first splat array seen and it's not the
1728 // last parameter, we want splatarray to dup it.
1729 //
1730 // foo(a, *b, c)
1731 // ^^
1732 if (index + 1 < arguments->size || has_regular_blockarg) {
1733 PUSH_INSN1(ret, location, splatarray, (*dup_rest & SPLATARRAY_TRUE) ? Qtrue : Qfalse);
1734 if (*dup_rest & SPLATARRAY_TRUE) *dup_rest &= ~SPLATARRAY_TRUE;
1735 }
1736 // If this is the first spalt array seen and it's the last
1737 // parameter, we don't want splatarray to dup it.
1738 //
1739 // foo(a, *b)
1740 // ^^
1741 else {
1742 PUSH_INSN1(ret, location, splatarray, Qfalse);
1743 }
1744 }
1745 else {
1746 // If this is not the first splat array seen and it is also
1747 // the last parameter, we don't want splatarray to dup it
1748 // and we need to concat the array.
1749 //
1750 // foo(a, *b, *c)
1751 // ^^
1752 PUSH_INSN(ret, location, concattoarray);
1753 }
1754
1755 has_splat = true;
1756 post_splat_counter = 0;
1757
1758 break;
1759 }
1760 case PM_FORWARDING_ARGUMENTS_NODE: { // not counted in argc return value
1761 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
1762
1763 if (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
1764 *flags |= VM_CALL_FORWARDING;
1765
1766 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_DOT3, 0);
1767 PUSH_GETLOCAL(ret, location, mult_local.index, mult_local.level);
1768
1769 break;
1770 }
1771
1772 if (has_splat) {
1773 // If we already have a splat, we're concatenating to existing array
1774 orig_argc += 1;
1775 } else {
1776 orig_argc += 2;
1777 }
1778
1779 *flags |= VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KW_SPLAT;
1780
1781 // Forwarding arguments nodes are treated as foo(*, **, &)
1782 // So foo(...) equals foo(*, **, &) and as such the local
1783 // table for this method is known in advance
1784 //
1785 // Push the *
1786 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
1787 PUSH_GETLOCAL(ret, location, mult_local.index, mult_local.level);
1788
1789 if (has_splat) {
1790 // If we already have a splat, we need to concatenate arrays
1791 PUSH_INSN(ret, location, concattoarray);
1792 } else {
1793 PUSH_INSN1(ret, location, splatarray, Qfalse);
1794 }
1795
1796 // Push the **
1797 pm_local_index_t pow_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_POW, 0);
1798 PUSH_GETLOCAL(ret, location, pow_local.index, pow_local.level);
1799
1800 // Push the &
1801 pm_local_index_t and_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_AND, 0);
1802 PUSH_INSN2(ret, location, getblockparamproxy, INT2FIX(and_local.index + VM_ENV_DATA_SIZE - 1), INT2FIX(and_local.level));
1803
1804 break;
1805 }
1806 default: {
1807 post_splat_counter++;
1808 PM_COMPILE_NOT_POPPED(argument);
1809
1810 // If we have a splat and we've seen a splat, we need to process
1811 // everything after the splat.
1812 if (has_splat) {
1813 // Stack items are turned into an array and concatenated in
1814 // the following cases:
1815 //
1816 // If the next node is a splat:
1817 //
1818 // foo(*a, b, *c)
1819 //
1820 // If the next node is a kwarg or kwarg splat:
1821 //
1822 // foo(*a, b, c: :d)
1823 // foo(*a, b, **c)
1824 //
1825 // If the next node is NULL (we have hit the end):
1826 //
1827 // foo(*a, b)
1828 if (index == arguments->size - 1) {
1829 RUBY_ASSERT(post_splat_counter > 0);
1830 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(post_splat_counter));
1831 }
1832 else {
1833 pm_node_t *next_arg = arguments->nodes[index + 1];
1834
1835 switch (PM_NODE_TYPE(next_arg)) {
1836 // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
1837 case PM_KEYWORD_HASH_NODE: {
1838 PUSH_INSN1(ret, location, newarray, INT2FIX(post_splat_counter));
1839 PUSH_INSN(ret, location, concatarray);
1840 break;
1841 }
1842 case PM_SPLAT_NODE: {
1843 PUSH_INSN1(ret, location, newarray, INT2FIX(post_splat_counter));
1844 PUSH_INSN(ret, location, concatarray);
1845 break;
1846 }
1847 default:
1848 break;
1849 }
1850 }
1851 }
1852 else {
1853 orig_argc++;
1854 }
1855 }
1856 }
1857 }
1858 }
1859
1860 if (has_splat) orig_argc++;
1861 if (has_keyword_splat) orig_argc++;
1862 return orig_argc;
1863}
1864
1869static inline bool
1870pm_setup_args_dup_rest_p(const pm_node_t *node)
1871{
1872 switch (PM_NODE_TYPE(node)) {
1873 case PM_BACK_REFERENCE_READ_NODE:
1874 case PM_CLASS_VARIABLE_READ_NODE:
1875 case PM_CONSTANT_READ_NODE:
1876 case PM_FALSE_NODE:
1877 case PM_FLOAT_NODE:
1878 case PM_GLOBAL_VARIABLE_READ_NODE:
1879 case PM_IMAGINARY_NODE:
1880 case PM_INSTANCE_VARIABLE_READ_NODE:
1881 case PM_INTEGER_NODE:
1882 case PM_LAMBDA_NODE:
1883 case PM_LOCAL_VARIABLE_READ_NODE:
1884 case PM_NIL_NODE:
1885 case PM_NUMBERED_REFERENCE_READ_NODE:
1886 case PM_RATIONAL_NODE:
1887 case PM_REGULAR_EXPRESSION_NODE:
1888 case PM_SELF_NODE:
1889 case PM_STRING_NODE:
1890 case PM_SYMBOL_NODE:
1891 case PM_TRUE_NODE:
1892 return false;
1893 case PM_CONSTANT_PATH_NODE: {
1894 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
1895 if (cast->parent != NULL) {
1896 return pm_setup_args_dup_rest_p(cast->parent);
1897 }
1898 return false;
1899 }
1900 case PM_IMPLICIT_NODE:
1901 return pm_setup_args_dup_rest_p(((const pm_implicit_node_t *) node)->value);
1902 case PM_ARRAY_NODE: {
1903 const pm_array_node_t *cast = (const pm_array_node_t *) node;
1904 for (size_t index = 0; index < cast->elements.size; index++) {
1905 if (pm_setup_args_dup_rest_p(cast->elements.nodes[index])) {
1906 return true;
1907 }
1908 }
1909 return false;
1910 }
1911 default:
1912 return true;
1913 }
1914}
1915
1919static int
1920pm_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)
1921{
1922 int dup_rest = SPLATARRAY_TRUE;
1923
1924 const pm_node_list_t *arguments;
1925 size_t arguments_size;
1926
1927 // Calls like foo(1, *f, **hash) that use splat and kwsplat could be
1928 // eligible for eliding duping the rest array (dup_reset=false).
1929 if (
1930 arguments_node != NULL &&
1931 (arguments = &arguments_node->arguments, arguments_size = arguments->size) >= 2 &&
1932 PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT) &&
1933 !PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS) &&
1934 PM_NODE_TYPE_P(arguments->nodes[arguments_size - 1], PM_KEYWORD_HASH_NODE)
1935 ) {
1936 // Start by assuming that dup_rest=false, then check each element of the
1937 // hash to ensure we don't need to flip it back to true (in case one of
1938 // the elements could potentially mutate the array).
1939 dup_rest = SPLATARRAY_FALSE;
1940
1941 const pm_keyword_hash_node_t *keyword_hash = (const pm_keyword_hash_node_t *) arguments->nodes[arguments_size - 1];
1942 const pm_node_list_t *elements = &keyword_hash->elements;
1943
1944 for (size_t index = 0; dup_rest == SPLATARRAY_FALSE && index < elements->size; index++) {
1945 const pm_node_t *element = elements->nodes[index];
1946
1947 switch (PM_NODE_TYPE(element)) {
1948 case PM_ASSOC_NODE: {
1949 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
1950 if (pm_setup_args_dup_rest_p(assoc->key) || pm_setup_args_dup_rest_p(assoc->value)) dup_rest = SPLATARRAY_TRUE;
1951 break;
1952 }
1953 case PM_ASSOC_SPLAT_NODE: {
1954 const pm_assoc_splat_node_t *assoc = (const pm_assoc_splat_node_t *) element;
1955 if (assoc->value != NULL && pm_setup_args_dup_rest_p(assoc->value)) dup_rest = SPLATARRAY_TRUE;
1956 break;
1957 }
1958 default:
1959 break;
1960 }
1961 }
1962 }
1963
1964 int initial_dup_rest = dup_rest;
1965 int argc;
1966
1967 if (block && PM_NODE_TYPE_P(block, PM_BLOCK_ARGUMENT_NODE)) {
1968 // We compile the `&block_arg` expression first and stitch it later
1969 // since the nature of the expression influences whether splat should
1970 // duplicate the array.
1971 bool regular_block_arg = true;
1972 const pm_node_t *block_expr = ((const pm_block_argument_node_t *)block)->expression;
1973
1974 if (block_expr && pm_setup_args_dup_rest_p(block_expr)) {
1975 dup_rest = SPLATARRAY_TRUE | DUP_SINGLE_KW_SPLAT;
1976 initial_dup_rest = dup_rest;
1977 }
1978
1979 DECL_ANCHOR(block_arg);
1980 pm_compile_node(iseq, block, block_arg, false, scope_node);
1981
1982 *flags |= VM_CALL_ARGS_BLOCKARG;
1983
1984 if (LIST_INSN_SIZE_ONE(block_arg)) {
1985 LINK_ELEMENT *elem = FIRST_ELEMENT(block_arg);
1986 if (IS_INSN(elem)) {
1987 INSN *iobj = (INSN *) elem;
1988 if (iobj->insn_id == BIN(getblockparam)) {
1989 iobj->insn_id = BIN(getblockparamproxy);
1990 }
1991
1992 // Allow splat without duplication for simple one-instruction
1993 // block arguments like `&arg`. It is known that this
1994 // optimization can be too aggressive in some cases. See
1995 // [Bug #16504].
1996 regular_block_arg = false;
1997 }
1998 }
1999
2000 argc = pm_setup_args_core(arguments_node, block, flags, regular_block_arg, kw_arg, &dup_rest, iseq, ret, scope_node, node_location);
2001 PUSH_SEQ(ret, block_arg);
2002 }
2003 else {
2004 argc = pm_setup_args_core(arguments_node, block, flags, false, kw_arg, &dup_rest, iseq, ret, scope_node, node_location);
2005 }
2006
2007 // If the dup_rest flag was consumed while compiling the arguments (which
2008 // effectively means we found the splat node), then it would have changed
2009 // during the call to pm_setup_args_core. In this case, we want to add the
2010 // VM_CALL_ARGS_SPLAT_MUT flag.
2011 if (*flags & VM_CALL_ARGS_SPLAT && dup_rest != initial_dup_rest) {
2012 *flags |= VM_CALL_ARGS_SPLAT_MUT;
2013 }
2014
2015 return argc;
2016}
2017
2028static void
2029pm_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)
2030{
2031 const pm_node_location_t location = *node_location;
2032 if (!popped) PUSH_INSN(ret, location, putnil);
2033
2034 PM_COMPILE_NOT_POPPED(node->receiver);
2035
2036 int boff = (node->block == NULL ? 0 : 1);
2037 int flag = PM_NODE_TYPE_P(node->receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
2038 struct rb_callinfo_kwarg *keywords = NULL;
2039 int argc = pm_setup_args(node->arguments, (const pm_node_t *) node->block, &flag, &keywords, iseq, ret, scope_node, node_location);
2040
2041 if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
2042 if (boff) {
2043 PUSH_INSN(ret, location, splatkw);
2044 }
2045 else {
2046 PUSH_INSN(ret, location, dup);
2047 PUSH_INSN(ret, location, splatkw);
2048 PUSH_INSN(ret, location, pop);
2049 }
2050 }
2051
2052 int dup_argn = argc + 1 + boff;
2053 int keyword_len = 0;
2054
2055 if (keywords) {
2056 keyword_len = keywords->keyword_len;
2057 dup_argn += keyword_len;
2058 }
2059
2060 PUSH_INSN1(ret, location, dupn, INT2FIX(dup_argn));
2061 PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords);
2062 PM_COMPILE_NOT_POPPED(node->value);
2063
2064 ID id_operator = pm_constant_id_lookup(scope_node, node->binary_operator);
2065 PUSH_SEND(ret, location, id_operator, INT2FIX(1));
2066
2067 if (!popped) {
2068 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2069 }
2070 if (flag & VM_CALL_ARGS_SPLAT) {
2071 if (flag & VM_CALL_KW_SPLAT) {
2072 PUSH_INSN1(ret, location, topn, INT2FIX(2 + boff));
2073
2074 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2075 PUSH_INSN1(ret, location, splatarray, Qtrue);
2076 flag |= VM_CALL_ARGS_SPLAT_MUT;
2077 }
2078
2079 PUSH_INSN(ret, location, swap);
2080 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2081 PUSH_INSN1(ret, location, setn, INT2FIX(2 + boff));
2082 PUSH_INSN(ret, location, pop);
2083 }
2084 else {
2085 if (boff > 0) {
2086 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2087 PUSH_INSN(ret, location, swap);
2088 PUSH_INSN(ret, location, pop);
2089 }
2090 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2091 PUSH_INSN(ret, location, swap);
2092 PUSH_INSN1(ret, location, splatarray, Qtrue);
2093 PUSH_INSN(ret, location, swap);
2094 flag |= VM_CALL_ARGS_SPLAT_MUT;
2095 }
2096 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2097 if (boff > 0) {
2098 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2099 PUSH_INSN(ret, location, pop);
2100 PUSH_INSN(ret, location, pop);
2101 }
2102 }
2103
2104 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc), NULL, INT2FIX(flag), keywords);
2105 }
2106 else if (flag & VM_CALL_KW_SPLAT) {
2107 if (boff > 0) {
2108 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2109 PUSH_INSN(ret, location, swap);
2110 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2111 PUSH_INSN(ret, location, pop);
2112 }
2113 PUSH_INSN(ret, location, swap);
2114 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2115 }
2116 else if (keyword_len) {
2117 PUSH_INSN(ret, location, dup);
2118 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 2));
2119 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 1));
2120 PUSH_INSN(ret, location, pop);
2121 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2122 }
2123 else {
2124 if (boff > 0) {
2125 PUSH_INSN(ret, location, swap);
2126 }
2127 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2128 }
2129
2130 PUSH_INSN(ret, location, pop);
2131}
2132
2145static void
2146pm_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)
2147{
2148 const pm_node_location_t location = *node_location;
2149 if (!popped) PUSH_INSN(ret, location, putnil);
2150 PM_COMPILE_NOT_POPPED(receiver);
2151
2152 int boff = (block == NULL ? 0 : 1);
2153 int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
2154 struct rb_callinfo_kwarg *keywords = NULL;
2155 int argc = pm_setup_args(arguments, (const pm_node_t *) block, &flag, &keywords, iseq, ret, scope_node, node_location);
2156
2157 if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
2158 if (boff) {
2159 PUSH_INSN(ret, location, splatkw);
2160 }
2161 else {
2162 PUSH_INSN(ret, location, dup);
2163 PUSH_INSN(ret, location, splatkw);
2164 PUSH_INSN(ret, location, pop);
2165 }
2166 }
2167
2168 int dup_argn = argc + 1 + boff;
2169 int keyword_len = 0;
2170
2171 if (keywords) {
2172 keyword_len = keywords->keyword_len;
2173 dup_argn += keyword_len;
2174 }
2175
2176 PUSH_INSN1(ret, location, dupn, INT2FIX(dup_argn));
2177 PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords);
2178
2179 LABEL *label = NEW_LABEL(location.line);
2180 LABEL *lfin = NEW_LABEL(location.line);
2181
2182 PUSH_INSN(ret, location, dup);
2183 if (PM_NODE_TYPE_P(node, PM_INDEX_AND_WRITE_NODE)) {
2184 PUSH_INSNL(ret, location, branchunless, label);
2185 }
2186 else {
2187 PUSH_INSNL(ret, location, branchif, label);
2188 }
2189
2190 PUSH_INSN(ret, location, pop);
2191 PM_COMPILE_NOT_POPPED(value);
2192
2193 if (!popped) {
2194 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2195 }
2196
2197 if (flag & VM_CALL_ARGS_SPLAT) {
2198 if (flag & VM_CALL_KW_SPLAT) {
2199 PUSH_INSN1(ret, location, topn, INT2FIX(2 + boff));
2200 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2201 PUSH_INSN1(ret, location, splatarray, Qtrue);
2202 flag |= VM_CALL_ARGS_SPLAT_MUT;
2203 }
2204
2205 PUSH_INSN(ret, location, swap);
2206 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2207 PUSH_INSN1(ret, location, setn, INT2FIX(2 + boff));
2208 PUSH_INSN(ret, location, pop);
2209 }
2210 else {
2211 if (boff > 0) {
2212 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2213 PUSH_INSN(ret, location, swap);
2214 PUSH_INSN(ret, location, pop);
2215 }
2216 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
2217 PUSH_INSN(ret, location, swap);
2218 PUSH_INSN1(ret, location, splatarray, Qtrue);
2219 PUSH_INSN(ret, location, swap);
2220 flag |= VM_CALL_ARGS_SPLAT_MUT;
2221 }
2222 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
2223 if (boff > 0) {
2224 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2225 PUSH_INSN(ret, location, pop);
2226 PUSH_INSN(ret, location, pop);
2227 }
2228 }
2229
2230 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc), NULL, INT2FIX(flag), keywords);
2231 }
2232 else if (flag & VM_CALL_KW_SPLAT) {
2233 if (boff > 0) {
2234 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2235 PUSH_INSN(ret, location, swap);
2236 PUSH_INSN1(ret, location, setn, INT2FIX(3));
2237 PUSH_INSN(ret, location, pop);
2238 }
2239
2240 PUSH_INSN(ret, location, swap);
2241 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2242 }
2243 else if (keyword_len) {
2244 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 1));
2245 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 0));
2246 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2247 }
2248 else {
2249 if (boff > 0) {
2250 PUSH_INSN(ret, location, swap);
2251 }
2252 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
2253 }
2254
2255 PUSH_INSN(ret, location, pop);
2256 PUSH_INSNL(ret, location, jump, lfin);
2257 PUSH_LABEL(ret, label);
2258 if (!popped) {
2259 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
2260 }
2261 PUSH_INSN1(ret, location, adjuststack, INT2FIX(dup_argn + 1));
2262 PUSH_LABEL(ret, lfin);
2263}
2264
2265// When we compile a pattern matching expression, we use the stack as a scratch
2266// space to store lots of different values (consider it like we have a pattern
2267// matching function and we need space for a bunch of different local
2268// variables). The "base index" refers to the index on the stack where we
2269// started compiling the pattern matching expression. These offsets from that
2270// base index indicate the location of the various locals we need.
2271#define PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE 0
2272#define PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING 1
2273#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P 2
2274#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE 3
2275#define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY 4
2276
2277// A forward declaration because this is the recursive function that handles
2278// compiling a pattern. It can be reentered by nesting patterns, as in the case
2279// of arrays or hashes.
2280static 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);
2281
2286static int
2287pm_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)
2288{
2289 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
2290 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2291
2292 PUSH_INSN(ret, location, dup);
2293 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2294
2295 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2296 PUSH_INSN1(ret, location, putobject, message);
2297 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2298 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(2));
2299 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2300
2301 PUSH_INSN1(ret, location, putobject, Qfalse);
2302 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2303
2304 PUSH_INSN(ret, location, pop);
2305 PUSH_INSN(ret, location, pop);
2306 PUSH_LABEL(ret, match_succeeded_label);
2307
2308 return COMPILE_OK;
2309}
2310
2316static int
2317pm_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)
2318{
2319 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
2320 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2321
2322 PUSH_INSN(ret, location, dup);
2323 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2324
2325 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2326 PUSH_INSN1(ret, location, putobject, message);
2327 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2328 PUSH_INSN(ret, location, dup);
2329 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2330 PUSH_INSN1(ret, location, putobject, length);
2331 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(4));
2332 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2333
2334 PUSH_INSN1(ret, location, putobject, Qfalse);
2335 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2336
2337 PUSH_INSN(ret, location, pop);
2338 PUSH_INSN(ret, location, pop);
2339 PUSH_LABEL(ret, match_succeeded_label);
2340
2341 return COMPILE_OK;
2342}
2343
2349static int
2350pm_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)
2351{
2352 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
2353 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2354
2355 PUSH_INSN(ret, location, dup);
2356 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2357 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2358
2359 VALUE operand = rb_fstring_lit("%p === %p does not return true");
2360 PUSH_INSN1(ret, location, putobject, operand);
2361
2362 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2363 PUSH_INSN1(ret, location, topn, INT2FIX(5));
2364 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2365 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2366 PUSH_INSN1(ret, location, putobject, Qfalse);
2367 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2368 PUSH_INSN(ret, location, pop);
2369 PUSH_INSN(ret, location, pop);
2370
2371 PUSH_LABEL(ret, match_succeeded_label);
2372 PUSH_INSN1(ret, location, setn, INT2FIX(2));
2373 PUSH_INSN(ret, location, pop);
2374 PUSH_INSN(ret, location, pop);
2375
2376 return COMPILE_OK;
2377}
2378
2385static int
2386pm_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)
2387{
2388 LABEL *matched_label = NEW_LABEL(pm_node_line_number(scope_node->parser, node));
2389 CHECK(pm_compile_pattern(iseq, scope_node, node, ret, matched_label, unmatched_label, in_single_pattern, in_alternation_pattern, use_deconstructed_cache, base_index));
2390 PUSH_LABEL(ret, matched_label);
2391 return COMPILE_OK;
2392}
2393
2399static int
2400pm_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)
2401{
2402 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
2403
2404 if (use_deconstructed_cache) {
2405 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2406 PUSH_INSNL(ret, location, branchnil, deconstruct_label);
2407
2408 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2409 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2410
2411 PUSH_INSN(ret, location, pop);
2412 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE - 1));
2413 PUSH_INSNL(ret, location, jump, deconstructed_label);
2414 }
2415 else {
2416 PUSH_INSNL(ret, location, jump, deconstruct_label);
2417 }
2418
2419 PUSH_LABEL(ret, deconstruct_label);
2420 PUSH_INSN(ret, location, dup);
2421
2422 VALUE operand = ID2SYM(rb_intern("deconstruct"));
2423 PUSH_INSN1(ret, location, putobject, operand);
2424 PUSH_SEND(ret, location, idRespond_to, INT2FIX(1));
2425
2426 if (use_deconstructed_cache) {
2427 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE + 1));
2428 }
2429
2430 if (in_single_pattern) {
2431 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p does not respond to #deconstruct"), base_index + 1));
2432 }
2433
2434 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2435 PUSH_SEND(ret, location, rb_intern("deconstruct"), INT2FIX(0));
2436
2437 if (use_deconstructed_cache) {
2438 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2439 }
2440
2441 PUSH_INSN(ret, location, dup);
2442 PUSH_INSN1(ret, location, checktype, INT2FIX(T_ARRAY));
2443 PUSH_INSNL(ret, location, branchunless, type_error_label);
2444 PUSH_LABEL(ret, deconstructed_label);
2445
2446 return COMPILE_OK;
2447}
2448
2453static int
2454pm_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)
2455{
2456 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
2457
2458 PUSH_INSN(ret, location, dup);
2459 PM_COMPILE_NOT_POPPED(node);
2460
2461 if (in_single_pattern) {
2462 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
2463 }
2464 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
2465 if (in_single_pattern) {
2466 CHECK(pm_compile_pattern_eqq_error(iseq, scope_node, node, ret, base_index + 3));
2467 }
2468 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2469 return COMPILE_OK;
2470}
2471
2476static void
2477pm_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)
2478{
2479 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
2480 LABEL *key_error_label = NEW_LABEL(location.line);
2481 LABEL *cleanup_label = NEW_LABEL(location.line);
2482
2483 struct rb_callinfo_kwarg *kw_arg = rb_xmalloc_mul_add(2, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
2484 kw_arg->references = 0;
2485 kw_arg->keyword_len = 2;
2486 kw_arg->keywords[0] = ID2SYM(rb_intern("matchee"));
2487 kw_arg->keywords[1] = ID2SYM(rb_intern("key"));
2488
2489 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2490 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2491 PUSH_INSNL(ret, location, branchif, key_error_label);
2492
2493 PUSH_INSN1(ret, location, putobject, rb_eNoMatchingPatternError);
2494 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2495
2496 {
2497 VALUE operand = rb_fstring_lit("%p: %s");
2498 PUSH_INSN1(ret, location, putobject, operand);
2499 }
2500
2501 PUSH_INSN1(ret, location, topn, INT2FIX(4));
2502 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 6));
2503 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2504 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2505 PUSH_INSNL(ret, location, jump, cleanup_label);
2506
2507 PUSH_LABEL(ret, key_error_label);
2508 PUSH_INSN1(ret, location, putobject, rb_eNoMatchingPatternKeyError);
2509 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2510
2511 {
2512 VALUE operand = rb_fstring_lit("%p: %s");
2513 PUSH_INSN1(ret, location, putobject, operand);
2514 }
2515
2516 PUSH_INSN1(ret, location, topn, INT2FIX(4));
2517 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 6));
2518 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2519 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE + 4));
2520 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY + 5));
2521 PUSH_SEND_R(ret, location, rb_intern("new"), INT2FIX(1), NULL, INT2FIX(VM_CALL_KWARG), kw_arg);
2522 PUSH_SEND(ret, location, id_core_raise, INT2FIX(1));
2523 PUSH_LABEL(ret, cleanup_label);
2524
2525 PUSH_INSN1(ret, location, adjuststack, INT2FIX(7));
2526 if (!popped) PUSH_INSN(ret, location, putnil);
2527 PUSH_INSNL(ret, location, jump, done_label);
2528 PUSH_INSN1(ret, location, dupn, INT2FIX(5));
2529 if (popped) PUSH_INSN(ret, location, putnil);
2530}
2531
2535static int
2536pm_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)
2537{
2538 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
2539
2540 switch (PM_NODE_TYPE(node)) {
2541 case PM_ARRAY_PATTERN_NODE: {
2542 // Array patterns in pattern matching are triggered by using commas in
2543 // a pattern or wrapping it in braces. They are represented by a
2544 // ArrayPatternNode. This looks like:
2545 //
2546 // foo => [1, 2, 3]
2547 //
2548 // It can optionally have a splat in the middle of it, which can
2549 // optionally have a name attached.
2550 const pm_array_pattern_node_t *cast = (const pm_array_pattern_node_t *) node;
2551
2552 const size_t requireds_size = cast->requireds.size;
2553 const size_t posts_size = cast->posts.size;
2554 const size_t minimum_size = requireds_size + posts_size;
2555
2556 bool rest_named = false;
2557 bool use_rest_size = false;
2558
2559 if (cast->rest != NULL) {
2560 rest_named = (PM_NODE_TYPE_P(cast->rest, PM_SPLAT_NODE) && ((const pm_splat_node_t *) cast->rest)->expression != NULL);
2561 use_rest_size = (rest_named || (!rest_named && posts_size > 0));
2562 }
2563
2564 LABEL *match_failed_label = NEW_LABEL(location.line);
2565 LABEL *type_error_label = NEW_LABEL(location.line);
2566 LABEL *deconstruct_label = NEW_LABEL(location.line);
2567 LABEL *deconstructed_label = NEW_LABEL(location.line);
2568
2569 if (use_rest_size) {
2570 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2571 PUSH_INSN(ret, location, swap);
2572 base_index++;
2573 }
2574
2575 if (cast->constant != NULL) {
2576 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2577 }
2578
2579 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));
2580
2581 PUSH_INSN(ret, location, dup);
2582 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2583 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2584 PUSH_SEND(ret, location, cast->rest == NULL ? idEq : idGE, INT2FIX(1));
2585 if (in_single_pattern) {
2586 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+)");
2587 CHECK(pm_compile_pattern_length_error(iseq, scope_node, node, ret, message, INT2FIX(minimum_size), base_index + 1));
2588 }
2589 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2590
2591 for (size_t index = 0; index < requireds_size; index++) {
2592 const pm_node_t *required = cast->requireds.nodes[index];
2593 PUSH_INSN(ret, location, dup);
2594 PUSH_INSN1(ret, location, putobject, INT2FIX(index));
2595 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2596 CHECK(pm_compile_pattern_match(iseq, scope_node, required, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
2597 }
2598
2599 if (cast->rest != NULL) {
2600 if (rest_named) {
2601 PUSH_INSN(ret, location, dup);
2602 PUSH_INSN1(ret, location, putobject, INT2FIX(requireds_size));
2603 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2604 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2605 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2606 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2607 PUSH_INSN1(ret, location, setn, INT2FIX(4));
2608 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2609 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));
2610 }
2611 else if (posts_size > 0) {
2612 PUSH_INSN(ret, location, dup);
2613 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2614 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2615 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2616 PUSH_INSN1(ret, location, setn, INT2FIX(2));
2617 PUSH_INSN(ret, location, pop);
2618 }
2619 }
2620
2621 for (size_t index = 0; index < posts_size; index++) {
2622 const pm_node_t *post = cast->posts.nodes[index];
2623 PUSH_INSN(ret, location, dup);
2624
2625 PUSH_INSN1(ret, location, putobject, INT2FIX(requireds_size + index));
2626 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2627 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2628 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2629 CHECK(pm_compile_pattern_match(iseq, scope_node, post, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
2630 }
2631
2632 PUSH_INSN(ret, location, pop);
2633 if (use_rest_size) {
2634 PUSH_INSN(ret, location, pop);
2635 }
2636
2637 PUSH_INSNL(ret, location, jump, matched_label);
2638 PUSH_INSN(ret, location, putnil);
2639 if (use_rest_size) {
2640 PUSH_INSN(ret, location, putnil);
2641 }
2642
2643 PUSH_LABEL(ret, type_error_label);
2644 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2645 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2646
2647 {
2648 VALUE operand = rb_fstring_lit("deconstruct must return Array");
2649 PUSH_INSN1(ret, location, putobject, operand);
2650 }
2651
2652 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2653 PUSH_INSN(ret, location, pop);
2654
2655 PUSH_LABEL(ret, match_failed_label);
2656 PUSH_INSN(ret, location, pop);
2657 if (use_rest_size) {
2658 PUSH_INSN(ret, location, pop);
2659 }
2660
2661 PUSH_INSNL(ret, location, jump, unmatched_label);
2662 break;
2663 }
2664 case PM_FIND_PATTERN_NODE: {
2665 // Find patterns in pattern matching are triggered by using commas in
2666 // a pattern or wrapping it in braces and using a splat on both the left
2667 // and right side of the pattern. This looks like:
2668 //
2669 // foo => [*, 1, 2, 3, *]
2670 //
2671 // There can be any number of requireds in the middle. The splats on
2672 // both sides can optionally have names attached.
2673 const pm_find_pattern_node_t *cast = (const pm_find_pattern_node_t *) node;
2674 const size_t size = cast->requireds.size;
2675
2676 LABEL *match_failed_label = NEW_LABEL(location.line);
2677 LABEL *type_error_label = NEW_LABEL(location.line);
2678 LABEL *deconstruct_label = NEW_LABEL(location.line);
2679 LABEL *deconstructed_label = NEW_LABEL(location.line);
2680
2681 if (cast->constant) {
2682 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2683 }
2684
2685 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));
2686
2687 PUSH_INSN(ret, location, dup);
2688 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2689 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2690 PUSH_SEND(ret, location, idGE, INT2FIX(1));
2691 if (in_single_pattern) {
2692 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));
2693 }
2694 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2695
2696 {
2697 LABEL *while_begin_label = NEW_LABEL(location.line);
2698 LABEL *next_loop_label = NEW_LABEL(location.line);
2699 LABEL *find_succeeded_label = NEW_LABEL(location.line);
2700 LABEL *find_failed_label = NEW_LABEL(location.line);
2701
2702 PUSH_INSN(ret, location, dup);
2703 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2704
2705 PUSH_INSN(ret, location, dup);
2706 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2707 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2708 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2709 PUSH_LABEL(ret, while_begin_label);
2710
2711 PUSH_INSN(ret, location, dup);
2712 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2713 PUSH_SEND(ret, location, idLE, INT2FIX(1));
2714 PUSH_INSNL(ret, location, branchunless, find_failed_label);
2715
2716 for (size_t index = 0; index < size; index++) {
2717 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2718 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2719
2720 if (index != 0) {
2721 PUSH_INSN1(ret, location, putobject, INT2FIX(index));
2722 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2723 }
2724
2725 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2726 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));
2727 }
2728
2729 const pm_splat_node_t *left = cast->left;
2730
2731 if (left->expression != NULL) {
2732 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2733 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2734 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2735 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2736 CHECK(pm_compile_pattern_match(iseq, scope_node, left->expression, ret, find_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 4));
2737 }
2738
2739 RUBY_ASSERT(PM_NODE_TYPE_P(cast->right, PM_SPLAT_NODE));
2740 const pm_splat_node_t *right = (const pm_splat_node_t *) cast->right;
2741
2742 if (right->expression != NULL) {
2743 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2744 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2745 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2746 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2747 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2748 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2749 pm_compile_pattern_match(iseq, scope_node, right->expression, ret, find_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 4);
2750 }
2751
2752 PUSH_INSNL(ret, location, jump, find_succeeded_label);
2753
2754 PUSH_LABEL(ret, next_loop_label);
2755 PUSH_INSN1(ret, location, putobject, INT2FIX(1));
2756 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2757 PUSH_INSNL(ret, location, jump, while_begin_label);
2758
2759 PUSH_LABEL(ret, find_failed_label);
2760 PUSH_INSN1(ret, location, adjuststack, INT2FIX(3));
2761 if (in_single_pattern) {
2762 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2763
2764 {
2765 VALUE operand = rb_fstring_lit("%p does not match to find pattern");
2766 PUSH_INSN1(ret, location, putobject, operand);
2767 }
2768
2769 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2770 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(2));
2771 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2772
2773 PUSH_INSN1(ret, location, putobject, Qfalse);
2774 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2775
2776 PUSH_INSN(ret, location, pop);
2777 PUSH_INSN(ret, location, pop);
2778 }
2779 PUSH_INSNL(ret, location, jump, match_failed_label);
2780 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2781
2782 PUSH_LABEL(ret, find_succeeded_label);
2783 PUSH_INSN1(ret, location, adjuststack, INT2FIX(3));
2784 }
2785
2786 PUSH_INSN(ret, location, pop);
2787 PUSH_INSNL(ret, location, jump, matched_label);
2788 PUSH_INSN(ret, location, putnil);
2789
2790 PUSH_LABEL(ret, type_error_label);
2791 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2792 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2793
2794 {
2795 VALUE operand = rb_fstring_lit("deconstruct must return Array");
2796 PUSH_INSN1(ret, location, putobject, operand);
2797 }
2798
2799 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2800 PUSH_INSN(ret, location, pop);
2801
2802 PUSH_LABEL(ret, match_failed_label);
2803 PUSH_INSN(ret, location, pop);
2804 PUSH_INSNL(ret, location, jump, unmatched_label);
2805
2806 break;
2807 }
2808 case PM_HASH_PATTERN_NODE: {
2809 // Hash patterns in pattern matching are triggered by using labels and
2810 // values in a pattern or by using the ** operator. They are represented
2811 // by the HashPatternNode. This looks like:
2812 //
2813 // foo => { a: 1, b: 2, **bar }
2814 //
2815 // It can optionally have an assoc splat in the middle of it, which can
2816 // optionally have a name.
2817 const pm_hash_pattern_node_t *cast = (const pm_hash_pattern_node_t *) node;
2818
2819 // We don't consider it a "rest" parameter if it's a ** that is unnamed.
2820 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);
2821 bool has_keys = cast->elements.size > 0 || cast->rest != NULL;
2822
2823 LABEL *match_failed_label = NEW_LABEL(location.line);
2824 LABEL *type_error_label = NEW_LABEL(location.line);
2825 VALUE keys = Qnil;
2826
2827 if (has_keys && !has_rest) {
2828 keys = rb_ary_new_capa(cast->elements.size);
2829
2830 for (size_t index = 0; index < cast->elements.size; index++) {
2831 const pm_node_t *element = cast->elements.nodes[index];
2832 RUBY_ASSERT(PM_NODE_TYPE_P(element, PM_ASSOC_NODE));
2833
2834 const pm_node_t *key = ((const pm_assoc_node_t *) element)->key;
2835 RUBY_ASSERT(PM_NODE_TYPE_P(key, PM_SYMBOL_NODE));
2836
2837 VALUE symbol = ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) key));
2838 rb_ary_push(keys, symbol);
2839 }
2840 }
2841
2842 if (cast->constant) {
2843 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2844 }
2845
2846 PUSH_INSN(ret, location, dup);
2847
2848 {
2849 VALUE operand = ID2SYM(rb_intern("deconstruct_keys"));
2850 PUSH_INSN1(ret, location, putobject, operand);
2851 }
2852
2853 PUSH_SEND(ret, location, idRespond_to, INT2FIX(1));
2854 if (in_single_pattern) {
2855 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p does not respond to #deconstruct_keys"), base_index + 1));
2856 }
2857 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2858
2859 if (NIL_P(keys)) {
2860 PUSH_INSN(ret, location, putnil);
2861 }
2862 else {
2863 PUSH_INSN1(ret, location, duparray, keys);
2864 RB_OBJ_WRITTEN(iseq, Qundef, rb_obj_hide(keys));
2865 }
2866 PUSH_SEND(ret, location, rb_intern("deconstruct_keys"), INT2FIX(1));
2867
2868 PUSH_INSN(ret, location, dup);
2869 PUSH_INSN1(ret, location, checktype, INT2FIX(T_HASH));
2870 PUSH_INSNL(ret, location, branchunless, type_error_label);
2871
2872 if (has_rest) {
2873 PUSH_SEND(ret, location, rb_intern("dup"), INT2FIX(0));
2874 }
2875
2876 if (has_keys) {
2877 DECL_ANCHOR(match_values);
2878
2879 for (size_t index = 0; index < cast->elements.size; index++) {
2880 const pm_node_t *element = cast->elements.nodes[index];
2881 RUBY_ASSERT(PM_NODE_TYPE_P(element, PM_ASSOC_NODE));
2882
2883 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
2884 const pm_node_t *key = assoc->key;
2885 RUBY_ASSERT(PM_NODE_TYPE_P(key, PM_SYMBOL_NODE));
2886
2887 VALUE symbol = ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) key));
2888 PUSH_INSN(ret, location, dup);
2889 PUSH_INSN1(ret, location, putobject, symbol);
2890 PUSH_SEND(ret, location, rb_intern("key?"), INT2FIX(1));
2891
2892 if (in_single_pattern) {
2893 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2894
2895 PUSH_INSN(ret, location, dup);
2896 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2897
2898 {
2899 VALUE operand = rb_str_freeze(rb_sprintf("key not found: %+"PRIsVALUE, symbol));
2900 PUSH_INSN1(ret, location, putobject, operand);
2901 }
2902
2903 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 2));
2904 PUSH_INSN1(ret, location, putobject, Qtrue);
2905 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 3));
2906 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2907 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE + 4));
2908 PUSH_INSN1(ret, location, putobject, symbol);
2909 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY + 5));
2910
2911 PUSH_INSN1(ret, location, adjuststack, INT2FIX(4));
2912 PUSH_LABEL(ret, match_succeeded_label);
2913 }
2914
2915 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2916 PUSH_INSN(match_values, location, dup);
2917 PUSH_INSN1(match_values, location, putobject, symbol);
2918 PUSH_SEND(match_values, location, has_rest ? rb_intern("delete") : idAREF, INT2FIX(1));
2919
2920 const pm_node_t *value = assoc->value;
2921 if (PM_NODE_TYPE_P(value, PM_IMPLICIT_NODE)) {
2922 value = ((const pm_implicit_node_t *) value)->value;
2923 }
2924
2925 CHECK(pm_compile_pattern_match(iseq, scope_node, value, match_values, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
2926 }
2927
2928 PUSH_SEQ(ret, match_values);
2929 }
2930 else {
2931 PUSH_INSN(ret, location, dup);
2932 PUSH_SEND(ret, location, idEmptyP, INT2FIX(0));
2933 if (in_single_pattern) {
2934 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p is not empty"), base_index + 1));
2935 }
2936 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2937 }
2938
2939 if (has_rest) {
2940 switch (PM_NODE_TYPE(cast->rest)) {
2941 case PM_NO_KEYWORDS_PARAMETER_NODE: {
2942 PUSH_INSN(ret, location, dup);
2943 PUSH_SEND(ret, location, idEmptyP, INT2FIX(0));
2944 if (in_single_pattern) {
2945 pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("rest of %p is not empty"), base_index + 1);
2946 }
2947 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2948 break;
2949 }
2950 case PM_ASSOC_SPLAT_NODE: {
2951 const pm_assoc_splat_node_t *splat = (const pm_assoc_splat_node_t *) cast->rest;
2952 PUSH_INSN(ret, location, dup);
2953 pm_compile_pattern_match(iseq, scope_node, splat->value, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1);
2954 break;
2955 }
2956 default:
2957 rb_bug("unreachable");
2958 break;
2959 }
2960 }
2961
2962 PUSH_INSN(ret, location, pop);
2963 PUSH_INSNL(ret, location, jump, matched_label);
2964 PUSH_INSN(ret, location, putnil);
2965
2966 PUSH_LABEL(ret, type_error_label);
2967 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2968 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2969
2970 {
2971 VALUE operand = rb_fstring_lit("deconstruct_keys must return Hash");
2972 PUSH_INSN1(ret, location, putobject, operand);
2973 }
2974
2975 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2976 PUSH_INSN(ret, location, pop);
2977
2978 PUSH_LABEL(ret, match_failed_label);
2979 PUSH_INSN(ret, location, pop);
2980 PUSH_INSNL(ret, location, jump, unmatched_label);
2981 break;
2982 }
2983 case PM_CAPTURE_PATTERN_NODE: {
2984 // Capture patterns allow you to pattern match against an element in a
2985 // pattern and also capture the value into a local variable. This looks
2986 // like:
2987 //
2988 // [1] => [Integer => foo]
2989 //
2990 // In this case the `Integer => foo` will be represented by a
2991 // CapturePatternNode, which has both a value (the pattern to match
2992 // against) and a target (the place to write the variable into).
2993 const pm_capture_pattern_node_t *cast = (const pm_capture_pattern_node_t *) node;
2994
2995 LABEL *match_failed_label = NEW_LABEL(location.line);
2996
2997 PUSH_INSN(ret, location, dup);
2998 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));
2999 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));
3000 PUSH_INSN(ret, location, putnil);
3001
3002 PUSH_LABEL(ret, match_failed_label);
3003 PUSH_INSN(ret, location, pop);
3004 PUSH_INSNL(ret, location, jump, unmatched_label);
3005
3006 break;
3007 }
3008 case PM_LOCAL_VARIABLE_TARGET_NODE: {
3009 // Local variables can be targeted by placing identifiers in the place
3010 // of a pattern. For example, foo in bar. This results in the value
3011 // being matched being written to that local variable.
3013 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
3014
3015 // If this local variable is being written from within an alternation
3016 // pattern, then it cannot actually be added to the local table since
3017 // it's ambiguous which value should be used. So instead we indicate
3018 // this with a compile error.
3019 if (in_alternation_pattern) {
3020 ID id = pm_constant_id_lookup(scope_node, cast->name);
3021 const char *name = rb_id2name(id);
3022
3023 if (name && strlen(name) > 0 && name[0] != '_') {
3024 COMPILE_ERROR(iseq, location.line, "illegal variable in alternative pattern (%"PRIsVALUE")", rb_id2str(id));
3025 return COMPILE_NG;
3026 }
3027 }
3028
3029 PUSH_SETLOCAL(ret, location, index.index, index.level);
3030 PUSH_INSNL(ret, location, jump, matched_label);
3031 break;
3032 }
3033 case PM_ALTERNATION_PATTERN_NODE: {
3034 // Alternation patterns allow you to specify multiple patterns in a
3035 // single expression using the | operator.
3037
3038 LABEL *matched_left_label = NEW_LABEL(location.line);
3039 LABEL *unmatched_left_label = NEW_LABEL(location.line);
3040
3041 // First, we're going to attempt to match against the left pattern. If
3042 // that pattern matches, then we'll skip matching the right pattern.
3043 PUSH_INSN(ret, location, dup);
3044 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));
3045
3046 // If we get here, then we matched on the left pattern. In this case we
3047 // should pop out the duplicate value that we preemptively added to
3048 // match against the right pattern and then jump to the match label.
3049 PUSH_LABEL(ret, matched_left_label);
3050 PUSH_INSN(ret, location, pop);
3051 PUSH_INSNL(ret, location, jump, matched_label);
3052 PUSH_INSN(ret, location, putnil);
3053
3054 // If we get here, then we didn't match on the left pattern. In this
3055 // case we attempt to match against the right pattern.
3056 PUSH_LABEL(ret, unmatched_left_label);
3057 CHECK(pm_compile_pattern(iseq, scope_node, cast->right, ret, matched_label, unmatched_label, in_single_pattern, true, use_deconstructed_cache, base_index));
3058 break;
3059 }
3060 case PM_PARENTHESES_NODE:
3061 // Parentheses are allowed to wrap expressions in pattern matching and
3062 // they do nothing since they can only wrap individual expressions and
3063 // not groups. In this case we'll recurse back into this same function
3064 // with the body of the parentheses.
3065 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);
3066 case PM_PINNED_EXPRESSION_NODE:
3067 // Pinned expressions are a way to match against the value of an
3068 // expression that should be evaluated at runtime. This looks like:
3069 // foo in ^(bar). To compile these, we compile the expression as if it
3070 // were a literal value by falling through to the literal case.
3071 node = ((const pm_pinned_expression_node_t *) node)->expression;
3072 /* fallthrough */
3073 case PM_ARRAY_NODE:
3074 case PM_CLASS_VARIABLE_READ_NODE:
3075 case PM_CONSTANT_PATH_NODE:
3076 case PM_CONSTANT_READ_NODE:
3077 case PM_FALSE_NODE:
3078 case PM_FLOAT_NODE:
3079 case PM_GLOBAL_VARIABLE_READ_NODE:
3080 case PM_IMAGINARY_NODE:
3081 case PM_INSTANCE_VARIABLE_READ_NODE:
3082 case PM_IT_LOCAL_VARIABLE_READ_NODE:
3083 case PM_INTEGER_NODE:
3084 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
3085 case PM_INTERPOLATED_STRING_NODE:
3086 case PM_INTERPOLATED_SYMBOL_NODE:
3087 case PM_INTERPOLATED_X_STRING_NODE:
3088 case PM_LAMBDA_NODE:
3089 case PM_LOCAL_VARIABLE_READ_NODE:
3090 case PM_NIL_NODE:
3091 case PM_SOURCE_ENCODING_NODE:
3092 case PM_SOURCE_FILE_NODE:
3093 case PM_SOURCE_LINE_NODE:
3094 case PM_RANGE_NODE:
3095 case PM_RATIONAL_NODE:
3096 case PM_REGULAR_EXPRESSION_NODE:
3097 case PM_SELF_NODE:
3098 case PM_STRING_NODE:
3099 case PM_SYMBOL_NODE:
3100 case PM_TRUE_NODE:
3101 case PM_X_STRING_NODE: {
3102 // These nodes are all simple patterns, which means we'll use the
3103 // checkmatch instruction to match against them, which is effectively a
3104 // VM-level === operator.
3105 PM_COMPILE_NOT_POPPED(node);
3106 if (in_single_pattern) {
3107 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
3108 }
3109
3110 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
3111
3112 if (in_single_pattern) {
3113 pm_compile_pattern_eqq_error(iseq, scope_node, node, ret, base_index + 2);
3114 }
3115
3116 PUSH_INSNL(ret, location, branchif, matched_label);
3117 PUSH_INSNL(ret, location, jump, unmatched_label);
3118 break;
3119 }
3120 case PM_PINNED_VARIABLE_NODE: {
3121 // Pinned variables are a way to match against the value of a variable
3122 // without it looking like you're trying to write to the variable. This
3123 // looks like: foo in ^@bar. To compile these, we compile the variable
3124 // that they hold.
3125 const pm_pinned_variable_node_t *cast = (const pm_pinned_variable_node_t *) node;
3126 CHECK(pm_compile_pattern(iseq, scope_node, cast->variable, ret, matched_label, unmatched_label, in_single_pattern, in_alternation_pattern, true, base_index));
3127 break;
3128 }
3129 case PM_IF_NODE:
3130 case PM_UNLESS_NODE: {
3131 // If and unless nodes can show up here as guards on `in` clauses. This
3132 // looks like:
3133 //
3134 // case foo
3135 // in bar if baz?
3136 // qux
3137 // end
3138 //
3139 // Because we know they're in the modifier form and they can't have any
3140 // variation on this pattern, we compile them differently (more simply)
3141 // here than we would in the normal compilation path.
3142 const pm_node_t *predicate;
3143 const pm_node_t *statement;
3144
3145 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3146 const pm_if_node_t *cast = (const pm_if_node_t *) node;
3147 predicate = cast->predicate;
3148
3149 RUBY_ASSERT(cast->statements != NULL && cast->statements->body.size == 1);
3150 statement = cast->statements->body.nodes[0];
3151 }
3152 else {
3153 const pm_unless_node_t *cast = (const pm_unless_node_t *) node;
3154 predicate = cast->predicate;
3155
3156 RUBY_ASSERT(cast->statements != NULL && cast->statements->body.size == 1);
3157 statement = cast->statements->body.nodes[0];
3158 }
3159
3160 CHECK(pm_compile_pattern_match(iseq, scope_node, statement, ret, unmatched_label, in_single_pattern, in_alternation_pattern, use_deconstructed_cache, base_index));
3161 PM_COMPILE_NOT_POPPED(predicate);
3162
3163 if (in_single_pattern) {
3164 LABEL *match_succeeded_label = NEW_LABEL(location.line);
3165
3166 PUSH_INSN(ret, location, dup);
3167 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3168 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
3169 }
3170 else {
3171 PUSH_INSNL(ret, location, branchunless, match_succeeded_label);
3172 }
3173
3174 {
3175 VALUE operand = rb_fstring_lit("guard clause does not return true");
3176 PUSH_INSN1(ret, location, putobject, operand);
3177 }
3178
3179 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
3180 PUSH_INSN1(ret, location, putobject, Qfalse);
3181 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
3182
3183 PUSH_INSN(ret, location, pop);
3184 PUSH_INSN(ret, location, pop);
3185
3186 PUSH_LABEL(ret, match_succeeded_label);
3187 }
3188
3189 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
3190 PUSH_INSNL(ret, location, branchunless, unmatched_label);
3191 }
3192 else {
3193 PUSH_INSNL(ret, location, branchif, unmatched_label);
3194 }
3195
3196 PUSH_INSNL(ret, location, jump, matched_label);
3197 break;
3198 }
3199 default:
3200 // If we get here, then we have a node type that should not be in this
3201 // position. This would be a bug in the parser, because a different node
3202 // type should never have been created in this position in the tree.
3203 rb_bug("Unexpected node type in pattern matching expression: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
3204 break;
3205 }
3206
3207 return COMPILE_OK;
3208}
3209
3210#undef PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE
3211#undef PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING
3212#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P
3213#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE
3214#undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY
3215
3216// Generate a scope node from the given node.
3217void
3218pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_t *previous)
3219{
3220 // This is very important, otherwise the scope node could be seen as having
3221 // certain flags set that _should not_ be set.
3222 memset(scope, 0, sizeof(pm_scope_node_t));
3223
3224 scope->base.type = PM_SCOPE_NODE;
3225 scope->base.location.start = node->location.start;
3226 scope->base.location.end = node->location.end;
3227
3228 scope->previous = previous;
3229 scope->ast_node = (pm_node_t *) node;
3230
3231 if (previous) {
3232 scope->parser = previous->parser;
3233 scope->encoding = previous->encoding;
3234 scope->filepath_encoding = previous->filepath_encoding;
3235 scope->constants = previous->constants;
3236 scope->coverage_enabled = previous->coverage_enabled;
3237 scope->script_lines = previous->script_lines;
3238 }
3239
3240 switch (PM_NODE_TYPE(node)) {
3241 case PM_BLOCK_NODE: {
3242 const pm_block_node_t *cast = (const pm_block_node_t *) node;
3243 scope->body = cast->body;
3244 scope->locals = cast->locals;
3245 scope->parameters = cast->parameters;
3246 break;
3247 }
3248 case PM_CLASS_NODE: {
3249 const pm_class_node_t *cast = (const pm_class_node_t *) node;
3250 scope->body = cast->body;
3251 scope->locals = cast->locals;
3252 break;
3253 }
3254 case PM_DEF_NODE: {
3255 const pm_def_node_t *cast = (const pm_def_node_t *) node;
3256 scope->parameters = (pm_node_t *) cast->parameters;
3257 scope->body = cast->body;
3258 scope->locals = cast->locals;
3259 break;
3260 }
3261 case PM_ENSURE_NODE: {
3262 const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node;
3263 scope->body = (pm_node_t *) node;
3264
3265 if (cast->statements != NULL) {
3266 scope->base.location.start = cast->statements->base.location.start;
3267 scope->base.location.end = cast->statements->base.location.end;
3268 }
3269
3270 break;
3271 }
3272 case PM_FOR_NODE: {
3273 const pm_for_node_t *cast = (const pm_for_node_t *) node;
3274 scope->body = (pm_node_t *) cast->statements;
3275 break;
3276 }
3277 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
3278 RUBY_ASSERT(node->flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE);
3279 scope->body = (pm_node_t *) node;
3280 break;
3281 }
3282 case PM_LAMBDA_NODE: {
3283 const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node;
3284 scope->parameters = cast->parameters;
3285 scope->body = cast->body;
3286 scope->locals = cast->locals;
3287
3288 if (cast->parameters != NULL) {
3289 scope->base.location.start = cast->parameters->location.start;
3290 }
3291 else {
3292 scope->base.location.start = cast->operator_loc.end;
3293 }
3294 break;
3295 }
3296 case PM_MODULE_NODE: {
3297 const pm_module_node_t *cast = (const pm_module_node_t *) node;
3298 scope->body = cast->body;
3299 scope->locals = cast->locals;
3300 break;
3301 }
3302 case PM_POST_EXECUTION_NODE: {
3303 const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) node;
3304 scope->body = (pm_node_t *) cast->statements;
3305 break;
3306 }
3307 case PM_PROGRAM_NODE: {
3308 const pm_program_node_t *cast = (const pm_program_node_t *) node;
3309 scope->body = (pm_node_t *) cast->statements;
3310 scope->locals = cast->locals;
3311 break;
3312 }
3313 case PM_RESCUE_NODE: {
3314 const pm_rescue_node_t *cast = (const pm_rescue_node_t *) node;
3315 scope->body = (pm_node_t *) cast->statements;
3316 break;
3317 }
3318 case PM_RESCUE_MODIFIER_NODE: {
3319 const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node;
3320 scope->body = (pm_node_t *) cast->rescue_expression;
3321 break;
3322 }
3323 case PM_SINGLETON_CLASS_NODE: {
3324 const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node;
3325 scope->body = cast->body;
3326 scope->locals = cast->locals;
3327 break;
3328 }
3329 case PM_STATEMENTS_NODE: {
3330 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
3331 scope->body = (pm_node_t *) cast;
3332 break;
3333 }
3334 default:
3335 rb_bug("unreachable");
3336 break;
3337 }
3338}
3339
3340void
3341pm_scope_node_destroy(pm_scope_node_t *scope_node)
3342{
3343 if (scope_node->index_lookup_table) {
3344 st_free_table(scope_node->index_lookup_table);
3345 }
3346}
3347
3359static void
3360pm_compile_retry_end_label(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *retry_end_l)
3361{
3362 INSN *iobj;
3363 LINK_ELEMENT *last_elem = LAST_ELEMENT(ret);
3364 iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem);
3365 while (!IS_INSN_ID(iobj, send) && !IS_INSN_ID(iobj, invokesuper) && !IS_INSN_ID(iobj, sendforward) && !IS_INSN_ID(iobj, invokesuperforward)) {
3366 iobj = (INSN*) get_prev_insn(iobj);
3367 }
3368 ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l);
3369
3370 // LINK_ANCHOR has a pointer to the last element, but
3371 // ELEM_INSERT_NEXT does not update it even if we add an insn to the
3372 // last of LINK_ANCHOR. So this updates it manually.
3373 if (&iobj->link == LAST_ELEMENT(ret)) {
3374 ret->last = (LINK_ELEMENT*) retry_end_l;
3375 }
3376}
3377
3378static const char *
3379pm_iseq_builtin_function_name(const pm_scope_node_t *scope_node, const pm_node_t *receiver, ID method_id)
3380{
3381 const char *name = rb_id2name(method_id);
3382 static const char prefix[] = "__builtin_";
3383 const size_t prefix_len = sizeof(prefix) - 1;
3384
3385 if (receiver == NULL) {
3386 if (UNLIKELY(strncmp(prefix, name, prefix_len) == 0)) {
3387 // __builtin_foo
3388 return &name[prefix_len];
3389 }
3390 }
3391 else if (PM_NODE_TYPE_P(receiver, PM_CALL_NODE)) {
3392 if (PM_NODE_FLAG_P(receiver, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
3393 const pm_call_node_t *cast = (const pm_call_node_t *) receiver;
3394 if (pm_constant_id_lookup(scope_node, cast->name) == rb_intern_const("__builtin")) {
3395 // __builtin.foo
3396 return name;
3397 }
3398 }
3399 }
3400 else if (PM_NODE_TYPE_P(receiver, PM_CONSTANT_READ_NODE)) {
3401 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) receiver;
3402 if (pm_constant_id_lookup(scope_node, cast->name) == rb_intern_const("Primitive")) {
3403 // Primitive.foo
3404 return name;
3405 }
3406 }
3407
3408 return NULL;
3409}
3410
3411// Compile Primitive.attr! :leaf, ...
3412static int
3413pm_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)
3414{
3415 if (arguments == NULL) {
3416 COMPILE_ERROR(iseq, node_location->line, "attr!: no argument");
3417 return COMPILE_NG;
3418 }
3419
3420 const pm_node_t *argument;
3421 PM_NODE_LIST_FOREACH(&arguments->arguments, index, argument) {
3422 if (!PM_NODE_TYPE_P(argument, PM_SYMBOL_NODE)) {
3423 COMPILE_ERROR(iseq, node_location->line, "non symbol argument to attr!: %s", pm_node_type_to_str(PM_NODE_TYPE(argument)));
3424 return COMPILE_NG;
3425 }
3426
3427 VALUE symbol = pm_static_literal_value(iseq, argument, scope_node);
3428 VALUE string = rb_sym2str(symbol);
3429
3430 if (strcmp(RSTRING_PTR(string), "leaf") == 0) {
3431 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_LEAF;
3432 }
3433 else if (strcmp(RSTRING_PTR(string), "inline_block") == 0) {
3434 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_INLINE_BLOCK;
3435 }
3436 else if (strcmp(RSTRING_PTR(string), "use_block") == 0) {
3437 iseq_set_use_block(iseq);
3438 }
3439 else if (strcmp(RSTRING_PTR(string), "c_trace") == 0) {
3440 // Let the iseq act like a C method in backtraces
3441 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_C_TRACE;
3442 }
3443 else {
3444 COMPILE_ERROR(iseq, node_location->line, "unknown argument to attr!: %s", RSTRING_PTR(string));
3445 return COMPILE_NG;
3446 }
3447 }
3448
3449 return COMPILE_OK;
3450}
3451
3452static int
3453pm_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)
3454{
3455 if (arguments == NULL) {
3456 COMPILE_ERROR(iseq, node_location->line, "arg!: no argument");
3457 return COMPILE_NG;
3458 }
3459
3460 if (arguments->arguments.size != 1) {
3461 COMPILE_ERROR(iseq, node_location->line, "arg!: too many argument");
3462 return COMPILE_NG;
3463 }
3464
3465 const pm_node_t *argument = arguments->arguments.nodes[0];
3466 if (!PM_NODE_TYPE_P(argument, PM_SYMBOL_NODE)) {
3467 COMPILE_ERROR(iseq, node_location->line, "non symbol argument to arg!: %s", pm_node_type_to_str(PM_NODE_TYPE(argument)));
3468 return COMPILE_NG;
3469 }
3470
3471 if (!popped) {
3472 ID name = parse_string_symbol(scope_node, ((const pm_symbol_node_t *) argument));
3473 int index = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size - get_local_var_idx(iseq, name);
3474
3475 debugs("id: %s idx: %d\n", rb_id2name(name), index);
3476 PUSH_GETLOCAL(ret, *node_location, index, get_lvar_level(iseq));
3477 }
3478
3479 return COMPILE_OK;
3480}
3481
3482static int
3483pm_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)
3484{
3485 const pm_node_t *ast_node = scope_node->ast_node;
3486 if (!PM_NODE_TYPE_P(ast_node, PM_DEF_NODE)) {
3487 rb_bug("mandatory_only?: not in method definition");
3488 return COMPILE_NG;
3489 }
3490
3491 const pm_def_node_t *def_node = (const pm_def_node_t *) ast_node;
3492 const pm_parameters_node_t *parameters_node = def_node->parameters;
3493 if (parameters_node == NULL) {
3494 rb_bug("mandatory_only?: in method definition with no parameters");
3495 return COMPILE_NG;
3496 }
3497
3498 const pm_node_t *body_node = def_node->body;
3499 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)) {
3500 rb_bug("mandatory_only?: not in method definition with plain statements");
3501 return COMPILE_NG;
3502 }
3503
3504 const pm_if_node_t *if_node = (const pm_if_node_t *) ((const pm_statements_node_t *) body_node)->body.nodes[0];
3505 if (if_node->predicate != ((const pm_node_t *) call_node)) {
3506 rb_bug("mandatory_only?: can't find mandatory node");
3507 return COMPILE_NG;
3508 }
3509
3510 pm_parameters_node_t parameters = {
3511 .base = parameters_node->base,
3512 .requireds = parameters_node->requireds
3513 };
3514
3515 const pm_def_node_t def = {
3516 .base = def_node->base,
3517 .name = def_node->name,
3518 .receiver = def_node->receiver,
3519 .parameters = &parameters,
3520 .body = (pm_node_t *) if_node->statements,
3521 .locals = {
3522 .ids = def_node->locals.ids,
3523 .size = parameters_node->requireds.size,
3524 .capacity = def_node->locals.capacity
3525 }
3526 };
3527
3528 pm_scope_node_t next_scope_node;
3529 pm_scope_node_init(&def.base, &next_scope_node, scope_node);
3530
3531 int error_state;
3532 const rb_iseq_t *mandatory_only_iseq = pm_iseq_new_with_opt(
3533 &next_scope_node,
3534 rb_iseq_base_label(iseq),
3535 rb_iseq_path(iseq),
3536 rb_iseq_realpath(iseq),
3537 node_location->line,
3538 NULL,
3539 0,
3540 ISEQ_TYPE_METHOD,
3541 ISEQ_COMPILE_DATA(iseq)->option,
3542 &error_state
3543 );
3544 RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->mandatory_only_iseq, (VALUE)mandatory_only_iseq);
3545
3546 if (error_state) {
3547 RUBY_ASSERT(ISEQ_BODY(iseq)->mandatory_only_iseq == NULL);
3548 rb_jump_tag(error_state);
3549 }
3550
3551 pm_scope_node_destroy(&next_scope_node);
3552 return COMPILE_OK;
3553}
3554
3555static int
3556pm_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)
3557{
3558 const pm_arguments_node_t *arguments = call_node->arguments;
3559
3560 if (parent_block != NULL) {
3561 COMPILE_ERROR(iseq, node_location->line, "should not call builtins here.");
3562 return COMPILE_NG;
3563 }
3564
3565#define BUILTIN_INLINE_PREFIX "_bi"
3566 char inline_func[sizeof(BUILTIN_INLINE_PREFIX) + DECIMAL_SIZE_OF(int)];
3567 bool cconst = false;
3568retry:;
3569 const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
3570
3571 if (bf == NULL) {
3572 if (strcmp("cstmt!", builtin_func) == 0 || strcmp("cexpr!", builtin_func) == 0) {
3573 // ok
3574 }
3575 else if (strcmp("cconst!", builtin_func) == 0) {
3576 cconst = true;
3577 }
3578 else if (strcmp("cinit!", builtin_func) == 0) {
3579 // ignore
3580 return COMPILE_OK;
3581 }
3582 else if (strcmp("attr!", builtin_func) == 0) {
3583 return pm_compile_builtin_attr(iseq, scope_node, arguments, node_location);
3584 }
3585 else if (strcmp("arg!", builtin_func) == 0) {
3586 return pm_compile_builtin_arg(iseq, ret, scope_node, arguments, node_location, popped);
3587 }
3588 else if (strcmp("mandatory_only?", builtin_func) == 0) {
3589 if (popped) {
3590 rb_bug("mandatory_only? should be in if condition");
3591 }
3592 else if (!LIST_INSN_SIZE_ZERO(ret)) {
3593 rb_bug("mandatory_only? should be put on top");
3594 }
3595
3596 PUSH_INSN1(ret, *node_location, putobject, Qfalse);
3597 return pm_compile_builtin_mandatory_only_method(iseq, scope_node, call_node, node_location);
3598 }
3599 else if (1) {
3600 rb_bug("can't find builtin function:%s", builtin_func);
3601 }
3602 else {
3603 COMPILE_ERROR(iseq, node_location->line, "can't find builtin function:%s", builtin_func);
3604 return COMPILE_NG;
3605 }
3606
3607 int inline_index = node_location->line;
3608 snprintf(inline_func, sizeof(inline_func), BUILTIN_INLINE_PREFIX "%d", inline_index);
3609 builtin_func = inline_func;
3610 arguments = NULL;
3611 goto retry;
3612 }
3613
3614 if (cconst) {
3615 typedef VALUE(*builtin_func0)(void *, VALUE);
3616 VALUE const_val = (*(builtin_func0)(uintptr_t)bf->func_ptr)(NULL, Qnil);
3617 PUSH_INSN1(ret, *node_location, putobject, const_val);
3618 return COMPILE_OK;
3619 }
3620
3621 // fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
3622
3623 DECL_ANCHOR(args_seq);
3624
3625 int flags = 0;
3626 struct rb_callinfo_kwarg *keywords = NULL;
3627 int argc = pm_setup_args(arguments, call_node->block, &flags, &keywords, iseq, args_seq, scope_node, node_location);
3628
3629 if (argc != bf->argc) {
3630 COMPILE_ERROR(iseq, node_location->line, "argc is not match for builtin function:%s (expect %d but %d)", builtin_func, bf->argc, argc);
3631 return COMPILE_NG;
3632 }
3633
3634 unsigned int start_index;
3635 if (delegate_call_p(iseq, argc, args_seq, &start_index)) {
3636 PUSH_INSN2(ret, *node_location, opt_invokebuiltin_delegate, bf, INT2FIX(start_index));
3637 }
3638 else {
3639 PUSH_SEQ(ret, args_seq);
3640 PUSH_INSN1(ret, *node_location, invokebuiltin, bf);
3641 }
3642
3643 if (popped) PUSH_INSN(ret, *node_location, pop);
3644 return COMPILE_OK;
3645}
3646
3650static void
3651pm_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)
3652{
3653 const pm_location_t *message_loc = &call_node->message_loc;
3654 if (message_loc->start == NULL) message_loc = &call_node->base.location;
3655
3656 const pm_node_location_t location = PM_LOCATION_START_LOCATION(scope_node->parser, message_loc, call_node->base.node_id);
3657
3658 LABEL *else_label = NEW_LABEL(location.line);
3659 LABEL *end_label = NEW_LABEL(location.line);
3660 LABEL *retry_end_l = NEW_LABEL(location.line);
3661
3662 VALUE branches = Qfalse;
3663 rb_code_location_t code_location = { 0 };
3664 int node_id = location.node_id;
3665
3666 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
3667 if (PM_BRANCH_COVERAGE_P(iseq)) {
3668 const uint8_t *cursors[3] = {
3669 call_node->closing_loc.end,
3670 call_node->arguments == NULL ? NULL : call_node->arguments->base.location.end,
3671 call_node->message_loc.end
3672 };
3673
3674 const uint8_t *end_cursor = cursors[0];
3675 end_cursor = (end_cursor == NULL || cursors[1] == NULL) ? cursors[1] : (end_cursor > cursors[1] ? end_cursor : cursors[1]);
3676 end_cursor = (end_cursor == NULL || cursors[2] == NULL) ? cursors[2] : (end_cursor > cursors[2] ? end_cursor : cursors[2]);
3677 if (!end_cursor) end_cursor = call_node->closing_loc.end;
3678
3679 const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, call_node);
3680 const pm_line_column_t end_location = pm_newline_list_line_column(&scope_node->parser->newline_list, end_cursor, scope_node->parser->start_line);
3681
3682 code_location = (rb_code_location_t) {
3683 .beg_pos = { .lineno = start_location.line, .column = start_location.column },
3684 .end_pos = { .lineno = end_location.line, .column = end_location.column }
3685 };
3686
3687 branches = decl_branch_base(iseq, PTR2NUM(call_node), &code_location, "&.");
3688 }
3689
3690 PUSH_INSN(ret, location, dup);
3691 PUSH_INSNL(ret, location, branchnil, else_label);
3692
3693 add_trace_branch_coverage(iseq, ret, &code_location, node_id, 0, "then", branches);
3694 }
3695
3696 LINK_ELEMENT *opt_new_prelude = LAST_ELEMENT(ret);
3697
3698 int flags = 0;
3699 struct rb_callinfo_kwarg *kw_arg = NULL;
3700
3701 int orig_argc = pm_setup_args(call_node->arguments, call_node->block, &flags, &kw_arg, iseq, ret, scope_node, &location);
3702 const rb_iseq_t *previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
3703 const rb_iseq_t *block_iseq = NULL;
3704
3705 if (call_node->block != NULL && PM_NODE_TYPE_P(call_node->block, PM_BLOCK_NODE)) {
3706 // Scope associated with the block
3707 pm_scope_node_t next_scope_node;
3708 pm_scope_node_init(call_node->block, &next_scope_node, scope_node);
3709
3710 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));
3711 pm_scope_node_destroy(&next_scope_node);
3712 ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
3713 }
3714 else {
3715 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
3716 flags |= VM_CALL_VCALL;
3717 }
3718
3719 if (!flags) {
3720 flags |= VM_CALL_ARGS_SIMPLE;
3721 }
3722 }
3723
3724 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) {
3725 flags |= VM_CALL_FCALL;
3726 }
3727
3728 if (!popped && PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) {
3729 if (flags & VM_CALL_ARGS_BLOCKARG) {
3730 PUSH_INSN1(ret, location, topn, INT2FIX(1));
3731 if (flags & VM_CALL_ARGS_SPLAT) {
3732 PUSH_INSN1(ret, location, putobject, INT2FIX(-1));
3733 PUSH_SEND_WITH_FLAG(ret, location, idAREF, INT2FIX(1), INT2FIX(0));
3734 }
3735 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 3));
3736 PUSH_INSN(ret, location, pop);
3737 }
3738 else if (flags & VM_CALL_ARGS_SPLAT) {
3739 PUSH_INSN(ret, location, dup);
3740 PUSH_INSN1(ret, location, putobject, INT2FIX(-1));
3741 PUSH_SEND_WITH_FLAG(ret, location, idAREF, INT2FIX(1), INT2FIX(0));
3742 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 2));
3743 PUSH_INSN(ret, location, pop);
3744 }
3745 else {
3746 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 1));
3747 }
3748 }
3749
3750 if ((flags & VM_CALL_KW_SPLAT) && (flags & VM_CALL_ARGS_BLOCKARG) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
3751 PUSH_INSN(ret, location, splatkw);
3752 }
3753
3754 LABEL *not_basic_new = NEW_LABEL(location.line);
3755 LABEL *not_basic_new_finish = NEW_LABEL(location.line);
3756
3757 bool inline_new = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction &&
3758 method_id == rb_intern("new") &&
3759 call_node->block == NULL &&
3760 (flags & VM_CALL_ARGS_BLOCKARG) == 0;
3761
3762 if (inline_new) {
3763 if (LAST_ELEMENT(ret) == opt_new_prelude) {
3764 PUSH_INSN(ret, location, putnil);
3765 PUSH_INSN(ret, location, swap);
3766 }
3767 else {
3768 ELEM_INSERT_NEXT(opt_new_prelude, &new_insn_body(iseq, location.line, location.node_id, BIN(swap), 0)->link);
3769 ELEM_INSERT_NEXT(opt_new_prelude, &new_insn_body(iseq, location.line, location.node_id, BIN(putnil), 0)->link);
3770 }
3771
3772 // Jump unless the receiver uses the "basic" implementation of "new"
3773 VALUE ci;
3774 if (flags & VM_CALL_FORWARDING) {
3775 ci = (VALUE)new_callinfo(iseq, method_id, orig_argc + 1, flags, kw_arg, 0);
3776 }
3777 else {
3778 ci = (VALUE)new_callinfo(iseq, method_id, orig_argc, flags, kw_arg, 0);
3779 }
3780
3781 PUSH_INSN2(ret, location, opt_new, ci, not_basic_new);
3782 LABEL_REF(not_basic_new);
3783 // optimized path
3784 PUSH_SEND_R(ret, location, rb_intern("initialize"), INT2FIX(orig_argc), block_iseq, INT2FIX(flags | VM_CALL_FCALL), kw_arg);
3785 PUSH_INSNL(ret, location, jump, not_basic_new_finish);
3786
3787 PUSH_LABEL(ret, not_basic_new);
3788 // Fall back to normal send
3789 PUSH_SEND_R(ret, location, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg);
3790 PUSH_INSN(ret, location, swap);
3791
3792 PUSH_LABEL(ret, not_basic_new_finish);
3793 PUSH_INSN(ret, location, pop);
3794 }
3795 else {
3796 PUSH_SEND_R(ret, location, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg);
3797 }
3798
3799 if (block_iseq && ISEQ_BODY(block_iseq)->catch_table) {
3800 pm_compile_retry_end_label(iseq, ret, retry_end_l);
3801 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, start, retry_end_l, block_iseq, retry_end_l);
3802 }
3803
3804 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
3805 PUSH_INSNL(ret, location, jump, end_label);
3806 PUSH_LABEL(ret, else_label);
3807 add_trace_branch_coverage(iseq, ret, &code_location, node_id, 1, "else", branches);
3808 PUSH_LABEL(ret, end_label);
3809 }
3810
3811 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && !popped) {
3812 PUSH_INSN(ret, location, pop);
3813 }
3814
3815 if (popped) PUSH_INSN(ret, location, pop);
3816 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
3817}
3818
3823static inline VALUE
3824pm_compile_back_reference_ref(const pm_back_reference_read_node_t *node)
3825{
3826 const char *type = (const char *) (node->base.location.start + 1);
3827
3828 // Since a back reference is `$<char>`, Ruby represents the ID as an
3829 // rb_intern on the value after the `$`.
3830 return INT2FIX(rb_intern2(type, 1)) << 1 | 1;
3831}
3832
3837static inline VALUE
3838pm_compile_numbered_reference_ref(const pm_numbered_reference_read_node_t *node)
3839{
3840 return INT2FIX(node->number << 1);
3841}
3842
3843static void
3844pm_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)
3845{
3846#define PUSH_VAL(type) (in_condition ? Qtrue : rb_iseq_defined_string(type))
3847
3848 // in_condition is the same as compile.c's needstr
3849 enum defined_type dtype = DEFINED_NOT_DEFINED;
3850 const pm_node_location_t location = *node_location;
3851
3852 switch (PM_NODE_TYPE(node)) {
3853/* DEFINED_NIL ****************************************************************/
3854 case PM_NIL_NODE:
3855 // defined?(nil)
3856 // ^^^
3857 dtype = DEFINED_NIL;
3858 break;
3859/* DEFINED_IVAR ***************************************************************/
3860 case PM_INSTANCE_VARIABLE_READ_NODE: {
3861 // defined?(@a)
3862 // ^^
3864 ID name = pm_constant_id_lookup(scope_node, cast->name);
3865
3866 PUSH_INSN3(ret, location, definedivar, ID2SYM(name), get_ivar_ic_value(iseq, name), PUSH_VAL(DEFINED_IVAR));
3867
3868 return;
3869 }
3870/* DEFINED_LVAR ***************************************************************/
3871 case PM_LOCAL_VARIABLE_READ_NODE:
3872 // a = 1; defined?(a)
3873 // ^
3874 case PM_IT_LOCAL_VARIABLE_READ_NODE:
3875 // 1.then { defined?(it) }
3876 // ^^
3877 dtype = DEFINED_LVAR;
3878 break;
3879/* DEFINED_GVAR ***************************************************************/
3880 case PM_GLOBAL_VARIABLE_READ_NODE: {
3881 // defined?($a)
3882 // ^^
3884 ID name = pm_constant_id_lookup(scope_node, cast->name);
3885
3886 PUSH_INSN(ret, location, putnil);
3887 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_GVAR), ID2SYM(name), PUSH_VAL(DEFINED_GVAR));
3888
3889 return;
3890 }
3891/* DEFINED_CVAR ***************************************************************/
3892 case PM_CLASS_VARIABLE_READ_NODE: {
3893 // defined?(@@a)
3894 // ^^^
3896 ID name = pm_constant_id_lookup(scope_node, cast->name);
3897
3898 PUSH_INSN(ret, location, putnil);
3899 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CVAR), ID2SYM(name), PUSH_VAL(DEFINED_CVAR));
3900
3901 return;
3902 }
3903/* DEFINED_CONST **************************************************************/
3904 case PM_CONSTANT_READ_NODE: {
3905 // defined?(A)
3906 // ^
3907 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
3908 ID name = pm_constant_id_lookup(scope_node, cast->name);
3909
3910 PUSH_INSN(ret, location, putnil);
3911 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), ID2SYM(name), PUSH_VAL(DEFINED_CONST));
3912
3913 return;
3914 }
3915/* DEFINED_YIELD **************************************************************/
3916 case PM_YIELD_NODE:
3917 // defined?(yield)
3918 // ^^^^^
3919 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
3920
3921 PUSH_INSN(ret, location, putnil);
3922 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_YIELD), 0, PUSH_VAL(DEFINED_YIELD));
3923
3924 return;
3925/* DEFINED_ZSUPER *************************************************************/
3926 case PM_SUPER_NODE: {
3927 // defined?(super 1, 2)
3928 // ^^^^^^^^^^
3929 const pm_super_node_t *cast = (const pm_super_node_t *) node;
3930
3931 if (cast->block != NULL && !PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE)) {
3932 dtype = DEFINED_EXPR;
3933 break;
3934 }
3935
3936 PUSH_INSN(ret, location, putnil);
3937 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_ZSUPER), 0, PUSH_VAL(DEFINED_ZSUPER));
3938 return;
3939 }
3940 case PM_FORWARDING_SUPER_NODE: {
3941 // defined?(super)
3942 // ^^^^^
3943 const pm_forwarding_super_node_t *cast = (const pm_forwarding_super_node_t *) node;
3944
3945 if (cast->block != NULL) {
3946 dtype = DEFINED_EXPR;
3947 break;
3948 }
3949
3950 PUSH_INSN(ret, location, putnil);
3951 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_ZSUPER), 0, PUSH_VAL(DEFINED_ZSUPER));
3952 return;
3953 }
3954/* DEFINED_SELF ***************************************************************/
3955 case PM_SELF_NODE:
3956 // defined?(self)
3957 // ^^^^
3958 dtype = DEFINED_SELF;
3959 break;
3960/* DEFINED_TRUE ***************************************************************/
3961 case PM_TRUE_NODE:
3962 // defined?(true)
3963 // ^^^^
3964 dtype = DEFINED_TRUE;
3965 break;
3966/* DEFINED_FALSE **************************************************************/
3967 case PM_FALSE_NODE:
3968 // defined?(false)
3969 // ^^^^^
3970 dtype = DEFINED_FALSE;
3971 break;
3972/* DEFINED_ASGN ***************************************************************/
3973 case PM_CALL_AND_WRITE_NODE:
3974 // defined?(a.a &&= 1)
3975 // ^^^^^^^^^
3976 case PM_CALL_OPERATOR_WRITE_NODE:
3977 // defined?(a.a += 1)
3978 // ^^^^^^^^
3979 case PM_CALL_OR_WRITE_NODE:
3980 // defined?(a.a ||= 1)
3981 // ^^^^^^^^^
3982 case PM_CLASS_VARIABLE_AND_WRITE_NODE:
3983 // defined?(@@a &&= 1)
3984 // ^^^^^^^^^
3985 case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE:
3986 // defined?(@@a += 1)
3987 // ^^^^^^^^
3988 case PM_CLASS_VARIABLE_OR_WRITE_NODE:
3989 // defined?(@@a ||= 1)
3990 // ^^^^^^^^^
3991 case PM_CLASS_VARIABLE_WRITE_NODE:
3992 // defined?(@@a = 1)
3993 // ^^^^^^^
3994 case PM_CONSTANT_AND_WRITE_NODE:
3995 // defined?(A &&= 1)
3996 // ^^^^^^^
3997 case PM_CONSTANT_OPERATOR_WRITE_NODE:
3998 // defined?(A += 1)
3999 // ^^^^^^
4000 case PM_CONSTANT_OR_WRITE_NODE:
4001 // defined?(A ||= 1)
4002 // ^^^^^^^
4003 case PM_CONSTANT_PATH_AND_WRITE_NODE:
4004 // defined?(A::A &&= 1)
4005 // ^^^^^^^^^^
4006 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE:
4007 // defined?(A::A += 1)
4008 // ^^^^^^^^^
4009 case PM_CONSTANT_PATH_OR_WRITE_NODE:
4010 // defined?(A::A ||= 1)
4011 // ^^^^^^^^^^
4012 case PM_CONSTANT_PATH_WRITE_NODE:
4013 // defined?(A::A = 1)
4014 // ^^^^^^^^
4015 case PM_CONSTANT_WRITE_NODE:
4016 // defined?(A = 1)
4017 // ^^^^^
4018 case PM_GLOBAL_VARIABLE_AND_WRITE_NODE:
4019 // defined?($a &&= 1)
4020 // ^^^^^^^^
4021 case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE:
4022 // defined?($a += 1)
4023 // ^^^^^^^
4024 case PM_GLOBAL_VARIABLE_OR_WRITE_NODE:
4025 // defined?($a ||= 1)
4026 // ^^^^^^^^
4027 case PM_GLOBAL_VARIABLE_WRITE_NODE:
4028 // defined?($a = 1)
4029 // ^^^^^^
4030 case PM_INDEX_AND_WRITE_NODE:
4031 // defined?(a[1] &&= 1)
4032 // ^^^^^^^^^^
4033 case PM_INDEX_OPERATOR_WRITE_NODE:
4034 // defined?(a[1] += 1)
4035 // ^^^^^^^^^
4036 case PM_INDEX_OR_WRITE_NODE:
4037 // defined?(a[1] ||= 1)
4038 // ^^^^^^^^^^
4039 case PM_INSTANCE_VARIABLE_AND_WRITE_NODE:
4040 // defined?(@a &&= 1)
4041 // ^^^^^^^^
4042 case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE:
4043 // defined?(@a += 1)
4044 // ^^^^^^^
4045 case PM_INSTANCE_VARIABLE_OR_WRITE_NODE:
4046 // defined?(@a ||= 1)
4047 // ^^^^^^^^
4048 case PM_INSTANCE_VARIABLE_WRITE_NODE:
4049 // defined?(@a = 1)
4050 // ^^^^^^
4051 case PM_LOCAL_VARIABLE_AND_WRITE_NODE:
4052 // defined?(a &&= 1)
4053 // ^^^^^^^
4054 case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE:
4055 // defined?(a += 1)
4056 // ^^^^^^
4057 case PM_LOCAL_VARIABLE_OR_WRITE_NODE:
4058 // defined?(a ||= 1)
4059 // ^^^^^^^
4060 case PM_LOCAL_VARIABLE_WRITE_NODE:
4061 // defined?(a = 1)
4062 // ^^^^^
4063 case PM_MULTI_WRITE_NODE:
4064 // defined?((a, = 1))
4065 // ^^^^^^
4066 dtype = DEFINED_ASGN;
4067 break;
4068/* DEFINED_EXPR ***************************************************************/
4069 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
4070 // defined?((alias $a $b))
4071 // ^^^^^^^^^^^
4072 case PM_ALIAS_METHOD_NODE:
4073 // defined?((alias a b))
4074 // ^^^^^^^^^
4075 case PM_AND_NODE:
4076 // defined?(a and b)
4077 // ^^^^^^^
4078 case PM_BREAK_NODE:
4079 // defined?(break 1)
4080 // ^^^^^^^
4081 case PM_CASE_MATCH_NODE:
4082 // defined?(case 1; in 1; end)
4083 // ^^^^^^^^^^^^^^^^^
4084 case PM_CASE_NODE:
4085 // defined?(case 1; when 1; end)
4086 // ^^^^^^^^^^^^^^^^^^^
4087 case PM_CLASS_NODE:
4088 // defined?(class Foo; end)
4089 // ^^^^^^^^^^^^^^
4090 case PM_DEF_NODE:
4091 // defined?(def a() end)
4092 // ^^^^^^^^^^^
4093 case PM_DEFINED_NODE:
4094 // defined?(defined?(a))
4095 // ^^^^^^^^^^^
4096 case PM_FLIP_FLOP_NODE:
4097 // defined?(not (a .. b))
4098 // ^^^^^^
4099 case PM_FLOAT_NODE:
4100 // defined?(1.0)
4101 // ^^^
4102 case PM_FOR_NODE:
4103 // defined?(for a in 1 do end)
4104 // ^^^^^^^^^^^^^^^^^
4105 case PM_IF_NODE:
4106 // defined?(if a then end)
4107 // ^^^^^^^^^^^^^
4108 case PM_IMAGINARY_NODE:
4109 // defined?(1i)
4110 // ^^
4111 case PM_INTEGER_NODE:
4112 // defined?(1)
4113 // ^
4114 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE:
4115 // defined?(not /#{1}/)
4116 // ^^^^^^
4117 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
4118 // defined?(/#{1}/)
4119 // ^^^^^^
4120 case PM_INTERPOLATED_STRING_NODE:
4121 // defined?("#{1}")
4122 // ^^^^^^
4123 case PM_INTERPOLATED_SYMBOL_NODE:
4124 // defined?(:"#{1}")
4125 // ^^^^^^^
4126 case PM_INTERPOLATED_X_STRING_NODE:
4127 // defined?(`#{1}`)
4128 // ^^^^^^
4129 case PM_LAMBDA_NODE:
4130 // defined?(-> {})
4131 // ^^^^^
4132 case PM_MATCH_LAST_LINE_NODE:
4133 // defined?(not //)
4134 // ^^^^^^
4135 case PM_MATCH_PREDICATE_NODE:
4136 // defined?(1 in 1)
4137 // ^^^^^^
4138 case PM_MATCH_REQUIRED_NODE:
4139 // defined?(1 => 1)
4140 // ^^^^^^
4141 case PM_MATCH_WRITE_NODE:
4142 // defined?(/(?<a>)/ =~ "")
4143 // ^^^^^^^^^^^^^^
4144 case PM_MODULE_NODE:
4145 // defined?(module A end)
4146 // ^^^^^^^^^^^^
4147 case PM_NEXT_NODE:
4148 // defined?(next 1)
4149 // ^^^^^^
4150 case PM_OR_NODE:
4151 // defined?(a or b)
4152 // ^^^^^^
4153 case PM_POST_EXECUTION_NODE:
4154 // defined?((END {}))
4155 // ^^^^^^^^
4156 case PM_RANGE_NODE:
4157 // defined?(1..1)
4158 // ^^^^
4159 case PM_RATIONAL_NODE:
4160 // defined?(1r)
4161 // ^^
4162 case PM_REDO_NODE:
4163 // defined?(redo)
4164 // ^^^^
4165 case PM_REGULAR_EXPRESSION_NODE:
4166 // defined?(//)
4167 // ^^
4168 case PM_RESCUE_MODIFIER_NODE:
4169 // defined?(a rescue b)
4170 // ^^^^^^^^^^
4171 case PM_RETRY_NODE:
4172 // defined?(retry)
4173 // ^^^^^
4174 case PM_RETURN_NODE:
4175 // defined?(return)
4176 // ^^^^^^
4177 case PM_SINGLETON_CLASS_NODE:
4178 // defined?(class << self; end)
4179 // ^^^^^^^^^^^^^^^^^^
4180 case PM_SOURCE_ENCODING_NODE:
4181 // defined?(__ENCODING__)
4182 // ^^^^^^^^^^^^
4183 case PM_SOURCE_FILE_NODE:
4184 // defined?(__FILE__)
4185 // ^^^^^^^^
4186 case PM_SOURCE_LINE_NODE:
4187 // defined?(__LINE__)
4188 // ^^^^^^^^
4189 case PM_STRING_NODE:
4190 // defined?("")
4191 // ^^
4192 case PM_SYMBOL_NODE:
4193 // defined?(:a)
4194 // ^^
4195 case PM_UNDEF_NODE:
4196 // defined?((undef a))
4197 // ^^^^^^^
4198 case PM_UNLESS_NODE:
4199 // defined?(unless a then end)
4200 // ^^^^^^^^^^^^^^^^^
4201 case PM_UNTIL_NODE:
4202 // defined?(until a do end)
4203 // ^^^^^^^^^^^^^^
4204 case PM_WHILE_NODE:
4205 // defined?(while a do end)
4206 // ^^^^^^^^^^^^^^
4207 case PM_X_STRING_NODE:
4208 // defined?(``)
4209 // ^^
4210 dtype = DEFINED_EXPR;
4211 break;
4212/* DEFINED_REF ****************************************************************/
4213 case PM_BACK_REFERENCE_READ_NODE: {
4214 // defined?($+)
4215 // ^^
4217 VALUE ref = pm_compile_back_reference_ref(cast);
4218
4219 PUSH_INSN(ret, location, putnil);
4220 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_REF), ref, PUSH_VAL(DEFINED_GVAR));
4221
4222 return;
4223 }
4224 case PM_NUMBERED_REFERENCE_READ_NODE: {
4225 // defined?($1)
4226 // ^^
4228 VALUE ref = pm_compile_numbered_reference_ref(cast);
4229
4230 PUSH_INSN(ret, location, putnil);
4231 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_REF), ref, PUSH_VAL(DEFINED_GVAR));
4232
4233 return;
4234 }
4235/* DEFINED_CONST_FROM *********************************************************/
4236 case PM_CONSTANT_PATH_NODE: {
4237 // defined?(A::A)
4238 // ^^^^
4239 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
4240 ID name = pm_constant_id_lookup(scope_node, cast->name);
4241
4242 if (cast->parent != NULL) {
4243 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4244 pm_compile_defined_expr0(iseq, cast->parent, node_location, ret, popped, scope_node, true, lfinish, false);
4245
4246 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4247 PM_COMPILE(cast->parent);
4248 }
4249 else {
4250 PUSH_INSN1(ret, location, putobject, rb_cObject);
4251 }
4252
4253 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), ID2SYM(name), PUSH_VAL(DEFINED_CONST));
4254 return;
4255 }
4256/* Containers *****************************************************************/
4257 case PM_BEGIN_NODE: {
4258 // defined?(begin end)
4259 // ^^^^^^^^^
4260 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
4261
4262 if (cast->rescue_clause == NULL && cast->ensure_clause == NULL && cast->else_clause == NULL) {
4263 if (cast->statements == NULL) {
4264 // If we have empty statements, then we want to return "nil".
4265 dtype = DEFINED_NIL;
4266 }
4267 else if (cast->statements->body.size == 1) {
4268 // If we have a begin node that is wrapping a single statement
4269 // then we want to recurse down to that statement and compile
4270 // it.
4271 pm_compile_defined_expr0(iseq, cast->statements->body.nodes[0], node_location, ret, popped, scope_node, in_condition, lfinish, false);
4272 return;
4273 }
4274 else {
4275 // Otherwise, we have a begin wrapping multiple statements, in
4276 // which case this is defined as "expression".
4277 dtype = DEFINED_EXPR;
4278 }
4279 } else {
4280 // If we have any of the other clauses besides the main begin/end,
4281 // this is defined as "expression".
4282 dtype = DEFINED_EXPR;
4283 }
4284
4285 break;
4286 }
4287 case PM_PARENTHESES_NODE: {
4288 // defined?(())
4289 // ^^
4290 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
4291
4292 if (cast->body == NULL) {
4293 // If we have empty parentheses, then we want to return "nil".
4294 dtype = DEFINED_NIL;
4295 }
4296 else if (PM_NODE_TYPE_P(cast->body, PM_STATEMENTS_NODE) && !PM_NODE_FLAG_P(cast, PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS)) {
4297 // If we have a parentheses node that is wrapping a single statement
4298 // then we want to recurse down to that statement and compile it.
4299 pm_compile_defined_expr0(iseq, ((const pm_statements_node_t *) cast->body)->body.nodes[0], node_location, ret, popped, scope_node, in_condition, lfinish, false);
4300 return;
4301 }
4302 else {
4303 // Otherwise, we have parentheses wrapping multiple statements, in
4304 // which case this is defined as "expression".
4305 dtype = DEFINED_EXPR;
4306 }
4307
4308 break;
4309 }
4310 case PM_ARRAY_NODE: {
4311 // defined?([])
4312 // ^^
4313 const pm_array_node_t *cast = (const pm_array_node_t *) node;
4314
4315 if (cast->elements.size > 0 && !lfinish[1]) {
4316 lfinish[1] = NEW_LABEL(location.line);
4317 }
4318
4319 for (size_t index = 0; index < cast->elements.size; index++) {
4320 pm_compile_defined_expr0(iseq, cast->elements.nodes[index], node_location, ret, popped, scope_node, true, lfinish, false);
4321 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4322 }
4323
4324 dtype = DEFINED_EXPR;
4325 break;
4326 }
4327 case PM_HASH_NODE:
4328 // defined?({ a: 1 })
4329 // ^^^^^^^^
4330 case PM_KEYWORD_HASH_NODE: {
4331 // defined?(a(a: 1))
4332 // ^^^^
4333 const pm_node_list_t *elements;
4334
4335 if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) {
4336 elements = &((const pm_hash_node_t *) node)->elements;
4337 }
4338 else {
4339 elements = &((const pm_keyword_hash_node_t *) node)->elements;
4340 }
4341
4342 if (elements->size > 0 && !lfinish[1]) {
4343 lfinish[1] = NEW_LABEL(location.line);
4344 }
4345
4346 for (size_t index = 0; index < elements->size; index++) {
4347 pm_compile_defined_expr0(iseq, elements->nodes[index], node_location, ret, popped, scope_node, true, lfinish, false);
4348 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4349 }
4350
4351 dtype = DEFINED_EXPR;
4352 break;
4353 }
4354 case PM_ASSOC_NODE: {
4355 // defined?({ a: 1 })
4356 // ^^^^
4357 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node;
4358
4359 pm_compile_defined_expr0(iseq, cast->key, node_location, ret, popped, scope_node, true, lfinish, false);
4360 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4361 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, true, lfinish, false);
4362
4363 return;
4364 }
4365 case PM_ASSOC_SPLAT_NODE: {
4366 // defined?({ **a })
4367 // ^^^^
4368 const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node;
4369
4370 if (cast->value == NULL) {
4371 dtype = DEFINED_EXPR;
4372 break;
4373 }
4374
4375 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, true, lfinish, false);
4376 return;
4377 }
4378 case PM_IMPLICIT_NODE: {
4379 // defined?({ a: })
4380 // ^^
4381 const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node;
4382 pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4383 return;
4384 }
4385 case PM_CALL_NODE: {
4386#define BLOCK_P(cast) ((cast)->block != NULL && PM_NODE_TYPE_P((cast)->block, PM_BLOCK_NODE))
4387
4388 // defined?(a(1, 2, 3))
4389 // ^^^^^^^^^^
4390 const pm_call_node_t *cast = ((const pm_call_node_t *) node);
4391
4392 if (BLOCK_P(cast)) {
4393 dtype = DEFINED_EXPR;
4394 break;
4395 }
4396
4397 if (cast->receiver || cast->arguments || (cast->block && PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE))) {
4398 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4399 if (!lfinish[2]) lfinish[2] = NEW_LABEL(location.line);
4400 }
4401
4402 if (cast->arguments) {
4403 pm_compile_defined_expr0(iseq, (const pm_node_t *) cast->arguments, node_location, ret, popped, scope_node, true, lfinish, false);
4404 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4405 }
4406
4407 if (cast->block && PM_NODE_TYPE_P(cast->block, PM_BLOCK_ARGUMENT_NODE)) {
4408 pm_compile_defined_expr0(iseq, cast->block, node_location, ret, popped, scope_node, true, lfinish, false);
4409 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4410 }
4411
4412 if (cast->receiver) {
4413 if (PM_NODE_TYPE_P(cast->receiver, PM_CALL_NODE) && !BLOCK_P((const pm_call_node_t *) cast->receiver)) {
4414 // Special behavior here where we chain calls together. This is
4415 // the only path that sets explicit_receiver to true.
4416 pm_compile_defined_expr0(iseq, cast->receiver, node_location, ret, popped, scope_node, true, lfinish, true);
4417 PUSH_INSNL(ret, location, branchunless, lfinish[2]);
4418
4419 const pm_call_node_t *receiver = (const pm_call_node_t *) cast->receiver;
4420 ID method_id = pm_constant_id_lookup(scope_node, receiver->name);
4421
4422 pm_compile_call(iseq, receiver, ret, popped, scope_node, method_id, NULL);
4423 }
4424 else {
4425 pm_compile_defined_expr0(iseq, cast->receiver, node_location, ret, popped, scope_node, true, lfinish, false);
4426 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4427 PM_COMPILE(cast->receiver);
4428 }
4429
4430 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
4431
4432 if (explicit_receiver) PUSH_INSN(ret, location, dup);
4433 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_METHOD), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
4434 }
4435 else {
4436 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
4437
4438 PUSH_INSN(ret, location, putself);
4439 if (explicit_receiver) PUSH_INSN(ret, location, dup);
4440
4441 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_FUNC), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
4442 }
4443
4444 return;
4445
4446#undef BLOCK_P
4447 }
4448 case PM_ARGUMENTS_NODE: {
4449 // defined?(a(1, 2, 3))
4450 // ^^^^^^^
4451 const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node;
4452
4453 for (size_t index = 0; index < cast->arguments.size; index++) {
4454 pm_compile_defined_expr0(iseq, cast->arguments.nodes[index], node_location, ret, popped, scope_node, in_condition, lfinish, false);
4455 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4456 }
4457
4458 dtype = DEFINED_EXPR;
4459 break;
4460 }
4461 case PM_BLOCK_ARGUMENT_NODE:
4462 // defined?(a(&b))
4463 // ^^
4464 dtype = DEFINED_EXPR;
4465 break;
4466 case PM_FORWARDING_ARGUMENTS_NODE:
4467 // def a(...) = defined?(a(...))
4468 // ^^^
4469 dtype = DEFINED_EXPR;
4470 break;
4471 case PM_SPLAT_NODE: {
4472 // def a(*) = defined?(a(*))
4473 // ^
4474 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
4475
4476 if (cast->expression == NULL) {
4477 dtype = DEFINED_EXPR;
4478 break;
4479 }
4480
4481 pm_compile_defined_expr0(iseq, cast->expression, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4482
4483 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
4484 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
4485
4486 dtype = DEFINED_EXPR;
4487 break;
4488 }
4489 case PM_SHAREABLE_CONSTANT_NODE:
4490 // # shareable_constant_value: literal
4491 // defined?(A = 1)
4492 // ^^^^^
4493 pm_compile_defined_expr0(iseq, ((const pm_shareable_constant_node_t *) node)->write, node_location, ret, popped, scope_node, in_condition, lfinish, explicit_receiver);
4494 return;
4495/* Unreachable (parameters) ***************************************************/
4496 case PM_BLOCK_LOCAL_VARIABLE_NODE:
4497 case PM_BLOCK_PARAMETER_NODE:
4498 case PM_BLOCK_PARAMETERS_NODE:
4499 case PM_FORWARDING_PARAMETER_NODE:
4500 case PM_IMPLICIT_REST_NODE:
4501 case PM_IT_PARAMETERS_NODE:
4502 case PM_PARAMETERS_NODE:
4503 case PM_KEYWORD_REST_PARAMETER_NODE:
4504 case PM_NO_KEYWORDS_PARAMETER_NODE:
4505 case PM_NUMBERED_PARAMETERS_NODE:
4506 case PM_OPTIONAL_KEYWORD_PARAMETER_NODE:
4507 case PM_OPTIONAL_PARAMETER_NODE:
4508 case PM_REQUIRED_KEYWORD_PARAMETER_NODE:
4509 case PM_REQUIRED_PARAMETER_NODE:
4510 case PM_REST_PARAMETER_NODE:
4511/* Unreachable (pattern matching) *********************************************/
4512 case PM_ALTERNATION_PATTERN_NODE:
4513 case PM_ARRAY_PATTERN_NODE:
4514 case PM_CAPTURE_PATTERN_NODE:
4515 case PM_FIND_PATTERN_NODE:
4516 case PM_HASH_PATTERN_NODE:
4517 case PM_PINNED_EXPRESSION_NODE:
4518 case PM_PINNED_VARIABLE_NODE:
4519/* Unreachable (indirect writes) **********************************************/
4520 case PM_CALL_TARGET_NODE:
4521 case PM_CLASS_VARIABLE_TARGET_NODE:
4522 case PM_CONSTANT_PATH_TARGET_NODE:
4523 case PM_CONSTANT_TARGET_NODE:
4524 case PM_GLOBAL_VARIABLE_TARGET_NODE:
4525 case PM_INDEX_TARGET_NODE:
4526 case PM_INSTANCE_VARIABLE_TARGET_NODE:
4527 case PM_LOCAL_VARIABLE_TARGET_NODE:
4528 case PM_MULTI_TARGET_NODE:
4529/* Unreachable (clauses) ******************************************************/
4530 case PM_ELSE_NODE:
4531 case PM_ENSURE_NODE:
4532 case PM_IN_NODE:
4533 case PM_RESCUE_NODE:
4534 case PM_WHEN_NODE:
4535/* Unreachable (miscellaneous) ************************************************/
4536 case PM_BLOCK_NODE:
4537 case PM_EMBEDDED_STATEMENTS_NODE:
4538 case PM_EMBEDDED_VARIABLE_NODE:
4539 case PM_MISSING_NODE:
4540 case PM_PRE_EXECUTION_NODE:
4541 case PM_PROGRAM_NODE:
4542 case PM_SCOPE_NODE:
4543 case PM_STATEMENTS_NODE:
4544 rb_bug("Unreachable node in defined?: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
4545 }
4546
4547 RUBY_ASSERT(dtype != DEFINED_NOT_DEFINED);
4548 PUSH_INSN1(ret, location, putobject, PUSH_VAL(dtype));
4549
4550#undef PUSH_VAL
4551}
4552
4553static void
4554pm_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)
4555{
4556 LINK_ELEMENT *lcur = ret->last;
4557 pm_compile_defined_expr0(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish, false);
4558
4559 if (lfinish[1]) {
4560 LABEL *lstart = NEW_LABEL(node_location->line);
4561 LABEL *lend = NEW_LABEL(node_location->line);
4562
4564 rb_iseq_new_with_callback_new_callback(build_defined_rescue_iseq, NULL);
4565
4566 const rb_iseq_t *rescue = new_child_iseq_with_callback(
4567 iseq,
4568 ifunc,
4569 rb_str_concat(rb_str_new2("defined guard in "), ISEQ_BODY(iseq)->location.label),
4570 iseq,
4571 ISEQ_TYPE_RESCUE,
4572 0
4573 );
4574
4575 lstart->rescued = LABEL_RESCUE_BEG;
4576 lend->rescued = LABEL_RESCUE_END;
4577
4578 APPEND_LABEL(ret, lcur, lstart);
4579 PUSH_LABEL(ret, lend);
4580 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
4581 }
4582}
4583
4584static void
4585pm_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)
4586{
4587 LABEL *lfinish[3];
4588 LINK_ELEMENT *last = ret->last;
4589
4590 lfinish[0] = NEW_LABEL(node_location->line);
4591 lfinish[1] = 0;
4592 lfinish[2] = 0;
4593
4594 if (!popped) {
4595 pm_defined_expr(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish);
4596 }
4597
4598 if (lfinish[1]) {
4599 ELEM_INSERT_NEXT(last, &new_insn_body(iseq, node_location->line, node_location->node_id, BIN(putnil), 0)->link);
4600 PUSH_INSN(ret, *node_location, swap);
4601
4602 if (lfinish[2]) PUSH_LABEL(ret, lfinish[2]);
4603 PUSH_INSN(ret, *node_location, pop);
4604 PUSH_LABEL(ret, lfinish[1]);
4605
4606 }
4607
4608 PUSH_LABEL(ret, lfinish[0]);
4609}
4610
4611// This is exactly the same as add_ensure_iseq, except it compiled
4612// the node as a Prism node, and not a CRuby node
4613static void
4614pm_add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return, pm_scope_node_t *scope_node)
4615{
4616 RUBY_ASSERT(can_add_ensure_iseq(iseq));
4617
4619 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
4620 struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
4621 DECL_ANCHOR(ensure);
4622
4623 while (enlp) {
4624 if (enlp->erange != NULL) {
4625 DECL_ANCHOR(ensure_part);
4626 LABEL *lstart = NEW_LABEL(0);
4627 LABEL *lend = NEW_LABEL(0);
4628
4629 add_ensure_range(iseq, enlp->erange, lstart, lend);
4630
4631 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
4632 PUSH_LABEL(ensure_part, lstart);
4633 bool popped = true;
4634 PM_COMPILE_INTO_ANCHOR(ensure_part, (const pm_node_t *) enlp->ensure_node);
4635 PUSH_LABEL(ensure_part, lend);
4636 PUSH_SEQ(ensure, ensure_part);
4637 }
4638 else {
4639 if (!is_return) {
4640 break;
4641 }
4642 }
4643 enlp = enlp->prev;
4644 }
4645 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = prev_enlp;
4646 PUSH_SEQ(ret, ensure);
4647}
4648
4650 pm_scope_node_t *scope_node;
4651 rb_ast_id_table_t *local_table_for_iseq;
4652 int local_index;
4653};
4654
4655static int
4656pm_local_table_insert_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
4657{
4658 if (!existing) {
4659 pm_constant_id_t constant_id = (pm_constant_id_t) *key;
4660 struct pm_local_table_insert_ctx * ctx = (struct pm_local_table_insert_ctx *) arg;
4661
4662 pm_scope_node_t *scope_node = ctx->scope_node;
4663 rb_ast_id_table_t *local_table_for_iseq = ctx->local_table_for_iseq;
4664 int local_index = ctx->local_index;
4665
4666 ID local = pm_constant_id_lookup(scope_node, constant_id);
4667 local_table_for_iseq->ids[local_index] = local;
4668
4669 *value = (st_data_t)local_index;
4670
4671 ctx->local_index++;
4672 }
4673
4674 return ST_CONTINUE;
4675}
4676
4682static void
4683pm_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)
4684{
4685 RUBY_ASSERT((constant_id & PM_SPECIAL_CONSTANT_FLAG) == 0);
4686
4687 ID local = pm_constant_id_lookup(scope_node, constant_id);
4688 local_table_for_iseq->ids[local_index] = local;
4689 st_insert(index_lookup_table, (st_data_t) constant_id, (st_data_t) local_index);
4690}
4691
4696static void
4697pm_insert_local_special(ID local_name, int local_index, st_table *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq)
4698{
4699 local_table_for_iseq->ids[local_index] = local_name;
4700 st_insert(index_lookup_table, (st_data_t) (local_name | PM_SPECIAL_CONSTANT_FLAG), (st_data_t) local_index);
4701}
4702
4709static int
4710pm_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)
4711{
4712 for (size_t index = 0; index < node->lefts.size; index++) {
4713 const pm_node_t *left = node->lefts.nodes[index];
4714
4715 if (PM_NODE_TYPE_P(left, PM_REQUIRED_PARAMETER_NODE)) {
4716 if (!PM_NODE_FLAG_P(left, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4717 pm_insert_local_index(((const pm_required_parameter_node_t *) left)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4718 local_index++;
4719 }
4720 }
4721 else {
4722 RUBY_ASSERT(PM_NODE_TYPE_P(left, PM_MULTI_TARGET_NODE));
4723 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);
4724 }
4725 }
4726
4727 if (node->rest != NULL && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE)) {
4728 const pm_splat_node_t *rest = (const pm_splat_node_t *) node->rest;
4729
4730 if (rest->expression != NULL) {
4731 RUBY_ASSERT(PM_NODE_TYPE_P(rest->expression, PM_REQUIRED_PARAMETER_NODE));
4732
4733 if (!PM_NODE_FLAG_P(rest->expression, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4734 pm_insert_local_index(((const pm_required_parameter_node_t *) rest->expression)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4735 local_index++;
4736 }
4737 }
4738 }
4739
4740 for (size_t index = 0; index < node->rights.size; index++) {
4741 const pm_node_t *right = node->rights.nodes[index];
4742
4743 if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) {
4744 if (!PM_NODE_FLAG_P(right, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
4745 pm_insert_local_index(((const pm_required_parameter_node_t *) right)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
4746 local_index++;
4747 }
4748 }
4749 else {
4750 RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE));
4751 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);
4752 }
4753 }
4754
4755 return local_index;
4756}
4757
4762static inline void
4763pm_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)
4764{
4765 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
4766 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, node->name, 0);
4767 PUSH_SETLOCAL(ret, location, index.index, index.level);
4768}
4769
4778static void
4779pm_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)
4780{
4781 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
4782 bool has_rest = (node->rest && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE) && (((const pm_splat_node_t *) node->rest)->expression) != NULL);
4783 bool has_rights = node->rights.size > 0;
4784
4785 int flag = (has_rest || has_rights) ? 1 : 0;
4786 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->lefts.size), INT2FIX(flag));
4787
4788 for (size_t index = 0; index < node->lefts.size; index++) {
4789 const pm_node_t *left = node->lefts.nodes[index];
4790
4791 if (PM_NODE_TYPE_P(left, PM_REQUIRED_PARAMETER_NODE)) {
4792 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) left, ret, scope_node);
4793 }
4794 else {
4795 RUBY_ASSERT(PM_NODE_TYPE_P(left, PM_MULTI_TARGET_NODE));
4796 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) left, ret, scope_node);
4797 }
4798 }
4799
4800 if (has_rest) {
4801 if (has_rights) {
4802 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->rights.size), INT2FIX(3));
4803 }
4804
4805 const pm_node_t *rest = ((const pm_splat_node_t *) node->rest)->expression;
4806 RUBY_ASSERT(PM_NODE_TYPE_P(rest, PM_REQUIRED_PARAMETER_NODE));
4807
4808 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) rest, ret, scope_node);
4809 }
4810
4811 if (has_rights) {
4812 if (!has_rest) {
4813 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->rights.size), INT2FIX(2));
4814 }
4815
4816 for (size_t index = 0; index < node->rights.size; index++) {
4817 const pm_node_t *right = node->rights.nodes[index];
4818
4819 if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) {
4820 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) right, ret, scope_node);
4821 }
4822 else {
4823 RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE));
4824 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) right, ret, scope_node);
4825 }
4826 }
4827 }
4828}
4829
4835 // The pointer to the topn instruction that will need to be modified after
4836 // we know the total stack size of all of the targets.
4837 INSN *topn;
4838
4839 // The index of the stack from the base of the entire multi target at which
4840 // the parent expression is located.
4841 size_t stack_index;
4842
4843 // The number of slots in the stack that this node occupies.
4844 size_t stack_size;
4845
4846 // The position of the node in the list of targets.
4847 size_t position;
4848
4849 // A pointer to the next node in this linked list.
4850 struct pm_multi_target_state_node *next;
4852
4860typedef struct {
4861 // The total number of slots in the stack that this multi target occupies.
4862 size_t stack_size;
4863
4864 // The position of the current node being compiled. This is forwarded to
4865 // nodes when they are allocated.
4866 size_t position;
4867
4868 // A pointer to the head of this linked list.
4870
4871 // A pointer to the tail of this linked list.
4874
4878static void
4879pm_multi_target_state_push(pm_multi_target_state_t *state, INSN *topn, size_t stack_size)
4880{
4882 node->topn = topn;
4883 node->stack_index = state->stack_size + 1;
4884 node->stack_size = stack_size;
4885 node->position = state->position;
4886 node->next = NULL;
4887
4888 if (state->head == NULL) {
4889 state->head = node;
4890 state->tail = node;
4891 }
4892 else {
4893 state->tail->next = node;
4894 state->tail = node;
4895 }
4896
4897 state->stack_size += stack_size;
4898}
4899
4905static void
4906pm_multi_target_state_update(pm_multi_target_state_t *state)
4907{
4908 // If nothing was ever pushed onto the stack, then we don't need to do any
4909 // kind of updates.
4910 if (state->stack_size == 0) return;
4911
4912 pm_multi_target_state_node_t *current = state->head;
4914
4915 while (current != NULL) {
4916 VALUE offset = INT2FIX(state->stack_size - current->stack_index + current->position);
4917 current->topn->operands[0] = offset;
4918
4919 // stack_size will be > 1 in the case that we compiled an index target
4920 // and it had arguments. In this case, we use multiple topn instructions
4921 // to grab up all of the arguments as well, so those offsets need to be
4922 // updated as well.
4923 if (current->stack_size > 1) {
4924 INSN *insn = current->topn;
4925
4926 for (size_t index = 1; index < current->stack_size; index += 1) {
4927 LINK_ELEMENT *element = get_next_insn(insn);
4928 RUBY_ASSERT(IS_INSN(element));
4929
4930 insn = (INSN *) element;
4931 RUBY_ASSERT(insn->insn_id == BIN(topn));
4932
4933 insn->operands[0] = offset;
4934 }
4935 }
4936
4937 previous = current;
4938 current = current->next;
4939
4940 xfree(previous);
4941 }
4942}
4943
4944static void
4945pm_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);
4946
4975static void
4976pm_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)
4977{
4978 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
4979
4980 switch (PM_NODE_TYPE(node)) {
4981 case PM_LOCAL_VARIABLE_TARGET_NODE: {
4982 // Local variable targets have no parent expression, so they only need
4983 // to compile the write.
4984 //
4985 // for i in []; end
4986 //
4988 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
4989
4990 PUSH_SETLOCAL(writes, location, index.index, index.level);
4991 break;
4992 }
4993 case PM_CLASS_VARIABLE_TARGET_NODE: {
4994 // Class variable targets have no parent expression, so they only need
4995 // to compile the write.
4996 //
4997 // for @@i in []; end
4998 //
5000 ID name = pm_constant_id_lookup(scope_node, cast->name);
5001
5002 VALUE operand = ID2SYM(name);
5003 PUSH_INSN2(writes, location, setclassvariable, operand, get_cvar_ic_value(iseq, name));
5004 break;
5005 }
5006 case PM_CONSTANT_TARGET_NODE: {
5007 // Constant targets have no parent expression, so they only need to
5008 // compile the write.
5009 //
5010 // for I in []; end
5011 //
5012 const pm_constant_target_node_t *cast = (const pm_constant_target_node_t *) node;
5013 ID name = pm_constant_id_lookup(scope_node, cast->name);
5014
5015 VALUE operand = ID2SYM(name);
5016 PUSH_INSN1(writes, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5017 PUSH_INSN1(writes, location, setconstant, operand);
5018 break;
5019 }
5020 case PM_GLOBAL_VARIABLE_TARGET_NODE: {
5021 // Global variable targets have no parent expression, so they only need
5022 // to compile the write.
5023 //
5024 // for $i in []; end
5025 //
5027 ID name = pm_constant_id_lookup(scope_node, cast->name);
5028
5029 VALUE operand = ID2SYM(name);
5030 PUSH_INSN1(writes, location, setglobal, operand);
5031 break;
5032 }
5033 case PM_INSTANCE_VARIABLE_TARGET_NODE: {
5034 // Instance variable targets have no parent expression, so they only
5035 // need to compile the write.
5036 //
5037 // for @i in []; end
5038 //
5040 ID name = pm_constant_id_lookup(scope_node, cast->name);
5041
5042 VALUE operand = ID2SYM(name);
5043 PUSH_INSN2(writes, location, setinstancevariable, operand, get_ivar_ic_value(iseq, name));
5044 break;
5045 }
5046 case PM_CONSTANT_PATH_TARGET_NODE: {
5047 // Constant path targets have a parent expression that is the object
5048 // that owns the constant. This needs to be compiled first into the
5049 // parents sequence. If no parent is found, then it represents using the
5050 // unary :: operator to indicate a top-level constant. In that case we
5051 // need to push Object onto the stack.
5052 //
5053 // for I::J in []; end
5054 //
5056 ID name = pm_constant_id_lookup(scope_node, cast->name);
5057
5058 if (cast->parent != NULL) {
5059 pm_compile_node(iseq, cast->parent, parents, false, scope_node);
5060 }
5061 else {
5062 PUSH_INSN1(parents, location, putobject, rb_cObject);
5063 }
5064
5065 if (state == NULL) {
5066 PUSH_INSN(writes, location, swap);
5067 }
5068 else {
5069 PUSH_INSN1(writes, location, topn, INT2FIX(1));
5070 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), 1);
5071 }
5072
5073 VALUE operand = ID2SYM(name);
5074 PUSH_INSN1(writes, location, setconstant, operand);
5075
5076 if (state != NULL) {
5077 PUSH_INSN(cleanup, location, pop);
5078 }
5079
5080 break;
5081 }
5082 case PM_CALL_TARGET_NODE: {
5083 // Call targets have a parent expression that is the receiver of the
5084 // method being called. This needs to be compiled first into the parents
5085 // sequence. These nodes cannot have arguments, so the method call is
5086 // compiled with a single argument which represents the value being
5087 // written.
5088 //
5089 // for i.j in []; end
5090 //
5091 const pm_call_target_node_t *cast = (const pm_call_target_node_t *) node;
5092 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
5093
5094 pm_compile_node(iseq, cast->receiver, parents, false, scope_node);
5095
5096 LABEL *safe_label = NULL;
5097 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
5098 safe_label = NEW_LABEL(location.line);
5099 PUSH_INSN(parents, location, dup);
5100 PUSH_INSNL(parents, location, branchnil, safe_label);
5101 }
5102
5103 if (state != NULL) {
5104 PUSH_INSN1(writes, location, topn, INT2FIX(1));
5105 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), 1);
5106 PUSH_INSN(writes, location, swap);
5107 }
5108
5109 int flags = VM_CALL_ARGS_SIMPLE;
5110 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) flags |= VM_CALL_FCALL;
5111
5112 PUSH_SEND_WITH_FLAG(writes, location, method_id, INT2FIX(1), INT2FIX(flags));
5113 if (safe_label != NULL && state == NULL) PUSH_LABEL(writes, safe_label);
5114 PUSH_INSN(writes, location, pop);
5115 if (safe_label != NULL && state != NULL) PUSH_LABEL(writes, safe_label);
5116
5117 if (state != NULL) {
5118 PUSH_INSN(cleanup, location, pop);
5119 }
5120
5121 break;
5122 }
5123 case PM_INDEX_TARGET_NODE: {
5124 // Index targets have a parent expression that is the receiver of the
5125 // method being called and any additional arguments that are being
5126 // passed along with the value being written. The receiver and arguments
5127 // both need to be on the stack. Note that this is even more complicated
5128 // by the fact that these nodes can hold a block using the unary &
5129 // operator.
5130 //
5131 // for i[:j] in []; end
5132 //
5133 const pm_index_target_node_t *cast = (const pm_index_target_node_t *) node;
5134
5135 pm_compile_node(iseq, cast->receiver, parents, false, scope_node);
5136
5137 int flags = 0;
5138 struct rb_callinfo_kwarg *kwargs = NULL;
5139 int argc = pm_setup_args(cast->arguments, (const pm_node_t *) cast->block, &flags, &kwargs, iseq, parents, scope_node, &location);
5140
5141 if (state != NULL) {
5142 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5143 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), argc + 1);
5144
5145 if (argc == 0) {
5146 PUSH_INSN(writes, location, swap);
5147 }
5148 else {
5149 for (int index = 0; index < argc; index++) {
5150 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5151 }
5152 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
5153 }
5154 }
5155
5156 // The argc that we're going to pass to the send instruction is the
5157 // number of arguments + 1 for the value being written. If there's a
5158 // splat, then we need to insert newarray and concatarray instructions
5159 // after the arguments have been written.
5160 int ci_argc = argc + 1;
5161 if (flags & VM_CALL_ARGS_SPLAT) {
5162 ci_argc--;
5163 PUSH_INSN1(writes, location, newarray, INT2FIX(1));
5164 PUSH_INSN(writes, location, concatarray);
5165 }
5166
5167 PUSH_SEND_R(writes, location, idASET, INT2NUM(ci_argc), NULL, INT2FIX(flags), kwargs);
5168 PUSH_INSN(writes, location, pop);
5169
5170 if (state != NULL) {
5171 if (argc != 0) {
5172 PUSH_INSN(writes, location, pop);
5173 }
5174
5175 for (int index = 0; index < argc + 1; index++) {
5176 PUSH_INSN(cleanup, location, pop);
5177 }
5178 }
5179
5180 break;
5181 }
5182 case PM_MULTI_TARGET_NODE: {
5183 // Multi target nodes represent a set of writes to multiple variables.
5184 // The parent expressions are the combined set of the parent expressions
5185 // of its inner target nodes.
5186 //
5187 // for i, j in []; end
5188 //
5189 size_t before_position;
5190 if (state != NULL) {
5191 before_position = state->position;
5192 state->position--;
5193 }
5194
5195 pm_compile_multi_target_node(iseq, node, parents, writes, cleanup, scope_node, state);
5196 if (state != NULL) state->position = before_position;
5197
5198 break;
5199 }
5200 case PM_SPLAT_NODE: {
5201 // Splat nodes capture all values into an array. They can be used
5202 // as targets in assignments or for loops.
5203 //
5204 // for *x in []; end
5205 //
5206 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
5207
5208 if (cast->expression != NULL) {
5209 pm_compile_target_node(iseq, cast->expression, parents, writes, cleanup, scope_node, state);
5210 }
5211
5212 break;
5213 }
5214 default:
5215 rb_bug("Unexpected node type: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
5216 break;
5217 }
5218}
5219
5225static void
5226pm_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)
5227{
5228 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
5229 const pm_node_list_t *lefts;
5230 const pm_node_t *rest;
5231 const pm_node_list_t *rights;
5232
5233 switch (PM_NODE_TYPE(node)) {
5234 case PM_MULTI_TARGET_NODE: {
5235 const pm_multi_target_node_t *cast = (const pm_multi_target_node_t *) node;
5236 lefts = &cast->lefts;
5237 rest = cast->rest;
5238 rights = &cast->rights;
5239 break;
5240 }
5241 case PM_MULTI_WRITE_NODE: {
5242 const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node;
5243 lefts = &cast->lefts;
5244 rest = cast->rest;
5245 rights = &cast->rights;
5246 break;
5247 }
5248 default:
5249 rb_bug("Unsupported node %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
5250 break;
5251 }
5252
5253 bool has_rest = (rest != NULL) && PM_NODE_TYPE_P(rest, PM_SPLAT_NODE) && ((const pm_splat_node_t *) rest)->expression != NULL;
5254 bool has_posts = rights->size > 0;
5255
5256 // The first instruction in the writes sequence is going to spread the
5257 // top value of the stack onto the number of values that we're going to
5258 // write.
5259 PUSH_INSN2(writes, location, expandarray, INT2FIX(lefts->size), INT2FIX((has_rest || has_posts) ? 1 : 0));
5260
5261 // We need to keep track of some additional state information as we're
5262 // going through the targets because we will need to revisit them once
5263 // we know how many values are being pushed onto the stack.
5264 pm_multi_target_state_t target_state = { 0 };
5265 if (state == NULL) state = &target_state;
5266
5267 size_t base_position = state->position;
5268 size_t splat_position = (has_rest || has_posts) ? 1 : 0;
5269
5270 // Next, we'll iterate through all of the leading targets.
5271 for (size_t index = 0; index < lefts->size; index++) {
5272 const pm_node_t *target = lefts->nodes[index];
5273 state->position = lefts->size - index + splat_position + base_position;
5274 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5275 }
5276
5277 // Next, we'll compile the rest target if there is one.
5278 if (has_rest) {
5279 const pm_node_t *target = ((const pm_splat_node_t *) rest)->expression;
5280 state->position = 1 + rights->size + base_position;
5281
5282 if (has_posts) {
5283 PUSH_INSN2(writes, location, expandarray, INT2FIX(rights->size), INT2FIX(3));
5284 }
5285
5286 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5287 }
5288
5289 // Finally, we'll compile the trailing targets.
5290 if (has_posts) {
5291 if (!has_rest && rest != NULL) {
5292 PUSH_INSN2(writes, location, expandarray, INT2FIX(rights->size), INT2FIX(2));
5293 }
5294
5295 for (size_t index = 0; index < rights->size; index++) {
5296 const pm_node_t *target = rights->nodes[index];
5297 state->position = rights->size - index + base_position;
5298 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
5299 }
5300 }
5301}
5302
5308static void
5309pm_compile_for_node_index(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
5310{
5311 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
5312
5313 switch (PM_NODE_TYPE(node)) {
5314 case PM_LOCAL_VARIABLE_TARGET_NODE: {
5315 // For local variables, all we have to do is retrieve the value and then
5316 // compile the index node.
5317 PUSH_GETLOCAL(ret, location, 1, 0);
5318 pm_compile_target_node(iseq, node, ret, ret, ret, scope_node, NULL);
5319 break;
5320 }
5321 case PM_CLASS_VARIABLE_TARGET_NODE:
5322 case PM_CONSTANT_TARGET_NODE:
5323 case PM_GLOBAL_VARIABLE_TARGET_NODE:
5324 case PM_INSTANCE_VARIABLE_TARGET_NODE:
5325 case PM_CONSTANT_PATH_TARGET_NODE:
5326 case PM_CALL_TARGET_NODE:
5327 case PM_INDEX_TARGET_NODE:
5328 case PM_SPLAT_NODE: {
5329 // For other targets, we need to potentially compile the parent or
5330 // owning expression of this target, then retrieve the value, expand it,
5331 // and then compile the necessary writes.
5332 DECL_ANCHOR(writes);
5333 DECL_ANCHOR(cleanup);
5334
5335 pm_multi_target_state_t state = { 0 };
5336 state.position = 1;
5337 pm_compile_target_node(iseq, node, ret, writes, cleanup, scope_node, &state);
5338
5339 PUSH_GETLOCAL(ret, location, 1, 0);
5340 PUSH_INSN2(ret, location, expandarray, INT2FIX(1), INT2FIX(0));
5341
5342 PUSH_SEQ(ret, writes);
5343 PUSH_SEQ(ret, cleanup);
5344
5345 pm_multi_target_state_update(&state);
5346 break;
5347 }
5348 case PM_MULTI_TARGET_NODE: {
5349 DECL_ANCHOR(writes);
5350 DECL_ANCHOR(cleanup);
5351
5352 pm_compile_target_node(iseq, node, ret, writes, cleanup, scope_node, NULL);
5353
5354 LABEL *not_single = NEW_LABEL(location.line);
5355 LABEL *not_ary = NEW_LABEL(location.line);
5356
5357 // When there are multiple targets, we'll do a bunch of work to convert
5358 // the value into an array before we expand it. Effectively we're trying
5359 // to accomplish:
5360 //
5361 // (args.length == 1 && Array.try_convert(args[0])) || args
5362 //
5363 PUSH_GETLOCAL(ret, location, 1, 0);
5364 PUSH_INSN(ret, location, dup);
5365 PUSH_CALL(ret, location, idLength, INT2FIX(0));
5366 PUSH_INSN1(ret, location, putobject, INT2FIX(1));
5367 PUSH_CALL(ret, location, idEq, INT2FIX(1));
5368 PUSH_INSNL(ret, location, branchunless, not_single);
5369 PUSH_INSN(ret, location, dup);
5370 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
5371 PUSH_CALL(ret, location, idAREF, INT2FIX(1));
5372 PUSH_INSN1(ret, location, putobject, rb_cArray);
5373 PUSH_INSN(ret, location, swap);
5374 PUSH_CALL(ret, location, rb_intern("try_convert"), INT2FIX(1));
5375 PUSH_INSN(ret, location, dup);
5376 PUSH_INSNL(ret, location, branchunless, not_ary);
5377 PUSH_INSN(ret, location, swap);
5378
5379 PUSH_LABEL(ret, not_ary);
5380 PUSH_INSN(ret, location, pop);
5381
5382 PUSH_LABEL(ret, not_single);
5383 PUSH_SEQ(ret, writes);
5384 PUSH_SEQ(ret, cleanup);
5385 break;
5386 }
5387 default:
5388 rb_bug("Unexpected node type for index in for node: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
5389 break;
5390 }
5391}
5392
5393static void
5394pm_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)
5395{
5396 const pm_parser_t *parser = scope_node->parser;
5397
5398 LABEL *lstart = NEW_LABEL(node_location->line);
5399 LABEL *lend = NEW_LABEL(node_location->line);
5400 LABEL *lcont = NEW_LABEL(node_location->line);
5401
5402 pm_scope_node_t rescue_scope_node;
5403 pm_scope_node_init((const pm_node_t *) cast->rescue_clause, &rescue_scope_node, scope_node);
5404
5405 rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(
5406 &rescue_scope_node,
5407 rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label),
5408 ISEQ_TYPE_RESCUE,
5409 pm_node_line_number(parser, (const pm_node_t *) cast->rescue_clause)
5410 );
5411
5412 pm_scope_node_destroy(&rescue_scope_node);
5413
5414 lstart->rescued = LABEL_RESCUE_BEG;
5415 lend->rescued = LABEL_RESCUE_END;
5416 PUSH_LABEL(ret, lstart);
5417
5418 bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
5419 ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
5420
5421 if (cast->statements != NULL) {
5422 PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->statements);
5423 }
5424 else {
5425 const pm_node_location_t location = PM_NODE_START_LOCATION(parser, cast->rescue_clause);
5426 PUSH_INSN(ret, location, putnil);
5427 }
5428
5429 ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
5430 PUSH_LABEL(ret, lend);
5431
5432 if (cast->else_clause != NULL) {
5433 if (!popped) PUSH_INSN(ret, *node_location, pop);
5434 PM_COMPILE((const pm_node_t *) cast->else_clause);
5435 }
5436
5437 PUSH_INSN(ret, *node_location, nop);
5438 PUSH_LABEL(ret, lcont);
5439
5440 if (popped) PUSH_INSN(ret, *node_location, pop);
5441 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
5442 PUSH_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
5443}
5444
5445static void
5446pm_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)
5447{
5448 const pm_parser_t *parser = scope_node->parser;
5449 const pm_statements_node_t *statements = cast->ensure_clause->statements;
5450
5451 pm_node_location_t location;
5452 if (statements != NULL) {
5453 location = PM_NODE_START_LOCATION(parser, statements);
5454 }
5455 else {
5456 location = *node_location;
5457 }
5458
5459 LABEL *lstart = NEW_LABEL(location.line);
5460 LABEL *lend = NEW_LABEL(location.line);
5461 LABEL *lcont = NEW_LABEL(location.line);
5462
5463 struct ensure_range er;
5465 struct ensure_range *erange;
5466
5467 DECL_ANCHOR(ensr);
5468 if (statements != NULL) {
5469 pm_compile_node(iseq, (const pm_node_t *) statements, ensr, true, scope_node);
5470 }
5471
5472 LINK_ELEMENT *last = ensr->last;
5473 bool last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave);
5474
5475 er.begin = lstart;
5476 er.end = lend;
5477 er.next = 0;
5478 push_ensure_entry(iseq, &enl, &er, (void *) cast->ensure_clause);
5479
5480 PUSH_LABEL(ret, lstart);
5481 if (cast->rescue_clause != NULL) {
5482 pm_compile_rescue(iseq, cast, node_location, ret, popped | last_leave, scope_node);
5483 }
5484 else if (cast->statements != NULL) {
5485 pm_compile_node(iseq, (const pm_node_t *) cast->statements, ret, popped | last_leave, scope_node);
5486 }
5487 else if (!(popped | last_leave)) {
5488 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
5489 }
5490
5491 PUSH_LABEL(ret, lend);
5492 PUSH_SEQ(ret, ensr);
5493 if (!popped && last_leave) PUSH_INSN(ret, *node_location, putnil);
5494 PUSH_LABEL(ret, lcont);
5495 if (last_leave) PUSH_INSN(ret, *node_location, pop);
5496
5497 pm_scope_node_t next_scope_node;
5498 pm_scope_node_init((const pm_node_t *) cast->ensure_clause, &next_scope_node, scope_node);
5499
5500 rb_iseq_t *child_iseq = NEW_CHILD_ISEQ(
5501 &next_scope_node,
5502 rb_str_concat(rb_str_new2("ensure in "), ISEQ_BODY(iseq)->location.label),
5503 ISEQ_TYPE_ENSURE,
5504 location.line
5505 );
5506
5507 pm_scope_node_destroy(&next_scope_node);
5508
5509 erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
5510 if (lstart->link.next != &lend->link) {
5511 while (erange) {
5512 PUSH_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end, child_iseq, lcont);
5513 erange = erange->next;
5514 }
5515 }
5516 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
5517}
5518
5523static inline bool
5524pm_opt_str_freeze_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
5525{
5526 return (
5527 !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) &&
5528 node->receiver != NULL &&
5529 PM_NODE_TYPE_P(node->receiver, PM_STRING_NODE) &&
5530 node->arguments == NULL &&
5531 node->block == NULL &&
5532 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
5533 );
5534}
5535
5540static void
5541pm_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)
5542{
5543 const pm_node_location_t location = PM_LOCATION_START_LOCATION(scope_node->parser, name_loc, node_id);
5544
5545 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
5546 ISEQ_BODY(iseq)->ic_size++;
5547 VALUE segments = rb_ary_new_from_args(1, name);
5548 PUSH_INSN1(ret, location, opt_getconstant_path, segments);
5549 }
5550 else {
5551 PUSH_INSN(ret, location, putnil);
5552 PUSH_INSN1(ret, location, putobject, Qtrue);
5553 PUSH_INSN1(ret, location, getconstant, name);
5554 }
5555}
5556
5561static VALUE
5562pm_constant_path_parts(const pm_node_t *node, const pm_scope_node_t *scope_node)
5563{
5564 VALUE parts = rb_ary_new();
5565
5566 while (true) {
5567 switch (PM_NODE_TYPE(node)) {
5568 case PM_CONSTANT_READ_NODE: {
5569 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
5570 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5571
5572 rb_ary_unshift(parts, name);
5573 return parts;
5574 }
5575 case PM_CONSTANT_PATH_NODE: {
5576 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
5577 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5578
5579 rb_ary_unshift(parts, name);
5580 if (cast->parent == NULL) {
5581 rb_ary_unshift(parts, ID2SYM(idNULL));
5582 return parts;
5583 }
5584
5585 node = cast->parent;
5586 break;
5587 }
5588 default:
5589 return Qnil;
5590 }
5591 }
5592}
5593
5599static void
5600pm_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)
5601{
5602 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
5603
5604 switch (PM_NODE_TYPE(node)) {
5605 case PM_CONSTANT_READ_NODE: {
5606 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
5607 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5608
5609 PUSH_INSN1(body, location, putobject, Qtrue);
5610 PUSH_INSN1(body, location, getconstant, name);
5611 break;
5612 }
5613 case PM_CONSTANT_PATH_NODE: {
5614 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
5615 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
5616
5617 if (cast->parent == NULL) {
5618 PUSH_INSN(body, location, pop);
5619 PUSH_INSN1(body, location, putobject, rb_cObject);
5620 PUSH_INSN1(body, location, putobject, Qtrue);
5621 PUSH_INSN1(body, location, getconstant, name);
5622 }
5623 else {
5624 pm_compile_constant_path(iseq, cast->parent, prefix, body, false, scope_node);
5625 PUSH_INSN1(body, location, putobject, Qfalse);
5626 PUSH_INSN1(body, location, getconstant, name);
5627 }
5628 break;
5629 }
5630 default:
5631 PM_COMPILE_INTO_ANCHOR(prefix, node);
5632 break;
5633 }
5634}
5635
5639static VALUE
5640pm_compile_shareable_constant_literal(rb_iseq_t *iseq, const pm_node_t *node, const pm_scope_node_t *scope_node)
5641{
5642 switch (PM_NODE_TYPE(node)) {
5643 case PM_TRUE_NODE:
5644 case PM_FALSE_NODE:
5645 case PM_NIL_NODE:
5646 case PM_SYMBOL_NODE:
5647 case PM_REGULAR_EXPRESSION_NODE:
5648 case PM_SOURCE_LINE_NODE:
5649 case PM_INTEGER_NODE:
5650 case PM_FLOAT_NODE:
5651 case PM_RATIONAL_NODE:
5652 case PM_IMAGINARY_NODE:
5653 case PM_SOURCE_ENCODING_NODE:
5654 return pm_static_literal_value(iseq, node, scope_node);
5655 case PM_STRING_NODE:
5656 return parse_static_literal_string(iseq, scope_node, node, &((const pm_string_node_t *) node)->unescaped);
5657 case PM_SOURCE_FILE_NODE:
5658 return pm_source_file_value((const pm_source_file_node_t *) node, scope_node);
5659 case PM_ARRAY_NODE: {
5660 const pm_array_node_t *cast = (const pm_array_node_t *) node;
5661 VALUE result = rb_ary_new_capa(cast->elements.size);
5662
5663 for (size_t index = 0; index < cast->elements.size; index++) {
5664 VALUE element = pm_compile_shareable_constant_literal(iseq, cast->elements.nodes[index], scope_node);
5665 if (element == Qundef) return Qundef;
5666
5667 rb_ary_push(result, element);
5668 }
5669
5670 return rb_ractor_make_shareable(result);
5671 }
5672 case PM_HASH_NODE: {
5673 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
5674 VALUE result = rb_hash_new_capa(cast->elements.size);
5675
5676 for (size_t index = 0; index < cast->elements.size; index++) {
5677 const pm_node_t *element = cast->elements.nodes[index];
5678 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) return Qundef;
5679
5680 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
5681
5682 VALUE key = pm_compile_shareable_constant_literal(iseq, assoc->key, scope_node);
5683 if (key == Qundef) return Qundef;
5684
5685 VALUE value = pm_compile_shareable_constant_literal(iseq, assoc->value, scope_node);
5686 if (value == Qundef) return Qundef;
5687
5688 rb_hash_aset(result, key, value);
5689 }
5690
5691 return rb_ractor_make_shareable(result);
5692 }
5693 default:
5694 return Qundef;
5695 }
5696}
5697
5702static void
5703pm_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)
5704{
5705 VALUE literal = pm_compile_shareable_constant_literal(iseq, node, scope_node);
5706 if (literal != Qundef) {
5707 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
5708 PUSH_INSN1(ret, location, putobject, literal);
5709 return;
5710 }
5711
5712 const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
5713 switch (PM_NODE_TYPE(node)) {
5714 case PM_ARRAY_NODE: {
5715 const pm_array_node_t *cast = (const pm_array_node_t *) node;
5716
5717 if (top) {
5718 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5719 }
5720
5721 for (size_t index = 0; index < cast->elements.size; index++) {
5722 pm_compile_shareable_constant_value(iseq, cast->elements.nodes[index], shareability, path, ret, scope_node, false);
5723 }
5724
5725 PUSH_INSN1(ret, location, newarray, INT2FIX(cast->elements.size));
5726
5727 if (top) {
5728 ID method_id = (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) ? rb_intern("make_shareable_copy") : rb_intern("make_shareable");
5729 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5730 }
5731
5732 return;
5733 }
5734 case PM_HASH_NODE: {
5735 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
5736
5737 if (top) {
5738 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5739 }
5740
5741 pm_compile_hash_elements(iseq, (const pm_node_t *) cast, &cast->elements, shareability, path, false, ret, scope_node);
5742
5743 if (top) {
5744 ID method_id = (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) ? rb_intern("make_shareable_copy") : rb_intern("make_shareable");
5745 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5746 }
5747
5748 return;
5749 }
5750 default: {
5751 DECL_ANCHOR(value_seq);
5752
5753 pm_compile_node(iseq, node, value_seq, false, scope_node);
5754 if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) {
5755 PUSH_SEND_WITH_FLAG(value_seq, location, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE));
5756 }
5757
5758 if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL) {
5759 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5760 PUSH_SEQ(ret, value_seq);
5761 PUSH_INSN1(ret, location, putobject, path);
5762 PUSH_SEND_WITH_FLAG(ret, location, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE));
5763 }
5764 else if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) {
5765 if (top) PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5766 PUSH_SEQ(ret, value_seq);
5767 if (top) PUSH_SEND_WITH_FLAG(ret, location, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5768 }
5769 else if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING) {
5770 if (top) PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5771 PUSH_SEQ(ret, value_seq);
5772 if (top) PUSH_SEND_WITH_FLAG(ret, location, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5773 }
5774
5775 break;
5776 }
5777 }
5778}
5779
5784static void
5785pm_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)
5786{
5787 const pm_node_location_t location = *node_location;
5788 ID name_id = pm_constant_id_lookup(scope_node, node->name);
5789
5790 if (shareability != 0) {
5791 pm_compile_shareable_constant_value(iseq, node->value, shareability, rb_id2str(name_id), ret, scope_node, true);
5792 }
5793 else {
5794 PM_COMPILE_NOT_POPPED(node->value);
5795 }
5796
5797 if (!popped) PUSH_INSN(ret, location, dup);
5798 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5799
5800 VALUE operand = ID2SYM(name_id);
5801 PUSH_INSN1(ret, location, setconstant, operand);
5802}
5803
5808static void
5809pm_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)
5810{
5811 const pm_node_location_t location = *node_location;
5812
5813 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5814 LABEL *end_label = NEW_LABEL(location.line);
5815
5816 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5817 if (!popped) PUSH_INSN(ret, location, dup);
5818
5819 PUSH_INSNL(ret, location, branchunless, end_label);
5820 if (!popped) PUSH_INSN(ret, location, pop);
5821
5822 if (shareability != 0) {
5823 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5824 }
5825 else {
5826 PM_COMPILE_NOT_POPPED(node->value);
5827 }
5828
5829 if (!popped) PUSH_INSN(ret, location, dup);
5830 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5831 PUSH_INSN1(ret, location, setconstant, name);
5832 PUSH_LABEL(ret, end_label);
5833}
5834
5839static void
5840pm_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)
5841{
5842 const pm_node_location_t location = *node_location;
5843 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5844
5845 LABEL *set_label = NEW_LABEL(location.line);
5846 LABEL *end_label = NEW_LABEL(location.line);
5847
5848 PUSH_INSN(ret, location, putnil);
5849 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), name, Qtrue);
5850 PUSH_INSNL(ret, location, branchunless, set_label);
5851
5852 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5853 if (!popped) PUSH_INSN(ret, location, dup);
5854
5855 PUSH_INSNL(ret, location, branchif, end_label);
5856 if (!popped) PUSH_INSN(ret, location, pop);
5857 PUSH_LABEL(ret, set_label);
5858
5859 if (shareability != 0) {
5860 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5861 }
5862 else {
5863 PM_COMPILE_NOT_POPPED(node->value);
5864 }
5865
5866 if (!popped) PUSH_INSN(ret, location, dup);
5867 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5868 PUSH_INSN1(ret, location, setconstant, name);
5869 PUSH_LABEL(ret, end_label);
5870}
5871
5876static void
5877pm_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)
5878{
5879 const pm_node_location_t location = *node_location;
5880
5881 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5882 ID method_id = pm_constant_id_lookup(scope_node, node->binary_operator);
5883
5884 pm_compile_constant_read(iseq, name, &node->name_loc, location.node_id, ret, scope_node);
5885
5886 if (shareability != 0) {
5887 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5888 }
5889 else {
5890 PM_COMPILE_NOT_POPPED(node->value);
5891 }
5892
5893 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5894 if (!popped) PUSH_INSN(ret, location, dup);
5895
5896 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5897 PUSH_INSN1(ret, location, setconstant, name);
5898}
5899
5904static VALUE
5905pm_constant_path_path(const pm_constant_path_node_t *node, const pm_scope_node_t *scope_node)
5906{
5907 VALUE parts = rb_ary_new();
5908 rb_ary_push(parts, rb_id2str(pm_constant_id_lookup(scope_node, node->name)));
5909
5910 const pm_node_t *current = node->parent;
5911 while (current != NULL && PM_NODE_TYPE_P(current, PM_CONSTANT_PATH_NODE)) {
5912 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) current;
5913 rb_ary_unshift(parts, rb_id2str(pm_constant_id_lookup(scope_node, cast->name)));
5914 current = cast->parent;
5915 }
5916
5917 if (current == NULL) {
5918 rb_ary_unshift(parts, rb_id2str(idNULL));
5919 }
5920 else if (PM_NODE_TYPE_P(current, PM_CONSTANT_READ_NODE)) {
5921 rb_ary_unshift(parts, rb_id2str(pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) current)->name)));
5922 }
5923 else {
5924 rb_ary_unshift(parts, rb_str_new_cstr("..."));
5925 }
5926
5927 return rb_ary_join(parts, rb_str_new_cstr("::"));
5928}
5929
5934static void
5935pm_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)
5936{
5937 const pm_node_location_t location = *node_location;
5938 const pm_constant_path_node_t *target = node->target;
5939 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
5940
5941 if (target->parent) {
5942 PM_COMPILE_NOT_POPPED((const pm_node_t *) target->parent);
5943 }
5944 else {
5945 PUSH_INSN1(ret, location, putobject, rb_cObject);
5946 }
5947
5948 if (shareability != 0) {
5949 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
5950 }
5951 else {
5952 PM_COMPILE_NOT_POPPED(node->value);
5953 }
5954
5955 if (!popped) {
5956 PUSH_INSN(ret, location, swap);
5957 PUSH_INSN1(ret, location, topn, INT2FIX(1));
5958 }
5959
5960 PUSH_INSN(ret, location, swap);
5961 PUSH_INSN1(ret, location, setconstant, name);
5962}
5963
5968static void
5969pm_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)
5970{
5971 const pm_node_location_t location = *node_location;
5972 const pm_constant_path_node_t *target = node->target;
5973
5974 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
5975 LABEL *lfin = NEW_LABEL(location.line);
5976
5977 if (target->parent) {
5978 PM_COMPILE_NOT_POPPED(target->parent);
5979 }
5980 else {
5981 PUSH_INSN1(ret, location, putobject, rb_cObject);
5982 }
5983
5984 PUSH_INSN(ret, location, dup);
5985 PUSH_INSN1(ret, location, putobject, Qtrue);
5986 PUSH_INSN1(ret, location, getconstant, name);
5987
5988 if (!popped) PUSH_INSN(ret, location, dup);
5989 PUSH_INSNL(ret, location, branchunless, lfin);
5990
5991 if (!popped) PUSH_INSN(ret, location, pop);
5992
5993 if (shareability != 0) {
5994 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
5995 }
5996 else {
5997 PM_COMPILE_NOT_POPPED(node->value);
5998 }
5999
6000 if (popped) {
6001 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6002 }
6003 else {
6004 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
6005 PUSH_INSN(ret, location, swap);
6006 }
6007
6008 PUSH_INSN1(ret, location, setconstant, name);
6009 PUSH_LABEL(ret, lfin);
6010
6011 if (!popped) PUSH_INSN(ret, location, swap);
6012 PUSH_INSN(ret, location, pop);
6013}
6014
6019static void
6020pm_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)
6021{
6022 const pm_node_location_t location = *node_location;
6023 const pm_constant_path_node_t *target = node->target;
6024
6025 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6026 LABEL *lassign = NEW_LABEL(location.line);
6027 LABEL *lfin = NEW_LABEL(location.line);
6028
6029 if (target->parent) {
6030 PM_COMPILE_NOT_POPPED(target->parent);
6031 }
6032 else {
6033 PUSH_INSN1(ret, location, putobject, rb_cObject);
6034 }
6035
6036 PUSH_INSN(ret, location, dup);
6037 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), name, Qtrue);
6038 PUSH_INSNL(ret, location, branchunless, lassign);
6039
6040 PUSH_INSN(ret, location, dup);
6041 PUSH_INSN1(ret, location, putobject, Qtrue);
6042 PUSH_INSN1(ret, location, getconstant, name);
6043
6044 if (!popped) PUSH_INSN(ret, location, dup);
6045 PUSH_INSNL(ret, location, branchif, lfin);
6046
6047 if (!popped) PUSH_INSN(ret, location, pop);
6048 PUSH_LABEL(ret, lassign);
6049
6050 if (shareability != 0) {
6051 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6052 }
6053 else {
6054 PM_COMPILE_NOT_POPPED(node->value);
6055 }
6056
6057 if (popped) {
6058 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6059 }
6060 else {
6061 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
6062 PUSH_INSN(ret, location, swap);
6063 }
6064
6065 PUSH_INSN1(ret, location, setconstant, name);
6066 PUSH_LABEL(ret, lfin);
6067
6068 if (!popped) PUSH_INSN(ret, location, swap);
6069 PUSH_INSN(ret, location, pop);
6070}
6071
6076static void
6077pm_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)
6078{
6079 const pm_node_location_t location = *node_location;
6080 const pm_constant_path_node_t *target = node->target;
6081
6082 ID method_id = pm_constant_id_lookup(scope_node, node->binary_operator);
6083 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
6084
6085 if (target->parent) {
6086 PM_COMPILE_NOT_POPPED(target->parent);
6087 }
6088 else {
6089 PUSH_INSN1(ret, location, putobject, rb_cObject);
6090 }
6091
6092 PUSH_INSN(ret, location, dup);
6093 PUSH_INSN1(ret, location, putobject, Qtrue);
6094 PUSH_INSN1(ret, location, getconstant, name);
6095
6096 if (shareability != 0) {
6097 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
6098 }
6099 else {
6100 PM_COMPILE_NOT_POPPED(node->value);
6101 }
6102
6103 PUSH_CALL(ret, location, method_id, INT2FIX(1));
6104 PUSH_INSN(ret, location, swap);
6105
6106 if (!popped) {
6107 PUSH_INSN1(ret, location, topn, INT2FIX(1));
6108 PUSH_INSN(ret, location, swap);
6109 }
6110
6111 PUSH_INSN1(ret, location, setconstant, name);
6112}
6113
6120#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))
6121
6126static inline void
6127pm_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)
6128{
6129 const pm_node_location_t location = *node_location;
6130 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
6131
6132 pm_constant_id_list_t *locals = &scope_node->locals;
6133 pm_parameters_node_t *parameters_node = NULL;
6134 pm_node_list_t *keywords_list = NULL;
6135 pm_node_list_t *optionals_list = NULL;
6136 pm_node_list_t *posts_list = NULL;
6137 pm_node_list_t *requireds_list = NULL;
6138 pm_node_list_t *block_locals = NULL;
6139 bool trailing_comma = false;
6140
6141 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE) || PM_NODE_TYPE_P(scope_node->ast_node, PM_MODULE_NODE)) {
6142 PUSH_TRACE(ret, RUBY_EVENT_CLASS);
6143 }
6144
6145 if (scope_node->parameters != NULL) {
6146 switch (PM_NODE_TYPE(scope_node->parameters)) {
6147 case PM_BLOCK_PARAMETERS_NODE: {
6148 pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) scope_node->parameters;
6149 parameters_node = cast->parameters;
6150 block_locals = &cast->locals;
6151
6152 if (parameters_node) {
6153 if (parameters_node->rest && PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE)) {
6154 trailing_comma = true;
6155 }
6156 }
6157 break;
6158 }
6159 case PM_PARAMETERS_NODE: {
6160 parameters_node = (pm_parameters_node_t *) scope_node->parameters;
6161 break;
6162 }
6163 case PM_NUMBERED_PARAMETERS_NODE: {
6164 uint32_t maximum = ((const pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
6165 body->param.lead_num = maximum;
6166 body->param.flags.ambiguous_param0 = maximum == 1;
6167 break;
6168 }
6169 case PM_IT_PARAMETERS_NODE:
6170 body->param.lead_num = 1;
6171 body->param.flags.ambiguous_param0 = true;
6172 break;
6173 default:
6174 rb_bug("Unexpected node type for parameters: %s", pm_node_type_to_str(PM_NODE_TYPE(scope_node->parameters)));
6175 }
6176 }
6177
6178 struct rb_iseq_param_keyword *keyword = NULL;
6179
6180 if (parameters_node) {
6181 optionals_list = &parameters_node->optionals;
6182 requireds_list = &parameters_node->requireds;
6183 keywords_list = &parameters_node->keywords;
6184 posts_list = &parameters_node->posts;
6185 }
6186 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))) {
6187 body->param.opt_num = 0;
6188 }
6189 else {
6190 body->param.lead_num = 0;
6191 body->param.opt_num = 0;
6192 }
6193
6194 //********STEP 1**********
6195 // Goal: calculate the table size for the locals, accounting for
6196 // hidden variables and multi target nodes
6197 size_t locals_size = locals->size;
6198
6199 // Index lookup table buffer size is only the number of the locals
6200 st_table *index_lookup_table = st_init_numtable();
6201
6202 int table_size = (int) locals_size;
6203
6204 // For nodes have a hidden iteration variable. We add that to the local
6205 // table size here.
6206 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) table_size++;
6207
6208 if (keywords_list && keywords_list->size) {
6209 table_size++;
6210 }
6211
6212 if (requireds_list) {
6213 for (size_t i = 0; i < requireds_list->size; i++) {
6214 // For each MultiTargetNode, we're going to have one
6215 // additional anonymous local not represented in the locals table
6216 // We want to account for this in our table size
6217 pm_node_t *required = requireds_list->nodes[i];
6218 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
6219 table_size++;
6220 }
6221 else if (PM_NODE_TYPE_P(required, PM_REQUIRED_PARAMETER_NODE)) {
6222 if (PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6223 table_size++;
6224 }
6225 }
6226 }
6227 }
6228
6229 // If we have the `it` implicit local variable, we need to account for
6230 // it in the local table size.
6231 if (scope_node->parameters != NULL && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
6232 table_size++;
6233 }
6234
6235 // Ensure there is enough room in the local table for any
6236 // parameters that have been repeated
6237 // ex: def underscore_parameters(_, _ = 1, _ = 2); _; end
6238 // ^^^^^^^^^^^^
6239 if (optionals_list && optionals_list->size) {
6240 for (size_t i = 0; i < optionals_list->size; i++) {
6241 pm_node_t * node = optionals_list->nodes[i];
6242 if (PM_NODE_FLAG_P(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6243 table_size++;
6244 }
6245 }
6246 }
6247
6248 // If we have an anonymous "rest" node, we'll need to increase the local
6249 // table size to take it in to account.
6250 // def m(foo, *, bar)
6251 // ^
6252 if (parameters_node) {
6253 if (parameters_node->rest) {
6254 if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
6255 if (!((const pm_rest_parameter_node_t *) parameters_node->rest)->name || PM_NODE_FLAG_P(parameters_node->rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6256 table_size++;
6257 }
6258 }
6259 }
6260
6261 // def foo(_, **_); _; end
6262 // ^^^
6263 if (parameters_node->keyword_rest) {
6264 // def foo(...); end
6265 // ^^^
6266 // When we have a `...` as the keyword_rest, it's a forwarding_parameter_node and
6267 // we need to leave space for 4 locals: *, **, &, ...
6268 if (PM_NODE_TYPE_P(parameters_node->keyword_rest, PM_FORWARDING_PARAMETER_NODE)) {
6269 // Only optimize specifically methods like this: `foo(...)`
6270 if (requireds_list->size == 0 && optionals_list->size == 0 && keywords_list->size == 0) {
6271 ISEQ_BODY(iseq)->param.flags.use_block = TRUE;
6272 ISEQ_BODY(iseq)->param.flags.forwardable = TRUE;
6273 table_size += 1;
6274 }
6275 else {
6276 table_size += 4;
6277 }
6278 }
6279 else {
6280 const pm_keyword_rest_parameter_node_t *kw_rest = (const pm_keyword_rest_parameter_node_t *) parameters_node->keyword_rest;
6281
6282 // If it's anonymous or repeated, then we need to allocate stack space
6283 if (!kw_rest->name || PM_NODE_FLAG_P(kw_rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6284 table_size++;
6285 }
6286 }
6287 }
6288 }
6289
6290 if (posts_list) {
6291 for (size_t i = 0; i < posts_list->size; i++) {
6292 // For each MultiTargetNode, we're going to have one
6293 // additional anonymous local not represented in the locals table
6294 // We want to account for this in our table size
6295 pm_node_t *required = posts_list->nodes[i];
6296 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE) || PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6297 table_size++;
6298 }
6299 }
6300 }
6301
6302 if (keywords_list && keywords_list->size) {
6303 for (size_t i = 0; i < keywords_list->size; i++) {
6304 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6305 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6306 table_size++;
6307 }
6308 }
6309 }
6310
6311 if (parameters_node && parameters_node->block) {
6312 const pm_block_parameter_node_t *block_node = (const pm_block_parameter_node_t *) parameters_node->block;
6313
6314 if (PM_NODE_FLAG_P(block_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER) || !block_node->name) {
6315 table_size++;
6316 }
6317 }
6318
6319 // We can create local_table_for_iseq with the correct size
6320 VALUE idtmp = 0;
6321 rb_ast_id_table_t *local_table_for_iseq = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
6322 local_table_for_iseq->size = table_size;
6323
6324 //********END OF STEP 1**********
6325
6326 //********STEP 2**********
6327 // Goal: populate iv index table as well as local table, keeping the
6328 // layout of the local table consistent with the layout of the
6329 // stack when calling the method
6330 //
6331 // Do a first pass on all of the parameters, setting their values in
6332 // the local_table_for_iseq, _except_ for Multis who get a hidden
6333 // variable in this step, and will get their names inserted in step 3
6334
6335 // local_index is a cursor that keeps track of the current
6336 // index into local_table_for_iseq. The local table is actually a list,
6337 // and the order of that list must match the order of the items pushed
6338 // on the stack. We need to take in to account things pushed on the
6339 // stack that _might not have a name_ (for example array destructuring).
6340 // This index helps us know which item we're dealing with and also give
6341 // those anonymous items temporary names (as below)
6342 int local_index = 0;
6343
6344 // Here we figure out local table indices and insert them in to the
6345 // index lookup table and local tables.
6346 //
6347 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6348 // ^^^^^^^^^^^^^
6349 if (requireds_list && requireds_list->size) {
6350 for (size_t i = 0; i < requireds_list->size; i++, local_index++) {
6351 ID local;
6352
6353 // For each MultiTargetNode, we're going to have one additional
6354 // anonymous local not represented in the locals table. We want
6355 // to account for this in our table size.
6356 pm_node_t *required = requireds_list->nodes[i];
6357
6358 switch (PM_NODE_TYPE(required)) {
6359 case PM_MULTI_TARGET_NODE: {
6360 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6361 // ^^^^^^^^^^
6362 local = rb_make_temporary_id(local_index);
6363 local_table_for_iseq->ids[local_index] = local;
6364 break;
6365 }
6366 case PM_REQUIRED_PARAMETER_NODE: {
6367 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6368 // ^
6369 const pm_required_parameter_node_t *param = (const pm_required_parameter_node_t *) required;
6370
6371 if (PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6372 ID local = pm_constant_id_lookup(scope_node, param->name);
6373 local_table_for_iseq->ids[local_index] = local;
6374 }
6375 else {
6376 pm_insert_local_index(param->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6377 }
6378
6379 break;
6380 }
6381 default:
6382 rb_bug("Unsupported node in requireds in parameters %s", pm_node_type_to_str(PM_NODE_TYPE(required)));
6383 }
6384 }
6385
6386 body->param.lead_num = (int) requireds_list->size;
6387 body->param.flags.has_lead = true;
6388 }
6389
6390 if (scope_node->parameters != NULL && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
6391 ID local = rb_make_temporary_id(local_index);
6392 local_table_for_iseq->ids[local_index++] = local;
6393 }
6394
6395 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6396 // ^^^^^
6397 if (optionals_list && optionals_list->size) {
6398 body->param.opt_num = (int) optionals_list->size;
6399 body->param.flags.has_opt = true;
6400
6401 for (size_t i = 0; i < optionals_list->size; i++, local_index++) {
6402 pm_node_t * node = optionals_list->nodes[i];
6403 pm_constant_id_t name = ((const pm_optional_parameter_node_t *) node)->name;
6404
6405 if (PM_NODE_FLAG_P(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6406 ID local = pm_constant_id_lookup(scope_node, name);
6407 local_table_for_iseq->ids[local_index] = local;
6408 }
6409 else {
6410 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6411 }
6412 }
6413 }
6414
6415 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6416 // ^^
6417 if (parameters_node && parameters_node->rest) {
6418 body->param.rest_start = local_index;
6419
6420 // If there's a trailing comma, we'll have an implicit rest node,
6421 // and we don't want it to impact the rest variables on param
6422 if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
6423 body->param.flags.has_rest = true;
6424 RUBY_ASSERT(body->param.rest_start != -1);
6425
6426 pm_constant_id_t name = ((const pm_rest_parameter_node_t *) parameters_node->rest)->name;
6427
6428 if (name) {
6429 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6430 // ^^
6431 if (PM_NODE_FLAG_P(parameters_node->rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6432 ID local = pm_constant_id_lookup(scope_node, name);
6433 local_table_for_iseq->ids[local_index] = local;
6434 }
6435 else {
6436 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6437 }
6438 }
6439 else {
6440 // def foo(a, (b, *c, d), e = 1, *, g, (h, *i, j), k:, l: 1, **m, &n)
6441 // ^
6442 body->param.flags.anon_rest = true;
6443 pm_insert_local_special(idMULT, local_index, index_lookup_table, local_table_for_iseq);
6444 }
6445
6446 local_index++;
6447 }
6448 }
6449
6450 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6451 // ^^^^^^^^^^^^^
6452 if (posts_list && posts_list->size) {
6453 body->param.post_num = (int) posts_list->size;
6454 body->param.post_start = local_index;
6455 body->param.flags.has_post = true;
6456
6457 for (size_t i = 0; i < posts_list->size; i++, local_index++) {
6458 ID local;
6459
6460 // For each MultiTargetNode, we're going to have one additional
6461 // anonymous local not represented in the locals table. We want
6462 // to account for this in our table size.
6463 const pm_node_t *post_node = posts_list->nodes[i];
6464
6465 switch (PM_NODE_TYPE(post_node)) {
6466 case PM_MULTI_TARGET_NODE: {
6467 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6468 // ^^^^^^^^^^
6469 local = rb_make_temporary_id(local_index);
6470 local_table_for_iseq->ids[local_index] = local;
6471 break;
6472 }
6473 case PM_REQUIRED_PARAMETER_NODE: {
6474 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6475 // ^
6476 const pm_required_parameter_node_t *param = (const pm_required_parameter_node_t *) post_node;
6477
6478 if (PM_NODE_FLAG_P(param, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6479 ID local = pm_constant_id_lookup(scope_node, param->name);
6480 local_table_for_iseq->ids[local_index] = local;
6481 }
6482 else {
6483 pm_insert_local_index(param->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6484 }
6485 break;
6486 }
6487 default:
6488 rb_bug("Unsupported node in posts in parameters %s", pm_node_type_to_str(PM_NODE_TYPE(post_node)));
6489 }
6490 }
6491 }
6492
6493 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6494 // ^^^^^^^^
6495 // Keywords create an internal variable on the parse tree
6496 if (keywords_list && keywords_list->size) {
6497 keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6498 keyword->num = (int) keywords_list->size;
6499
6500 const VALUE default_values = rb_ary_hidden_new(1);
6501 const VALUE complex_mark = rb_str_tmp_new(0);
6502
6503 for (size_t i = 0; i < keywords_list->size; i++) {
6504 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6505 pm_constant_id_t name;
6506
6507 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6508 // ^^
6509 if (PM_NODE_TYPE_P(keyword_parameter_node, PM_REQUIRED_KEYWORD_PARAMETER_NODE)) {
6510 name = ((const pm_required_keyword_parameter_node_t *) keyword_parameter_node)->name;
6511 keyword->required_num++;
6512 ID local = pm_constant_id_lookup(scope_node, name);
6513
6514 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6515 local_table_for_iseq->ids[local_index] = local;
6516 }
6517 else {
6518 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6519 }
6520 local_index++;
6521 }
6522 }
6523
6524 for (size_t i = 0; i < keywords_list->size; i++) {
6525 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6526 pm_constant_id_t name;
6527
6528 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6529 // ^^^^
6530 if (PM_NODE_TYPE_P(keyword_parameter_node, PM_OPTIONAL_KEYWORD_PARAMETER_NODE)) {
6531 const pm_optional_keyword_parameter_node_t *cast = ((const pm_optional_keyword_parameter_node_t *) keyword_parameter_node);
6532
6533 pm_node_t *value = cast->value;
6534 name = cast->name;
6535
6536 if (PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL) && !PM_CONTAINER_P(value)) {
6537 rb_ary_push(default_values, pm_static_literal_value(iseq, value, scope_node));
6538 }
6539 else {
6540 rb_ary_push(default_values, complex_mark);
6541 }
6542
6543 ID local = pm_constant_id_lookup(scope_node, name);
6544 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6545 local_table_for_iseq->ids[local_index] = local;
6546 }
6547 else {
6548 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6549 }
6550 local_index++;
6551 }
6552
6553 }
6554
6555 if (RARRAY_LEN(default_values)) {
6556 VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
6557
6558 for (int i = 0; i < RARRAY_LEN(default_values); i++) {
6559 VALUE dv = RARRAY_AREF(default_values, i);
6560 if (dv == complex_mark) dv = Qundef;
6561 RB_OBJ_WRITE(iseq, &dvs[i], dv);
6562 }
6563
6564 keyword->default_values = dvs;
6565 }
6566
6567 // Hidden local for keyword arguments
6568 keyword->bits_start = local_index;
6569 ID local = rb_make_temporary_id(local_index);
6570 local_table_for_iseq->ids[local_index] = local;
6571 local_index++;
6572
6573 body->param.keyword = keyword;
6574 body->param.flags.has_kw = true;
6575 }
6576
6577 if (body->type == ISEQ_TYPE_BLOCK && local_index == 1 && requireds_list && requireds_list->size == 1 && !trailing_comma) {
6578 body->param.flags.ambiguous_param0 = true;
6579 }
6580
6581 if (parameters_node) {
6582 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6583 // ^^^
6584 if (parameters_node->keyword_rest) {
6585 switch (PM_NODE_TYPE(parameters_node->keyword_rest)) {
6586 case PM_NO_KEYWORDS_PARAMETER_NODE: {
6587 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **nil, &n)
6588 // ^^^^^
6589 body->param.flags.accepts_no_kwarg = true;
6590 break;
6591 }
6592 case PM_KEYWORD_REST_PARAMETER_NODE: {
6593 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6594 // ^^^
6595 const pm_keyword_rest_parameter_node_t *kw_rest_node = (const pm_keyword_rest_parameter_node_t *) parameters_node->keyword_rest;
6596 if (!body->param.flags.has_kw) {
6597 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6598 }
6599
6600 keyword->rest_start = local_index;
6601 body->param.flags.has_kwrest = true;
6602
6603 pm_constant_id_t constant_id = kw_rest_node->name;
6604 if (constant_id) {
6605 if (PM_NODE_FLAG_P(kw_rest_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6606 ID local = pm_constant_id_lookup(scope_node, constant_id);
6607 local_table_for_iseq->ids[local_index] = local;
6608 }
6609 else {
6610 pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6611 }
6612 }
6613 else {
6614 body->param.flags.anon_kwrest = true;
6615 pm_insert_local_special(idPow, local_index, index_lookup_table, local_table_for_iseq);
6616 }
6617
6618 local_index++;
6619 break;
6620 }
6621 case PM_FORWARDING_PARAMETER_NODE: {
6622 // def foo(...)
6623 // ^^^
6624 if (!ISEQ_BODY(iseq)->param.flags.forwardable) {
6625 // Add the anonymous *
6626 body->param.rest_start = local_index;
6627 body->param.flags.has_rest = true;
6628 body->param.flags.anon_rest = true;
6629 pm_insert_local_special(idMULT, local_index++, index_lookup_table, local_table_for_iseq);
6630
6631 // Add the anonymous **
6632 RUBY_ASSERT(!body->param.flags.has_kw);
6633 body->param.flags.has_kw = false;
6634 body->param.flags.has_kwrest = true;
6635 body->param.flags.anon_kwrest = true;
6636 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
6637 keyword->rest_start = local_index;
6638 pm_insert_local_special(idPow, local_index++, index_lookup_table, local_table_for_iseq);
6639
6640 // Add the anonymous &
6641 body->param.block_start = local_index;
6642 body->param.flags.has_block = true;
6643 pm_insert_local_special(idAnd, local_index++, index_lookup_table, local_table_for_iseq);
6644 }
6645
6646 // Add the ...
6647 pm_insert_local_special(idDot3, local_index++, index_lookup_table, local_table_for_iseq);
6648 break;
6649 }
6650 default:
6651 rb_bug("node type %s not expected as keyword_rest", pm_node_type_to_str(PM_NODE_TYPE(parameters_node->keyword_rest)));
6652 }
6653 }
6654
6655 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6656 // ^^
6657 if (parameters_node->block) {
6658 body->param.block_start = local_index;
6659 body->param.flags.has_block = true;
6660 iseq_set_use_block(iseq);
6661
6662 pm_constant_id_t name = ((const pm_block_parameter_node_t *) parameters_node->block)->name;
6663
6664 if (name) {
6665 if (PM_NODE_FLAG_P(parameters_node->block, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
6666 ID local = pm_constant_id_lookup(scope_node, name);
6667 local_table_for_iseq->ids[local_index] = local;
6668 }
6669 else {
6670 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6671 }
6672 }
6673 else {
6674 pm_insert_local_special(idAnd, local_index, index_lookup_table, local_table_for_iseq);
6675 }
6676
6677 local_index++;
6678 }
6679 }
6680
6681 //********END OF STEP 2**********
6682 // The local table is now consistent with expected
6683 // stack layout
6684
6685 // If there's only one required element in the parameters
6686 // CRuby needs to recognize it as an ambiguous parameter
6687
6688 //********STEP 3**********
6689 // Goal: fill in the names of the parameters in MultiTargetNodes
6690 //
6691 // Go through requireds again to set the multis
6692
6693 if (requireds_list && requireds_list->size) {
6694 for (size_t i = 0; i < requireds_list->size; i++) {
6695 // For each MultiTargetNode, we're going to have one
6696 // additional anonymous local not represented in the locals table
6697 // We want to account for this in our table size
6698 const pm_node_t *required = requireds_list->nodes[i];
6699
6700 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
6701 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);
6702 }
6703 }
6704 }
6705
6706 // Go through posts again to set the multis
6707 if (posts_list && posts_list->size) {
6708 for (size_t i = 0; i < posts_list->size; i++) {
6709 // For each MultiTargetNode, we're going to have one
6710 // additional anonymous local not represented in the locals table
6711 // We want to account for this in our table size
6712 const pm_node_t *post = posts_list->nodes[i];
6713
6714 if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
6715 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);
6716 }
6717 }
6718 }
6719
6720 // Set any anonymous locals for the for node
6721 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
6722 if (PM_NODE_TYPE_P(((const pm_for_node_t *) scope_node->ast_node)->index, PM_LOCAL_VARIABLE_TARGET_NODE)) {
6723 body->param.lead_num++;
6724 }
6725 else {
6726 body->param.rest_start = local_index;
6727 body->param.flags.has_rest = true;
6728 }
6729
6730 ID local = rb_make_temporary_id(local_index);
6731 local_table_for_iseq->ids[local_index] = local;
6732 local_index++;
6733 }
6734
6735 // Fill in any NumberedParameters, if they exist
6736 if (scope_node->parameters && PM_NODE_TYPE_P(scope_node->parameters, PM_NUMBERED_PARAMETERS_NODE)) {
6737 int maximum = ((const pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
6738 RUBY_ASSERT(0 < maximum && maximum <= 9);
6739 for (int i = 0; i < maximum; i++, local_index++) {
6740 const uint8_t param_name[] = { '_', '1' + i };
6741 pm_constant_id_t constant_id = pm_constant_pool_find(&scope_node->parser->constant_pool, param_name, 2);
6742 RUBY_ASSERT(constant_id && "parser should fill in any gaps in numbered parameters");
6743 pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6744 }
6745 body->param.lead_num = maximum;
6746 body->param.flags.has_lead = true;
6747 }
6748
6749 // Fill in the anonymous `it` parameter, if it exists
6750 if (scope_node->parameters && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
6751 body->param.lead_num = 1;
6752 body->param.flags.has_lead = true;
6753 }
6754
6755 //********END OF STEP 3**********
6756
6757 //********STEP 4**********
6758 // Goal: fill in the method body locals
6759 // To be explicit, these are the non-parameter locals
6760 // We fill in the block_locals, if they exist
6761 // lambda { |x; y| y }
6762 // ^
6763 if (block_locals && block_locals->size) {
6764 for (size_t i = 0; i < block_locals->size; i++, local_index++) {
6765 pm_constant_id_t constant_id = ((const pm_block_local_variable_node_t *) block_locals->nodes[i])->name;
6766 pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
6767 }
6768 }
6769
6770 // Fill in any locals we missed
6771 if (scope_node->locals.size) {
6772 for (size_t i = 0; i < scope_node->locals.size; i++) {
6773 pm_constant_id_t constant_id = locals->ids[i];
6774 if (constant_id) {
6775 struct pm_local_table_insert_ctx ctx;
6776 ctx.scope_node = scope_node;
6777 ctx.local_table_for_iseq = local_table_for_iseq;
6778 ctx.local_index = local_index;
6779
6780 st_update(index_lookup_table, (st_data_t)constant_id, pm_local_table_insert_func, (st_data_t)&ctx);
6781
6782 local_index = ctx.local_index;
6783 }
6784 }
6785 }
6786
6787 //********END OF STEP 4**********
6788
6789 // We set the index_lookup_table on the scope node so we can
6790 // refer to the parameters correctly
6791 if (scope_node->index_lookup_table) {
6792 st_free_table(scope_node->index_lookup_table);
6793 }
6794 scope_node->index_lookup_table = index_lookup_table;
6795 iseq_calc_param_size(iseq);
6796
6797 if (ISEQ_BODY(iseq)->param.flags.forwardable) {
6798 // We're treating `...` as a parameter so that frame
6799 // pushing won't clobber it.
6800 ISEQ_BODY(iseq)->param.size += 1;
6801 }
6802
6803 // FIXME: args?
6804 iseq_set_local_table(iseq, local_table_for_iseq, 0);
6805 iseq_set_parameters_lvar_state(iseq);
6806
6807 scope_node->local_table_for_iseq_size = local_table_for_iseq->size;
6808
6809 if (keyword != NULL) {
6810 size_t keyword_start_index = keyword->bits_start - keyword->num;
6811 keyword->table = (ID *)&ISEQ_BODY(iseq)->local_table[keyword_start_index];
6812 }
6813
6814 //********STEP 5************
6815 // Goal: compile anything that needed to be compiled
6816 if (optionals_list && optionals_list->size) {
6817 LABEL **opt_table = (LABEL **) ALLOC_N(VALUE, optionals_list->size + 1);
6818 LABEL *label;
6819
6820 // TODO: Should we make an api for NEW_LABEL where you can pass
6821 // a pointer to the label it should fill out? We already
6822 // have a list of labels allocated above so it seems wasteful
6823 // to do the copies.
6824 for (size_t i = 0; i < optionals_list->size; i++) {
6825 label = NEW_LABEL(location.line);
6826 opt_table[i] = label;
6827 PUSH_LABEL(ret, label);
6828 pm_node_t *optional_node = optionals_list->nodes[i];
6829 PM_COMPILE_NOT_POPPED(optional_node);
6830 }
6831
6832 // Set the last label
6833 label = NEW_LABEL(location.line);
6834 opt_table[optionals_list->size] = label;
6835 PUSH_LABEL(ret, label);
6836
6837 body->param.opt_table = (const VALUE *) opt_table;
6838 }
6839
6840 if (keywords_list && keywords_list->size) {
6841 size_t optional_index = 0;
6842 for (size_t i = 0; i < keywords_list->size; i++) {
6843 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
6844 pm_constant_id_t name;
6845
6846 switch (PM_NODE_TYPE(keyword_parameter_node)) {
6847 case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: {
6848 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6849 // ^^^^
6850 const pm_optional_keyword_parameter_node_t *cast = ((const pm_optional_keyword_parameter_node_t *) keyword_parameter_node);
6851
6852 pm_node_t *value = cast->value;
6853 name = cast->name;
6854
6855 if (!PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL) || PM_CONTAINER_P(value)) {
6856 LABEL *end_label = NEW_LABEL(location.line);
6857
6858 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, name, 0);
6859 int kw_bits_idx = table_size - body->param.keyword->bits_start;
6860 PUSH_INSN2(ret, location, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(optional_index));
6861 PUSH_INSNL(ret, location, branchif, end_label);
6862 PM_COMPILE(value);
6863 PUSH_SETLOCAL(ret, location, index.index, index.level);
6864 PUSH_LABEL(ret, end_label);
6865 }
6866 optional_index++;
6867 break;
6868 }
6869 case PM_REQUIRED_KEYWORD_PARAMETER_NODE:
6870 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
6871 // ^^
6872 break;
6873 default:
6874 rb_bug("Unexpected keyword parameter node type %s", pm_node_type_to_str(PM_NODE_TYPE(keyword_parameter_node)));
6875 }
6876 }
6877 }
6878
6879 if (requireds_list && requireds_list->size) {
6880 for (size_t i = 0; i < requireds_list->size; i++) {
6881 // For each MultiTargetNode, we're going to have one additional
6882 // anonymous local not represented in the locals table. We want
6883 // to account for this in our table size.
6884 const pm_node_t *required = requireds_list->nodes[i];
6885
6886 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
6887 PUSH_GETLOCAL(ret, location, table_size - (int)i, 0);
6888 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) required, ret, scope_node);
6889 }
6890 }
6891 }
6892
6893 if (posts_list && posts_list->size) {
6894 for (size_t i = 0; i < posts_list->size; i++) {
6895 // For each MultiTargetNode, we're going to have one additional
6896 // anonymous local not represented in the locals table. We want
6897 // to account for this in our table size.
6898 const pm_node_t *post = posts_list->nodes[i];
6899
6900 if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
6901 PUSH_GETLOCAL(ret, location, table_size - body->param.post_start - (int) i, 0);
6902 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) post, ret, scope_node);
6903 }
6904 }
6905 }
6906
6907 switch (body->type) {
6908 case ISEQ_TYPE_PLAIN: {
6909 RUBY_ASSERT(PM_NODE_TYPE_P(scope_node->ast_node, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE));
6910
6912 pm_compile_regexp_dynamic(iseq, (const pm_node_t *) cast, &cast->parts, &location, ret, popped, scope_node);
6913
6914 break;
6915 }
6916 case ISEQ_TYPE_BLOCK: {
6917 LABEL *start = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(0);
6918 LABEL *end = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(0);
6919 const pm_node_location_t block_location = { .line = body->location.first_lineno, .node_id = scope_node->ast_node->node_id };
6920
6921 start->rescued = LABEL_RESCUE_BEG;
6922 end->rescued = LABEL_RESCUE_END;
6923
6924 // For nodes automatically assign the iteration variable to whatever
6925 // index variable. We need to handle that write here because it has
6926 // to happen in the context of the block. Note that this happens
6927 // before the B_CALL tracepoint event.
6928 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
6929 pm_compile_for_node_index(iseq, ((const pm_for_node_t *) scope_node->ast_node)->index, ret, scope_node);
6930 }
6931
6932 PUSH_TRACE(ret, RUBY_EVENT_B_CALL);
6933 PUSH_INSN(ret, block_location, nop);
6934 PUSH_LABEL(ret, start);
6935
6936 if (scope_node->body != NULL) {
6937 switch (PM_NODE_TYPE(scope_node->ast_node)) {
6938 case PM_POST_EXECUTION_NODE: {
6939 const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) scope_node->ast_node;
6940 PUSH_INSN1(ret, block_location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6941
6942 // We create another ScopeNode from the statements within the PostExecutionNode
6943 pm_scope_node_t next_scope_node;
6944 pm_scope_node_init((const pm_node_t *) cast->statements, &next_scope_node, scope_node);
6945
6946 const rb_iseq_t *block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(body->parent_iseq), ISEQ_TYPE_BLOCK, location.line);
6947 pm_scope_node_destroy(&next_scope_node);
6948
6949 PUSH_CALL_WITH_BLOCK(ret, block_location, id_core_set_postexe, INT2FIX(0), block);
6950 break;
6951 }
6952 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
6954 pm_compile_regexp_dynamic(iseq, (const pm_node_t *) cast, &cast->parts, &location, ret, popped, scope_node);
6955 break;
6956 }
6957 default:
6958 pm_compile_node(iseq, scope_node->body, ret, popped, scope_node);
6959 break;
6960 }
6961 }
6962 else {
6963 PUSH_INSN(ret, block_location, putnil);
6964 }
6965
6966 PUSH_LABEL(ret, end);
6967 PUSH_TRACE(ret, RUBY_EVENT_B_RETURN);
6968 ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
6969
6970 /* wide range catch handler must put at last */
6971 PUSH_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, NULL, start);
6972 PUSH_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, NULL, end);
6973 break;
6974 }
6975 case ISEQ_TYPE_ENSURE: {
6976 const pm_node_location_t statements_location = (scope_node->body != NULL ? PM_NODE_START_LOCATION(scope_node->parser, scope_node->body) : location);
6977 iseq_set_exception_local_table(iseq);
6978
6979 if (scope_node->body != NULL) {
6980 PM_COMPILE_POPPED((const pm_node_t *) scope_node->body);
6981 }
6982
6983 PUSH_GETLOCAL(ret, statements_location, 1, 0);
6984 PUSH_INSN1(ret, statements_location, throw, INT2FIX(0));
6985 return;
6986 }
6987 case ISEQ_TYPE_METHOD: {
6988 ISEQ_COMPILE_DATA(iseq)->root_node = (const void *) scope_node->body;
6989 PUSH_TRACE(ret, RUBY_EVENT_CALL);
6990
6991 if (scope_node->body) {
6992 PM_COMPILE((const pm_node_t *) scope_node->body);
6993 }
6994 else {
6995 PUSH_INSN(ret, location, putnil);
6996 }
6997
6998 ISEQ_COMPILE_DATA(iseq)->root_node = (const void *) scope_node->body;
6999 PUSH_TRACE(ret, RUBY_EVENT_RETURN);
7000
7001 ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
7002 break;
7003 }
7004 case ISEQ_TYPE_RESCUE: {
7005 iseq_set_exception_local_table(iseq);
7006 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_RESCUE_MODIFIER_NODE)) {
7007 LABEL *lab = NEW_LABEL(location.line);
7008 LABEL *rescue_end = NEW_LABEL(location.line);
7009 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
7010 PUSH_INSN1(ret, location, putobject, rb_eStandardError);
7011 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
7012 PUSH_INSNL(ret, location, branchif, lab);
7013 PUSH_INSNL(ret, location, jump, rescue_end);
7014 PUSH_LABEL(ret, lab);
7015 PUSH_TRACE(ret, RUBY_EVENT_RESCUE);
7016 PM_COMPILE((const pm_node_t *) scope_node->body);
7017 PUSH_INSN(ret, location, leave);
7018 PUSH_LABEL(ret, rescue_end);
7019 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
7020 }
7021 else {
7022 PM_COMPILE((const pm_node_t *) scope_node->ast_node);
7023 }
7024 PUSH_INSN1(ret, location, throw, INT2FIX(0));
7025
7026 return;
7027 }
7028 default:
7029 if (scope_node->body) {
7030 PM_COMPILE((const pm_node_t *) scope_node->body);
7031 }
7032 else {
7033 PUSH_INSN(ret, location, putnil);
7034 }
7035 break;
7036 }
7037
7038 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE) || PM_NODE_TYPE_P(scope_node->ast_node, PM_MODULE_NODE)) {
7039 const pm_node_location_t end_location = PM_NODE_END_LOCATION(scope_node->parser, scope_node->ast_node);
7040 PUSH_TRACE(ret, RUBY_EVENT_END);
7041 ISEQ_COMPILE_DATA(iseq)->last_line = end_location.line;
7042 }
7043
7044 if (!PM_NODE_TYPE_P(scope_node->ast_node, PM_ENSURE_NODE)) {
7045 const pm_node_location_t location = { .line = ISEQ_COMPILE_DATA(iseq)->last_line, .node_id = scope_node->ast_node->node_id };
7046 PUSH_INSN(ret, location, leave);
7047 }
7048}
7049
7050static inline void
7051pm_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)
7052{
7053 // alias $foo $bar
7054 // ^^^^^^^^^^^^^^^
7055 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7056
7057 {
7058 const pm_location_t *name_loc = &node->new_name->location;
7059 VALUE operand = ID2SYM(rb_intern3((const char *) name_loc->start, name_loc->end - name_loc->start, scope_node->encoding));
7060 PUSH_INSN1(ret, *location, putobject, operand);
7061 }
7062
7063 {
7064 const pm_location_t *name_loc = &node->old_name->location;
7065 VALUE operand = ID2SYM(rb_intern3((const char *) name_loc->start, name_loc->end - name_loc->start, scope_node->encoding));
7066 PUSH_INSN1(ret, *location, putobject, operand);
7067 }
7068
7069 PUSH_SEND(ret, *location, id_core_set_variable_alias, INT2FIX(2));
7070 if (popped) PUSH_INSN(ret, *location, pop);
7071}
7072
7073static inline void
7074pm_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)
7075{
7076 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7077 PUSH_INSN1(ret, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
7078 PM_COMPILE_NOT_POPPED(node->new_name);
7079 PM_COMPILE_NOT_POPPED(node->old_name);
7080
7081 PUSH_SEND(ret, *location, id_core_set_method_alias, INT2FIX(3));
7082 if (popped) PUSH_INSN(ret, *location, pop);
7083}
7084
7085static inline void
7086pm_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)
7087{
7088 LABEL *end_label = NEW_LABEL(location->line);
7089
7090 PM_COMPILE_NOT_POPPED(node->left);
7091 if (!popped) PUSH_INSN(ret, *location, dup);
7092 PUSH_INSNL(ret, *location, branchunless, end_label);
7093
7094 if (!popped) PUSH_INSN(ret, *location, pop);
7095 PM_COMPILE(node->right);
7096 PUSH_LABEL(ret, end_label);
7097}
7098
7099static inline void
7100pm_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)
7101{
7102 // If every node in the array is static, then we can compile the entire
7103 // array now instead of later.
7104 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
7105 // We're only going to compile this node if it's not popped. If it
7106 // is popped, then we know we don't need to do anything since it's
7107 // statically known.
7108 if (!popped) {
7109 if (elements->size) {
7110 VALUE value = pm_static_literal_value(iseq, node, scope_node);
7111 PUSH_INSN1(ret, *location, duparray, value);
7112 }
7113 else {
7114 PUSH_INSN1(ret, *location, newarray, INT2FIX(0));
7115 }
7116 }
7117 return;
7118 }
7119
7120 // Here since we know there are possible side-effects inside the
7121 // array contents, we're going to build it entirely at runtime.
7122 // We'll do this by pushing all of the elements onto the stack and
7123 // then combining them with newarray.
7124 //
7125 // If this array is popped, then this serves only to ensure we enact
7126 // all side-effects (like method calls) that are contained within
7127 // the array contents.
7128 //
7129 // We treat all sequences of non-splat elements as their
7130 // own arrays, followed by a newarray, and then continually
7131 // concat the arrays with the SplatNode nodes.
7132 const int max_new_array_size = 0x100;
7133 const unsigned int min_tmp_array_size = 0x40;
7134
7135 int new_array_size = 0;
7136 bool first_chunk = true;
7137
7138 // This is an optimization wherein we keep track of whether or not
7139 // the previous element was a static literal. If it was, then we do
7140 // not attempt to check if we have a subarray that can be optimized.
7141 // If it was not, then we do check.
7142 bool static_literal = false;
7143
7144 // Either create a new array, or push to the existing array.
7145#define FLUSH_CHUNK \
7146 if (new_array_size) { \
7147 if (first_chunk) PUSH_INSN1(ret, *location, newarray, INT2FIX(new_array_size)); \
7148 else PUSH_INSN1(ret, *location, pushtoarray, INT2FIX(new_array_size)); \
7149 first_chunk = false; \
7150 new_array_size = 0; \
7151 }
7152
7153 for (size_t index = 0; index < elements->size; index++) {
7154 const pm_node_t *element = elements->nodes[index];
7155
7156 if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) {
7157 FLUSH_CHUNK;
7158
7159 const pm_splat_node_t *splat_element = (const pm_splat_node_t *) element;
7160 if (splat_element->expression) {
7161 PM_COMPILE_NOT_POPPED(splat_element->expression);
7162 }
7163 else {
7164 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
7165 PUSH_GETLOCAL(ret, *location, index.index, index.level);
7166 }
7167
7168 if (first_chunk) {
7169 // If this is the first element of the array then we
7170 // need to splatarray the elements into the list.
7171 PUSH_INSN1(ret, *location, splatarray, Qtrue);
7172 first_chunk = false;
7173 }
7174 else {
7175 PUSH_INSN(ret, *location, concattoarray);
7176 }
7177
7178 static_literal = false;
7179 }
7180 else if (PM_NODE_TYPE_P(element, PM_KEYWORD_HASH_NODE)) {
7181 if (new_array_size == 0 && first_chunk) {
7182 PUSH_INSN1(ret, *location, newarray, INT2FIX(0));
7183 first_chunk = false;
7184 }
7185 else {
7186 FLUSH_CHUNK;
7187 }
7188
7189 // If we get here, then this is the last element of the
7190 // array/arguments, because it cannot be followed by
7191 // anything else without a syntax error. This looks like:
7192 //
7193 // [foo, bar, baz: qux]
7194 // ^^^^^^^^
7195 //
7196 // [foo, bar, **baz]
7197 // ^^^^^
7198 //
7199 const pm_keyword_hash_node_t *keyword_hash = (const pm_keyword_hash_node_t *) element;
7200 pm_compile_hash_elements(iseq, element, &keyword_hash->elements, 0, Qundef, false, ret, scope_node);
7201
7202 // This boolean controls the manner in which we push the
7203 // hash onto the array. If it's all keyword splats, then we
7204 // can use the very specialized pushtoarraykwsplat
7205 // instruction to check if it's empty before we push it.
7206 size_t splats = 0;
7207 while (splats < keyword_hash->elements.size && PM_NODE_TYPE_P(keyword_hash->elements.nodes[splats], PM_ASSOC_SPLAT_NODE)) splats++;
7208
7209 if (keyword_hash->elements.size == splats) {
7210 PUSH_INSN(ret, *location, pushtoarraykwsplat);
7211 }
7212 else {
7213 new_array_size++;
7214 }
7215 }
7216 else if (
7217 PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL) &&
7218 !PM_CONTAINER_P(element) &&
7219 !static_literal &&
7220 ((index + min_tmp_array_size) < elements->size)
7221 ) {
7222 // If we have a static literal, then there's the potential
7223 // to group a bunch of them together with a literal array
7224 // and then concat them together.
7225 size_t right_index = index + 1;
7226 while (
7227 right_index < elements->size &&
7228 PM_NODE_FLAG_P(elements->nodes[right_index], PM_NODE_FLAG_STATIC_LITERAL) &&
7229 !PM_CONTAINER_P(elements->nodes[right_index])
7230 ) right_index++;
7231
7232 size_t tmp_array_size = right_index - index;
7233 if (tmp_array_size >= min_tmp_array_size) {
7234 VALUE tmp_array = rb_ary_hidden_new(tmp_array_size);
7235
7236 // Create the temporary array.
7237 for (; tmp_array_size; tmp_array_size--)
7238 rb_ary_push(tmp_array, pm_static_literal_value(iseq, elements->nodes[index++], scope_node));
7239
7240 index--; // about to be incremented by for loop
7241 OBJ_FREEZE(tmp_array);
7242
7243 // Emit the optimized code.
7244 FLUSH_CHUNK;
7245 if (first_chunk) {
7246 PUSH_INSN1(ret, *location, duparray, tmp_array);
7247 first_chunk = false;
7248 }
7249 else {
7250 PUSH_INSN1(ret, *location, putobject, tmp_array);
7251 PUSH_INSN(ret, *location, concattoarray);
7252 }
7253 }
7254 else {
7255 PM_COMPILE_NOT_POPPED(element);
7256 if (++new_array_size >= max_new_array_size) FLUSH_CHUNK;
7257 static_literal = true;
7258 }
7259 } else {
7260 PM_COMPILE_NOT_POPPED(element);
7261 if (++new_array_size >= max_new_array_size) FLUSH_CHUNK;
7262 static_literal = false;
7263 }
7264 }
7265
7266 FLUSH_CHUNK;
7267 if (popped) PUSH_INSN(ret, *location, pop);
7268
7269#undef FLUSH_CHUNK
7270}
7271
7272static inline void
7273pm_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)
7274{
7275 unsigned long throw_flag = 0;
7276
7277 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
7278 /* while/until */
7279 LABEL *splabel = NEW_LABEL(0);
7280 PUSH_LABEL(ret, splabel);
7281 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
7282
7283 if (node->arguments != NULL) {
7284 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
7285 }
7286 else {
7287 PUSH_INSN(ret, *location, putnil);
7288 }
7289
7290 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
7291 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
7292 PUSH_ADJUST_RESTORE(ret, splabel);
7293 if (!popped) PUSH_INSN(ret, *location, putnil);
7294 }
7295 else {
7296 const rb_iseq_t *ip = iseq;
7297
7298 while (ip) {
7299 if (!ISEQ_COMPILE_DATA(ip)) {
7300 ip = 0;
7301 break;
7302 }
7303
7304 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
7305 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
7306 }
7307 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
7308 throw_flag = 0;
7309 }
7310 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
7311 COMPILE_ERROR(iseq, location->line, "Invalid break");
7312 return;
7313 }
7314 else {
7315 ip = ISEQ_BODY(ip)->parent_iseq;
7316 continue;
7317 }
7318
7319 /* escape from block */
7320 if (node->arguments != NULL) {
7321 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
7322 }
7323 else {
7324 PUSH_INSN(ret, *location, putnil);
7325 }
7326
7327 PUSH_INSN1(ret, *location, throw, INT2FIX(throw_flag | TAG_BREAK));
7328 if (popped) PUSH_INSN(ret, *location, pop);
7329
7330 return;
7331 }
7332
7333 COMPILE_ERROR(iseq, location->line, "Invalid break");
7334 }
7335}
7336
7337static inline void
7338pm_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)
7339{
7340 ID method_id = pm_constant_id_lookup(scope_node, node->name);
7341
7342 const pm_location_t *message_loc = &node->message_loc;
7343 if (message_loc->start == NULL) message_loc = &node->base.location;
7344
7345 const pm_node_location_t location = PM_LOCATION_START_LOCATION(scope_node->parser, message_loc, node->base.node_id);
7346 const char *builtin_func;
7347
7348 if (UNLIKELY(iseq_has_builtin_function_table(iseq)) && (builtin_func = pm_iseq_builtin_function_name(scope_node, node->receiver, method_id)) != NULL) {
7349 pm_compile_builtin_function_call(iseq, ret, scope_node, node, &location, popped, ISEQ_COMPILE_DATA(iseq)->current_block, builtin_func);
7350 return;
7351 }
7352
7353 LABEL *start = NEW_LABEL(location.line);
7354 if (node->block) PUSH_LABEL(ret, start);
7355
7356 switch (method_id) {
7357 case idUMinus: {
7358 if (pm_opt_str_freeze_p(iseq, node)) {
7359 VALUE value = parse_static_literal_string(iseq, scope_node, node->receiver, &((const pm_string_node_t * ) node->receiver)->unescaped);
7360 const struct rb_callinfo *callinfo = new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE);
7361 PUSH_INSN2(ret, location, opt_str_uminus, value, callinfo);
7362 if (popped) PUSH_INSN(ret, location, pop);
7363 return;
7364 }
7365 break;
7366 }
7367 case idFreeze: {
7368 if (pm_opt_str_freeze_p(iseq, node)) {
7369 VALUE value = parse_static_literal_string(iseq, scope_node, node->receiver, &((const pm_string_node_t * ) node->receiver)->unescaped);
7370 const struct rb_callinfo *callinfo = new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE);
7371 PUSH_INSN2(ret, location, opt_str_freeze, value, callinfo);
7372 if (popped) PUSH_INSN(ret, location, pop);
7373 return;
7374 }
7375 break;
7376 }
7377 }
7378
7379 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && !popped) {
7380 PUSH_INSN(ret, location, putnil);
7381 }
7382
7383 if (node->receiver == NULL) {
7384 PUSH_INSN(ret, location, putself);
7385 }
7386 else {
7387 if (method_id == idCall && PM_NODE_TYPE_P(node->receiver, PM_LOCAL_VARIABLE_READ_NODE)) {
7388 const pm_local_variable_read_node_t *read_node_cast = (const pm_local_variable_read_node_t *) node->receiver;
7389 uint32_t node_id = node->receiver->node_id;
7390 int idx, level;
7391
7392 if (iseq_block_param_id_p(iseq, pm_constant_id_lookup(scope_node, read_node_cast->name), &idx, &level)) {
7393 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)));
7394 }
7395 else {
7396 PM_COMPILE_NOT_POPPED(node->receiver);
7397 }
7398 }
7399 else {
7400 PM_COMPILE_NOT_POPPED(node->receiver);
7401 }
7402 }
7403
7404 pm_compile_call(iseq, node, ret, popped, scope_node, method_id, start);
7405 return;
7406}
7407
7408static inline void
7409pm_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)
7410{
7411 int flag = 0;
7412
7413 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) {
7414 flag = VM_CALL_FCALL;
7415 }
7416
7417 PM_COMPILE_NOT_POPPED(node->receiver);
7418
7419 LABEL *safe_label = NULL;
7420 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
7421 safe_label = NEW_LABEL(location->line);
7422 PUSH_INSN(ret, *location, dup);
7423 PUSH_INSNL(ret, *location, branchnil, safe_label);
7424 }
7425
7426 PUSH_INSN(ret, *location, dup);
7427
7428 ID id_read_name = pm_constant_id_lookup(scope_node, node->read_name);
7429 PUSH_SEND_WITH_FLAG(ret, *location, id_read_name, INT2FIX(0), INT2FIX(flag));
7430
7431 PM_COMPILE_NOT_POPPED(node->value);
7432 ID id_operator = pm_constant_id_lookup(scope_node, node->binary_operator);
7433 PUSH_SEND(ret, *location, id_operator, INT2FIX(1));
7434
7435 if (!popped) {
7436 PUSH_INSN(ret, *location, swap);
7437 PUSH_INSN1(ret, *location, topn, INT2FIX(1));
7438 }
7439
7440 ID id_write_name = pm_constant_id_lookup(scope_node, node->write_name);
7441 PUSH_SEND_WITH_FLAG(ret, *location, id_write_name, INT2FIX(1), INT2FIX(flag));
7442
7443 if (safe_label != NULL && popped) PUSH_LABEL(ret, safe_label);
7444 PUSH_INSN(ret, *location, pop);
7445 if (safe_label != NULL && !popped) PUSH_LABEL(ret, safe_label);
7446}
7447
7464static VALUE
7465pm_compile_case_node_dispatch(rb_iseq_t *iseq, VALUE dispatch, const pm_node_t *node, LABEL *label, const pm_scope_node_t *scope_node)
7466{
7467 VALUE key = Qundef;
7468
7469 switch (PM_NODE_TYPE(node)) {
7470 case PM_FLOAT_NODE: {
7471 key = pm_static_literal_value(iseq, node, scope_node);
7472 double intptr;
7473
7474 if (modf(RFLOAT_VALUE(key), &intptr) == 0.0) {
7475 key = (FIXABLE(intptr) ? LONG2FIX((long) intptr) : rb_dbl2big(intptr));
7476 }
7477
7478 break;
7479 }
7480 case PM_FALSE_NODE:
7481 case PM_INTEGER_NODE:
7482 case PM_NIL_NODE:
7483 case PM_SOURCE_FILE_NODE:
7484 case PM_SOURCE_LINE_NODE:
7485 case PM_SYMBOL_NODE:
7486 case PM_TRUE_NODE:
7487 key = pm_static_literal_value(iseq, node, scope_node);
7488 break;
7489 case PM_STRING_NODE: {
7490 const pm_string_node_t *cast = (const pm_string_node_t *) node;
7491 key = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
7492 break;
7493 }
7494 default:
7495 return Qundef;
7496 }
7497
7498 if (NIL_P(rb_hash_lookup(dispatch, key))) {
7499 rb_hash_aset(dispatch, key, ((VALUE) label) | 1);
7500 }
7501
7502 return dispatch;
7503}
7504
7508static inline void
7509pm_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)
7510{
7511 const pm_parser_t *parser = scope_node->parser;
7512 const pm_node_location_t location = *node_location;
7513 const pm_node_list_t *conditions = &cast->conditions;
7514
7515 // This is the anchor that we will compile the conditions of the various
7516 // `when` nodes into. If a match is found, they will need to jump into
7517 // the body_seq anchor to the correct spot.
7518 DECL_ANCHOR(cond_seq);
7519
7520 // This is the anchor that we will compile the bodies of the various
7521 // `when` nodes into. We'll make sure that the clauses that are compiled
7522 // jump into the correct spots within this anchor.
7523 DECL_ANCHOR(body_seq);
7524
7525 // This is the label where all of the when clauses will jump to if they
7526 // have matched and are done executing their bodies.
7527 LABEL *end_label = NEW_LABEL(location.line);
7528
7529 // If we have a predicate on this case statement, then it's going to
7530 // compare all of the various when clauses to the predicate. If we
7531 // don't, then it's basically an if-elsif-else chain.
7532 if (cast->predicate == NULL) {
7533 // Establish branch coverage for the case node.
7534 VALUE branches = Qfalse;
7535 rb_code_location_t case_location = { 0 };
7536 int branch_id = 0;
7537
7538 if (PM_BRANCH_COVERAGE_P(iseq)) {
7539 case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
7540 branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case");
7541 }
7542
7543 // Loop through each clauses in the case node and compile each of
7544 // the conditions within them into cond_seq. If they match, they
7545 // should jump into their respective bodies in body_seq.
7546 for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) {
7547 const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index];
7548 const pm_node_list_t *conditions = &clause->conditions;
7549
7550 int clause_lineno = pm_node_line_number(parser, (const pm_node_t *) clause);
7551 LABEL *label = NEW_LABEL(clause_lineno);
7552 PUSH_LABEL(body_seq, label);
7553
7554 // Establish branch coverage for the when clause.
7555 if (PM_BRANCH_COVERAGE_P(iseq)) {
7556 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));
7557 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches);
7558 }
7559
7560 if (clause->statements != NULL) {
7561 pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node);
7562 }
7563 else if (!popped) {
7564 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
7565 }
7566
7567 PUSH_INSNL(body_seq, location, jump, end_label);
7568
7569 // Compile each of the conditions for the when clause into the
7570 // cond_seq. Each one should have a unique condition and should
7571 // jump to the subsequent one if it doesn't match.
7572 for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) {
7573 const pm_node_t *condition = conditions->nodes[condition_index];
7574
7575 if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) {
7576 pm_node_location_t cond_location = PM_NODE_START_LOCATION(parser, condition);
7577 PUSH_INSN(cond_seq, cond_location, putnil);
7578 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7579 PUSH_INSN1(cond_seq, cond_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
7580 PUSH_INSNL(cond_seq, cond_location, branchif, label);
7581 }
7582 else {
7583 LABEL *next_label = NEW_LABEL(pm_node_line_number(parser, condition));
7584 pm_compile_branch_condition(iseq, cond_seq, condition, label, next_label, false, scope_node);
7585 PUSH_LABEL(cond_seq, next_label);
7586 }
7587 }
7588 }
7589
7590 // Establish branch coverage for the else clause (implicit or
7591 // explicit).
7592 if (PM_BRANCH_COVERAGE_P(iseq)) {
7593 rb_code_location_t branch_location;
7594
7595 if (cast->else_clause == NULL) {
7596 branch_location = case_location;
7597 } else if (cast->else_clause->statements == NULL) {
7598 branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->else_clause);
7599 } else {
7600 branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->else_clause->statements);
7601 }
7602
7603 add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
7604 }
7605
7606 // Compile the else clause if there is one.
7607 if (cast->else_clause != NULL) {
7608 pm_compile_node(iseq, (const pm_node_t *) cast->else_clause, cond_seq, popped, scope_node);
7609 }
7610 else if (!popped) {
7611 PUSH_SYNTHETIC_PUTNIL(cond_seq, iseq);
7612 }
7613
7614 // Finally, jump to the end label if none of the other conditions
7615 // have matched.
7616 PUSH_INSNL(cond_seq, location, jump, end_label);
7617 PUSH_SEQ(ret, cond_seq);
7618 }
7619 else {
7620 // Establish branch coverage for the case node.
7621 VALUE branches = Qfalse;
7622 rb_code_location_t case_location = { 0 };
7623 int branch_id = 0;
7624
7625 if (PM_BRANCH_COVERAGE_P(iseq)) {
7626 case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
7627 branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case");
7628 }
7629
7630 // This is the label where everything will fall into if none of the
7631 // conditions matched.
7632 LABEL *else_label = NEW_LABEL(location.line);
7633
7634 // It's possible for us to speed up the case node by using a
7635 // dispatch hash. This is a hash that maps the conditions of the
7636 // various when clauses to the labels of their bodies. If we can
7637 // compile the conditions into a hash key, then we can use a hash
7638 // lookup to jump directly to the correct when clause body.
7639 VALUE dispatch = Qundef;
7640 if (ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
7641 dispatch = rb_hash_new();
7642 RHASH_TBL_RAW(dispatch)->type = &cdhash_type;
7643 }
7644
7645 // We're going to loop through each of the conditions in the case
7646 // node and compile each of their contents into both the cond_seq
7647 // and the body_seq. Each condition will use its own label to jump
7648 // from its conditions into its body.
7649 //
7650 // Note that none of the code in the loop below should be adding
7651 // anything to ret, as we're going to be laying out the entire case
7652 // node instructions later.
7653 for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) {
7654 const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index];
7655 pm_node_location_t clause_location = PM_NODE_START_LOCATION(parser, (const pm_node_t *) clause);
7656
7657 const pm_node_list_t *conditions = &clause->conditions;
7658 LABEL *label = NEW_LABEL(clause_location.line);
7659
7660 // Compile each of the conditions for the when clause into the
7661 // cond_seq. Each one should have a unique comparison that then
7662 // jumps into the body if it matches.
7663 for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) {
7664 const pm_node_t *condition = conditions->nodes[condition_index];
7665 const pm_node_location_t condition_location = PM_NODE_START_LOCATION(parser, condition);
7666
7667 // If we haven't already abandoned the optimization, then
7668 // we're going to try to compile the condition into the
7669 // dispatch hash.
7670 if (dispatch != Qundef) {
7671 dispatch = pm_compile_case_node_dispatch(iseq, dispatch, condition, label, scope_node);
7672 }
7673
7674 if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) {
7675 PUSH_INSN(cond_seq, condition_location, dup);
7676 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7677 PUSH_INSN1(cond_seq, condition_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
7678 }
7679 else {
7680 if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
7681 const pm_string_node_t *string = (const pm_string_node_t *) condition;
7682 VALUE value = parse_static_literal_string(iseq, scope_node, condition, &string->unescaped);
7683 PUSH_INSN1(cond_seq, condition_location, putobject, value);
7684 }
7685 else {
7686 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
7687 }
7688
7689 PUSH_INSN1(cond_seq, condition_location, topn, INT2FIX(1));
7690 PUSH_SEND_WITH_FLAG(cond_seq, condition_location, idEqq, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
7691 }
7692
7693 PUSH_INSNL(cond_seq, condition_location, branchif, label);
7694 }
7695
7696 // Now, add the label to the body and compile the body of the
7697 // when clause. This involves popping the predicate, compiling
7698 // the statements to be executed, and then compiling a jump to
7699 // the end of the case node.
7700 PUSH_LABEL(body_seq, label);
7701 PUSH_INSN(body_seq, clause_location, pop);
7702
7703 // Establish branch coverage for the when clause.
7704 if (PM_BRANCH_COVERAGE_P(iseq)) {
7705 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));
7706 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches);
7707 }
7708
7709 if (clause->statements != NULL) {
7710 pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node);
7711 }
7712 else if (!popped) {
7713 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
7714 }
7715
7716 PUSH_INSNL(body_seq, clause_location, jump, end_label);
7717 }
7718
7719 // Now that we have compiled the conditions and the bodies of the
7720 // various when clauses, we can compile the predicate, lay out the
7721 // conditions, compile the fallback subsequent if there is one, and
7722 // finally put in the bodies of the when clauses.
7723 PM_COMPILE_NOT_POPPED(cast->predicate);
7724
7725 // If we have a dispatch hash, then we'll use it here to create the
7726 // optimization.
7727 if (dispatch != Qundef) {
7728 PUSH_INSN(ret, location, dup);
7729 PUSH_INSN2(ret, location, opt_case_dispatch, dispatch, else_label);
7730 LABEL_REF(else_label);
7731 }
7732
7733 PUSH_SEQ(ret, cond_seq);
7734
7735 // Compile either the explicit else clause or an implicit else
7736 // clause.
7737 PUSH_LABEL(ret, else_label);
7738
7739 if (cast->else_clause != NULL) {
7740 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));
7741 PUSH_INSN(ret, else_location, pop);
7742
7743 // Establish branch coverage for the else clause.
7744 if (PM_BRANCH_COVERAGE_P(iseq)) {
7745 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));
7746 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
7747 }
7748
7749 PM_COMPILE((const pm_node_t *) cast->else_clause);
7750 PUSH_INSNL(ret, else_location, jump, end_label);
7751 }
7752 else {
7753 PUSH_INSN(ret, location, pop);
7754
7755 // Establish branch coverage for the implicit else clause.
7756 if (PM_BRANCH_COVERAGE_P(iseq)) {
7757 add_trace_branch_coverage(iseq, ret, &case_location, case_location.beg_pos.column, branch_id, "else", branches);
7758 }
7759
7760 if (!popped) PUSH_INSN(ret, location, putnil);
7761 PUSH_INSNL(ret, location, jump, end_label);
7762 }
7763 }
7764
7765 PUSH_SEQ(ret, body_seq);
7766 PUSH_LABEL(ret, end_label);
7767}
7768
7769static inline void
7770pm_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)
7771{
7772 // This is the anchor that we will compile the bodies of the various
7773 // `in` nodes into. We'll make sure that the patterns that are compiled
7774 // jump into the correct spots within this anchor.
7775 DECL_ANCHOR(body_seq);
7776
7777 // This is the anchor that we will compile the patterns of the various
7778 // `in` nodes into. If a match is found, they will need to jump into the
7779 // body_seq anchor to the correct spot.
7780 DECL_ANCHOR(cond_seq);
7781
7782 // This label is used to indicate the end of the entire node. It is
7783 // jumped to after the entire stack is cleaned up.
7784 LABEL *end_label = NEW_LABEL(location->line);
7785
7786 // This label is used as the fallback for the case match. If no match is
7787 // found, then we jump to this label. This is either an `else` clause or
7788 // an error handler.
7789 LABEL *else_label = NEW_LABEL(location->line);
7790
7791 // We're going to use this to uniquely identify each branch so that we
7792 // can track coverage information.
7793 rb_code_location_t case_location = { 0 };
7794 VALUE branches = Qfalse;
7795 int branch_id = 0;
7796
7797 if (PM_BRANCH_COVERAGE_P(iseq)) {
7798 case_location = pm_code_location(scope_node, (const pm_node_t *) node);
7799 branches = decl_branch_base(iseq, PTR2NUM(node), &case_location, "case");
7800 }
7801
7802 // If there is only one pattern, then the behavior changes a bit. It
7803 // effectively gets treated as a match required node (this is how it is
7804 // represented in the other parser).
7805 bool in_single_pattern = node->else_clause == NULL && node->conditions.size == 1;
7806
7807 // First, we're going to push a bunch of stuff onto the stack that is
7808 // going to serve as our scratch space.
7809 if (in_single_pattern) {
7810 PUSH_INSN(ret, *location, putnil); // key error key
7811 PUSH_INSN(ret, *location, putnil); // key error matchee
7812 PUSH_INSN1(ret, *location, putobject, Qfalse); // key error?
7813 PUSH_INSN(ret, *location, putnil); // error string
7814 }
7815
7816 // Now we're going to compile the value to match against.
7817 PUSH_INSN(ret, *location, putnil); // deconstruct cache
7818 PM_COMPILE_NOT_POPPED(node->predicate);
7819
7820 // Next, we'll loop through every in clause and compile its body into
7821 // the body_seq anchor and its pattern into the cond_seq anchor. We'll
7822 // make sure the pattern knows how to jump correctly into the body if it
7823 // finds a match.
7824 for (size_t index = 0; index < node->conditions.size; index++) {
7825 const pm_node_t *condition = node->conditions.nodes[index];
7826 RUBY_ASSERT(PM_NODE_TYPE_P(condition, PM_IN_NODE));
7827
7828 const pm_in_node_t *in_node = (const pm_in_node_t *) condition;
7829 const pm_node_location_t in_location = PM_NODE_START_LOCATION(scope_node->parser, in_node);
7830 const pm_node_location_t pattern_location = PM_NODE_START_LOCATION(scope_node->parser, in_node->pattern);
7831
7832 if (branch_id) {
7833 PUSH_INSN(body_seq, in_location, putnil);
7834 }
7835
7836 LABEL *body_label = NEW_LABEL(in_location.line);
7837 PUSH_LABEL(body_seq, body_label);
7838 PUSH_INSN1(body_seq, in_location, adjuststack, INT2FIX(in_single_pattern ? 6 : 2));
7839
7840 // Establish branch coverage for the in clause.
7841 if (PM_BRANCH_COVERAGE_P(iseq)) {
7842 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));
7843 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "in", branches);
7844 }
7845
7846 if (in_node->statements != NULL) {
7847 PM_COMPILE_INTO_ANCHOR(body_seq, (const pm_node_t *) in_node->statements);
7848 }
7849 else if (!popped) {
7850 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
7851 }
7852
7853 PUSH_INSNL(body_seq, in_location, jump, end_label);
7854 LABEL *next_pattern_label = NEW_LABEL(pattern_location.line);
7855
7856 PUSH_INSN(cond_seq, pattern_location, dup);
7857 pm_compile_pattern(iseq, scope_node, in_node->pattern, cond_seq, body_label, next_pattern_label, in_single_pattern, false, true, 2);
7858 PUSH_LABEL(cond_seq, next_pattern_label);
7859 LABEL_UNREMOVABLE(next_pattern_label);
7860 }
7861
7862 if (node->else_clause != NULL) {
7863 // If we have an `else` clause, then this becomes our fallback (and
7864 // there is no need to compile in code to potentially raise an
7865 // error).
7866 const pm_else_node_t *else_node = node->else_clause;
7867
7868 PUSH_LABEL(cond_seq, else_label);
7869 PUSH_INSN(cond_seq, *location, pop);
7870 PUSH_INSN(cond_seq, *location, pop);
7871
7872 // Establish branch coverage for the else clause.
7873 if (PM_BRANCH_COVERAGE_P(iseq)) {
7874 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));
7875 add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
7876 }
7877
7878 PM_COMPILE_INTO_ANCHOR(cond_seq, (const pm_node_t *) else_node);
7879 PUSH_INSNL(cond_seq, *location, jump, end_label);
7880 PUSH_INSN(cond_seq, *location, putnil);
7881 if (popped) PUSH_INSN(cond_seq, *location, putnil);
7882 }
7883 else {
7884 // Otherwise, if we do not have an `else` clause, we will compile in
7885 // the code to handle raising an appropriate error.
7886 PUSH_LABEL(cond_seq, else_label);
7887
7888 // Establish branch coverage for the implicit else clause.
7889 add_trace_branch_coverage(iseq, cond_seq, &case_location, case_location.beg_pos.column, branch_id, "else", branches);
7890
7891 if (in_single_pattern) {
7892 pm_compile_pattern_error_handler(iseq, scope_node, (const pm_node_t *) node, cond_seq, end_label, popped);
7893 }
7894 else {
7895 PUSH_INSN1(cond_seq, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7896 PUSH_INSN1(cond_seq, *location, putobject, rb_eNoMatchingPatternError);
7897 PUSH_INSN1(cond_seq, *location, topn, INT2FIX(2));
7898 PUSH_SEND(cond_seq, *location, id_core_raise, INT2FIX(2));
7899
7900 PUSH_INSN1(cond_seq, *location, adjuststack, INT2FIX(3));
7901 if (!popped) PUSH_INSN(cond_seq, *location, putnil);
7902 PUSH_INSNL(cond_seq, *location, jump, end_label);
7903 PUSH_INSN1(cond_seq, *location, dupn, INT2FIX(1));
7904 if (popped) PUSH_INSN(cond_seq, *location, putnil);
7905 }
7906 }
7907
7908 // At the end of all of this compilation, we will add the code for the
7909 // conditions first, then the various bodies, then mark the end of the
7910 // entire sequence with the end label.
7911 PUSH_SEQ(ret, cond_seq);
7912 PUSH_SEQ(ret, body_seq);
7913 PUSH_LABEL(ret, end_label);
7914}
7915
7916static inline void
7917pm_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)
7918{
7919 const rb_iseq_t *block = NULL;
7920 const rb_iseq_t *previous_block = NULL;
7921 LABEL *retry_label = NULL;
7922 LABEL *retry_end_l = NULL;
7923
7924 if (node->block != NULL) {
7925 previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
7926 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
7927
7928 retry_label = NEW_LABEL(location->line);
7929 retry_end_l = NEW_LABEL(location->line);
7930
7931 PUSH_LABEL(ret, retry_label);
7932 }
7933 else {
7934 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
7935 }
7936
7937 PUSH_INSN(ret, *location, putself);
7938 int flag = VM_CALL_ZSUPER | VM_CALL_SUPER | VM_CALL_FCALL;
7939
7940 if (node->block != NULL) {
7941 pm_scope_node_t next_scope_node;
7942 pm_scope_node_init((const pm_node_t *) node->block, &next_scope_node, scope_node);
7943
7944 ISEQ_COMPILE_DATA(iseq)->current_block = block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location->line);
7945 pm_scope_node_destroy(&next_scope_node);
7946 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) block);
7947 }
7948
7949 DECL_ANCHOR(args);
7950
7951 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
7952 const rb_iseq_t *local_iseq = body->local_iseq;
7953 const struct rb_iseq_constant_body *const local_body = ISEQ_BODY(local_iseq);
7954
7955 int argc = 0;
7956 int depth = get_lvar_level(iseq);
7957
7958 if (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
7959 flag |= VM_CALL_FORWARDING;
7960 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_DOT3, 0);
7961 PUSH_GETLOCAL(ret, *location, mult_local.index, mult_local.level);
7962
7963 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, 0, flag, NULL, block != NULL);
7964 PUSH_INSN2(ret, *location, invokesuperforward, callinfo, block);
7965
7966 if (popped) PUSH_INSN(ret, *location, pop);
7967 if (node->block) {
7968 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
7969 }
7970 return;
7971 }
7972
7973 if (local_body->param.flags.has_lead) {
7974 /* required arguments */
7975 for (int i = 0; i < local_body->param.lead_num; i++) {
7976 int idx = local_body->local_table_size - i;
7977 PUSH_GETLOCAL(args, *location, idx, depth);
7978 }
7979 argc += local_body->param.lead_num;
7980 }
7981
7982 if (local_body->param.flags.has_opt) {
7983 /* optional arguments */
7984 for (int j = 0; j < local_body->param.opt_num; j++) {
7985 int idx = local_body->local_table_size - (argc + j);
7986 PUSH_GETLOCAL(args, *location, idx, depth);
7987 }
7988 argc += local_body->param.opt_num;
7989 }
7990
7991 if (local_body->param.flags.has_rest) {
7992 /* rest argument */
7993 int idx = local_body->local_table_size - local_body->param.rest_start;
7994 PUSH_GETLOCAL(args, *location, idx, depth);
7995 PUSH_INSN1(args, *location, splatarray, Qfalse);
7996
7997 argc = local_body->param.rest_start + 1;
7998 flag |= VM_CALL_ARGS_SPLAT;
7999 }
8000
8001 if (local_body->param.flags.has_post) {
8002 /* post arguments */
8003 int post_len = local_body->param.post_num;
8004 int post_start = local_body->param.post_start;
8005
8006 int j = 0;
8007 for (; j < post_len; j++) {
8008 int idx = local_body->local_table_size - (post_start + j);
8009 PUSH_GETLOCAL(args, *location, idx, depth);
8010 }
8011
8012 if (local_body->param.flags.has_rest) {
8013 // argc remains unchanged from rest branch
8014 PUSH_INSN1(args, *location, newarray, INT2FIX(j));
8015 PUSH_INSN(args, *location, concatarray);
8016 }
8017 else {
8018 argc = post_len + post_start;
8019 }
8020 }
8021
8022 const struct rb_iseq_param_keyword *const local_keyword = local_body->param.keyword;
8023 if (local_body->param.flags.has_kw) {
8024 int local_size = local_body->local_table_size;
8025 argc++;
8026
8027 PUSH_INSN1(args, *location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8028
8029 if (local_body->param.flags.has_kwrest) {
8030 int idx = local_body->local_table_size - local_keyword->rest_start;
8031 PUSH_GETLOCAL(args, *location, idx, depth);
8032 RUBY_ASSERT(local_keyword->num > 0);
8033 PUSH_SEND(args, *location, rb_intern("dup"), INT2FIX(0));
8034 }
8035 else {
8036 PUSH_INSN1(args, *location, newhash, INT2FIX(0));
8037 }
8038 int i = 0;
8039 for (; i < local_keyword->num; ++i) {
8040 ID id = local_keyword->table[i];
8041 int idx = local_size - get_local_var_idx(local_iseq, id);
8042
8043 {
8044 VALUE operand = ID2SYM(id);
8045 PUSH_INSN1(args, *location, putobject, operand);
8046 }
8047
8048 PUSH_GETLOCAL(args, *location, idx, depth);
8049 }
8050
8051 PUSH_SEND(args, *location, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
8052 flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT;
8053 }
8054 else if (local_body->param.flags.has_kwrest) {
8055 int idx = local_body->local_table_size - local_keyword->rest_start;
8056 PUSH_GETLOCAL(args, *location, idx, depth);
8057 argc++;
8058 flag |= VM_CALL_KW_SPLAT;
8059 }
8060
8061 PUSH_SEQ(ret, args);
8062
8063 {
8064 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flag, NULL, block != NULL);
8065 PUSH_INSN2(ret, *location, invokesuper, callinfo, block);
8066 }
8067
8068 if (node->block != NULL) {
8069 pm_compile_retry_end_label(iseq, ret, retry_end_l);
8070 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, block, retry_end_l);
8071 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
8072 }
8073
8074 if (popped) PUSH_INSN(ret, *location, pop);
8075}
8076
8077static inline void
8078pm_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)
8079{
8080 LABEL *matched_label = NEW_LABEL(location->line);
8081 LABEL *unmatched_label = NEW_LABEL(location->line);
8082 LABEL *done_label = NEW_LABEL(location->line);
8083
8084 // First, we're going to push a bunch of stuff onto the stack that is
8085 // going to serve as our scratch space.
8086 PUSH_INSN(ret, *location, putnil); // key error key
8087 PUSH_INSN(ret, *location, putnil); // key error matchee
8088 PUSH_INSN1(ret, *location, putobject, Qfalse); // key error?
8089 PUSH_INSN(ret, *location, putnil); // error string
8090 PUSH_INSN(ret, *location, putnil); // deconstruct cache
8091
8092 // Next we're going to compile the value expression such that it's on
8093 // the stack.
8094 PM_COMPILE_NOT_POPPED(node->value);
8095
8096 // Here we'll dup it so that it can be used for comparison, but also be
8097 // used for error handling.
8098 PUSH_INSN(ret, *location, dup);
8099
8100 // Next we'll compile the pattern. We indicate to the pm_compile_pattern
8101 // function that this is the only pattern that will be matched against
8102 // through the in_single_pattern parameter. We also indicate that the
8103 // value to compare against is 2 slots from the top of the stack (the
8104 // base_index parameter).
8105 pm_compile_pattern(iseq, scope_node, node->pattern, ret, matched_label, unmatched_label, true, false, true, 2);
8106
8107 // If the pattern did not match the value, then we're going to compile
8108 // in our error handler code. This will determine which error to raise
8109 // and raise it.
8110 PUSH_LABEL(ret, unmatched_label);
8111 pm_compile_pattern_error_handler(iseq, scope_node, (const pm_node_t *) node, ret, done_label, popped);
8112
8113 // If the pattern did match, we'll clean up the values we've pushed onto
8114 // the stack and then push nil onto the stack if it's not popped.
8115 PUSH_LABEL(ret, matched_label);
8116 PUSH_INSN1(ret, *location, adjuststack, INT2FIX(6));
8117 if (!popped) PUSH_INSN(ret, *location, putnil);
8118 PUSH_INSNL(ret, *location, jump, done_label);
8119
8120 PUSH_LABEL(ret, done_label);
8121}
8122
8123static inline void
8124pm_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)
8125{
8126 LABEL *fail_label = NEW_LABEL(location->line);
8127 LABEL *end_label = NEW_LABEL(location->line);
8128
8129 // First, we'll compile the call so that all of its instructions are
8130 // present. Then we'll compile all of the local variable targets.
8131 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->call);
8132
8133 // Now, check if the match was successful. If it was, then we'll
8134 // continue on and assign local variables. Otherwise we'll skip over the
8135 // assignment code.
8136 {
8137 VALUE operand = rb_id2sym(idBACKREF);
8138 PUSH_INSN1(ret, *location, getglobal, operand);
8139 }
8140
8141 PUSH_INSN(ret, *location, dup);
8142 PUSH_INSNL(ret, *location, branchunless, fail_label);
8143
8144 // If there's only a single local variable target, we can skip some of
8145 // the bookkeeping, so we'll put a special branch here.
8146 size_t targets_count = node->targets.size;
8147
8148 if (targets_count == 1) {
8149 const pm_node_t *target = node->targets.nodes[0];
8150 RUBY_ASSERT(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
8151
8152 const pm_local_variable_target_node_t *local_target = (const pm_local_variable_target_node_t *) target;
8153 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, local_target->name, local_target->depth);
8154
8155 {
8156 VALUE operand = rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name));
8157 PUSH_INSN1(ret, *location, putobject, operand);
8158 }
8159
8160 PUSH_SEND(ret, *location, idAREF, INT2FIX(1));
8161 PUSH_LABEL(ret, fail_label);
8162 PUSH_SETLOCAL(ret, *location, index.index, index.level);
8163 if (popped) PUSH_INSN(ret, *location, pop);
8164 return;
8165 }
8166
8167 DECL_ANCHOR(fail_anchor);
8168
8169 // Otherwise there is more than one local variable target, so we'll need
8170 // to do some bookkeeping.
8171 for (size_t targets_index = 0; targets_index < targets_count; targets_index++) {
8172 const pm_node_t *target = node->targets.nodes[targets_index];
8173 RUBY_ASSERT(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
8174
8175 const pm_local_variable_target_node_t *local_target = (const pm_local_variable_target_node_t *) target;
8176 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, local_target->name, local_target->depth);
8177
8178 if (((size_t) targets_index) < (targets_count - 1)) {
8179 PUSH_INSN(ret, *location, dup);
8180 }
8181
8182 {
8183 VALUE operand = rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name));
8184 PUSH_INSN1(ret, *location, putobject, operand);
8185 }
8186
8187 PUSH_SEND(ret, *location, idAREF, INT2FIX(1));
8188 PUSH_SETLOCAL(ret, *location, index.index, index.level);
8189
8190 PUSH_INSN(fail_anchor, *location, putnil);
8191 PUSH_SETLOCAL(fail_anchor, *location, index.index, index.level);
8192 }
8193
8194 // Since we matched successfully, now we'll jump to the end.
8195 PUSH_INSNL(ret, *location, jump, end_label);
8196
8197 // In the case that the match failed, we'll loop through each local
8198 // variable target and set all of them to `nil`.
8199 PUSH_LABEL(ret, fail_label);
8200 PUSH_INSN(ret, *location, pop);
8201 PUSH_SEQ(ret, fail_anchor);
8202
8203 // Finally, we can push the end label for either case.
8204 PUSH_LABEL(ret, end_label);
8205 if (popped) PUSH_INSN(ret, *location, pop);
8206}
8207
8208static inline void
8209pm_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)
8210{
8211 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
8212 LABEL *splabel = NEW_LABEL(0);
8213 PUSH_LABEL(ret, splabel);
8214
8215 if (node->arguments) {
8216 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8217 }
8218 else {
8219 PUSH_INSN(ret, *location, putnil);
8220 }
8221 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8222
8223 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
8224 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8225
8226 PUSH_ADJUST_RESTORE(ret, splabel);
8227 if (!popped) PUSH_INSN(ret, *location, putnil);
8228 }
8229 else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
8230 LABEL *splabel = NEW_LABEL(0);
8231
8232 PUSH_LABEL(ret, splabel);
8233 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->start_label);
8234
8235 if (node->arguments != NULL) {
8236 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8237 }
8238 else {
8239 PUSH_INSN(ret, *location, putnil);
8240 }
8241
8242 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8243 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
8244 PUSH_ADJUST_RESTORE(ret, splabel);
8245 splabel->unremovable = FALSE;
8246
8247 if (!popped) PUSH_INSN(ret, *location, putnil);
8248 }
8249 else {
8250 const rb_iseq_t *ip = iseq;
8251 unsigned long throw_flag = 0;
8252
8253 while (ip) {
8254 if (!ISEQ_COMPILE_DATA(ip)) {
8255 ip = 0;
8256 break;
8257 }
8258
8259 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
8260 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8261 /* while loop */
8262 break;
8263 }
8264 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8265 break;
8266 }
8267 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8268 COMPILE_ERROR(iseq, location->line, "Invalid next");
8269 return;
8270 }
8271
8272 ip = ISEQ_BODY(ip)->parent_iseq;
8273 }
8274
8275 if (ip != 0) {
8276 if (node->arguments) {
8277 PM_COMPILE_NOT_POPPED((const pm_node_t *) node->arguments);
8278 }
8279 else {
8280 PUSH_INSN(ret, *location, putnil);
8281 }
8282
8283 PUSH_INSN1(ret, *location, throw, INT2FIX(throw_flag | TAG_NEXT));
8284 if (popped) PUSH_INSN(ret, *location, pop);
8285 }
8286 else {
8287 COMPILE_ERROR(iseq, location->line, "Invalid next");
8288 }
8289 }
8290}
8291
8292static inline void
8293pm_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)
8294{
8295 if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
8296 LABEL *splabel = NEW_LABEL(0);
8297
8298 PUSH_LABEL(ret, splabel);
8299 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->redo_label);
8300 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8301
8302 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
8303 PUSH_ADJUST_RESTORE(ret, splabel);
8304 if (!popped) PUSH_INSN(ret, *location, putnil);
8305 }
8306 else if (ISEQ_BODY(iseq)->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
8307 LABEL *splabel = NEW_LABEL(0);
8308
8309 PUSH_LABEL(ret, splabel);
8310 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
8311 PUSH_ADJUST(ret, *location, ISEQ_COMPILE_DATA(iseq)->start_label);
8312
8313 PUSH_INSNL(ret, *location, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8314 PUSH_ADJUST_RESTORE(ret, splabel);
8315 if (!popped) PUSH_INSN(ret, *location, putnil);
8316 }
8317 else {
8318 const rb_iseq_t *ip = iseq;
8319
8320 while (ip) {
8321 if (!ISEQ_COMPILE_DATA(ip)) {
8322 ip = 0;
8323 break;
8324 }
8325
8326 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8327 break;
8328 }
8329 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8330 break;
8331 }
8332 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8333 COMPILE_ERROR(iseq, location->line, "Invalid redo");
8334 return;
8335 }
8336
8337 ip = ISEQ_BODY(ip)->parent_iseq;
8338 }
8339
8340 if (ip != 0) {
8341 PUSH_INSN(ret, *location, putnil);
8342 PUSH_INSN1(ret, *location, throw, INT2FIX(VM_THROW_NO_ESCAPE_FLAG | TAG_REDO));
8343 if (popped) PUSH_INSN(ret, *location, pop);
8344 }
8345 else {
8346 COMPILE_ERROR(iseq, location->line, "Invalid redo");
8347 }
8348 }
8349}
8350
8351static inline void
8352pm_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)
8353{
8354 iseq_set_exception_local_table(iseq);
8355
8356 // First, establish the labels that we need to be able to jump to within
8357 // this compilation block.
8358 LABEL *exception_match_label = NEW_LABEL(location->line);
8359 LABEL *rescue_end_label = NEW_LABEL(location->line);
8360
8361 // Next, compile each of the exceptions that we're going to be
8362 // handling. For each one, we'll add instructions to check if the
8363 // exception matches the raised one, and if it does then jump to the
8364 // exception_match_label label. Otherwise it will fall through to the
8365 // subsequent check. If there are no exceptions, we'll only check
8366 // StandardError.
8367 const pm_node_list_t *exceptions = &node->exceptions;
8368
8369 if (exceptions->size > 0) {
8370 for (size_t index = 0; index < exceptions->size; index++) {
8371 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8372 PM_COMPILE(exceptions->nodes[index]);
8373 int checkmatch_flags = VM_CHECKMATCH_TYPE_RESCUE;
8374 if (PM_NODE_TYPE_P(exceptions->nodes[index], PM_SPLAT_NODE)) {
8375 checkmatch_flags |= VM_CHECKMATCH_ARRAY;
8376 }
8377 PUSH_INSN1(ret, *location, checkmatch, INT2FIX(checkmatch_flags));
8378 PUSH_INSNL(ret, *location, branchif, exception_match_label);
8379 }
8380 }
8381 else {
8382 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8383 PUSH_INSN1(ret, *location, putobject, rb_eStandardError);
8384 PUSH_INSN1(ret, *location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
8385 PUSH_INSNL(ret, *location, branchif, exception_match_label);
8386 }
8387
8388 // If none of the exceptions that we are matching against matched, then
8389 // we'll jump straight to the rescue_end_label label.
8390 PUSH_INSNL(ret, *location, jump, rescue_end_label);
8391
8392 // Here we have the exception_match_label, which is where the
8393 // control-flow goes in the case that one of the exceptions matched.
8394 // Here we will compile the instructions to handle the exception.
8395 PUSH_LABEL(ret, exception_match_label);
8396 PUSH_TRACE(ret, RUBY_EVENT_RESCUE);
8397
8398 // If we have a reference to the exception, then we'll compile the write
8399 // into the instruction sequence. This can look quite different
8400 // depending on the kind of write being performed.
8401 if (node->reference) {
8402 DECL_ANCHOR(writes);
8403 DECL_ANCHOR(cleanup);
8404
8405 pm_compile_target_node(iseq, node->reference, ret, writes, cleanup, scope_node, NULL);
8406 PUSH_GETLOCAL(ret, *location, LVAR_ERRINFO, 0);
8407
8408 PUSH_SEQ(ret, writes);
8409 PUSH_SEQ(ret, cleanup);
8410 }
8411
8412 // If we have statements to execute, we'll compile them here. Otherwise
8413 // we'll push nil onto the stack.
8414 if (node->statements != NULL) {
8415 // We'll temporarily remove the end_label location from the iseq
8416 // when compiling the statements so that next/redo statements
8417 // inside the body will throw to the correct place instead of
8418 // jumping straight to the end of this iseq
8419 LABEL *prev_end = ISEQ_COMPILE_DATA(iseq)->end_label;
8420 ISEQ_COMPILE_DATA(iseq)->end_label = NULL;
8421
8422 PM_COMPILE((const pm_node_t *) node->statements);
8423
8424 // Now restore the end_label
8425 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end;
8426 }
8427 else {
8428 PUSH_INSN(ret, *location, putnil);
8429 }
8430
8431 PUSH_INSN(ret, *location, leave);
8432
8433 // Here we'll insert the rescue_end_label label, which is jumped to if
8434 // none of the exceptions matched. It will cause the control-flow to
8435 // either jump to the next rescue clause or it will fall through to the
8436 // subsequent instruction returning the raised error.
8437 PUSH_LABEL(ret, rescue_end_label);
8438 if (node->subsequent != NULL) {
8439 PM_COMPILE((const pm_node_t *) node->subsequent);
8440 }
8441 else {
8442 PUSH_GETLOCAL(ret, *location, 1, 0);
8443 }
8444}
8445
8446static inline void
8447pm_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)
8448{
8449 const pm_arguments_node_t *arguments = node->arguments;
8450 enum rb_iseq_type type = ISEQ_BODY(iseq)->type;
8451 LABEL *splabel = 0;
8452
8453 const rb_iseq_t *parent_iseq = iseq;
8454 enum rb_iseq_type parent_type = ISEQ_BODY(parent_iseq)->type;
8455 while (parent_type == ISEQ_TYPE_RESCUE || parent_type == ISEQ_TYPE_ENSURE) {
8456 if (!(parent_iseq = ISEQ_BODY(parent_iseq)->parent_iseq)) break;
8457 parent_type = ISEQ_BODY(parent_iseq)->type;
8458 }
8459
8460 switch (parent_type) {
8461 case ISEQ_TYPE_TOP:
8462 case ISEQ_TYPE_MAIN:
8463 if (arguments) {
8464 rb_warn("argument of top-level return is ignored");
8465 }
8466 if (parent_iseq == iseq) {
8467 type = ISEQ_TYPE_METHOD;
8468 }
8469 break;
8470 default:
8471 break;
8472 }
8473
8474 if (type == ISEQ_TYPE_METHOD) {
8475 splabel = NEW_LABEL(0);
8476 PUSH_LABEL(ret, splabel);
8477 PUSH_ADJUST(ret, *location, 0);
8478 }
8479
8480 if (arguments != NULL) {
8481 PM_COMPILE_NOT_POPPED((const pm_node_t *) arguments);
8482 }
8483 else {
8484 PUSH_INSN(ret, *location, putnil);
8485 }
8486
8487 if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
8488 pm_add_ensure_iseq(ret, iseq, 1, scope_node);
8489 PUSH_TRACE(ret, RUBY_EVENT_RETURN);
8490 PUSH_INSN(ret, *location, leave);
8491 PUSH_ADJUST_RESTORE(ret, splabel);
8492 if (!popped) PUSH_INSN(ret, *location, putnil);
8493 }
8494 else {
8495 PUSH_INSN1(ret, *location, throw, INT2FIX(TAG_RETURN));
8496 if (popped) PUSH_INSN(ret, *location, pop);
8497 }
8498}
8499
8500static inline void
8501pm_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)
8502{
8503 DECL_ANCHOR(args);
8504
8505 LABEL *retry_label = NEW_LABEL(location->line);
8506 LABEL *retry_end_l = NEW_LABEL(location->line);
8507
8508 const rb_iseq_t *previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
8509 const rb_iseq_t *current_block;
8510 ISEQ_COMPILE_DATA(iseq)->current_block = current_block = NULL;
8511
8512 PUSH_LABEL(ret, retry_label);
8513 PUSH_INSN(ret, *location, putself);
8514
8515 int flags = 0;
8516 struct rb_callinfo_kwarg *keywords = NULL;
8517 int argc = pm_setup_args(node->arguments, node->block, &flags, &keywords, iseq, ret, scope_node, location);
8518 bool is_forwardable = (node->arguments != NULL) && PM_NODE_FLAG_P(node->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING);
8519 flags |= VM_CALL_SUPER | VM_CALL_FCALL;
8520
8521 if (node->block && PM_NODE_TYPE_P(node->block, PM_BLOCK_NODE)) {
8522 pm_scope_node_t next_scope_node;
8523 pm_scope_node_init(node->block, &next_scope_node, scope_node);
8524
8525 ISEQ_COMPILE_DATA(iseq)->current_block = current_block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location->line);
8526 pm_scope_node_destroy(&next_scope_node);
8527 }
8528
8529 if (!node->block) {
8530 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
8531 }
8532
8533 if ((flags & VM_CALL_ARGS_BLOCKARG) && (flags & VM_CALL_KW_SPLAT) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
8534 PUSH_INSN(args, *location, splatkw);
8535 }
8536
8537 PUSH_SEQ(ret, args);
8538 if (is_forwardable && ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
8539 flags |= VM_CALL_FORWARDING;
8540
8541 {
8542 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, current_block != NULL);
8543 PUSH_INSN2(ret, *location, invokesuperforward, callinfo, current_block);
8544 }
8545 }
8546 else {
8547 {
8548 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, current_block != NULL);
8549 PUSH_INSN2(ret, *location, invokesuper, callinfo, current_block);
8550 }
8551
8552 }
8553
8554 pm_compile_retry_end_label(iseq, ret, retry_end_l);
8555
8556 if (popped) PUSH_INSN(ret, *location, pop);
8557 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
8558 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, current_block, retry_end_l);
8559}
8560
8561static inline void
8562pm_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)
8563{
8564 switch (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->type) {
8565 case ISEQ_TYPE_TOP:
8566 case ISEQ_TYPE_MAIN:
8567 case ISEQ_TYPE_CLASS:
8568 COMPILE_ERROR(iseq, location->line, "Invalid yield");
8569 return;
8570 default: /* valid */;
8571 }
8572
8573 int argc = 0;
8574 int flags = 0;
8575 struct rb_callinfo_kwarg *keywords = NULL;
8576
8577 if (node->arguments) {
8578 argc = pm_setup_args(node->arguments, NULL, &flags, &keywords, iseq, ret, scope_node, location);
8579 }
8580
8581 const struct rb_callinfo *callinfo = new_callinfo(iseq, 0, argc, flags, keywords, FALSE);
8582 PUSH_INSN1(ret, *location, invokeblock, callinfo);
8583
8584 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
8585 if (popped) PUSH_INSN(ret, *location, pop);
8586
8587 int level = 0;
8588 for (const rb_iseq_t *tmp_iseq = iseq; tmp_iseq != ISEQ_BODY(iseq)->local_iseq; level++) {
8589 tmp_iseq = ISEQ_BODY(tmp_iseq)->parent_iseq;
8590 }
8591
8592 if (level > 0) access_outer_variables(iseq, level, rb_intern("yield"), true);
8593}
8594
8605static void
8606pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
8607{
8608 const pm_parser_t *parser = scope_node->parser;
8609 const pm_node_location_t location = PM_NODE_START_LOCATION(parser, node);
8610 int lineno = (int) location.line;
8611
8612 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)) {
8613 // If this node is a begin node and it has empty statements and also
8614 // has a rescue clause, then the other parser considers it as
8615 // starting on the same line as the rescue, as opposed to the
8616 // location of the begin keyword. We replicate that behavior here.
8617 lineno = (int) PM_NODE_START_LINE_COLUMN(parser, ((const pm_begin_node_t *) node)->rescue_clause).line;
8618 }
8619
8620 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_NEWLINE) && ISEQ_COMPILE_DATA(iseq)->last_line != lineno) {
8621 // If this node has the newline flag set and it is on a new line
8622 // from the previous nodes that have been compiled for this ISEQ,
8623 // then we need to emit a newline event.
8624 int event = RUBY_EVENT_LINE;
8625
8626 ISEQ_COMPILE_DATA(iseq)->last_line = lineno;
8627 if (lineno > 0 && ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
8628 event |= RUBY_EVENT_COVERAGE_LINE;
8629 }
8630 PUSH_TRACE(ret, event);
8631 }
8632
8633 switch (PM_NODE_TYPE(node)) {
8634 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
8635 // alias $foo $bar
8636 // ^^^^^^^^^^^^^^^
8637 pm_compile_alias_global_variable_node(iseq, (const pm_alias_global_variable_node_t *) node, &location, ret, popped, scope_node);
8638 return;
8639 case PM_ALIAS_METHOD_NODE:
8640 // alias foo bar
8641 // ^^^^^^^^^^^^^
8642 pm_compile_alias_method_node(iseq, (const pm_alias_method_node_t *) node, &location, ret, popped, scope_node);
8643 return;
8644 case PM_AND_NODE:
8645 // a and b
8646 // ^^^^^^^
8647 pm_compile_and_node(iseq, (const pm_and_node_t *) node, &location, ret, popped, scope_node);
8648 return;
8649 case PM_ARGUMENTS_NODE: {
8650 // break foo
8651 // ^^^
8652 //
8653 // These are ArgumentsNodes that are not compiled directly by their
8654 // parent call nodes, used in the cases of NextNodes, ReturnNodes, and
8655 // BreakNodes. They can create an array like ArrayNode.
8656 const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node;
8657 const pm_node_list_t *elements = &cast->arguments;
8658
8659 if (elements->size == 1) {
8660 // If we are only returning a single element through one of the jump
8661 // nodes, then we will only compile that node directly.
8662 PM_COMPILE(elements->nodes[0]);
8663 }
8664 else {
8665 pm_compile_array_node(iseq, (const pm_node_t *) cast, elements, &location, ret, popped, scope_node);
8666 }
8667 return;
8668 }
8669 case PM_ARRAY_NODE: {
8670 // [foo, bar, baz]
8671 // ^^^^^^^^^^^^^^^
8672 const pm_array_node_t *cast = (const pm_array_node_t *) node;
8673 pm_compile_array_node(iseq, (const pm_node_t *) cast, &cast->elements, &location, ret, popped, scope_node);
8674 return;
8675 }
8676 case PM_ASSOC_NODE: {
8677 // { foo: 1 }
8678 // ^^^^^^
8679 //
8680 // foo(bar: 1)
8681 // ^^^^^^
8682 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node;
8683
8684 PM_COMPILE(cast->key);
8685 PM_COMPILE(cast->value);
8686
8687 return;
8688 }
8689 case PM_ASSOC_SPLAT_NODE: {
8690 // { **foo }
8691 // ^^^^^
8692 //
8693 // def foo(**); bar(**); end
8694 // ^^
8695 const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node;
8696
8697 if (cast->value != NULL) {
8698 PM_COMPILE(cast->value);
8699 }
8700 else if (!popped) {
8701 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_POW, 0);
8702 PUSH_GETLOCAL(ret, location, index.index, index.level);
8703 }
8704
8705 return;
8706 }
8707 case PM_BACK_REFERENCE_READ_NODE: {
8708 // $+
8709 // ^^
8710 if (!popped) {
8712 VALUE backref = pm_compile_back_reference_ref(cast);
8713
8714 PUSH_INSN2(ret, location, getspecial, INT2FIX(1), backref);
8715 }
8716 return;
8717 }
8718 case PM_BEGIN_NODE: {
8719 // begin end
8720 // ^^^^^^^^^
8721 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
8722
8723 if (cast->ensure_clause) {
8724 // Compiling the ensure clause will compile the rescue clause (if
8725 // there is one), which will compile the begin statements.
8726 pm_compile_ensure(iseq, cast, &location, ret, popped, scope_node);
8727 }
8728 else if (cast->rescue_clause) {
8729 // Compiling rescue will compile begin statements (if applicable).
8730 pm_compile_rescue(iseq, cast, &location, ret, popped, scope_node);
8731 }
8732 else {
8733 // If there is neither ensure or rescue, the just compile the
8734 // statements.
8735 if (cast->statements != NULL) {
8736 PM_COMPILE((const pm_node_t *) cast->statements);
8737 }
8738 else if (!popped) {
8739 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
8740 }
8741 }
8742 return;
8743 }
8744 case PM_BLOCK_ARGUMENT_NODE: {
8745 // foo(&bar)
8746 // ^^^^
8747 const pm_block_argument_node_t *cast = (const pm_block_argument_node_t *) node;
8748
8749 if (cast->expression != NULL) {
8750 PM_COMPILE(cast->expression);
8751 }
8752 else {
8753 // If there's no expression, this must be block forwarding.
8754 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_AND, 0);
8755 PUSH_INSN2(ret, location, getblockparamproxy, INT2FIX(local_index.index + VM_ENV_DATA_SIZE - 1), INT2FIX(local_index.level));
8756 }
8757 return;
8758 }
8759 case PM_BREAK_NODE:
8760 // break
8761 // ^^^^^
8762 //
8763 // break foo
8764 // ^^^^^^^^^
8765 pm_compile_break_node(iseq, (const pm_break_node_t *) node, &location, ret, popped, scope_node);
8766 return;
8767 case PM_CALL_NODE:
8768 // foo
8769 // ^^^
8770 //
8771 // foo.bar
8772 // ^^^^^^^
8773 //
8774 // foo.bar() {}
8775 // ^^^^^^^^^^^^
8776 pm_compile_call_node(iseq, (const pm_call_node_t *) node, ret, popped, scope_node);
8777 return;
8778 case PM_CALL_AND_WRITE_NODE: {
8779 // foo.bar &&= baz
8780 // ^^^^^^^^^^^^^^^
8781 const pm_call_and_write_node_t *cast = (const pm_call_and_write_node_t *) node;
8782 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);
8783 return;
8784 }
8785 case PM_CALL_OR_WRITE_NODE: {
8786 // foo.bar ||= baz
8787 // ^^^^^^^^^^^^^^^
8788 const pm_call_or_write_node_t *cast = (const pm_call_or_write_node_t *) node;
8789 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);
8790 return;
8791 }
8792 case PM_CALL_OPERATOR_WRITE_NODE:
8793 // foo.bar += baz
8794 // ^^^^^^^^^^^^^^^
8795 //
8796 // Call operator writes occur when you have a call node on the left-hand
8797 // side of a write operator that is not `=`. As an example,
8798 // `foo.bar *= 1`. This breaks down to caching the receiver on the
8799 // stack and then performing three method calls, one to read the value,
8800 // one to compute the result, and one to write the result back to the
8801 // receiver.
8802 pm_compile_call_operator_write_node(iseq, (const pm_call_operator_write_node_t *) node, &location, ret, popped, scope_node);
8803 return;
8804 case PM_CASE_NODE:
8805 // case foo; when bar; end
8806 // ^^^^^^^^^^^^^^^^^^^^^^^
8807 pm_compile_case_node(iseq, (const pm_case_node_t *) node, &location, ret, popped, scope_node);
8808 return;
8809 case PM_CASE_MATCH_NODE:
8810 // case foo; in bar; end
8811 // ^^^^^^^^^^^^^^^^^^^^^
8812 //
8813 // If you use the `case` keyword to create a case match node, it will
8814 // match against all of the `in` clauses until it finds one that
8815 // matches. If it doesn't find one, it can optionally fall back to an
8816 // `else` clause. If none is present and a match wasn't found, it will
8817 // raise an appropriate error.
8818 pm_compile_case_match_node(iseq, (const pm_case_match_node_t *) node, &location, ret, popped, scope_node);
8819 return;
8820 case PM_CLASS_NODE: {
8821 // class Foo; end
8822 // ^^^^^^^^^^^^^^
8823 const pm_class_node_t *cast = (const pm_class_node_t *) node;
8824
8825 ID class_id = pm_constant_id_lookup(scope_node, cast->name);
8826 VALUE class_name = rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(class_id)));
8827
8828 pm_scope_node_t next_scope_node;
8829 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
8830
8831 const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(&next_scope_node, class_name, ISEQ_TYPE_CLASS, location.line);
8832 pm_scope_node_destroy(&next_scope_node);
8833
8834 // TODO: Once we merge constant path nodes correctly, fix this flag
8835 const int flags = VM_DEFINECLASS_TYPE_CLASS |
8836 (cast->superclass ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
8837 pm_compile_class_path(iseq, cast->constant_path, &location, ret, false, scope_node);
8838
8839 if (cast->superclass) {
8840 PM_COMPILE_NOT_POPPED(cast->superclass);
8841 }
8842 else {
8843 PUSH_INSN(ret, location, putnil);
8844 }
8845
8846 {
8847 VALUE operand = ID2SYM(class_id);
8848 PUSH_INSN3(ret, location, defineclass, operand, class_iseq, INT2FIX(flags));
8849 }
8850 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq);
8851
8852 if (popped) PUSH_INSN(ret, location, pop);
8853 return;
8854 }
8855 case PM_CLASS_VARIABLE_AND_WRITE_NODE: {
8856 // @@foo &&= bar
8857 // ^^^^^^^^^^^^^
8859 LABEL *end_label = NEW_LABEL(location.line);
8860
8861 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
8862 VALUE name = ID2SYM(name_id);
8863
8864 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
8865 if (!popped) PUSH_INSN(ret, location, dup);
8866
8867 PUSH_INSNL(ret, location, branchunless, end_label);
8868 if (!popped) PUSH_INSN(ret, location, pop);
8869
8870 PM_COMPILE_NOT_POPPED(cast->value);
8871 if (!popped) PUSH_INSN(ret, location, dup);
8872
8873 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
8874 PUSH_LABEL(ret, end_label);
8875
8876 return;
8877 }
8878 case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: {
8879 // @@foo += bar
8880 // ^^^^^^^^^^^^
8882
8883 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
8884 VALUE name = ID2SYM(name_id);
8885
8886 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
8887 PM_COMPILE_NOT_POPPED(cast->value);
8888
8889 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
8890 int flags = VM_CALL_ARGS_SIMPLE;
8891 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
8892
8893 if (!popped) PUSH_INSN(ret, location, dup);
8894 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
8895
8896 return;
8897 }
8898 case PM_CLASS_VARIABLE_OR_WRITE_NODE: {
8899 // @@foo ||= bar
8900 // ^^^^^^^^^^^^^
8902 LABEL *end_label = NEW_LABEL(location.line);
8903 LABEL *start_label = NEW_LABEL(location.line);
8904
8905 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
8906 VALUE name = ID2SYM(name_id);
8907
8908 PUSH_INSN(ret, location, putnil);
8909 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CVAR), name, Qtrue);
8910 PUSH_INSNL(ret, location, branchunless, start_label);
8911
8912 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
8913 if (!popped) PUSH_INSN(ret, location, dup);
8914
8915 PUSH_INSNL(ret, location, branchif, end_label);
8916 if (!popped) PUSH_INSN(ret, location, pop);
8917
8918 PUSH_LABEL(ret, start_label);
8919 PM_COMPILE_NOT_POPPED(cast->value);
8920 if (!popped) PUSH_INSN(ret, location, dup);
8921
8922 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
8923 PUSH_LABEL(ret, end_label);
8924
8925 return;
8926 }
8927 case PM_CLASS_VARIABLE_READ_NODE: {
8928 // @@foo
8929 // ^^^^^
8930 if (!popped) {
8932 ID name = pm_constant_id_lookup(scope_node, cast->name);
8933 PUSH_INSN2(ret, location, getclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
8934 }
8935 return;
8936 }
8937 case PM_CLASS_VARIABLE_WRITE_NODE: {
8938 // @@foo = 1
8939 // ^^^^^^^^^
8941 PM_COMPILE_NOT_POPPED(cast->value);
8942 if (!popped) PUSH_INSN(ret, location, dup);
8943
8944 ID name = pm_constant_id_lookup(scope_node, cast->name);
8945 PUSH_INSN2(ret, location, setclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
8946
8947 return;
8948 }
8949 case PM_CONSTANT_PATH_NODE: {
8950 // Foo::Bar
8951 // ^^^^^^^^
8952 VALUE parts;
8953
8954 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache && ((parts = pm_constant_path_parts(node, scope_node)) != Qnil)) {
8955 ISEQ_BODY(iseq)->ic_size++;
8956 PUSH_INSN1(ret, location, opt_getconstant_path, parts);
8957 }
8958 else {
8959 DECL_ANCHOR(prefix);
8960 DECL_ANCHOR(body);
8961
8962 pm_compile_constant_path(iseq, node, prefix, body, popped, scope_node);
8963 if (LIST_INSN_SIZE_ZERO(prefix)) {
8964 PUSH_INSN(ret, location, putnil);
8965 }
8966 else {
8967 PUSH_SEQ(ret, prefix);
8968 }
8969
8970 PUSH_SEQ(ret, body);
8971 }
8972
8973 if (popped) PUSH_INSN(ret, location, pop);
8974 return;
8975 }
8976 case PM_CONSTANT_PATH_AND_WRITE_NODE: {
8977 // Foo::Bar &&= baz
8978 // ^^^^^^^^^^^^^^^^
8980 pm_compile_constant_path_and_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
8981 return;
8982 }
8983 case PM_CONSTANT_PATH_OR_WRITE_NODE: {
8984 // Foo::Bar ||= baz
8985 // ^^^^^^^^^^^^^^^^
8987 pm_compile_constant_path_or_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
8988 return;
8989 }
8990 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: {
8991 // Foo::Bar += baz
8992 // ^^^^^^^^^^^^^^^
8994 pm_compile_constant_path_operator_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
8995 return;
8996 }
8997 case PM_CONSTANT_PATH_WRITE_NODE: {
8998 // Foo::Bar = 1
8999 // ^^^^^^^^^^^^
9001 pm_compile_constant_path_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9002 return;
9003 }
9004 case PM_CONSTANT_READ_NODE: {
9005 // Foo
9006 // ^^^
9007 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
9008 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9009
9010 pm_compile_constant_read(iseq, name, &cast->base.location, location.node_id, ret, scope_node);
9011 if (popped) PUSH_INSN(ret, location, pop);
9012
9013 return;
9014 }
9015 case PM_CONSTANT_AND_WRITE_NODE: {
9016 // Foo &&= bar
9017 // ^^^^^^^^^^^
9019 pm_compile_constant_and_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9020 return;
9021 }
9022 case PM_CONSTANT_OR_WRITE_NODE: {
9023 // Foo ||= bar
9024 // ^^^^^^^^^^^
9025 const pm_constant_or_write_node_t *cast = (const pm_constant_or_write_node_t *) node;
9026 pm_compile_constant_or_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9027 return;
9028 }
9029 case PM_CONSTANT_OPERATOR_WRITE_NODE: {
9030 // Foo += bar
9031 // ^^^^^^^^^^
9033 pm_compile_constant_operator_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9034 return;
9035 }
9036 case PM_CONSTANT_WRITE_NODE: {
9037 // Foo = 1
9038 // ^^^^^^^
9039 const pm_constant_write_node_t *cast = (const pm_constant_write_node_t *) node;
9040 pm_compile_constant_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
9041 return;
9042 }
9043 case PM_DEF_NODE: {
9044 // def foo; end
9045 // ^^^^^^^^^^^^
9046 //
9047 // def self.foo; end
9048 // ^^^^^^^^^^^^^^^^^
9049 const pm_def_node_t *cast = (const pm_def_node_t *) node;
9050 ID method_name = pm_constant_id_lookup(scope_node, cast->name);
9051
9052 pm_scope_node_t next_scope_node;
9053 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9054
9055 rb_iseq_t *method_iseq = NEW_ISEQ(&next_scope_node, rb_id2str(method_name), ISEQ_TYPE_METHOD, location.line);
9056 pm_scope_node_destroy(&next_scope_node);
9057
9058 if (cast->receiver) {
9059 PM_COMPILE_NOT_POPPED(cast->receiver);
9060 PUSH_INSN2(ret, location, definesmethod, ID2SYM(method_name), method_iseq);
9061 }
9062 else {
9063 PUSH_INSN2(ret, location, definemethod, ID2SYM(method_name), method_iseq);
9064 }
9065 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) method_iseq);
9066
9067 if (!popped) {
9068 PUSH_INSN1(ret, location, putobject, ID2SYM(method_name));
9069 }
9070
9071 return;
9072 }
9073 case PM_DEFINED_NODE: {
9074 // defined?(a)
9075 // ^^^^^^^^^^^
9076 const pm_defined_node_t *cast = (const pm_defined_node_t *) node;
9077 pm_compile_defined_expr(iseq, cast->value, &location, ret, popped, scope_node, false);
9078 return;
9079 }
9080 case PM_EMBEDDED_STATEMENTS_NODE: {
9081 // "foo #{bar}"
9082 // ^^^^^^
9084
9085 if (cast->statements != NULL) {
9086 PM_COMPILE((const pm_node_t *) (cast->statements));
9087 }
9088 else {
9089 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
9090 }
9091
9092 if (popped) PUSH_INSN(ret, location, pop);
9093 return;
9094 }
9095 case PM_EMBEDDED_VARIABLE_NODE: {
9096 // "foo #@bar"
9097 // ^^^^^
9098 const pm_embedded_variable_node_t *cast = (const pm_embedded_variable_node_t *) node;
9099 PM_COMPILE(cast->variable);
9100 return;
9101 }
9102 case PM_FALSE_NODE: {
9103 // false
9104 // ^^^^^
9105 if (!popped) {
9106 PUSH_INSN1(ret, location, putobject, Qfalse);
9107 }
9108 return;
9109 }
9110 case PM_ENSURE_NODE: {
9111 const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node;
9112
9113 if (cast->statements != NULL) {
9114 PM_COMPILE((const pm_node_t *) cast->statements);
9115 }
9116
9117 return;
9118 }
9119 case PM_ELSE_NODE: {
9120 // if foo then bar else baz end
9121 // ^^^^^^^^^^^^
9122 const pm_else_node_t *cast = (const pm_else_node_t *) node;
9123
9124 if (cast->statements != NULL) {
9125 PM_COMPILE((const pm_node_t *) cast->statements);
9126 }
9127 else if (!popped) {
9128 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
9129 }
9130
9131 return;
9132 }
9133 case PM_FLIP_FLOP_NODE: {
9134 // if foo .. bar; end
9135 // ^^^^^^^^^^
9136 const pm_flip_flop_node_t *cast = (const pm_flip_flop_node_t *) node;
9137
9138 LABEL *final_label = NEW_LABEL(location.line);
9139 LABEL *then_label = NEW_LABEL(location.line);
9140 LABEL *else_label = NEW_LABEL(location.line);
9141
9142 pm_compile_flip_flop(cast, else_label, then_label, iseq, location.line, ret, popped, scope_node);
9143
9144 PUSH_LABEL(ret, then_label);
9145 PUSH_INSN1(ret, location, putobject, Qtrue);
9146 PUSH_INSNL(ret, location, jump, final_label);
9147 PUSH_LABEL(ret, else_label);
9148 PUSH_INSN1(ret, location, putobject, Qfalse);
9149 PUSH_LABEL(ret, final_label);
9150
9151 return;
9152 }
9153 case PM_FLOAT_NODE: {
9154 // 1.0
9155 // ^^^
9156 if (!popped) {
9157 VALUE operand = parse_float((const pm_float_node_t *) node);
9158 PUSH_INSN1(ret, location, putobject, operand);
9159 }
9160 return;
9161 }
9162 case PM_FOR_NODE: {
9163 // for foo in bar do end
9164 // ^^^^^^^^^^^^^^^^^^^^^
9165 const pm_for_node_t *cast = (const pm_for_node_t *) node;
9166
9167 LABEL *retry_label = NEW_LABEL(location.line);
9168 LABEL *retry_end_l = NEW_LABEL(location.line);
9169
9170 // First, compile the collection that we're going to be iterating over.
9171 PUSH_LABEL(ret, retry_label);
9172 PM_COMPILE_NOT_POPPED(cast->collection);
9173
9174 // Next, create the new scope that is going to contain the block that
9175 // will be passed to the each method.
9176 pm_scope_node_t next_scope_node;
9177 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9178
9179 const rb_iseq_t *child_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location.line);
9180 pm_scope_node_destroy(&next_scope_node);
9181
9182 const rb_iseq_t *prev_block = ISEQ_COMPILE_DATA(iseq)->current_block;
9183 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
9184
9185 // Now, create the method call to each that will be used to iterate over
9186 // the collection, and pass the newly created iseq as the block.
9187 PUSH_SEND_WITH_BLOCK(ret, location, idEach, INT2FIX(0), child_iseq);
9188 pm_compile_retry_end_label(iseq, ret, retry_end_l);
9189
9190 if (popped) PUSH_INSN(ret, location, pop);
9191 ISEQ_COMPILE_DATA(iseq)->current_block = prev_block;
9192 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
9193 return;
9194 }
9195 case PM_FORWARDING_ARGUMENTS_NODE:
9196 rb_bug("Cannot compile a ForwardingArgumentsNode directly\n");
9197 return;
9198 case PM_FORWARDING_SUPER_NODE:
9199 // super
9200 // ^^^^^
9201 //
9202 // super {}
9203 // ^^^^^^^^
9204 pm_compile_forwarding_super_node(iseq, (const pm_forwarding_super_node_t *) node, &location, ret, popped, scope_node);
9205 return;
9206 case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: {
9207 // $foo &&= bar
9208 // ^^^^^^^^^^^^
9210 LABEL *end_label = NEW_LABEL(location.line);
9211
9212 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9213 PUSH_INSN1(ret, location, getglobal, name);
9214 if (!popped) PUSH_INSN(ret, location, dup);
9215
9216 PUSH_INSNL(ret, location, branchunless, end_label);
9217 if (!popped) PUSH_INSN(ret, location, pop);
9218
9219 PM_COMPILE_NOT_POPPED(cast->value);
9220 if (!popped) PUSH_INSN(ret, location, dup);
9221
9222 PUSH_INSN1(ret, location, setglobal, name);
9223 PUSH_LABEL(ret, end_label);
9224
9225 return;
9226 }
9227 case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: {
9228 // $foo += bar
9229 // ^^^^^^^^^^^
9231
9232 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9233 PUSH_INSN1(ret, location, getglobal, name);
9234 PM_COMPILE_NOT_POPPED(cast->value);
9235
9236 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9237 int flags = VM_CALL_ARGS_SIMPLE;
9238 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
9239
9240 if (!popped) PUSH_INSN(ret, location, dup);
9241 PUSH_INSN1(ret, location, setglobal, name);
9242
9243 return;
9244 }
9245 case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: {
9246 // $foo ||= bar
9247 // ^^^^^^^^^^^^
9249 LABEL *set_label = NEW_LABEL(location.line);
9250 LABEL *end_label = NEW_LABEL(location.line);
9251
9252 PUSH_INSN(ret, location, putnil);
9253 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9254
9255 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_GVAR), name, Qtrue);
9256 PUSH_INSNL(ret, location, branchunless, set_label);
9257
9258 PUSH_INSN1(ret, location, getglobal, name);
9259 if (!popped) PUSH_INSN(ret, location, dup);
9260
9261 PUSH_INSNL(ret, location, branchif, end_label);
9262 if (!popped) PUSH_INSN(ret, location, pop);
9263
9264 PUSH_LABEL(ret, set_label);
9265 PM_COMPILE_NOT_POPPED(cast->value);
9266 if (!popped) PUSH_INSN(ret, location, dup);
9267
9268 PUSH_INSN1(ret, location, setglobal, name);
9269 PUSH_LABEL(ret, end_label);
9270
9271 return;
9272 }
9273 case PM_GLOBAL_VARIABLE_READ_NODE: {
9274 // $foo
9275 // ^^^^
9277 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
9278
9279 PUSH_INSN1(ret, location, getglobal, name);
9280 if (popped) PUSH_INSN(ret, location, pop);
9281
9282 return;
9283 }
9284 case PM_GLOBAL_VARIABLE_WRITE_NODE: {
9285 // $foo = 1
9286 // ^^^^^^^^
9288 PM_COMPILE_NOT_POPPED(cast->value);
9289 if (!popped) PUSH_INSN(ret, location, dup);
9290
9291 ID name = pm_constant_id_lookup(scope_node, cast->name);
9292 PUSH_INSN1(ret, location, setglobal, ID2SYM(name));
9293
9294 return;
9295 }
9296 case PM_HASH_NODE: {
9297 // {}
9298 // ^^
9299 //
9300 // If every node in the hash is static, then we can compile the entire
9301 // hash now instead of later.
9302 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9303 // We're only going to compile this node if it's not popped. If it
9304 // is popped, then we know we don't need to do anything since it's
9305 // statically known.
9306 if (!popped) {
9307 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
9308
9309 if (cast->elements.size == 0) {
9310 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
9311 }
9312 else {
9313 VALUE value = pm_static_literal_value(iseq, node, scope_node);
9314 PUSH_INSN1(ret, location, duphash, value);
9315 RB_OBJ_WRITTEN(iseq, Qundef, value);
9316 }
9317 }
9318 }
9319 else {
9320 // Here since we know there are possible side-effects inside the
9321 // hash contents, we're going to build it entirely at runtime. We'll
9322 // do this by pushing all of the key-value pairs onto the stack and
9323 // then combining them with newhash.
9324 //
9325 // If this hash is popped, then this serves only to ensure we enact
9326 // all side-effects (like method calls) that are contained within
9327 // the hash contents.
9328 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
9329 const pm_node_list_t *elements = &cast->elements;
9330
9331 if (popped) {
9332 // If this hash is popped, then we can iterate through each
9333 // element and compile it. The result of each compilation will
9334 // only include the side effects of the element itself.
9335 for (size_t index = 0; index < elements->size; index++) {
9336 PM_COMPILE_POPPED(elements->nodes[index]);
9337 }
9338 }
9339 else {
9340 pm_compile_hash_elements(iseq, node, elements, 0, Qundef, false, ret, scope_node);
9341 }
9342 }
9343
9344 return;
9345 }
9346 case PM_IF_NODE: {
9347 // if foo then bar end
9348 // ^^^^^^^^^^^^^^^^^^^
9349 //
9350 // bar if foo
9351 // ^^^^^^^^^^
9352 //
9353 // foo ? bar : baz
9354 // ^^^^^^^^^^^^^^^
9355 const pm_if_node_t *cast = (const pm_if_node_t *) node;
9356 pm_compile_conditional(iseq, &location, PM_IF_NODE, (const pm_node_t *) cast, cast->statements, cast->subsequent, cast->predicate, ret, popped, scope_node);
9357 return;
9358 }
9359 case PM_IMAGINARY_NODE: {
9360 // 1i
9361 // ^^
9362 if (!popped) {
9363 VALUE operand = parse_imaginary((const pm_imaginary_node_t *) node);
9364 PUSH_INSN1(ret, location, putobject, operand);
9365 }
9366 return;
9367 }
9368 case PM_IMPLICIT_NODE: {
9369 // Implicit nodes mark places in the syntax tree where explicit syntax
9370 // was omitted, but implied. For example,
9371 //
9372 // { foo: }
9373 //
9374 // In this case a method call/local variable read is implied by virtue
9375 // of the missing value. To compile these nodes, we simply compile the
9376 // value that is implied, which is helpfully supplied by the parser.
9377 const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node;
9378 PM_COMPILE(cast->value);
9379 return;
9380 }
9381 case PM_IN_NODE: {
9382 // In nodes are handled by the case match node directly, so we should
9383 // never end up hitting them through this path.
9384 rb_bug("Should not ever enter an in node directly");
9385 return;
9386 }
9387 case PM_INDEX_OPERATOR_WRITE_NODE: {
9388 // foo[bar] += baz
9389 // ^^^^^^^^^^^^^^^
9391 pm_compile_index_operator_write_node(iseq, cast, &location, ret, popped, scope_node);
9392 return;
9393 }
9394 case PM_INDEX_AND_WRITE_NODE: {
9395 // foo[bar] &&= baz
9396 // ^^^^^^^^^^^^^^^^
9397 const pm_index_and_write_node_t *cast = (const pm_index_and_write_node_t *) node;
9398 pm_compile_index_control_flow_write_node(iseq, node, cast->receiver, cast->arguments, cast->block, cast->value, &location, ret, popped, scope_node);
9399 return;
9400 }
9401 case PM_INDEX_OR_WRITE_NODE: {
9402 // foo[bar] ||= baz
9403 // ^^^^^^^^^^^^^^^^
9404 const pm_index_or_write_node_t *cast = (const pm_index_or_write_node_t *) node;
9405 pm_compile_index_control_flow_write_node(iseq, node, cast->receiver, cast->arguments, cast->block, cast->value, &location, ret, popped, scope_node);
9406 return;
9407 }
9408 case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: {
9409 // @foo &&= bar
9410 // ^^^^^^^^^^^^
9412 LABEL *end_label = NEW_LABEL(location.line);
9413
9414 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9415 VALUE name = ID2SYM(name_id);
9416
9417 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9418 if (!popped) PUSH_INSN(ret, location, dup);
9419
9420 PUSH_INSNL(ret, location, branchunless, end_label);
9421 if (!popped) PUSH_INSN(ret, location, pop);
9422
9423 PM_COMPILE_NOT_POPPED(cast->value);
9424 if (!popped) PUSH_INSN(ret, location, dup);
9425
9426 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9427 PUSH_LABEL(ret, end_label);
9428
9429 return;
9430 }
9431 case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: {
9432 // @foo += bar
9433 // ^^^^^^^^^^^
9435
9436 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9437 VALUE name = ID2SYM(name_id);
9438
9439 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9440 PM_COMPILE_NOT_POPPED(cast->value);
9441
9442 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9443 int flags = VM_CALL_ARGS_SIMPLE;
9444 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
9445
9446 if (!popped) PUSH_INSN(ret, location, dup);
9447 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9448
9449 return;
9450 }
9451 case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: {
9452 // @foo ||= bar
9453 // ^^^^^^^^^^^^
9455 LABEL *end_label = NEW_LABEL(location.line);
9456
9457 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
9458 VALUE name = ID2SYM(name_id);
9459
9460 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9461 if (!popped) PUSH_INSN(ret, location, dup);
9462
9463 PUSH_INSNL(ret, location, branchif, end_label);
9464 if (!popped) PUSH_INSN(ret, location, pop);
9465
9466 PM_COMPILE_NOT_POPPED(cast->value);
9467 if (!popped) PUSH_INSN(ret, location, dup);
9468
9469 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
9470 PUSH_LABEL(ret, end_label);
9471
9472 return;
9473 }
9474 case PM_INSTANCE_VARIABLE_READ_NODE: {
9475 // @foo
9476 // ^^^^
9477 if (!popped) {
9479 ID name = pm_constant_id_lookup(scope_node, cast->name);
9480 PUSH_INSN2(ret, location, getinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
9481 }
9482 return;
9483 }
9484 case PM_INSTANCE_VARIABLE_WRITE_NODE: {
9485 // @foo = 1
9486 // ^^^^^^^^
9488 PM_COMPILE_NOT_POPPED(cast->value);
9489 if (!popped) PUSH_INSN(ret, location, dup);
9490
9491 ID name = pm_constant_id_lookup(scope_node, cast->name);
9492 PUSH_INSN2(ret, location, setinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
9493
9494 return;
9495 }
9496 case PM_INTEGER_NODE: {
9497 // 1
9498 // ^
9499 if (!popped) {
9500 VALUE operand = parse_integer((const pm_integer_node_t *) node);
9501 PUSH_INSN1(ret, location, putobject, operand);
9502 }
9503 return;
9504 }
9505 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: {
9506 // if /foo #{bar}/ then end
9507 // ^^^^^^^^^^^^
9508 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9509 if (!popped) {
9510 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9511 PUSH_INSN1(ret, location, putobject, regexp);
9512 }
9513 }
9514 else {
9515 pm_compile_regexp_dynamic(iseq, node, &((const pm_interpolated_match_last_line_node_t *) node)->parts, &location, ret, popped, scope_node);
9516 }
9517
9518 PUSH_INSN1(ret, location, getglobal, rb_id2sym(idLASTLINE));
9519 PUSH_SEND(ret, location, idEqTilde, INT2NUM(1));
9520 if (popped) PUSH_INSN(ret, location, pop);
9521
9522 return;
9523 }
9524 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
9525 // /foo #{bar}/
9526 // ^^^^^^^^^^^^
9527 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) {
9528 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
9529 const rb_iseq_t *block_iseq = NULL;
9530 int ise_index = ISEQ_BODY(iseq)->ise_size++;
9531
9532 pm_scope_node_t next_scope_node;
9533 pm_scope_node_init(node, &next_scope_node, scope_node);
9534
9535 block_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, location.line);
9536 pm_scope_node_destroy(&next_scope_node);
9537
9538 ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
9539 PUSH_INSN2(ret, location, once, block_iseq, INT2FIX(ise_index));
9540 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
9541
9542 if (popped) PUSH_INSN(ret, location, pop);
9543 return;
9544 }
9545
9546 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9547 if (!popped) {
9548 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9549 PUSH_INSN1(ret, location, putobject, regexp);
9550 }
9551 }
9552 else {
9553 pm_compile_regexp_dynamic(iseq, node, &((const pm_interpolated_regular_expression_node_t *) node)->parts, &location, ret, popped, scope_node);
9554 if (popped) PUSH_INSN(ret, location, pop);
9555 }
9556
9557 return;
9558 }
9559 case PM_INTERPOLATED_STRING_NODE: {
9560 // "foo #{bar}"
9561 // ^^^^^^^^^^^^
9562 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
9563 if (!popped) {
9564 VALUE string = pm_static_literal_value(iseq, node, scope_node);
9565
9566 if (PM_NODE_FLAG_P(node, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN)) {
9567 PUSH_INSN1(ret, location, putobject, string);
9568 }
9569 else if (PM_NODE_FLAG_P(node, PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) {
9570 PUSH_INSN1(ret, location, putstring, string);
9571 }
9572 else {
9573 PUSH_INSN1(ret, location, putchilledstring, string);
9574 }
9575 }
9576 }
9577 else {
9579 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));
9580 if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9581 if (popped) PUSH_INSN(ret, location, pop);
9582 }
9583
9584 return;
9585 }
9586 case PM_INTERPOLATED_SYMBOL_NODE: {
9587 // :"foo #{bar}"
9588 // ^^^^^^^^^^^^^
9590 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL, false, false);
9591
9592 if (length > 1) {
9593 PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9594 }
9595
9596 if (!popped) {
9597 PUSH_INSN(ret, location, intern);
9598 }
9599 else {
9600 PUSH_INSN(ret, location, pop);
9601 }
9602
9603 return;
9604 }
9605 case PM_INTERPOLATED_X_STRING_NODE: {
9606 // `foo #{bar}`
9607 // ^^^^^^^^^^^^
9609
9610 PUSH_INSN(ret, location, putself);
9611
9612 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, false, scope_node, NULL, NULL, false, false);
9613 if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
9614
9615 PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
9616 if (popped) PUSH_INSN(ret, location, pop);
9617
9618 return;
9619 }
9620 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
9621 // -> { it }
9622 // ^^
9623 if (!popped) {
9624 pm_scope_node_t *current_scope_node = scope_node;
9625 int level = 0;
9626
9627 while (current_scope_node) {
9628 if (current_scope_node->parameters && PM_NODE_TYPE_P(current_scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
9629 PUSH_GETLOCAL(ret, location, current_scope_node->local_table_for_iseq_size, level);
9630 return;
9631 }
9632
9633 current_scope_node = current_scope_node->previous;
9634 level++;
9635 }
9636 rb_bug("Local `it` does not exist");
9637 }
9638
9639 return;
9640 }
9641 case PM_KEYWORD_HASH_NODE: {
9642 // foo(bar: baz)
9643 // ^^^^^^^^
9644 const pm_keyword_hash_node_t *cast = (const pm_keyword_hash_node_t *) node;
9645 const pm_node_list_t *elements = &cast->elements;
9646
9647 const pm_node_t *element;
9648 PM_NODE_LIST_FOREACH(elements, index, element) {
9649 PM_COMPILE(element);
9650 }
9651
9652 if (!popped) PUSH_INSN1(ret, location, newhash, INT2FIX(elements->size * 2));
9653 return;
9654 }
9655 case PM_LAMBDA_NODE: {
9656 // -> {}
9657 // ^^^^^
9658 const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node;
9659
9660 pm_scope_node_t next_scope_node;
9661 pm_scope_node_init(node, &next_scope_node, scope_node);
9662
9663 int opening_lineno = pm_location_line_number(parser, &cast->opening_loc);
9664 const rb_iseq_t *block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, opening_lineno);
9665 pm_scope_node_destroy(&next_scope_node);
9666
9667 VALUE argc = INT2FIX(0);
9668 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
9669 PUSH_CALL_WITH_BLOCK(ret, location, idLambda, argc, block);
9670 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) block);
9671
9672 if (popped) PUSH_INSN(ret, location, pop);
9673 return;
9674 }
9675 case PM_LOCAL_VARIABLE_AND_WRITE_NODE: {
9676 // foo &&= bar
9677 // ^^^^^^^^^^^
9679 LABEL *end_label = NEW_LABEL(location.line);
9680
9681 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9682 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9683 if (!popped) PUSH_INSN(ret, location, dup);
9684
9685 PUSH_INSNL(ret, location, branchunless, end_label);
9686 if (!popped) PUSH_INSN(ret, location, pop);
9687
9688 PM_COMPILE_NOT_POPPED(cast->value);
9689 if (!popped) PUSH_INSN(ret, location, dup);
9690
9691 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9692 PUSH_LABEL(ret, end_label);
9693
9694 return;
9695 }
9696 case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: {
9697 // foo += bar
9698 // ^^^^^^^^^^
9700
9701 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9702 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9703
9704 PM_COMPILE_NOT_POPPED(cast->value);
9705
9706 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
9707 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
9708
9709 if (!popped) PUSH_INSN(ret, location, dup);
9710 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9711
9712 return;
9713 }
9714 case PM_LOCAL_VARIABLE_OR_WRITE_NODE: {
9715 // foo ||= bar
9716 // ^^^^^^^^^^^
9718
9719 LABEL *set_label = NEW_LABEL(location.line);
9720 LABEL *end_label = NEW_LABEL(location.line);
9721
9722 PUSH_INSN1(ret, location, putobject, Qtrue);
9723 PUSH_INSNL(ret, location, branchunless, set_label);
9724
9725 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9726 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
9727 if (!popped) PUSH_INSN(ret, location, dup);
9728
9729 PUSH_INSNL(ret, location, branchif, end_label);
9730 if (!popped) PUSH_INSN(ret, location, pop);
9731
9732 PUSH_LABEL(ret, set_label);
9733 PM_COMPILE_NOT_POPPED(cast->value);
9734 if (!popped) PUSH_INSN(ret, location, dup);
9735
9736 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
9737 PUSH_LABEL(ret, end_label);
9738
9739 return;
9740 }
9741 case PM_LOCAL_VARIABLE_READ_NODE: {
9742 // foo
9743 // ^^^
9744 if (!popped) {
9746 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9747 PUSH_GETLOCAL(ret, location, index.index, index.level);
9748 }
9749
9750 return;
9751 }
9752 case PM_LOCAL_VARIABLE_WRITE_NODE: {
9753 // foo = 1
9754 // ^^^^^^^
9756 PM_COMPILE_NOT_POPPED(cast->value);
9757 if (!popped) PUSH_INSN(ret, location, dup);
9758
9759 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
9760 PUSH_SETLOCAL(ret, location, index.index, index.level);
9761 return;
9762 }
9763 case PM_MATCH_LAST_LINE_NODE: {
9764 // if /foo/ then end
9765 // ^^^^^
9766 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
9767
9768 PUSH_INSN1(ret, location, putobject, regexp);
9769 PUSH_INSN2(ret, location, getspecial, INT2FIX(0), INT2FIX(0));
9770 PUSH_SEND(ret, location, idEqTilde, INT2NUM(1));
9771 if (popped) PUSH_INSN(ret, location, pop);
9772
9773 return;
9774 }
9775 case PM_MATCH_PREDICATE_NODE: {
9776 // foo in bar
9777 // ^^^^^^^^^^
9778 const pm_match_predicate_node_t *cast = (const pm_match_predicate_node_t *) node;
9779
9780 // First, allocate some stack space for the cached return value of any
9781 // calls to #deconstruct.
9782 PUSH_INSN(ret, location, putnil);
9783
9784 // Next, compile the expression that we're going to match against.
9785 PM_COMPILE_NOT_POPPED(cast->value);
9786 PUSH_INSN(ret, location, dup);
9787
9788 // Now compile the pattern that is going to be used to match against the
9789 // expression.
9790 LABEL *matched_label = NEW_LABEL(location.line);
9791 LABEL *unmatched_label = NEW_LABEL(location.line);
9792 LABEL *done_label = NEW_LABEL(location.line);
9793 pm_compile_pattern(iseq, scope_node, cast->pattern, ret, matched_label, unmatched_label, false, false, true, 2);
9794
9795 // If the pattern did not match, then compile the necessary instructions
9796 // to handle pushing false onto the stack, then jump to the end.
9797 PUSH_LABEL(ret, unmatched_label);
9798 PUSH_INSN(ret, location, pop);
9799 PUSH_INSN(ret, location, pop);
9800
9801 if (!popped) PUSH_INSN1(ret, location, putobject, Qfalse);
9802 PUSH_INSNL(ret, location, jump, done_label);
9803 PUSH_INSN(ret, location, putnil);
9804
9805 // If the pattern did match, then compile the necessary instructions to
9806 // handle pushing true onto the stack, then jump to the end.
9807 PUSH_LABEL(ret, matched_label);
9808 PUSH_INSN1(ret, location, adjuststack, INT2FIX(2));
9809 if (!popped) PUSH_INSN1(ret, location, putobject, Qtrue);
9810 PUSH_INSNL(ret, location, jump, done_label);
9811
9812 PUSH_LABEL(ret, done_label);
9813 return;
9814 }
9815 case PM_MATCH_REQUIRED_NODE:
9816 // foo => bar
9817 // ^^^^^^^^^^
9818 //
9819 // A match required node represents pattern matching against a single
9820 // pattern using the => operator. For example,
9821 //
9822 // foo => bar
9823 //
9824 // This is somewhat analogous to compiling a case match statement with a
9825 // single pattern. In both cases, if the pattern fails it should
9826 // immediately raise an error.
9827 pm_compile_match_required_node(iseq, (const pm_match_required_node_t *) node, &location, ret, popped, scope_node);
9828 return;
9829 case PM_MATCH_WRITE_NODE:
9830 // /(?<foo>foo)/ =~ bar
9831 // ^^^^^^^^^^^^^^^^^^^^
9832 //
9833 // Match write nodes are specialized call nodes that have a regular
9834 // expression with valid named capture groups on the left, the =~
9835 // operator, and some value on the right. The nodes themselves simply
9836 // wrap the call with the local variable targets that will be written
9837 // when the call is executed.
9838 pm_compile_match_write_node(iseq, (const pm_match_write_node_t *) node, &location, ret, popped, scope_node);
9839 return;
9840 case PM_MISSING_NODE:
9841 rb_bug("A pm_missing_node_t should not exist in prism's AST.");
9842 return;
9843 case PM_MODULE_NODE: {
9844 // module Foo; end
9845 // ^^^^^^^^^^^^^^^
9846 const pm_module_node_t *cast = (const pm_module_node_t *) node;
9847
9848 ID module_id = pm_constant_id_lookup(scope_node, cast->name);
9849 VALUE module_name = rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(module_id)));
9850
9851 pm_scope_node_t next_scope_node;
9852 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9853
9854 const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(&next_scope_node, module_name, ISEQ_TYPE_CLASS, location.line);
9855 pm_scope_node_destroy(&next_scope_node);
9856
9857 const int flags = VM_DEFINECLASS_TYPE_MODULE | pm_compile_class_path(iseq, cast->constant_path, &location, ret, false, scope_node);
9858 PUSH_INSN(ret, location, putnil);
9859 PUSH_INSN3(ret, location, defineclass, ID2SYM(module_id), module_iseq, INT2FIX(flags));
9860 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) module_iseq);
9861
9862 if (popped) PUSH_INSN(ret, location, pop);
9863 return;
9864 }
9865 case PM_REQUIRED_PARAMETER_NODE: {
9866 // def foo(bar); end
9867 // ^^^
9869 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, 0);
9870
9871 PUSH_SETLOCAL(ret, location, index.index, index.level);
9872 return;
9873 }
9874 case PM_MULTI_WRITE_NODE: {
9875 // foo, bar = baz
9876 // ^^^^^^^^^^^^^^
9877 //
9878 // A multi write node represents writing to multiple values using an =
9879 // operator. Importantly these nodes are only parsed when the left-hand
9880 // side of the operator has multiple targets. The right-hand side of the
9881 // operator having multiple targets represents an implicit array
9882 // instead.
9883 const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node;
9884
9885 DECL_ANCHOR(writes);
9886 DECL_ANCHOR(cleanup);
9887
9888 pm_multi_target_state_t state = { 0 };
9889 state.position = popped ? 0 : 1;
9890 pm_compile_multi_target_node(iseq, node, ret, writes, cleanup, scope_node, &state);
9891
9892 PM_COMPILE_NOT_POPPED(cast->value);
9893 if (!popped) PUSH_INSN(ret, location, dup);
9894
9895 PUSH_SEQ(ret, writes);
9896 if (!popped && state.stack_size >= 1) {
9897 // Make sure the value on the right-hand side of the = operator is
9898 // being returned before we pop the parent expressions.
9899 PUSH_INSN1(ret, location, setn, INT2FIX(state.stack_size));
9900 }
9901
9902 // Now, we need to go back and modify the topn instructions in order to
9903 // ensure they can correctly retrieve the parent expressions.
9904 pm_multi_target_state_update(&state);
9905
9906 PUSH_SEQ(ret, cleanup);
9907 return;
9908 }
9909 case PM_NEXT_NODE:
9910 // next
9911 // ^^^^
9912 //
9913 // next foo
9914 // ^^^^^^^^
9915 pm_compile_next_node(iseq, (const pm_next_node_t *) node, &location, ret, popped, scope_node);
9916 return;
9917 case PM_NIL_NODE: {
9918 // nil
9919 // ^^^
9920 if (!popped) {
9921 PUSH_INSN(ret, location, putnil);
9922 }
9923
9924 return;
9925 }
9926 case PM_NO_KEYWORDS_PARAMETER_NODE: {
9927 // def foo(**nil); end
9928 // ^^^^^
9929 ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg = TRUE;
9930 return;
9931 }
9932 case PM_NUMBERED_REFERENCE_READ_NODE: {
9933 // $1
9934 // ^^
9935 if (!popped) {
9937
9938 if (cast->number != 0) {
9939 VALUE ref = pm_compile_numbered_reference_ref(cast);
9940 PUSH_INSN2(ret, location, getspecial, INT2FIX(1), ref);
9941 }
9942 else {
9943 PUSH_INSN(ret, location, putnil);
9944 }
9945 }
9946
9947 return;
9948 }
9949 case PM_OR_NODE: {
9950 // a or b
9951 // ^^^^^^
9952 const pm_or_node_t *cast = (const pm_or_node_t *) node;
9953
9954 LABEL *end_label = NEW_LABEL(location.line);
9955 PM_COMPILE_NOT_POPPED(cast->left);
9956
9957 if (!popped) PUSH_INSN(ret, location, dup);
9958 PUSH_INSNL(ret, location, branchif, end_label);
9959
9960 if (!popped) PUSH_INSN(ret, location, pop);
9961 PM_COMPILE(cast->right);
9962 PUSH_LABEL(ret, end_label);
9963
9964 return;
9965 }
9966 case PM_OPTIONAL_PARAMETER_NODE: {
9967 // def foo(bar = 1); end
9968 // ^^^^^^^
9970 PM_COMPILE_NOT_POPPED(cast->value);
9971
9972 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, 0);
9973 PUSH_SETLOCAL(ret, location, index.index, index.level);
9974
9975 return;
9976 }
9977 case PM_PARENTHESES_NODE: {
9978 // ()
9979 // ^^
9980 //
9981 // (1)
9982 // ^^^
9983 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
9984
9985 if (cast->body != NULL) {
9986 PM_COMPILE(cast->body);
9987 }
9988 else if (!popped) {
9989 PUSH_INSN(ret, location, putnil);
9990 }
9991
9992 return;
9993 }
9994 case PM_PRE_EXECUTION_NODE: {
9995 // BEGIN {}
9996 // ^^^^^^^^
9997 const pm_pre_execution_node_t *cast = (const pm_pre_execution_node_t *) node;
9998
9999 LINK_ANCHOR *outer_pre = scope_node->pre_execution_anchor;
10000 RUBY_ASSERT(outer_pre != NULL);
10001
10002 // BEGIN{} nodes can be nested, so here we're going to do the same thing
10003 // that we did for the top-level compilation where we create two
10004 // anchors and then join them in the correct order into the resulting
10005 // anchor.
10006 DECL_ANCHOR(inner_pre);
10007 scope_node->pre_execution_anchor = inner_pre;
10008
10009 DECL_ANCHOR(inner_body);
10010
10011 if (cast->statements != NULL) {
10012 const pm_node_list_t *body = &cast->statements->body;
10013
10014 for (size_t index = 0; index < body->size; index++) {
10015 pm_compile_node(iseq, body->nodes[index], inner_body, true, scope_node);
10016 }
10017 }
10018
10019 if (!popped) {
10020 PUSH_INSN(inner_body, location, putnil);
10021 }
10022
10023 // Now that everything has been compiled, join both anchors together
10024 // into the correct outer pre execution anchor, and reset the value so
10025 // that subsequent BEGIN{} nodes can be compiled correctly.
10026 PUSH_SEQ(outer_pre, inner_pre);
10027 PUSH_SEQ(outer_pre, inner_body);
10028 scope_node->pre_execution_anchor = outer_pre;
10029
10030 return;
10031 }
10032 case PM_POST_EXECUTION_NODE: {
10033 // END {}
10034 // ^^^^^^
10035 const rb_iseq_t *child_iseq;
10036 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
10037
10038 pm_scope_node_t next_scope_node;
10039 pm_scope_node_init(node, &next_scope_node, scope_node);
10040 child_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, lineno);
10041 pm_scope_node_destroy(&next_scope_node);
10042
10043 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
10044
10045 int is_index = ISEQ_BODY(iseq)->ise_size++;
10046 PUSH_INSN2(ret, location, once, child_iseq, INT2FIX(is_index));
10047 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) child_iseq);
10048 if (popped) PUSH_INSN(ret, location, pop);
10049
10050 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
10051
10052 return;
10053 }
10054 case PM_RANGE_NODE: {
10055 // 0..5
10056 // ^^^^
10057 const pm_range_node_t *cast = (const pm_range_node_t *) node;
10058 bool exclude_end = PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END);
10059
10060 if (pm_optimizable_range_item_p(cast->left) && pm_optimizable_range_item_p(cast->right)) {
10061 if (!popped) {
10062 const pm_node_t *left = cast->left;
10063 const pm_node_t *right = cast->right;
10064
10065 VALUE val = rb_range_new(
10066 (left && PM_NODE_TYPE_P(left, PM_INTEGER_NODE)) ? parse_integer((const pm_integer_node_t *) left) : Qnil,
10067 (right && PM_NODE_TYPE_P(right, PM_INTEGER_NODE)) ? parse_integer((const pm_integer_node_t *) right) : Qnil,
10068 exclude_end
10069 );
10070
10071 PUSH_INSN1(ret, location, putobject, val);
10072 }
10073 }
10074 else {
10075 if (cast->left != NULL) {
10076 PM_COMPILE(cast->left);
10077 }
10078 else if (!popped) {
10079 PUSH_INSN(ret, location, putnil);
10080 }
10081
10082 if (cast->right != NULL) {
10083 PM_COMPILE(cast->right);
10084 }
10085 else if (!popped) {
10086 PUSH_INSN(ret, location, putnil);
10087 }
10088
10089 if (!popped) {
10090 PUSH_INSN1(ret, location, newrange, INT2FIX(exclude_end ? 1 : 0));
10091 }
10092 }
10093 return;
10094 }
10095 case PM_RATIONAL_NODE: {
10096 // 1r
10097 // ^^
10098 if (!popped) {
10099 PUSH_INSN1(ret, location, putobject, parse_rational((const pm_rational_node_t *) node));
10100 }
10101 return;
10102 }
10103 case PM_REDO_NODE:
10104 // redo
10105 // ^^^^
10106 pm_compile_redo_node(iseq, &location, ret, popped, scope_node);
10107 return;
10108 case PM_REGULAR_EXPRESSION_NODE: {
10109 // /foo/
10110 // ^^^^^
10111 if (!popped) {
10112 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
10113 PUSH_INSN1(ret, location, putobject, regexp);
10114 }
10115 return;
10116 }
10117 case PM_RESCUE_NODE:
10118 // begin; rescue; end
10119 // ^^^^^^^
10120 pm_compile_rescue_node(iseq, (const pm_rescue_node_t *) node, &location, ret, popped, scope_node);
10121 return;
10122 case PM_RESCUE_MODIFIER_NODE: {
10123 // foo rescue bar
10124 // ^^^^^^^^^^^^^^
10125 const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node;
10126
10127 pm_scope_node_t rescue_scope_node;
10128 pm_scope_node_init((const pm_node_t *) cast, &rescue_scope_node, scope_node);
10129
10130 rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(
10131 &rescue_scope_node,
10132 rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label),
10133 ISEQ_TYPE_RESCUE,
10134 pm_node_line_number(parser, cast->rescue_expression)
10135 );
10136
10137 pm_scope_node_destroy(&rescue_scope_node);
10138
10139 LABEL *lstart = NEW_LABEL(location.line);
10140 LABEL *lend = NEW_LABEL(location.line);
10141 LABEL *lcont = NEW_LABEL(location.line);
10142
10143 lstart->rescued = LABEL_RESCUE_BEG;
10144 lend->rescued = LABEL_RESCUE_END;
10145
10146 PUSH_LABEL(ret, lstart);
10147 PM_COMPILE_NOT_POPPED(cast->expression);
10148 PUSH_LABEL(ret, lend);
10149
10150 PUSH_INSN(ret, location, nop);
10151 PUSH_LABEL(ret, lcont);
10152 if (popped) PUSH_INSN(ret, location, pop);
10153
10154 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
10155 PUSH_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
10156 return;
10157 }
10158 case PM_RETURN_NODE:
10159 // return
10160 // ^^^^^^
10161 //
10162 // return 1
10163 // ^^^^^^^^
10164 pm_compile_return_node(iseq, (const pm_return_node_t *) node, &location, ret, popped, scope_node);
10165 return;
10166 case PM_RETRY_NODE: {
10167 // retry
10168 // ^^^^^
10169 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
10170 PUSH_INSN(ret, location, putnil);
10171 PUSH_INSN1(ret, location, throw, INT2FIX(TAG_RETRY));
10172 if (popped) PUSH_INSN(ret, location, pop);
10173 }
10174 else {
10175 COMPILE_ERROR(iseq, location.line, "Invalid retry");
10176 return;
10177 }
10178 return;
10179 }
10180 case PM_SCOPE_NODE:
10181 pm_compile_scope_node(iseq, (pm_scope_node_t *) node, &location, ret, popped);
10182 return;
10183 case PM_SELF_NODE: {
10184 // self
10185 // ^^^^
10186 if (!popped) {
10187 PUSH_INSN(ret, location, putself);
10188 }
10189 return;
10190 }
10191 case PM_SHAREABLE_CONSTANT_NODE: {
10192 // A value that is being written to a constant that is being marked as
10193 // shared depending on the current lexical context.
10195 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));
10196
10197 switch (PM_NODE_TYPE(cast->write)) {
10198 case PM_CONSTANT_WRITE_NODE:
10199 pm_compile_constant_write_node(iseq, (const pm_constant_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10200 break;
10201 case PM_CONSTANT_AND_WRITE_NODE:
10202 pm_compile_constant_and_write_node(iseq, (const pm_constant_and_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10203 break;
10204 case PM_CONSTANT_OR_WRITE_NODE:
10205 pm_compile_constant_or_write_node(iseq, (const pm_constant_or_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10206 break;
10207 case PM_CONSTANT_OPERATOR_WRITE_NODE:
10208 pm_compile_constant_operator_write_node(iseq, (const pm_constant_operator_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10209 break;
10210 case PM_CONSTANT_PATH_WRITE_NODE:
10211 pm_compile_constant_path_write_node(iseq, (const pm_constant_path_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10212 break;
10213 case PM_CONSTANT_PATH_AND_WRITE_NODE:
10214 pm_compile_constant_path_and_write_node(iseq, (const pm_constant_path_and_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10215 break;
10216 case PM_CONSTANT_PATH_OR_WRITE_NODE:
10217 pm_compile_constant_path_or_write_node(iseq, (const pm_constant_path_or_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10218 break;
10219 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE:
10220 pm_compile_constant_path_operator_write_node(iseq, (const pm_constant_path_operator_write_node_t *) cast->write, shareability, &location, ret, popped, scope_node);
10221 break;
10222 default:
10223 rb_bug("Unexpected node type for shareable constant write: %s", pm_node_type_to_str(PM_NODE_TYPE(cast->write)));
10224 break;
10225 }
10226
10227 return;
10228 }
10229 case PM_SINGLETON_CLASS_NODE: {
10230 // class << self; end
10231 // ^^^^^^^^^^^^^^^^^^
10232 const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node;
10233
10234 pm_scope_node_t next_scope_node;
10235 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
10236 const rb_iseq_t *child_iseq = NEW_ISEQ(&next_scope_node, rb_fstring_lit("singleton class"), ISEQ_TYPE_CLASS, location.line);
10237 pm_scope_node_destroy(&next_scope_node);
10238
10239 PM_COMPILE_NOT_POPPED(cast->expression);
10240 PUSH_INSN(ret, location, putnil);
10241
10242 ID singletonclass;
10243 CONST_ID(singletonclass, "singletonclass");
10244 PUSH_INSN3(ret, location, defineclass, ID2SYM(singletonclass), child_iseq, INT2FIX(VM_DEFINECLASS_TYPE_SINGLETON_CLASS));
10245
10246 if (popped) PUSH_INSN(ret, location, pop);
10247 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) child_iseq);
10248
10249 return;
10250 }
10251 case PM_SOURCE_ENCODING_NODE: {
10252 // __ENCODING__
10253 // ^^^^^^^^^^^^
10254 if (!popped) {
10255 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10256 PUSH_INSN1(ret, location, putobject, value);
10257 }
10258 return;
10259 }
10260 case PM_SOURCE_FILE_NODE: {
10261 // __FILE__
10262 // ^^^^^^^^
10263 if (!popped) {
10264 const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node;
10265 VALUE string = pm_source_file_value(cast, scope_node);
10266
10267 if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FROZEN)) {
10268 PUSH_INSN1(ret, location, putobject, string);
10269 }
10270 else if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_MUTABLE)) {
10271 PUSH_INSN1(ret, location, putstring, string);
10272 }
10273 else {
10274 PUSH_INSN1(ret, location, putchilledstring, string);
10275 }
10276 }
10277 return;
10278 }
10279 case PM_SOURCE_LINE_NODE: {
10280 // __LINE__
10281 // ^^^^^^^^
10282 if (!popped) {
10283 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10284 PUSH_INSN1(ret, location, putobject, value);
10285 }
10286 return;
10287 }
10288 case PM_SPLAT_NODE: {
10289 // foo(*bar)
10290 // ^^^^
10291 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
10292 if (cast->expression) {
10293 PM_COMPILE(cast->expression);
10294 }
10295
10296 if (!popped) {
10297 PUSH_INSN1(ret, location, splatarray, Qtrue);
10298 }
10299 return;
10300 }
10301 case PM_STATEMENTS_NODE: {
10302 // A list of statements.
10303 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
10304 const pm_node_list_t *body = &cast->body;
10305
10306 if (body->size > 0) {
10307 for (size_t index = 0; index < body->size - 1; index++) {
10308 PM_COMPILE_POPPED(body->nodes[index]);
10309 }
10310 PM_COMPILE(body->nodes[body->size - 1]);
10311 }
10312 else {
10313 PUSH_INSN(ret, location, putnil);
10314 }
10315 return;
10316 }
10317 case PM_STRING_NODE: {
10318 // "foo"
10319 // ^^^^^
10320 if (!popped) {
10321 const pm_string_node_t *cast = (const pm_string_node_t *) node;
10322 VALUE value = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
10323
10324 if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_FROZEN)) {
10325 PUSH_INSN1(ret, location, putobject, value);
10326 }
10327 else if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_MUTABLE)) {
10328 PUSH_INSN1(ret, location, putstring, value);
10329 }
10330 else {
10331 PUSH_INSN1(ret, location, putchilledstring, value);
10332 }
10333 }
10334 return;
10335 }
10336 case PM_SUPER_NODE:
10337 // super()
10338 // super(foo)
10339 // super(...)
10340 pm_compile_super_node(iseq, (const pm_super_node_t *) node, &location, ret, popped, scope_node);
10341 return;
10342 case PM_SYMBOL_NODE: {
10343 // :foo
10344 // ^^^^
10345 if (!popped) {
10346 VALUE value = pm_static_literal_value(iseq, node, scope_node);
10347 PUSH_INSN1(ret, location, putobject, value);
10348 }
10349 return;
10350 }
10351 case PM_TRUE_NODE: {
10352 // true
10353 // ^^^^
10354 if (!popped) {
10355 PUSH_INSN1(ret, location, putobject, Qtrue);
10356 }
10357 return;
10358 }
10359 case PM_UNDEF_NODE: {
10360 // undef foo
10361 // ^^^^^^^^^
10362 const pm_undef_node_t *cast = (const pm_undef_node_t *) node;
10363 const pm_node_list_t *names = &cast->names;
10364
10365 for (size_t index = 0; index < names->size; index++) {
10366 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10367 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
10368
10369 PM_COMPILE_NOT_POPPED(names->nodes[index]);
10370 PUSH_SEND(ret, location, id_core_undef_method, INT2NUM(2));
10371
10372 if (index < names->size - 1) {
10373 PUSH_INSN(ret, location, pop);
10374 }
10375 }
10376
10377 if (popped) PUSH_INSN(ret, location, pop);
10378 return;
10379 }
10380 case PM_UNLESS_NODE: {
10381 // unless foo; bar end
10382 // ^^^^^^^^^^^^^^^^^^^
10383 //
10384 // bar unless foo
10385 // ^^^^^^^^^^^^^^
10386 const pm_unless_node_t *cast = (const pm_unless_node_t *) node;
10387 const pm_statements_node_t *statements = NULL;
10388 if (cast->else_clause != NULL) {
10389 statements = ((const pm_else_node_t *) cast->else_clause)->statements;
10390 }
10391
10392 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);
10393 return;
10394 }
10395 case PM_UNTIL_NODE: {
10396 // until foo; bar end
10397 // ^^^^^^^^^^^^^^^^^
10398 //
10399 // bar until foo
10400 // ^^^^^^^^^^^^^
10401 const pm_until_node_t *cast = (const pm_until_node_t *) node;
10402 pm_compile_loop(iseq, &location, cast->base.flags, PM_UNTIL_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node);
10403 return;
10404 }
10405 case PM_WHILE_NODE: {
10406 // while foo; bar end
10407 // ^^^^^^^^^^^^^^^^^^
10408 //
10409 // bar while foo
10410 // ^^^^^^^^^^^^^
10411 const pm_while_node_t *cast = (const pm_while_node_t *) node;
10412 pm_compile_loop(iseq, &location, cast->base.flags, PM_WHILE_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node);
10413 return;
10414 }
10415 case PM_X_STRING_NODE: {
10416 // `foo`
10417 // ^^^^^
10418 const pm_x_string_node_t *cast = (const pm_x_string_node_t *) node;
10419 VALUE value = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
10420
10421 PUSH_INSN(ret, location, putself);
10422 PUSH_INSN1(ret, location, putobject, value);
10423 PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
10424 if (popped) PUSH_INSN(ret, location, pop);
10425
10426 return;
10427 }
10428 case PM_YIELD_NODE:
10429 // yield
10430 // ^^^^^
10431 //
10432 // yield 1
10433 // ^^^^^^^
10434 pm_compile_yield_node(iseq, (const pm_yield_node_t *) node, &location, ret, popped, scope_node);
10435 return;
10436 default:
10437 rb_raise(rb_eNotImpError, "node type %s not implemented", pm_node_type_to_str(PM_NODE_TYPE(node)));
10438 return;
10439 }
10440}
10441
10442#undef PM_CONTAINER_P
10443
10445static inline bool
10446pm_iseq_pre_execution_p(rb_iseq_t *iseq)
10447{
10448 switch (ISEQ_BODY(iseq)->type) {
10449 case ISEQ_TYPE_TOP:
10450 case ISEQ_TYPE_EVAL:
10451 case ISEQ_TYPE_MAIN:
10452 return true;
10453 default:
10454 return false;
10455 }
10456}
10457
10465VALUE
10466pm_iseq_compile_node(rb_iseq_t *iseq, pm_scope_node_t *node)
10467{
10468 DECL_ANCHOR(ret);
10469
10470 if (pm_iseq_pre_execution_p(iseq)) {
10471 // Because these ISEQs can have BEGIN{}, we're going to create two
10472 // anchors to compile them, a "pre" and a "body". We'll mark the "pre"
10473 // on the scope node so that when BEGIN{} is found, its contents will be
10474 // added to the "pre" anchor.
10475 DECL_ANCHOR(pre);
10476 node->pre_execution_anchor = pre;
10477
10478 // Now we'll compile the body as normal. We won't compile directly into
10479 // the "ret" anchor yet because we want to add the "pre" anchor to the
10480 // beginning of the "ret" anchor first.
10481 DECL_ANCHOR(body);
10482 pm_compile_node(iseq, (const pm_node_t *) node, body, false, node);
10483
10484 // Now we'll join both anchors together so that the content is in the
10485 // correct order.
10486 PUSH_SEQ(ret, pre);
10487 PUSH_SEQ(ret, body);
10488 }
10489 else {
10490 // In other circumstances, we can just compile the node directly into
10491 // the "ret" anchor.
10492 pm_compile_node(iseq, (const pm_node_t *) node, ret, false, node);
10493 }
10494
10495 CHECK(iseq_setup_insn(iseq, ret));
10496 return iseq_setup(iseq, ret);
10497}
10498
10503void
10504pm_parse_result_free(pm_parse_result_t *result)
10505{
10506 if (result->node.ast_node != NULL) {
10507 pm_node_destroy(&result->parser, result->node.ast_node);
10508 }
10509
10510 if (result->parsed) {
10511 xfree(result->node.constants);
10512 pm_scope_node_destroy(&result->node);
10513 }
10514
10515 pm_parser_free(&result->parser);
10516 pm_string_free(&result->input);
10517 pm_options_free(&result->options);
10518}
10519
10521typedef struct {
10524
10526 int32_t line;
10527
10530
10532 uint32_t column_end;
10534
10536typedef struct {
10538 const char *number_prefix;
10539
10541 const char *blank_prefix;
10542
10544 const char *divider;
10545
10548
10552
10553#define PM_COLOR_BOLD "\033[1m"
10554#define PM_COLOR_GRAY "\033[2m"
10555#define PM_COLOR_RED "\033[1;31m"
10556#define PM_COLOR_RESET "\033[m"
10557#define PM_ERROR_TRUNCATE 30
10558
10559static inline pm_parse_error_t *
10560pm_parse_errors_format_sort(const pm_parser_t *parser, const pm_list_t *error_list, const pm_newline_list_t *newline_list) {
10561 pm_parse_error_t *errors = xcalloc(error_list->size, sizeof(pm_parse_error_t));
10562 if (errors == NULL) return NULL;
10563
10564 int32_t start_line = parser->start_line;
10565 pm_diagnostic_t *finish = (pm_diagnostic_t * )error_list->tail->next;
10566
10567 for (pm_diagnostic_t *error = (pm_diagnostic_t *) error_list->head; error != finish; error = (pm_diagnostic_t *) error->node.next) {
10568 pm_line_column_t start = pm_newline_list_line_column(newline_list, error->location.start, start_line);
10569 pm_line_column_t end = pm_newline_list_line_column(newline_list, error->location.end, start_line);
10570
10571 // We're going to insert this error into the array in sorted order. We
10572 // do this by finding the first error that has a line number greater
10573 // than the current error and then inserting the current error before
10574 // that one.
10575 size_t index = 0;
10576 while (
10577 (index < error_list->size) &&
10578 (errors[index].error != NULL) &&
10579 (
10580 (errors[index].line < start.line) ||
10581 ((errors[index].line == start.line) && (errors[index].column_start < start.column))
10582 )
10583 ) index++;
10584
10585 // Now we're going to shift all of the errors after this one down one
10586 // index to make room for the new error.
10587 if (index + 1 < error_list->size) {
10588 memmove(&errors[index + 1], &errors[index], sizeof(pm_parse_error_t) * (error_list->size - index - 1));
10589 }
10590
10591 // Finally, we'll insert the error into the array.
10592 uint32_t column_end;
10593 if (start.line == end.line) {
10594 column_end = end.column;
10595 } else {
10596 column_end = (uint32_t) (newline_list->offsets[start.line - start_line + 1] - newline_list->offsets[start.line - start_line] - 1);
10597 }
10598
10599 // Ensure we have at least one column of error.
10600 if (start.column == column_end) column_end++;
10601
10602 errors[index] = (pm_parse_error_t) {
10603 .error = error,
10604 .line = start.line,
10605 .column_start = start.column,
10606 .column_end = column_end
10607 };
10608 }
10609
10610 return errors;
10611}
10612
10613/* Append a literal string to the buffer. */
10614#define pm_buffer_append_literal(buffer, str) pm_buffer_append_string(buffer, str, rb_strlen_lit(str))
10615
10616static inline void
10617pm_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) {
10618 int32_t line_delta = line - parser->start_line;
10619 assert(line_delta >= 0);
10620
10621 size_t index = (size_t) line_delta;
10622 assert(index < newline_list->size);
10623
10624 const uint8_t *start = &parser->start[newline_list->offsets[index]];
10625 const uint8_t *end;
10626
10627 if (index >= newline_list->size - 1) {
10628 end = parser->end;
10629 } else {
10630 end = &parser->start[newline_list->offsets[index + 1]];
10631 }
10632
10633 pm_buffer_append_format(buffer, number_prefix, line);
10634
10635 // Here we determine if we should truncate the end of the line.
10636 bool truncate_end = false;
10637 if ((column_end != 0) && ((end - (start + column_end)) >= PM_ERROR_TRUNCATE)) {
10638 const uint8_t *end_candidate = start + column_end + PM_ERROR_TRUNCATE;
10639
10640 for (const uint8_t *ptr = start; ptr < end_candidate;) {
10641 size_t char_width = parser->encoding->char_width(ptr, parser->end - ptr);
10642
10643 // If we failed to decode a character, then just bail out and
10644 // truncate at the fixed width.
10645 if (char_width == 0) break;
10646
10647 // If this next character would go past the end candidate,
10648 // then we need to truncate before it.
10649 if (ptr + char_width > end_candidate) {
10650 end_candidate = ptr;
10651 break;
10652 }
10653
10654 ptr += char_width;
10655 }
10656
10657 end = end_candidate;
10658 truncate_end = true;
10659 }
10660
10661 // Here we determine if we should truncate the start of the line.
10662 if (column_start >= PM_ERROR_TRUNCATE) {
10663 pm_buffer_append_string(buffer, "... ", 4);
10664 start += column_start;
10665 }
10666
10667 pm_buffer_append_string(buffer, (const char *) start, (size_t) (end - start));
10668
10669 if (truncate_end) {
10670 pm_buffer_append_string(buffer, " ...\n", 5);
10671 } else if (end == parser->end && end[-1] != '\n') {
10672 pm_buffer_append_string(buffer, "\n", 1);
10673 }
10674}
10675
10679static void
10680pm_parse_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, pm_buffer_t *buffer, int highlight, bool inline_messages) {
10681 assert(error_list->size != 0);
10682
10683 // First, we're going to sort all of the errors by line number using an
10684 // insertion sort into a newly allocated array.
10685 const int32_t start_line = parser->start_line;
10686 const pm_newline_list_t *newline_list = &parser->newline_list;
10687
10688 pm_parse_error_t *errors = pm_parse_errors_format_sort(parser, error_list, newline_list);
10689 if (errors == NULL) return;
10690
10691 // Now we're going to determine how we're going to format line numbers and
10692 // blank lines based on the maximum number of digits in the line numbers
10693 // that are going to be displaid.
10694 pm_parse_error_format_t error_format;
10695 int32_t first_line_number = errors[0].line;
10696 int32_t last_line_number = errors[error_list->size - 1].line;
10697
10698 // If we have a maximum line number that is negative, then we're going to
10699 // use the absolute value for comparison but multiple by 10 to additionally
10700 // have a column for the negative sign.
10701 if (first_line_number < 0) first_line_number = (-first_line_number) * 10;
10702 if (last_line_number < 0) last_line_number = (-last_line_number) * 10;
10703 int32_t max_line_number = first_line_number > last_line_number ? first_line_number : last_line_number;
10704
10705 if (max_line_number < 10) {
10706 if (highlight > 0) {
10707 error_format = (pm_parse_error_format_t) {
10708 .number_prefix = PM_COLOR_GRAY "%1" PRIi32 " | " PM_COLOR_RESET,
10709 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10710 .divider = PM_COLOR_GRAY " ~~~~~" PM_COLOR_RESET "\n"
10711 };
10712 } else {
10713 error_format = (pm_parse_error_format_t) {
10714 .number_prefix = "%1" PRIi32 " | ",
10715 .blank_prefix = " | ",
10716 .divider = " ~~~~~\n"
10717 };
10718 }
10719 } else if (max_line_number < 100) {
10720 if (highlight > 0) {
10721 error_format = (pm_parse_error_format_t) {
10722 .number_prefix = PM_COLOR_GRAY "%2" PRIi32 " | " PM_COLOR_RESET,
10723 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10724 .divider = PM_COLOR_GRAY " ~~~~~~" PM_COLOR_RESET "\n"
10725 };
10726 } else {
10727 error_format = (pm_parse_error_format_t) {
10728 .number_prefix = "%2" PRIi32 " | ",
10729 .blank_prefix = " | ",
10730 .divider = " ~~~~~~\n"
10731 };
10732 }
10733 } else if (max_line_number < 1000) {
10734 if (highlight > 0) {
10735 error_format = (pm_parse_error_format_t) {
10736 .number_prefix = PM_COLOR_GRAY "%3" PRIi32 " | " PM_COLOR_RESET,
10737 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10738 .divider = PM_COLOR_GRAY " ~~~~~~~" PM_COLOR_RESET "\n"
10739 };
10740 } else {
10741 error_format = (pm_parse_error_format_t) {
10742 .number_prefix = "%3" PRIi32 " | ",
10743 .blank_prefix = " | ",
10744 .divider = " ~~~~~~~\n"
10745 };
10746 }
10747 } else if (max_line_number < 10000) {
10748 if (highlight > 0) {
10749 error_format = (pm_parse_error_format_t) {
10750 .number_prefix = PM_COLOR_GRAY "%4" PRIi32 " | " PM_COLOR_RESET,
10751 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10752 .divider = PM_COLOR_GRAY " ~~~~~~~~" PM_COLOR_RESET "\n"
10753 };
10754 } else {
10755 error_format = (pm_parse_error_format_t) {
10756 .number_prefix = "%4" PRIi32 " | ",
10757 .blank_prefix = " | ",
10758 .divider = " ~~~~~~~~\n"
10759 };
10760 }
10761 } else {
10762 if (highlight > 0) {
10763 error_format = (pm_parse_error_format_t) {
10764 .number_prefix = PM_COLOR_GRAY "%5" PRIi32 " | " PM_COLOR_RESET,
10765 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
10766 .divider = PM_COLOR_GRAY " ~~~~~~~~" PM_COLOR_RESET "\n"
10767 };
10768 } else {
10769 error_format = (pm_parse_error_format_t) {
10770 .number_prefix = "%5" PRIi32 " | ",
10771 .blank_prefix = " | ",
10772 .divider = " ~~~~~~~~\n"
10773 };
10774 }
10775 }
10776
10777 error_format.blank_prefix_length = strlen(error_format.blank_prefix);
10778 error_format.divider_length = strlen(error_format.divider);
10779
10780 // Now we're going to iterate through every error in our error list and
10781 // display it. While we're iterating, we will display some padding lines of
10782 // the source before the error to give some context. We'll be careful not to
10783 // display the same line twice in case the errors are close enough in the
10784 // source.
10785 int32_t last_line = parser->start_line - 1;
10786 uint32_t last_column_start = 0;
10787 const pm_encoding_t *encoding = parser->encoding;
10788
10789 for (size_t index = 0; index < error_list->size; index++) {
10790 pm_parse_error_t *error = &errors[index];
10791
10792 // Here we determine how many lines of padding of the source to display,
10793 // based on the difference from the last line that was displaid.
10794 if (error->line - last_line > 1) {
10795 if (error->line - last_line > 2) {
10796 if ((index != 0) && (error->line - last_line > 3)) {
10797 pm_buffer_append_string(buffer, error_format.divider, error_format.divider_length);
10798 }
10799
10800 pm_buffer_append_string(buffer, " ", 2);
10801 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 2, 0, 0, buffer);
10802 }
10803
10804 pm_buffer_append_string(buffer, " ", 2);
10805 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 1, 0, 0, buffer);
10806 }
10807
10808 // If this is the first error or we're on a new line, then we'll display
10809 // the line that has the error in it.
10810 if ((index == 0) || (error->line != last_line)) {
10811 if (highlight > 1) {
10812 pm_buffer_append_literal(buffer, PM_COLOR_RED "> " PM_COLOR_RESET);
10813 } else if (highlight > 0) {
10814 pm_buffer_append_literal(buffer, PM_COLOR_BOLD "> " PM_COLOR_RESET);
10815 } else {
10816 pm_buffer_append_literal(buffer, "> ");
10817 }
10818
10819 last_column_start = error->column_start;
10820
10821 // Find the maximum column end of all the errors on this line.
10822 uint32_t column_end = error->column_end;
10823 for (size_t next_index = index + 1; next_index < error_list->size; next_index++) {
10824 if (errors[next_index].line != error->line) break;
10825 if (errors[next_index].column_end > column_end) column_end = errors[next_index].column_end;
10826 }
10827
10828 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, error->line, error->column_start, column_end, buffer);
10829 }
10830
10831 const uint8_t *start = &parser->start[newline_list->offsets[error->line - start_line]];
10832 if (start == parser->end) pm_buffer_append_byte(buffer, '\n');
10833
10834 // Now we'll display the actual error message. We'll do this by first
10835 // putting the prefix to the line, then a bunch of blank spaces
10836 // depending on the column, then as many carets as we need to display
10837 // the width of the error, then the error message itself.
10838 //
10839 // Note that this doesn't take into account the width of the actual
10840 // character when displaid in the terminal. For some east-asian
10841 // languages or emoji, this means it can be thrown off pretty badly. We
10842 // will need to solve this eventually.
10843 pm_buffer_append_string(buffer, " ", 2);
10844 pm_buffer_append_string(buffer, error_format.blank_prefix, error_format.blank_prefix_length);
10845
10846 size_t column = 0;
10847 if (last_column_start >= PM_ERROR_TRUNCATE) {
10848 pm_buffer_append_string(buffer, " ", 4);
10849 column = last_column_start;
10850 }
10851
10852 while (column < error->column_start) {
10853 pm_buffer_append_byte(buffer, ' ');
10854
10855 size_t char_width = encoding->char_width(start + column, parser->end - (start + column));
10856 column += (char_width == 0 ? 1 : char_width);
10857 }
10858
10859 if (highlight > 1) pm_buffer_append_literal(buffer, PM_COLOR_RED);
10860 else if (highlight > 0) pm_buffer_append_literal(buffer, PM_COLOR_BOLD);
10861 pm_buffer_append_byte(buffer, '^');
10862
10863 size_t char_width = encoding->char_width(start + column, parser->end - (start + column));
10864 column += (char_width == 0 ? 1 : char_width);
10865
10866 while (column < error->column_end) {
10867 pm_buffer_append_byte(buffer, '~');
10868
10869 size_t char_width = encoding->char_width(start + column, parser->end - (start + column));
10870 column += (char_width == 0 ? 1 : char_width);
10871 }
10872
10873 if (highlight > 0) pm_buffer_append_literal(buffer, PM_COLOR_RESET);
10874
10875 if (inline_messages) {
10876 pm_buffer_append_byte(buffer, ' ');
10877 assert(error->error != NULL);
10878
10879 const char *message = error->error->message;
10880 pm_buffer_append_string(buffer, message, strlen(message));
10881 }
10882
10883 pm_buffer_append_byte(buffer, '\n');
10884
10885 // Here we determine how many lines of padding to display after the
10886 // error, depending on where the next error is in source.
10887 last_line = error->line;
10888 int32_t next_line;
10889
10890 if (index == error_list->size - 1) {
10891 next_line = (((int32_t) newline_list->size) + parser->start_line);
10892
10893 // If the file ends with a newline, subtract one from our "next_line"
10894 // so that we don't output an extra line at the end of the file
10895 if ((parser->start + newline_list->offsets[newline_list->size - 1]) == parser->end) {
10896 next_line--;
10897 }
10898 }
10899 else {
10900 next_line = errors[index + 1].line;
10901 }
10902
10903 if (next_line - last_line > 1) {
10904 pm_buffer_append_string(buffer, " ", 2);
10905 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, 0, 0, buffer);
10906 }
10907
10908 if (next_line - last_line > 1) {
10909 pm_buffer_append_string(buffer, " ", 2);
10910 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, 0, 0, buffer);
10911 }
10912 }
10913
10914 // Finally, we'll free the array of errors that we allocated.
10915 xfree(errors);
10916}
10917
10918#undef PM_ERROR_TRUNCATE
10919#undef PM_COLOR_GRAY
10920#undef PM_COLOR_RED
10921#undef PM_COLOR_RESET
10922
10929static bool
10930pm_parse_process_error_utf8_p(const pm_parser_t *parser, const pm_location_t *location)
10931{
10932 const size_t start_line = pm_newline_list_line_column(&parser->newline_list, location->start, 1).line;
10933 const size_t end_line = pm_newline_list_line_column(&parser->newline_list, location->end, 1).line;
10934
10935 const uint8_t *start = parser->start + parser->newline_list.offsets[start_line - 1];
10936 const uint8_t *end = ((end_line == parser->newline_list.size) ? parser->end : (parser->start + parser->newline_list.offsets[end_line]));
10937 size_t width;
10938
10939 while (start < end) {
10940 if ((width = pm_encoding_utf_8_char_width(start, end - start)) == 0) return false;
10941 start += width;
10942 }
10943
10944 return true;
10945}
10946
10951static VALUE
10952pm_parse_process_error(const pm_parse_result_t *result)
10953{
10954 const pm_parser_t *parser = &result->parser;
10955 const pm_diagnostic_t *head = (const pm_diagnostic_t *) parser->error_list.head;
10956 bool valid_utf8 = true;
10957
10958 pm_buffer_t buffer = { 0 };
10959 const pm_string_t *filepath = &parser->filepath;
10960
10961 int highlight = rb_stderr_tty_p();
10962 if (highlight) {
10963 const char *no_color = getenv("NO_COLOR");
10964 highlight = (no_color == NULL || no_color[0] == '\0') ? 2 : 1;
10965 }
10966
10967 for (const pm_diagnostic_t *error = head; error != NULL; error = (const pm_diagnostic_t *) error->node.next) {
10968 switch (error->level) {
10970 // It is implicitly assumed that the error messages will be
10971 // encodeable as UTF-8. Because of this, we can't include source
10972 // examples that contain invalid byte sequences. So if any source
10973 // examples include invalid UTF-8 byte sequences, we will skip
10974 // showing source examples entirely.
10975 if (valid_utf8 && !pm_parse_process_error_utf8_p(parser, &error->location)) {
10976 valid_utf8 = false;
10977 }
10978 break;
10980 // Any errors with the level PM_ERROR_LEVEL_ARGUMENT take over as
10981 // the only argument that gets raised. This is to allow priority
10982 // messages that should be handled before anything else.
10983 int32_t line_number = (int32_t) pm_location_line_number(parser, &error->location);
10984
10985 pm_buffer_append_format(
10986 &buffer,
10987 "%.*s:%" PRIi32 ": %s",
10988 (int) pm_string_length(filepath),
10989 pm_string_source(filepath),
10990 line_number,
10991 error->message
10992 );
10993
10994 if (pm_parse_process_error_utf8_p(parser, &error->location)) {
10995 pm_buffer_append_byte(&buffer, '\n');
10996
10997 pm_list_node_t *list_node = (pm_list_node_t *) error;
10998 pm_list_t error_list = { .size = 1, .head = list_node, .tail = list_node };
10999
11000 pm_parse_errors_format(parser, &error_list, &buffer, highlight, false);
11001 }
11002
11003 VALUE value = rb_exc_new(rb_eArgError, pm_buffer_value(&buffer), pm_buffer_length(&buffer));
11004 pm_buffer_free(&buffer);
11005
11006 return value;
11007 }
11008 case PM_ERROR_LEVEL_LOAD: {
11009 // Load errors are much simpler, because they don't include any of
11010 // the source in them. We create the error directly from the
11011 // message.
11012 VALUE message = rb_enc_str_new_cstr(error->message, rb_locale_encoding());
11013 VALUE value = rb_exc_new3(rb_eLoadError, message);
11014 rb_ivar_set(value, rb_intern_const("@path"), Qnil);
11015 return value;
11016 }
11017 }
11018 }
11019
11020 pm_buffer_append_format(
11021 &buffer,
11022 "%.*s:%" PRIi32 ": syntax error%s found\n",
11023 (int) pm_string_length(filepath),
11024 pm_string_source(filepath),
11025 (int32_t) pm_location_line_number(parser, &head->location),
11026 (parser->error_list.size > 1) ? "s" : ""
11027 );
11028
11029 if (valid_utf8) {
11030 pm_parse_errors_format(parser, &parser->error_list, &buffer, highlight, true);
11031 }
11032 else {
11033 for (const pm_diagnostic_t *error = head; error != NULL; error = (const pm_diagnostic_t *) error->node.next) {
11034 if (error != head) pm_buffer_append_byte(&buffer, '\n');
11035 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);
11036 }
11037 }
11038
11039 VALUE message = rb_enc_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer), result->node.encoding);
11040 VALUE error = rb_exc_new_str(rb_eSyntaxError, message);
11041
11042 rb_encoding *filepath_encoding = result->node.filepath_encoding != NULL ? result->node.filepath_encoding : rb_utf8_encoding();
11043 VALUE path = rb_enc_str_new((const char *) pm_string_source(filepath), pm_string_length(filepath), filepath_encoding);
11044
11045 rb_ivar_set(error, rb_intern_const("@path"), path);
11046 pm_buffer_free(&buffer);
11047
11048 return error;
11049}
11050
11056static VALUE
11057pm_parse_process(pm_parse_result_t *result, pm_node_t *node, VALUE *script_lines)
11058{
11059 pm_parser_t *parser = &result->parser;
11060
11061 // First, set up the scope node so that the AST node is attached and can be
11062 // freed regardless of whether or we return an error.
11063 pm_scope_node_t *scope_node = &result->node;
11064 rb_encoding *filepath_encoding = scope_node->filepath_encoding;
11065 int coverage_enabled = scope_node->coverage_enabled;
11066
11067 pm_scope_node_init(node, scope_node, NULL);
11068 scope_node->filepath_encoding = filepath_encoding;
11069
11070 scope_node->encoding = rb_enc_find(parser->encoding->name);
11071 if (!scope_node->encoding) rb_bug("Encoding not found %s!", parser->encoding->name);
11072
11073 scope_node->coverage_enabled = coverage_enabled;
11074
11075 // If RubyVM.keep_script_lines is set to true, then we need to create that
11076 // array of script lines here.
11077 if (script_lines != NULL) {
11078 *script_lines = rb_ary_new_capa(parser->newline_list.size);
11079
11080 for (size_t index = 0; index < parser->newline_list.size; index++) {
11081 size_t offset = parser->newline_list.offsets[index];
11082 size_t length = index == parser->newline_list.size - 1 ? ((size_t) (parser->end - (parser->start + offset))) : (parser->newline_list.offsets[index + 1] - offset);
11083 rb_ary_push(*script_lines, rb_enc_str_new((const char *) parser->start + offset, length, scope_node->encoding));
11084 }
11085
11086 scope_node->script_lines = script_lines;
11087 }
11088
11089 // Emit all of the various warnings from the parse.
11090 const pm_diagnostic_t *warning;
11091 const char *warning_filepath = (const char *) pm_string_source(&parser->filepath);
11092
11093 for (warning = (const pm_diagnostic_t *) parser->warning_list.head; warning != NULL; warning = (const pm_diagnostic_t *) warning->node.next) {
11094 int line = pm_location_line_number(parser, &warning->location);
11095
11096 if (warning->level == PM_WARNING_LEVEL_VERBOSE) {
11097 rb_enc_compile_warning(scope_node->encoding, warning_filepath, line, "%s", warning->message);
11098 }
11099 else {
11100 rb_enc_compile_warn(scope_node->encoding, warning_filepath, line, "%s", warning->message);
11101 }
11102 }
11103
11104 // If there are errors, raise an appropriate error and free the result.
11105 if (parser->error_list.size > 0) {
11106 VALUE error = pm_parse_process_error(result);
11107
11108 // TODO: We need to set the backtrace.
11109 // rb_funcallv(error, rb_intern("set_backtrace"), 1, &path);
11110 return error;
11111 }
11112
11113 // Now set up the constant pool and intern all of the various constants into
11114 // their corresponding IDs.
11115 scope_node->parser = parser;
11116 scope_node->constants = parser->constant_pool.size ? xcalloc(parser->constant_pool.size, sizeof(ID)) : NULL;
11117
11118 for (uint32_t index = 0; index < parser->constant_pool.size; index++) {
11119 pm_constant_t *constant = &parser->constant_pool.constants[index];
11120 scope_node->constants[index] = rb_intern3((const char *) constant->start, constant->length, scope_node->encoding);
11121 }
11122
11123 scope_node->index_lookup_table = st_init_numtable();
11124 pm_constant_id_list_t *locals = &scope_node->locals;
11125 for (size_t index = 0; index < locals->size; index++) {
11126 st_insert(scope_node->index_lookup_table, locals->ids[index], index);
11127 }
11128
11129 // If we got here, this is a success and we can return Qnil to indicate that
11130 // no error should be raised.
11131 result->parsed = true;
11132 return Qnil;
11133}
11134
11139static void
11140pm_options_frozen_string_literal_init(pm_options_t *options)
11141{
11142 int frozen_string_literal = rb_iseq_opt_frozen_string_literal();
11143
11144 switch (frozen_string_literal) {
11145 case ISEQ_FROZEN_STRING_LITERAL_UNSET:
11146 break;
11147 case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
11149 break;
11150 case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
11152 break;
11153 default:
11154 rb_bug("pm_options_frozen_string_literal_init: invalid frozen_string_literal=%d", frozen_string_literal);
11155 break;
11156 }
11157}
11158
11163static inline VALUE
11164pm_parse_file_script_lines(const pm_scope_node_t *scope_node, const pm_parser_t *parser)
11165{
11166 const pm_newline_list_t *newline_list = &parser->newline_list;
11167 const char *start = (const char *) parser->start;
11168 const char *end = (const char *) parser->end;
11169
11170 // If we end exactly on a newline, then there's no need to push on a final
11171 // segment. If we don't, then we need to push on the last offset up to the
11172 // end of the string.
11173 size_t last_offset = newline_list->offsets[newline_list->size - 1];
11174 bool last_push = start + last_offset != end;
11175
11176 // Create the ruby strings that represent the lines of the source.
11177 VALUE lines = rb_ary_new_capa(newline_list->size - (last_push ? 0 : 1));
11178
11179 for (size_t index = 0; index < newline_list->size - 1; index++) {
11180 size_t offset = newline_list->offsets[index];
11181 size_t length = newline_list->offsets[index + 1] - offset;
11182
11183 rb_ary_push(lines, rb_enc_str_new(start + offset, length, scope_node->encoding));
11184 }
11185
11186 // Push on the last line if we need to.
11187 if (last_push) {
11188 rb_ary_push(lines, rb_enc_str_new(start + last_offset, end - (start + last_offset), scope_node->encoding));
11189 }
11190
11191 return lines;
11192}
11193
11194// This is essentially pm_string_mapped_init(), preferring to memory map the
11195// file, with additional handling for files that require blocking to properly
11196// read (e.g. pipes).
11198pm_read_file(pm_string_t *string, const char *filepath)
11199{
11200#ifdef _WIN32
11201 // Open the file for reading.
11202 int length = MultiByteToWideChar(CP_UTF8, 0, filepath, -1, NULL, 0);
11203 if (length == 0) return PM_STRING_INIT_ERROR_GENERIC;
11204
11205 WCHAR *wfilepath = xmalloc(sizeof(WCHAR) * ((size_t) length));
11206 if ((wfilepath == NULL) || (MultiByteToWideChar(CP_UTF8, 0, filepath, -1, wfilepath, length) == 0)) {
11207 xfree(wfilepath);
11209 }
11210
11211 HANDLE file = CreateFileW(wfilepath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
11212 if (file == INVALID_HANDLE_VALUE) {
11214
11215 if (GetLastError() == ERROR_ACCESS_DENIED) {
11216 DWORD attributes = GetFileAttributesW(wfilepath);
11217 if ((attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
11219 }
11220 }
11221
11222 xfree(wfilepath);
11223 return result;
11224 }
11225
11226 // Get the file size.
11227 DWORD file_size = GetFileSize(file, NULL);
11228 if (file_size == INVALID_FILE_SIZE) {
11229 CloseHandle(file);
11230 xfree(wfilepath);
11232 }
11233
11234 // If the file is empty, then we don't need to do anything else, we'll set
11235 // the source to a constant empty string and return.
11236 if (file_size == 0) {
11237 CloseHandle(file);
11238 xfree(wfilepath);
11239 const uint8_t source[] = "";
11240 *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
11242 }
11243
11244 // Create a mapping of the file.
11245 HANDLE mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
11246 if (mapping == NULL) {
11247 CloseHandle(file);
11248 xfree(wfilepath);
11250 }
11251
11252 // Map the file into memory.
11253 uint8_t *source = (uint8_t *) MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
11254 CloseHandle(mapping);
11255 CloseHandle(file);
11256 xfree(wfilepath);
11257
11258 if (source == NULL) {
11260 }
11261
11262 *string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = (size_t) file_size };
11264#elif defined(_POSIX_MAPPED_FILES)
11265 // Open the file for reading
11266 const int open_mode = O_RDONLY | O_NONBLOCK;
11267 int fd = open(filepath, open_mode);
11268 if (fd == -1) {
11270 }
11271
11272 // Stat the file to get the file size
11273 struct stat sb;
11274 if (fstat(fd, &sb) == -1) {
11275 close(fd);
11277 }
11278
11279 // Ensure it is a file and not a directory
11280 if (S_ISDIR(sb.st_mode)) {
11281 close(fd);
11283 }
11284
11285 // We need to wait for data first before reading from pipes and character
11286 // devices. To not block the entire VM, we need to release the GVL while
11287 // reading. Use IO#read to do this and let the GC handle closing the FD.
11288 if (S_ISFIFO(sb.st_mode) || S_ISCHR(sb.st_mode)) {
11289 VALUE io = rb_io_fdopen((int) fd, open_mode, filepath);
11291 VALUE contents = rb_funcall(io, rb_intern("read"), 0);
11292
11293 if (!RB_TYPE_P(contents, T_STRING)) {
11295 }
11296
11297 long len = RSTRING_LEN(contents);
11298 if (len < 0) {
11300 }
11301
11302 size_t length = (size_t) len;
11303 uint8_t *source = malloc(length);
11304 memcpy(source, RSTRING_PTR(contents), length);
11305 *string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = length };
11306
11308 }
11309
11310 // mmap the file descriptor to virtually get the contents
11311 size_t size = (size_t) sb.st_size;
11312 uint8_t *source = NULL;
11313
11314 if (size == 0) {
11315 close(fd);
11316 const uint8_t source[] = "";
11317 *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
11319 }
11320
11321 source = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
11322 if (source == MAP_FAILED) {
11323 close(fd);
11325 }
11326
11327 close(fd);
11328 *string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = size };
11330#else
11331 return pm_string_file_init(string, filepath);
11332#endif
11333}
11334
11339VALUE
11340pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error)
11341{
11342 pm_string_init_result_t init_result = pm_read_file(&result->input, RSTRING_PTR(filepath));
11343
11344 if (init_result == PM_STRING_INIT_SUCCESS) {
11345 pm_options_frozen_string_literal_init(&result->options);
11346 return Qnil;
11347 }
11348
11349 int err;
11350 if (init_result == PM_STRING_INIT_ERROR_DIRECTORY) {
11351 err = EISDIR;
11352 } else {
11353#ifdef _WIN32
11354 err = rb_w32_map_errno(GetLastError());
11355#else
11356 err = errno;
11357#endif
11358 }
11359
11360 VALUE error;
11361 if (load_error) {
11362 VALUE message = rb_str_buf_new_cstr(strerror(err));
11363 rb_str_cat2(message, " -- ");
11364 rb_str_append(message, filepath);
11365
11366 error = rb_exc_new3(rb_eLoadError, message);
11367 rb_ivar_set(error, rb_intern_const("@path"), filepath);
11368 } else {
11369 error = rb_syserr_new(err, RSTRING_PTR(filepath));
11370 RB_GC_GUARD(filepath);
11371 }
11372
11373 return error;
11374}
11375
11382VALUE
11383pm_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines)
11384{
11385 result->node.filepath_encoding = rb_enc_get(filepath);
11386 pm_options_filepath_set(&result->options, RSTRING_PTR(filepath));
11387 RB_GC_GUARD(filepath);
11388
11389 pm_options_version_for_current_ruby_set(&result->options);
11390
11391 pm_parser_init(&result->parser, pm_string_source(&result->input), pm_string_length(&result->input), &result->options);
11392 pm_node_t *node = pm_parse(&result->parser);
11393
11394 VALUE error = pm_parse_process(result, node, script_lines);
11395
11396 // If we're parsing a filepath, then we need to potentially support the
11397 // SCRIPT_LINES__ constant, which can be a hash that has an array of lines
11398 // of every read file.
11399 ID id_script_lines = rb_intern("SCRIPT_LINES__");
11400
11401 if (rb_const_defined_at(rb_cObject, id_script_lines)) {
11402 VALUE constant_script_lines = rb_const_get_at(rb_cObject, id_script_lines);
11403
11404 if (RB_TYPE_P(constant_script_lines, T_HASH)) {
11405 rb_hash_aset(constant_script_lines, filepath, pm_parse_file_script_lines(&result->node, &result->parser));
11406 }
11407 }
11408
11409 return error;
11410}
11411
11416VALUE
11417pm_load_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines)
11418{
11419 VALUE error = pm_load_file(result, filepath, false);
11420 if (NIL_P(error)) {
11421 error = pm_parse_file(result, filepath, script_lines);
11422 }
11423
11424 return error;
11425}
11426
11433VALUE
11434pm_parse_string(pm_parse_result_t *result, VALUE source, VALUE filepath, VALUE *script_lines)
11435{
11436 rb_encoding *encoding = rb_enc_get(source);
11437 if (!rb_enc_asciicompat(encoding)) {
11438 return rb_exc_new_cstr(rb_eArgError, "invalid source encoding");
11439 }
11440
11441 pm_options_frozen_string_literal_init(&result->options);
11442 pm_string_constant_init(&result->input, RSTRING_PTR(source), RSTRING_LEN(source));
11443 pm_options_encoding_set(&result->options, rb_enc_name(encoding));
11444
11445 result->node.filepath_encoding = rb_enc_get(filepath);
11446 pm_options_filepath_set(&result->options, RSTRING_PTR(filepath));
11447 RB_GC_GUARD(filepath);
11448
11449 pm_options_version_for_current_ruby_set(&result->options);
11450
11451 pm_parser_init(&result->parser, pm_string_source(&result->input), pm_string_length(&result->input), &result->options);
11452 pm_node_t *node = pm_parse(&result->parser);
11453
11454 return pm_parse_process(result, node, script_lines);
11455}
11456
11458 VALUE rb_stdin;
11459 int eof_seen;
11460};
11461
11462static int
11463pm_parse_stdin_eof(void *stream)
11464{
11465 struct rb_stdin_wrapper * wrapped_stdin = (struct rb_stdin_wrapper *)stream;
11466 return wrapped_stdin->eof_seen;
11467}
11468
11469VALUE rb_io_gets_limit_internal(VALUE io, long limit);
11470
11474static char *
11475pm_parse_stdin_fgets(char *string, int size, void *stream)
11476{
11477 RUBY_ASSERT(size > 0);
11478
11479 struct rb_stdin_wrapper * wrapped_stdin = (struct rb_stdin_wrapper *)stream;
11480
11481 VALUE line = rb_io_gets_limit_internal(wrapped_stdin->rb_stdin, size - 1);
11482 if (NIL_P(line)) {
11483 return NULL;
11484 }
11485
11486 const char *cstr = RSTRING_PTR(line);
11487 long length = RSTRING_LEN(line);
11488
11489 memcpy(string, cstr, length);
11490 string[length] = '\0';
11491
11492 // We're reading strings from stdin via gets. We'll assume that if the
11493 // string is smaller than the requested length, and doesn't end with a
11494 // newline, that we hit EOF.
11495 if (length < (size - 1) && string[length - 1] != '\n') {
11496 wrapped_stdin->eof_seen = 1;
11497 }
11498
11499 return string;
11500}
11501
11502// We need access to this function when we're done parsing stdin.
11503void rb_reset_argf_lineno(long n);
11504
11510VALUE
11511pm_parse_stdin(pm_parse_result_t *result)
11512{
11513 pm_options_frozen_string_literal_init(&result->options);
11514
11515 struct rb_stdin_wrapper wrapped_stdin = {
11516 rb_stdin,
11517 0
11518 };
11519
11520 pm_buffer_t buffer;
11521 pm_node_t *node = pm_parse_stream(&result->parser, &buffer, (void *) &wrapped_stdin, pm_parse_stdin_fgets, pm_parse_stdin_eof, &result->options);
11522
11523 // Copy the allocated buffer contents into the input string so that it gets
11524 // freed. At this point we've handed over ownership, so we don't need to
11525 // free the buffer itself.
11526 pm_string_owned_init(&result->input, (uint8_t *) pm_buffer_value(&buffer), pm_buffer_length(&buffer));
11527
11528 // When we're done parsing, we reset $. because we don't want the fact that
11529 // we went through an IO object to be visible to the user.
11530 rb_reset_argf_lineno(0);
11531
11532 return pm_parse_process(result, node, NULL);
11533}
11534
11535#define PM_VERSION_FOR_RELEASE(major, minor) PM_VERSION_FOR_RELEASE_IMPL(major, minor)
11536#define PM_VERSION_FOR_RELEASE_IMPL(major, minor) PM_OPTIONS_VERSION_CRUBY_##major##_##minor
11537
11538void pm_options_version_for_current_ruby_set(pm_options_t *options) {
11539 options->version = PM_VERSION_FOR_RELEASE(RUBY_API_VERSION_MAJOR, RUBY_API_VERSION_MINOR);
11540}
11541
11542#undef NEW_ISEQ
11543#define NEW_ISEQ OLD_ISEQ
11544
11545#undef NEW_CHILD_ISEQ
11546#define NEW_CHILD_ISEQ OLD_CHILD_ISEQ
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
@ PM_WARNING_LEVEL_VERBOSE
For warnings which should be emitted if $VERBOSE == true.
Definition diagnostic.h:413
@ PM_ERROR_LEVEL_ARGUMENT
For errors that should raise an argument error.
Definition diagnostic.h:399
@ PM_ERROR_LEVEL_LOAD
For errors that should raise a load error.
Definition diagnostic.h:402
@ PM_ERROR_LEVEL_SYNTAX
For errors that should raise a syntax error.
Definition diagnostic.h:396
#define RUBY_EVENT_END
Encountered an end of a class clause.
Definition event.h:40
#define RUBY_EVENT_B_RETURN
Encountered a next statement.
Definition event.h:56
#define RUBY_EVENT_CLASS
Encountered a new class.
Definition event.h:39
#define RUBY_EVENT_LINE
Encountered a new line.
Definition event.h:38
#define RUBY_EVENT_RETURN
Encountered a return statement.
Definition event.h:42
#define RUBY_EVENT_B_CALL
Encountered an yield statement.
Definition event.h:55
#define RUBY_EVENT_CALL
A method, written in Ruby, is called.
Definition event.h:41
#define RUBY_EVENT_RESCUE
Encountered a rescue statement.
Definition event.h:61
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1674
#define ALLOCV
Old name of RB_ALLOCV.
Definition memory.h:404
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:400
#define RFLOAT_VALUE
Old name of rb_float_value.
Definition double.h:28
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define rb_str_cat2
Old name of rb_str_cat_cstr.
Definition string.h:1682
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
Definition fl_type.h:134
#define ULONG2NUM
Old name of RB_ULONG2NUM.
Definition long.h:60
#define FIXABLE
Old name of RB_FIXABLE.
Definition fixnum.h:25
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define ZALLOC_N
Old name of RB_ZALLOC_N.
Definition memory.h:401
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
#define ALLOC_N
Old name of RB_ALLOC_N.
Definition memory.h:399
#define rb_exc_new3
Old name of rb_exc_new_str.
Definition error.h:38
#define Qtrue
Old name of RUBY_Qtrue.
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define NIL_P
Old name of RB_NIL_P.
#define DBL2NUM
Old name of rb_float_new.
Definition double.h:29
#define xcalloc
Old name of ruby_xcalloc.
Definition xmalloc.h:55
#define NUM2LONG
Old name of RB_NUM2LONG.
Definition long.h:51
#define UINT2NUM
Old name of RB_UINT2NUM.
Definition int.h:46
#define CONST_ID
Old name of RUBY_CONST_ID.
Definition symbol.h:47
#define ruby_debug
This variable controls whether the interpreter is in debug mode.
Definition error.h:486
VALUE rb_eNotImpError
NotImplementedError exception.
Definition error.c:1440
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:683
VALUE rb_eStandardError
StandardError exception.
Definition error.c:1427
VALUE rb_eLoadError
LoadError exception.
Definition error.c:1448
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1430
VALUE rb_eNoMatchingPatternError
NoMatchingPatternError exception.
Definition error.c:1443
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:466
VALUE rb_exc_new(VALUE etype, const char *ptr, long len)
Creates an instance of the passed exception class.
Definition error.c:1468
VALUE rb_eNoMatchingPatternKeyError
NoMatchingPatternKeyError exception.
Definition error.c:1444
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Identical to rb_exc_new_cstr(), except it takes a Ruby's string instead of C's.
Definition error.c:1481
VALUE rb_eSyntaxError
SyntaxError exception.
Definition error.c:1447
VALUE rb_syserr_new(int n, const char *mesg)
Creates an exception object that represents the given C errno.
Definition error.c:3894
VALUE rb_cArray
Array class.
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:100
VALUE rb_stdin
STDIN constant.
Definition io.c:201
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1329
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:615
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:603
int rb_enc_str_coderange(VALUE str)
Scans the passed string to collect its code range.
Definition string.c:931
VALUE rb_enc_interned_str(const char *ptr, long len, rb_encoding *enc)
Identical to rb_enc_str_new(), except it returns a "f"string.
Definition string.c:12742
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:1139
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1117
VALUE rb_ary_cat(VALUE ary, const VALUE *train, long len)
Destructively appends multiple elements at the end of the array.
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_new_capa(long capa)
Identical to rb_ary_new(), except it additionally specifies how many rooms of objects it should alloc...
VALUE rb_ary_hidden_new(long capa)
Allocates a hidden (no class) empty array.
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
VALUE rb_ary_join(VALUE ary, VALUE sep)
Recursively stringises the elements of the passed array, flattens that result, then joins the sequenc...
void rb_ary_store(VALUE ary, long key, VALUE val)
Destructively stores the passed value to the passed array's passed index.
VALUE rb_io_fdopen(int fd, int flags, const char *path)
Creates an IO instance whose backend is the given file descriptor.
Definition io.c:9376
VALUE rb_range_new(VALUE beg, VALUE end, int excl)
Creates a new Range.
Definition range.c:69
VALUE rb_rational_new(VALUE num, VALUE den)
Constructs a Rational, with reduction.
Definition rational.c:1974
VALUE rb_str_append(VALUE dst, VALUE src)
Identical to rb_str_buf_append(), except it converts the right hand side before concatenating.
Definition string.c:3771
VALUE rb_str_tmp_new(long len)
Allocates a "temporary" string.
Definition string.c:1720
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1497
#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:1669
#define rb_str_buf_new_cstr(str)
Identical to rb_str_new_cstr, except done differently.
Definition string.h:1638
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:4008
VALUE rb_str_freeze(VALUE str)
This is the implementation of String#freeze.
Definition string.c:3252
#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:1513
VALUE rb_obj_as_string(VALUE obj)
Try converting an object to its stringised representation using its to_s method, if any.
Definition string.c:1824
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:1984
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:3416
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:3739
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:284
VALUE rb_id2sym(ID id)
Allocates an instance of rb_cSymbol that has the given id.
Definition symbol.c:974
VALUE rb_sym2str(VALUE symbol)
Obtain a frozen string representation of a symbol (not including the leading colon).
Definition symbol.c:993
@ RUBY_IO_READABLE
IO::READABLE
Definition io.h:97
VALUE rb_io_wait(VALUE io, VALUE events, VALUE timeout)
Blocks until the passed IO is ready for the passed events.
Definition io.c:1454
int len
Length of the buffer.
Definition io.h:8
VALUE rb_ractor_make_shareable(VALUE obj)
Destructively transforms the passed object so that multiple Ractors can share it.
Definition ractor.c:1430
#define DECIMAL_SIZE_OF(expr)
An approximation of decimal representation size.
Definition util.h:48
#define RUBY_API_VERSION_MAJOR
Major version.
Definition version.h:64
#define RUBY_API_VERSION_MINOR
Minor version.
Definition version.h:70
#define RB_INT2NUM
Just another name of rb_int2num_inline.
Definition int.h:37
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
VALUE type(ANYARGS)
ANYARGS-ed function type.
PRISM_EXPORTED_FUNCTION void pm_options_encoding_set(pm_options_t *options, const char *encoding)
Set the encoding option on the given options struct.
Definition options.c:24
PRISM_EXPORTED_FUNCTION void pm_options_free(pm_options_t *options)
Free the internal memory associated with the options.
Definition options.c:208
PRISM_EXPORTED_FUNCTION void pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal)
Set the frozen string literal option on the given options struct.
Definition options.c:48
PRISM_EXPORTED_FUNCTION void pm_options_filepath_set(pm_options_t *options, const char *filepath)
Set the filepath option on the given options struct.
Definition options.c:16
void pm_buffer_free(pm_buffer_t *buffer)
Free the memory associated with the buffer.
Definition pm_buffer.c:355
size_t pm_buffer_length(const pm_buffer_t *buffer)
Return the length of the buffer.
Definition pm_buffer.c:43
char * pm_buffer_value(const pm_buffer_t *buffer)
Return the value of the buffer.
Definition pm_buffer.c:35
uint32_t pm_constant_id_t
A constant id is a unique identifier for a constant in the constant pool.
PRISM_EXPORTED_FUNCTION size_t pm_string_length(const pm_string_t *string)
Returns the length associated with the string.
Definition pm_string.c:351
PRISM_EXPORTED_FUNCTION const uint8_t * pm_string_source(const pm_string_t *string)
Returns the start pointer associated with the string.
Definition pm_string.c:359
PRISM_EXPORTED_FUNCTION void pm_string_free(pm_string_t *string)
Free the associated memory of the given string.
Definition pm_string.c:367
PRISM_EXPORTED_FUNCTION pm_string_init_result_t pm_string_file_init(pm_string_t *string, const char *filepath)
Read the file indicated by the filepath parameter into source and load its contents and size into the...
Definition pm_string.c:210
pm_string_init_result_t
Represents the result of calling pm_string_mapped_init or pm_string_file_init.
Definition pm_string.h:105
@ PM_STRING_INIT_SUCCESS
Indicates that the string was successfully initialized.
Definition pm_string.h:107
@ PM_STRING_INIT_ERROR_GENERIC
Indicates a generic error from a string_*_init function, where the type of error should be read from ...
Definition pm_string.h:112
@ PM_STRING_INIT_ERROR_DIRECTORY
Indicates that the file that was attempted to be opened was a directory.
Definition pm_string.h:116
#define PM_ENCODING_US_ASCII_ENTRY
This is the US-ASCII encoding.
Definition encoding.h:252
#define PM_NODE_LIST_FOREACH(list, index, node)
Loop through each node in the node list, writing each node to the given pm_node_t pointer.
Definition node.h:17
PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse(pm_parser_t *parser)
Parse the Ruby source associated with the given parser and return the tree.
Definition prism.c:22970
PRISM_EXPORTED_FUNCTION void pm_parser_free(pm_parser_t *parser)
Free any memory associated with the given parser.
Definition prism.c:22944
PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, const pm_options_t *options)
Parse a stream of Ruby source and return the tree.
Definition prism.c:23057
PRISM_EXPORTED_FUNCTION void pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options)
Initialize a parser with the given start and end pointers.
Definition prism.c:22645
The main header file for the prism parser.
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
#define RARRAY_AREF(a, i)
Definition rarray.h:403
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
Definition rarray.h:52
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
#define RTEST
This is an old name of RB_TEST.
AliasGlobalVariableNode.
Definition ast.h:1106
struct pm_node * old_name
AliasGlobalVariableNode::old_name.
Definition ast.h:1129
struct pm_node * new_name
AliasGlobalVariableNode::new_name.
Definition ast.h:1119
AliasMethodNode.
Definition ast.h:1154
struct pm_node * old_name
AliasMethodNode::old_name.
Definition ast.h:1189
struct pm_node * new_name
AliasMethodNode::new_name.
Definition ast.h:1173
AlternationPatternNode.
Definition ast.h:1214
struct pm_node * left
AlternationPatternNode::left.
Definition ast.h:1227
struct pm_node * right
AlternationPatternNode::right.
Definition ast.h:1237
AndNode.
Definition ast.h:1262
struct pm_node * left
AndNode::left.
Definition ast.h:1278
struct pm_node * right
AndNode::right.
Definition ast.h:1291
ArgumentsNode.
Definition ast.h:1323
pm_node_t base
The embedded base node.
Definition ast.h:1325
struct pm_node_list arguments
ArgumentsNode::arguments.
Definition ast.h:1336
ArrayNode.
Definition ast.h:1354
struct pm_node_list elements
ArrayNode::elements.
Definition ast.h:1364
ArrayPatternNode.
Definition ast.h:1415
struct pm_node_list requireds
ArrayPatternNode::requireds.
Definition ast.h:1444
struct pm_node * rest
ArrayPatternNode::rest.
Definition ast.h:1454
struct pm_node * constant
ArrayPatternNode::constant.
Definition ast.h:1434
struct pm_node_list posts
ArrayPatternNode::posts.
Definition ast.h:1464
AssocNode.
Definition ast.h:1499
struct pm_node * value
AssocNode::value.
Definition ast.h:1531
struct pm_node * key
AssocNode::key.
Definition ast.h:1518
AssocSplatNode.
Definition ast.h:1556
struct pm_node * value
AssocSplatNode::value.
Definition ast.h:1569
BackReferenceReadNode.
Definition ast.h:1594
pm_node_t base
The embedded base node.
Definition ast.h:1596
BeginNode.
Definition ast.h:1625
struct pm_ensure_node * ensure_clause
BeginNode::ensure_clause.
Definition ast.h:1678
struct pm_rescue_node * rescue_clause
BeginNode::rescue_clause.
Definition ast.h:1658
struct pm_statements_node * statements
BeginNode::statements.
Definition ast.h:1648
struct pm_else_node * else_clause
BeginNode::else_clause.
Definition ast.h:1668
BlockArgumentNode.
Definition ast.h:1703
struct pm_node * expression
BlockArgumentNode::expression.
Definition ast.h:1716
BlockLocalVariableNode.
Definition ast.h:1744
BlockNode.
Definition ast.h:1772
struct pm_node * parameters
BlockNode::parameters.
Definition ast.h:1799
struct pm_node * body
BlockNode::body.
Definition ast.h:1809
pm_constant_id_list_t locals
BlockNode::locals.
Definition ast.h:1785
BlockParameterNode.
Definition ast.h:1848
BlockParametersNode.
Definition ast.h:1902
BreakNode.
Definition ast.h:1976
struct pm_arguments_node * arguments
BreakNode::arguments.
Definition ast.h:1989
A pm_buffer_t is a simple memory buffer that stores data in a contiguous block of memory.
Definition pm_buffer.h:22
CallAndWriteNode.
Definition ast.h:2020
struct pm_node * value
CallAndWriteNode::value.
Definition ast.h:2093
pm_constant_id_t read_name
CallAndWriteNode::read_name.
Definition ast.h:2063
pm_constant_id_t write_name
CallAndWriteNode::write_name.
Definition ast.h:2073
struct pm_node * receiver
CallAndWriteNode::receiver.
Definition ast.h:2033
CallNode.
Definition ast.h:2129
pm_location_t closing_loc
CallNode::closing_loc.
Definition ast.h:2210
struct pm_node * receiver
CallNode::receiver.
Definition ast.h:2148
pm_constant_id_t name
CallNode::name.
Definition ast.h:2171
pm_node_t base
The embedded base node.
Definition ast.h:2131
pm_location_t message_loc
CallNode::message_loc.
Definition ast.h:2181
struct pm_arguments_node * arguments
CallNode::arguments.
Definition ast.h:2200
struct pm_node * block
CallNode::block.
Definition ast.h:2220
CallOperatorWriteNode.
Definition ast.h:2241
pm_constant_id_t read_name
CallOperatorWriteNode::read_name.
Definition ast.h:2284
pm_constant_id_t binary_operator
CallOperatorWriteNode::binary_operator.
Definition ast.h:2304
struct pm_node * receiver
CallOperatorWriteNode::receiver.
Definition ast.h:2254
pm_constant_id_t write_name
CallOperatorWriteNode::write_name.
Definition ast.h:2294
struct pm_node * value
CallOperatorWriteNode::value.
Definition ast.h:2324
CallOrWriteNode.
Definition ast.h:2345
struct pm_node * receiver
CallOrWriteNode::receiver.
Definition ast.h:2358
struct pm_node * value
CallOrWriteNode::value.
Definition ast.h:2418
pm_constant_id_t write_name
CallOrWriteNode::write_name.
Definition ast.h:2398
pm_constant_id_t read_name
CallOrWriteNode::read_name.
Definition ast.h:2388
CallTargetNode.
Definition ast.h:2447
pm_constant_id_t name
CallTargetNode::name.
Definition ast.h:2480
struct pm_node * receiver
CallTargetNode::receiver.
Definition ast.h:2460
CapturePatternNode.
Definition ast.h:2505
struct pm_local_variable_target_node * target
CapturePatternNode::target.
Definition ast.h:2528
struct pm_node * value
CapturePatternNode::value.
Definition ast.h:2518
CaseMatchNode.
Definition ast.h:2555
struct pm_node_list conditions
CaseMatchNode::conditions.
Definition ast.h:2578
struct pm_else_node * else_clause
CaseMatchNode::else_clause.
Definition ast.h:2588
struct pm_node * predicate
CaseMatchNode::predicate.
Definition ast.h:2568
CaseNode.
Definition ast.h:2625
struct pm_node * predicate
CaseNode::predicate.
Definition ast.h:2638
struct pm_else_node * else_clause
CaseNode::else_clause.
Definition ast.h:2658
struct pm_node_list conditions
CaseNode::conditions.
Definition ast.h:2648
ClassNode.
Definition ast.h:2693
struct pm_node * constant_path
ClassNode::constant_path.
Definition ast.h:2716
pm_constant_id_list_t locals
ClassNode::locals.
Definition ast.h:2701
pm_constant_id_t name
ClassNode::name.
Definition ast.h:2766
struct pm_node * body
ClassNode::body.
Definition ast.h:2747
struct pm_node * superclass
ClassNode::superclass.
Definition ast.h:2736
ClassVariableAndWriteNode.
Definition ast.h:2781
struct pm_node * value
ClassVariableAndWriteNode::value.
Definition ast.h:2824
pm_constant_id_t name
ClassVariableAndWriteNode::name.
Definition ast.h:2794
ClassVariableOperatorWriteNode.
Definition ast.h:2839
pm_constant_id_t name
ClassVariableOperatorWriteNode::name.
Definition ast.h:2847
pm_constant_id_t binary_operator
ClassVariableOperatorWriteNode::binary_operator.
Definition ast.h:2867
struct pm_node * value
ClassVariableOperatorWriteNode::value.
Definition ast.h:2862
ClassVariableOrWriteNode.
Definition ast.h:2882
pm_constant_id_t name
ClassVariableOrWriteNode::name.
Definition ast.h:2890
struct pm_node * value
ClassVariableOrWriteNode::value.
Definition ast.h:2905
ClassVariableReadNode.
Definition ast.h:2920
pm_constant_id_t name
ClassVariableReadNode::name.
Definition ast.h:2934
ClassVariableTargetNode.
Definition ast.h:2949
pm_constant_id_t name
ClassVariableTargetNode::name.
Definition ast.h:2957
ClassVariableWriteNode.
Definition ast.h:2972
struct pm_node * value
ClassVariableWriteNode::value.
Definition ast.h:3009
pm_constant_id_t name
ClassVariableWriteNode::name.
Definition ast.h:2986
ConstantAndWriteNode.
Definition ast.h:3034
pm_location_t name_loc
ConstantAndWriteNode::name_loc.
Definition ast.h:3047
pm_constant_id_t name
ConstantAndWriteNode::name.
Definition ast.h:3042
struct pm_node * value
ConstantAndWriteNode::value.
Definition ast.h:3057
A list of constant IDs.
size_t size
The number of constant ids in the list.
size_t capacity
The number of constant ids that have been allocated in the list.
pm_constant_id_t * ids
The constant ids in the list.
ConstantOperatorWriteNode.
Definition ast.h:3072
pm_constant_id_t name
ConstantOperatorWriteNode::name.
Definition ast.h:3080
pm_location_t name_loc
ConstantOperatorWriteNode::name_loc.
Definition ast.h:3085
pm_constant_id_t binary_operator
ConstantOperatorWriteNode::binary_operator.
Definition ast.h:3100
struct pm_node * value
ConstantOperatorWriteNode::value.
Definition ast.h:3095
ConstantOrWriteNode.
Definition ast.h:3115
pm_location_t name_loc
ConstantOrWriteNode::name_loc.
Definition ast.h:3128
pm_constant_id_t name
ConstantOrWriteNode::name.
Definition ast.h:3123
struct pm_node * value
ConstantOrWriteNode::value.
Definition ast.h:3138
ConstantPathAndWriteNode.
Definition ast.h:3153
struct pm_constant_path_node * target
ConstantPathAndWriteNode::target.
Definition ast.h:3161
struct pm_node * value
ConstantPathAndWriteNode::value.
Definition ast.h:3171
ConstantPathNode.
Definition ast.h:3186
pm_constant_id_t name
ConstantPathNode::name.
Definition ast.h:3212
struct pm_node * parent
ConstantPathNode::parent.
Definition ast.h:3205
ConstantPathOperatorWriteNode.
Definition ast.h:3253
struct pm_constant_path_node * target
ConstantPathOperatorWriteNode::target.
Definition ast.h:3261
struct pm_node * value
ConstantPathOperatorWriteNode::value.
Definition ast.h:3271
pm_constant_id_t binary_operator
ConstantPathOperatorWriteNode::binary_operator.
Definition ast.h:3276
ConstantPathOrWriteNode.
Definition ast.h:3291
struct pm_node * value
ConstantPathOrWriteNode::value.
Definition ast.h:3309
struct pm_constant_path_node * target
ConstantPathOrWriteNode::target.
Definition ast.h:3299
ConstantPathTargetNode.
Definition ast.h:3324
struct pm_node * parent
ConstantPathTargetNode::parent.
Definition ast.h:3332
pm_constant_id_t name
ConstantPathTargetNode::name.
Definition ast.h:3337
ConstantPathWriteNode.
Definition ast.h:3368
struct pm_constant_path_node * target
ConstantPathWriteNode::target.
Definition ast.h:3384
struct pm_node * value
ConstantPathWriteNode::value.
Definition ast.h:3404
uint32_t size
The number of buckets in the hash map.
pm_constant_t * constants
The constants that are stored in the buckets.
ConstantReadNode.
Definition ast.h:3419
pm_node_t base
The embedded base node.
Definition ast.h:3421
pm_constant_id_t name
ConstantReadNode::name.
Definition ast.h:3433
A constant in the pool which effectively stores a string.
size_t length
The length of the string.
const uint8_t * start
A pointer to the start of the string.
ConstantTargetNode.
Definition ast.h:3448
pm_constant_id_t name
ConstantTargetNode::name.
Definition ast.h:3456
ConstantWriteNode.
Definition ast.h:3471
struct pm_node * value
ConstantWriteNode::value.
Definition ast.h:3508
pm_constant_id_t name
ConstantWriteNode::name.
Definition ast.h:3485
DefNode.
Definition ast.h:3534
struct pm_parameters_node * parameters
DefNode::parameters.
Definition ast.h:3557
pm_constant_id_t name
DefNode::name.
Definition ast.h:3542
struct pm_node * body
DefNode::body.
Definition ast.h:3562
struct pm_node * receiver
DefNode::receiver.
Definition ast.h:3552
pm_node_t base
The embedded base node.
Definition ast.h:3536
pm_constant_id_list_t locals
DefNode::locals.
Definition ast.h:3567
DefinedNode.
Definition ast.h:3612
struct pm_node * value
DefinedNode::value.
Definition ast.h:3625
This struct represents a diagnostic generated during parsing.
Definition diagnostic.h:364
pm_location_t location
The location of the diagnostic in the source.
Definition diagnostic.h:369
const char * message
The message associated with the diagnostic.
Definition diagnostic.h:375
pm_list_node_t node
The embedded base node.
Definition diagnostic.h:366
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
ElseNode.
Definition ast.h:3650
struct pm_statements_node * statements
ElseNode::statements.
Definition ast.h:3663
EmbeddedStatementsNode.
Definition ast.h:3683
struct pm_statements_node * statements
EmbeddedStatementsNode::statements.
Definition ast.h:3696
EmbeddedVariableNode.
Definition ast.h:3716
struct pm_node * variable
EmbeddedVariableNode::variable.
Definition ast.h:3729
This struct defines the functions necessary to implement the encoding interface so we can determine h...
Definition encoding.h:23
size_t(* char_width)(const uint8_t *b, ptrdiff_t n)
Return the number of bytes that the next character takes if it is valid in the encoding.
Definition encoding.h:29
const char * name
The name of the encoding.
Definition encoding.h:56
EnsureNode.
Definition ast.h:3748
struct pm_statements_node * statements
EnsureNode::statements.
Definition ast.h:3761
FindPatternNode.
Definition ast.h:3808
struct pm_node * constant
FindPatternNode::constant.
Definition ast.h:3821
struct pm_node * right
FindPatternNode::right.
Definition ast.h:3860
struct pm_node_list requireds
FindPatternNode::requireds.
Definition ast.h:3847
struct pm_splat_node * left
FindPatternNode::left.
Definition ast.h:3834
FlipFlopNode.
Definition ast.h:3904
pm_node_t base
The embedded base node.
Definition ast.h:3906
struct pm_node * left
FlipFlopNode::left.
Definition ast.h:3912
struct pm_node * right
FlipFlopNode::right.
Definition ast.h:3917
FloatNode.
Definition ast.h:3937
double value
FloatNode::value.
Definition ast.h:3947
ForNode.
Definition ast.h:3962
struct pm_statements_node * statements
ForNode::statements.
Definition ast.h:3997
struct pm_node * collection
ForNode::collection.
Definition ast.h:3985
ForwardingSuperNode.
Definition ast.h:4091
struct pm_block_node * block
ForwardingSuperNode::block.
Definition ast.h:4099
GlobalVariableAndWriteNode.
Definition ast.h:4114
struct pm_node * value
GlobalVariableAndWriteNode::value.
Definition ast.h:4137
pm_constant_id_t name
GlobalVariableAndWriteNode::name.
Definition ast.h:4122
GlobalVariableOperatorWriteNode.
Definition ast.h:4152
pm_constant_id_t name
GlobalVariableOperatorWriteNode::name.
Definition ast.h:4160
pm_constant_id_t binary_operator
GlobalVariableOperatorWriteNode::binary_operator.
Definition ast.h:4180
struct pm_node * value
GlobalVariableOperatorWriteNode::value.
Definition ast.h:4175
GlobalVariableOrWriteNode.
Definition ast.h:4195
pm_constant_id_t name
GlobalVariableOrWriteNode::name.
Definition ast.h:4203
struct pm_node * value
GlobalVariableOrWriteNode::value.
Definition ast.h:4218
GlobalVariableReadNode.
Definition ast.h:4233
pm_constant_id_t name
GlobalVariableReadNode::name.
Definition ast.h:4247
GlobalVariableTargetNode.
Definition ast.h:4262
pm_constant_id_t name
GlobalVariableTargetNode::name.
Definition ast.h:4270
GlobalVariableWriteNode.
Definition ast.h:4285
struct pm_node * value
GlobalVariableWriteNode::value.
Definition ast.h:4322
pm_constant_id_t name
GlobalVariableWriteNode::name.
Definition ast.h:4299
HashNode.
Definition ast.h:4347
struct pm_node_list elements
HashNode::elements.
Definition ast.h:4373
HashPatternNode.
Definition ast.h:4407
struct pm_node_list elements
HashPatternNode::elements.
Definition ast.h:4433
struct pm_node * rest
HashPatternNode::rest.
Definition ast.h:4449
struct pm_node * constant
HashPatternNode::constant.
Definition ast.h:4423
IfNode.
Definition ast.h:4496
struct pm_node * predicate
IfNode::predicate.
Definition ast.h:4529
struct pm_statements_node * statements
IfNode::statements.
Definition ast.h:4556
ImaginaryNode.
Definition ast.h:4602
struct pm_node * numeric
ImaginaryNode::numeric.
Definition ast.h:4610
ImplicitNode.
Definition ast.h:4631
struct pm_node * value
ImplicitNode::value.
Definition ast.h:4639
InNode.
Definition ast.h:4681
struct pm_statements_node * statements
InNode::statements.
Definition ast.h:4694
struct pm_node * pattern
InNode::pattern.
Definition ast.h:4689
IndexAndWriteNode.
Definition ast.h:4725
struct pm_arguments_node * arguments
IndexAndWriteNode::arguments.
Definition ast.h:4748
struct pm_node * receiver
IndexAndWriteNode::receiver.
Definition ast.h:4733
struct pm_block_argument_node * block
IndexAndWriteNode::block.
Definition ast.h:4758
struct pm_node * value
IndexAndWriteNode::value.
Definition ast.h:4768
IndexOperatorWriteNode.
Definition ast.h:4789
struct pm_block_argument_node * block
IndexOperatorWriteNode::block.
Definition ast.h:4822
struct pm_node * value
IndexOperatorWriteNode::value.
Definition ast.h:4837
struct pm_arguments_node * arguments
IndexOperatorWriteNode::arguments.
Definition ast.h:4812
pm_constant_id_t binary_operator
IndexOperatorWriteNode::binary_operator.
Definition ast.h:4827
struct pm_node * receiver
IndexOperatorWriteNode::receiver.
Definition ast.h:4797
IndexOrWriteNode.
Definition ast.h:4858
struct pm_block_argument_node * block
IndexOrWriteNode::block.
Definition ast.h:4891
struct pm_node * receiver
IndexOrWriteNode::receiver.
Definition ast.h:4866
struct pm_node * value
IndexOrWriteNode::value.
Definition ast.h:4901
struct pm_arguments_node * arguments
IndexOrWriteNode::arguments.
Definition ast.h:4881
IndexTargetNode.
Definition ast.h:4930
struct pm_node * receiver
IndexTargetNode::receiver.
Definition ast.h:4938
struct pm_arguments_node * arguments
IndexTargetNode::arguments.
Definition ast.h:4948
struct pm_block_argument_node * block
IndexTargetNode::block.
Definition ast.h:4958
InstanceVariableAndWriteNode.
Definition ast.h:4973
struct pm_node * value
InstanceVariableAndWriteNode::value.
Definition ast.h:4996
pm_constant_id_t name
InstanceVariableAndWriteNode::name.
Definition ast.h:4981
InstanceVariableOperatorWriteNode.
Definition ast.h:5011
struct pm_node * value
InstanceVariableOperatorWriteNode::value.
Definition ast.h:5034
pm_constant_id_t binary_operator
InstanceVariableOperatorWriteNode::binary_operator.
Definition ast.h:5039
pm_constant_id_t name
InstanceVariableOperatorWriteNode::name.
Definition ast.h:5019
InstanceVariableOrWriteNode.
Definition ast.h:5054
struct pm_node * value
InstanceVariableOrWriteNode::value.
Definition ast.h:5077
pm_constant_id_t name
InstanceVariableOrWriteNode::name.
Definition ast.h:5062
InstanceVariableReadNode.
Definition ast.h:5092
pm_constant_id_t name
InstanceVariableReadNode::name.
Definition ast.h:5106
InstanceVariableTargetNode.
Definition ast.h:5121
pm_constant_id_t name
InstanceVariableTargetNode::name.
Definition ast.h:5129
InstanceVariableWriteNode.
Definition ast.h:5144
pm_constant_id_t name
InstanceVariableWriteNode::name.
Definition ast.h:5158
struct pm_node * value
InstanceVariableWriteNode::value.
Definition ast.h:5181
IntegerNode.
Definition ast.h:5212
pm_integer_t value
IntegerNode::value.
Definition ast.h:5222
A structure represents an arbitrary-sized integer.
Definition pm_integer.h:20
size_t length
The number of allocated values.
Definition pm_integer.h:25
uint32_t value
Embedded value for small integer.
Definition pm_integer.h:36
uint32_t * values
List of 32-bit integers.
Definition pm_integer.h:30
bool negative
Whether or not the integer is negative.
Definition pm_integer.h:42
InterpolatedMatchLastLineNode.
Definition ast.h:5250
InterpolatedRegularExpressionNode.
Definition ast.h:5296
InterpolatedStringNode.
Definition ast.h:5333
struct pm_node_list parts
InterpolatedStringNode::parts.
Definition ast.h:5346
InterpolatedSymbolNode.
Definition ast.h:5366
struct pm_node_list parts
InterpolatedSymbolNode::parts.
Definition ast.h:5379
InterpolatedXStringNode.
Definition ast.h:5399
struct pm_node_list parts
InterpolatedXStringNode::parts.
Definition ast.h:5412
KeywordHashNode.
Definition ast.h:5471
struct pm_node_list elements
KeywordHashNode::elements.
Definition ast.h:5479
KeywordRestParameterNode.
Definition ast.h:5498
LambdaNode.
Definition ast.h:5531
struct pm_node * body
LambdaNode::body.
Definition ast.h:5564
pm_location_t opening_loc
LambdaNode::opening_loc.
Definition ast.h:5549
struct pm_node * parameters
LambdaNode::parameters.
Definition ast.h:5559
pm_location_t operator_loc
LambdaNode::operator_loc.
Definition ast.h:5544
pm_constant_id_list_t locals
LambdaNode::locals.
Definition ast.h:5539
A line and column in a string.
uint32_t column
The column number.
int32_t line
The line number.
This struct represents an abstract linked list that provides common functionality.
Definition pm_list.h:46
struct pm_list_node * next
A pointer to the next node in the list.
Definition pm_list.h:48
This represents the overall linked list.
Definition pm_list.h:55
pm_list_node_t * tail
A pointer to the tail of the list.
Definition pm_list.h:63
pm_list_node_t * head
A pointer to the head of the list.
Definition pm_list.h:60
size_t size
The size of the list.
Definition pm_list.h:57
the getlocal and setlocal instructions require two parameters.
LocalVariableAndWriteNode.
Definition ast.h:5579
pm_constant_id_t name
LocalVariableAndWriteNode::name.
Definition ast.h:5602
uint32_t depth
LocalVariableAndWriteNode::depth.
Definition ast.h:5607
struct pm_node * value
LocalVariableAndWriteNode::value.
Definition ast.h:5597
LocalVariableOperatorWriteNode.
Definition ast.h:5622
uint32_t depth
LocalVariableOperatorWriteNode::depth.
Definition ast.h:5655
pm_constant_id_t binary_operator
LocalVariableOperatorWriteNode::binary_operator.
Definition ast.h:5650
struct pm_node * value
LocalVariableOperatorWriteNode::value.
Definition ast.h:5640
pm_constant_id_t name
LocalVariableOperatorWriteNode::name.
Definition ast.h:5645
LocalVariableOrWriteNode.
Definition ast.h:5670
uint32_t depth
LocalVariableOrWriteNode::depth.
Definition ast.h:5698
struct pm_node * value
LocalVariableOrWriteNode::value.
Definition ast.h:5688
pm_constant_id_t name
LocalVariableOrWriteNode::name.
Definition ast.h:5693
LocalVariableReadNode.
Definition ast.h:5713
uint32_t depth
LocalVariableReadNode::depth.
Definition ast.h:5744
pm_constant_id_t name
LocalVariableReadNode::name.
Definition ast.h:5731
LocalVariableTargetNode.
Definition ast.h:5762
uint32_t depth
LocalVariableTargetNode::depth.
Definition ast.h:5775
pm_constant_id_t name
LocalVariableTargetNode::name.
Definition ast.h:5770
LocalVariableWriteNode.
Definition ast.h:5790
struct pm_node * value
LocalVariableWriteNode::value.
Definition ast.h:5844
uint32_t depth
LocalVariableWriteNode::depth.
Definition ast.h:5817
pm_constant_id_t name
LocalVariableWriteNode::name.
Definition ast.h:5804
This represents a range of bytes in the source string to which a node or token corresponds.
Definition ast.h:544
const uint8_t * start
A pointer to the start location of the range in the source.
Definition ast.h:546
const uint8_t * end
A pointer to the end location of the range in the source.
Definition ast.h:549
MatchLastLineNode.
Definition ast.h:5882
MatchPredicateNode.
Definition ast.h:5920
struct pm_node * pattern
MatchPredicateNode::pattern.
Definition ast.h:5933
struct pm_node * value
MatchPredicateNode::value.
Definition ast.h:5928
MatchRequiredNode.
Definition ast.h:5953
struct pm_node * value
MatchRequiredNode::value.
Definition ast.h:5966
struct pm_node * pattern
MatchRequiredNode::pattern.
Definition ast.h:6015
MatchWriteNode.
Definition ast.h:6040
struct pm_node_list targets
MatchWriteNode::targets.
Definition ast.h:6053
struct pm_call_node * call
MatchWriteNode::call.
Definition ast.h:6048
ModuleNode.
Definition ast.h:6083
struct pm_node * constant_path
ModuleNode::constant_path.
Definition ast.h:6101
struct pm_node * body
ModuleNode::body.
Definition ast.h:6106
pm_constant_id_list_t locals
ModuleNode::locals.
Definition ast.h:6091
pm_constant_id_t name
ModuleNode::name.
Definition ast.h:6116
MultiTargetNode.
Definition ast.h:6136
struct pm_node_list lefts
MultiTargetNode::lefts.
Definition ast.h:6154
struct pm_node * rest
MultiTargetNode::rest.
Definition ast.h:6174
struct pm_node_list rights
MultiTargetNode::rights.
Definition ast.h:6184
This is a node in the multi target state linked list.
As we're compiling a multi target, we need to track additional information whenever there is a parent...
MultiWriteNode.
Definition ast.h:6219
struct pm_node * value
MultiWriteNode::value.
Definition ast.h:6307
struct pm_node * rest
MultiWriteNode::rest.
Definition ast.h:6257
struct pm_node_list rights
MultiWriteNode::rights.
Definition ast.h:6267
struct pm_node_list lefts
MultiWriteNode::lefts.
Definition ast.h:6237
A list of offsets of newlines in a string.
const uint8_t * start
A pointer to the start of the source string.
size_t * offsets
The list of offsets.
size_t size
The number of offsets in the list.
NextNode.
Definition ast.h:6322
struct pm_arguments_node * arguments
NextNode::arguments.
Definition ast.h:6330
A list of nodes in the source, most often used for lists of children.
Definition ast.h:557
size_t size
The number of nodes in the list.
Definition ast.h:559
struct pm_node ** nodes
The nodes in the list.
Definition ast.h:565
This compiler defines its own concept of the location of a node.
int32_t line
This is the line number of a node.
uint32_t node_id
This is a unique identifier for the node.
This is the base structure that represents a node in the syntax tree.
Definition ast.h:1068
pm_node_type_t type
This represents the type of the node.
Definition ast.h:1073
uint32_t node_id
The unique identifier for this node, which is deterministic based on the source.
Definition ast.h:1085
pm_node_flags_t flags
This represents any flags on the node.
Definition ast.h:1079
pm_location_t location
This is the location of the node in the source.
Definition ast.h:1091
NumberedParametersNode.
Definition ast.h:6397
NumberedReferenceReadNode.
Definition ast.h:6420
uint32_t number
NumberedReferenceReadNode::number.
Definition ast.h:6436
OptionalKeywordParameterNode.
Definition ast.h:6455
pm_constant_id_t name
OptionalKeywordParameterNode::name.
Definition ast.h:6463
struct pm_node * value
OptionalKeywordParameterNode::value.
Definition ast.h:6473
OptionalParameterNode.
Definition ast.h:6492
struct pm_node * value
OptionalParameterNode::value.
Definition ast.h:6515
pm_constant_id_t name
OptionalParameterNode::name.
Definition ast.h:6500
The options that can be passed to the parser.
Definition options.h:104
pm_options_version_t version
The version of prism that we should be parsing with.
Definition options.h:150
OrNode.
Definition ast.h:6530
struct pm_node * left
OrNode::left.
Definition ast.h:6546
struct pm_node * right
OrNode::right.
Definition ast.h:6559
ParametersNode.
Definition ast.h:6585
struct pm_node * rest
ParametersNode::rest.
Definition ast.h:6603
struct pm_node_list requireds
ParametersNode::requireds.
Definition ast.h:6593
struct pm_block_parameter_node * block
ParametersNode::block.
Definition ast.h:6623
struct pm_node_list optionals
ParametersNode::optionals.
Definition ast.h:6598
struct pm_node_list posts
ParametersNode::posts.
Definition ast.h:6608
pm_node_t base
The embedded base node.
Definition ast.h:6587
struct pm_node * keyword_rest
ParametersNode::keyword_rest.
Definition ast.h:6618
struct pm_node_list keywords
ParametersNode::keywords.
Definition ast.h:6613
ParenthesesNode.
Definition ast.h:6641
struct pm_node * body
ParenthesesNode::body.
Definition ast.h:6649
The format that will be used to format the errors into the output.
size_t blank_prefix_length
The length of the blank prefix.
const char * blank_prefix
The prefix that will be used for blank lines.
size_t divider_length
The length of the divider.
const char * number_prefix
The prefix that will be used for line numbers.
const char * divider
The divider that will be used between sections of source code.
An error that is going to be formatted into the output.
pm_diagnostic_t * error
A pointer to the diagnostic that was generated during parsing.
uint32_t column_end
The column end of the diagnostic message.
int32_t line
The start line of the diagnostic message.
uint32_t column_start
The column start of the diagnostic message.
bool parsed
Whether or not this parse result has performed its parsing yet.
pm_scope_node_t node
The resulting scope node that will hold the generated AST.
pm_string_t input
The input that represents the source to be parsed.
pm_parser_t parser
The parser that will do the actual parsing.
pm_options_t options
The options that will be passed to the parser.
This struct represents the overall parser.
Definition parser.h:640
const pm_encoding_t * encoding
The encoding functions for the current file is attached to the parser as it's parsing so that it can ...
Definition parser.h:755
const uint8_t * end
The pointer to the end of the source.
Definition parser.h:694
pm_constant_pool_t constant_pool
This constant pool keeps all of the constants defined throughout the file so that we can reference th...
Definition parser.h:786
const uint8_t * start
The pointer to the start of the source.
Definition parser.h:691
pm_list_t error_list
The list of errors that have been found while parsing.
Definition parser.h:734
pm_list_t warning_list
The list of warnings that have been found while parsing.
Definition parser.h:731
int32_t start_line
The line number at the start of the parse.
Definition parser.h:809
pm_string_t filepath
This is the path of the file being parsed.
Definition parser.h:780
pm_newline_list_t newline_list
This is the list of newline offsets in the source file.
Definition parser.h:789
PinnedExpressionNode.
Definition ast.h:6674
PinnedVariableNode.
Definition ast.h:6732
struct pm_node * variable
PinnedVariableNode::variable.
Definition ast.h:6745
PostExecutionNode.
Definition ast.h:6770
struct pm_statements_node * statements
PostExecutionNode::statements.
Definition ast.h:6778
PreExecutionNode.
Definition ast.h:6808
struct pm_statements_node * statements
PreExecutionNode::statements.
Definition ast.h:6816
ProgramNode.
Definition ast.h:6843
struct pm_statements_node * statements
ProgramNode::statements.
Definition ast.h:6856
RangeNode.
Definition ast.h:6877
struct pm_node * right
RangeNode::right.
Definition ast.h:6907
struct pm_node * left
RangeNode::left.
Definition ast.h:6893
RationalNode.
Definition ast.h:6935
pm_integer_t denominator
RationalNode::denominator.
Definition ast.h:6956
pm_integer_t numerator
RationalNode::numerator.
Definition ast.h:6947
RegularExpressionNode.
Definition ast.h:7002
RequiredKeywordParameterNode.
Definition ast.h:7044
RequiredParameterNode.
Definition ast.h:7076
pm_constant_id_t name
RequiredParameterNode::name.
Definition ast.h:7084
RescueModifierNode.
Definition ast.h:7099
struct pm_node * rescue_expression
RescueModifierNode::rescue_expression.
Definition ast.h:7117
struct pm_node * expression
RescueModifierNode::expression.
Definition ast.h:7107
RescueNode.
Definition ast.h:7137
struct pm_rescue_node * subsequent
RescueNode::subsequent.
Definition ast.h:7175
struct pm_node * reference
RescueNode::reference.
Definition ast.h:7160
struct pm_node_list exceptions
RescueNode::exceptions.
Definition ast.h:7150
struct pm_statements_node * statements
RescueNode::statements.
Definition ast.h:7170
RestParameterNode.
Definition ast.h:7194
ReturnNode.
Definition ast.h:7245
struct pm_arguments_node * arguments
ReturnNode::arguments.
Definition ast.h:7258
rb_encoding * filepath_encoding
This is the encoding of the actual filepath object that will be used when a FILE node is compiled or ...
struct iseq_link_anchor * pre_execution_anchor
This will only be set on the top-level scope node.
VALUE * script_lines
This is a pointer to the list of script lines for the ISEQs that will be associated with this scope n...
ShareableConstantNode.
Definition ast.h:7297
struct pm_node * write
ShareableConstantNode::write.
Definition ast.h:7307
pm_node_t base
The embedded base node.
Definition ast.h:7299
SingletonClassNode.
Definition ast.h:7322
pm_constant_id_list_t locals
SingletonClassNode::locals.
Definition ast.h:7330
struct pm_node * expression
SingletonClassNode::expression.
Definition ast.h:7345
struct pm_node * body
SingletonClassNode::body.
Definition ast.h:7350
SourceFileNode.
Definition ast.h:7394
pm_string_t filepath
SourceFileNode::filepath.
Definition ast.h:7404
SplatNode.
Definition ast.h:7437
struct pm_node * expression
SplatNode::expression.
Definition ast.h:7450
StatementsNode.
Definition ast.h:7465
struct pm_node_list body
StatementsNode::body.
Definition ast.h:7473
pm_node_t base
The embedded base node.
Definition ast.h:7467
StringNode.
Definition ast.h:7500
pm_string_t unescaped
StringNode::unescaped.
Definition ast.h:7523
A generic string type that can have various ownership semantics.
Definition pm_string.h:33
SuperNode.
Definition ast.h:7541
struct pm_arguments_node * arguments
SuperNode::arguments.
Definition ast.h:7559
struct pm_node * block
SuperNode::block.
Definition ast.h:7569
SymbolNode.
Definition ast.h:7592
pm_string_t unescaped
SymbolNode::unescaped.
Definition ast.h:7615
pm_node_t base
The embedded base node.
Definition ast.h:7594
UndefNode.
Definition ast.h:7648
struct pm_node_list names
UndefNode::names.
Definition ast.h:7656
UnlessNode.
Definition ast.h:7679
struct pm_statements_node * statements
UnlessNode::statements.
Definition ast.h:7729
struct pm_node * predicate
UnlessNode::predicate.
Definition ast.h:7708
struct pm_else_node * else_clause
UnlessNode::else_clause.
Definition ast.h:7739
UntilNode.
Definition ast.h:7770
pm_node_t base
The embedded base node.
Definition ast.h:7772
WhenNode.
Definition ast.h:7815
WhileNode.
Definition ast.h:7859
pm_node_t base
The embedded base node.
Definition ast.h:7861
XStringNode.
Definition ast.h:7906
pm_string_t unescaped
XStringNode::unescaped.
Definition ast.h:7929
YieldNode.
Definition ast.h:7944
struct pm_arguments_node * arguments
YieldNode::arguments.
Definition ast.h:7962
struct rb_iseq_constant_body::@157 param
parameter information
Definition st.h:79
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376