[ruby/digest] [DOC] Update document to use `rb_digest_make_metadata`
[ruby.git] / prism_compile.c
blob7fefe7c0e40ee63b047d8b1735dbb971daf828aa
1 #include "prism.h"
3 /******************************************************************************/
4 /* These macros operate on pm_line_column_t structs as opposed to NODE*s. */
5 /******************************************************************************/
7 #define PUSH_ADJUST(seq, location, label) \
8 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), (int) (location).line))
10 #define PUSH_ADJUST_RESTORE(seq, label) \
11 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), -1))
13 #define PUSH_INSN(seq, location, insn) \
14 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).column, BIN(insn), 0))
16 #define PUSH_INSN1(seq, location, insn, op1) \
17 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).column, BIN(insn), 1, (VALUE)(op1)))
19 #define PUSH_INSN2(seq, location, insn, op1, op2) \
20 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).column, BIN(insn), 2, (VALUE)(op1), (VALUE)(op2)))
22 #define PUSH_INSN3(seq, location, insn, op1, op2, op3) \
23 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (int) (location).line, (int) (location).column, BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3)))
25 #define PUSH_INSNL(seq, location, insn, label) \
26 (PUSH_INSN1(seq, location, insn, label), LABEL_REF(label))
28 #define PUSH_LABEL(seq, label) \
29 ADD_ELEM((seq), (LINK_ELEMENT *) (label))
31 #define PUSH_SEND_R(seq, location, id, argc, block, flag, keywords) \
32 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (int) (location).line, (int) (location).column, (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords)))
34 #define PUSH_SEND(seq, location, id, argc) \
35 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)INT2FIX(0), NULL)
37 #define PUSH_SEND_WITH_FLAG(seq, location, id, argc, flag) \
38 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)(flag), NULL)
40 #define PUSH_SEND_WITH_BLOCK(seq, location, id, argc, block) \
41 PUSH_SEND_R((seq), location, (id), (argc), (block), (VALUE)INT2FIX(0), NULL)
43 #define PUSH_CALL(seq, location, id, argc) \
44 PUSH_SEND_R((seq), location, (id), (argc), NULL, (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
46 #define PUSH_CALL_WITH_BLOCK(seq, location, id, argc, block) \
47 PUSH_SEND_R((seq), location, (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
49 #define PUSH_TRACE(seq, event) \
50 ADD_ELEM((seq), (LINK_ELEMENT *) new_trace_body(iseq, (event), 0))
52 #define PUSH_CATCH_ENTRY(type, ls, le, iseqv, lc) \
53 ADD_CATCH_ENTRY((type), (ls), (le), (iseqv), (lc))
55 #define PUSH_SEQ(seq1, seq2) \
56 APPEND_LIST((seq1), (seq2))
58 #define PUSH_SYNTHETIC_PUTNIL(seq, iseq) \
59 do { \
60 int lineno = ISEQ_COMPILE_DATA(iseq)->last_line; \
61 if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq)); \
62 ADD_SYNTHETIC_INSN(seq, lineno, -1, putnil); \
63 } while (0)
65 /******************************************************************************/
66 /* These functions compile getlocal/setlocal instructions but operate on */
67 /* prism locations instead of NODEs. */
68 /******************************************************************************/
70 static void
71 pm_iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line_no, int column, int idx, int level)
73 if (iseq_local_block_param_p(iseq, idx, level)) {
74 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line_no, column, BIN(getblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
76 else {
77 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line_no, column, BIN(getlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
79 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qfalse);
82 static void
83 pm_iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line_no, int column, int idx, int level)
85 if (iseq_local_block_param_p(iseq, idx, level)) {
86 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line_no, column, BIN(setblockparam), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
88 else {
89 ADD_ELEM(seq, (LINK_ELEMENT *) new_insn_body(iseq, line_no, column, BIN(setlocal), 2, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)));
91 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue);
94 #define PUSH_GETLOCAL(seq, location, idx, level) \
95 pm_iseq_add_getlocal(iseq, (seq), (int) (location).line, (int) (location).column, (idx), (level))
97 #define PUSH_SETLOCAL(seq, location, idx, level) \
98 pm_iseq_add_setlocal(iseq, (seq), (int) (location).line, (int) (location).column, (idx), (level))
100 /******************************************************************************/
101 /* These are helper macros for the compiler. */
102 /******************************************************************************/
104 #define OLD_ISEQ NEW_ISEQ
105 #undef NEW_ISEQ
107 #define NEW_ISEQ(node, name, type, line_no) \
108 pm_new_child_iseq(iseq, (node), rb_fstring(name), 0, (type), (line_no))
110 #define OLD_CHILD_ISEQ NEW_CHILD_ISEQ
111 #undef NEW_CHILD_ISEQ
113 #define NEW_CHILD_ISEQ(node, name, type, line_no) \
114 pm_new_child_iseq(iseq, (node), rb_fstring(name), iseq, (type), (line_no))
116 #define PM_COMPILE(node) \
117 pm_compile_node(iseq, (node), ret, popped, scope_node)
119 #define PM_COMPILE_INTO_ANCHOR(_ret, node) \
120 pm_compile_node(iseq, (node), _ret, popped, scope_node)
122 #define PM_COMPILE_POPPED(node) \
123 pm_compile_node(iseq, (node), ret, true, scope_node)
125 #define PM_COMPILE_NOT_POPPED(node) \
126 pm_compile_node(iseq, (node), ret, false, scope_node)
128 #define PM_SPECIAL_CONSTANT_FLAG ((pm_constant_id_t)(1 << 31))
129 #define PM_CONSTANT_AND ((pm_constant_id_t)(idAnd | PM_SPECIAL_CONSTANT_FLAG))
130 #define PM_CONSTANT_DOT3 ((pm_constant_id_t)(idDot3 | PM_SPECIAL_CONSTANT_FLAG))
131 #define PM_CONSTANT_MULT ((pm_constant_id_t)(idMULT | PM_SPECIAL_CONSTANT_FLAG))
132 #define PM_CONSTANT_POW ((pm_constant_id_t)(idPow | PM_SPECIAL_CONSTANT_FLAG))
134 #define PM_NODE_START_LINE_COLUMN(parser, node) \
135 pm_newline_list_line_column(&(parser)->newline_list, ((const pm_node_t *) (node))->location.start, (parser)->start_line)
137 #define PM_NODE_END_LINE_COLUMN(parser, node) \
138 pm_newline_list_line_column(&(parser)->newline_list, ((const pm_node_t *) (node))->location.end, (parser)->start_line)
140 #define PM_LOCATION_START_LINE_COLUMN(parser, location) \
141 pm_newline_list_line_column(&(parser)->newline_list, (location)->start, (parser)->start_line)
143 static int
144 pm_node_line_number(const pm_parser_t *parser, const pm_node_t *node)
146 return (int) PM_NODE_START_LINE_COLUMN(parser, node).line;
149 static int
150 pm_location_line_number(const pm_parser_t *parser, const pm_location_t *location) {
151 return (int) PM_LOCATION_START_LINE_COLUMN(parser, location).line;
155 * Parse the value of a pm_integer_t into a Ruby Integer.
157 static VALUE
158 parse_integer_value(const pm_integer_t *integer)
160 VALUE result;
162 if (integer->values == NULL) {
163 result = UINT2NUM(integer->value);
165 else {
166 VALUE string = rb_str_new(NULL, integer->length * 8);
167 unsigned char *bytes = (unsigned char *) RSTRING_PTR(string);
169 size_t offset = integer->length * 8;
170 for (size_t value_index = 0; value_index < integer->length; value_index++) {
171 uint32_t value = integer->values[value_index];
173 for (int index = 0; index < 8; index++) {
174 int byte = (value >> (4 * index)) & 0xf;
175 bytes[--offset] = byte < 10 ? byte + '0' : byte - 10 + 'a';
179 result = rb_funcall(string, rb_intern("to_i"), 1, UINT2NUM(16));
182 if (integer->negative) {
183 result = rb_funcall(result, rb_intern("-@"), 0);
186 return result;
190 * Convert the value of an integer node into a Ruby Integer.
192 static inline VALUE
193 parse_integer(const pm_integer_node_t *node)
195 return parse_integer_value(&node->value);
199 * Convert the value of a float node into a Ruby Float.
201 static VALUE
202 parse_float(const pm_float_node_t *node)
204 return DBL2NUM(node->value);
208 * Convert the value of a rational node into a Ruby Rational. Rational nodes can
209 * either be wrapping an integer node or a float node. If it's an integer node,
210 * we can reuse our parsing. If it's not, then we'll parse the numerator and
211 * then parse the denominator and create the rational from those two values.
213 static VALUE
214 parse_rational(const pm_rational_node_t *node)
216 VALUE numerator = parse_integer_value(&node->numerator);
217 VALUE denominator = parse_integer_value(&node->denominator);
218 return rb_rational_new(numerator, denominator);
222 * Convert the value of an imaginary node into a Ruby Complex. Imaginary nodes
223 * can be wrapping an integer node, a float node, or a rational node. In all
224 * cases we will reuse parsing functions seen above to get the inner value, and
225 * then convert into an imaginary with rb_complex_raw.
227 static VALUE
228 parse_imaginary(const pm_imaginary_node_t *node)
230 VALUE imaginary_part;
231 switch (PM_NODE_TYPE(node->numeric)) {
232 case PM_FLOAT_NODE: {
233 imaginary_part = parse_float((const pm_float_node_t *) node->numeric);
234 break;
236 case PM_INTEGER_NODE: {
237 imaginary_part = parse_integer((const pm_integer_node_t *) node->numeric);
238 break;
240 case PM_RATIONAL_NODE: {
241 imaginary_part = parse_rational((const pm_rational_node_t *) node->numeric);
242 break;
244 default:
245 rb_bug("Unexpected numeric type on imaginary number %s\n", pm_node_type_to_str(PM_NODE_TYPE(node->numeric)));
248 return rb_complex_raw(INT2FIX(0), imaginary_part);
251 static inline VALUE
252 parse_string(const pm_scope_node_t *scope_node, const pm_string_t *string)
254 return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), scope_node->encoding);
258 * Certain strings can have their encoding differ from the parser's encoding due
259 * to bytes or escape sequences that have the top bit set. This function handles
260 * creating those strings based on the flags set on the owning node.
262 static inline VALUE
263 parse_string_encoded(const pm_node_t *node, const pm_string_t *string, rb_encoding *default_encoding)
265 rb_encoding *encoding;
267 if (node->flags & PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING) {
268 encoding = rb_ascii8bit_encoding();
270 else if (node->flags & PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING) {
271 encoding = rb_utf8_encoding();
273 else {
274 encoding = default_encoding;
277 return rb_enc_str_new((const char *) pm_string_source(string), pm_string_length(string), encoding);
280 static inline VALUE
281 parse_static_literal_string(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *string)
283 rb_encoding *encoding;
285 if (node->flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) {
286 encoding = rb_ascii8bit_encoding();
288 else if (node->flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) {
289 encoding = rb_utf8_encoding();
291 else {
292 encoding = scope_node->encoding;
295 VALUE value = rb_enc_literal_str((const char *) pm_string_source(string), pm_string_length(string), encoding);
296 rb_enc_str_coderange(value);
298 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
299 int line_number = pm_node_line_number(scope_node->parser, node);
300 VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line_number));
301 value = rb_str_dup(value);
302 rb_ivar_set(value, id_debug_created_info, rb_obj_freeze(debug_info));
303 rb_str_freeze(value);
306 return value;
309 static inline ID
310 parse_string_symbol(const pm_scope_node_t *scope_node, const pm_symbol_node_t *symbol)
312 rb_encoding *encoding;
313 if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING) {
314 encoding = rb_utf8_encoding();
316 else if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING) {
317 encoding = rb_ascii8bit_encoding();
319 else if (symbol->base.flags & PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING) {
320 encoding = rb_usascii_encoding();
322 else {
323 encoding = scope_node->encoding;
326 return rb_intern3((const char *) pm_string_source(&symbol->unescaped), pm_string_length(&symbol->unescaped), encoding);
329 static int
330 pm_optimizable_range_item_p(const pm_node_t *node)
332 return (!node || PM_NODE_TYPE_P(node, PM_INTEGER_NODE) || PM_NODE_TYPE_P(node, PM_NIL_NODE));
335 /** Raise an error corresponding to the invalid regular expression. */
336 static VALUE
337 parse_regexp_error(rb_iseq_t *iseq, int32_t line_number, const char *fmt, ...)
339 va_list args;
340 va_start(args, fmt);
341 VALUE error = rb_syntax_error_append(Qnil, rb_iseq_path(iseq), line_number, -1, NULL, "%" PRIsVALUE, args);
342 va_end(args);
343 rb_exc_raise(error);
346 static VALUE
347 parse_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)
349 // If we were passed an explicit regexp encoding, then we need to double
350 // check that it's okay here for this fragment of the string.
351 rb_encoding *encoding;
353 if (explicit_regexp_encoding != NULL) {
354 encoding = explicit_regexp_encoding;
356 else if (node->flags & PM_STRING_FLAGS_FORCED_BINARY_ENCODING) {
357 encoding = rb_ascii8bit_encoding();
359 else if (node->flags & PM_STRING_FLAGS_FORCED_UTF8_ENCODING) {
360 encoding = rb_utf8_encoding();
362 else {
363 encoding = implicit_regexp_encoding;
366 VALUE string = rb_enc_str_new((const char *) pm_string_source(unescaped), pm_string_length(unescaped), encoding);
367 VALUE error = rb_reg_check_preprocess(string);
369 if (error != Qnil) parse_regexp_error(iseq, pm_node_line_number(scope_node->parser, node), "%" PRIsVALUE, rb_obj_as_string(error));
370 return string;
373 static VALUE
374 pm_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)
376 VALUE current = Qnil;
378 for (size_t index = 0; index < nodes->size; index++) {
379 const pm_node_t *part = nodes->nodes[index];
380 VALUE string;
382 switch (PM_NODE_TYPE(part)) {
383 case PM_STRING_NODE:
384 if (implicit_regexp_encoding != NULL) {
385 if (top) {
386 string = parse_regexp_string_part(iseq, scope_node, part, &((const pm_string_node_t *) part)->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
388 else {
389 string = parse_string_encoded(part, &((const pm_string_node_t *) part)->unescaped, scope_node->encoding);
390 VALUE error = rb_reg_check_preprocess(string);
391 if (error != Qnil) parse_regexp_error(iseq, pm_node_line_number(scope_node->parser, part), "%" PRIsVALUE, rb_obj_as_string(error));
394 else {
395 string = parse_string_encoded(part, &((const pm_string_node_t *) part)->unescaped, scope_node->encoding);
397 break;
398 case PM_INTERPOLATED_STRING_NODE:
399 string = pm_static_literal_concat(iseq, &((const pm_interpolated_string_node_t *) part)->parts, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
400 break;
401 case PM_EMBEDDED_STATEMENTS_NODE: {
402 const pm_embedded_statements_node_t *cast = (const pm_embedded_statements_node_t *) part;
403 string = pm_static_literal_concat(iseq, &cast->statements->body, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
404 break;
406 default:
407 RUBY_ASSERT(false && "unexpected node type in pm_static_literal_concat");
408 return Qnil;
411 if (current != Qnil) {
412 current = rb_str_concat(current, string);
414 else {
415 current = string;
419 return top ? rb_fstring(current) : current;
422 #define RE_OPTION_ENCODING_SHIFT 8
423 #define RE_OPTION_ENCODING(encoding) (((encoding) & 0xFF) << RE_OPTION_ENCODING_SHIFT)
424 #define ARG_ENCODING_NONE 32
425 #define ARG_ENCODING_FIXED 16
426 #define ENC_ASCII8BIT 1
427 #define ENC_EUC_JP 2
428 #define ENC_Windows_31J 3
429 #define ENC_UTF8 4
432 * Check the prism flags of a regular expression-like node and return the flags
433 * that are expected by the CRuby VM.
435 static int
436 parse_regexp_flags(const pm_node_t *node)
438 int flags = 0;
440 // Check "no encoding" first so that flags don't get clobbered
441 // We're calling `rb_char_to_option_kcode` in this case so that
442 // we don't need to have access to `ARG_ENCODING_NONE`
443 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) {
444 flags |= ARG_ENCODING_NONE;
447 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) {
448 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_EUC_JP));
451 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) {
452 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_Windows_31J));
455 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) {
456 flags |= (ARG_ENCODING_FIXED | RE_OPTION_ENCODING(ENC_UTF8));
459 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) {
460 flags |= ONIG_OPTION_IGNORECASE;
463 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) {
464 flags |= ONIG_OPTION_MULTILINE;
467 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) {
468 flags |= ONIG_OPTION_EXTEND;
471 return flags;
474 #undef RE_OPTION_ENCODING_SHIFT
475 #undef RE_OPTION_ENCODING
476 #undef ARG_ENCODING_FIXED
477 #undef ARG_ENCODING_NONE
478 #undef ENC_ASCII8BIT
479 #undef ENC_EUC_JP
480 #undef ENC_Windows_31J
481 #undef ENC_UTF8
483 static rb_encoding *
484 parse_regexp_encoding(const pm_scope_node_t *scope_node, const pm_node_t *node)
486 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) {
487 return rb_ascii8bit_encoding();
489 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_UTF_8)) {
490 return rb_utf8_encoding();
492 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EUC_JP)) {
493 return rb_enc_get_from_index(ENCINDEX_EUC_JP);
495 else if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J)) {
496 return rb_enc_get_from_index(ENCINDEX_Windows_31J);
498 else {
499 return NULL;
503 static VALUE
504 parse_regexp(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, VALUE string)
506 VALUE errinfo = rb_errinfo();
508 int32_t line_number = pm_node_line_number(scope_node->parser, node);
509 VALUE regexp = rb_reg_compile(string, parse_regexp_flags(node), (const char *) pm_string_source(&scope_node->parser->filepath), line_number);
511 if (NIL_P(regexp)) {
512 VALUE message = rb_attr_get(rb_errinfo(), idMesg);
513 rb_set_errinfo(errinfo);
515 parse_regexp_error(iseq, line_number, "%" PRIsVALUE, message);
516 return Qnil;
519 rb_obj_freeze(regexp);
520 return regexp;
523 static inline VALUE
524 parse_regexp_literal(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_string_t *unescaped)
526 rb_encoding *regexp_encoding = parse_regexp_encoding(scope_node, node);
527 if (regexp_encoding == NULL) regexp_encoding = scope_node->encoding;
529 VALUE string = rb_enc_str_new((const char *) pm_string_source(unescaped), pm_string_length(unescaped), regexp_encoding);
530 return parse_regexp(iseq, scope_node, node, string);
533 static inline VALUE
534 parse_regexp_concat(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_node_t *node, const pm_node_list_t *parts)
536 rb_encoding *explicit_regexp_encoding = parse_regexp_encoding(scope_node, node);
537 rb_encoding *implicit_regexp_encoding = explicit_regexp_encoding != NULL ? explicit_regexp_encoding : scope_node->encoding;
539 VALUE string = pm_static_literal_concat(iseq, parts, scope_node, implicit_regexp_encoding, explicit_regexp_encoding, false);
540 return parse_regexp(iseq, scope_node, node, string);
543 static 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);
545 static int
546 pm_interpolated_node_compile(rb_iseq_t *iseq, const pm_node_list_t *parts, const pm_line_column_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)
548 int stack_size = 0;
549 size_t parts_size = parts->size;
550 bool interpolated = false;
552 if (parts_size > 0) {
553 VALUE current_string = Qnil;
554 pm_line_column_t current_location = *node_location;
556 for (size_t index = 0; index < parts_size; index++) {
557 const pm_node_t *part = parts->nodes[index];
559 if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
560 const pm_string_node_t *string_node = (const pm_string_node_t *) part;
561 VALUE string_value;
563 if (implicit_regexp_encoding == NULL) {
564 string_value = parse_string_encoded(part, &string_node->unescaped, scope_node->encoding);
566 else {
567 string_value = parse_regexp_string_part(iseq, scope_node, (const pm_node_t *) string_node, &string_node->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
570 if (RTEST(current_string)) {
571 current_string = rb_str_concat(current_string, string_value);
573 else {
574 current_string = string_value;
575 if (index != 0) current_location = PM_NODE_END_LINE_COLUMN(scope_node->parser, part);
578 else {
579 interpolated = true;
581 if (
582 PM_NODE_TYPE_P(part, PM_EMBEDDED_STATEMENTS_NODE) &&
583 ((const pm_embedded_statements_node_t *) part)->statements != NULL &&
584 ((const pm_embedded_statements_node_t *) part)->statements->body.size == 1 &&
585 PM_NODE_TYPE_P(((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0], PM_STRING_NODE)
587 const pm_string_node_t *string_node = (const pm_string_node_t *) ((const pm_embedded_statements_node_t *) part)->statements->body.nodes[0];
588 VALUE string_value;
590 if (implicit_regexp_encoding == NULL) {
591 string_value = parse_string_encoded(part, &string_node->unescaped, scope_node->encoding);
593 else {
594 string_value = parse_regexp_string_part(iseq, scope_node, (const pm_node_t *) string_node, &string_node->unescaped, implicit_regexp_encoding, explicit_regexp_encoding);
597 if (RTEST(current_string)) {
598 current_string = rb_str_concat(current_string, string_value);
600 else {
601 current_string = string_value;
602 current_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, part);
605 else {
606 if (!RTEST(current_string)) {
607 rb_encoding *encoding;
609 if (implicit_regexp_encoding != NULL) {
610 if (explicit_regexp_encoding != NULL) {
611 encoding = explicit_regexp_encoding;
613 else if (scope_node->parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
614 encoding = rb_ascii8bit_encoding();
616 else {
617 encoding = implicit_regexp_encoding;
620 else {
621 encoding = scope_node->encoding;
624 current_string = rb_enc_str_new(NULL, 0, encoding);
627 PUSH_INSN1(ret, current_location, putobject, rb_fstring(current_string));
628 PM_COMPILE_NOT_POPPED(part);
630 const pm_line_column_t current_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, part);
631 PUSH_INSN(ret, current_location, dup);
632 PUSH_INSN1(ret, current_location, objtostring, new_callinfo(iseq, idTo_s, 0, VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE, NULL, FALSE));
633 PUSH_INSN(ret, current_location, anytostring);
635 current_string = Qnil;
636 stack_size += 2;
641 if (RTEST(current_string)) {
642 current_string = rb_fstring(current_string);
644 if (stack_size == 0 && interpolated) {
645 PUSH_INSN1(ret, current_location, putstring, current_string);
647 else {
648 PUSH_INSN1(ret, current_location, putobject, current_string);
651 current_string = Qnil;
652 stack_size++;
655 else {
656 PUSH_INSN(ret, *node_location, putnil);
659 return stack_size;
662 static void
663 pm_compile_regexp_dynamic(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *parts, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
665 rb_encoding *explicit_regexp_encoding = parse_regexp_encoding(scope_node, node);
666 rb_encoding *implicit_regexp_encoding = explicit_regexp_encoding != NULL ? explicit_regexp_encoding : scope_node->encoding;
668 int length = pm_interpolated_node_compile(iseq, parts, node_location, ret, popped, scope_node, implicit_regexp_encoding, explicit_regexp_encoding);
669 PUSH_INSN2(ret, *node_location, toregexp, INT2FIX(parse_regexp_flags(node) & 0xFF), INT2FIX(length));
672 static VALUE
673 pm_source_file_value(const pm_source_file_node_t *node, const pm_scope_node_t *scope_node)
675 const pm_string_t *filepath = &node->filepath;
676 size_t length = pm_string_length(filepath);
678 if (length > 0) {
679 rb_encoding *filepath_encoding = scope_node->filepath_encoding != NULL ? scope_node->filepath_encoding : rb_utf8_encoding();
680 return rb_enc_interned_str((const char *) pm_string_source(filepath), length, filepath_encoding);
682 else {
683 return rb_fstring_lit("<compiled>");
688 * Return a static literal string, optionally with attached debugging
689 * information.
691 static VALUE
692 pm_static_literal_string(rb_iseq_t *iseq, VALUE string, int line_number)
694 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
695 VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line_number));
696 rb_ivar_set(string, id_debug_created_info, rb_obj_freeze(debug_info));
697 return rb_str_freeze(string);
699 else {
700 return rb_fstring(string);
705 * Certain nodes can be compiled literally. This function returns the literal
706 * value described by the given node. For example, an array node with all static
707 * literal values can be compiled into a literal array.
709 static VALUE
710 pm_static_literal_value(rb_iseq_t *iseq, const pm_node_t *node, const pm_scope_node_t *scope_node)
712 // Every node that comes into this function should already be marked as
713 // static literal. If it's not, then we have a bug somewhere.
714 RUBY_ASSERT(PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL));
716 switch (PM_NODE_TYPE(node)) {
717 case PM_ARRAY_NODE: {
718 const pm_array_node_t *cast = (const pm_array_node_t *) node;
719 const pm_node_list_t *elements = &cast->elements;
721 VALUE value = rb_ary_hidden_new(elements->size);
722 for (size_t index = 0; index < elements->size; index++) {
723 rb_ary_push(value, pm_static_literal_value(iseq, elements->nodes[index], scope_node));
726 OBJ_FREEZE(value);
727 return value;
729 case PM_FALSE_NODE:
730 return Qfalse;
731 case PM_FLOAT_NODE:
732 return parse_float((const pm_float_node_t *) node);
733 case PM_HASH_NODE: {
734 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
735 const pm_node_list_t *elements = &cast->elements;
737 VALUE array = rb_ary_hidden_new(elements->size * 2);
738 for (size_t index = 0; index < elements->size; index++) {
739 RUBY_ASSERT(PM_NODE_TYPE_P(elements->nodes[index], PM_ASSOC_NODE));
740 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) elements->nodes[index];
741 VALUE pair[2] = { pm_static_literal_value(iseq, cast->key, scope_node), pm_static_literal_value(iseq, cast->value, scope_node) };
742 rb_ary_cat(array, pair, 2);
745 VALUE value = rb_hash_new_with_size(elements->size);
746 rb_hash_bulk_insert(RARRAY_LEN(array), RARRAY_CONST_PTR(array), value);
748 value = rb_obj_hide(value);
749 OBJ_FREEZE(value);
750 return value;
752 case PM_IMAGINARY_NODE:
753 return parse_imaginary((const pm_imaginary_node_t *) node);
754 case PM_INTEGER_NODE:
755 return parse_integer((const pm_integer_node_t *) node);
756 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: {
757 const pm_interpolated_match_last_line_node_t *cast = (const pm_interpolated_match_last_line_node_t *) node;
758 return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts);
760 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
761 const pm_interpolated_regular_expression_node_t *cast = (const pm_interpolated_regular_expression_node_t *) node;
762 return parse_regexp_concat(iseq, scope_node, (const pm_node_t *) cast, &cast->parts);
764 case PM_INTERPOLATED_STRING_NODE: {
765 VALUE string = pm_static_literal_concat(iseq, &((const pm_interpolated_string_node_t *) node)->parts, scope_node, NULL, NULL, false);
766 int line_number = pm_node_line_number(scope_node->parser, node);
767 return pm_static_literal_string(iseq, string, line_number);
769 case PM_INTERPOLATED_SYMBOL_NODE: {
770 const pm_interpolated_symbol_node_t *cast = (const pm_interpolated_symbol_node_t *) node;
771 VALUE string = pm_static_literal_concat(iseq, &cast->parts, scope_node, NULL, NULL, true);
773 return ID2SYM(rb_intern_str(string));
775 case PM_MATCH_LAST_LINE_NODE: {
776 const pm_match_last_line_node_t *cast = (const pm_match_last_line_node_t *) node;
777 return parse_regexp_literal(iseq, scope_node, (const pm_node_t *) cast, &cast->unescaped);
779 case PM_NIL_NODE:
780 return Qnil;
781 case PM_RATIONAL_NODE:
782 return parse_rational((const pm_rational_node_t *) node);
783 case PM_REGULAR_EXPRESSION_NODE: {
784 const pm_regular_expression_node_t *cast = (const pm_regular_expression_node_t *) node;
785 return parse_regexp_literal(iseq, scope_node, (const pm_node_t *) cast, &cast->unescaped);
787 case PM_SOURCE_ENCODING_NODE:
788 return rb_enc_from_encoding(scope_node->encoding);
789 case PM_SOURCE_FILE_NODE: {
790 const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node;
791 return pm_source_file_value(cast, scope_node);
793 case PM_SOURCE_LINE_NODE:
794 return INT2FIX(pm_node_line_number(scope_node->parser, node));
795 case PM_STRING_NODE: {
796 const pm_string_node_t *cast = (const pm_string_node_t *) node;
797 return parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
799 case PM_SYMBOL_NODE:
800 return ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) node));
801 case PM_TRUE_NODE:
802 return Qtrue;
803 default:
804 rb_bug("Don't have a literal value for node type %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
805 return Qfalse;
810 * A helper for converting a pm_location_t into a rb_code_location_t.
812 static rb_code_location_t
813 pm_code_location(const pm_scope_node_t *scope_node, const pm_node_t *node)
815 const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
816 const pm_line_column_t end_location = PM_NODE_END_LINE_COLUMN(scope_node->parser, node);
818 return (rb_code_location_t) {
819 .beg_pos = { .lineno = start_location.line, .column = start_location.column },
820 .end_pos = { .lineno = end_location.line, .column = end_location.column }
825 * A macro for determining if we should go through the work of adding branch
826 * coverage to the current iseq. We check this manually each time because we
827 * want to avoid the overhead of creating rb_code_location_t objects.
829 #define PM_BRANCH_COVERAGE_P(iseq) (ISEQ_COVERAGE(iseq) && ISEQ_BRANCH_COVERAGE(iseq))
831 static void
832 pm_compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_node_t *cond,
833 LABEL *then_label, LABEL *else_label, bool popped, pm_scope_node_t *scope_node);
835 static void
836 pm_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)
838 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, cond);
840 DECL_ANCHOR(seq);
841 INIT_ANCHOR(seq);
843 LABEL *label = NEW_LABEL(location.line);
844 if (!then_label) then_label = label;
845 else if (!else_label) else_label = label;
847 pm_compile_branch_condition(iseq, seq, cond, then_label, else_label, popped, scope_node);
849 if (LIST_INSN_SIZE_ONE(seq)) {
850 INSN *insn = (INSN *) ELEM_FIRST_INSN(FIRST_ELEMENT(seq));
851 if (insn->insn_id == BIN(jump) && (LABEL *)(insn->operands[0]) == label) return;
854 if (!label->refcnt) {
855 if (popped) PUSH_INSN(ret, location, putnil);
857 else {
858 PUSH_LABEL(seq, label);
861 PUSH_SEQ(ret, seq);
862 return;
865 static void
866 pm_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)
868 const pm_line_column_t location = { .line = ISEQ_BODY(iseq)->location.first_lineno, .column = -1 };
870 if (PM_NODE_TYPE_P(node, PM_INTEGER_NODE)) {
871 PM_COMPILE_NOT_POPPED(node);
872 PUSH_INSN1(ret, location, getglobal, ID2SYM(rb_intern("$.")));
873 PUSH_SEND(ret, location, idEq, INT2FIX(1));
874 if (popped) PUSH_INSN(ret, location, pop);
876 else {
877 PM_COMPILE(node);
881 static void
882 pm_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)
884 const pm_line_column_t location = { .line = ISEQ_BODY(iseq)->location.first_lineno, .column = -1 };
885 LABEL *lend = NEW_LABEL(location.line);
887 int again = !(flip_flop_node->base.flags & PM_RANGE_FLAGS_EXCLUDE_END);
889 rb_num_t count = ISEQ_FLIP_CNT_INCREMENT(ISEQ_BODY(iseq)->local_iseq) + VM_SVAR_FLIPFLOP_START;
890 VALUE key = INT2FIX(count);
892 PUSH_INSN2(ret, location, getspecial, key, INT2FIX(0));
893 PUSH_INSNL(ret, location, branchif, lend);
895 if (flip_flop_node->left) {
896 pm_compile_flip_flop_bound(iseq, flip_flop_node->left, ret, popped, scope_node);
898 else {
899 PUSH_INSN(ret, location, putnil);
902 PUSH_INSNL(ret, location, branchunless, else_label);
903 PUSH_INSN1(ret, location, putobject, Qtrue);
904 PUSH_INSN1(ret, location, setspecial, key);
905 if (!again) {
906 PUSH_INSNL(ret, location, jump, then_label);
909 PUSH_LABEL(ret, lend);
910 if (flip_flop_node->right) {
911 pm_compile_flip_flop_bound(iseq, flip_flop_node->right, ret, popped, scope_node);
913 else {
914 PUSH_INSN(ret, location, putnil);
917 PUSH_INSNL(ret, location, branchunless, then_label);
918 PUSH_INSN1(ret, location, putobject, Qfalse);
919 PUSH_INSN1(ret, location, setspecial, key);
920 PUSH_INSNL(ret, location, jump, then_label);
923 static void pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition);
925 static void
926 pm_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)
928 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, cond);
930 again:
931 switch (PM_NODE_TYPE(cond)) {
932 case PM_AND_NODE: {
933 const pm_and_node_t *cast = (const pm_and_node_t *) cond;
934 pm_compile_logical(iseq, ret, cast->left, NULL, else_label, popped, scope_node);
936 cond = cast->right;
937 goto again;
939 case PM_OR_NODE: {
940 const pm_or_node_t *cast = (const pm_or_node_t *) cond;
941 pm_compile_logical(iseq, ret, cast->left, then_label, NULL, popped, scope_node);
943 cond = cast->right;
944 goto again;
946 case PM_FALSE_NODE:
947 case PM_NIL_NODE:
948 PUSH_INSNL(ret, location, jump, else_label);
949 return;
950 case PM_FLOAT_NODE:
951 case PM_IMAGINARY_NODE:
952 case PM_INTEGER_NODE:
953 case PM_LAMBDA_NODE:
954 case PM_RATIONAL_NODE:
955 case PM_REGULAR_EXPRESSION_NODE:
956 case PM_STRING_NODE:
957 case PM_SYMBOL_NODE:
958 case PM_TRUE_NODE:
959 PUSH_INSNL(ret, location, jump, then_label);
960 return;
961 case PM_FLIP_FLOP_NODE:
962 pm_compile_flip_flop((const pm_flip_flop_node_t *) cond, else_label, then_label, iseq, location.line, ret, popped, scope_node);
963 return;
964 case PM_DEFINED_NODE: {
965 const pm_defined_node_t *cast = (const pm_defined_node_t *) cond;
966 pm_compile_defined_expr(iseq, cast->value, &location, ret, popped, scope_node, true);
967 break;
969 default: {
970 pm_compile_node(iseq, cond, ret, false, scope_node);
971 break;
975 PUSH_INSNL(ret, location, branchunless, else_label);
976 PUSH_INSNL(ret, location, jump, then_label);
980 * Compile an if or unless node.
982 static void
983 pm_compile_conditional(rb_iseq_t *iseq, const pm_line_column_t *line_column, pm_node_type_t type, const pm_node_t *node, const pm_statements_node_t *statements, const pm_node_t *consequent, const pm_node_t *predicate, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
985 const pm_line_column_t location = *line_column;
986 LABEL *then_label = NEW_LABEL(location.line);
987 LABEL *else_label = NEW_LABEL(location.line);
988 LABEL *end_label = NULL;
990 DECL_ANCHOR(cond_seq);
991 INIT_ANCHOR(cond_seq);
992 pm_compile_branch_condition(iseq, cond_seq, predicate, then_label, else_label, false, scope_node);
993 PUSH_SEQ(ret, cond_seq);
995 rb_code_location_t conditional_location = { 0 };
996 VALUE branches = Qfalse;
998 if (then_label->refcnt && else_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) {
999 conditional_location = pm_code_location(scope_node, node);
1000 branches = decl_branch_base(iseq, PTR2NUM(node), &conditional_location, type == PM_IF_NODE ? "if" : "unless");
1003 if (then_label->refcnt) {
1004 PUSH_LABEL(ret, then_label);
1006 DECL_ANCHOR(then_seq);
1007 INIT_ANCHOR(then_seq);
1009 if (statements != NULL) {
1010 pm_compile_node(iseq, (const pm_node_t *) statements, then_seq, popped, scope_node);
1012 else if (!popped) {
1013 PUSH_SYNTHETIC_PUTNIL(then_seq, iseq);
1016 if (else_label->refcnt) {
1017 // Establish branch coverage for the then block.
1018 if (PM_BRANCH_COVERAGE_P(iseq)) {
1019 rb_code_location_t branch_location;
1021 if (statements != NULL) {
1022 branch_location = pm_code_location(scope_node, (const pm_node_t *) statements);
1023 } else if (type == PM_IF_NODE) {
1024 pm_line_column_t predicate_end = PM_NODE_END_LINE_COLUMN(scope_node->parser, predicate);
1025 branch_location = (rb_code_location_t) {
1026 .beg_pos = { .lineno = predicate_end.line, .column = predicate_end.column },
1027 .end_pos = { .lineno = predicate_end.line, .column = predicate_end.column }
1029 } else {
1030 branch_location = conditional_location;
1033 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, type == PM_IF_NODE ? "then" : "else", branches);
1036 end_label = NEW_LABEL(location.line);
1037 PUSH_INSNL(then_seq, location, jump, end_label);
1038 if (!popped) PUSH_INSN(then_seq, location, pop);
1041 PUSH_SEQ(ret, then_seq);
1044 if (else_label->refcnt) {
1045 PUSH_LABEL(ret, else_label);
1047 DECL_ANCHOR(else_seq);
1048 INIT_ANCHOR(else_seq);
1050 if (consequent != NULL) {
1051 pm_compile_node(iseq, consequent, else_seq, popped, scope_node);
1053 else if (!popped) {
1054 PUSH_SYNTHETIC_PUTNIL(else_seq, iseq);
1057 // Establish branch coverage for the else block.
1058 if (then_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) {
1059 rb_code_location_t branch_location;
1061 if (consequent == NULL) {
1062 branch_location = conditional_location;
1063 } else if (PM_NODE_TYPE_P(consequent, PM_ELSE_NODE)) {
1064 const pm_else_node_t *else_node = (const pm_else_node_t *) consequent;
1065 branch_location = pm_code_location(scope_node, else_node->statements != NULL ? ((const pm_node_t *) else_node->statements) : (const pm_node_t *) else_node);
1066 } else {
1067 branch_location = pm_code_location(scope_node, (const pm_node_t *) consequent);
1070 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 1, type == PM_IF_NODE ? "else" : "then", branches);
1073 PUSH_SEQ(ret, else_seq);
1076 if (end_label) {
1077 PUSH_LABEL(ret, end_label);
1080 return;
1084 * Compile a while or until loop.
1086 static void
1087 pm_compile_loop(rb_iseq_t *iseq, const pm_line_column_t *line_column, 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)
1089 const pm_line_column_t location = *line_column;
1091 LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
1092 LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
1093 LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
1095 // TODO: Deal with ensures in here
1096 LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(location.line); /* next */
1097 LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(location.line); /* redo */
1098 LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(location.line); /* break */
1099 LABEL *end_label = NEW_LABEL(location.line);
1100 LABEL *adjust_label = NEW_LABEL(location.line);
1102 LABEL *next_catch_label = NEW_LABEL(location.line);
1103 LABEL *tmp_label = NULL;
1105 // begin; end while true
1106 if (flags & PM_LOOP_FLAGS_BEGIN_MODIFIER) {
1107 tmp_label = NEW_LABEL(location.line);
1108 PUSH_INSNL(ret, location, jump, tmp_label);
1110 else {
1111 // while true; end
1112 PUSH_INSNL(ret, location, jump, next_label);
1115 PUSH_LABEL(ret, adjust_label);
1116 PUSH_INSN(ret, location, putnil);
1117 PUSH_LABEL(ret, next_catch_label);
1118 PUSH_INSN(ret, location, pop);
1119 PUSH_INSNL(ret, location, jump, next_label);
1120 if (tmp_label) PUSH_LABEL(ret, tmp_label);
1122 PUSH_LABEL(ret, redo_label);
1124 // Establish branch coverage for the loop.
1125 if (PM_BRANCH_COVERAGE_P(iseq)) {
1126 rb_code_location_t loop_location = pm_code_location(scope_node, node);
1127 VALUE branches = decl_branch_base(iseq, PTR2NUM(node), &loop_location, type == PM_WHILE_NODE ? "while" : "until");
1129 rb_code_location_t branch_location = statements != NULL ? pm_code_location(scope_node, (const pm_node_t *) statements) : loop_location;
1130 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, "body", branches);
1133 if (statements != NULL) PM_COMPILE_POPPED((const pm_node_t *) statements);
1134 PUSH_LABEL(ret, next_label);
1136 if (type == PM_WHILE_NODE) {
1137 pm_compile_branch_condition(iseq, ret, predicate, redo_label, end_label, popped, scope_node);
1139 else if (type == PM_UNTIL_NODE) {
1140 pm_compile_branch_condition(iseq, ret, predicate, end_label, redo_label, popped, scope_node);
1143 PUSH_LABEL(ret, end_label);
1144 PUSH_ADJUST_RESTORE(ret, adjust_label);
1145 PUSH_INSN(ret, location, putnil);
1147 PUSH_LABEL(ret, break_label);
1148 if (popped) PUSH_INSN(ret, location, pop);
1150 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL, break_label);
1151 PUSH_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL, next_catch_label);
1152 PUSH_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL, ISEQ_COMPILE_DATA(iseq)->redo_label);
1154 ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
1155 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
1156 ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
1157 return;
1160 // This recurses through scopes and finds the local index at any scope level
1161 // It also takes a pointer to depth, and increments depth appropriately
1162 // according to the depth of the local.
1163 static pm_local_index_t
1164 pm_lookup_local_index(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, pm_constant_id_t constant_id, int start_depth)
1166 pm_local_index_t lindex = { 0 };
1167 st_data_t local_index;
1169 int level;
1170 for (level = 0; level < start_depth; level++) {
1171 scope_node = scope_node->previous;
1174 while (!st_lookup(scope_node->index_lookup_table, constant_id, &local_index)) {
1175 level++;
1177 if (scope_node->previous) {
1178 scope_node = scope_node->previous;
1180 else {
1181 // We have recursed up all scope nodes
1182 // and have not found the local yet
1183 rb_bug("Local with constant_id %u does not exist", (unsigned int) constant_id);
1187 lindex.level = level;
1188 lindex.index = scope_node->local_table_for_iseq_size - (int) local_index;
1189 return lindex;
1192 // This returns the CRuby ID which maps to the pm_constant_id_t
1194 // Constant_ids in prism are indexes of the constants in prism's constant pool.
1195 // We add a constants mapping on the scope_node which is a mapping from
1196 // these constant_id indexes to the CRuby IDs that they represent.
1197 // This helper method allows easy access to those IDs
1198 static ID
1199 pm_constant_id_lookup(const pm_scope_node_t *scope_node, pm_constant_id_t constant_id)
1201 if (constant_id < 1 || constant_id > scope_node->parser->constant_pool.size) {
1202 rb_bug("constant_id out of range: %u", (unsigned int)constant_id);
1204 return scope_node->constants[constant_id - 1];
1207 static rb_iseq_t *
1208 pm_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)
1210 debugs("[new_child_iseq]> ---------------------------------------\n");
1211 int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
1212 rb_iseq_t *ret_iseq = pm_iseq_new_with_opt(node, name,
1213 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
1214 line_no, parent,
1215 isolated_depth ? isolated_depth + 1 : 0,
1216 type, ISEQ_COMPILE_DATA(iseq)->option);
1217 debugs("[new_child_iseq]< ---------------------------------------\n");
1218 return ret_iseq;
1221 static int
1222 pm_compile_class_path(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1224 if (PM_NODE_TYPE_P(node, PM_CONSTANT_PATH_NODE)) {
1225 const pm_node_t *parent = ((const pm_constant_path_node_t *) node)->parent;
1227 if (parent) {
1228 /* Bar::Foo */
1229 PM_COMPILE(parent);
1230 return VM_DEFINECLASS_FLAG_SCOPED;
1232 else {
1233 /* toplevel class ::Foo */
1234 PUSH_INSN1(ret, *node_location, putobject, rb_cObject);
1235 return VM_DEFINECLASS_FLAG_SCOPED;
1238 else {
1239 /* class at cbase Foo */
1240 PUSH_INSN1(ret, *node_location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
1241 return 0;
1246 * Compile either a call and write node or a call or write node. These look like
1247 * method calls that are followed by a ||= or &&= operator.
1249 static void
1250 pm_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_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1252 const pm_line_column_t location = *node_location;
1253 LABEL *lfin = NEW_LABEL(location.line);
1254 LABEL *lcfin = NEW_LABEL(location.line);
1255 LABEL *lskip = NULL;
1257 int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
1258 ID id_read_name = pm_constant_id_lookup(scope_node, read_name);
1260 PM_COMPILE_NOT_POPPED(receiver);
1261 if (safe_nav) {
1262 lskip = NEW_LABEL(location.line);
1263 PUSH_INSN(ret, location, dup);
1264 PUSH_INSNL(ret, location, branchnil, lskip);
1267 PUSH_INSN(ret, location, dup);
1268 PUSH_SEND_WITH_FLAG(ret, location, id_read_name, INT2FIX(0), INT2FIX(flag));
1269 if (!popped) PUSH_INSN(ret, location, dup);
1271 if (and_node) {
1272 PUSH_INSNL(ret, location, branchunless, lcfin);
1274 else {
1275 PUSH_INSNL(ret, location, branchif, lcfin);
1278 if (!popped) PUSH_INSN(ret, location, pop);
1279 PM_COMPILE_NOT_POPPED(value);
1281 if (!popped) {
1282 PUSH_INSN(ret, location, swap);
1283 PUSH_INSN1(ret, location, topn, INT2FIX(1));
1286 ID id_write_name = pm_constant_id_lookup(scope_node, write_name);
1287 PUSH_SEND_WITH_FLAG(ret, location, id_write_name, INT2FIX(1), INT2FIX(flag));
1288 PUSH_INSNL(ret, location, jump, lfin);
1290 PUSH_LABEL(ret, lcfin);
1291 if (!popped) PUSH_INSN(ret, location, swap);
1293 PUSH_LABEL(ret, lfin);
1295 if (lskip && popped) PUSH_LABEL(ret, lskip);
1296 PUSH_INSN(ret, location, pop);
1297 if (lskip && !popped) PUSH_LABEL(ret, lskip);
1301 * This function compiles a hash onto the stack. It is used to compile hash
1302 * literals and keyword arguments. It is assumed that if we get here that the
1303 * contents of the hash are not popped.
1305 static void
1306 pm_compile_hash_elements(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_list_t *elements, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
1308 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
1310 // If this element is not popped, then we need to create the hash on the
1311 // stack. Neighboring plain assoc nodes should be grouped together (either
1312 // by newhash or hash merge). Double splat nodes should be merged using the
1313 // merge_kwd method call.
1314 int assoc_length = 0;
1315 bool made_hash = false;
1317 for (size_t index = 0; index < elements->size; index++) {
1318 const pm_node_t *element = elements->nodes[index];
1320 switch (PM_NODE_TYPE(element)) {
1321 case PM_ASSOC_NODE: {
1322 // If this is a plain assoc node, then we can compile it directly
1323 // and then add to the number of assoc nodes we've seen so far.
1324 PM_COMPILE_NOT_POPPED(element);
1325 assoc_length++;
1326 break;
1328 case PM_ASSOC_SPLAT_NODE: {
1329 // If we are at a splat and we have already compiled some elements
1330 // of the hash, then we need to either create the first hash or
1331 // merge the current elements into the existing hash.
1332 if (assoc_length > 0) {
1333 if (!made_hash) {
1334 PUSH_INSN1(ret, location, newhash, INT2FIX(assoc_length * 2));
1335 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1336 PUSH_INSN(ret, location, swap);
1337 made_hash = true;
1339 else {
1340 // Here we are merging plain assoc nodes into the hash on
1341 // the stack.
1342 PUSH_SEND(ret, location, id_core_hash_merge_ptr, INT2FIX(assoc_length * 2 + 1));
1344 // Since we already have a hash on the stack, we need to set
1345 // up the method call for the next merge that will occur.
1346 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1347 PUSH_INSN(ret, location, swap);
1350 assoc_length = 0;
1353 // If this is the first time we've seen a splat, then we need to
1354 // create a hash that we can merge into.
1355 if (!made_hash) {
1356 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1357 PUSH_INSN1(ret, location, newhash, INT2FIX(0));
1358 made_hash = true;
1361 // Now compile the splat node itself and merge it into the hash.
1362 PM_COMPILE_NOT_POPPED(element);
1363 PUSH_SEND(ret, location, id_core_hash_merge_kwd, INT2FIX(2));
1365 // We know that any subsequent elements will need to be merged in
1366 // using one of the special core methods. So here we will put the
1367 // receiver of the merge and then swap it with the hash that is
1368 // going to be the first argument.
1369 if (index != elements->size - 1) {
1370 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1371 PUSH_INSN(ret, location, swap);
1374 break;
1376 default:
1377 RUBY_ASSERT("Invalid node type for hash" && false);
1378 break;
1382 if (!made_hash) {
1383 // If we haven't already made the hash, then this means we only saw
1384 // plain assoc nodes. In this case, we can just create the hash
1385 // directly.
1386 PUSH_INSN1(ret, location, newhash, INT2FIX(assoc_length * 2));
1388 else if (assoc_length > 0) {
1389 // If we have already made the hash, then we need to merge the remaining
1390 // assoc nodes into the hash on the stack.
1391 PUSH_SEND(ret, location, id_core_hash_merge_ptr, INT2FIX(assoc_length * 2 + 1));
1395 // This is details. Users should call pm_setup_args() instead.
1396 static int
1397 pm_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, rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_line_column_t *node_location)
1399 const pm_line_column_t location = *node_location;
1401 int orig_argc = 0;
1402 bool has_splat = false;
1403 bool has_keyword_splat = false;
1405 if (arguments_node == NULL) {
1406 if (*flags & VM_CALL_FCALL) {
1407 *flags |= VM_CALL_VCALL;
1410 else {
1411 const pm_node_list_t *arguments = &arguments_node->arguments;
1412 has_keyword_splat = PM_NODE_FLAG_P(arguments_node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT);
1414 // We count the number of elements post the splat node that are not keyword elements to
1415 // eventually pass as an argument to newarray
1416 int post_splat_counter = 0;
1417 const pm_node_t *argument;
1419 PM_NODE_LIST_FOREACH(arguments, index, argument) {
1420 switch (PM_NODE_TYPE(argument)) {
1421 // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
1422 case PM_KEYWORD_HASH_NODE: {
1423 const pm_keyword_hash_node_t *keyword_arg = (const pm_keyword_hash_node_t *) argument;
1424 const pm_node_list_t *elements = &keyword_arg->elements;
1426 if (has_keyword_splat || has_splat) {
1427 *flags |= VM_CALL_KW_SPLAT;
1428 has_keyword_splat = true;
1429 pm_compile_hash_elements(iseq, argument, elements, ret, scope_node);
1431 else {
1432 // We need to first figure out if all elements of the
1433 // KeywordHashNode are AssocNodes with symbol keys.
1434 if (PM_NODE_FLAG_P(keyword_arg, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS)) {
1435 // If they are all symbol keys then we can pass them as
1436 // keyword arguments. The first thing we need to do is
1437 // deduplicate. We'll do this using the combination of a
1438 // Ruby hash and a Ruby array.
1439 VALUE stored_indices = rb_hash_new();
1440 VALUE keyword_indices = rb_ary_new_capa(elements->size);
1442 size_t size = 0;
1443 for (size_t element_index = 0; element_index < elements->size; element_index++) {
1444 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1446 // Retrieve the stored index from the hash for this
1447 // keyword.
1448 VALUE keyword = pm_static_literal_value(iseq, assoc->key, scope_node);
1449 VALUE stored_index = rb_hash_aref(stored_indices, keyword);
1451 // If this keyword was already seen in the hash,
1452 // then mark the array at that index as false and
1453 // decrement the keyword size.
1454 if (!NIL_P(stored_index)) {
1455 rb_ary_store(keyword_indices, NUM2LONG(stored_index), Qfalse);
1456 size--;
1459 // Store (and possibly overwrite) the index for this
1460 // keyword in the hash, mark the array at that index
1461 // as true, and increment the keyword size.
1462 rb_hash_aset(stored_indices, keyword, ULONG2NUM(element_index));
1463 rb_ary_store(keyword_indices, (long) element_index, Qtrue);
1464 size++;
1467 *kw_arg = rb_xmalloc_mul_add(size, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
1468 *flags |= VM_CALL_KWARG;
1470 VALUE *keywords = (*kw_arg)->keywords;
1471 (*kw_arg)->references = 0;
1472 (*kw_arg)->keyword_len = (int) size;
1474 size_t keyword_index = 0;
1475 for (size_t element_index = 0; element_index < elements->size; element_index++) {
1476 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1477 bool popped = true;
1479 if (rb_ary_entry(keyword_indices, (long) element_index) == Qtrue) {
1480 keywords[keyword_index++] = pm_static_literal_value(iseq, assoc->key, scope_node);
1481 popped = false;
1484 PM_COMPILE(assoc->value);
1487 RUBY_ASSERT(keyword_index == size);
1489 else {
1490 // If they aren't all symbol keys then we need to
1491 // construct a new hash and pass that as an argument.
1492 orig_argc++;
1493 *flags |= VM_CALL_KW_SPLAT;
1495 size_t size = elements->size;
1496 if (size > 1) {
1497 // A new hash will be created for the keyword
1498 // arguments in this case, so mark the method as
1499 // passing mutable keyword splat.
1500 *flags |= VM_CALL_KW_SPLAT_MUT;
1503 for (size_t element_index = 0; element_index < size; element_index++) {
1504 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) elements->nodes[element_index];
1505 PM_COMPILE_NOT_POPPED(assoc->key);
1506 PM_COMPILE_NOT_POPPED(assoc->value);
1509 PUSH_INSN1(ret, location, newhash, INT2FIX(size * 2));
1512 break;
1514 case PM_SPLAT_NODE: {
1515 *flags |= VM_CALL_ARGS_SPLAT;
1516 const pm_splat_node_t *splat_node = (const pm_splat_node_t *) argument;
1518 if (splat_node->expression) {
1519 PM_COMPILE_NOT_POPPED(splat_node->expression);
1521 else {
1522 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
1523 PUSH_GETLOCAL(ret, location, index.index, index.level);
1526 bool first_splat = !has_splat;
1528 if (first_splat) {
1529 // If this is the first splat array seen and it's not the
1530 // last parameter, we want splatarray to dup it.
1532 // foo(a, *b, c)
1533 // ^^
1534 if (index + 1 < arguments->size || has_regular_blockarg) {
1535 PUSH_INSN1(ret, location, splatarray, Qtrue);
1536 *flags |= VM_CALL_ARGS_SPLAT_MUT;
1538 // If this is the first spalt array seen and it's the last
1539 // parameter, we don't want splatarray to dup it.
1541 // foo(a, *b)
1542 // ^^
1543 else {
1544 PUSH_INSN1(ret, location, splatarray, Qfalse);
1547 else {
1548 // If this is not the first splat array seen and it is also
1549 // the last parameter, we don't want splatarray to dup it
1550 // and we need to concat the array.
1552 // foo(a, *b, *c)
1553 // ^^
1554 PUSH_INSN1(ret, location, splatarray, Qfalse);
1555 PUSH_INSN(ret, location, concatarray);
1558 has_splat = true;
1559 post_splat_counter = 0;
1561 break;
1563 case PM_FORWARDING_ARGUMENTS_NODE: {
1564 orig_argc += 2;
1565 *flags |= VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_SPLAT_MUT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KW_SPLAT;
1567 // Forwarding arguments nodes are treated as foo(*, **, &)
1568 // So foo(...) equals foo(*, **, &) and as such the local
1569 // table for this method is known in advance
1571 // Push the *
1572 pm_local_index_t mult_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
1573 PUSH_GETLOCAL(ret, location, mult_local.index, mult_local.level);
1574 PUSH_INSN1(ret, location, splatarray, Qtrue);
1576 // Push the **
1577 pm_local_index_t pow_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_POW, 0);
1578 PUSH_GETLOCAL(ret, location, pow_local.index, pow_local.level);
1580 // Push the &
1581 pm_local_index_t and_local = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_AND, 0);
1582 PUSH_INSN2(ret, location, getblockparamproxy, INT2FIX(and_local.index + VM_ENV_DATA_SIZE - 1), INT2FIX(and_local.level));
1583 PUSH_INSN(ret, location, splatkw);
1585 break;
1587 default: {
1588 post_splat_counter++;
1589 PM_COMPILE_NOT_POPPED(argument);
1591 // If we have a splat and we've seen a splat, we need to process
1592 // everything after the splat.
1593 if (has_splat) {
1594 // Stack items are turned into an array and concatenated in
1595 // the following cases:
1597 // If the next node is a splat:
1599 // foo(*a, b, *c)
1601 // If the next node is a kwarg or kwarg splat:
1603 // foo(*a, b, c: :d)
1604 // foo(*a, b, **c)
1606 // If the next node is NULL (we have hit the end):
1608 // foo(*a, b)
1609 if (index == arguments->size - 1) {
1610 RUBY_ASSERT(post_splat_counter > 0);
1611 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(post_splat_counter));
1613 else {
1614 pm_node_t *next_arg = arguments->nodes[index + 1];
1616 switch (PM_NODE_TYPE(next_arg)) {
1617 // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
1618 case PM_KEYWORD_HASH_NODE: {
1619 PUSH_INSN1(ret, location, newarray, INT2FIX(post_splat_counter));
1620 PUSH_INSN(ret, location, concatarray);
1621 break;
1623 case PM_SPLAT_NODE: {
1624 PUSH_INSN1(ret, location, newarray, INT2FIX(post_splat_counter));
1625 PUSH_INSN(ret, location, concatarray);
1626 break;
1628 default:
1629 break;
1633 else {
1634 orig_argc++;
1641 if (has_splat) orig_argc++;
1642 if (has_keyword_splat) orig_argc++;
1643 return orig_argc;
1646 // Compile the argument parts of a call
1647 static int
1648 pm_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_line_column_t *node_location)
1650 if (block && PM_NODE_TYPE_P(block, PM_BLOCK_ARGUMENT_NODE)) {
1651 // We compile the `&block_arg` expression first and stitch it later
1652 // since the nature of the expression influences whether splat should
1653 // duplicate the array.
1654 bool regular_block_arg = true;
1655 DECL_ANCHOR(block_arg);
1656 INIT_ANCHOR(block_arg);
1657 pm_compile_node(iseq, block, block_arg, false, scope_node);
1659 *flags |= VM_CALL_ARGS_BLOCKARG;
1661 if (LIST_INSN_SIZE_ONE(block_arg)) {
1662 LINK_ELEMENT *elem = FIRST_ELEMENT(block_arg);
1663 if (IS_INSN(elem)) {
1664 INSN *iobj = (INSN *) elem;
1665 if (iobj->insn_id == BIN(getblockparam)) {
1666 iobj->insn_id = BIN(getblockparamproxy);
1668 // Allow splat without duplication for simple one-instruction
1669 // block arguments like `&arg`. It is known that this optimization
1670 // can be too aggressive in some cases. See [Bug #16504].
1671 regular_block_arg = false;
1675 int argc = pm_setup_args_core(arguments_node, block, flags, regular_block_arg, kw_arg, iseq, ret, scope_node, node_location);
1676 PUSH_SEQ(ret, block_arg);
1677 return argc;
1680 return pm_setup_args_core(arguments_node, block, flags, false, kw_arg, iseq, ret, scope_node, node_location);
1684 * Compile an index operator write node, which is a node that is writing a value
1685 * using the [] and []= methods. It looks like:
1687 * foo[bar] += baz
1689 * This breaks down to caching the receiver and arguments on the stack, calling
1690 * the [] method, calling the operator method with the result of the [] method,
1691 * and then calling the []= method with the result of the operator method.
1693 static void
1694 pm_compile_index_operator_write_node(rb_iseq_t *iseq, const pm_index_operator_write_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1696 const pm_line_column_t location = *node_location;
1697 if (!popped) PUSH_INSN(ret, location, putnil);
1699 PM_COMPILE_NOT_POPPED(node->receiver);
1701 int boff = (node->block == NULL ? 0 : 1);
1702 int flag = PM_NODE_TYPE_P(node->receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
1703 struct rb_callinfo_kwarg *keywords = NULL;
1704 int argc = pm_setup_args(node->arguments, node->block, &flag, &keywords, iseq, ret, scope_node, node_location);
1706 if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
1707 if (boff) {
1708 PUSH_INSN(ret, location, splatkw);
1710 else {
1711 PUSH_INSN(ret, location, dup);
1712 PUSH_INSN(ret, location, splatkw);
1713 PUSH_INSN(ret, location, pop);
1717 int dup_argn = argc + 1 + boff;
1718 int keyword_len = 0;
1720 if (keywords) {
1721 keyword_len = keywords->keyword_len;
1722 dup_argn += keyword_len;
1725 PUSH_INSN1(ret, location, dupn, INT2FIX(dup_argn));
1726 PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords);
1727 PM_COMPILE_NOT_POPPED(node->value);
1729 ID id_operator = pm_constant_id_lookup(scope_node, node->binary_operator);
1730 PUSH_SEND(ret, location, id_operator, INT2FIX(1));
1732 if (!popped) {
1733 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
1735 if (flag & VM_CALL_ARGS_SPLAT) {
1736 if (flag & VM_CALL_KW_SPLAT) {
1737 PUSH_INSN1(ret, location, topn, INT2FIX(2 + boff));
1739 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
1740 PUSH_INSN1(ret, location, splatarray, Qtrue);
1741 flag |= VM_CALL_ARGS_SPLAT_MUT;
1744 PUSH_INSN(ret, location, swap);
1745 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
1746 PUSH_INSN1(ret, location, setn, INT2FIX(2 + boff));
1747 PUSH_INSN(ret, location, pop);
1749 else {
1750 if (boff > 0) {
1751 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
1752 PUSH_INSN(ret, location, swap);
1753 PUSH_INSN(ret, location, pop);
1755 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
1756 PUSH_INSN(ret, location, swap);
1757 PUSH_INSN1(ret, location, splatarray, Qtrue);
1758 PUSH_INSN(ret, location, swap);
1759 flag |= VM_CALL_ARGS_SPLAT_MUT;
1761 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
1762 if (boff > 0) {
1763 PUSH_INSN1(ret, location, setn, INT2FIX(3));
1764 PUSH_INSN(ret, location, pop);
1765 PUSH_INSN(ret, location, pop);
1769 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc), NULL, INT2FIX(flag), keywords);
1771 else if (flag & VM_CALL_KW_SPLAT) {
1772 if (boff > 0) {
1773 PUSH_INSN1(ret, location, topn, INT2FIX(2));
1774 PUSH_INSN(ret, location, swap);
1775 PUSH_INSN1(ret, location, setn, INT2FIX(3));
1776 PUSH_INSN(ret, location, pop);
1778 PUSH_INSN(ret, location, swap);
1779 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
1781 else if (keyword_len) {
1782 PUSH_INSN(ret, location, dup);
1783 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 2));
1784 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 1));
1785 PUSH_INSN(ret, location, pop);
1786 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
1788 else {
1789 if (boff > 0) {
1790 PUSH_INSN(ret, location, swap);
1792 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
1795 PUSH_INSN(ret, location, pop);
1799 * Compile an index control flow write node, which is a node that is writing a
1800 * value using the [] and []= methods and the &&= and ||= operators. It looks
1801 * like:
1803 * foo[bar] ||= baz
1805 * This breaks down to caching the receiver and arguments on the stack, calling
1806 * the [] method, checking the result and then changing control flow based on
1807 * it. If the value would result in a write, then the value is written using the
1808 * []= method.
1810 static void
1811 pm_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_node_t *block, const pm_node_t *value, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
1813 const pm_line_column_t location = *node_location;
1814 if (!popped) PUSH_INSN(ret, location, putnil);
1815 PM_COMPILE_NOT_POPPED(receiver);
1817 int boff = (block == NULL ? 0 : 1);
1818 int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
1819 struct rb_callinfo_kwarg *keywords = NULL;
1820 int argc = pm_setup_args(arguments, block, &flag, &keywords, iseq, ret, scope_node, node_location);
1822 if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
1823 if (boff) {
1824 PUSH_INSN(ret, location, splatkw);
1826 else {
1827 PUSH_INSN(ret, location, dup);
1828 PUSH_INSN(ret, location, splatkw);
1829 PUSH_INSN(ret, location, pop);
1833 int dup_argn = argc + 1 + boff;
1834 int keyword_len = 0;
1836 if (keywords) {
1837 keyword_len = keywords->keyword_len;
1838 dup_argn += keyword_len;
1841 PUSH_INSN1(ret, location, dupn, INT2FIX(dup_argn));
1842 PUSH_SEND_R(ret, location, idAREF, INT2FIX(argc), NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT | VM_CALL_KW_SPLAT_MUT)), keywords);
1844 LABEL *label = NEW_LABEL(location.line);
1845 LABEL *lfin = NEW_LABEL(location.line);
1847 PUSH_INSN(ret, location, dup);
1848 if (PM_NODE_TYPE_P(node, PM_INDEX_AND_WRITE_NODE)) {
1849 PUSH_INSNL(ret, location, branchunless, label);
1851 else {
1852 PUSH_INSNL(ret, location, branchif, label);
1855 PUSH_INSN(ret, location, pop);
1856 PM_COMPILE_NOT_POPPED(value);
1858 if (!popped) {
1859 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
1862 if (flag & VM_CALL_ARGS_SPLAT) {
1863 if (flag & VM_CALL_KW_SPLAT) {
1864 PUSH_INSN1(ret, location, topn, INT2FIX(2 + boff));
1865 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
1866 PUSH_INSN1(ret, location, splatarray, Qtrue);
1867 flag |= VM_CALL_ARGS_SPLAT_MUT;
1870 PUSH_INSN(ret, location, swap);
1871 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
1872 PUSH_INSN1(ret, location, setn, INT2FIX(2 + boff));
1873 PUSH_INSN(ret, location, pop);
1875 else {
1876 if (boff > 0) {
1877 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
1878 PUSH_INSN(ret, location, swap);
1879 PUSH_INSN(ret, location, pop);
1881 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
1882 PUSH_INSN(ret, location, swap);
1883 PUSH_INSN1(ret, location, splatarray, Qtrue);
1884 PUSH_INSN(ret, location, swap);
1885 flag |= VM_CALL_ARGS_SPLAT_MUT;
1887 PUSH_INSN1(ret, location, pushtoarray, INT2FIX(1));
1888 if (boff > 0) {
1889 PUSH_INSN1(ret, location, setn, INT2FIX(3));
1890 PUSH_INSN(ret, location, pop);
1891 PUSH_INSN(ret, location, pop);
1895 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc), NULL, INT2FIX(flag), keywords);
1897 else if (flag & VM_CALL_KW_SPLAT) {
1898 if (boff > 0) {
1899 PUSH_INSN1(ret, location, topn, INT2FIX(2));
1900 PUSH_INSN(ret, location, swap);
1901 PUSH_INSN1(ret, location, setn, INT2FIX(3));
1902 PUSH_INSN(ret, location, pop);
1905 PUSH_INSN(ret, location, swap);
1906 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
1908 else if (keyword_len) {
1909 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 1));
1910 PUSH_INSN1(ret, location, opt_reverse, INT2FIX(keyword_len + boff + 0));
1911 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
1913 else {
1914 if (boff > 0) {
1915 PUSH_INSN(ret, location, swap);
1917 PUSH_SEND_R(ret, location, idASET, INT2FIX(argc + 1), NULL, INT2FIX(flag), keywords);
1920 PUSH_INSN(ret, location, pop);
1921 PUSH_INSNL(ret, location, jump, lfin);
1922 PUSH_LABEL(ret, label);
1923 if (!popped) {
1924 PUSH_INSN1(ret, location, setn, INT2FIX(dup_argn + 1));
1926 PUSH_INSN1(ret, location, adjuststack, INT2FIX(dup_argn + 1));
1927 PUSH_LABEL(ret, lfin);
1930 // When we compile a pattern matching expression, we use the stack as a scratch
1931 // space to store lots of different values (consider it like we have a pattern
1932 // matching function and we need space for a bunch of different local
1933 // variables). The "base index" refers to the index on the stack where we
1934 // started compiling the pattern matching expression. These offsets from that
1935 // base index indicate the location of the various locals we need.
1936 #define PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE 0
1937 #define PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING 1
1938 #define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P 2
1939 #define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE 3
1940 #define PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY 4
1942 // A forward declaration because this is the recursive function that handles
1943 // compiling a pattern. It can be reentered by nesting patterns, as in the case
1944 // of arrays or hashes.
1945 static 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);
1948 * This function generates the code to set up the error string and error_p
1949 * locals depending on whether or not the pattern matched.
1951 static int
1952 pm_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)
1954 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
1955 LABEL *match_succeeded_label = NEW_LABEL(location.line);
1957 PUSH_INSN(ret, location, dup);
1958 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
1960 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1961 PUSH_INSN1(ret, location, putobject, message);
1962 PUSH_INSN1(ret, location, topn, INT2FIX(3));
1963 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(2));
1964 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
1966 PUSH_INSN1(ret, location, putobject, Qfalse);
1967 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
1969 PUSH_INSN(ret, location, pop);
1970 PUSH_INSN(ret, location, pop);
1971 PUSH_LABEL(ret, match_succeeded_label);
1973 return COMPILE_OK;
1977 * This function generates the code to set up the error string and error_p
1978 * locals depending on whether or not the pattern matched when the value needs
1979 * to match a specific deconstructed length.
1981 static int
1982 pm_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)
1984 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
1985 LABEL *match_succeeded_label = NEW_LABEL(location.line);
1987 PUSH_INSN(ret, location, dup);
1988 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
1990 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
1991 PUSH_INSN1(ret, location, putobject, message);
1992 PUSH_INSN1(ret, location, topn, INT2FIX(3));
1993 PUSH_INSN(ret, location, dup);
1994 PUSH_SEND(ret, location, idLength, INT2FIX(0));
1995 PUSH_INSN1(ret, location, putobject, length);
1996 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(4));
1997 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
1999 PUSH_INSN1(ret, location, putobject, Qfalse);
2000 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2002 PUSH_INSN(ret, location, pop);
2003 PUSH_INSN(ret, location, pop);
2004 PUSH_LABEL(ret, match_succeeded_label);
2006 return COMPILE_OK;
2010 * This function generates the code to set up the error string and error_p
2011 * locals depending on whether or not the pattern matched when the value needs
2012 * to pass a specific #=== method call.
2014 static int
2015 pm_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)
2017 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
2018 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2020 PUSH_INSN(ret, location, dup);
2021 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2023 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2024 PUSH_INSN1(ret, location, putobject, rb_fstring_lit("%p === %p does not return true"));
2025 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2026 PUSH_INSN1(ret, location, topn, INT2FIX(5));
2027 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2028 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2029 PUSH_INSN1(ret, location, putobject, Qfalse);
2030 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2031 PUSH_INSN(ret, location, pop);
2032 PUSH_INSN(ret, location, pop);
2034 PUSH_LABEL(ret, match_succeeded_label);
2035 PUSH_INSN1(ret, location, setn, INT2FIX(2));
2036 PUSH_INSN(ret, location, pop);
2037 PUSH_INSN(ret, location, pop);
2039 return COMPILE_OK;
2043 * This is a variation on compiling a pattern matching expression that is used
2044 * to have the pattern matching instructions fall through to immediately after
2045 * the pattern if it passes. Otherwise it jumps to the given unmatched_label
2046 * label.
2048 static int
2049 pm_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)
2051 LABEL *matched_label = NEW_LABEL(pm_node_line_number(scope_node->parser, node));
2052 CHECK(pm_compile_pattern(iseq, scope_node, node, ret, matched_label, unmatched_label, in_single_pattern, in_alternation_pattern, use_deconstructed_cache, base_index));
2053 PUSH_LABEL(ret, matched_label);
2054 return COMPILE_OK;
2058 * This function compiles in the code necessary to call #deconstruct on the
2059 * value to match against. It raises appropriate errors if the method does not
2060 * exist or if it returns the wrong type.
2062 static int
2063 pm_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)
2065 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
2067 if (use_deconstructed_cache) {
2068 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2069 PUSH_INSNL(ret, location, branchnil, deconstruct_label);
2071 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2072 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2074 PUSH_INSN(ret, location, pop);
2075 PUSH_INSN1(ret, location, topn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE - 1));
2076 PUSH_INSNL(ret, location, jump, deconstructed_label);
2078 else {
2079 PUSH_INSNL(ret, location, jump, deconstruct_label);
2082 PUSH_LABEL(ret, deconstruct_label);
2083 PUSH_INSN(ret, location, dup);
2084 PUSH_INSN1(ret, location, putobject, ID2SYM(rb_intern("deconstruct")));
2085 PUSH_SEND(ret, location, idRespond_to, INT2FIX(1));
2087 if (use_deconstructed_cache) {
2088 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE + 1));
2091 if (in_single_pattern) {
2092 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p does not respond to #deconstruct"), base_index + 1));
2095 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2096 PUSH_SEND(ret, location, rb_intern("deconstruct"), INT2FIX(0));
2098 if (use_deconstructed_cache) {
2099 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE));
2102 PUSH_INSN(ret, location, dup);
2103 PUSH_INSN1(ret, location, checktype, INT2FIX(T_ARRAY));
2104 PUSH_INSNL(ret, location, branchunless, type_error_label);
2105 PUSH_LABEL(ret, deconstructed_label);
2107 return COMPILE_OK;
2111 * This function compiles in the code necessary to match against the optional
2112 * constant path that is attached to an array, find, or hash pattern.
2114 static int
2115 pm_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)
2117 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
2119 PUSH_INSN(ret, location, dup);
2120 PM_COMPILE_NOT_POPPED(node);
2122 if (in_single_pattern) {
2123 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
2125 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
2126 if (in_single_pattern) {
2127 CHECK(pm_compile_pattern_eqq_error(iseq, scope_node, node, ret, base_index + 3));
2129 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2130 return COMPILE_OK;
2134 * When matching fails, an appropriate error must be raised. This function is
2135 * responsible for compiling in those error raising instructions.
2137 static void
2138 pm_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)
2140 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
2141 LABEL *key_error_label = NEW_LABEL(location.line);
2142 LABEL *cleanup_label = NEW_LABEL(location.line);
2144 struct rb_callinfo_kwarg *kw_arg = rb_xmalloc_mul_add(2, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
2145 kw_arg->references = 0;
2146 kw_arg->keyword_len = 2;
2147 kw_arg->keywords[0] = ID2SYM(rb_intern("matchee"));
2148 kw_arg->keywords[1] = ID2SYM(rb_intern("key"));
2150 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2151 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2152 PUSH_INSNL(ret, location, branchif, key_error_label);
2154 PUSH_INSN1(ret, location, putobject, rb_eNoMatchingPatternError);
2155 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2156 PUSH_INSN1(ret, location, putobject, rb_fstring_lit("%p: %s"));
2157 PUSH_INSN1(ret, location, topn, INT2FIX(4));
2158 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 6));
2159 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2160 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2161 PUSH_INSNL(ret, location, jump, cleanup_label);
2163 PUSH_LABEL(ret, key_error_label);
2164 PUSH_INSN1(ret, location, putobject, rb_eNoMatchingPatternKeyError);
2165 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2166 PUSH_INSN1(ret, location, putobject, rb_fstring_lit("%p: %s"));
2167 PUSH_INSN1(ret, location, topn, INT2FIX(4));
2168 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 6));
2169 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(3));
2170 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE + 4));
2171 PUSH_INSN1(ret, location, topn, INT2FIX(PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY + 5));
2172 PUSH_SEND_R(ret, location, rb_intern("new"), INT2FIX(1), NULL, INT2FIX(VM_CALL_KWARG), kw_arg);
2173 PUSH_SEND(ret, location, id_core_raise, INT2FIX(1));
2174 PUSH_LABEL(ret, cleanup_label);
2176 PUSH_INSN1(ret, location, adjuststack, INT2FIX(7));
2177 if (!popped) PUSH_INSN(ret, location, putnil);
2178 PUSH_INSNL(ret, location, jump, done_label);
2179 PUSH_INSN1(ret, location, dupn, INT2FIX(5));
2180 if (popped) PUSH_INSN(ret, location, putnil);
2184 * Compile a pattern matching expression.
2186 static int
2187 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)
2189 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
2191 switch (PM_NODE_TYPE(node)) {
2192 case PM_ARRAY_PATTERN_NODE: {
2193 // Array patterns in pattern matching are triggered by using commas in
2194 // a pattern or wrapping it in braces. They are represented by a
2195 // ArrayPatternNode. This looks like:
2197 // foo => [1, 2, 3]
2199 // It can optionally have a splat in the middle of it, which can
2200 // optionally have a name attached.
2201 const pm_array_pattern_node_t *cast = (const pm_array_pattern_node_t *) node;
2203 const size_t requireds_size = cast->requireds.size;
2204 const size_t posts_size = cast->posts.size;
2205 const size_t minimum_size = requireds_size + posts_size;
2207 bool rest_named = false;
2208 bool use_rest_size = false;
2210 if (cast->rest != NULL) {
2211 rest_named = (PM_NODE_TYPE_P(cast->rest, PM_SPLAT_NODE) && ((const pm_splat_node_t *) cast->rest)->expression != NULL);
2212 use_rest_size = (rest_named || (!rest_named && posts_size > 0));
2215 LABEL *match_failed_label = NEW_LABEL(location.line);
2216 LABEL *type_error_label = NEW_LABEL(location.line);
2217 LABEL *deconstruct_label = NEW_LABEL(location.line);
2218 LABEL *deconstructed_label = NEW_LABEL(location.line);
2220 if (use_rest_size) {
2221 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2222 PUSH_INSN(ret, location, swap);
2223 base_index++;
2226 if (cast->constant != NULL) {
2227 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2230 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));
2232 PUSH_INSN(ret, location, dup);
2233 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2234 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2235 PUSH_SEND(ret, location, cast->rest == NULL ? idEq : idGE, INT2FIX(1));
2236 if (in_single_pattern) {
2237 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+)");
2238 CHECK(pm_compile_pattern_length_error(iseq, scope_node, node, ret, message, INT2FIX(minimum_size), base_index + 1));
2240 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2242 for (size_t index = 0; index < requireds_size; index++) {
2243 const pm_node_t *required = cast->requireds.nodes[index];
2244 PUSH_INSN(ret, location, dup);
2245 PUSH_INSN1(ret, location, putobject, INT2FIX(index));
2246 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2247 CHECK(pm_compile_pattern_match(iseq, scope_node, required, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
2250 if (cast->rest != NULL) {
2251 if (rest_named) {
2252 PUSH_INSN(ret, location, dup);
2253 PUSH_INSN1(ret, location, putobject, INT2FIX(requireds_size));
2254 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2255 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2256 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2257 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2258 PUSH_INSN1(ret, location, setn, INT2FIX(4));
2259 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2260 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));
2262 else if (posts_size > 0) {
2263 PUSH_INSN(ret, location, dup);
2264 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2265 PUSH_INSN1(ret, location, putobject, INT2FIX(minimum_size));
2266 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2267 PUSH_INSN1(ret, location, setn, INT2FIX(2));
2268 PUSH_INSN(ret, location, pop);
2272 for (size_t index = 0; index < posts_size; index++) {
2273 const pm_node_t *post = cast->posts.nodes[index];
2274 PUSH_INSN(ret, location, dup);
2276 PUSH_INSN1(ret, location, putobject, INT2FIX(requireds_size + index));
2277 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2278 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2279 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2280 CHECK(pm_compile_pattern_match(iseq, scope_node, post, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
2283 PUSH_INSN(ret, location, pop);
2284 if (use_rest_size) {
2285 PUSH_INSN(ret, location, pop);
2288 PUSH_INSNL(ret, location, jump, matched_label);
2289 PUSH_INSN(ret, location, putnil);
2290 if (use_rest_size) {
2291 PUSH_INSN(ret, location, putnil);
2294 PUSH_LABEL(ret, type_error_label);
2295 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2296 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2297 PUSH_INSN1(ret, location, putobject, rb_fstring_lit("deconstruct must return Array"));
2298 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2299 PUSH_INSN(ret, location, pop);
2301 PUSH_LABEL(ret, match_failed_label);
2302 PUSH_INSN(ret, location, pop);
2303 if (use_rest_size) {
2304 PUSH_INSN(ret, location, pop);
2307 PUSH_INSNL(ret, location, jump, unmatched_label);
2308 break;
2310 case PM_FIND_PATTERN_NODE: {
2311 // Find patterns in pattern matching are triggered by using commas in
2312 // a pattern or wrapping it in braces and using a splat on both the left
2313 // and right side of the pattern. This looks like:
2315 // foo => [*, 1, 2, 3, *]
2317 // There can be any number of requireds in the middle. The splats on
2318 // both sides can optionally have names attached.
2319 const pm_find_pattern_node_t *cast = (const pm_find_pattern_node_t *) node;
2320 const size_t size = cast->requireds.size;
2322 LABEL *match_failed_label = NEW_LABEL(location.line);
2323 LABEL *type_error_label = NEW_LABEL(location.line);
2324 LABEL *deconstruct_label = NEW_LABEL(location.line);
2325 LABEL *deconstructed_label = NEW_LABEL(location.line);
2327 if (cast->constant) {
2328 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2331 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));
2333 PUSH_INSN(ret, location, dup);
2334 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2335 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2336 PUSH_SEND(ret, location, idGE, INT2FIX(1));
2337 if (in_single_pattern) {
2338 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));
2340 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2343 LABEL *while_begin_label = NEW_LABEL(location.line);
2344 LABEL *next_loop_label = NEW_LABEL(location.line);
2345 LABEL *find_succeeded_label = NEW_LABEL(location.line);
2346 LABEL *find_failed_label = NEW_LABEL(location.line);
2348 PUSH_INSN(ret, location, dup);
2349 PUSH_SEND(ret, location, idLength, INT2FIX(0));
2351 PUSH_INSN(ret, location, dup);
2352 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2353 PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
2354 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2355 PUSH_LABEL(ret, while_begin_label);
2357 PUSH_INSN(ret, location, dup);
2358 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2359 PUSH_SEND(ret, location, idLE, INT2FIX(1));
2360 PUSH_INSNL(ret, location, branchunless, find_failed_label);
2362 for (size_t index = 0; index < size; index++) {
2363 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2364 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2366 if (index != 0) {
2367 PUSH_INSN1(ret, location, putobject, INT2FIX(index));
2368 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2371 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
2372 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));
2375 RUBY_ASSERT(PM_NODE_TYPE_P(cast->left, PM_SPLAT_NODE));
2376 const pm_splat_node_t *left = (const pm_splat_node_t *) cast->left;
2378 if (left->expression != NULL) {
2379 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2380 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
2381 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2382 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2383 CHECK(pm_compile_pattern_match(iseq, scope_node, left->expression, ret, find_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 4));
2386 RUBY_ASSERT(PM_NODE_TYPE_P(cast->right, PM_SPLAT_NODE));
2387 const pm_splat_node_t *right = (const pm_splat_node_t *) cast->right;
2389 if (right->expression != NULL) {
2390 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2391 PUSH_INSN1(ret, location, topn, INT2FIX(1));
2392 PUSH_INSN1(ret, location, putobject, INT2FIX(size));
2393 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2394 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2395 PUSH_SEND(ret, location, idAREF, INT2FIX(2));
2396 pm_compile_pattern_match(iseq, scope_node, right->expression, ret, find_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 4);
2399 PUSH_INSNL(ret, location, jump, find_succeeded_label);
2401 PUSH_LABEL(ret, next_loop_label);
2402 PUSH_INSN1(ret, location, putobject, INT2FIX(1));
2403 PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
2404 PUSH_INSNL(ret, location, jump, while_begin_label);
2406 PUSH_LABEL(ret, find_failed_label);
2407 PUSH_INSN1(ret, location, adjuststack, INT2FIX(3));
2408 if (in_single_pattern) {
2409 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2410 PUSH_INSN1(ret, location, putobject, rb_fstring_lit("%p does not match to find pattern"));
2411 PUSH_INSN1(ret, location, topn, INT2FIX(2));
2412 PUSH_SEND(ret, location, id_core_sprintf, INT2FIX(2));
2413 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2415 PUSH_INSN1(ret, location, putobject, Qfalse);
2416 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2418 PUSH_INSN(ret, location, pop);
2419 PUSH_INSN(ret, location, pop);
2421 PUSH_INSNL(ret, location, jump, match_failed_label);
2422 PUSH_INSN1(ret, location, dupn, INT2FIX(3));
2424 PUSH_LABEL(ret, find_succeeded_label);
2425 PUSH_INSN1(ret, location, adjuststack, INT2FIX(3));
2428 PUSH_INSN(ret, location, pop);
2429 PUSH_INSNL(ret, location, jump, matched_label);
2430 PUSH_INSN(ret, location, putnil);
2432 PUSH_LABEL(ret, type_error_label);
2433 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2434 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2435 PUSH_INSN1(ret, location, putobject, rb_fstring_lit("deconstruct must return Array"));
2436 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2437 PUSH_INSN(ret, location, pop);
2439 PUSH_LABEL(ret, match_failed_label);
2440 PUSH_INSN(ret, location, pop);
2441 PUSH_INSNL(ret, location, jump, unmatched_label);
2443 break;
2445 case PM_HASH_PATTERN_NODE: {
2446 // Hash patterns in pattern matching are triggered by using labels and
2447 // values in a pattern or by using the ** operator. They are represented
2448 // by the HashPatternNode. This looks like:
2450 // foo => { a: 1, b: 2, **bar }
2452 // It can optionally have an assoc splat in the middle of it, which can
2453 // optionally have a name.
2454 const pm_hash_pattern_node_t *cast = (const pm_hash_pattern_node_t *) node;
2456 // We don't consider it a "rest" parameter if it's a ** that is unnamed.
2457 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);
2458 bool has_keys = cast->elements.size > 0 || cast->rest != NULL;
2460 LABEL *match_failed_label = NEW_LABEL(location.line);
2461 LABEL *type_error_label = NEW_LABEL(location.line);
2462 VALUE keys = Qnil;
2464 if (has_keys && !has_rest) {
2465 keys = rb_ary_new_capa(cast->elements.size);
2467 for (size_t index = 0; index < cast->elements.size; index++) {
2468 const pm_node_t *element = cast->elements.nodes[index];
2469 RUBY_ASSERT(PM_NODE_TYPE_P(element, PM_ASSOC_NODE));
2471 const pm_node_t *key = ((const pm_assoc_node_t *) element)->key;
2472 RUBY_ASSERT(PM_NODE_TYPE_P(key, PM_SYMBOL_NODE));
2474 VALUE symbol = ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) key));
2475 rb_ary_push(keys, symbol);
2479 if (cast->constant) {
2480 CHECK(pm_compile_pattern_constant(iseq, scope_node, cast->constant, ret, match_failed_label, in_single_pattern, base_index));
2483 PUSH_INSN(ret, location, dup);
2484 PUSH_INSN1(ret, location, putobject, ID2SYM(rb_intern("deconstruct_keys")));
2485 PUSH_SEND(ret, location, idRespond_to, INT2FIX(1));
2486 if (in_single_pattern) {
2487 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p does not respond to #deconstruct_keys"), base_index + 1));
2489 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2491 if (NIL_P(keys)) {
2492 PUSH_INSN(ret, location, putnil);
2494 else {
2495 PUSH_INSN1(ret, location, duparray, keys);
2496 RB_OBJ_WRITTEN(iseq, Qundef, rb_obj_hide(keys));
2498 PUSH_SEND(ret, location, rb_intern("deconstruct_keys"), INT2FIX(1));
2500 PUSH_INSN(ret, location, dup);
2501 PUSH_INSN1(ret, location, checktype, INT2FIX(T_HASH));
2502 PUSH_INSNL(ret, location, branchunless, type_error_label);
2504 if (has_rest) {
2505 PUSH_SEND(ret, location, rb_intern("dup"), INT2FIX(0));
2508 if (has_keys) {
2509 DECL_ANCHOR(match_values);
2510 INIT_ANCHOR(match_values);
2512 for (size_t index = 0; index < cast->elements.size; index++) {
2513 const pm_node_t *element = cast->elements.nodes[index];
2514 RUBY_ASSERT(PM_NODE_TYPE_P(element, PM_ASSOC_NODE));
2516 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
2517 const pm_node_t *key = assoc->key;
2518 RUBY_ASSERT(PM_NODE_TYPE_P(key, PM_SYMBOL_NODE));
2520 VALUE symbol = ID2SYM(parse_string_symbol(scope_node, (const pm_symbol_node_t *) key));
2521 PUSH_INSN(ret, location, dup);
2522 PUSH_INSN1(ret, location, putobject, symbol);
2523 PUSH_SEND(ret, location, rb_intern("key?"), INT2FIX(1));
2525 if (in_single_pattern) {
2526 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2528 PUSH_INSN(ret, location, dup);
2529 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2531 PUSH_INSN1(ret, location, putobject, rb_str_freeze(rb_sprintf("key not found: %+"PRIsVALUE, symbol)));
2532 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 2));
2533 PUSH_INSN1(ret, location, putobject, Qtrue);
2534 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 3));
2535 PUSH_INSN1(ret, location, topn, INT2FIX(3));
2536 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE + 4));
2537 PUSH_INSN1(ret, location, putobject, symbol);
2538 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY + 5));
2540 PUSH_INSN1(ret, location, adjuststack, INT2FIX(4));
2541 PUSH_LABEL(ret, match_succeeded_label);
2544 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2545 PUSH_INSN(match_values, location, dup);
2546 PUSH_INSN1(match_values, location, putobject, symbol);
2547 PUSH_SEND(match_values, location, has_rest ? rb_intern("delete") : idAREF, INT2FIX(1));
2549 const pm_node_t *value = assoc->value;
2550 if (PM_NODE_TYPE_P(value, PM_IMPLICIT_NODE)) {
2551 value = ((const pm_implicit_node_t *) value)->value;
2554 CHECK(pm_compile_pattern_match(iseq, scope_node, value, match_values, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
2557 PUSH_SEQ(ret, match_values);
2559 else {
2560 PUSH_INSN(ret, location, dup);
2561 PUSH_SEND(ret, location, idEmptyP, INT2FIX(0));
2562 if (in_single_pattern) {
2563 CHECK(pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("%p is not empty"), base_index + 1));
2565 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2568 if (has_rest) {
2569 switch (PM_NODE_TYPE(cast->rest)) {
2570 case PM_NO_KEYWORDS_PARAMETER_NODE: {
2571 PUSH_INSN(ret, location, dup);
2572 PUSH_SEND(ret, location, idEmptyP, INT2FIX(0));
2573 if (in_single_pattern) {
2574 pm_compile_pattern_generic_error(iseq, scope_node, node, ret, rb_fstring_lit("rest of %p is not empty"), base_index + 1);
2576 PUSH_INSNL(ret, location, branchunless, match_failed_label);
2577 break;
2579 case PM_ASSOC_SPLAT_NODE: {
2580 const pm_assoc_splat_node_t *splat = (const pm_assoc_splat_node_t *) cast->rest;
2581 PUSH_INSN(ret, location, dup);
2582 pm_compile_pattern_match(iseq, scope_node, splat->value, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1);
2583 break;
2585 default:
2586 rb_bug("unreachable");
2587 break;
2591 PUSH_INSN(ret, location, pop);
2592 PUSH_INSNL(ret, location, jump, matched_label);
2593 PUSH_INSN(ret, location, putnil);
2595 PUSH_LABEL(ret, type_error_label);
2596 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
2597 PUSH_INSN1(ret, location, putobject, rb_eTypeError);
2598 PUSH_INSN1(ret, location, putobject, rb_fstring_lit("deconstruct_keys must return Hash"));
2599 PUSH_SEND(ret, location, id_core_raise, INT2FIX(2));
2600 PUSH_INSN(ret, location, pop);
2602 PUSH_LABEL(ret, match_failed_label);
2603 PUSH_INSN(ret, location, pop);
2604 PUSH_INSNL(ret, location, jump, unmatched_label);
2605 break;
2607 case PM_CAPTURE_PATTERN_NODE: {
2608 // Capture patterns allow you to pattern match against an element in a
2609 // pattern and also capture the value into a local variable. This looks
2610 // like:
2612 // [1] => [Integer => foo]
2614 // In this case the `Integer => foo` will be represented by a
2615 // CapturePatternNode, which has both a value (the pattern to match
2616 // against) and a target (the place to write the variable into).
2617 const pm_capture_pattern_node_t *cast = (const pm_capture_pattern_node_t *) node;
2619 LABEL *match_failed_label = NEW_LABEL(location.line);
2621 PUSH_INSN(ret, location, dup);
2622 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));
2623 CHECK(pm_compile_pattern(iseq, scope_node, cast->target, ret, matched_label, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index));
2624 PUSH_INSN(ret, location, putnil);
2626 PUSH_LABEL(ret, match_failed_label);
2627 PUSH_INSN(ret, location, pop);
2628 PUSH_INSNL(ret, location, jump, unmatched_label);
2630 break;
2632 case PM_LOCAL_VARIABLE_TARGET_NODE: {
2633 // Local variables can be targeted by placing identifiers in the place
2634 // of a pattern. For example, foo in bar. This results in the value
2635 // being matched being written to that local variable.
2636 const pm_local_variable_target_node_t *cast = (const pm_local_variable_target_node_t *) node;
2637 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
2639 // If this local variable is being written from within an alternation
2640 // pattern, then it cannot actually be added to the local table since
2641 // it's ambiguous which value should be used. So instead we indicate
2642 // this with a compile error.
2643 if (in_alternation_pattern) {
2644 ID id = pm_constant_id_lookup(scope_node, cast->name);
2645 const char *name = rb_id2name(id);
2647 if (name && strlen(name) > 0 && name[0] != '_') {
2648 COMPILE_ERROR(iseq, location.line, "illegal variable in alternative pattern (%"PRIsVALUE")", rb_id2str(id));
2649 return COMPILE_NG;
2653 PUSH_SETLOCAL(ret, location, index.index, index.level);
2654 PUSH_INSNL(ret, location, jump, matched_label);
2655 break;
2657 case PM_ALTERNATION_PATTERN_NODE: {
2658 // Alternation patterns allow you to specify multiple patterns in a
2659 // single expression using the | operator.
2660 const pm_alternation_pattern_node_t *cast = (const pm_alternation_pattern_node_t *) node;
2662 LABEL *matched_left_label = NEW_LABEL(location.line);
2663 LABEL *unmatched_left_label = NEW_LABEL(location.line);
2665 // First, we're going to attempt to match against the left pattern. If
2666 // that pattern matches, then we'll skip matching the right pattern.
2667 PUSH_INSN(ret, location, dup);
2668 CHECK(pm_compile_pattern(iseq, scope_node, cast->left, ret, matched_left_label, unmatched_left_label, in_single_pattern, true, true, base_index + 1));
2670 // If we get here, then we matched on the left pattern. In this case we
2671 // should pop out the duplicate value that we preemptively added to
2672 // match against the right pattern and then jump to the match label.
2673 PUSH_LABEL(ret, matched_left_label);
2674 PUSH_INSN(ret, location, pop);
2675 PUSH_INSNL(ret, location, jump, matched_label);
2676 PUSH_INSN(ret, location, putnil);
2678 // If we get here, then we didn't match on the left pattern. In this
2679 // case we attempt to match against the right pattern.
2680 PUSH_LABEL(ret, unmatched_left_label);
2681 CHECK(pm_compile_pattern(iseq, scope_node, cast->right, ret, matched_label, unmatched_label, in_single_pattern, true, true, base_index));
2682 break;
2684 case PM_PARENTHESES_NODE:
2685 // Parentheses are allowed to wrap expressions in pattern matching and
2686 // they do nothing since they can only wrap individual expressions and
2687 // not groups. In this case we'll recurse back into this same function
2688 // with the body of the parentheses.
2689 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);
2690 case PM_PINNED_EXPRESSION_NODE:
2691 // Pinned expressions are a way to match against the value of an
2692 // expression that should be evaluated at runtime. This looks like:
2693 // foo in ^(bar). To compile these, we compile the expression as if it
2694 // were a literal value by falling through to the literal case.
2695 node = ((const pm_pinned_expression_node_t *) node)->expression;
2696 /* fallthrough */
2697 case PM_ARRAY_NODE:
2698 case PM_CLASS_VARIABLE_READ_NODE:
2699 case PM_CONSTANT_PATH_NODE:
2700 case PM_CONSTANT_READ_NODE:
2701 case PM_FALSE_NODE:
2702 case PM_FLOAT_NODE:
2703 case PM_GLOBAL_VARIABLE_READ_NODE:
2704 case PM_IMAGINARY_NODE:
2705 case PM_INSTANCE_VARIABLE_READ_NODE:
2706 case PM_INTEGER_NODE:
2707 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
2708 case PM_INTERPOLATED_STRING_NODE:
2709 case PM_INTERPOLATED_SYMBOL_NODE:
2710 case PM_INTERPOLATED_X_STRING_NODE:
2711 case PM_LAMBDA_NODE:
2712 case PM_LOCAL_VARIABLE_READ_NODE:
2713 case PM_NIL_NODE:
2714 case PM_SOURCE_ENCODING_NODE:
2715 case PM_SOURCE_FILE_NODE:
2716 case PM_SOURCE_LINE_NODE:
2717 case PM_RANGE_NODE:
2718 case PM_RATIONAL_NODE:
2719 case PM_REGULAR_EXPRESSION_NODE:
2720 case PM_SELF_NODE:
2721 case PM_STRING_NODE:
2722 case PM_SYMBOL_NODE:
2723 case PM_TRUE_NODE:
2724 case PM_X_STRING_NODE: {
2725 // These nodes are all simple patterns, which means we'll use the
2726 // checkmatch instruction to match against them, which is effectively a
2727 // VM-level === operator.
2728 PM_COMPILE_NOT_POPPED(node);
2729 if (in_single_pattern) {
2730 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
2733 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
2735 if (in_single_pattern) {
2736 pm_compile_pattern_eqq_error(iseq, scope_node, node, ret, base_index + 2);
2739 PUSH_INSNL(ret, location, branchif, matched_label);
2740 PUSH_INSNL(ret, location, jump, unmatched_label);
2741 break;
2743 case PM_PINNED_VARIABLE_NODE: {
2744 // Pinned variables are a way to match against the value of a variable
2745 // without it looking like you're trying to write to the variable. This
2746 // looks like: foo in ^@bar. To compile these, we compile the variable
2747 // that they hold.
2748 const pm_pinned_variable_node_t *cast = (const pm_pinned_variable_node_t *) node;
2749 CHECK(pm_compile_pattern(iseq, scope_node, cast->variable, ret, matched_label, unmatched_label, in_single_pattern, in_alternation_pattern, true, base_index));
2750 break;
2752 case PM_IF_NODE:
2753 case PM_UNLESS_NODE: {
2754 // If and unless nodes can show up here as guards on `in` clauses. This
2755 // looks like:
2757 // case foo
2758 // in bar if baz?
2759 // qux
2760 // end
2762 // Because we know they're in the modifier form and they can't have any
2763 // variation on this pattern, we compile them differently (more simply)
2764 // here than we would in the normal compilation path.
2765 const pm_node_t *predicate;
2766 const pm_node_t *statement;
2768 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
2769 const pm_if_node_t *cast = (const pm_if_node_t *) node;
2770 predicate = cast->predicate;
2772 RUBY_ASSERT(cast->statements != NULL && cast->statements->body.size == 1);
2773 statement = cast->statements->body.nodes[0];
2775 else {
2776 const pm_unless_node_t *cast = (const pm_unless_node_t *) node;
2777 predicate = cast->predicate;
2779 RUBY_ASSERT(cast->statements != NULL && cast->statements->body.size == 1);
2780 statement = cast->statements->body.nodes[0];
2783 CHECK(pm_compile_pattern_match(iseq, scope_node, statement, ret, unmatched_label, in_single_pattern, in_alternation_pattern, use_deconstructed_cache, base_index));
2784 PM_COMPILE_NOT_POPPED(predicate);
2786 if (in_single_pattern) {
2787 LABEL *match_succeeded_label = NEW_LABEL(location.line);
2789 PUSH_INSN(ret, location, dup);
2790 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
2791 PUSH_INSNL(ret, location, branchif, match_succeeded_label);
2793 else {
2794 PUSH_INSNL(ret, location, branchunless, match_succeeded_label);
2797 PUSH_INSN1(ret, location, putobject, rb_fstring_lit("guard clause does not return true"));
2798 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING + 1));
2799 PUSH_INSN1(ret, location, putobject, Qfalse);
2800 PUSH_INSN1(ret, location, setn, INT2FIX(base_index + PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P + 2));
2802 PUSH_INSN(ret, location, pop);
2803 PUSH_INSN(ret, location, pop);
2805 PUSH_LABEL(ret, match_succeeded_label);
2808 if (PM_NODE_TYPE_P(node, PM_IF_NODE)) {
2809 PUSH_INSNL(ret, location, branchunless, unmatched_label);
2811 else {
2812 PUSH_INSNL(ret, location, branchif, unmatched_label);
2815 PUSH_INSNL(ret, location, jump, matched_label);
2816 break;
2818 default:
2819 // If we get here, then we have a node type that should not be in this
2820 // position. This would be a bug in the parser, because a different node
2821 // type should never have been created in this position in the tree.
2822 rb_bug("Unexpected node type in pattern matching expression: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
2823 break;
2826 return COMPILE_OK;
2829 #undef PM_PATTERN_BASE_INDEX_OFFSET_DECONSTRUCTED_CACHE
2830 #undef PM_PATTERN_BASE_INDEX_OFFSET_ERROR_STRING
2831 #undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_P
2832 #undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_MATCHEE
2833 #undef PM_PATTERN_BASE_INDEX_OFFSET_KEY_ERROR_KEY
2835 // Generate a scope node from the given node.
2836 void
2837 pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_t *previous)
2839 // This is very important, otherwise the scope node could be seen as having
2840 // certain flags set that _should not_ be set.
2841 memset(scope, 0, sizeof(pm_scope_node_t));
2843 scope->base.type = PM_SCOPE_NODE;
2844 scope->base.location.start = node->location.start;
2845 scope->base.location.end = node->location.end;
2847 scope->previous = previous;
2848 scope->ast_node = (pm_node_t *) node;
2850 if (previous) {
2851 scope->parser = previous->parser;
2852 scope->encoding = previous->encoding;
2853 scope->filepath_encoding = previous->filepath_encoding;
2854 scope->constants = previous->constants;
2855 scope->coverage_enabled = previous->coverage_enabled;
2858 switch (PM_NODE_TYPE(node)) {
2859 case PM_BLOCK_NODE: {
2860 const pm_block_node_t *cast = (const pm_block_node_t *) node;
2861 scope->body = cast->body;
2862 scope->locals = cast->locals;
2863 scope->parameters = cast->parameters;
2864 break;
2866 case PM_CLASS_NODE: {
2867 const pm_class_node_t *cast = (const pm_class_node_t *) node;
2868 scope->body = cast->body;
2869 scope->locals = cast->locals;
2870 break;
2872 case PM_DEF_NODE: {
2873 const pm_def_node_t *cast = (const pm_def_node_t *) node;
2874 scope->parameters = (pm_node_t *) cast->parameters;
2875 scope->body = cast->body;
2876 scope->locals = cast->locals;
2877 break;
2879 case PM_ENSURE_NODE: {
2880 const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node;
2881 scope->body = (pm_node_t *) node;
2883 if (cast->statements != NULL) {
2884 scope->base.location.start = cast->statements->base.location.start;
2885 scope->base.location.end = cast->statements->base.location.end;
2888 break;
2890 case PM_FOR_NODE: {
2891 const pm_for_node_t *cast = (const pm_for_node_t *) node;
2892 scope->body = (pm_node_t *) cast->statements;
2893 break;
2895 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
2896 RUBY_ASSERT(node->flags & PM_REGULAR_EXPRESSION_FLAGS_ONCE);
2897 scope->body = (pm_node_t *) node;
2898 break;
2900 case PM_LAMBDA_NODE: {
2901 const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node;
2902 scope->parameters = cast->parameters;
2903 scope->body = cast->body;
2904 scope->locals = cast->locals;
2906 if (cast->parameters != NULL) {
2907 scope->base.location.start = cast->parameters->location.start;
2909 else {
2910 scope->base.location.start = cast->operator_loc.end;
2912 break;
2914 case PM_MODULE_NODE: {
2915 const pm_module_node_t *cast = (const pm_module_node_t *) node;
2916 scope->body = cast->body;
2917 scope->locals = cast->locals;
2918 break;
2920 case PM_POST_EXECUTION_NODE: {
2921 const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) node;
2922 scope->body = (pm_node_t *) cast->statements;
2923 break;
2925 case PM_PROGRAM_NODE: {
2926 const pm_program_node_t *cast = (const pm_program_node_t *) node;
2927 scope->body = (pm_node_t *) cast->statements;
2928 scope->locals = cast->locals;
2929 break;
2931 case PM_RESCUE_NODE: {
2932 const pm_rescue_node_t *cast = (const pm_rescue_node_t *) node;
2933 scope->body = (pm_node_t *) cast->statements;
2934 break;
2936 case PM_RESCUE_MODIFIER_NODE: {
2937 const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node;
2938 scope->body = (pm_node_t *) cast->rescue_expression;
2939 break;
2941 case PM_SINGLETON_CLASS_NODE: {
2942 const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node;
2943 scope->body = cast->body;
2944 scope->locals = cast->locals;
2945 break;
2947 case PM_STATEMENTS_NODE: {
2948 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
2949 scope->body = (pm_node_t *) cast;
2950 break;
2952 default:
2953 rb_bug("unreachable");
2954 break;
2958 void
2959 pm_scope_node_destroy(pm_scope_node_t *scope_node)
2961 if (scope_node->index_lookup_table) {
2962 st_free_table(scope_node->index_lookup_table);
2967 * We need to put the label "retry_end_l" immediately after the last "send"
2968 * instruction. This because vm_throw checks if the break cont is equal to the
2969 * index of next insn of the "send". (Otherwise, it is considered
2970 * "break from proc-closure". See "TAG_BREAK" handling in "vm_throw_start".)
2972 * Normally, "send" instruction is at the last. However, qcall under branch
2973 * coverage measurement adds some instructions after the "send".
2975 * Note that "invokesuper" appears instead of "send".
2977 static void
2978 pm_compile_retry_end_label(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *retry_end_l)
2980 INSN *iobj;
2981 LINK_ELEMENT *last_elem = LAST_ELEMENT(ret);
2982 iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem);
2983 while (INSN_OF(iobj) != BIN(send) && INSN_OF(iobj) != BIN(invokesuper)) {
2984 iobj = (INSN*) get_prev_insn(iobj);
2986 ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l);
2988 // LINK_ANCHOR has a pointer to the last element, but
2989 // ELEM_INSERT_NEXT does not update it even if we add an insn to the
2990 // last of LINK_ANCHOR. So this updates it manually.
2991 if (&iobj->link == LAST_ELEMENT(ret)) {
2992 ret->last = (LINK_ELEMENT*) retry_end_l;
2996 static const char *
2997 pm_iseq_builtin_function_name(const pm_scope_node_t *scope_node, const pm_node_t *receiver, ID method_id)
2999 const char *name = rb_id2name(method_id);
3000 static const char prefix[] = "__builtin_";
3001 const size_t prefix_len = sizeof(prefix) - 1;
3003 if (receiver == NULL) {
3004 if (UNLIKELY(strncmp(prefix, name, prefix_len) == 0)) {
3005 // __builtin_foo
3006 return &name[prefix_len];
3009 else if (PM_NODE_TYPE_P(receiver, PM_CALL_NODE)) {
3010 if (PM_NODE_FLAG_P(receiver, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
3011 const pm_call_node_t *cast = (const pm_call_node_t *) receiver;
3012 if (pm_constant_id_lookup(scope_node, cast->name) == rb_intern_const("__builtin")) {
3013 // __builtin.foo
3014 return name;
3018 else if (PM_NODE_TYPE_P(receiver, PM_CONSTANT_READ_NODE)) {
3019 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) receiver;
3020 if (pm_constant_id_lookup(scope_node, cast->name) == rb_intern_const("Primitive")) {
3021 // Primitive.foo
3022 return name;
3026 return NULL;
3029 // Compile Primitive.attr! :leaf, ...
3030 static int
3031 pm_compile_builtin_attr(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_arguments_node_t *arguments, const pm_line_column_t *node_location)
3033 if (arguments == NULL) {
3034 COMPILE_ERROR(iseq, node_location->line, "attr!: no argument");
3035 return COMPILE_NG;
3038 const pm_node_t *argument;
3039 PM_NODE_LIST_FOREACH(&arguments->arguments, index, argument) {
3040 if (!PM_NODE_TYPE_P(argument, PM_SYMBOL_NODE)) {
3041 COMPILE_ERROR(iseq, node_location->line, "non symbol argument to attr!: %s", pm_node_type_to_str(PM_NODE_TYPE(argument)));
3042 return COMPILE_NG;
3045 VALUE symbol = pm_static_literal_value(iseq, argument, scope_node);
3046 VALUE string = rb_sym_to_s(symbol);
3048 if (strcmp(RSTRING_PTR(string), "leaf") == 0) {
3049 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_LEAF;
3051 else if (strcmp(RSTRING_PTR(string), "inline_block") == 0) {
3052 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_INLINE_BLOCK;
3054 else if (strcmp(RSTRING_PTR(string), "use_block") == 0) {
3055 iseq_set_use_block(iseq);
3057 else {
3058 COMPILE_ERROR(iseq, node_location->line, "unknown argument to attr!: %s", RSTRING_PTR(string));
3059 return COMPILE_NG;
3063 return COMPILE_OK;
3066 static int
3067 pm_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_line_column_t *node_location, int popped)
3069 if (arguments == NULL) {
3070 COMPILE_ERROR(iseq, node_location->line, "arg!: no argument");
3071 return COMPILE_NG;
3074 if (arguments->arguments.size != 1) {
3075 COMPILE_ERROR(iseq, node_location->line, "arg!: too many argument");
3076 return COMPILE_NG;
3079 const pm_node_t *argument = arguments->arguments.nodes[0];
3080 if (!PM_NODE_TYPE_P(argument, PM_SYMBOL_NODE)) {
3081 COMPILE_ERROR(iseq, node_location->line, "non symbol argument to arg!: %s", pm_node_type_to_str(PM_NODE_TYPE(argument)));
3082 return COMPILE_NG;
3085 if (!popped) {
3086 ID name = parse_string_symbol(scope_node, ((const pm_symbol_node_t *) argument));
3087 int index = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size - get_local_var_idx(iseq, name);
3089 debugs("id: %s idx: %d\n", rb_id2name(name), index);
3090 PUSH_GETLOCAL(ret, *node_location, index, get_lvar_level(iseq));
3093 return COMPILE_OK;
3096 static int
3097 pm_compile_builtin_mandatory_only_method(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_call_node_t *call_node, const pm_line_column_t *node_location)
3099 const pm_node_t *ast_node = scope_node->ast_node;
3100 if (!PM_NODE_TYPE_P(ast_node, PM_DEF_NODE)) {
3101 rb_bug("mandatory_only?: not in method definition");
3102 return COMPILE_NG;
3105 const pm_def_node_t *def_node = (const pm_def_node_t *) ast_node;
3106 const pm_parameters_node_t *parameters_node = def_node->parameters;
3107 if (parameters_node == NULL) {
3108 rb_bug("mandatory_only?: in method definition with no parameters");
3109 return COMPILE_NG;
3112 const pm_node_t *body_node = def_node->body;
3113 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)) {
3114 rb_bug("mandatory_only?: not in method definition with plain statements");
3115 return COMPILE_NG;
3118 const pm_if_node_t *if_node = (const pm_if_node_t *) ((const pm_statements_node_t *) body_node)->body.nodes[0];
3119 if (if_node->predicate != ((const pm_node_t *) call_node)) {
3120 rb_bug("mandatory_only?: can't find mandatory node");
3121 return COMPILE_NG;
3124 pm_parameters_node_t parameters = {
3125 .base = parameters_node->base,
3126 .requireds = parameters_node->requireds
3129 const pm_def_node_t def = {
3130 .base = def_node->base,
3131 .name = def_node->name,
3132 .receiver = def_node->receiver,
3133 .parameters = &parameters,
3134 .body = (pm_node_t *) if_node->statements,
3135 .locals = {
3136 .ids = def_node->locals.ids,
3137 .size = parameters_node->requireds.size,
3138 .capacity = def_node->locals.capacity
3142 pm_scope_node_t next_scope_node;
3143 pm_scope_node_init(&def.base, &next_scope_node, scope_node);
3145 ISEQ_BODY(iseq)->mandatory_only_iseq = pm_iseq_new_with_opt(
3146 &next_scope_node,
3147 rb_iseq_base_label(iseq),
3148 rb_iseq_path(iseq),
3149 rb_iseq_realpath(iseq),
3150 node_location->line,
3151 NULL,
3153 ISEQ_TYPE_METHOD,
3154 ISEQ_COMPILE_DATA(iseq)->option
3157 pm_scope_node_destroy(&next_scope_node);
3158 return COMPILE_OK;
3161 static int
3162 pm_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_line_column_t *node_location, int popped, const rb_iseq_t *parent_block, const char *builtin_func)
3164 const pm_arguments_node_t *arguments = call_node->arguments;
3166 if (parent_block != NULL) {
3167 COMPILE_ERROR(iseq, node_location->line, "should not call builtins here.");
3168 return COMPILE_NG;
3171 #define BUILTIN_INLINE_PREFIX "_bi"
3172 char inline_func[sizeof(BUILTIN_INLINE_PREFIX) + DECIMAL_SIZE_OF(int)];
3173 bool cconst = false;
3174 retry:;
3175 const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
3177 if (bf == NULL) {
3178 if (strcmp("cstmt!", builtin_func) == 0 || strcmp("cexpr!", builtin_func) == 0) {
3179 // ok
3181 else if (strcmp("cconst!", builtin_func) == 0) {
3182 cconst = true;
3184 else if (strcmp("cinit!", builtin_func) == 0) {
3185 // ignore
3186 return COMPILE_OK;
3188 else if (strcmp("attr!", builtin_func) == 0) {
3189 return pm_compile_builtin_attr(iseq, scope_node, arguments, node_location);
3191 else if (strcmp("arg!", builtin_func) == 0) {
3192 return pm_compile_builtin_arg(iseq, ret, scope_node, arguments, node_location, popped);
3194 else if (strcmp("mandatory_only?", builtin_func) == 0) {
3195 if (popped) {
3196 rb_bug("mandatory_only? should be in if condition");
3198 else if (!LIST_INSN_SIZE_ZERO(ret)) {
3199 rb_bug("mandatory_only? should be put on top");
3202 PUSH_INSN1(ret, *node_location, putobject, Qfalse);
3203 return pm_compile_builtin_mandatory_only_method(iseq, scope_node, call_node, node_location);
3205 else if (1) {
3206 rb_bug("can't find builtin function:%s", builtin_func);
3208 else {
3209 COMPILE_ERROR(iseq, node_location->line, "can't find builtin function:%s", builtin_func);
3210 return COMPILE_NG;
3213 int inline_index = node_location->line;
3214 snprintf(inline_func, sizeof(inline_func), BUILTIN_INLINE_PREFIX "%d", inline_index);
3215 builtin_func = inline_func;
3216 arguments = NULL;
3217 goto retry;
3220 if (cconst) {
3221 typedef VALUE(*builtin_func0)(void *, VALUE);
3222 VALUE const_val = (*(builtin_func0)bf->func_ptr)(NULL, Qnil);
3223 PUSH_INSN1(ret, *node_location, putobject, const_val);
3224 return COMPILE_OK;
3227 // fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
3229 DECL_ANCHOR(args_seq);
3230 INIT_ANCHOR(args_seq);
3232 int flags = 0;
3233 struct rb_callinfo_kwarg *keywords = NULL;
3234 int argc = pm_setup_args(arguments, call_node->block, &flags, &keywords, iseq, args_seq, scope_node, node_location);
3236 if (argc != bf->argc) {
3237 COMPILE_ERROR(iseq, node_location->line, "argc is not match for builtin function:%s (expect %d but %d)", builtin_func, bf->argc, argc);
3238 return COMPILE_NG;
3241 unsigned int start_index;
3242 if (delegate_call_p(iseq, argc, args_seq, &start_index)) {
3243 PUSH_INSN2(ret, *node_location, opt_invokebuiltin_delegate, bf, INT2FIX(start_index));
3245 else {
3246 PUSH_SEQ(ret, args_seq);
3247 PUSH_INSN1(ret, *node_location, invokebuiltin, bf);
3250 if (popped) PUSH_INSN(ret, *node_location, pop);
3251 return COMPILE_OK;
3255 * Compile a call node into the given iseq.
3257 static void
3258 pm_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)
3260 const pm_location_t *message_loc = &call_node->message_loc;
3261 if (message_loc->start == NULL) message_loc = &call_node->base.location;
3263 const pm_line_column_t location = PM_LOCATION_START_LINE_COLUMN(scope_node->parser, message_loc);
3264 LABEL *else_label = NEW_LABEL(location.line);
3265 LABEL *end_label = NEW_LABEL(location.line);
3266 LABEL *retry_end_l = NEW_LABEL(location.line);
3268 VALUE branches = Qfalse;
3269 rb_code_location_t code_location = { 0 };
3270 int node_id = location.column;
3272 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
3273 if (PM_BRANCH_COVERAGE_P(iseq)) {
3274 const uint8_t *cursors[3] = {
3275 call_node->closing_loc.end,
3276 call_node->arguments == NULL ? NULL : call_node->arguments->base.location.end,
3277 call_node->message_loc.end
3280 const uint8_t *end_cursor = cursors[0];
3281 end_cursor = (end_cursor == NULL || cursors[1] == NULL) ? cursors[1] : (end_cursor > cursors[1] ? end_cursor : cursors[1]);
3282 end_cursor = (end_cursor == NULL || cursors[2] == NULL) ? cursors[2] : (end_cursor > cursors[2] ? end_cursor : cursors[2]);
3284 const pm_line_column_t start_location = PM_NODE_START_LINE_COLUMN(scope_node->parser, call_node);
3285 const pm_line_column_t end_location = pm_newline_list_line_column(&scope_node->parser->newline_list, end_cursor, scope_node->parser->start_line);
3287 code_location = (rb_code_location_t) {
3288 .beg_pos = { .lineno = start_location.line, .column = start_location.column },
3289 .end_pos = { .lineno = end_location.line, .column = end_location.column }
3292 branches = decl_branch_base(iseq, PTR2NUM(call_node), &code_location, "&.");
3295 PUSH_INSN(ret, location, dup);
3296 PUSH_INSNL(ret, location, branchnil, else_label);
3298 add_trace_branch_coverage(iseq, ret, &code_location, node_id, 0, "then", branches);
3301 int flags = 0;
3302 struct rb_callinfo_kwarg *kw_arg = NULL;
3304 int orig_argc = pm_setup_args(call_node->arguments, call_node->block, &flags, &kw_arg, iseq, ret, scope_node, &location);
3305 const rb_iseq_t *previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
3306 const rb_iseq_t *block_iseq = NULL;
3308 if (call_node->block != NULL && PM_NODE_TYPE_P(call_node->block, PM_BLOCK_NODE)) {
3309 // Scope associated with the block
3310 pm_scope_node_t next_scope_node;
3311 pm_scope_node_init(call_node->block, &next_scope_node, scope_node);
3313 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));
3314 pm_scope_node_destroy(&next_scope_node);
3315 ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
3317 else {
3318 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
3319 flags |= VM_CALL_VCALL;
3322 if (!flags) {
3323 flags |= VM_CALL_ARGS_SIMPLE;
3327 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) {
3328 flags |= VM_CALL_FCALL;
3331 if (!popped && PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE)) {
3332 if (flags & VM_CALL_ARGS_BLOCKARG) {
3333 PUSH_INSN1(ret, location, topn, INT2FIX(1));
3334 if (flags & VM_CALL_ARGS_SPLAT) {
3335 PUSH_INSN1(ret, location, putobject, INT2FIX(-1));
3336 PUSH_SEND_WITH_FLAG(ret, location, idAREF, INT2FIX(1), INT2FIX(0));
3338 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 3));
3339 PUSH_INSN(ret, location, pop);
3341 else if (flags & VM_CALL_ARGS_SPLAT) {
3342 PUSH_INSN(ret, location, dup);
3343 PUSH_INSN1(ret, location, putobject, INT2FIX(-1));
3344 PUSH_SEND_WITH_FLAG(ret, location, idAREF, INT2FIX(1), INT2FIX(0));
3345 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 2));
3346 PUSH_INSN(ret, location, pop);
3348 else {
3349 PUSH_INSN1(ret, location, setn, INT2FIX(orig_argc + 1));
3353 if ((flags & VM_CALL_KW_SPLAT) && (flags & VM_CALL_ARGS_BLOCKARG) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
3354 PUSH_INSN(ret, location, splatkw);
3357 PUSH_SEND_R(ret, location, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg);
3359 if (block_iseq && ISEQ_BODY(block_iseq)->catch_table) {
3360 pm_compile_retry_end_label(iseq, ret, retry_end_l);
3361 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, start, retry_end_l, block_iseq, retry_end_l);
3364 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
3365 PUSH_INSNL(ret, location, jump, end_label);
3366 PUSH_LABEL(ret, else_label);
3367 add_trace_branch_coverage(iseq, ret, &code_location, node_id, 1, "else", branches);
3368 PUSH_LABEL(ret, end_label);
3371 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && !popped) {
3372 PUSH_INSN(ret, location, pop);
3375 if (popped) PUSH_INSN(ret, location, pop);
3376 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
3379 static void
3380 pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition, LABEL **lfinish, bool explicit_receiver)
3382 // in_condition is the same as compile.c's needstr
3383 enum defined_type dtype = DEFINED_NOT_DEFINED;
3384 const pm_line_column_t location = *node_location;
3386 switch (PM_NODE_TYPE(node)) {
3387 case PM_ARGUMENTS_NODE: {
3388 const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node;
3389 const pm_node_list_t *arguments = &cast->arguments;
3390 for (size_t idx = 0; idx < arguments->size; idx++) {
3391 const pm_node_t *argument = arguments->nodes[idx];
3392 pm_compile_defined_expr0(iseq, argument, node_location, ret, popped, scope_node, in_condition, lfinish, explicit_receiver);
3394 if (!lfinish[1]) {
3395 lfinish[1] = NEW_LABEL(location.line);
3397 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
3399 dtype = DEFINED_TRUE;
3400 break;
3402 case PM_NIL_NODE:
3403 dtype = DEFINED_NIL;
3404 break;
3405 case PM_PARENTHESES_NODE: {
3406 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
3408 if (cast->body == NULL) {
3409 // If we have empty parentheses, then we want to return "nil".
3410 dtype = DEFINED_NIL;
3412 else if (PM_NODE_TYPE_P(cast->body, PM_STATEMENTS_NODE) && ((const pm_statements_node_t *) cast->body)->body.size == 1) {
3413 // If we have a parentheses node that is wrapping a single statement
3414 // then we want to recurse down to that statement and compile it.
3415 pm_compile_defined_expr0(iseq, ((const pm_statements_node_t *) cast->body)->body.nodes[0], node_location, ret, popped, scope_node, in_condition, lfinish, explicit_receiver);
3416 return;
3418 else {
3419 // Otherwise, we have parentheses wrapping multiple statements, in
3420 // which case this is defined as "expression".
3421 dtype = DEFINED_EXPR;
3424 break;
3426 case PM_SELF_NODE:
3427 dtype = DEFINED_SELF;
3428 break;
3429 case PM_TRUE_NODE:
3430 dtype = DEFINED_TRUE;
3431 break;
3432 case PM_FALSE_NODE:
3433 dtype = DEFINED_FALSE;
3434 break;
3435 case PM_ARRAY_NODE: {
3436 const pm_array_node_t *cast = (const pm_array_node_t *) node;
3438 if (!PM_NODE_FLAG_P(cast, PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT)) {
3439 for (size_t index = 0; index < cast->elements.size; index++) {
3440 pm_compile_defined_expr0(iseq, cast->elements.nodes[index], node_location, ret, popped, scope_node, true, lfinish, false);
3442 if (!lfinish[1]) {
3443 lfinish[1] = NEW_LABEL(location.line);
3446 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
3450 case PM_AND_NODE:
3451 case PM_BEGIN_NODE:
3452 case PM_BREAK_NODE:
3453 case PM_CASE_NODE:
3454 case PM_CASE_MATCH_NODE:
3455 case PM_CLASS_NODE:
3456 case PM_DEF_NODE:
3457 case PM_DEFINED_NODE:
3458 case PM_FLOAT_NODE:
3459 case PM_FOR_NODE:
3460 case PM_HASH_NODE:
3461 case PM_IF_NODE:
3462 case PM_IMAGINARY_NODE:
3463 case PM_INTEGER_NODE:
3464 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
3465 case PM_INTERPOLATED_STRING_NODE:
3466 case PM_INTERPOLATED_SYMBOL_NODE:
3467 case PM_INTERPOLATED_X_STRING_NODE:
3468 case PM_KEYWORD_HASH_NODE:
3469 case PM_LAMBDA_NODE:
3470 case PM_MATCH_PREDICATE_NODE:
3471 case PM_MATCH_REQUIRED_NODE:
3472 case PM_MATCH_WRITE_NODE:
3473 case PM_MODULE_NODE:
3474 case PM_NEXT_NODE:
3475 case PM_OR_NODE:
3476 case PM_RANGE_NODE:
3477 case PM_RATIONAL_NODE:
3478 case PM_REDO_NODE:
3479 case PM_REGULAR_EXPRESSION_NODE:
3480 case PM_RETRY_NODE:
3481 case PM_RETURN_NODE:
3482 case PM_SINGLETON_CLASS_NODE:
3483 case PM_SOURCE_ENCODING_NODE:
3484 case PM_SOURCE_FILE_NODE:
3485 case PM_SOURCE_LINE_NODE:
3486 case PM_STRING_NODE:
3487 case PM_SYMBOL_NODE:
3488 case PM_UNLESS_NODE:
3489 case PM_UNTIL_NODE:
3490 case PM_WHILE_NODE:
3491 case PM_X_STRING_NODE:
3492 dtype = DEFINED_EXPR;
3493 break;
3494 case PM_LOCAL_VARIABLE_READ_NODE:
3495 dtype = DEFINED_LVAR;
3496 break;
3498 #define PUSH_VAL(type) (in_condition ? Qtrue : rb_iseq_defined_string(type))
3500 case PM_INSTANCE_VARIABLE_READ_NODE: {
3501 const pm_instance_variable_read_node_t *cast = (const pm_instance_variable_read_node_t *) node;
3503 ID name = pm_constant_id_lookup(scope_node, cast->name);
3504 PUSH_INSN3(ret, location, definedivar, ID2SYM(name), get_ivar_ic_value(iseq, name), PUSH_VAL(DEFINED_IVAR));
3506 return;
3508 case PM_BACK_REFERENCE_READ_NODE: {
3509 const char *char_ptr = (const char *) (node->location.start + 1);
3510 ID backref_val = INT2FIX(rb_intern2(char_ptr, 1)) << 1 | 1;
3512 PUSH_INSN(ret, location, putnil);
3513 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_REF), backref_val, PUSH_VAL(DEFINED_GVAR));
3515 return;
3517 case PM_NUMBERED_REFERENCE_READ_NODE: {
3518 uint32_t reference_number = ((const pm_numbered_reference_read_node_t *) node)->number;
3520 PUSH_INSN(ret, location, putnil);
3521 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_REF), INT2FIX(reference_number << 1), PUSH_VAL(DEFINED_GVAR));
3523 return;
3525 case PM_GLOBAL_VARIABLE_READ_NODE: {
3526 const pm_global_variable_read_node_t *cast = (const pm_global_variable_read_node_t *) node;
3527 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
3529 PUSH_INSN(ret, location, putnil);
3530 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_GVAR), name, PUSH_VAL(DEFINED_GVAR));
3532 return;
3534 case PM_CLASS_VARIABLE_READ_NODE: {
3535 const pm_class_variable_read_node_t *cast = (const pm_class_variable_read_node_t *) node;
3536 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
3538 PUSH_INSN(ret, location, putnil);
3539 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CVAR), name, PUSH_VAL(DEFINED_CVAR));
3541 return;
3543 case PM_CONSTANT_READ_NODE: {
3544 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
3545 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
3547 PUSH_INSN(ret, location, putnil);
3548 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), name, PUSH_VAL(DEFINED_CONST));
3550 return;
3552 case PM_CONSTANT_PATH_NODE: {
3553 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
3554 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
3556 if (cast->parent != NULL) {
3557 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
3558 pm_compile_defined_expr0(iseq, cast->parent, node_location, ret, popped, scope_node, true, lfinish, false);
3560 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
3561 PM_COMPILE(cast->parent);
3563 else {
3564 PUSH_INSN1(ret, location, putobject, rb_cObject);
3567 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), name, PUSH_VAL(DEFINED_CONST));
3568 return;
3570 case PM_CALL_NODE: {
3571 const pm_call_node_t *cast = ((const pm_call_node_t *) node);
3572 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
3574 if (cast->receiver || cast->arguments) {
3575 if (!lfinish[1]) lfinish[1] = NEW_LABEL(location.line);
3576 if (!lfinish[2]) lfinish[2] = NEW_LABEL(location.line);
3579 if (cast->arguments) {
3580 pm_compile_defined_expr0(iseq, (const pm_node_t *) cast->arguments, node_location, ret, popped, scope_node, true, lfinish, false);
3581 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
3584 if (cast->receiver) {
3585 pm_compile_defined_expr0(iseq, cast->receiver, node_location, ret, popped, scope_node, true, lfinish, true);
3587 if (PM_NODE_TYPE_P(cast->receiver, PM_CALL_NODE)) {
3588 PUSH_INSNL(ret, location, branchunless, lfinish[2]);
3590 const pm_call_node_t *receiver = (const pm_call_node_t *) cast->receiver;
3591 ID method_id = pm_constant_id_lookup(scope_node, receiver->name);
3592 pm_compile_call(iseq, receiver, ret, popped, scope_node, method_id, NULL);
3594 else {
3595 PUSH_INSNL(ret, location, branchunless, lfinish[1]);
3596 PM_COMPILE(cast->receiver);
3599 if (explicit_receiver) PUSH_INSN(ret, location, dup);
3600 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_METHOD), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
3602 else {
3603 PUSH_INSN(ret, location, putself);
3604 if (explicit_receiver) PUSH_INSN(ret, location, dup);
3605 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_FUNC), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
3608 return;
3610 case PM_YIELD_NODE:
3611 PUSH_INSN(ret, location, putnil);
3612 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_YIELD), 0, PUSH_VAL(DEFINED_YIELD));
3613 return;
3614 case PM_SUPER_NODE:
3615 case PM_FORWARDING_SUPER_NODE:
3616 PUSH_INSN(ret, location, putnil);
3617 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_ZSUPER), 0, PUSH_VAL(DEFINED_ZSUPER));
3618 return;
3619 case PM_CALL_AND_WRITE_NODE:
3620 case PM_CALL_OPERATOR_WRITE_NODE:
3621 case PM_CALL_OR_WRITE_NODE:
3623 case PM_CONSTANT_WRITE_NODE:
3624 case PM_CONSTANT_OPERATOR_WRITE_NODE:
3625 case PM_CONSTANT_AND_WRITE_NODE:
3626 case PM_CONSTANT_OR_WRITE_NODE:
3628 case PM_CONSTANT_PATH_AND_WRITE_NODE:
3629 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE:
3630 case PM_CONSTANT_PATH_OR_WRITE_NODE:
3631 case PM_CONSTANT_PATH_WRITE_NODE:
3633 case PM_GLOBAL_VARIABLE_WRITE_NODE:
3634 case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE:
3635 case PM_GLOBAL_VARIABLE_AND_WRITE_NODE:
3636 case PM_GLOBAL_VARIABLE_OR_WRITE_NODE:
3638 case PM_CLASS_VARIABLE_WRITE_NODE:
3639 case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE:
3640 case PM_CLASS_VARIABLE_AND_WRITE_NODE:
3641 case PM_CLASS_VARIABLE_OR_WRITE_NODE:
3643 case PM_INDEX_AND_WRITE_NODE:
3644 case PM_INDEX_OPERATOR_WRITE_NODE:
3645 case PM_INDEX_OR_WRITE_NODE:
3647 case PM_INSTANCE_VARIABLE_WRITE_NODE:
3648 case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE:
3649 case PM_INSTANCE_VARIABLE_AND_WRITE_NODE:
3650 case PM_INSTANCE_VARIABLE_OR_WRITE_NODE:
3652 case PM_LOCAL_VARIABLE_WRITE_NODE:
3653 case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE:
3654 case PM_LOCAL_VARIABLE_AND_WRITE_NODE:
3655 case PM_LOCAL_VARIABLE_OR_WRITE_NODE:
3657 case PM_MULTI_WRITE_NODE:
3658 dtype = DEFINED_ASGN;
3659 break;
3660 default:
3661 rb_bug("Unsupported node %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
3664 RUBY_ASSERT(dtype != DEFINED_NOT_DEFINED);
3665 PUSH_INSN1(ret, location, putobject, PUSH_VAL(dtype));
3666 #undef PUSH_VAL
3669 static void
3670 pm_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition, LABEL **lfinish, bool explicit_receiver)
3672 LINK_ELEMENT *lcur = ret->last;
3673 pm_compile_defined_expr0(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish, false);
3675 if (lfinish[1]) {
3676 LABEL *lstart = NEW_LABEL(node_location->line);
3677 LABEL *lend = NEW_LABEL(node_location->line);
3679 struct rb_iseq_new_with_callback_callback_func *ifunc =
3680 rb_iseq_new_with_callback_new_callback(build_defined_rescue_iseq, NULL);
3682 const rb_iseq_t *rescue = new_child_iseq_with_callback(
3683 iseq,
3684 ifunc,
3685 rb_str_concat(rb_str_new2("defined guard in "), ISEQ_BODY(iseq)->location.label),
3686 iseq,
3687 ISEQ_TYPE_RESCUE,
3691 lstart->rescued = LABEL_RESCUE_BEG;
3692 lend->rescued = LABEL_RESCUE_END;
3694 APPEND_LABEL(ret, lcur, lstart);
3695 PUSH_LABEL(ret, lend);
3696 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
3700 static void
3701 pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, bool in_condition)
3703 LABEL *lfinish[3];
3704 LINK_ELEMENT *last = ret->last;
3706 lfinish[0] = NEW_LABEL(node_location->line);
3707 lfinish[1] = 0;
3708 lfinish[2] = 0;
3710 if (!popped) {
3711 pm_defined_expr(iseq, node, node_location, ret, popped, scope_node, in_condition, lfinish, false);
3714 if (lfinish[1]) {
3715 ELEM_INSERT_NEXT(last, &new_insn_body(iseq, node_location->line, node_location->column, BIN(putnil), 0)->link);
3716 PUSH_INSN(ret, *node_location, swap);
3718 if (lfinish[2]) PUSH_LABEL(ret, lfinish[2]);
3719 PUSH_INSN(ret, *node_location, pop);
3720 PUSH_LABEL(ret, lfinish[1]);
3724 PUSH_LABEL(ret, lfinish[0]);
3727 // This is exactly the same as add_ensure_iseq, except it compiled
3728 // the node as a Prism node, and not a CRuby node
3729 static void
3730 pm_add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return, pm_scope_node_t *scope_node)
3732 RUBY_ASSERT(can_add_ensure_iseq(iseq));
3734 struct iseq_compile_data_ensure_node_stack *enlp =
3735 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
3736 struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
3737 DECL_ANCHOR(ensure);
3739 INIT_ANCHOR(ensure);
3740 while (enlp) {
3741 if (enlp->erange != NULL) {
3742 DECL_ANCHOR(ensure_part);
3743 LABEL *lstart = NEW_LABEL(0);
3744 LABEL *lend = NEW_LABEL(0);
3745 INIT_ANCHOR(ensure_part);
3747 add_ensure_range(iseq, enlp->erange, lstart, lend);
3749 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
3750 PUSH_LABEL(ensure_part, lstart);
3751 bool popped = true;
3752 PM_COMPILE_INTO_ANCHOR(ensure_part, (const pm_node_t *) enlp->ensure_node);
3753 PUSH_LABEL(ensure_part, lend);
3754 PUSH_SEQ(ensure, ensure_part);
3756 else {
3757 if (!is_return) {
3758 break;
3761 enlp = enlp->prev;
3763 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = prev_enlp;
3764 PUSH_SEQ(ret, ensure);
3767 struct pm_local_table_insert_ctx {
3768 pm_scope_node_t *scope_node;
3769 rb_ast_id_table_t *local_table_for_iseq;
3770 int local_index;
3773 static int
3774 pm_local_table_insert_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
3776 if (!existing) {
3777 pm_constant_id_t constant_id = (pm_constant_id_t) *key;
3778 struct pm_local_table_insert_ctx * ctx = (struct pm_local_table_insert_ctx *) arg;
3780 pm_scope_node_t *scope_node = ctx->scope_node;
3781 rb_ast_id_table_t *local_table_for_iseq = ctx->local_table_for_iseq;
3782 int local_index = ctx->local_index;
3784 ID local = pm_constant_id_lookup(scope_node, constant_id);
3785 local_table_for_iseq->ids[local_index] = local;
3787 *value = (st_data_t)local_index;
3789 ctx->local_index++;
3792 return ST_CONTINUE;
3796 * Insert a local into the local table for the iseq. This is used to create the
3797 * local table in the correct order while compiling the scope. The locals being
3798 * inserted are regular named locals, as opposed to special forwarding locals.
3800 static void
3801 pm_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)
3803 RUBY_ASSERT((constant_id & PM_SPECIAL_CONSTANT_FLAG) == 0);
3805 ID local = pm_constant_id_lookup(scope_node, constant_id);
3806 local_table_for_iseq->ids[local_index] = local;
3807 st_insert(index_lookup_table, (st_data_t) constant_id, (st_data_t) local_index);
3811 * Insert a local into the local table for the iseq that is a special forwarding
3812 * local variable.
3814 static void
3815 pm_insert_local_special(ID local_name, int local_index, st_table *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq)
3817 local_table_for_iseq->ids[local_index] = local_name;
3818 st_insert(index_lookup_table, (st_data_t) (local_name | PM_SPECIAL_CONSTANT_FLAG), (st_data_t) local_index);
3822 * Compile the locals of a multi target node that is used as a positional
3823 * parameter in a method, block, or lambda definition. Note that this doesn't
3824 * actually add any instructions to the iseq. Instead, it adds locals to the
3825 * local and index lookup tables and increments the local index as necessary.
3827 static int
3828 pm_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)
3830 for (size_t index = 0; index < node->lefts.size; index++) {
3831 const pm_node_t *left = node->lefts.nodes[index];
3833 if (PM_NODE_TYPE_P(left, PM_REQUIRED_PARAMETER_NODE)) {
3834 if (!PM_NODE_FLAG_P(left, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
3835 pm_insert_local_index(((const pm_required_parameter_node_t *) left)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
3836 local_index++;
3839 else {
3840 RUBY_ASSERT(PM_NODE_TYPE_P(left, PM_MULTI_TARGET_NODE));
3841 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);
3845 if (node->rest != NULL && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE)) {
3846 const pm_splat_node_t *rest = (const pm_splat_node_t *) node->rest;
3848 if (rest->expression != NULL) {
3849 RUBY_ASSERT(PM_NODE_TYPE_P(rest->expression, PM_REQUIRED_PARAMETER_NODE));
3851 if (!PM_NODE_FLAG_P(rest->expression, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
3852 pm_insert_local_index(((const pm_required_parameter_node_t *) rest->expression)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
3853 local_index++;
3858 for (size_t index = 0; index < node->rights.size; index++) {
3859 const pm_node_t *right = node->rights.nodes[index];
3861 if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) {
3862 if (!PM_NODE_FLAG_P(right, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
3863 pm_insert_local_index(((const pm_required_parameter_node_t *) right)->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
3864 local_index++;
3867 else {
3868 RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE));
3869 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);
3873 return local_index;
3877 * Compile a required parameter node that is part of a destructure that is used
3878 * as a positional parameter in a method, block, or lambda definition.
3880 static inline void
3881 pm_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)
3883 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
3884 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, node->name, 0);
3885 PUSH_SETLOCAL(ret, location, index.index, index.level);
3889 * Compile a multi target node that is used as a positional parameter in a
3890 * method, block, or lambda definition. Note that this is effectively the same
3891 * as a multi write, but with the added context that all of the targets
3892 * contained in the write are required parameter nodes. With this context, we
3893 * know they won't have any parent expressions so we build a separate code path
3894 * for this simplified case.
3896 static void
3897 pm_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)
3899 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
3900 bool has_rest = (node->rest && PM_NODE_TYPE_P(node->rest, PM_SPLAT_NODE) && (((const pm_splat_node_t *) node->rest)->expression) != NULL);
3901 bool has_rights = node->rights.size > 0;
3903 int flag = (has_rest || has_rights) ? 1 : 0;
3904 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->lefts.size), INT2FIX(flag));
3906 for (size_t index = 0; index < node->lefts.size; index++) {
3907 const pm_node_t *left = node->lefts.nodes[index];
3909 if (PM_NODE_TYPE_P(left, PM_REQUIRED_PARAMETER_NODE)) {
3910 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) left, ret, scope_node);
3912 else {
3913 RUBY_ASSERT(PM_NODE_TYPE_P(left, PM_MULTI_TARGET_NODE));
3914 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) left, ret, scope_node);
3918 if (has_rest) {
3919 if (has_rights) {
3920 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->rights.size), INT2FIX(3));
3923 const pm_node_t *rest = ((const pm_splat_node_t *) node->rest)->expression;
3924 RUBY_ASSERT(PM_NODE_TYPE_P(rest, PM_REQUIRED_PARAMETER_NODE));
3926 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) rest, ret, scope_node);
3929 if (has_rights) {
3930 if (!has_rest) {
3931 PUSH_INSN2(ret, location, expandarray, INT2FIX(node->rights.size), INT2FIX(2));
3934 for (size_t index = 0; index < node->rights.size; index++) {
3935 const pm_node_t *right = node->rights.nodes[index];
3937 if (PM_NODE_TYPE_P(right, PM_REQUIRED_PARAMETER_NODE)) {
3938 pm_compile_destructured_param_write(iseq, (const pm_required_parameter_node_t *) right, ret, scope_node);
3940 else {
3941 RUBY_ASSERT(PM_NODE_TYPE_P(right, PM_MULTI_TARGET_NODE));
3942 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) right, ret, scope_node);
3949 * This is a node in the multi target state linked list. It tracks the
3950 * information for a particular target that necessarily has a parent expression.
3952 typedef struct pm_multi_target_state_node {
3953 // The pointer to the topn instruction that will need to be modified after
3954 // we know the total stack size of all of the targets.
3955 INSN *topn;
3957 // The index of the stack from the base of the entire multi target at which
3958 // the parent expression is located.
3959 size_t stack_index;
3961 // The number of slots in the stack that this node occupies.
3962 size_t stack_size;
3964 // The position of the node in the list of targets.
3965 size_t position;
3967 // A pointer to the next node in this linked list.
3968 struct pm_multi_target_state_node *next;
3969 } pm_multi_target_state_node_t;
3972 * As we're compiling a multi target, we need to track additional information
3973 * whenever there is a parent expression on the left hand side of the target.
3974 * This is because we need to go back and tell the expression where to fetch its
3975 * parent expression from the stack. We use a linked list of nodes to track this
3976 * information.
3978 typedef struct {
3979 // The total number of slots in the stack that this multi target occupies.
3980 size_t stack_size;
3982 // The position of the current node being compiled. This is forwarded to
3983 // nodes when they are allocated.
3984 size_t position;
3986 // A pointer to the head of this linked list.
3987 pm_multi_target_state_node_t *head;
3989 // A pointer to the tail of this linked list.
3990 pm_multi_target_state_node_t *tail;
3991 } pm_multi_target_state_t;
3994 * Push a new state node onto the multi target state.
3996 static void
3997 pm_multi_target_state_push(pm_multi_target_state_t *state, INSN *topn, size_t stack_size)
3999 pm_multi_target_state_node_t *node = ALLOC(pm_multi_target_state_node_t);
4000 node->topn = topn;
4001 node->stack_index = state->stack_size + 1;
4002 node->stack_size = stack_size;
4003 node->position = state->position;
4004 node->next = NULL;
4006 if (state->head == NULL) {
4007 state->head = node;
4008 state->tail = node;
4010 else {
4011 state->tail->next = node;
4012 state->tail = node;
4015 state->stack_size += stack_size;
4019 * Walk through a multi target state's linked list and update the topn
4020 * instructions that were inserted into the write sequence to make sure they can
4021 * correctly retrieve their parent expressions.
4023 static void
4024 pm_multi_target_state_update(pm_multi_target_state_t *state)
4026 // If nothing was ever pushed onto the stack, then we don't need to do any
4027 // kind of updates.
4028 if (state->stack_size == 0) return;
4030 pm_multi_target_state_node_t *current = state->head;
4031 pm_multi_target_state_node_t *previous;
4033 while (current != NULL) {
4034 VALUE offset = INT2FIX(state->stack_size - current->stack_index + current->position);
4035 current->topn->operands[0] = offset;
4037 // stack_size will be > 1 in the case that we compiled an index target
4038 // and it had arguments. In this case, we use multiple topn instructions
4039 // to grab up all of the arguments as well, so those offsets need to be
4040 // updated as well.
4041 if (current->stack_size > 1) {
4042 INSN *insn = current->topn;
4044 for (size_t index = 1; index < current->stack_size; index += 1) {
4045 LINK_ELEMENT *element = get_next_insn(insn);
4046 RUBY_ASSERT(IS_INSN(element));
4048 insn = (INSN *) element;
4049 RUBY_ASSERT(insn->insn_id == BIN(topn));
4051 insn->operands[0] = offset;
4055 previous = current;
4056 current = current->next;
4058 xfree(previous);
4062 static void
4063 pm_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);
4066 * A target node represents an indirect write to a variable or a method call to
4067 * a method ending in =. Compiling one of these nodes requires three sequences:
4069 * * The first is to compile retrieving the parent expression if there is one.
4070 * This could be the object that owns a constant or the receiver of a method
4071 * call.
4072 * * The second is to compile the writes to the targets. This could be writing
4073 * to variables, or it could be performing method calls.
4074 * * The third is to compile any cleanup that needs to happen, i.e., popping the
4075 * appropriate number of values off the stack.
4077 * When there is a parent expression and this target is part of a multi write, a
4078 * topn instruction will be inserted into the write sequence. This is to move
4079 * the parent expression to the top of the stack so that it can be used as the
4080 * receiver of the method call or the owner of the constant. To facilitate this,
4081 * we return a pointer to the topn instruction that was used to be later
4082 * modified with the correct offset.
4084 * These nodes can appear in a couple of places, but most commonly:
4086 * * For loops - the index variable is a target node
4087 * * Rescue clauses - the exception reference variable is a target node
4088 * * Multi writes - the left hand side contains a list of target nodes
4090 * For the comments with examples within this function, we'll use for loops as
4091 * the containing node.
4093 static void
4094 pm_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)
4096 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
4098 switch (PM_NODE_TYPE(node)) {
4099 case PM_LOCAL_VARIABLE_TARGET_NODE: {
4100 // Local variable targets have no parent expression, so they only need
4101 // to compile the write.
4103 // for i in []; end
4105 const pm_local_variable_target_node_t *cast = (const pm_local_variable_target_node_t *) node;
4106 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
4108 PUSH_SETLOCAL(writes, location, index.index, index.level);
4109 break;
4111 case PM_CLASS_VARIABLE_TARGET_NODE: {
4112 // Class variable targets have no parent expression, so they only need
4113 // to compile the write.
4115 // for @@i in []; end
4117 const pm_class_variable_target_node_t *cast = (const pm_class_variable_target_node_t *) node;
4118 ID name = pm_constant_id_lookup(scope_node, cast->name);
4120 PUSH_INSN2(writes, location, setclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
4121 break;
4123 case PM_CONSTANT_TARGET_NODE: {
4124 // Constant targets have no parent expression, so they only need to
4125 // compile the write.
4127 // for I in []; end
4129 const pm_constant_target_node_t *cast = (const pm_constant_target_node_t *) node;
4130 ID name = pm_constant_id_lookup(scope_node, cast->name);
4132 PUSH_INSN1(writes, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
4133 PUSH_INSN1(writes, location, setconstant, ID2SYM(name));
4134 break;
4136 case PM_GLOBAL_VARIABLE_TARGET_NODE: {
4137 // Global variable targets have no parent expression, so they only need
4138 // to compile the write.
4140 // for $i in []; end
4142 const pm_global_variable_target_node_t *cast = (const pm_global_variable_target_node_t *) node;
4143 ID name = pm_constant_id_lookup(scope_node, cast->name);
4145 PUSH_INSN1(writes, location, setglobal, ID2SYM(name));
4146 break;
4148 case PM_INSTANCE_VARIABLE_TARGET_NODE: {
4149 // Instance variable targets have no parent expression, so they only
4150 // need to compile the write.
4152 // for @i in []; end
4154 const pm_instance_variable_target_node_t *cast = (const pm_instance_variable_target_node_t *) node;
4155 ID name = pm_constant_id_lookup(scope_node, cast->name);
4157 PUSH_INSN2(writes, location, setinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
4158 break;
4160 case PM_CONSTANT_PATH_TARGET_NODE: {
4161 // Constant path targets have a parent expression that is the object
4162 // that owns the constant. This needs to be compiled first into the
4163 // parents sequence. If no parent is found, then it represents using the
4164 // unary :: operator to indicate a top-level constant. In that case we
4165 // need to push Object onto the stack.
4167 // for I::J in []; end
4169 const pm_constant_path_target_node_t *cast = (const pm_constant_path_target_node_t *) node;
4170 ID name = pm_constant_id_lookup(scope_node, cast->name);
4172 if (cast->parent != NULL) {
4173 pm_compile_node(iseq, cast->parent, parents, false, scope_node);
4175 else {
4176 PUSH_INSN1(parents, location, putobject, rb_cObject);
4179 if (state == NULL) {
4180 PUSH_INSN(writes, location, swap);
4182 else {
4183 PUSH_INSN1(writes, location, topn, INT2FIX(1));
4184 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), 1);
4187 PUSH_INSN1(writes, location, setconstant, ID2SYM(name));
4189 if (state != NULL) {
4190 PUSH_INSN(cleanup, location, pop);
4193 break;
4195 case PM_CALL_TARGET_NODE: {
4196 // Call targets have a parent expression that is the receiver of the
4197 // method being called. This needs to be compiled first into the parents
4198 // sequence. These nodes cannot have arguments, so the method call is
4199 // compiled with a single argument which represents the value being
4200 // written.
4202 // for i.j in []; end
4204 const pm_call_target_node_t *cast = (const pm_call_target_node_t *) node;
4205 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
4207 pm_compile_node(iseq, cast->receiver, parents, false, scope_node);
4209 LABEL *safe_label = NULL;
4210 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
4211 safe_label = NEW_LABEL(location.line);
4212 PUSH_INSN(parents, location, dup);
4213 PUSH_INSNL(parents, location, branchnil, safe_label);
4216 if (state != NULL) {
4217 PUSH_INSN1(writes, location, topn, INT2FIX(1));
4218 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), 1);
4219 PUSH_INSN(writes, location, swap);
4222 int flags = VM_CALL_ARGS_SIMPLE;
4223 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) flags |= VM_CALL_FCALL;
4225 PUSH_SEND_WITH_FLAG(writes, location, method_id, INT2FIX(1), INT2FIX(flags));
4226 if (safe_label != NULL && state == NULL) PUSH_LABEL(writes, safe_label);
4227 PUSH_INSN(writes, location, pop);
4228 if (safe_label != NULL && state != NULL) PUSH_LABEL(writes, safe_label);
4230 if (state != NULL) {
4231 PUSH_INSN(cleanup, location, pop);
4234 break;
4236 case PM_INDEX_TARGET_NODE: {
4237 // Index targets have a parent expression that is the receiver of the
4238 // method being called and any additional arguments that are being
4239 // passed along with the value being written. The receiver and arguments
4240 // both need to be on the stack. Note that this is even more complicated
4241 // by the fact that these nodes can hold a block using the unary &
4242 // operator.
4244 // for i[:j] in []; end
4246 const pm_index_target_node_t *cast = (const pm_index_target_node_t *) node;
4248 pm_compile_node(iseq, cast->receiver, parents, false, scope_node);
4250 int flags = 0;
4251 struct rb_callinfo_kwarg *kwargs = NULL;
4252 int argc = pm_setup_args(cast->arguments, cast->block, &flags, &kwargs, iseq, parents, scope_node, &location);
4254 if (state != NULL) {
4255 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
4256 pm_multi_target_state_push(state, (INSN *) LAST_ELEMENT(writes), argc + 1);
4258 if (argc == 0) {
4259 PUSH_INSN(writes, location, swap);
4261 else {
4262 for (int index = 0; index < argc; index++) {
4263 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
4265 PUSH_INSN1(writes, location, topn, INT2FIX(argc + 1));
4269 // The argc that we're going to pass to the send instruction is the
4270 // number of arguments + 1 for the value being written. If there's a
4271 // splat, then we need to insert newarray and concatarray instructions
4272 // after the arguments have been written.
4273 int ci_argc = argc + 1;
4274 if (flags & VM_CALL_ARGS_SPLAT) {
4275 ci_argc--;
4276 PUSH_INSN1(writes, location, newarray, INT2FIX(1));
4277 PUSH_INSN(writes, location, concatarray);
4280 PUSH_SEND_R(writes, location, idASET, INT2NUM(ci_argc), NULL, INT2FIX(flags), kwargs);
4281 PUSH_INSN(writes, location, pop);
4283 if (state != NULL) {
4284 if (argc != 0) {
4285 PUSH_INSN(writes, location, pop);
4288 for (int index = 0; index < argc + 1; index++) {
4289 PUSH_INSN(cleanup, location, pop);
4293 break;
4295 case PM_MULTI_TARGET_NODE: {
4296 // Multi target nodes represent a set of writes to multiple variables.
4297 // The parent expressions are the combined set of the parent expressions
4298 // of its inner target nodes.
4300 // for i, j in []; end
4302 size_t before_position;
4303 if (state != NULL) {
4304 before_position = state->position;
4305 state->position--;
4308 pm_compile_multi_target_node(iseq, node, parents, writes, cleanup, scope_node, state);
4309 if (state != NULL) state->position = before_position;
4311 break;
4313 default:
4314 rb_bug("Unexpected node type: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
4315 break;
4320 * Compile a multi target or multi write node. It returns the number of values
4321 * on the stack that correspond to the parent expressions of the various
4322 * targets.
4324 static void
4325 pm_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)
4327 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
4328 const pm_node_list_t *lefts;
4329 const pm_node_t *rest;
4330 const pm_node_list_t *rights;
4332 switch (PM_NODE_TYPE(node)) {
4333 case PM_MULTI_TARGET_NODE: {
4334 const pm_multi_target_node_t *cast = (const pm_multi_target_node_t *) node;
4335 lefts = &cast->lefts;
4336 rest = cast->rest;
4337 rights = &cast->rights;
4338 break;
4340 case PM_MULTI_WRITE_NODE: {
4341 const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node;
4342 lefts = &cast->lefts;
4343 rest = cast->rest;
4344 rights = &cast->rights;
4345 break;
4347 default:
4348 rb_bug("Unsupported node %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
4349 break;
4352 bool has_rest = (rest != NULL) && PM_NODE_TYPE_P(rest, PM_SPLAT_NODE) && ((const pm_splat_node_t *) rest)->expression != NULL;
4353 bool has_posts = rights->size > 0;
4355 // The first instruction in the writes sequence is going to spread the
4356 // top value of the stack onto the number of values that we're going to
4357 // write.
4358 PUSH_INSN2(writes, location, expandarray, INT2FIX(lefts->size), INT2FIX((has_rest || has_posts) ? 1 : 0));
4360 // We need to keep track of some additional state information as we're
4361 // going through the targets because we will need to revisit them once
4362 // we know how many values are being pushed onto the stack.
4363 pm_multi_target_state_t target_state = { 0 };
4364 if (state == NULL) state = &target_state;
4366 size_t base_position = state->position;
4367 size_t splat_position = (has_rest || has_posts) ? 1 : 0;
4369 // Next, we'll iterate through all of the leading targets.
4370 for (size_t index = 0; index < lefts->size; index++) {
4371 const pm_node_t *target = lefts->nodes[index];
4372 state->position = lefts->size - index + splat_position + base_position;
4373 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
4376 // Next, we'll compile the rest target if there is one.
4377 if (has_rest) {
4378 const pm_node_t *target = ((const pm_splat_node_t *) rest)->expression;
4379 state->position = 1 + rights->size + base_position;
4381 if (has_posts) {
4382 PUSH_INSN2(writes, location, expandarray, INT2FIX(rights->size), INT2FIX(3));
4385 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
4388 // Finally, we'll compile the trailing targets.
4389 if (has_posts) {
4390 if (!has_rest && rest != NULL) {
4391 PUSH_INSN2(writes, location, expandarray, INT2FIX(rights->size), INT2FIX(2));
4394 for (size_t index = 0; index < rights->size; index++) {
4395 const pm_node_t *target = rights->nodes[index];
4396 state->position = rights->size - index + base_position;
4397 pm_compile_target_node(iseq, target, parents, writes, cleanup, scope_node, state);
4403 * When compiling a for loop, we need to write the iteration variable to
4404 * whatever expression exists in the index slot. This function performs that
4405 * compilation.
4407 static void
4408 pm_compile_for_node_index(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node)
4410 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
4412 switch (PM_NODE_TYPE(node)) {
4413 case PM_LOCAL_VARIABLE_TARGET_NODE: {
4414 // For local variables, all we have to do is retrieve the value and then
4415 // compile the index node.
4416 PUSH_GETLOCAL(ret, location, 1, 0);
4417 pm_compile_target_node(iseq, node, ret, ret, ret, scope_node, NULL);
4418 break;
4420 case PM_CLASS_VARIABLE_TARGET_NODE:
4421 case PM_CONSTANT_TARGET_NODE:
4422 case PM_GLOBAL_VARIABLE_TARGET_NODE:
4423 case PM_INSTANCE_VARIABLE_TARGET_NODE:
4424 case PM_CONSTANT_PATH_TARGET_NODE:
4425 case PM_CALL_TARGET_NODE:
4426 case PM_INDEX_TARGET_NODE: {
4427 // For other targets, we need to potentially compile the parent or
4428 // owning expression of this target, then retrieve the value, expand it,
4429 // and then compile the necessary writes.
4430 DECL_ANCHOR(writes);
4431 INIT_ANCHOR(writes);
4433 DECL_ANCHOR(cleanup);
4434 INIT_ANCHOR(cleanup);
4436 pm_multi_target_state_t state = { 0 };
4437 state.position = 1;
4438 pm_compile_target_node(iseq, node, ret, writes, cleanup, scope_node, &state);
4440 PUSH_GETLOCAL(ret, location, 1, 0);
4441 PUSH_INSN2(ret, location, expandarray, INT2FIX(1), INT2FIX(0));
4443 PUSH_SEQ(ret, writes);
4444 PUSH_SEQ(ret, cleanup);
4446 pm_multi_target_state_update(&state);
4447 break;
4449 case PM_MULTI_TARGET_NODE: {
4450 DECL_ANCHOR(writes);
4451 INIT_ANCHOR(writes);
4453 DECL_ANCHOR(cleanup);
4454 INIT_ANCHOR(cleanup);
4456 pm_compile_target_node(iseq, node, ret, writes, cleanup, scope_node, NULL);
4458 LABEL *not_single = NEW_LABEL(location.line);
4459 LABEL *not_ary = NEW_LABEL(location.line);
4461 // When there are multiple targets, we'll do a bunch of work to convert
4462 // the value into an array before we expand it. Effectively we're trying
4463 // to accomplish:
4465 // (args.length == 1 && Array.try_convert(args[0])) || args
4467 PUSH_GETLOCAL(ret, location, 1, 0);
4468 PUSH_INSN(ret, location, dup);
4469 PUSH_CALL(ret, location, idLength, INT2FIX(0));
4470 PUSH_INSN1(ret, location, putobject, INT2FIX(1));
4471 PUSH_CALL(ret, location, idEq, INT2FIX(1));
4472 PUSH_INSNL(ret, location, branchunless, not_single);
4473 PUSH_INSN(ret, location, dup);
4474 PUSH_INSN1(ret, location, putobject, INT2FIX(0));
4475 PUSH_CALL(ret, location, idAREF, INT2FIX(1));
4476 PUSH_INSN1(ret, location, putobject, rb_cArray);
4477 PUSH_INSN(ret, location, swap);
4478 PUSH_CALL(ret, location, rb_intern("try_convert"), INT2FIX(1));
4479 PUSH_INSN(ret, location, dup);
4480 PUSH_INSNL(ret, location, branchunless, not_ary);
4481 PUSH_INSN(ret, location, swap);
4483 PUSH_LABEL(ret, not_ary);
4484 PUSH_INSN(ret, location, pop);
4486 PUSH_LABEL(ret, not_single);
4487 PUSH_SEQ(ret, writes);
4488 PUSH_SEQ(ret, cleanup);
4489 break;
4491 default:
4492 rb_bug("Unexpected node type for index in for node: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
4493 break;
4497 static void
4498 pm_compile_rescue(rb_iseq_t *iseq, const pm_begin_node_t *cast, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
4500 const pm_parser_t *parser = scope_node->parser;
4502 LABEL *lstart = NEW_LABEL(node_location->line);
4503 LABEL *lend = NEW_LABEL(node_location->line);
4504 LABEL *lcont = NEW_LABEL(node_location->line);
4506 pm_scope_node_t rescue_scope_node;
4507 pm_scope_node_init((const pm_node_t *) cast->rescue_clause, &rescue_scope_node, scope_node);
4509 rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(
4510 &rescue_scope_node,
4511 rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label),
4512 ISEQ_TYPE_RESCUE,
4513 pm_node_line_number(parser, (const pm_node_t *) cast->rescue_clause)
4516 pm_scope_node_destroy(&rescue_scope_node);
4518 lstart->rescued = LABEL_RESCUE_BEG;
4519 lend->rescued = LABEL_RESCUE_END;
4520 PUSH_LABEL(ret, lstart);
4522 bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
4523 ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
4525 if (cast->statements != NULL) {
4526 PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->statements);
4528 else {
4529 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
4532 ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
4533 PUSH_LABEL(ret, lend);
4535 if (cast->else_clause != NULL) {
4536 if (!popped) PUSH_INSN(ret, *node_location, pop);
4537 PM_COMPILE((const pm_node_t *) cast->else_clause);
4540 PUSH_INSN(ret, *node_location, nop);
4541 PUSH_LABEL(ret, lcont);
4543 if (popped) PUSH_INSN(ret, *node_location, pop);
4544 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
4545 PUSH_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
4548 static void
4549 pm_compile_ensure(rb_iseq_t *iseq, const pm_begin_node_t *cast, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
4551 const pm_parser_t *parser = scope_node->parser;
4552 const pm_statements_node_t *statements = cast->ensure_clause->statements;
4553 const pm_line_column_t location = statements != NULL ? PM_NODE_START_LINE_COLUMN(parser, statements) : *node_location;
4555 LABEL *estart = NEW_LABEL(location.line);
4556 LABEL *eend = NEW_LABEL(location.line);
4557 LABEL *econt = NEW_LABEL(location.line);
4559 struct ensure_range er;
4560 struct iseq_compile_data_ensure_node_stack enl;
4561 struct ensure_range *erange;
4563 er.begin = estart;
4564 er.end = eend;
4565 er.next = 0;
4566 push_ensure_entry(iseq, &enl, &er, (void *) cast->ensure_clause);
4568 PUSH_LABEL(ret, estart);
4569 if (cast->rescue_clause) {
4570 pm_compile_rescue(iseq, cast, &location, ret, popped, scope_node);
4572 else {
4573 if (cast->statements) {
4574 PM_COMPILE((const pm_node_t *) cast->statements);
4576 else if (!popped) {
4577 PUSH_INSN(ret, *node_location, putnil);
4581 PUSH_LABEL(ret, eend);
4582 PUSH_LABEL(ret, econt);
4584 pm_scope_node_t next_scope_node;
4585 pm_scope_node_init((const pm_node_t *) cast->ensure_clause, &next_scope_node, scope_node);
4587 rb_iseq_t *child_iseq = NEW_CHILD_ISEQ(
4588 &next_scope_node,
4589 rb_str_concat(rb_str_new2("ensure in "), ISEQ_BODY(iseq)->location.label),
4590 ISEQ_TYPE_ENSURE,
4591 location.line
4594 pm_scope_node_destroy(&next_scope_node);
4596 erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
4597 if (estart->link.next != &eend->link) {
4598 while (erange) {
4599 PUSH_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end, child_iseq, econt);
4600 erange = erange->next;
4603 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
4605 // Compile the ensure entry
4606 if (statements != NULL) {
4607 PM_COMPILE((const pm_node_t *) statements);
4608 if (!popped) PUSH_INSN(ret, *node_location, pop);
4613 * Returns true if the given call node can use the opt_str_uminus or
4614 * opt_str_freeze instructions as an optimization with the current iseq options.
4616 static inline bool
4617 pm_opt_str_freeze_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
4619 return (
4620 !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) &&
4621 node->receiver != NULL &&
4622 PM_NODE_TYPE_P(node->receiver, PM_STRING_NODE) &&
4623 node->arguments == NULL &&
4624 node->block == NULL &&
4625 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
4630 * Returns true if the given call node can use the opt_aref_with optimization
4631 * with the current iseq options.
4633 static inline bool
4634 pm_opt_aref_with_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
4636 return (
4637 !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) &&
4638 node->arguments != NULL &&
4639 PM_NODE_TYPE_P((const pm_node_t *) node->arguments, PM_ARGUMENTS_NODE) &&
4640 ((const pm_arguments_node_t *) node->arguments)->arguments.size == 1 &&
4641 PM_NODE_TYPE_P(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0], PM_STRING_NODE) &&
4642 node->block == NULL &&
4643 !PM_NODE_FLAG_P(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0], PM_STRING_FLAGS_FROZEN) &&
4644 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
4649 * Returns true if the given call node can use the opt_aset_with optimization
4650 * with the current iseq options.
4652 static inline bool
4653 pm_opt_aset_with_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
4655 return (
4656 !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION) &&
4657 node->arguments != NULL &&
4658 PM_NODE_TYPE_P((const pm_node_t *) node->arguments, PM_ARGUMENTS_NODE) &&
4659 ((const pm_arguments_node_t *) node->arguments)->arguments.size == 2 &&
4660 PM_NODE_TYPE_P(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0], PM_STRING_NODE) &&
4661 node->block == NULL &&
4662 !PM_NODE_FLAG_P(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0], PM_STRING_FLAGS_FROZEN) &&
4663 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
4668 * Compile the instructions necessary to read a constant, based on the options
4669 * of the current iseq.
4671 static void
4672 pm_compile_constant_read(rb_iseq_t *iseq, VALUE name, const pm_location_t *name_loc, LINK_ANCHOR *const ret, const pm_scope_node_t *scope_node)
4674 const pm_line_column_t location = PM_LOCATION_START_LINE_COLUMN(scope_node->parser, name_loc);
4676 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
4677 ISEQ_BODY(iseq)->ic_size++;
4678 VALUE segments = rb_ary_new_from_args(1, name);
4679 PUSH_INSN1(ret, location, opt_getconstant_path, segments);
4681 else {
4682 PUSH_INSN(ret, location, putnil);
4683 PUSH_INSN1(ret, location, putobject, Qtrue);
4684 PUSH_INSN1(ret, location, getconstant, name);
4689 * Returns a Ruby array of the parts of the constant path node if it is constant
4690 * reads all of the way down. If it isn't, then Qnil is returned.
4692 static VALUE
4693 pm_constant_path_parts(const pm_node_t *node, const pm_scope_node_t *scope_node)
4695 VALUE parts = rb_ary_new();
4697 while (true) {
4698 switch (PM_NODE_TYPE(node)) {
4699 case PM_CONSTANT_READ_NODE: {
4700 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
4701 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
4703 rb_ary_unshift(parts, name);
4704 return parts;
4706 case PM_CONSTANT_PATH_NODE: {
4707 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
4708 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
4710 rb_ary_unshift(parts, name);
4711 if (cast->parent == NULL) {
4712 rb_ary_unshift(parts, ID2SYM(idNULL));
4713 return parts;
4716 node = cast->parent;
4717 break;
4719 default:
4720 return Qnil;
4726 * Compile a constant path into two sequences of instructions, one for the
4727 * owning expression if there is one (prefix) and one for the constant reads
4728 * (body).
4730 static void
4731 pm_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)
4733 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
4735 switch (PM_NODE_TYPE(node)) {
4736 case PM_CONSTANT_READ_NODE: {
4737 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
4738 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
4740 PUSH_INSN1(body, location, putobject, Qtrue);
4741 PUSH_INSN1(body, location, getconstant, name);
4742 break;
4744 case PM_CONSTANT_PATH_NODE: {
4745 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) node;
4746 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
4748 if (cast->parent == NULL) {
4749 PUSH_INSN(body, location, pop);
4750 PUSH_INSN1(body, location, putobject, rb_cObject);
4751 PUSH_INSN1(body, location, putobject, Qtrue);
4752 PUSH_INSN1(body, location, getconstant, name);
4754 else {
4755 pm_compile_constant_path(iseq, cast->parent, prefix, body, false, scope_node);
4756 PUSH_INSN1(body, location, putobject, Qfalse);
4757 PUSH_INSN1(body, location, getconstant, name);
4759 break;
4761 default:
4762 PM_COMPILE_INTO_ANCHOR(prefix, node);
4763 break;
4768 * When we're compiling a case node, it's possible that we can speed it up using
4769 * a dispatch hash, which will allow us to jump directly to the correct when
4770 * clause body based on a hash lookup of the value. This can only happen when
4771 * the conditions are literals that can be compiled into a hash key.
4773 * This function accepts a dispatch hash and the condition of a when clause. It
4774 * is responsible for compiling the condition into a hash key and then adding it
4775 * to the dispatch hash.
4777 * If the value can be successfully compiled into the hash, then this function
4778 * returns the dispatch hash with the new key added. If the value cannot be
4779 * compiled into the hash, then this function returns Qundef. In the case of
4780 * Qundef, this function is signaling that the caller should abandon the
4781 * optimization entirely.
4783 static VALUE
4784 pm_compile_case_node_dispatch(rb_iseq_t *iseq, VALUE dispatch, const pm_node_t *node, LABEL *label, const pm_scope_node_t *scope_node)
4786 VALUE key = Qundef;
4788 switch (PM_NODE_TYPE(node)) {
4789 case PM_FLOAT_NODE: {
4790 key = pm_static_literal_value(iseq, node, scope_node);
4791 double intptr;
4793 if (modf(RFLOAT_VALUE(key), &intptr) == 0.0) {
4794 key = (FIXABLE(intptr) ? LONG2FIX((long) intptr) : rb_dbl2big(intptr));
4797 break;
4799 case PM_FALSE_NODE:
4800 case PM_INTEGER_NODE:
4801 case PM_NIL_NODE:
4802 case PM_SOURCE_FILE_NODE:
4803 case PM_SOURCE_LINE_NODE:
4804 case PM_SYMBOL_NODE:
4805 case PM_TRUE_NODE:
4806 key = pm_static_literal_value(iseq, node, scope_node);
4807 break;
4808 case PM_STRING_NODE: {
4809 const pm_string_node_t *cast = (const pm_string_node_t *) node;
4810 key = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
4811 break;
4813 default:
4814 return Qundef;
4817 if (NIL_P(rb_hash_lookup(dispatch, key))) {
4818 rb_hash_aset(dispatch, key, ((VALUE) label) | 1);
4821 return dispatch;
4825 * Return the object that will be pushed onto the stack for the given node.
4827 static VALUE
4828 pm_compile_shareable_constant_literal(rb_iseq_t *iseq, const pm_node_t *node, const pm_scope_node_t *scope_node)
4830 switch (PM_NODE_TYPE(node)) {
4831 case PM_TRUE_NODE:
4832 case PM_FALSE_NODE:
4833 case PM_NIL_NODE:
4834 case PM_SYMBOL_NODE:
4835 case PM_REGULAR_EXPRESSION_NODE:
4836 case PM_SOURCE_LINE_NODE:
4837 case PM_INTEGER_NODE:
4838 case PM_FLOAT_NODE:
4839 case PM_RATIONAL_NODE:
4840 case PM_IMAGINARY_NODE:
4841 case PM_SOURCE_ENCODING_NODE:
4842 return pm_static_literal_value(iseq, node, scope_node);
4843 case PM_STRING_NODE:
4844 return parse_static_literal_string(iseq, scope_node, node, &((const pm_string_node_t *) node)->unescaped);
4845 case PM_SOURCE_FILE_NODE:
4846 return pm_source_file_value((const pm_source_file_node_t *) node, scope_node);
4847 case PM_ARRAY_NODE: {
4848 const pm_array_node_t *cast = (const pm_array_node_t *) node;
4849 VALUE result = rb_ary_new_capa(cast->elements.size);
4851 for (size_t index = 0; index < cast->elements.size; index++) {
4852 VALUE element = pm_compile_shareable_constant_literal(iseq, cast->elements.nodes[index], scope_node);
4853 if (element == Qundef) return Qundef;
4855 rb_ary_push(result, element);
4858 return rb_ractor_make_shareable(result);
4860 case PM_HASH_NODE: {
4861 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
4862 VALUE result = rb_hash_new_capa(cast->elements.size);
4864 for (size_t index = 0; index < cast->elements.size; index++) {
4865 const pm_node_t *element = cast->elements.nodes[index];
4866 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) return Qundef;
4868 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
4870 VALUE key = pm_compile_shareable_constant_literal(iseq, assoc->key, scope_node);
4871 if (key == Qundef) return Qundef;
4873 VALUE value = pm_compile_shareable_constant_literal(iseq, assoc->value, scope_node);
4874 if (value == Qundef) return Qundef;
4876 rb_hash_aset(result, key, value);
4879 return rb_ractor_make_shareable(result);
4881 default:
4882 return Qundef;
4887 * Compile the instructions for pushing the value that will be written to a
4888 * shared constant.
4890 static void
4891 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)
4893 VALUE literal = pm_compile_shareable_constant_literal(iseq, node, scope_node);
4894 if (literal != Qundef) {
4895 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
4896 PUSH_INSN1(ret, location, putobject, literal);
4897 return;
4900 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(scope_node->parser, node);
4901 switch (PM_NODE_TYPE(node)) {
4902 case PM_ARRAY_NODE: {
4903 const pm_array_node_t *cast = (const pm_array_node_t *) node;
4905 if (top) {
4906 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
4909 for (size_t index = 0; index < cast->elements.size; index++) {
4910 pm_compile_shareable_constant_value(iseq, cast->elements.nodes[index], shareability, path, ret, scope_node, false);
4913 PUSH_INSN1(ret, location, newarray, INT2FIX(cast->elements.size));
4915 if (top) {
4916 ID method_id = (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) ? rb_intern("make_shareable_copy") : rb_intern("make_shareable");
4917 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
4920 return;
4922 case PM_HASH_NODE: {
4923 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
4925 if (top) {
4926 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
4929 for (size_t index = 0; index < cast->elements.size; index++) {
4930 const pm_node_t *element = cast->elements.nodes[index];
4932 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) {
4933 COMPILE_ERROR(iseq, location.line, "Ractor constant writes do not support **");
4936 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
4937 pm_compile_shareable_constant_value(iseq, assoc->key, shareability, path, ret, scope_node, false);
4938 pm_compile_shareable_constant_value(iseq, assoc->value, shareability, path, ret, scope_node, false);
4941 PUSH_INSN1(ret, location, newhash, INT2FIX(cast->elements.size * 2));
4943 if (top) {
4944 ID method_id = (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) ? rb_intern("make_shareable_copy") : rb_intern("make_shareable");
4945 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
4948 return;
4950 default: {
4951 DECL_ANCHOR(value_seq);
4952 INIT_ANCHOR(value_seq);
4954 pm_compile_node(iseq, node, value_seq, false, scope_node);
4955 if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) {
4956 PUSH_SEND_WITH_FLAG(value_seq, location, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE));
4959 if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_LITERAL) {
4960 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
4961 PUSH_SEQ(ret, value_seq);
4962 PUSH_INSN1(ret, location, putobject, path);
4963 PUSH_SEND_WITH_FLAG(ret, location, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE));
4965 else if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY) {
4966 if (top) PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
4967 PUSH_SEQ(ret, value_seq);
4968 if (top) PUSH_SEND_WITH_FLAG(ret, location, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
4970 else if (shareability & PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_EVERYTHING) {
4971 if (top) PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
4972 PUSH_SEQ(ret, value_seq);
4973 if (top) PUSH_SEND_WITH_FLAG(ret, location, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
4976 break;
4982 * Compile a constant write node, either in the context of a ractor pragma or
4983 * not.
4985 static void
4986 pm_compile_constant_write_node(rb_iseq_t *iseq, const pm_constant_write_node_t *node, const pm_node_flags_t shareability, const pm_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
4988 const pm_line_column_t location = *node_location;
4989 ID name_id = pm_constant_id_lookup(scope_node, node->name);
4991 if (shareability != 0) {
4992 pm_compile_shareable_constant_value(iseq, node->value, shareability, rb_id2str(name_id), ret, scope_node, true);
4994 else {
4995 PM_COMPILE_NOT_POPPED(node->value);
4998 if (!popped) PUSH_INSN(ret, location, dup);
4999 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5000 PUSH_INSN1(ret, location, setconstant, ID2SYM(name_id));
5004 * Compile a constant and write node, either in the context of a ractor pragma
5005 * or not.
5007 static void
5008 pm_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_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5010 const pm_line_column_t location = *node_location;
5012 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5013 LABEL *end_label = NEW_LABEL(location.line);
5015 pm_compile_constant_read(iseq, name, &node->name_loc, ret, scope_node);
5016 if (!popped) PUSH_INSN(ret, location, dup);
5018 PUSH_INSNL(ret, location, branchunless, end_label);
5019 if (!popped) PUSH_INSN(ret, location, pop);
5021 if (shareability != 0) {
5022 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5024 else {
5025 PM_COMPILE_NOT_POPPED(node->value);
5028 if (!popped) PUSH_INSN(ret, location, dup);
5029 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5030 PUSH_INSN1(ret, location, setconstant, name);
5031 PUSH_LABEL(ret, end_label);
5035 * Compile a constant or write node, either in the context of a ractor pragma or
5036 * not.
5038 static void
5039 pm_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_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5041 const pm_line_column_t location = *node_location;
5042 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5044 LABEL *set_label = NEW_LABEL(location.line);
5045 LABEL *end_label = NEW_LABEL(location.line);
5047 PUSH_INSN(ret, location, putnil);
5048 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST), name, Qtrue);
5049 PUSH_INSNL(ret, location, branchunless, set_label);
5051 pm_compile_constant_read(iseq, name, &node->name_loc, ret, scope_node);
5052 if (!popped) PUSH_INSN(ret, location, dup);
5054 PUSH_INSNL(ret, location, branchif, end_label);
5055 if (!popped) PUSH_INSN(ret, location, pop);
5056 PUSH_LABEL(ret, set_label);
5058 if (shareability != 0) {
5059 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5061 else {
5062 PM_COMPILE_NOT_POPPED(node->value);
5065 if (!popped) PUSH_INSN(ret, location, dup);
5066 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5067 PUSH_INSN1(ret, location, setconstant, name);
5068 PUSH_LABEL(ret, end_label);
5072 * Compile a constant operator write node, either in the context of a ractor
5073 * pragma or not.
5075 static void
5076 pm_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_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5078 const pm_line_column_t location = *node_location;
5080 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, node->name));
5081 ID method_id = pm_constant_id_lookup(scope_node, node->binary_operator);
5083 pm_compile_constant_read(iseq, name, &node->name_loc, ret, scope_node);
5085 if (shareability != 0) {
5086 pm_compile_shareable_constant_value(iseq, node->value, shareability, name, ret, scope_node, true);
5088 else {
5089 PM_COMPILE_NOT_POPPED(node->value);
5092 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
5093 if (!popped) PUSH_INSN(ret, location, dup);
5095 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
5096 PUSH_INSN1(ret, location, setconstant, name);
5100 * Creates a string that is used in ractor error messages to describe the
5101 * constant path being written.
5103 static VALUE
5104 pm_constant_path_path(const pm_constant_path_node_t *node, const pm_scope_node_t *scope_node)
5106 VALUE parts = rb_ary_new();
5107 rb_ary_push(parts, rb_id2str(pm_constant_id_lookup(scope_node, node->name)));
5109 const pm_node_t *current = node->parent;
5110 while (current != NULL && PM_NODE_TYPE_P(current, PM_CONSTANT_PATH_NODE)) {
5111 const pm_constant_path_node_t *cast = (const pm_constant_path_node_t *) current;
5112 rb_ary_unshift(parts, rb_id2str(pm_constant_id_lookup(scope_node, cast->name)));
5113 current = cast->parent;
5116 if (current == NULL) {
5117 rb_ary_unshift(parts, rb_id2str(idNULL));
5119 else if (PM_NODE_TYPE_P(current, PM_CONSTANT_READ_NODE)) {
5120 rb_ary_unshift(parts, rb_id2str(pm_constant_id_lookup(scope_node, ((const pm_constant_read_node_t *) current)->name)));
5122 else {
5123 rb_ary_unshift(parts, rb_str_new_cstr("..."));
5126 return rb_ary_join(parts, rb_str_new_cstr("::"));
5130 * Compile a constant path write node, either in the context of a ractor pragma
5131 * or not.
5133 static void
5134 pm_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_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5136 const pm_line_column_t location = *node_location;
5137 const pm_constant_path_node_t *target = node->target;
5138 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
5140 if (target->parent) {
5141 PM_COMPILE_NOT_POPPED((const pm_node_t *) target->parent);
5143 else {
5144 PUSH_INSN1(ret, location, putobject, rb_cObject);
5147 if (shareability != 0) {
5148 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
5150 else {
5151 PM_COMPILE_NOT_POPPED(node->value);
5154 if (!popped) {
5155 PUSH_INSN(ret, location, swap);
5156 PUSH_INSN1(ret, location, topn, INT2FIX(1));
5159 PUSH_INSN(ret, location, swap);
5160 PUSH_INSN1(ret, location, setconstant, name);
5164 * Compile a constant path and write node, either in the context of a ractor
5165 * pragma or not.
5167 static void
5168 pm_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_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5170 const pm_line_column_t location = *node_location;
5171 const pm_constant_path_node_t *target = node->target;
5173 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
5174 LABEL *lfin = NEW_LABEL(location.line);
5176 if (target->parent) {
5177 PM_COMPILE_NOT_POPPED(target->parent);
5179 else {
5180 PUSH_INSN1(ret, location, putobject, rb_cObject);
5183 PUSH_INSN(ret, location, dup);
5184 PUSH_INSN1(ret, location, putobject, Qtrue);
5185 PUSH_INSN1(ret, location, getconstant, name);
5187 if (!popped) PUSH_INSN(ret, location, dup);
5188 PUSH_INSNL(ret, location, branchunless, lfin);
5190 if (!popped) PUSH_INSN(ret, location, pop);
5192 if (shareability != 0) {
5193 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
5195 else {
5196 PM_COMPILE_NOT_POPPED(node->value);
5199 if (popped) {
5200 PUSH_INSN1(ret, location, topn, INT2FIX(1));
5202 else {
5203 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
5204 PUSH_INSN(ret, location, swap);
5207 PUSH_INSN1(ret, location, setconstant, name);
5208 PUSH_LABEL(ret, lfin);
5210 if (!popped) PUSH_INSN(ret, location, swap);
5211 PUSH_INSN(ret, location, pop);
5215 * Compile a constant path or write node, either in the context of a ractor
5216 * pragma or not.
5218 static void
5219 pm_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_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5221 const pm_line_column_t location = *node_location;
5222 const pm_constant_path_node_t *target = node->target;
5224 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
5225 LABEL *lassign = NEW_LABEL(location.line);
5226 LABEL *lfin = NEW_LABEL(location.line);
5228 if (target->parent) {
5229 PM_COMPILE_NOT_POPPED(target->parent);
5231 else {
5232 PUSH_INSN1(ret, location, putobject, rb_cObject);
5235 PUSH_INSN(ret, location, dup);
5236 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CONST_FROM), name, Qtrue);
5237 PUSH_INSNL(ret, location, branchunless, lassign);
5239 PUSH_INSN(ret, location, dup);
5240 PUSH_INSN1(ret, location, putobject, Qtrue);
5241 PUSH_INSN1(ret, location, getconstant, name);
5243 if (!popped) PUSH_INSN(ret, location, dup);
5244 PUSH_INSNL(ret, location, branchif, lfin);
5246 if (!popped) PUSH_INSN(ret, location, pop);
5247 PUSH_LABEL(ret, lassign);
5249 if (shareability != 0) {
5250 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
5252 else {
5253 PM_COMPILE_NOT_POPPED(node->value);
5256 if (popped) {
5257 PUSH_INSN1(ret, location, topn, INT2FIX(1));
5259 else {
5260 PUSH_INSN1(ret, location, dupn, INT2FIX(2));
5261 PUSH_INSN(ret, location, swap);
5264 PUSH_INSN1(ret, location, setconstant, name);
5265 PUSH_LABEL(ret, lfin);
5267 if (!popped) PUSH_INSN(ret, location, swap);
5268 PUSH_INSN(ret, location, pop);
5272 * Compile a constant path operator write node, either in the context of a
5273 * ractor pragma or not.
5275 static void
5276 pm_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_line_column_t *node_location, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5278 const pm_line_column_t location = *node_location;
5279 const pm_constant_path_node_t *target = node->target;
5281 ID method_id = pm_constant_id_lookup(scope_node, node->binary_operator);
5282 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, target->name));
5284 if (target->parent) {
5285 PM_COMPILE_NOT_POPPED(target->parent);
5287 else {
5288 PUSH_INSN1(ret, location, putobject, rb_cObject);
5291 PUSH_INSN(ret, location, dup);
5292 PUSH_INSN1(ret, location, putobject, Qtrue);
5293 PUSH_INSN1(ret, location, getconstant, name);
5295 if (shareability != 0) {
5296 pm_compile_shareable_constant_value(iseq, node->value, shareability, pm_constant_path_path(node->target, scope_node), ret, scope_node, true);
5298 else {
5299 PM_COMPILE_NOT_POPPED(node->value);
5302 PUSH_CALL(ret, location, method_id, INT2FIX(1));
5303 PUSH_INSN(ret, location, swap);
5305 if (!popped) {
5306 PUSH_INSN1(ret, location, topn, INT2FIX(1));
5307 PUSH_INSN(ret, location, swap);
5310 PUSH_INSN1(ret, location, setconstant, name);
5314 * Compiles a prism node into instruction sequences.
5316 * iseq - The current instruction sequence object (used for locals)
5317 * node - The prism node to compile
5318 * ret - The linked list of instructions to append instructions onto
5319 * popped - True if compiling something with no side effects, so instructions don't
5320 * need to be added
5321 * scope_node - Stores parser and local information
5323 static void
5324 pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node)
5326 const pm_parser_t *parser = scope_node->parser;
5327 const pm_line_column_t location = PM_NODE_START_LINE_COLUMN(parser, node);
5328 int lineno = (int) location.line;
5330 if (PM_NODE_TYPE_P(node, PM_RETURN_NODE) && PM_NODE_FLAG_P(node, PM_RETURN_NODE_FLAGS_REDUNDANT) && ((const pm_return_node_t *) node)->arguments == NULL) {
5331 // If the node that we're compiling is a return node that is redundant,
5332 // then it cannot be considered a line node because the other parser
5333 // eliminates it from the parse tree. In this case we must replicate
5334 // this behavior.
5335 } else {
5336 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)) {
5337 // If this node is a begin node and it has empty statements and also
5338 // has a rescue clause, then the other parser considers it as
5339 // starting on the same line as the rescue, as opposed to the
5340 // location of the begin keyword. We replicate that behavior here.
5341 lineno = (int) PM_NODE_START_LINE_COLUMN(parser, ((const pm_begin_node_t *) node)->rescue_clause).line;
5344 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_NEWLINE) && ISEQ_COMPILE_DATA(iseq)->last_line != lineno) {
5345 // If this node has the newline flag set and it is on a new line
5346 // from the previous nodes that have been compiled for this ISEQ,
5347 // then we need to emit a newline event.
5348 int event = RUBY_EVENT_LINE;
5350 ISEQ_COMPILE_DATA(iseq)->last_line = lineno;
5351 if (ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
5352 event |= RUBY_EVENT_COVERAGE_LINE;
5354 PUSH_TRACE(ret, event);
5358 switch (PM_NODE_TYPE(node)) {
5359 case PM_ALIAS_GLOBAL_VARIABLE_NODE: {
5360 // alias $foo $bar
5361 // ^^^^^^^^^^^^^^^
5362 const pm_alias_global_variable_node_t *cast = (const pm_alias_global_variable_node_t *) node;
5363 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5365 const pm_location_t *new_name_loc = &cast->new_name->location;
5366 PUSH_INSN1(ret, location, putobject, ID2SYM(rb_intern3((const char *) new_name_loc->start, new_name_loc->end - new_name_loc->start, scope_node->encoding)));
5368 const pm_location_t *old_name_loc = &cast->old_name->location;
5369 PUSH_INSN1(ret, location, putobject, ID2SYM(rb_intern3((const char *) old_name_loc->start, old_name_loc->end - old_name_loc->start, scope_node->encoding)));
5371 PUSH_SEND(ret, location, id_core_set_variable_alias, INT2FIX(2));
5372 if (popped) PUSH_INSN(ret, location, pop);
5374 return;
5376 case PM_ALIAS_METHOD_NODE: {
5377 // alias foo bar
5378 // ^^^^^^^^^^^^^
5379 const pm_alias_method_node_t *cast = (const pm_alias_method_node_t *) node;
5381 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5382 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
5383 PM_COMPILE_NOT_POPPED(cast->new_name);
5384 PM_COMPILE_NOT_POPPED(cast->old_name);
5386 PUSH_SEND(ret, location, id_core_set_method_alias, INT2FIX(3));
5387 if (popped) PUSH_INSN(ret, location, pop);
5389 return;
5391 case PM_AND_NODE: {
5392 // a and b
5393 // ^^^^^^^
5394 const pm_and_node_t *cast = (const pm_and_node_t *) node;
5395 LABEL *end_label = NEW_LABEL(lineno);
5397 PM_COMPILE_NOT_POPPED(cast->left);
5398 if (!popped) PUSH_INSN(ret, location, dup);
5399 PUSH_INSNL(ret, location, branchunless, end_label);
5401 if (!popped) PUSH_INSN(ret, location, pop);
5402 PM_COMPILE(cast->right);
5403 PUSH_LABEL(ret, end_label);
5405 return;
5407 case PM_ARGUMENTS_NODE:
5408 // These are ArgumentsNodes that are not compiled directly by their
5409 // parent call nodes, used in the cases of NextNodes, ReturnNodes, and
5410 // BreakNodes. They can create an array like ArrayNode.
5411 case PM_ARRAY_NODE: {
5412 const pm_node_list_t *elements;
5414 if (PM_NODE_TYPE(node) == PM_ARGUMENTS_NODE) {
5415 // break foo
5416 // ^^^
5417 const pm_arguments_node_t *cast = (const pm_arguments_node_t *) node;
5418 elements = &cast->arguments;
5420 // If we are only returning a single element through one of the jump
5421 // nodes, then we will only compile that node directly.
5422 if (elements->size == 1) {
5423 PM_COMPILE(elements->nodes[0]);
5424 return;
5427 else {
5428 // [foo, bar, baz]
5429 // ^^^^^^^^^^^^^^^
5430 const pm_array_node_t *cast = (const pm_array_node_t *) node;
5431 elements = &cast->elements;
5434 // If every node in the array is static, then we can compile the entire
5435 // array now instead of later.
5436 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
5437 // We're only going to compile this node if it's not popped. If it
5438 // is popped, then we know we don't need to do anything since it's
5439 // statically known.
5440 if (!popped) {
5441 if (elements->size) {
5442 VALUE value = pm_static_literal_value(iseq, node, scope_node);
5443 PUSH_INSN1(ret, location, duparray, value);
5445 else {
5446 PUSH_INSN1(ret, location, newarray, INT2FIX(0));
5450 else {
5451 // Here since we know there are possible side-effects inside the
5452 // array contents, we're going to build it entirely at runtime.
5453 // We'll do this by pushing all of the elements onto the stack and
5454 // then combining them with newarray.
5456 // If this array is popped, then this serves only to ensure we enact
5457 // all side-effects (like method calls) that are contained within
5458 // the array contents.
5460 // We treat all sequences of non-splat elements as their
5461 // own arrays, followed by a newarray, and then continually
5462 // concat the arrays with the SplatNode nodes.
5463 int new_array_size = 0;
5465 bool need_to_concat_array = false;
5466 bool has_kw_splat = false;
5468 for (size_t index = 0; index < elements->size; index++) {
5469 const pm_node_t *element = elements->nodes[index];
5471 if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) {
5472 const pm_splat_node_t *splat_element = (const pm_splat_node_t *) element;
5474 // If we already have non-splat elements, we need to emit a
5475 // newarray instruction.
5476 if (new_array_size > 0) {
5477 PUSH_INSN1(ret, location, newarray, INT2FIX(new_array_size));
5478 new_array_size = 0;
5480 // We don't want to emit a concat array in the case
5481 // where we're seeing our first splat, and already have
5482 // elements.
5483 if (need_to_concat_array) PUSH_INSN(ret, location, concatarray);
5486 if (splat_element->expression) {
5487 PM_COMPILE_NOT_POPPED(splat_element->expression);
5489 else {
5490 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_MULT, 0);
5491 PUSH_GETLOCAL(ret, location, index.index, index.level);
5494 if (index > 0) {
5495 PUSH_INSN(ret, location, concatarray);
5497 else {
5498 // If this is the first element of the array then we
5499 // need to splatarray the elements into the list.
5500 PUSH_INSN1(ret, location, splatarray, Qtrue);
5503 // Since we have now seen a splat and are concat-ing arrays,
5504 // all subsequent splats will need to concat as well.
5505 need_to_concat_array = true;
5507 else if (PM_NODE_TYPE_P(element, PM_KEYWORD_HASH_NODE)) {
5508 new_array_size++;
5509 has_kw_splat = true;
5510 pm_compile_hash_elements(iseq, element, &((const pm_keyword_hash_node_t *) element)->elements, ret, scope_node);
5512 else {
5513 new_array_size++;
5514 PM_COMPILE_NOT_POPPED(element);
5518 if (new_array_size) {
5519 if (has_kw_splat) {
5520 PUSH_INSN1(ret, location, newarraykwsplat, INT2FIX(new_array_size));
5522 else {
5523 PUSH_INSN1(ret, location, newarray, INT2FIX(new_array_size));
5526 if (need_to_concat_array) PUSH_INSN(ret, location, concatarray);
5529 if (popped) PUSH_INSN(ret, location, pop);
5531 return;
5533 case PM_ASSOC_NODE: {
5534 // { foo: 1 }
5535 // ^^^^^^
5537 // foo(bar: 1)
5538 // ^^^^^^
5539 const pm_assoc_node_t *cast = (const pm_assoc_node_t *) node;
5541 PM_COMPILE(cast->key);
5542 PM_COMPILE(cast->value);
5544 return;
5546 case PM_ASSOC_SPLAT_NODE: {
5547 // { **foo }
5548 // ^^^^^
5550 // def foo(**); bar(**); end
5551 // ^^
5552 const pm_assoc_splat_node_t *cast = (const pm_assoc_splat_node_t *) node;
5554 if (cast->value != NULL) {
5555 PM_COMPILE(cast->value);
5557 else if (!popped) {
5558 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_POW, 0);
5559 PUSH_GETLOCAL(ret, location, index.index, index.level);
5562 return;
5564 case PM_BACK_REFERENCE_READ_NODE: {
5565 // $+
5566 // ^^
5567 if (!popped) {
5568 // Since a back reference is `$<char>`, ruby represents the ID as the
5569 // an rb_intern on the value after the `$`.
5570 char *char_ptr = (char *)(node->location.start) + 1;
5571 ID backref_val = INT2FIX(rb_intern2(char_ptr, 1)) << 1 | 1;
5572 PUSH_INSN2(ret, location, getspecial, INT2FIX(1), backref_val);
5574 return;
5576 case PM_BEGIN_NODE: {
5577 // begin end
5578 // ^^^^^^^^^
5579 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
5581 if (cast->ensure_clause) {
5582 // Compiling the ensure clause will compile the rescue clause (if
5583 // there is one), which will compile the begin statements.
5584 pm_compile_ensure(iseq, cast, &location, ret, popped, scope_node);
5586 else if (cast->rescue_clause) {
5587 // Compiling rescue will compile begin statements (if applicable).
5588 pm_compile_rescue(iseq, cast, &location, ret, popped, scope_node);
5590 else {
5591 // If there is neither ensure or rescue, the just compile the
5592 // statements.
5593 if (cast->statements != NULL) {
5594 PM_COMPILE((const pm_node_t *) cast->statements);
5596 else if (!popped) {
5597 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
5600 return;
5602 case PM_BLOCK_ARGUMENT_NODE: {
5603 // foo(&bar)
5604 // ^^^^
5605 const pm_block_argument_node_t *cast = (const pm_block_argument_node_t *) node;
5607 if (cast->expression != NULL) {
5608 PM_COMPILE(cast->expression);
5610 else {
5611 // If there's no expression, this must be block forwarding.
5612 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, PM_CONSTANT_AND, 0);
5613 PUSH_INSN2(ret, location, getblockparamproxy, INT2FIX(local_index.index + VM_ENV_DATA_SIZE - 1), INT2FIX(local_index.level));
5615 return;
5617 case PM_BREAK_NODE: {
5618 // break
5619 // ^^^^^
5621 // break foo
5622 // ^^^^^^^^^
5623 const pm_break_node_t *cast = (const pm_break_node_t *) node;
5624 unsigned long throw_flag = 0;
5626 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
5627 /* while/until */
5628 LABEL *splabel = NEW_LABEL(0);
5629 PUSH_LABEL(ret, splabel);
5630 PUSH_ADJUST(ret, location, ISEQ_COMPILE_DATA(iseq)->redo_label);
5632 if (cast->arguments != NULL) {
5633 PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->arguments);
5635 else {
5636 PUSH_INSN(ret, location, putnil);
5639 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
5640 PUSH_INSNL(ret, location, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
5641 PUSH_ADJUST_RESTORE(ret, splabel);
5642 if (!popped) PUSH_INSN(ret, location, putnil);
5644 else {
5645 const rb_iseq_t *ip = iseq;
5647 while (ip) {
5648 if (!ISEQ_COMPILE_DATA(ip)) {
5649 ip = 0;
5650 break;
5653 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
5654 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
5656 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
5657 throw_flag = 0;
5659 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
5660 COMPILE_ERROR(iseq, location.line, "Can't escape from eval with break");
5661 return;
5663 else {
5664 ip = ISEQ_BODY(ip)->parent_iseq;
5665 continue;
5668 /* escape from block */
5669 if (cast->arguments != NULL) {
5670 PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->arguments);
5672 else {
5673 PUSH_INSN(ret, location, putnil);
5676 PUSH_INSN1(ret, location, throw, INT2FIX(throw_flag | TAG_BREAK));
5677 if (popped) PUSH_INSN(ret, location, pop);
5679 return;
5682 COMPILE_ERROR(iseq, location.line, "Invalid break");
5684 return;
5686 case PM_CALL_NODE: {
5687 // foo
5688 // ^^^
5690 // foo.bar
5691 // ^^^^^^^
5693 // foo.bar() {}
5694 // ^^^^^^^^^^^^
5695 const pm_call_node_t *cast = (const pm_call_node_t *) node;
5696 ID method_id = pm_constant_id_lookup(scope_node, cast->name);
5698 const pm_location_t *message_loc = &cast->message_loc;
5699 if (message_loc->start == NULL) message_loc = &cast->base.location;
5701 const pm_line_column_t location = PM_LOCATION_START_LINE_COLUMN(scope_node->parser, message_loc);
5702 const char *builtin_func;
5704 if (UNLIKELY(iseq_has_builtin_function_table(iseq)) && (builtin_func = pm_iseq_builtin_function_name(scope_node, cast->receiver, method_id)) != NULL) {
5705 pm_compile_builtin_function_call(iseq, ret, scope_node, cast, &location, popped, ISEQ_COMPILE_DATA(iseq)->current_block, builtin_func);
5706 return;
5709 LABEL *start = NEW_LABEL(location.line);
5710 if (cast->block) PUSH_LABEL(ret, start);
5712 switch (method_id) {
5713 case idUMinus: {
5714 if (pm_opt_str_freeze_p(iseq, cast)) {
5715 VALUE value = parse_static_literal_string(iseq, scope_node, cast->receiver, &((const pm_string_node_t * ) cast->receiver)->unescaped);
5716 PUSH_INSN2(ret, location, opt_str_uminus, value, new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE));
5717 return;
5719 break;
5721 case idFreeze: {
5722 if (pm_opt_str_freeze_p(iseq, cast)) {
5723 VALUE value = parse_static_literal_string(iseq, scope_node, cast->receiver, &((const pm_string_node_t * ) cast->receiver)->unescaped);
5724 PUSH_INSN2(ret, location, opt_str_freeze, value, new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE));
5725 return;
5727 break;
5729 case idAREF: {
5730 if (pm_opt_aref_with_p(iseq, cast)) {
5731 const pm_string_node_t *string = (const pm_string_node_t *) ((const pm_arguments_node_t *) cast->arguments)->arguments.nodes[0];
5732 VALUE value = parse_static_literal_string(iseq, scope_node, (const pm_node_t *) string, &string->unescaped);
5734 PM_COMPILE_NOT_POPPED(cast->receiver);
5735 PUSH_INSN2(ret, location, opt_aref_with, value, new_callinfo(iseq, idAREF, 1, 0, NULL, FALSE));
5737 if (popped) {
5738 PUSH_INSN(ret, location, pop);
5741 return;
5743 break;
5745 case idASET: {
5746 if (pm_opt_aset_with_p(iseq, cast)) {
5747 const pm_string_node_t *string = (const pm_string_node_t *) ((const pm_arguments_node_t *) cast->arguments)->arguments.nodes[0];
5748 VALUE value = parse_static_literal_string(iseq, scope_node, (const pm_node_t *) string, &string->unescaped);
5750 PM_COMPILE_NOT_POPPED(cast->receiver);
5751 PM_COMPILE_NOT_POPPED(((const pm_arguments_node_t *) cast->arguments)->arguments.nodes[1]);
5753 if (!popped) {
5754 PUSH_INSN(ret, location, swap);
5755 PUSH_INSN1(ret, location, topn, INT2FIX(1));
5758 PUSH_INSN2(ret, location, opt_aset_with, value, new_callinfo(iseq, idASET, 2, 0, NULL, FALSE));
5759 PUSH_INSN(ret, location, pop);
5760 return;
5762 break;
5766 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && !popped) {
5767 PUSH_INSN(ret, location, putnil);
5770 if (cast->receiver == NULL) {
5771 PUSH_INSN(ret, location, putself);
5773 else {
5774 PM_COMPILE_NOT_POPPED(cast->receiver);
5777 pm_compile_call(iseq, cast, ret, popped, scope_node, method_id, start);
5778 return;
5780 case PM_CALL_AND_WRITE_NODE: {
5781 // foo.bar &&= baz
5782 // ^^^^^^^^^^^^^^^
5783 const pm_call_and_write_node_t *cast = (const pm_call_and_write_node_t *) node;
5784 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);
5785 return;
5787 case PM_CALL_OR_WRITE_NODE: {
5788 // foo.bar ||= baz
5789 // ^^^^^^^^^^^^^^^
5790 const pm_call_or_write_node_t *cast = (const pm_call_or_write_node_t *) node;
5791 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);
5792 return;
5794 case PM_CALL_OPERATOR_WRITE_NODE: {
5795 // foo.bar += baz
5796 // ^^^^^^^^^^^^^^^
5798 // Call operator writes occur when you have a call node on the left-hand
5799 // side of a write operator that is not `=`. As an example,
5800 // `foo.bar *= 1`. This breaks down to caching the receiver on the
5801 // stack and then performing three method calls, one to read the value,
5802 // one to compute the result, and one to write the result back to the
5803 // receiver.
5804 const pm_call_operator_write_node_t *cast = (const pm_call_operator_write_node_t *) node;
5805 int flag = 0;
5807 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY)) {
5808 flag = VM_CALL_FCALL;
5811 PM_COMPILE_NOT_POPPED(cast->receiver);
5813 LABEL *safe_label = NULL;
5814 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
5815 safe_label = NEW_LABEL(location.line);
5816 PUSH_INSN(ret, location, dup);
5817 PUSH_INSNL(ret, location, branchnil, safe_label);
5820 PUSH_INSN(ret, location, dup);
5822 ID id_read_name = pm_constant_id_lookup(scope_node, cast->read_name);
5823 PUSH_SEND_WITH_FLAG(ret, location, id_read_name, INT2FIX(0), INT2FIX(flag));
5825 PM_COMPILE_NOT_POPPED(cast->value);
5826 ID id_operator = pm_constant_id_lookup(scope_node, cast->binary_operator);
5827 PUSH_SEND(ret, location, id_operator, INT2FIX(1));
5829 if (!popped) {
5830 PUSH_INSN(ret, location, swap);
5831 PUSH_INSN1(ret, location, topn, INT2FIX(1));
5834 ID id_write_name = pm_constant_id_lookup(scope_node, cast->write_name);
5835 PUSH_SEND_WITH_FLAG(ret, location, id_write_name, INT2FIX(1), INT2FIX(flag));
5837 if (safe_label != NULL && popped) PUSH_LABEL(ret, safe_label);
5838 PUSH_INSN(ret, location, pop);
5839 if (safe_label != NULL && !popped) PUSH_LABEL(ret, safe_label);
5841 return;
5843 case PM_CASE_NODE: {
5844 // case foo; when bar; end
5845 // ^^^^^^^^^^^^^^^^^^^^^^^
5846 const pm_case_node_t *cast = (const pm_case_node_t *) node;
5847 const pm_node_list_t *conditions = &cast->conditions;
5849 // This is the anchor that we will compile the conditions of the various
5850 // `when` nodes into. If a match is found, they will need to jump into
5851 // the body_seq anchor to the correct spot.
5852 DECL_ANCHOR(cond_seq);
5853 INIT_ANCHOR(cond_seq);
5855 // This is the anchor that we will compile the bodies of the various
5856 // `when` nodes into. We'll make sure that the clauses that are compiled
5857 // jump into the correct spots within this anchor.
5858 DECL_ANCHOR(body_seq);
5859 INIT_ANCHOR(body_seq);
5861 // This is the label where all of the when clauses will jump to if they
5862 // have matched and are done executing their bodies.
5863 LABEL *end_label = NEW_LABEL(location.line);
5865 // If we have a predicate on this case statement, then it's going to
5866 // compare all of the various when clauses to the predicate. If we
5867 // don't, then it's basically an if-elsif-else chain.
5868 if (cast->predicate == NULL) {
5869 // Establish branch coverage for the case node.
5870 VALUE branches = Qfalse;
5871 rb_code_location_t case_location = { 0 };
5872 int branch_id = 0;
5874 if (PM_BRANCH_COVERAGE_P(iseq)) {
5875 case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
5876 branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case");
5879 // Loop through each clauses in the case node and compile each of
5880 // the conditions within them into cond_seq. If they match, they
5881 // should jump into their respective bodies in body_seq.
5882 for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) {
5883 const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index];
5884 const pm_node_list_t *conditions = &clause->conditions;
5886 int clause_lineno = pm_node_line_number(parser, (const pm_node_t *) clause);
5887 LABEL *label = NEW_LABEL(clause_lineno);
5888 PUSH_LABEL(body_seq, label);
5890 // Establish branch coverage for the when clause.
5891 if (PM_BRANCH_COVERAGE_P(iseq)) {
5892 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));
5893 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches);
5896 if (clause->statements != NULL) {
5897 pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node);
5899 else if (!popped) {
5900 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
5903 PUSH_INSNL(body_seq, location, jump, end_label);
5905 // Compile each of the conditions for the when clause into the
5906 // cond_seq. Each one should have a unique condition and should
5907 // jump to the subsequent one if it doesn't match.
5908 for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) {
5909 const pm_node_t *condition = conditions->nodes[condition_index];
5911 if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) {
5912 pm_line_column_t cond_location = PM_NODE_START_LINE_COLUMN(parser, condition);
5913 PUSH_INSN(cond_seq, cond_location, putnil);
5914 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
5915 PUSH_INSN1(cond_seq, cond_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
5916 PUSH_INSNL(cond_seq, cond_location, branchif, label);
5918 else {
5919 LABEL *next_label = NEW_LABEL(pm_node_line_number(parser, condition));
5920 pm_compile_branch_condition(iseq, cond_seq, condition, label, next_label, false, scope_node);
5921 PUSH_LABEL(cond_seq, next_label);
5926 // Establish branch coverage for the else clause (implicit or
5927 // explicit).
5928 if (PM_BRANCH_COVERAGE_P(iseq)) {
5929 rb_code_location_t branch_location;
5931 if (cast->consequent == NULL) {
5932 branch_location = case_location;
5933 } else if (cast->consequent->statements == NULL) {
5934 branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->consequent);
5935 } else {
5936 branch_location = pm_code_location(scope_node, (const pm_node_t *) cast->consequent->statements);
5939 add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
5942 // Compile the consequent else clause if there is one.
5943 if (cast->consequent != NULL) {
5944 pm_compile_node(iseq, (const pm_node_t *) cast->consequent, cond_seq, popped, scope_node);
5946 else if (!popped) {
5947 PUSH_SYNTHETIC_PUTNIL(cond_seq, iseq);
5950 // Finally, jump to the end label if none of the other conditions
5951 // have matched.
5952 PUSH_INSNL(cond_seq, location, jump, end_label);
5953 PUSH_SEQ(ret, cond_seq);
5955 else {
5956 // Establish branch coverage for the case node.
5957 VALUE branches = Qfalse;
5958 rb_code_location_t case_location = { 0 };
5959 int branch_id = 0;
5961 if (PM_BRANCH_COVERAGE_P(iseq)) {
5962 case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
5963 branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case");
5966 // This is the label where everything will fall into if none of the
5967 // conditions matched.
5968 LABEL *else_label = NEW_LABEL(location.line);
5970 // It's possible for us to speed up the case node by using a
5971 // dispatch hash. This is a hash that maps the conditions of the
5972 // various when clauses to the labels of their bodies. If we can
5973 // compile the conditions into a hash key, then we can use a hash
5974 // lookup to jump directly to the correct when clause body.
5975 VALUE dispatch = Qundef;
5976 if (ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
5977 dispatch = rb_hash_new();
5978 RHASH_TBL_RAW(dispatch)->type = &cdhash_type;
5981 // We're going to loop through each of the conditions in the case
5982 // node and compile each of their contents into both the cond_seq
5983 // and the body_seq. Each condition will use its own label to jump
5984 // from its conditions into its body.
5986 // Note that none of the code in the loop below should be adding
5987 // anything to ret, as we're going to be laying out the entire case
5988 // node instructions later.
5989 for (size_t clause_index = 0; clause_index < conditions->size; clause_index++) {
5990 const pm_when_node_t *clause = (const pm_when_node_t *) conditions->nodes[clause_index];
5991 pm_line_column_t clause_location = PM_NODE_START_LINE_COLUMN(parser, (const pm_node_t *) clause);
5993 const pm_node_list_t *conditions = &clause->conditions;
5994 LABEL *label = NEW_LABEL(clause_location.line);
5996 // Compile each of the conditions for the when clause into the
5997 // cond_seq. Each one should have a unique comparison that then
5998 // jumps into the body if it matches.
5999 for (size_t condition_index = 0; condition_index < conditions->size; condition_index++) {
6000 const pm_node_t *condition = conditions->nodes[condition_index];
6001 const pm_line_column_t condition_location = PM_NODE_START_LINE_COLUMN(parser, condition);
6003 // If we haven't already abandoned the optimization, then
6004 // we're going to try to compile the condition into the
6005 // dispatch hash.
6006 if (dispatch != Qundef) {
6007 dispatch = pm_compile_case_node_dispatch(iseq, dispatch, condition, label, scope_node);
6010 if (PM_NODE_TYPE_P(condition, PM_SPLAT_NODE)) {
6011 PUSH_INSN(cond_seq, condition_location, dup);
6012 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
6013 PUSH_INSN1(cond_seq, condition_location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
6015 else {
6016 if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
6017 const pm_string_node_t *string = (const pm_string_node_t *) condition;
6018 VALUE value = parse_static_literal_string(iseq, scope_node, condition, &string->unescaped);
6019 PUSH_INSN1(cond_seq, condition_location, putobject, value);
6021 else {
6022 pm_compile_node(iseq, condition, cond_seq, false, scope_node);
6025 PUSH_INSN1(cond_seq, condition_location, topn, INT2FIX(1));
6026 PUSH_SEND_WITH_FLAG(cond_seq, condition_location, idEqq, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
6029 PUSH_INSNL(cond_seq, condition_location, branchif, label);
6032 // Now, add the label to the body and compile the body of the
6033 // when clause. This involves popping the predicate, compiling
6034 // the statements to be executed, and then compiling a jump to
6035 // the end of the case node.
6036 PUSH_LABEL(body_seq, label);
6037 PUSH_INSN(body_seq, clause_location, pop);
6039 // Establish branch coverage for the when clause.
6040 if (PM_BRANCH_COVERAGE_P(iseq)) {
6041 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));
6042 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "when", branches);
6045 if (clause->statements != NULL) {
6046 pm_compile_node(iseq, (const pm_node_t *) clause->statements, body_seq, popped, scope_node);
6048 else if (!popped) {
6049 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
6052 PUSH_INSNL(body_seq, clause_location, jump, end_label);
6055 // Now that we have compiled the conditions and the bodies of the
6056 // various when clauses, we can compile the predicate, lay out the
6057 // conditions, compile the fallback consequent if there is one, and
6058 // finally put in the bodies of the when clauses.
6059 PM_COMPILE_NOT_POPPED(cast->predicate);
6061 // If we have a dispatch hash, then we'll use it here to create the
6062 // optimization.
6063 if (dispatch != Qundef) {
6064 PUSH_INSN(ret, location, dup);
6065 PUSH_INSN2(ret, location, opt_case_dispatch, dispatch, else_label);
6066 LABEL_REF(else_label);
6069 PUSH_SEQ(ret, cond_seq);
6071 // Compile either the explicit else clause or an implicit else
6072 // clause.
6073 PUSH_LABEL(ret, else_label);
6075 if (cast->consequent != NULL) {
6076 pm_line_column_t else_location = PM_NODE_START_LINE_COLUMN(parser, cast->consequent->statements != NULL ? ((const pm_node_t *) cast->consequent->statements) : ((const pm_node_t *) cast->consequent));
6077 PUSH_INSN(ret, else_location, pop);
6079 // Establish branch coverage for the else clause.
6080 if (PM_BRANCH_COVERAGE_P(iseq)) {
6081 rb_code_location_t branch_location = pm_code_location(scope_node, cast->consequent->statements != NULL ? ((const pm_node_t *) cast->consequent->statements) : ((const pm_node_t *) cast->consequent));
6082 add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
6085 PM_COMPILE((const pm_node_t *) cast->consequent);
6086 PUSH_INSNL(ret, else_location, jump, end_label);
6088 else {
6089 PUSH_INSN(ret, location, pop);
6091 // Establish branch coverage for the implicit else clause.
6092 if (PM_BRANCH_COVERAGE_P(iseq)) {
6093 add_trace_branch_coverage(iseq, ret, &case_location, case_location.beg_pos.column, branch_id, "else", branches);
6096 if (!popped) PUSH_INSN(ret, location, putnil);
6097 PUSH_INSNL(ret, location, jump, end_label);
6101 PUSH_SEQ(ret, body_seq);
6102 PUSH_LABEL(ret, end_label);
6104 return;
6106 case PM_CASE_MATCH_NODE: {
6107 // case foo; in bar; end
6108 // ^^^^^^^^^^^^^^^^^^^^^
6110 // If you use the `case` keyword to create a case match node, it will
6111 // match against all of the `in` clauses until it finds one that
6112 // matches. If it doesn't find one, it can optionally fall back to an
6113 // `else` clause. If none is present and a match wasn't found, it will
6114 // raise an appropriate error.
6115 const pm_case_match_node_t *cast = (const pm_case_match_node_t *) node;
6117 // This is the anchor that we will compile the bodies of the various
6118 // `in` nodes into. We'll make sure that the patterns that are compiled
6119 // jump into the correct spots within this anchor.
6120 DECL_ANCHOR(body_seq);
6121 INIT_ANCHOR(body_seq);
6123 // This is the anchor that we will compile the patterns of the various
6124 // `in` nodes into. If a match is found, they will need to jump into the
6125 // body_seq anchor to the correct spot.
6126 DECL_ANCHOR(cond_seq);
6127 INIT_ANCHOR(cond_seq);
6129 // This label is used to indicate the end of the entire node. It is
6130 // jumped to after the entire stack is cleaned up.
6131 LABEL *end_label = NEW_LABEL(location.line);
6133 // This label is used as the fallback for the case match. If no match is
6134 // found, then we jump to this label. This is either an `else` clause or
6135 // an error handler.
6136 LABEL *else_label = NEW_LABEL(location.line);
6138 // We're going to use this to uniquely identify each branch so that we
6139 // can track coverage information.
6140 rb_code_location_t case_location = { 0 };
6141 VALUE branches = Qfalse;
6142 int branch_id = 0;
6144 if (PM_BRANCH_COVERAGE_P(iseq)) {
6145 case_location = pm_code_location(scope_node, (const pm_node_t *) cast);
6146 branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case");
6149 // If there is only one pattern, then the behavior changes a bit. It
6150 // effectively gets treated as a match required node (this is how it is
6151 // represented in the other parser).
6152 bool in_single_pattern = cast->consequent == NULL && cast->conditions.size == 1;
6154 // First, we're going to push a bunch of stuff onto the stack that is
6155 // going to serve as our scratch space.
6156 if (in_single_pattern) {
6157 PUSH_INSN(ret, location, putnil); // key error key
6158 PUSH_INSN(ret, location, putnil); // key error matchee
6159 PUSH_INSN1(ret, location, putobject, Qfalse); // key error?
6160 PUSH_INSN(ret, location, putnil); // error string
6163 // Now we're going to compile the value to match against.
6164 PUSH_INSN(ret, location, putnil); // deconstruct cache
6165 PM_COMPILE_NOT_POPPED(cast->predicate);
6167 // Next, we'll loop through every in clause and compile its body into
6168 // the body_seq anchor and its pattern into the cond_seq anchor. We'll
6169 // make sure the pattern knows how to jump correctly into the body if it
6170 // finds a match.
6171 for (size_t index = 0; index < cast->conditions.size; index++) {
6172 const pm_node_t *condition = cast->conditions.nodes[index];
6173 RUBY_ASSERT(PM_NODE_TYPE_P(condition, PM_IN_NODE));
6175 const pm_in_node_t *in_node = (const pm_in_node_t *) condition;
6176 const pm_line_column_t in_location = PM_NODE_START_LINE_COLUMN(parser, in_node);
6177 const pm_line_column_t pattern_location = PM_NODE_START_LINE_COLUMN(parser, in_node->pattern);
6179 if (branch_id) {
6180 PUSH_INSN(body_seq, in_location, putnil);
6183 LABEL *body_label = NEW_LABEL(in_location.line);
6184 PUSH_LABEL(body_seq, body_label);
6185 PUSH_INSN1(body_seq, in_location, adjuststack, INT2FIX(in_single_pattern ? 6 : 2));
6187 // Establish branch coverage for the in clause.
6188 if (PM_BRANCH_COVERAGE_P(iseq)) {
6189 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));
6190 add_trace_branch_coverage(iseq, body_seq, &branch_location, branch_location.beg_pos.column, branch_id++, "in", branches);
6193 if (in_node->statements != NULL) {
6194 PM_COMPILE_INTO_ANCHOR(body_seq, (const pm_node_t *) in_node->statements);
6196 else if (!popped) {
6197 PUSH_SYNTHETIC_PUTNIL(body_seq, iseq);
6200 PUSH_INSNL(body_seq, in_location, jump, end_label);
6201 LABEL *next_pattern_label = NEW_LABEL(pattern_location.line);
6203 PUSH_INSN(cond_seq, pattern_location, dup);
6204 pm_compile_pattern(iseq, scope_node, in_node->pattern, cond_seq, body_label, next_pattern_label, in_single_pattern, false, true, 2);
6205 PUSH_LABEL(cond_seq, next_pattern_label);
6206 LABEL_UNREMOVABLE(next_pattern_label);
6209 if (cast->consequent != NULL) {
6210 // If we have an `else` clause, then this becomes our fallback (and
6211 // there is no need to compile in code to potentially raise an
6212 // error).
6213 const pm_else_node_t *else_node = (const pm_else_node_t *) cast->consequent;
6215 PUSH_LABEL(cond_seq, else_label);
6216 PUSH_INSN(cond_seq, location, pop);
6217 PUSH_INSN(cond_seq, location, pop);
6219 // Establish branch coverage for the else clause.
6220 if (PM_BRANCH_COVERAGE_P(iseq)) {
6221 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));
6222 add_trace_branch_coverage(iseq, cond_seq, &branch_location, branch_location.beg_pos.column, branch_id, "else", branches);
6225 PM_COMPILE_INTO_ANCHOR(cond_seq, (const pm_node_t *) else_node);
6226 PUSH_INSNL(cond_seq, location, jump, end_label);
6227 PUSH_INSN(cond_seq, location, putnil);
6228 if (popped) PUSH_INSN(cond_seq, location, putnil);
6230 else {
6231 // Otherwise, if we do not have an `else` clause, we will compile in
6232 // the code to handle raising an appropriate error.
6233 PUSH_LABEL(cond_seq, else_label);
6235 // Establish branch coverage for the implicit else clause.
6236 add_trace_branch_coverage(iseq, cond_seq, &case_location, case_location.beg_pos.column, branch_id, "else", branches);
6238 if (in_single_pattern) {
6239 pm_compile_pattern_error_handler(iseq, scope_node, node, cond_seq, end_label, popped);
6241 else {
6242 PUSH_INSN1(cond_seq, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6243 PUSH_INSN1(cond_seq, location, putobject, rb_eNoMatchingPatternError);
6244 PUSH_INSN1(cond_seq, location, topn, INT2FIX(2));
6245 PUSH_SEND(cond_seq, location, id_core_raise, INT2FIX(2));
6247 PUSH_INSN1(cond_seq, location, adjuststack, INT2FIX(3));
6248 if (!popped) PUSH_INSN(cond_seq, location, putnil);
6249 PUSH_INSNL(cond_seq, location, jump, end_label);
6250 PUSH_INSN1(cond_seq, location, dupn, INT2FIX(1));
6251 if (popped) PUSH_INSN(cond_seq, location, putnil);
6255 // At the end of all of this compilation, we will add the code for the
6256 // conditions first, then the various bodies, then mark the end of the
6257 // entire sequence with the end label.
6258 PUSH_SEQ(ret, cond_seq);
6259 PUSH_SEQ(ret, body_seq);
6260 PUSH_LABEL(ret, end_label);
6262 return;
6264 case PM_CLASS_NODE: {
6265 // class Foo; end
6266 // ^^^^^^^^^^^^^^
6267 const pm_class_node_t *cast = (const pm_class_node_t *) node;
6269 ID class_id = pm_constant_id_lookup(scope_node, cast->name);
6270 VALUE class_name = rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(class_id)));
6272 pm_scope_node_t next_scope_node;
6273 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
6275 const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(&next_scope_node, class_name, ISEQ_TYPE_CLASS, location.line);
6276 pm_scope_node_destroy(&next_scope_node);
6278 // TODO: Once we merge constant path nodes correctly, fix this flag
6279 const int flags = VM_DEFINECLASS_TYPE_CLASS |
6280 (cast->superclass ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
6281 pm_compile_class_path(iseq, cast->constant_path, &location, ret, false, scope_node);
6283 if (cast->superclass) {
6284 PM_COMPILE_NOT_POPPED(cast->superclass);
6286 else {
6287 PUSH_INSN(ret, location, putnil);
6290 PUSH_INSN3(ret, location, defineclass, ID2SYM(class_id), class_iseq, INT2FIX(flags));
6291 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq);
6293 if (popped) PUSH_INSN(ret, location, pop);
6294 return;
6296 case PM_CLASS_VARIABLE_AND_WRITE_NODE: {
6297 // @@foo &&= bar
6298 // ^^^^^^^^^^^^^
6299 const pm_class_variable_and_write_node_t *cast = (const pm_class_variable_and_write_node_t *) node;
6300 LABEL *end_label = NEW_LABEL(location.line);
6302 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
6303 VALUE name = ID2SYM(name_id);
6305 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
6306 if (!popped) PUSH_INSN(ret, location, dup);
6308 PUSH_INSNL(ret, location, branchunless, end_label);
6309 if (!popped) PUSH_INSN(ret, location, pop);
6311 PM_COMPILE_NOT_POPPED(cast->value);
6312 if (!popped) PUSH_INSN(ret, location, dup);
6314 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
6315 PUSH_LABEL(ret, end_label);
6317 return;
6319 case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: {
6320 // @@foo += bar
6321 // ^^^^^^^^^^^^
6322 const pm_class_variable_operator_write_node_t *cast = (const pm_class_variable_operator_write_node_t *) node;
6324 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
6325 VALUE name = ID2SYM(name_id);
6327 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
6328 PM_COMPILE_NOT_POPPED(cast->value);
6330 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
6331 int flags = VM_CALL_ARGS_SIMPLE;
6332 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
6334 if (!popped) PUSH_INSN(ret, location, dup);
6335 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
6337 return;
6339 case PM_CLASS_VARIABLE_OR_WRITE_NODE: {
6340 // @@foo ||= bar
6341 // ^^^^^^^^^^^^^
6342 const pm_class_variable_or_write_node_t *cast = (const pm_class_variable_or_write_node_t *) node;
6343 LABEL *end_label = NEW_LABEL(location.line);
6344 LABEL *start_label = NEW_LABEL(location.line);
6346 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
6347 VALUE name = ID2SYM(name_id);
6349 PUSH_INSN(ret, location, putnil);
6350 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_CVAR), name, Qtrue);
6351 PUSH_INSNL(ret, location, branchunless, start_label);
6353 PUSH_INSN2(ret, location, getclassvariable, name, get_cvar_ic_value(iseq, name_id));
6354 if (!popped) PUSH_INSN(ret, location, dup);
6356 PUSH_INSNL(ret, location, branchif, end_label);
6357 if (!popped) PUSH_INSN(ret, location, pop);
6359 PUSH_LABEL(ret, start_label);
6360 PM_COMPILE_NOT_POPPED(cast->value);
6361 if (!popped) PUSH_INSN(ret, location, dup);
6363 PUSH_INSN2(ret, location, setclassvariable, name, get_cvar_ic_value(iseq, name_id));
6364 PUSH_LABEL(ret, end_label);
6366 return;
6368 case PM_CLASS_VARIABLE_READ_NODE: {
6369 // @@foo
6370 // ^^^^^
6371 if (!popped) {
6372 const pm_class_variable_read_node_t *cast = (const pm_class_variable_read_node_t *) node;
6373 ID name = pm_constant_id_lookup(scope_node, cast->name);
6374 PUSH_INSN2(ret, location, getclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
6376 return;
6378 case PM_CLASS_VARIABLE_WRITE_NODE: {
6379 // @@foo = 1
6380 // ^^^^^^^^^
6381 const pm_class_variable_write_node_t *cast = (const pm_class_variable_write_node_t *) node;
6382 PM_COMPILE_NOT_POPPED(cast->value);
6383 if (!popped) PUSH_INSN(ret, location, dup);
6385 ID name = pm_constant_id_lookup(scope_node, cast->name);
6386 PUSH_INSN2(ret, location, setclassvariable, ID2SYM(name), get_cvar_ic_value(iseq, name));
6388 return;
6390 case PM_CONSTANT_PATH_NODE: {
6391 // Foo::Bar
6392 // ^^^^^^^^
6393 VALUE parts;
6395 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache && ((parts = pm_constant_path_parts(node, scope_node)) != Qnil)) {
6396 ISEQ_BODY(iseq)->ic_size++;
6397 PUSH_INSN1(ret, location, opt_getconstant_path, parts);
6399 else {
6400 DECL_ANCHOR(prefix);
6401 INIT_ANCHOR(prefix);
6403 DECL_ANCHOR(body);
6404 INIT_ANCHOR(body);
6406 pm_compile_constant_path(iseq, node, prefix, body, popped, scope_node);
6407 if (LIST_INSN_SIZE_ZERO(prefix)) {
6408 PUSH_INSN(ret, location, putnil);
6410 else {
6411 PUSH_SEQ(ret, prefix);
6414 PUSH_SEQ(ret, body);
6417 if (popped) PUSH_INSN(ret, location, pop);
6418 return;
6420 case PM_CONSTANT_PATH_AND_WRITE_NODE: {
6421 // Foo::Bar &&= baz
6422 // ^^^^^^^^^^^^^^^^
6423 const pm_constant_path_and_write_node_t *cast = (const pm_constant_path_and_write_node_t *) node;
6424 pm_compile_constant_path_and_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
6425 return;
6427 case PM_CONSTANT_PATH_OR_WRITE_NODE: {
6428 // Foo::Bar ||= baz
6429 // ^^^^^^^^^^^^^^^^
6430 const pm_constant_path_or_write_node_t *cast = (const pm_constant_path_or_write_node_t *) node;
6431 pm_compile_constant_path_or_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
6432 return;
6434 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: {
6435 // Foo::Bar += baz
6436 // ^^^^^^^^^^^^^^^
6437 const pm_constant_path_operator_write_node_t *cast = (const pm_constant_path_operator_write_node_t *) node;
6438 pm_compile_constant_path_operator_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
6439 return;
6441 case PM_CONSTANT_PATH_WRITE_NODE: {
6442 // Foo::Bar = 1
6443 // ^^^^^^^^^^^^
6444 const pm_constant_path_write_node_t *cast = (const pm_constant_path_write_node_t *) node;
6445 pm_compile_constant_path_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
6446 return;
6448 case PM_CONSTANT_READ_NODE: {
6449 // Foo
6450 // ^^^
6451 const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) node;
6452 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
6454 pm_compile_constant_read(iseq, name, &cast->base.location, ret, scope_node);
6455 if (popped) PUSH_INSN(ret, location, pop);
6457 return;
6459 case PM_CONSTANT_AND_WRITE_NODE: {
6460 // Foo &&= bar
6461 // ^^^^^^^^^^^
6462 const pm_constant_and_write_node_t *cast = (const pm_constant_and_write_node_t *) node;
6463 pm_compile_constant_and_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
6464 return;
6466 case PM_CONSTANT_OR_WRITE_NODE: {
6467 // Foo ||= bar
6468 // ^^^^^^^^^^^
6469 const pm_constant_or_write_node_t *cast = (const pm_constant_or_write_node_t *) node;
6470 pm_compile_constant_or_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
6471 return;
6473 case PM_CONSTANT_OPERATOR_WRITE_NODE: {
6474 // Foo += bar
6475 // ^^^^^^^^^^
6476 const pm_constant_operator_write_node_t *cast = (const pm_constant_operator_write_node_t *) node;
6477 pm_compile_constant_operator_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
6478 return;
6480 case PM_CONSTANT_WRITE_NODE: {
6481 // Foo = 1
6482 // ^^^^^^^
6483 const pm_constant_write_node_t *cast = (const pm_constant_write_node_t *) node;
6484 pm_compile_constant_write_node(iseq, cast, 0, &location, ret, popped, scope_node);
6485 return;
6487 case PM_DEF_NODE: {
6488 // def foo; end
6489 // ^^^^^^^^^^^^
6491 // def self.foo; end
6492 // ^^^^^^^^^^^^^^^^^
6493 const pm_def_node_t *cast = (const pm_def_node_t *) node;
6494 ID method_name = pm_constant_id_lookup(scope_node, cast->name);
6496 pm_scope_node_t next_scope_node;
6497 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
6499 rb_iseq_t *method_iseq = NEW_ISEQ(&next_scope_node, rb_id2str(method_name), ISEQ_TYPE_METHOD, location.line);
6500 pm_scope_node_destroy(&next_scope_node);
6502 if (cast->receiver) {
6503 PM_COMPILE_NOT_POPPED(cast->receiver);
6504 PUSH_INSN2(ret, location, definesmethod, ID2SYM(method_name), method_iseq);
6506 else {
6507 PUSH_INSN2(ret, location, definemethod, ID2SYM(method_name), method_iseq);
6509 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) method_iseq);
6511 if (!popped) {
6512 PUSH_INSN1(ret, location, putobject, ID2SYM(method_name));
6515 return;
6517 case PM_DEFINED_NODE: {
6518 // defined?(a)
6519 // ^^^^^^^^^^^
6520 const pm_defined_node_t *cast = (const pm_defined_node_t *) node;
6521 pm_compile_defined_expr(iseq, cast->value, &location, ret, popped, scope_node, false);
6522 return;
6524 case PM_EMBEDDED_STATEMENTS_NODE: {
6525 // "foo #{bar}"
6526 // ^^^^^^
6527 const pm_embedded_statements_node_t *cast = (const pm_embedded_statements_node_t *) node;
6529 if (cast->statements != NULL) {
6530 PM_COMPILE((const pm_node_t *) (cast->statements));
6532 else {
6533 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
6536 if (popped) PUSH_INSN(ret, location, pop);
6537 return;
6539 case PM_EMBEDDED_VARIABLE_NODE: {
6540 // "foo #@bar"
6541 // ^^^^^
6542 const pm_embedded_variable_node_t *cast = (const pm_embedded_variable_node_t *) node;
6543 PM_COMPILE(cast->variable);
6544 return;
6546 case PM_FALSE_NODE: {
6547 // false
6548 // ^^^^^
6549 if (!popped) {
6550 PUSH_INSN1(ret, location, putobject, Qfalse);
6552 return;
6554 case PM_ENSURE_NODE: {
6555 const pm_ensure_node_t *cast = (const pm_ensure_node_t *) node;
6557 if (cast->statements != NULL) {
6558 LABEL *start = NEW_LABEL(location.line);
6559 LABEL *end = NEW_LABEL(location.line);
6560 PUSH_LABEL(ret, start);
6562 LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
6563 ISEQ_COMPILE_DATA(iseq)->end_label = end;
6565 PM_COMPILE((const pm_node_t *) cast->statements);
6566 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
6567 PUSH_LABEL(ret, end);
6570 return;
6572 case PM_ELSE_NODE: {
6573 // if foo then bar else baz end
6574 // ^^^^^^^^^^^^
6575 const pm_else_node_t *cast = (const pm_else_node_t *) node;
6577 if (cast->statements != NULL) {
6578 PM_COMPILE((const pm_node_t *) cast->statements);
6580 else if (!popped) {
6581 PUSH_SYNTHETIC_PUTNIL(ret, iseq);
6584 return;
6586 case PM_FLIP_FLOP_NODE: {
6587 // if foo .. bar; end
6588 // ^^^^^^^^^^
6589 const pm_flip_flop_node_t *cast = (const pm_flip_flop_node_t *) node;
6591 LABEL *final_label = NEW_LABEL(location.line);
6592 LABEL *then_label = NEW_LABEL(location.line);
6593 LABEL *else_label = NEW_LABEL(location.line);
6595 pm_compile_flip_flop(cast, else_label, then_label, iseq, location.line, ret, popped, scope_node);
6597 PUSH_LABEL(ret, then_label);
6598 PUSH_INSN1(ret, location, putobject, Qtrue);
6599 PUSH_INSNL(ret, location, jump, final_label);
6600 PUSH_LABEL(ret, else_label);
6601 PUSH_INSN1(ret, location, putobject, Qfalse);
6602 PUSH_LABEL(ret, final_label);
6604 return;
6606 case PM_FLOAT_NODE: {
6607 // 1.0
6608 // ^^^
6609 if (!popped) {
6610 PUSH_INSN1(ret, location, putobject, parse_float((const pm_float_node_t *) node));
6612 return;
6614 case PM_FOR_NODE: {
6615 // for foo in bar do end
6616 // ^^^^^^^^^^^^^^^^^^^^^
6617 const pm_for_node_t *cast = (const pm_for_node_t *) node;
6619 LABEL *retry_label = NEW_LABEL(location.line);
6620 LABEL *retry_end_l = NEW_LABEL(location.line);
6622 // First, compile the collection that we're going to be iterating over.
6623 PUSH_LABEL(ret, retry_label);
6624 PM_COMPILE_NOT_POPPED(cast->collection);
6626 // Next, create the new scope that is going to contain the block that
6627 // will be passed to the each method.
6628 pm_scope_node_t next_scope_node;
6629 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
6631 const rb_iseq_t *child_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location.line);
6632 pm_scope_node_destroy(&next_scope_node);
6634 const rb_iseq_t *prev_block = ISEQ_COMPILE_DATA(iseq)->current_block;
6635 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
6637 // Now, create the method call to each that will be used to iterate over
6638 // the collection, and pass the newly created iseq as the block.
6639 PUSH_SEND_WITH_BLOCK(ret, location, idEach, INT2FIX(0), child_iseq);
6640 pm_compile_retry_end_label(iseq, ret, retry_end_l);
6642 if (popped) PUSH_INSN(ret, location, pop);
6643 ISEQ_COMPILE_DATA(iseq)->current_block = prev_block;
6644 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
6645 return;
6647 case PM_FORWARDING_ARGUMENTS_NODE: {
6648 rb_bug("Cannot compile a ForwardingArgumentsNode directly\n");
6649 return;
6651 case PM_FORWARDING_SUPER_NODE: {
6652 // super
6653 // ^^^^^
6655 // super {}
6656 // ^^^^^^^^
6657 const pm_forwarding_super_node_t *cast = (const pm_forwarding_super_node_t *) node;
6658 const rb_iseq_t *block = NULL;
6660 const rb_iseq_t *previous_block = NULL;
6661 LABEL *retry_label = NULL;
6662 LABEL *retry_end_l = NULL;
6664 if (cast->block != NULL) {
6665 previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
6666 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
6668 retry_label = NEW_LABEL(location.line);
6669 retry_end_l = NEW_LABEL(location.line);
6671 PUSH_LABEL(ret, retry_label);
6674 PUSH_INSN(ret, location, putself);
6675 int flag = VM_CALL_ZSUPER | VM_CALL_SUPER | VM_CALL_FCALL;
6677 if (cast->block != NULL) {
6678 pm_scope_node_t next_scope_node;
6679 pm_scope_node_init((const pm_node_t *) cast->block, &next_scope_node, scope_node);
6681 ISEQ_COMPILE_DATA(iseq)->current_block = block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location.line);
6682 pm_scope_node_destroy(&next_scope_node);
6683 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) block);
6686 DECL_ANCHOR(args);
6687 INIT_ANCHOR(args);
6689 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
6690 const rb_iseq_t *local_iseq = body->local_iseq;
6691 const struct rb_iseq_constant_body *const local_body = ISEQ_BODY(local_iseq);
6693 int argc = 0;
6694 int depth = get_lvar_level(iseq);
6696 if (local_body->param.flags.has_lead) {
6697 /* required arguments */
6698 for (int i = 0; i < local_body->param.lead_num; i++) {
6699 int idx = local_body->local_table_size - i;
6700 PUSH_GETLOCAL(args, location, idx, depth);
6702 argc += local_body->param.lead_num;
6705 if (local_body->param.flags.has_opt) {
6706 /* optional arguments */
6707 for (int j = 0; j < local_body->param.opt_num; j++) {
6708 int idx = local_body->local_table_size - (argc + j);
6709 PUSH_GETLOCAL(args, location, idx, depth);
6711 argc += local_body->param.opt_num;
6714 if (local_body->param.flags.has_rest) {
6715 /* rest argument */
6716 int idx = local_body->local_table_size - local_body->param.rest_start;
6717 PUSH_GETLOCAL(args, location, idx, depth);
6718 PUSH_INSN1(args, location, splatarray, Qfalse);
6720 argc = local_body->param.rest_start + 1;
6721 flag |= VM_CALL_ARGS_SPLAT;
6724 if (local_body->param.flags.has_post) {
6725 /* post arguments */
6726 int post_len = local_body->param.post_num;
6727 int post_start = local_body->param.post_start;
6729 int j = 0;
6730 for (; j < post_len; j++) {
6731 int idx = local_body->local_table_size - (post_start + j);
6732 PUSH_GETLOCAL(args, location, idx, depth);
6735 if (local_body->param.flags.has_rest) {
6736 // argc remains unchanged from rest branch
6737 PUSH_INSN1(args, location, newarray, INT2FIX(j));
6738 PUSH_INSN(args, location, concatarray);
6740 else {
6741 argc = post_len + post_start;
6745 const struct rb_iseq_param_keyword *const local_keyword = local_body->param.keyword;
6746 if (local_body->param.flags.has_kw) {
6747 int local_size = local_body->local_table_size;
6748 argc++;
6750 PUSH_INSN1(args, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6752 if (local_body->param.flags.has_kwrest) {
6753 int idx = local_body->local_table_size - local_keyword->rest_start;
6754 PUSH_GETLOCAL(args, location, idx, depth);
6755 RUBY_ASSERT(local_keyword->num > 0);
6756 PUSH_SEND(args, location, rb_intern("dup"), INT2FIX(0));
6758 else {
6759 PUSH_INSN1(args, location, newhash, INT2FIX(0));
6761 int i = 0;
6762 for (; i < local_keyword->num; ++i) {
6763 ID id = local_keyword->table[i];
6764 int idx = local_size - get_local_var_idx(local_iseq, id);
6765 PUSH_INSN1(args, location, putobject, ID2SYM(id));
6766 PUSH_GETLOCAL(args, location, idx, depth);
6769 PUSH_SEND(args, location, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
6770 flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT;
6772 else if (local_body->param.flags.has_kwrest) {
6773 int idx = local_body->local_table_size - local_keyword->rest_start;
6774 PUSH_GETLOCAL(args, location, idx, depth);
6775 argc++;
6776 flag |= VM_CALL_KW_SPLAT;
6779 PUSH_SEQ(ret, args);
6780 PUSH_INSN2(ret, location, invokesuper, new_callinfo(iseq, 0, argc, flag, NULL, block != NULL), block);
6782 if (cast->block != NULL) {
6783 pm_compile_retry_end_label(iseq, ret, retry_end_l);
6784 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, block, retry_end_l);
6785 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
6788 if (popped) PUSH_INSN(ret, location, pop);
6789 return;
6791 case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: {
6792 // $foo &&= bar
6793 // ^^^^^^^^^^^^
6794 const pm_global_variable_and_write_node_t *cast = (const pm_global_variable_and_write_node_t *) node;
6795 LABEL *end_label = NEW_LABEL(location.line);
6797 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
6798 PUSH_INSN1(ret, location, getglobal, name);
6799 if (!popped) PUSH_INSN(ret, location, dup);
6801 PUSH_INSNL(ret, location, branchunless, end_label);
6802 if (!popped) PUSH_INSN(ret, location, pop);
6804 PM_COMPILE_NOT_POPPED(cast->value);
6805 if (!popped) PUSH_INSN(ret, location, dup);
6807 PUSH_INSN1(ret, location, setglobal, name);
6808 PUSH_LABEL(ret, end_label);
6810 return;
6812 case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: {
6813 // $foo += bar
6814 // ^^^^^^^^^^^
6815 const pm_global_variable_operator_write_node_t *cast = (const pm_global_variable_operator_write_node_t *) node;
6817 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
6818 PUSH_INSN1(ret, location, getglobal, name);
6819 PM_COMPILE_NOT_POPPED(cast->value);
6821 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
6822 int flags = VM_CALL_ARGS_SIMPLE;
6823 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
6825 if (!popped) PUSH_INSN(ret, location, dup);
6826 PUSH_INSN1(ret, location, setglobal, name);
6828 return;
6830 case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: {
6831 // $foo ||= bar
6832 // ^^^^^^^^^^^^
6833 const pm_global_variable_or_write_node_t *cast = (const pm_global_variable_or_write_node_t *) node;
6834 LABEL *set_label = NEW_LABEL(location.line);
6835 LABEL *end_label = NEW_LABEL(location.line);
6837 PUSH_INSN(ret, location, putnil);
6838 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
6840 PUSH_INSN3(ret, location, defined, INT2FIX(DEFINED_GVAR), name, Qtrue);
6841 PUSH_INSNL(ret, location, branchunless, set_label);
6843 PUSH_INSN1(ret, location, getglobal, name);
6844 if (!popped) PUSH_INSN(ret, location, dup);
6846 PUSH_INSNL(ret, location, branchif, end_label);
6847 if (!popped) PUSH_INSN(ret, location, pop);
6849 PUSH_LABEL(ret, set_label);
6850 PM_COMPILE_NOT_POPPED(cast->value);
6851 if (!popped) PUSH_INSN(ret, location, dup);
6853 PUSH_INSN1(ret, location, setglobal, name);
6854 PUSH_LABEL(ret, end_label);
6856 return;
6858 case PM_GLOBAL_VARIABLE_READ_NODE: {
6859 // $foo
6860 // ^^^^
6861 const pm_global_variable_read_node_t *cast = (const pm_global_variable_read_node_t *) node;
6862 VALUE name = ID2SYM(pm_constant_id_lookup(scope_node, cast->name));
6864 PUSH_INSN1(ret, location, getglobal, name);
6865 if (popped) PUSH_INSN(ret, location, pop);
6867 return;
6869 case PM_GLOBAL_VARIABLE_WRITE_NODE: {
6870 // $foo = 1
6871 // ^^^^^^^^
6872 const pm_global_variable_write_node_t *cast = (const pm_global_variable_write_node_t *) node;
6873 PM_COMPILE_NOT_POPPED(cast->value);
6874 if (!popped) PUSH_INSN(ret, location, dup);
6876 ID name = pm_constant_id_lookup(scope_node, cast->name);
6877 PUSH_INSN1(ret, location, setglobal, ID2SYM(name));
6879 return;
6881 case PM_HASH_NODE: {
6882 // {}
6883 // ^^
6885 // If every node in the hash is static, then we can compile the entire
6886 // hash now instead of later.
6887 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
6888 // We're only going to compile this node if it's not popped. If it
6889 // is popped, then we know we don't need to do anything since it's
6890 // statically known.
6891 if (!popped) {
6892 VALUE value = pm_static_literal_value(iseq, node, scope_node);
6893 PUSH_INSN1(ret, location, duphash, value);
6894 RB_OBJ_WRITTEN(iseq, Qundef, value);
6897 else {
6898 // Here since we know there are possible side-effects inside the
6899 // hash contents, we're going to build it entirely at runtime. We'll
6900 // do this by pushing all of the key-value pairs onto the stack and
6901 // then combining them with newhash.
6903 // If this hash is popped, then this serves only to ensure we enact
6904 // all side-effects (like method calls) that are contained within
6905 // the hash contents.
6906 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
6907 const pm_node_list_t *elements = &cast->elements;
6909 if (popped) {
6910 // If this hash is popped, then we can iterate through each
6911 // element and compile it. The result of each compilation will
6912 // only include the side effects of the element itself.
6913 for (size_t index = 0; index < elements->size; index++) {
6914 PM_COMPILE_POPPED(elements->nodes[index]);
6917 else {
6918 pm_compile_hash_elements(iseq, node, elements, ret, scope_node);
6922 return;
6924 case PM_IF_NODE: {
6925 // if foo then bar end
6926 // ^^^^^^^^^^^^^^^^^^^
6928 // bar if foo
6929 // ^^^^^^^^^^
6931 // foo ? bar : baz
6932 // ^^^^^^^^^^^^^^^
6933 const pm_if_node_t *cast = (const pm_if_node_t *) node;
6934 pm_compile_conditional(iseq, &location, PM_IF_NODE, (const pm_node_t *) cast, cast->statements, cast->consequent, cast->predicate, ret, popped, scope_node);
6935 return;
6937 case PM_IMAGINARY_NODE: {
6938 // 1i
6939 // ^^
6940 if (!popped) {
6941 PUSH_INSN1(ret, location, putobject, parse_imaginary((const pm_imaginary_node_t *) node));
6943 return;
6945 case PM_IMPLICIT_NODE: {
6946 // Implicit nodes mark places in the syntax tree where explicit syntax
6947 // was omitted, but implied. For example,
6949 // { foo: }
6951 // In this case a method call/local variable read is implied by virtue
6952 // of the missing value. To compile these nodes, we simply compile the
6953 // value that is implied, which is helpfully supplied by the parser.
6954 const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node;
6955 PM_COMPILE(cast->value);
6956 return;
6958 case PM_IN_NODE: {
6959 // In nodes are handled by the case match node directly, so we should
6960 // never end up hitting them through this path.
6961 rb_bug("Should not ever enter an in node directly");
6962 return;
6964 case PM_INDEX_OPERATOR_WRITE_NODE: {
6965 // foo[bar] += baz
6966 // ^^^^^^^^^^^^^^^
6967 const pm_index_operator_write_node_t *cast = (const pm_index_operator_write_node_t *) node;
6968 pm_compile_index_operator_write_node(iseq, cast, &location, ret, popped, scope_node);
6969 return;
6971 case PM_INDEX_AND_WRITE_NODE: {
6972 // foo[bar] &&= baz
6973 // ^^^^^^^^^^^^^^^^
6974 const pm_index_and_write_node_t *cast = (const pm_index_and_write_node_t *) node;
6975 pm_compile_index_control_flow_write_node(iseq, node, cast->receiver, cast->arguments, cast->block, cast->value, &location, ret, popped, scope_node);
6976 return;
6978 case PM_INDEX_OR_WRITE_NODE: {
6979 // foo[bar] ||= baz
6980 // ^^^^^^^^^^^^^^^^
6981 const pm_index_or_write_node_t *cast = (const pm_index_or_write_node_t *) node;
6982 pm_compile_index_control_flow_write_node(iseq, node, cast->receiver, cast->arguments, cast->block, cast->value, &location, ret, popped, scope_node);
6983 return;
6985 case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: {
6986 // @foo &&= bar
6987 // ^^^^^^^^^^^^
6988 const pm_instance_variable_and_write_node_t *cast = (const pm_instance_variable_and_write_node_t *) node;
6989 LABEL *end_label = NEW_LABEL(location.line);
6991 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
6992 VALUE name = ID2SYM(name_id);
6994 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
6995 if (!popped) PUSH_INSN(ret, location, dup);
6997 PUSH_INSNL(ret, location, branchunless, end_label);
6998 if (!popped) PUSH_INSN(ret, location, pop);
7000 PM_COMPILE_NOT_POPPED(cast->value);
7001 if (!popped) PUSH_INSN(ret, location, dup);
7003 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
7004 PUSH_LABEL(ret, end_label);
7006 return;
7008 case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: {
7009 // @foo += bar
7010 // ^^^^^^^^^^^
7011 const pm_instance_variable_operator_write_node_t *cast = (const pm_instance_variable_operator_write_node_t *) node;
7013 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
7014 VALUE name = ID2SYM(name_id);
7016 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
7017 PM_COMPILE_NOT_POPPED(cast->value);
7019 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
7020 int flags = VM_CALL_ARGS_SIMPLE;
7021 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(flags));
7023 if (!popped) PUSH_INSN(ret, location, dup);
7024 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
7026 return;
7028 case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: {
7029 // @foo ||= bar
7030 // ^^^^^^^^^^^^
7031 const pm_instance_variable_or_write_node_t *cast = (const pm_instance_variable_or_write_node_t *) node;
7032 LABEL *end_label = NEW_LABEL(location.line);
7034 ID name_id = pm_constant_id_lookup(scope_node, cast->name);
7035 VALUE name = ID2SYM(name_id);
7037 PUSH_INSN2(ret, location, getinstancevariable, name, get_ivar_ic_value(iseq, name_id));
7038 if (!popped) PUSH_INSN(ret, location, dup);
7040 PUSH_INSNL(ret, location, branchif, end_label);
7041 if (!popped) PUSH_INSN(ret, location, pop);
7043 PM_COMPILE_NOT_POPPED(cast->value);
7044 if (!popped) PUSH_INSN(ret, location, dup);
7046 PUSH_INSN2(ret, location, setinstancevariable, name, get_ivar_ic_value(iseq, name_id));
7047 PUSH_LABEL(ret, end_label);
7049 return;
7051 case PM_INSTANCE_VARIABLE_READ_NODE: {
7052 // @foo
7053 // ^^^^
7054 if (!popped) {
7055 const pm_instance_variable_read_node_t *cast = (const pm_instance_variable_read_node_t *) node;
7056 ID name = pm_constant_id_lookup(scope_node, cast->name);
7057 PUSH_INSN2(ret, location, getinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
7059 return;
7061 case PM_INSTANCE_VARIABLE_WRITE_NODE: {
7062 // @foo = 1
7063 // ^^^^^^^^
7064 const pm_instance_variable_write_node_t *cast = (const pm_instance_variable_write_node_t *) node;
7065 PM_COMPILE_NOT_POPPED(cast->value);
7066 if (!popped) PUSH_INSN(ret, location, dup);
7068 ID name = pm_constant_id_lookup(scope_node, cast->name);
7069 PUSH_INSN2(ret, location, setinstancevariable, ID2SYM(name), get_ivar_ic_value(iseq, name));
7071 return;
7073 case PM_INTEGER_NODE: {
7074 // 1
7075 // ^
7076 if (!popped) {
7077 PUSH_INSN1(ret, location, putobject, parse_integer((const pm_integer_node_t *) node));
7079 return;
7081 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: {
7082 // if /foo #{bar}/ then end
7083 // ^^^^^^^^^^^^
7084 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
7085 if (!popped) {
7086 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
7087 PUSH_INSN1(ret, location, putobject, regexp);
7090 else {
7091 pm_compile_regexp_dynamic(iseq, node, &((const pm_interpolated_match_last_line_node_t *) node)->parts, &location, ret, popped, scope_node);
7094 PUSH_INSN1(ret, location, getglobal, rb_id2sym(idLASTLINE));
7095 PUSH_SEND(ret, location, idEqTilde, INT2NUM(1));
7096 if (popped) PUSH_INSN(ret, location, pop);
7098 return;
7100 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
7101 // /foo #{bar}/
7102 // ^^^^^^^^^^^^
7103 if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ONCE)) {
7104 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
7105 const rb_iseq_t *block_iseq = NULL;
7106 int ise_index = ISEQ_BODY(iseq)->ise_size++;
7108 pm_scope_node_t next_scope_node;
7109 pm_scope_node_init(node, &next_scope_node, scope_node);
7111 block_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, location.line);
7112 pm_scope_node_destroy(&next_scope_node);
7114 ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
7115 PUSH_INSN2(ret, location, once, block_iseq, INT2FIX(ise_index));
7116 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
7118 if (popped) PUSH_INSN(ret, location, pop);
7119 return;
7122 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
7123 if (!popped) {
7124 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
7125 PUSH_INSN1(ret, location, putobject, regexp);
7128 else {
7129 pm_compile_regexp_dynamic(iseq, node, &((const pm_interpolated_regular_expression_node_t *) node)->parts, &location, ret, popped, scope_node);
7130 if (popped) PUSH_INSN(ret, location, pop);
7133 return;
7135 case PM_INTERPOLATED_STRING_NODE: {
7136 // "foo #{bar}"
7137 // ^^^^^^^^^^^^
7138 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
7139 if (!popped) {
7140 VALUE string = pm_static_literal_value(iseq, node, scope_node);
7142 if (PM_NODE_FLAG_P(node, PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN)) {
7143 PUSH_INSN1(ret, location, putobject, string);
7145 else if (PM_NODE_FLAG_P(node, PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE)) {
7146 PUSH_INSN1(ret, location, putstring, string);
7148 else {
7149 PUSH_INSN1(ret, location, putchilledstring, string);
7153 else {
7154 const pm_interpolated_string_node_t *cast = (const pm_interpolated_string_node_t *) node;
7155 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL);
7156 if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
7157 if (popped) PUSH_INSN(ret, location, pop);
7160 return;
7162 case PM_INTERPOLATED_SYMBOL_NODE: {
7163 // :"foo #{bar}"
7164 // ^^^^^^^^^^^^^
7165 const pm_interpolated_symbol_node_t *cast = (const pm_interpolated_symbol_node_t *) node;
7167 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
7168 if (!popped) {
7169 VALUE symbol = pm_static_literal_value(iseq, node, scope_node);
7170 PUSH_INSN1(ret, location, putobject, symbol);
7173 else {
7174 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, popped, scope_node, NULL, NULL);
7175 if (length > 1) {
7176 PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
7179 if (!popped) {
7180 PUSH_INSN(ret, location, intern);
7182 else {
7183 PUSH_INSN(ret, location, pop);
7187 return;
7189 case PM_INTERPOLATED_X_STRING_NODE: {
7190 // `foo #{bar}`
7191 // ^^^^^^^^^^^^
7192 const pm_interpolated_x_string_node_t *cast = (const pm_interpolated_x_string_node_t *) node;
7194 PUSH_INSN(ret, location, putself);
7196 int length = pm_interpolated_node_compile(iseq, &cast->parts, &location, ret, false, scope_node, NULL, NULL);
7197 if (length > 1) PUSH_INSN1(ret, location, concatstrings, INT2FIX(length));
7199 PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
7200 if (popped) PUSH_INSN(ret, location, pop);
7202 return;
7204 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
7205 // -> { it }
7206 // ^^
7207 if (!popped) {
7208 PUSH_GETLOCAL(ret, location, scope_node->local_table_for_iseq_size, 0);
7211 return;
7213 case PM_KEYWORD_HASH_NODE: {
7214 // foo(bar: baz)
7215 // ^^^^^^^^
7216 const pm_keyword_hash_node_t *cast = (const pm_keyword_hash_node_t *) node;
7217 const pm_node_list_t *elements = &cast->elements;
7219 const pm_node_t *element;
7220 PM_NODE_LIST_FOREACH(elements, index, element) {
7221 PM_COMPILE(element);
7224 if (!popped) PUSH_INSN1(ret, location, newhash, INT2FIX(elements->size * 2));
7225 return;
7227 case PM_LAMBDA_NODE: {
7228 // -> {}
7229 // ^^^^^
7230 const pm_lambda_node_t *cast = (const pm_lambda_node_t *) node;
7232 pm_scope_node_t next_scope_node;
7233 pm_scope_node_init(node, &next_scope_node, scope_node);
7235 int opening_lineno = pm_location_line_number(parser, &cast->opening_loc);
7236 const rb_iseq_t *block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, opening_lineno);
7237 pm_scope_node_destroy(&next_scope_node);
7239 VALUE argc = INT2FIX(0);
7240 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7241 PUSH_CALL_WITH_BLOCK(ret, location, idLambda, argc, block);
7242 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) block);
7244 if (popped) PUSH_INSN(ret, location, pop);
7245 return;
7247 case PM_LOCAL_VARIABLE_AND_WRITE_NODE: {
7248 // foo &&= bar
7249 // ^^^^^^^^^^^
7250 const pm_local_variable_and_write_node_t *cast = (const pm_local_variable_and_write_node_t *) node;
7251 LABEL *end_label = NEW_LABEL(location.line);
7253 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
7254 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
7255 if (!popped) PUSH_INSN(ret, location, dup);
7257 PUSH_INSNL(ret, location, branchunless, end_label);
7258 if (!popped) PUSH_INSN(ret, location, pop);
7260 PM_COMPILE_NOT_POPPED(cast->value);
7261 if (!popped) PUSH_INSN(ret, location, dup);
7263 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
7264 PUSH_LABEL(ret, end_label);
7266 return;
7268 case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: {
7269 // foo += bar
7270 // ^^^^^^^^^^
7271 const pm_local_variable_operator_write_node_t *cast = (const pm_local_variable_operator_write_node_t *) node;
7273 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
7274 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
7276 PM_COMPILE_NOT_POPPED(cast->value);
7278 ID method_id = pm_constant_id_lookup(scope_node, cast->binary_operator);
7279 PUSH_SEND_WITH_FLAG(ret, location, method_id, INT2NUM(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
7281 if (!popped) PUSH_INSN(ret, location, dup);
7282 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
7284 return;
7286 case PM_LOCAL_VARIABLE_OR_WRITE_NODE: {
7287 // foo ||= bar
7288 // ^^^^^^^^^^^
7289 const pm_local_variable_or_write_node_t *cast = (const pm_local_variable_or_write_node_t *) node;
7291 LABEL *set_label = NEW_LABEL(location.line);
7292 LABEL *end_label = NEW_LABEL(location.line);
7294 PUSH_INSN1(ret, location, putobject, Qtrue);
7295 PUSH_INSNL(ret, location, branchunless, set_label);
7297 pm_local_index_t local_index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
7298 PUSH_GETLOCAL(ret, location, local_index.index, local_index.level);
7299 if (!popped) PUSH_INSN(ret, location, dup);
7301 PUSH_INSNL(ret, location, branchif, end_label);
7302 if (!popped) PUSH_INSN(ret, location, pop);
7304 PUSH_LABEL(ret, set_label);
7305 PM_COMPILE_NOT_POPPED(cast->value);
7306 if (!popped) PUSH_INSN(ret, location, dup);
7308 PUSH_SETLOCAL(ret, location, local_index.index, local_index.level);
7309 PUSH_LABEL(ret, end_label);
7311 return;
7313 case PM_LOCAL_VARIABLE_READ_NODE: {
7314 // foo
7315 // ^^^
7316 if (!popped) {
7317 const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) node;
7318 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
7319 PUSH_GETLOCAL(ret, location, index.index, index.level);
7322 return;
7324 case PM_LOCAL_VARIABLE_WRITE_NODE: {
7325 // foo = 1
7326 // ^^^^^^^
7327 const pm_local_variable_write_node_t *cast = (const pm_local_variable_write_node_t *) node;
7328 PM_COMPILE_NOT_POPPED(cast->value);
7329 if (!popped) PUSH_INSN(ret, location, dup);
7331 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
7332 PUSH_SETLOCAL(ret, location, index.index, index.level);
7333 return;
7335 case PM_MATCH_LAST_LINE_NODE: {
7336 // if /foo/ then end
7337 // ^^^^^
7338 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
7340 PUSH_INSN1(ret, location, putobject, regexp);
7341 PUSH_INSN2(ret, location, getspecial, INT2FIX(0), INT2FIX(0));
7342 PUSH_SEND(ret, location, idEqTilde, INT2NUM(1));
7343 if (popped) PUSH_INSN(ret, location, pop);
7345 return;
7347 case PM_MATCH_PREDICATE_NODE: {
7348 // foo in bar
7349 // ^^^^^^^^^^
7350 const pm_match_predicate_node_t *cast = (const pm_match_predicate_node_t *) node;
7352 // First, allocate some stack space for the cached return value of any
7353 // calls to #deconstruct.
7354 PUSH_INSN(ret, location, putnil);
7356 // Next, compile the expression that we're going to match against.
7357 PM_COMPILE_NOT_POPPED(cast->value);
7358 PUSH_INSN(ret, location, dup);
7360 // Now compile the pattern that is going to be used to match against the
7361 // expression.
7362 LABEL *matched_label = NEW_LABEL(location.line);
7363 LABEL *unmatched_label = NEW_LABEL(location.line);
7364 LABEL *done_label = NEW_LABEL(location.line);
7365 pm_compile_pattern(iseq, scope_node, cast->pattern, ret, matched_label, unmatched_label, false, false, true, 2);
7367 // If the pattern did not match, then compile the necessary instructions
7368 // to handle pushing false onto the stack, then jump to the end.
7369 PUSH_LABEL(ret, unmatched_label);
7370 PUSH_INSN(ret, location, pop);
7371 PUSH_INSN(ret, location, pop);
7373 if (!popped) PUSH_INSN1(ret, location, putobject, Qfalse);
7374 PUSH_INSNL(ret, location, jump, done_label);
7375 PUSH_INSN(ret, location, putnil);
7377 // If the pattern did match, then compile the necessary instructions to
7378 // handle pushing true onto the stack, then jump to the end.
7379 PUSH_LABEL(ret, matched_label);
7380 PUSH_INSN1(ret, location, adjuststack, INT2FIX(2));
7381 if (!popped) PUSH_INSN1(ret, location, putobject, Qtrue);
7382 PUSH_INSNL(ret, location, jump, done_label);
7384 PUSH_LABEL(ret, done_label);
7385 return;
7387 case PM_MATCH_REQUIRED_NODE: {
7388 // foo => bar
7389 // ^^^^^^^^^^
7391 // A match required node represents pattern matching against a single
7392 // pattern using the => operator. For example,
7394 // foo => bar
7396 // This is somewhat analogous to compiling a case match statement with a
7397 // single pattern. In both cases, if the pattern fails it should
7398 // immediately raise an error.
7399 const pm_match_required_node_t *cast = (const pm_match_required_node_t *) node;
7401 LABEL *matched_label = NEW_LABEL(location.line);
7402 LABEL *unmatched_label = NEW_LABEL(location.line);
7403 LABEL *done_label = NEW_LABEL(location.line);
7405 // First, we're going to push a bunch of stuff onto the stack that is
7406 // going to serve as our scratch space.
7407 PUSH_INSN(ret, location, putnil); // key error key
7408 PUSH_INSN(ret, location, putnil); // key error matchee
7409 PUSH_INSN1(ret, location, putobject, Qfalse); // key error?
7410 PUSH_INSN(ret, location, putnil); // error string
7411 PUSH_INSN(ret, location, putnil); // deconstruct cache
7413 // Next we're going to compile the value expression such that it's on
7414 // the stack.
7415 PM_COMPILE_NOT_POPPED(cast->value);
7417 // Here we'll dup it so that it can be used for comparison, but also be
7418 // used for error handling.
7419 PUSH_INSN(ret, location, dup);
7421 // Next we'll compile the pattern. We indicate to the pm_compile_pattern
7422 // function that this is the only pattern that will be matched against
7423 // through the in_single_pattern parameter. We also indicate that the
7424 // value to compare against is 2 slots from the top of the stack (the
7425 // base_index parameter).
7426 pm_compile_pattern(iseq, scope_node, cast->pattern, ret, matched_label, unmatched_label, true, false, true, 2);
7428 // If the pattern did not match the value, then we're going to compile
7429 // in our error handler code. This will determine which error to raise
7430 // and raise it.
7431 PUSH_LABEL(ret, unmatched_label);
7432 pm_compile_pattern_error_handler(iseq, scope_node, node, ret, done_label, popped);
7434 // If the pattern did match, we'll clean up the values we've pushed onto
7435 // the stack and then push nil onto the stack if it's not popped.
7436 PUSH_LABEL(ret, matched_label);
7437 PUSH_INSN1(ret, location, adjuststack, INT2FIX(6));
7438 if (!popped) PUSH_INSN(ret, location, putnil);
7439 PUSH_INSNL(ret, location, jump, done_label);
7441 PUSH_LABEL(ret, done_label);
7442 return;
7444 case PM_MATCH_WRITE_NODE: {
7445 // /(?<foo>foo)/ =~ bar
7446 // ^^^^^^^^^^^^^^^^^^^^
7448 // Match write nodes are specialized call nodes that have a regular
7449 // expression with valid named capture groups on the left, the =~
7450 // operator, and some value on the right. The nodes themselves simply
7451 // wrap the call with the local variable targets that will be written
7452 // when the call is executed.
7453 const pm_match_write_node_t *cast = (const pm_match_write_node_t *) node;
7454 LABEL *fail_label = NEW_LABEL(location.line);
7455 LABEL *end_label = NEW_LABEL(location.line);
7457 // First, we'll compile the call so that all of its instructions are
7458 // present. Then we'll compile all of the local variable targets.
7459 PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->call);
7461 // Now, check if the match was successful. If it was, then we'll
7462 // continue on and assign local variables. Otherwise we'll skip over the
7463 // assignment code.
7464 PUSH_INSN1(ret, location, getglobal, rb_id2sym(idBACKREF));
7465 PUSH_INSN(ret, location, dup);
7466 PUSH_INSNL(ret, location, branchunless, fail_label);
7468 // If there's only a single local variable target, we can skip some of
7469 // the bookkeeping, so we'll put a special branch here.
7470 size_t targets_count = cast->targets.size;
7472 if (targets_count == 1) {
7473 const pm_node_t *target = cast->targets.nodes[0];
7474 RUBY_ASSERT(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
7476 const pm_local_variable_target_node_t *local_target = (const pm_local_variable_target_node_t *) target;
7477 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, local_target->name, local_target->depth);
7479 PUSH_INSN1(ret, location, putobject, rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name)));
7480 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
7481 PUSH_LABEL(ret, fail_label);
7482 PUSH_SETLOCAL(ret, location, index.index, index.level);
7483 if (popped) PUSH_INSN(ret, location, pop);
7484 return;
7487 DECL_ANCHOR(fail_anchor);
7488 INIT_ANCHOR(fail_anchor);
7490 // Otherwise there is more than one local variable target, so we'll need
7491 // to do some bookkeeping.
7492 for (size_t targets_index = 0; targets_index < targets_count; targets_index++) {
7493 const pm_node_t *target = cast->targets.nodes[targets_index];
7494 RUBY_ASSERT(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_TARGET_NODE));
7496 const pm_local_variable_target_node_t *local_target = (const pm_local_variable_target_node_t *) target;
7497 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, local_target->name, local_target->depth);
7499 if (((size_t) targets_index) < (targets_count - 1)) {
7500 PUSH_INSN(ret, location, dup);
7502 PUSH_INSN1(ret, location, putobject, rb_id2sym(pm_constant_id_lookup(scope_node, local_target->name)));
7503 PUSH_SEND(ret, location, idAREF, INT2FIX(1));
7504 PUSH_SETLOCAL(ret, location, index.index, index.level);
7506 PUSH_INSN(fail_anchor, location, putnil);
7507 PUSH_SETLOCAL(fail_anchor, location, index.index, index.level);
7510 // Since we matched successfully, now we'll jump to the end.
7511 PUSH_INSNL(ret, location, jump, end_label);
7513 // In the case that the match failed, we'll loop through each local
7514 // variable target and set all of them to `nil`.
7515 PUSH_LABEL(ret, fail_label);
7516 PUSH_INSN(ret, location, pop);
7517 PUSH_SEQ(ret, fail_anchor);
7519 // Finally, we can push the end label for either case.
7520 PUSH_LABEL(ret, end_label);
7521 if (popped) PUSH_INSN(ret, location, pop);
7522 return;
7524 case PM_MISSING_NODE: {
7525 rb_bug("A pm_missing_node_t should not exist in prism's AST.");
7526 return;
7528 case PM_MODULE_NODE: {
7529 // module Foo; end
7530 // ^^^^^^^^^^^^^^^
7531 const pm_module_node_t *cast = (const pm_module_node_t *) node;
7533 ID module_id = pm_constant_id_lookup(scope_node, cast->name);
7534 VALUE module_name = rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(module_id)));
7536 pm_scope_node_t next_scope_node;
7537 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
7539 const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(&next_scope_node, module_name, ISEQ_TYPE_CLASS, location.line);
7540 pm_scope_node_destroy(&next_scope_node);
7542 const int flags = VM_DEFINECLASS_TYPE_MODULE | pm_compile_class_path(iseq, cast->constant_path, &location, ret, false, scope_node);
7543 PUSH_INSN(ret, location, putnil);
7544 PUSH_INSN3(ret, location, defineclass, ID2SYM(module_id), module_iseq, INT2FIX(flags));
7545 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) module_iseq);
7547 if (popped) PUSH_INSN(ret, location, pop);
7548 return;
7550 case PM_REQUIRED_PARAMETER_NODE: {
7551 // def foo(bar); end
7552 // ^^^
7553 const pm_required_parameter_node_t *cast = (const pm_required_parameter_node_t *) node;
7554 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, 0);
7556 PUSH_SETLOCAL(ret, location, index.index, index.level);
7557 return;
7559 case PM_MULTI_WRITE_NODE: {
7560 // foo, bar = baz
7561 // ^^^^^^^^^^^^^^
7563 // A multi write node represents writing to multiple values using an =
7564 // operator. Importantly these nodes are only parsed when the left-hand
7565 // side of the operator has multiple targets. The right-hand side of the
7566 // operator having multiple targets represents an implicit array
7567 // instead.
7568 const pm_multi_write_node_t *cast = (const pm_multi_write_node_t *) node;
7570 DECL_ANCHOR(writes);
7571 INIT_ANCHOR(writes);
7573 DECL_ANCHOR(cleanup);
7574 INIT_ANCHOR(cleanup);
7576 pm_multi_target_state_t state = { 0 };
7577 state.position = popped ? 0 : 1;
7578 pm_compile_multi_target_node(iseq, node, ret, writes, cleanup, scope_node, &state);
7580 PM_COMPILE_NOT_POPPED(cast->value);
7581 if (!popped) PUSH_INSN(ret, location, dup);
7583 PUSH_SEQ(ret, writes);
7584 if (!popped && state.stack_size >= 1) {
7585 // Make sure the value on the right-hand side of the = operator is
7586 // being returned before we pop the parent expressions.
7587 PUSH_INSN1(ret, location, setn, INT2FIX(state.stack_size));
7590 // Now, we need to go back and modify the topn instructions in order to
7591 // ensure they can correctly retrieve the parent expressions.
7592 pm_multi_target_state_update(&state);
7594 PUSH_SEQ(ret, cleanup);
7595 return;
7597 case PM_NEXT_NODE: {
7598 // next
7599 // ^^^^
7601 // next foo
7602 // ^^^^^^^^
7603 const pm_next_node_t *cast = (const pm_next_node_t *) node;
7605 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
7606 LABEL *splabel = NEW_LABEL(0);
7607 PUSH_LABEL(ret, splabel);
7609 if (cast->arguments) {
7610 PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->arguments);
7612 else {
7613 PUSH_INSN(ret, location, putnil);
7615 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
7617 PUSH_ADJUST(ret, location, ISEQ_COMPILE_DATA(iseq)->redo_label);
7618 PUSH_INSNL(ret, location, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
7620 PUSH_ADJUST_RESTORE(ret, splabel);
7621 if (!popped) PUSH_INSN(ret, location, putnil);
7623 else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
7624 LABEL *splabel = NEW_LABEL(0);
7626 PUSH_LABEL(ret, splabel);
7627 PUSH_ADJUST(ret, location, ISEQ_COMPILE_DATA(iseq)->start_label);
7629 if (cast->arguments != NULL) {
7630 PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->arguments);
7632 else {
7633 PUSH_INSN(ret, location, putnil);
7636 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
7637 PUSH_INSNL(ret, location, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
7638 PUSH_ADJUST_RESTORE(ret, splabel);
7639 splabel->unremovable = FALSE;
7641 if (!popped) PUSH_INSN(ret, location, putnil);
7643 else {
7644 const rb_iseq_t *ip = iseq;
7645 unsigned long throw_flag = 0;
7647 while (ip) {
7648 if (!ISEQ_COMPILE_DATA(ip)) {
7649 ip = 0;
7650 break;
7653 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
7654 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
7655 /* while loop */
7656 break;
7658 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
7659 break;
7661 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
7662 COMPILE_ERROR(iseq, location.line, "Can't escape from eval with next");
7663 return;
7666 ip = ISEQ_BODY(ip)->parent_iseq;
7668 if (ip != 0) {
7669 if (cast->arguments) {
7670 PM_COMPILE_NOT_POPPED((const pm_node_t *) cast->arguments);
7672 else {
7673 PUSH_INSN(ret, location, putnil);
7676 PUSH_INSN1(ret, location, throw, INT2FIX(throw_flag | TAG_NEXT));
7677 if (popped) PUSH_INSN(ret, location, pop);
7679 else {
7680 COMPILE_ERROR(iseq, location.line, "Invalid next");
7681 return;
7685 return;
7687 case PM_NIL_NODE: {
7688 // nil
7689 // ^^^
7690 if (!popped) {
7691 PUSH_INSN(ret, location, putnil);
7694 return;
7696 case PM_NO_KEYWORDS_PARAMETER_NODE: {
7697 // def foo(**nil); end
7698 // ^^^^^
7699 ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg = TRUE;
7700 return;
7702 case PM_NUMBERED_REFERENCE_READ_NODE: {
7703 // $1
7704 // ^^
7705 if (!popped) {
7706 uint32_t reference_number = ((const pm_numbered_reference_read_node_t *) node)->number;
7708 if (reference_number > 0) {
7709 PUSH_INSN2(ret, location, getspecial, INT2FIX(1), INT2FIX(reference_number << 1));
7711 else {
7712 PUSH_INSN(ret, location, putnil);
7716 return;
7718 case PM_OR_NODE: {
7719 // a or b
7720 // ^^^^^^
7721 const pm_or_node_t *cast = (const pm_or_node_t *) node;
7723 LABEL *end_label = NEW_LABEL(location.line);
7724 PM_COMPILE_NOT_POPPED(cast->left);
7726 if (!popped) PUSH_INSN(ret, location, dup);
7727 PUSH_INSNL(ret, location, branchif, end_label);
7729 if (!popped) PUSH_INSN(ret, location, pop);
7730 PM_COMPILE(cast->right);
7731 PUSH_LABEL(ret, end_label);
7733 return;
7735 case PM_OPTIONAL_PARAMETER_NODE: {
7736 // def foo(bar = 1); end
7737 // ^^^^^^^
7738 const pm_optional_parameter_node_t *cast = (const pm_optional_parameter_node_t *) node;
7739 PM_COMPILE_NOT_POPPED(cast->value);
7741 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, 0);
7742 PUSH_SETLOCAL(ret, location, index.index, index.level);
7744 return;
7746 case PM_PARENTHESES_NODE: {
7747 // ()
7748 // ^^
7750 // (1)
7751 // ^^^
7752 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
7754 if (cast->body != NULL) {
7755 PM_COMPILE(cast->body);
7757 else if (!popped) {
7758 PUSH_INSN(ret, location, putnil);
7761 return;
7763 case PM_PRE_EXECUTION_NODE: {
7764 // BEGIN {}
7765 // ^^^^^^^^
7766 const pm_pre_execution_node_t *cast = (const pm_pre_execution_node_t *) node;
7768 LINK_ANCHOR *outer_pre = scope_node->pre_execution_anchor;
7769 RUBY_ASSERT(outer_pre != NULL);
7771 // BEGIN{} nodes can be nested, so here we're going to do the same thing
7772 // that we did for the top-level compilation where we create two
7773 // anchors and then join them in the correct order into the resulting
7774 // anchor.
7775 DECL_ANCHOR(inner_pre);
7776 INIT_ANCHOR(inner_pre);
7777 scope_node->pre_execution_anchor = inner_pre;
7779 DECL_ANCHOR(inner_body);
7780 INIT_ANCHOR(inner_body);
7782 if (cast->statements != NULL) {
7783 const pm_node_list_t *body = &cast->statements->body;
7785 for (size_t index = 0; index < body->size; index++) {
7786 pm_compile_node(iseq, body->nodes[index], inner_body, true, scope_node);
7790 if (!popped) {
7791 PUSH_INSN(inner_body, location, putnil);
7794 // Now that everything has been compiled, join both anchors together
7795 // into the correct outer pre execution anchor, and reset the value so
7796 // that subsequent BEGIN{} nodes can be compiled correctly.
7797 PUSH_SEQ(outer_pre, inner_pre);
7798 PUSH_SEQ(outer_pre, inner_body);
7799 scope_node->pre_execution_anchor = outer_pre;
7801 return;
7803 case PM_POST_EXECUTION_NODE: {
7804 // END {}
7805 // ^^^^^^
7806 const rb_iseq_t *child_iseq;
7807 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
7809 pm_scope_node_t next_scope_node;
7810 pm_scope_node_init(node, &next_scope_node, scope_node);
7811 child_iseq = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, lineno);
7812 pm_scope_node_destroy(&next_scope_node);
7814 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
7816 int is_index = ISEQ_BODY(iseq)->ise_size++;
7817 PUSH_INSN2(ret, location, once, child_iseq, INT2FIX(is_index));
7818 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) child_iseq);
7819 if (popped) PUSH_INSN(ret, location, pop);
7821 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
7823 return;
7825 case PM_RANGE_NODE: {
7826 // 0..5
7827 // ^^^^
7828 const pm_range_node_t *cast = (const pm_range_node_t *) node;
7829 bool exclude_end = PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END);
7831 if (pm_optimizable_range_item_p(cast->left) && pm_optimizable_range_item_p(cast->right)) {
7832 if (!popped) {
7833 const pm_node_t *left = cast->left;
7834 const pm_node_t *right = cast->right;
7836 VALUE val = rb_range_new(
7837 (left && PM_NODE_TYPE_P(left, PM_INTEGER_NODE)) ? parse_integer((const pm_integer_node_t *) left) : Qnil,
7838 (right && PM_NODE_TYPE_P(right, PM_INTEGER_NODE)) ? parse_integer((const pm_integer_node_t *) right) : Qnil,
7839 exclude_end
7842 PUSH_INSN1(ret, location, putobject, val);
7845 else {
7846 if (cast->left == NULL) {
7847 PUSH_INSN(ret, location, putnil);
7849 else {
7850 PM_COMPILE(cast->left);
7853 if (cast->right == NULL) {
7854 PUSH_INSN(ret, location, putnil);
7856 else {
7857 PM_COMPILE(cast->right);
7860 if (!popped) {
7861 PUSH_INSN1(ret, location, newrange, INT2FIX(exclude_end ? 1 : 0));
7864 return;
7866 case PM_RATIONAL_NODE: {
7867 // 1r
7868 // ^^
7869 if (!popped) {
7870 PUSH_INSN1(ret, location, putobject, parse_rational((const pm_rational_node_t *) node));
7872 return;
7874 case PM_REDO_NODE: {
7875 // redo
7876 // ^^^^
7877 if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
7878 LABEL *splabel = NEW_LABEL(0);
7880 PUSH_LABEL(ret, splabel);
7881 PUSH_ADJUST(ret, location, ISEQ_COMPILE_DATA(iseq)->redo_label);
7882 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
7884 PUSH_INSNL(ret, location, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
7885 PUSH_ADJUST_RESTORE(ret, splabel);
7886 if (!popped) PUSH_INSN(ret, location, putnil);
7888 else if (ISEQ_BODY(iseq)->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
7889 LABEL *splabel = NEW_LABEL(0);
7891 PUSH_LABEL(ret, splabel);
7892 pm_add_ensure_iseq(ret, iseq, 0, scope_node);
7893 PUSH_ADJUST(ret, location, ISEQ_COMPILE_DATA(iseq)->start_label);
7895 PUSH_INSNL(ret, location, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
7896 PUSH_ADJUST_RESTORE(ret, splabel);
7897 if (!popped) PUSH_INSN(ret, location, putnil);
7899 else {
7900 const rb_iseq_t *ip = iseq;
7902 while (ip) {
7903 if (!ISEQ_COMPILE_DATA(ip)) {
7904 ip = 0;
7905 break;
7908 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
7909 break;
7911 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
7912 break;
7914 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
7915 COMPILE_ERROR(iseq, location.line, "Can't escape from eval with redo");
7916 return;
7919 ip = ISEQ_BODY(ip)->parent_iseq;
7922 if (ip != 0) {
7923 PUSH_INSN(ret, location, putnil);
7924 PUSH_INSN1(ret, location, throw, INT2FIX(VM_THROW_NO_ESCAPE_FLAG | TAG_REDO));
7925 if (popped) PUSH_INSN(ret, location, pop);
7927 else {
7928 COMPILE_ERROR(iseq, location.line, "Invalid redo");
7929 return;
7932 return;
7934 case PM_REGULAR_EXPRESSION_NODE: {
7935 // /foo/
7936 // ^^^^^
7937 if (!popped) {
7938 VALUE regexp = pm_static_literal_value(iseq, node, scope_node);
7939 PUSH_INSN1(ret, location, putobject, regexp);
7941 return;
7943 case PM_RESCUE_NODE: {
7944 // begin; rescue; end
7945 // ^^^^^^^
7946 const pm_rescue_node_t *cast = (const pm_rescue_node_t *) node;
7947 iseq_set_exception_local_table(iseq);
7949 // First, establish the labels that we need to be able to jump to within
7950 // this compilation block.
7951 LABEL *exception_match_label = NEW_LABEL(location.line);
7952 LABEL *rescue_end_label = NEW_LABEL(location.line);
7954 // Next, compile each of the exceptions that we're going to be
7955 // handling. For each one, we'll add instructions to check if the
7956 // exception matches the raised one, and if it does then jump to the
7957 // exception_match_label label. Otherwise it will fall through to the
7958 // subsequent check. If there are no exceptions, we'll only check
7959 // StandardError.
7960 const pm_node_list_t *exceptions = &cast->exceptions;
7962 if (exceptions->size > 0) {
7963 for (size_t index = 0; index < exceptions->size; index++) {
7964 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
7965 PM_COMPILE(exceptions->nodes[index]);
7966 int checkmatch_flags = VM_CHECKMATCH_TYPE_RESCUE;
7967 if (PM_NODE_TYPE_P(exceptions->nodes[index], PM_SPLAT_NODE)) {
7968 checkmatch_flags |= VM_CHECKMATCH_ARRAY;
7970 PUSH_INSN1(ret, location, checkmatch, INT2FIX(checkmatch_flags));
7971 PUSH_INSNL(ret, location, branchif, exception_match_label);
7974 else {
7975 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
7976 PUSH_INSN1(ret, location, putobject, rb_eStandardError);
7977 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
7978 PUSH_INSNL(ret, location, branchif, exception_match_label);
7981 // If none of the exceptions that we are matching against matched, then
7982 // we'll jump straight to the rescue_end_label label.
7983 PUSH_INSNL(ret, location, jump, rescue_end_label);
7985 // Here we have the exception_match_label, which is where the
7986 // control-flow goes in the case that one of the exceptions matched.
7987 // Here we will compile the instructions to handle the exception.
7988 PUSH_LABEL(ret, exception_match_label);
7989 PUSH_TRACE(ret, RUBY_EVENT_RESCUE);
7991 // If we have a reference to the exception, then we'll compile the write
7992 // into the instruction sequence. This can look quite different
7993 // depending on the kind of write being performed.
7994 if (cast->reference) {
7995 DECL_ANCHOR(writes);
7996 INIT_ANCHOR(writes);
7998 DECL_ANCHOR(cleanup);
7999 INIT_ANCHOR(cleanup);
8001 pm_compile_target_node(iseq, cast->reference, ret, writes, cleanup, scope_node, NULL);
8002 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
8004 PUSH_SEQ(ret, writes);
8005 PUSH_SEQ(ret, cleanup);
8008 // If we have statements to execute, we'll compile them here. Otherwise
8009 // we'll push nil onto the stack.
8010 if (cast->statements) {
8011 // We'll temporarily remove the end_label location from the iseq
8012 // when compiling the statements so that next/redo statements
8013 // inside the body will throw to the correct place instead of
8014 // jumping straight to the end of this iseq
8015 LABEL *prev_end = ISEQ_COMPILE_DATA(iseq)->end_label;
8016 ISEQ_COMPILE_DATA(iseq)->end_label = NULL;
8018 PM_COMPILE((const pm_node_t *) cast->statements);
8020 // Now restore the end_label
8021 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end;
8023 else {
8024 PUSH_INSN(ret, location, putnil);
8027 PUSH_INSN(ret, location, leave);
8029 // Here we'll insert the rescue_end_label label, which is jumped to if
8030 // none of the exceptions matched. It will cause the control-flow to
8031 // either jump to the next rescue clause or it will fall through to the
8032 // subsequent instruction returning the raised error.
8033 PUSH_LABEL(ret, rescue_end_label);
8034 if (cast->consequent) {
8035 PM_COMPILE((const pm_node_t *) cast->consequent);
8037 else {
8038 PUSH_GETLOCAL(ret, location, 1, 0);
8041 return;
8043 case PM_RESCUE_MODIFIER_NODE: {
8044 // foo rescue bar
8045 // ^^^^^^^^^^^^^^
8046 const pm_rescue_modifier_node_t *cast = (const pm_rescue_modifier_node_t *) node;
8048 pm_scope_node_t rescue_scope_node;
8049 pm_scope_node_init((const pm_node_t *) cast, &rescue_scope_node, scope_node);
8051 rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(
8052 &rescue_scope_node,
8053 rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label),
8054 ISEQ_TYPE_RESCUE,
8055 pm_node_line_number(parser, cast->rescue_expression)
8058 pm_scope_node_destroy(&rescue_scope_node);
8060 LABEL *lstart = NEW_LABEL(location.line);
8061 LABEL *lend = NEW_LABEL(location.line);
8062 LABEL *lcont = NEW_LABEL(location.line);
8064 lstart->rescued = LABEL_RESCUE_BEG;
8065 lend->rescued = LABEL_RESCUE_END;
8066 PUSH_LABEL(ret, lstart);
8067 PM_COMPILE_NOT_POPPED(cast->expression);
8068 PUSH_LABEL(ret, lend);
8069 PUSH_INSN(ret, location, nop);
8070 PUSH_LABEL(ret, lcont);
8071 if (popped) PUSH_INSN(ret, location, pop);
8073 PUSH_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
8074 PUSH_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
8075 return;
8077 case PM_RETURN_NODE: {
8078 // return
8079 // ^^^^^^
8081 // return 1
8082 // ^^^^^^^^
8083 const pm_return_node_t *cast = (const pm_return_node_t *) node;
8084 const pm_arguments_node_t *arguments = cast->arguments;
8086 if (PM_NODE_FLAG_P(cast, PM_RETURN_NODE_FLAGS_REDUNDANT)) {
8087 if (arguments) {
8088 PM_COMPILE_NOT_POPPED((const pm_node_t *) arguments);
8090 else {
8091 PUSH_INSN(ret, location, putnil);
8094 else {
8095 enum rb_iseq_type type = ISEQ_BODY(iseq)->type;
8096 LABEL *splabel = 0;
8098 const rb_iseq_t *parent_iseq = iseq;
8099 enum rb_iseq_type parent_type = ISEQ_BODY(parent_iseq)->type;
8100 while (parent_type == ISEQ_TYPE_RESCUE || parent_type == ISEQ_TYPE_ENSURE) {
8101 if (!(parent_iseq = ISEQ_BODY(parent_iseq)->parent_iseq)) break;
8102 parent_type = ISEQ_BODY(parent_iseq)->type;
8105 switch (parent_type) {
8106 case ISEQ_TYPE_TOP:
8107 case ISEQ_TYPE_MAIN:
8108 if (arguments) {
8109 rb_warn("argument of top-level return is ignored");
8111 if (parent_iseq == iseq) {
8112 type = ISEQ_TYPE_METHOD;
8114 break;
8115 default:
8116 break;
8119 if (type == ISEQ_TYPE_METHOD) {
8120 splabel = NEW_LABEL(0);
8121 PUSH_LABEL(ret, splabel);
8122 PUSH_ADJUST(ret, location, 0);
8125 if (arguments) {
8126 PM_COMPILE_NOT_POPPED((const pm_node_t *) arguments);
8128 else {
8129 PUSH_INSN(ret, location, putnil);
8132 if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
8133 pm_add_ensure_iseq(ret, iseq, 1, scope_node);
8134 PUSH_TRACE(ret, RUBY_EVENT_RETURN);
8135 PUSH_INSN(ret, location, leave);
8136 PUSH_ADJUST_RESTORE(ret, splabel);
8137 if (!popped) PUSH_INSN(ret, location, putnil);
8139 else {
8140 PUSH_INSN1(ret, location, throw, INT2FIX(TAG_RETURN));
8141 if (popped) PUSH_INSN(ret, location, pop);
8145 return;
8147 case PM_RETRY_NODE: {
8148 // retry
8149 // ^^^^^
8150 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
8151 PUSH_INSN(ret, location, putnil);
8152 PUSH_INSN1(ret, location, throw, INT2FIX(TAG_RETRY));
8153 if (popped) PUSH_INSN(ret, location, pop);
8155 else {
8156 COMPILE_ERROR(iseq, location.line, "Invalid retry");
8157 return;
8159 return;
8161 case PM_SCOPE_NODE: {
8162 pm_scope_node_t *scope_node = (pm_scope_node_t *) node;
8163 pm_constant_id_list_t *locals = &scope_node->locals;
8165 pm_parameters_node_t *parameters_node = NULL;
8166 pm_node_list_t *keywords_list = NULL;
8167 pm_node_list_t *optionals_list = NULL;
8168 pm_node_list_t *posts_list = NULL;
8169 pm_node_list_t *requireds_list = NULL;
8170 pm_node_list_t *block_locals = NULL;
8171 bool trailing_comma = false;
8173 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
8175 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE)) {
8176 ADD_TRACE(ret, RUBY_EVENT_CLASS);
8179 if (scope_node->parameters) {
8180 switch (PM_NODE_TYPE(scope_node->parameters)) {
8181 case PM_BLOCK_PARAMETERS_NODE: {
8182 pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) scope_node->parameters;
8183 parameters_node = cast->parameters;
8184 block_locals = &cast->locals;
8186 if (parameters_node) {
8187 if (parameters_node->rest && PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE)) {
8188 trailing_comma = true;
8191 break;
8193 case PM_PARAMETERS_NODE: {
8194 parameters_node = (pm_parameters_node_t *) scope_node->parameters;
8195 break;
8197 case PM_NUMBERED_PARAMETERS_NODE: {
8198 uint32_t maximum = ((const pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
8199 body->param.lead_num = maximum;
8200 body->param.flags.ambiguous_param0 = maximum == 1;
8201 break;
8203 case PM_IT_PARAMETERS_NODE:
8204 body->param.lead_num = 1;
8205 body->param.flags.ambiguous_param0 = true;
8206 break;
8207 default:
8208 rb_bug("Unexpected node type for parameters: %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
8212 struct rb_iseq_param_keyword *keyword = NULL;
8214 if (parameters_node) {
8215 optionals_list = &parameters_node->optionals;
8216 requireds_list = &parameters_node->requireds;
8217 keywords_list = &parameters_node->keywords;
8218 posts_list = &parameters_node->posts;
8220 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))) {
8221 body->param.opt_num = 0;
8223 else {
8224 body->param.lead_num = 0;
8225 body->param.opt_num = 0;
8228 //********STEP 1**********
8229 // Goal: calculate the table size for the locals, accounting for
8230 // hidden variables and multi target nodes
8231 size_t locals_size = locals->size;
8233 // Index lookup table buffer size is only the number of the locals
8234 st_table *index_lookup_table = st_init_numtable();
8236 int table_size = (int) locals_size;
8238 // For nodes have a hidden iteration variable. We add that to the local
8239 // table size here.
8240 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) table_size++;
8242 if (keywords_list && keywords_list->size) {
8243 table_size++;
8246 if (requireds_list) {
8247 for (size_t i = 0; i < requireds_list->size; i++) {
8248 // For each MultiTargetNode, we're going to have one
8249 // additional anonymous local not represented in the locals table
8250 // We want to account for this in our table size
8251 pm_node_t *required = requireds_list->nodes[i];
8252 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
8253 table_size++;
8255 else if (PM_NODE_TYPE_P(required, PM_REQUIRED_PARAMETER_NODE)) {
8256 if (PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
8257 table_size++;
8263 // If we have the `it` implicit local variable, we need to account for
8264 // it in the local table size.
8265 if (scope_node->parameters != NULL && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
8266 table_size++;
8269 // Ensure there is enough room in the local table for any
8270 // parameters that have been repeated
8271 // ex: def underscore_parameters(_, _ = 1, _ = 2); _; end
8272 // ^^^^^^^^^^^^
8273 if (optionals_list && optionals_list->size) {
8274 for (size_t i = 0; i < optionals_list->size; i++) {
8275 pm_node_t * node = optionals_list->nodes[i];
8276 if (PM_NODE_FLAG_P(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
8277 table_size++;
8282 // If we have an anonymous "rest" node, we'll need to increase the local
8283 // table size to take it in to account.
8284 // def m(foo, *, bar)
8285 // ^
8286 if (parameters_node) {
8287 if (parameters_node->rest) {
8288 if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
8289 if (!((const pm_rest_parameter_node_t *) parameters_node->rest)->name || PM_NODE_FLAG_P(parameters_node->rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
8290 table_size++;
8295 // def foo(_, **_); _; end
8296 // ^^^
8297 if (parameters_node->keyword_rest) {
8298 // def foo(...); end
8299 // ^^^
8300 // When we have a `...` as the keyword_rest, it's a forwarding_parameter_node and
8301 // we need to leave space for 4 locals: *, **, &, ...
8302 if (PM_NODE_TYPE_P(parameters_node->keyword_rest, PM_FORWARDING_PARAMETER_NODE)) {
8303 table_size += 4;
8305 else {
8306 const pm_keyword_rest_parameter_node_t *kw_rest = (const pm_keyword_rest_parameter_node_t *) parameters_node->keyword_rest;
8308 // If it's anonymous or repeated, then we need to allocate stack space
8309 if (!kw_rest->name || PM_NODE_FLAG_P(kw_rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
8310 table_size++;
8316 if (posts_list) {
8317 for (size_t i = 0; i < posts_list->size; i++) {
8318 // For each MultiTargetNode, we're going to have one
8319 // additional anonymous local not represented in the locals table
8320 // We want to account for this in our table size
8321 pm_node_t *required = posts_list->nodes[i];
8322 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE) || PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
8323 table_size++;
8328 if (keywords_list && keywords_list->size) {
8329 for (size_t i = 0; i < keywords_list->size; i++) {
8330 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
8331 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
8332 table_size++;
8337 if (parameters_node && parameters_node->block) {
8338 const pm_block_parameter_node_t *block_node = (const pm_block_parameter_node_t *) parameters_node->block;
8340 if (PM_NODE_FLAG_P(block_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER) || !block_node->name) {
8341 table_size++;
8345 // We can create local_table_for_iseq with the correct size
8346 VALUE idtmp = 0;
8347 rb_ast_id_table_t *local_table_for_iseq = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
8348 local_table_for_iseq->size = table_size;
8350 //********END OF STEP 1**********
8352 //********STEP 2**********
8353 // Goal: populate iv index table as well as local table, keeping the
8354 // layout of the local table consistent with the layout of the
8355 // stack when calling the method
8357 // Do a first pass on all of the parameters, setting their values in
8358 // the local_table_for_iseq, _except_ for Multis who get a hidden
8359 // variable in this step, and will get their names inserted in step 3
8361 // local_index is a cursor that keeps track of the current
8362 // index into local_table_for_iseq. The local table is actually a list,
8363 // and the order of that list must match the order of the items pushed
8364 // on the stack. We need to take in to account things pushed on the
8365 // stack that _might not have a name_ (for example array destructuring).
8366 // This index helps us know which item we're dealing with and also give
8367 // those anonymous items temporary names (as below)
8368 int local_index = 0;
8370 // Here we figure out local table indices and insert them in to the
8371 // index lookup table and local tables.
8373 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8374 // ^^^^^^^^^^^^^
8375 if (requireds_list && requireds_list->size) {
8376 for (size_t i = 0; i < requireds_list->size; i++, local_index++) {
8377 ID local;
8379 // For each MultiTargetNode, we're going to have one additional
8380 // anonymous local not represented in the locals table. We want
8381 // to account for this in our table size.
8382 pm_node_t *required = requireds_list->nodes[i];
8384 switch (PM_NODE_TYPE(required)) {
8385 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8386 // ^^^^^^^^^^
8387 case PM_MULTI_TARGET_NODE: {
8388 local = rb_make_temporary_id(local_index);
8389 local_table_for_iseq->ids[local_index] = local;
8390 break;
8392 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8393 // ^
8394 case PM_REQUIRED_PARAMETER_NODE: {
8395 const pm_required_parameter_node_t *param = (const pm_required_parameter_node_t *) required;
8397 if (PM_NODE_FLAG_P(required, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
8398 ID local = pm_constant_id_lookup(scope_node, param->name);
8399 local_table_for_iseq->ids[local_index] = local;
8401 else {
8402 pm_insert_local_index(param->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
8405 break;
8407 default: {
8408 rb_bug("Unsupported node in requireds in parameters %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
8413 body->param.lead_num = (int) requireds_list->size;
8414 body->param.flags.has_lead = true;
8417 if (scope_node->parameters != NULL && PM_NODE_TYPE_P(scope_node->parameters, PM_IT_PARAMETERS_NODE)) {
8418 ID local = rb_make_temporary_id(local_index);
8419 local_table_for_iseq->ids[local_index++] = local;
8422 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8423 // ^^^^^
8424 if (optionals_list && optionals_list->size) {
8425 body->param.opt_num = (int) optionals_list->size;
8426 body->param.flags.has_opt = true;
8428 for (size_t i = 0; i < optionals_list->size; i++, local_index++) {
8429 pm_node_t * node = optionals_list->nodes[i];
8430 pm_constant_id_t name = ((const pm_optional_parameter_node_t *) node)->name;
8432 if (PM_NODE_FLAG_P(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
8433 ID local = pm_constant_id_lookup(scope_node, name);
8434 local_table_for_iseq->ids[local_index] = local;
8436 else {
8437 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
8442 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8443 // ^^
8444 if (parameters_node && parameters_node->rest) {
8445 body->param.rest_start = local_index;
8447 // If there's a trailing comma, we'll have an implicit rest node,
8448 // and we don't want it to impact the rest variables on param
8449 if (!(PM_NODE_TYPE_P(parameters_node->rest, PM_IMPLICIT_REST_NODE))) {
8450 body->param.flags.has_rest = true;
8451 RUBY_ASSERT(body->param.rest_start != -1);
8453 pm_constant_id_t name = ((const pm_rest_parameter_node_t *) parameters_node->rest)->name;
8455 if (name) {
8456 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8457 // ^^
8458 if (PM_NODE_FLAG_P(parameters_node->rest, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
8459 ID local = pm_constant_id_lookup(scope_node, name);
8460 local_table_for_iseq->ids[local_index] = local;
8462 else {
8463 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
8466 else {
8467 // def foo(a, (b, *c, d), e = 1, *, g, (h, *i, j), k:, l: 1, **m, &n)
8468 // ^
8469 pm_insert_local_special(idMULT, local_index, index_lookup_table, local_table_for_iseq);
8472 local_index++;
8476 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8477 // ^^^^^^^^^^^^^
8478 if (posts_list && posts_list->size) {
8479 body->param.post_num = (int) posts_list->size;
8480 body->param.post_start = local_index;
8481 body->param.flags.has_post = true;
8483 for (size_t i = 0; i < posts_list->size; i++, local_index++) {
8484 ID local;
8486 // For each MultiTargetNode, we're going to have one additional
8487 // anonymous local not represented in the locals table. We want
8488 // to account for this in our table size.
8489 const pm_node_t *post_node = posts_list->nodes[i];
8491 switch (PM_NODE_TYPE(post_node)) {
8492 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8493 // ^^^^^^^^^^
8494 case PM_MULTI_TARGET_NODE: {
8495 local = rb_make_temporary_id(local_index);
8496 local_table_for_iseq->ids[local_index] = local;
8497 break;
8499 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8500 // ^
8501 case PM_REQUIRED_PARAMETER_NODE: {
8502 const pm_required_parameter_node_t *param = (const pm_required_parameter_node_t *) post_node;
8504 if (PM_NODE_FLAG_P(param, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
8505 ID local = pm_constant_id_lookup(scope_node, param->name);
8506 local_table_for_iseq->ids[local_index] = local;
8508 else {
8509 pm_insert_local_index(param->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
8511 break;
8513 default: {
8514 rb_bug("Unsupported node in posts in parameters %s", pm_node_type_to_str(PM_NODE_TYPE(node)));
8520 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8521 // ^^^^^^^^
8522 // Keywords create an internal variable on the parse tree
8523 if (keywords_list && keywords_list->size) {
8524 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
8525 keyword->num = (int) keywords_list->size;
8527 body->param.flags.has_kw = true;
8528 const VALUE default_values = rb_ary_hidden_new(1);
8529 const VALUE complex_mark = rb_str_tmp_new(0);
8531 ID *ids = xcalloc(keywords_list->size, sizeof(ID));
8533 size_t kw_index = 0;
8535 for (size_t i = 0; i < keywords_list->size; i++) {
8536 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
8537 pm_constant_id_t name;
8539 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8540 // ^^
8541 if (PM_NODE_TYPE_P(keyword_parameter_node, PM_REQUIRED_KEYWORD_PARAMETER_NODE)) {
8542 name = ((const pm_required_keyword_parameter_node_t *) keyword_parameter_node)->name;
8543 keyword->required_num++;
8544 ID local = pm_constant_id_lookup(scope_node, name);
8546 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
8547 local_table_for_iseq->ids[local_index] = local;
8549 else {
8550 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
8552 local_index++;
8553 ids[kw_index++] = local;
8557 for (size_t i = 0; i < keywords_list->size; i++) {
8558 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
8559 pm_constant_id_t name;
8561 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8562 // ^^^^
8563 if (PM_NODE_TYPE_P(keyword_parameter_node, PM_OPTIONAL_KEYWORD_PARAMETER_NODE)) {
8564 const pm_optional_keyword_parameter_node_t *cast = ((const pm_optional_keyword_parameter_node_t *) keyword_parameter_node);
8566 pm_node_t *value = cast->value;
8567 name = cast->name;
8569 if (PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL) && !(PM_NODE_TYPE_P(value, PM_ARRAY_NODE) || PM_NODE_TYPE_P(value, PM_HASH_NODE) || PM_NODE_TYPE_P(value, PM_RANGE_NODE))) {
8570 rb_ary_push(default_values, pm_static_literal_value(iseq, value, scope_node));
8572 else {
8573 rb_ary_push(default_values, complex_mark);
8576 ID local = pm_constant_id_lookup(scope_node, name);
8577 if (PM_NODE_FLAG_P(keyword_parameter_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
8578 local_table_for_iseq->ids[local_index] = local;
8580 else {
8581 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
8583 ids[kw_index++] = local;
8584 local_index++;
8589 keyword->bits_start = local_index;
8590 keyword->table = ids;
8592 VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
8594 for (int i = 0; i < RARRAY_LEN(default_values); i++) {
8595 VALUE dv = RARRAY_AREF(default_values, i);
8596 if (dv == complex_mark) dv = Qundef;
8597 if (!SPECIAL_CONST_P(dv)) {
8598 RB_OBJ_WRITTEN(iseq, Qundef, dv);
8600 dvs[i] = dv;
8603 keyword->default_values = dvs;
8605 // Hidden local for keyword arguments
8606 ID local = rb_make_temporary_id(local_index);
8607 local_table_for_iseq->ids[local_index] = local;
8608 local_index++;
8611 if (body->type == ISEQ_TYPE_BLOCK && local_index == 1 && requireds_list && requireds_list->size == 1 && !trailing_comma) {
8612 body->param.flags.ambiguous_param0 = true;
8615 if (parameters_node) {
8616 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8617 // ^^^
8618 if (parameters_node->keyword_rest) {
8619 switch (PM_NODE_TYPE(parameters_node->keyword_rest)) {
8620 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **nil, &n)
8621 // ^^^^^
8622 case PM_NO_KEYWORDS_PARAMETER_NODE: {
8623 body->param.flags.accepts_no_kwarg = true;
8624 break;
8626 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8627 // ^^^
8628 case PM_KEYWORD_REST_PARAMETER_NODE: {
8629 const pm_keyword_rest_parameter_node_t *kw_rest_node = (const pm_keyword_rest_parameter_node_t *) parameters_node->keyword_rest;
8630 if (!body->param.flags.has_kw) {
8631 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
8634 keyword->rest_start = local_index;
8635 body->param.flags.has_kwrest = true;
8637 pm_constant_id_t constant_id = kw_rest_node->name;
8638 if (constant_id) {
8639 if (PM_NODE_FLAG_P(kw_rest_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
8640 ID local = pm_constant_id_lookup(scope_node, constant_id);
8641 local_table_for_iseq->ids[local_index] = local;
8643 else {
8644 pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
8647 else {
8648 pm_insert_local_special(idPow, local_index, index_lookup_table, local_table_for_iseq);
8651 local_index++;
8652 break;
8654 // def foo(...)
8655 // ^^^
8656 case PM_FORWARDING_PARAMETER_NODE: {
8657 body->param.rest_start = local_index;
8658 body->param.flags.has_rest = true;
8660 // Add the leading *
8661 pm_insert_local_special(idMULT, local_index++, index_lookup_table, local_table_for_iseq);
8663 // Add the kwrest **
8664 RUBY_ASSERT(!body->param.flags.has_kw);
8666 // There are no keywords declared (in the text of the program)
8667 // but the forwarding node implies we support kwrest (**)
8668 body->param.flags.has_kw = false;
8669 body->param.flags.has_kwrest = true;
8670 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
8672 keyword->rest_start = local_index;
8674 pm_insert_local_special(idPow, local_index++, index_lookup_table, local_table_for_iseq);
8676 body->param.block_start = local_index;
8677 body->param.flags.has_block = true;
8679 pm_insert_local_special(idAnd, local_index++, index_lookup_table, local_table_for_iseq);
8680 pm_insert_local_special(idDot3, local_index++, index_lookup_table, local_table_for_iseq);
8681 break;
8683 default: {
8684 rb_bug("node type %s not expected as keyword_rest", pm_node_type_to_str(PM_NODE_TYPE(parameters_node->keyword_rest)));
8689 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8690 // ^^
8691 if (parameters_node->block) {
8692 body->param.block_start = local_index;
8693 body->param.flags.has_block = true;
8695 pm_constant_id_t name = ((const pm_block_parameter_node_t *) parameters_node->block)->name;
8697 if (name) {
8698 if (PM_NODE_FLAG_P(parameters_node->block, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) {
8699 ID local = pm_constant_id_lookup(scope_node, name);
8700 local_table_for_iseq->ids[local_index] = local;
8702 else {
8703 pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
8706 else {
8707 pm_insert_local_special(idAnd, local_index, index_lookup_table, local_table_for_iseq);
8710 local_index++;
8714 //********END OF STEP 2**********
8715 // The local table is now consistent with expected
8716 // stack layout
8718 // If there's only one required element in the parameters
8719 // CRuby needs to recognize it as an ambiguous parameter
8721 //********STEP 3**********
8722 // Goal: fill in the names of the parameters in MultiTargetNodes
8724 // Go through requireds again to set the multis
8726 if (requireds_list && requireds_list->size) {
8727 for (size_t i = 0; i < requireds_list->size; i++) {
8728 // For each MultiTargetNode, we're going to have one
8729 // additional anonymous local not represented in the locals table
8730 // We want to account for this in our table size
8731 const pm_node_t *required = requireds_list->nodes[i];
8733 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
8734 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);
8739 // Go through posts again to set the multis
8740 if (posts_list && posts_list->size) {
8741 for (size_t i = 0; i < posts_list->size; i++) {
8742 // For each MultiTargetNode, we're going to have one
8743 // additional anonymous local not represented in the locals table
8744 // We want to account for this in our table size
8745 const pm_node_t *post = posts_list->nodes[i];
8747 if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
8748 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);
8753 // Set any anonymous locals for the for node
8754 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
8755 if (PM_NODE_TYPE_P(((const pm_for_node_t *) scope_node->ast_node)->index, PM_LOCAL_VARIABLE_TARGET_NODE)) {
8756 body->param.lead_num++;
8758 else {
8759 body->param.rest_start = local_index;
8760 body->param.flags.has_rest = true;
8763 ID local = rb_make_temporary_id(local_index);
8764 local_table_for_iseq->ids[local_index] = local;
8765 local_index++;
8768 // Fill in any NumberedParameters, if they exist
8769 if (scope_node->parameters && PM_NODE_TYPE_P(scope_node->parameters, PM_NUMBERED_PARAMETERS_NODE)) {
8770 int maximum = ((const pm_numbered_parameters_node_t *) scope_node->parameters)->maximum;
8771 RUBY_ASSERT(0 < maximum && maximum <= 9);
8772 for (int i = 0; i < maximum; i++, local_index++) {
8773 const uint8_t param_name[] = { '_', '1' + i };
8774 pm_constant_id_t constant_id = pm_constant_pool_find(&parser->constant_pool, param_name, 2);
8775 RUBY_ASSERT(constant_id && "parser should fill in any gaps in numbered parameters");
8776 pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
8778 body->param.lead_num = maximum;
8779 body->param.flags.has_lead = true;
8782 //********END OF STEP 3**********
8784 //********STEP 4**********
8785 // Goal: fill in the method body locals
8786 // To be explicit, these are the non-parameter locals
8787 // We fill in the block_locals, if they exist
8788 // lambda { |x; y| y }
8789 // ^
8790 if (block_locals && block_locals->size) {
8791 for (size_t i = 0; i < block_locals->size; i++, local_index++) {
8792 pm_constant_id_t constant_id = ((const pm_block_local_variable_node_t *) block_locals->nodes[i])->name;
8793 pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
8797 // Fill in any locals we missed
8798 if (scope_node->locals.size) {
8799 for (size_t i = 0; i < scope_node->locals.size; i++) {
8800 pm_constant_id_t constant_id = locals->ids[i];
8801 if (constant_id) {
8802 struct pm_local_table_insert_ctx ctx;
8803 ctx.scope_node = scope_node;
8804 ctx.local_table_for_iseq = local_table_for_iseq;
8805 ctx.local_index = local_index;
8807 st_update(index_lookup_table, (st_data_t)constant_id, pm_local_table_insert_func, (st_data_t)&ctx);
8809 local_index = ctx.local_index;
8814 //********END OF STEP 4**********
8816 // We set the index_lookup_table on the scope node so we can
8817 // refer to the parameters correctly
8818 if (scope_node->index_lookup_table) {
8819 st_free_table(scope_node->index_lookup_table);
8821 scope_node->index_lookup_table = index_lookup_table;
8822 iseq_calc_param_size(iseq);
8823 iseq_set_local_table(iseq, local_table_for_iseq);
8824 scope_node->local_table_for_iseq_size = local_table_for_iseq->size;
8826 //********STEP 5************
8827 // Goal: compile anything that needed to be compiled
8828 if (optionals_list && optionals_list->size) {
8829 LABEL **opt_table = (LABEL **) ALLOC_N(VALUE, optionals_list->size + 1);
8830 LABEL *label;
8832 // TODO: Should we make an api for NEW_LABEL where you can pass
8833 // a pointer to the label it should fill out? We already
8834 // have a list of labels allocated above so it seems wasteful
8835 // to do the copies.
8836 for (size_t i = 0; i < optionals_list->size; i++) {
8837 label = NEW_LABEL(lineno);
8838 opt_table[i] = label;
8839 PUSH_LABEL(ret, label);
8840 pm_node_t *optional_node = optionals_list->nodes[i];
8841 PM_COMPILE_NOT_POPPED(optional_node);
8844 // Set the last label
8845 label = NEW_LABEL(lineno);
8846 opt_table[optionals_list->size] = label;
8847 PUSH_LABEL(ret, label);
8849 body->param.opt_table = (const VALUE *) opt_table;
8852 if (keywords_list && keywords_list->size) {
8853 size_t optional_index = 0;
8854 for (size_t i = 0; i < keywords_list->size; i++) {
8855 pm_node_t *keyword_parameter_node = keywords_list->nodes[i];
8856 pm_constant_id_t name;
8858 switch (PM_NODE_TYPE(keyword_parameter_node)) {
8859 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8860 // ^^^^
8861 case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: {
8862 const pm_optional_keyword_parameter_node_t *cast = ((const pm_optional_keyword_parameter_node_t *) keyword_parameter_node);
8864 pm_node_t *value = cast->value;
8865 name = cast->name;
8867 if (!PM_NODE_FLAG_P(value, PM_NODE_FLAG_STATIC_LITERAL) || PM_NODE_TYPE_P(value, PM_ARRAY_NODE) || PM_NODE_TYPE_P(value, PM_HASH_NODE) || PM_NODE_TYPE_P(value, PM_RANGE_NODE)) {
8868 LABEL *end_label = NEW_LABEL(location.line);
8870 pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, name, 0);
8871 int kw_bits_idx = table_size - body->param.keyword->bits_start;
8872 PUSH_INSN2(ret, location, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(optional_index));
8873 PUSH_INSNL(ret, location, branchif, end_label);
8874 PM_COMPILE(value);
8875 PUSH_SETLOCAL(ret, location, index.index, index.level);
8876 PUSH_LABEL(ret, end_label);
8878 optional_index++;
8879 break;
8881 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8882 // ^^
8883 case PM_REQUIRED_KEYWORD_PARAMETER_NODE: {
8884 break;
8886 default: {
8887 rb_bug("Unexpected keyword parameter node type %s", pm_node_type_to_str(PM_NODE_TYPE(keyword_parameter_node)));
8893 if (requireds_list && requireds_list->size) {
8894 for (size_t i = 0; i < requireds_list->size; i++) {
8895 // For each MultiTargetNode, we're going to have one additional
8896 // anonymous local not represented in the locals table. We want
8897 // to account for this in our table size.
8898 const pm_node_t *required = requireds_list->nodes[i];
8900 if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
8901 PUSH_GETLOCAL(ret, location, table_size - (int)i, 0);
8902 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) required, ret, scope_node);
8907 if (posts_list && posts_list->size) {
8908 for (size_t i = 0; i < posts_list->size; i++) {
8909 // For each MultiTargetNode, we're going to have one additional
8910 // anonymous local not represented in the locals table. We want
8911 // to account for this in our table size.
8912 const pm_node_t *post = posts_list->nodes[i];
8914 if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
8915 PUSH_GETLOCAL(ret, location, table_size - body->param.post_start - (int) i, 0);
8916 pm_compile_destructured_param_writes(iseq, (const pm_multi_target_node_t *) post, ret, scope_node);
8921 switch (body->type) {
8922 case ISEQ_TYPE_BLOCK: {
8923 LABEL *start = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(0);
8924 LABEL *end = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(0);
8925 const pm_line_column_t block_location = { .line = body->location.first_lineno, .column = -1 };
8927 start->rescued = LABEL_RESCUE_BEG;
8928 end->rescued = LABEL_RESCUE_END;
8930 // For nodes automatically assign the iteration variable to whatever
8931 // index variable. We need to handle that write here because it has
8932 // to happen in the context of the block. Note that this happens
8933 // before the B_CALL tracepoint event.
8934 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_FOR_NODE)) {
8935 pm_compile_for_node_index(iseq, ((const pm_for_node_t *) scope_node->ast_node)->index, ret, scope_node);
8938 PUSH_TRACE(ret, RUBY_EVENT_B_CALL);
8939 PUSH_INSN(ret, block_location, nop);
8940 PUSH_LABEL(ret, start);
8942 if (scope_node->body != NULL) {
8943 switch (PM_NODE_TYPE(scope_node->ast_node)) {
8944 case PM_POST_EXECUTION_NODE: {
8945 const pm_post_execution_node_t *cast = (const pm_post_execution_node_t *) scope_node->ast_node;
8946 PUSH_INSN1(ret, block_location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8948 // We create another ScopeNode from the statements within the PostExecutionNode
8949 pm_scope_node_t next_scope_node;
8950 pm_scope_node_init((const pm_node_t *) cast->statements, &next_scope_node, scope_node);
8952 const rb_iseq_t *block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(body->parent_iseq), ISEQ_TYPE_BLOCK, location.line);
8953 pm_scope_node_destroy(&next_scope_node);
8955 PUSH_CALL_WITH_BLOCK(ret, block_location, id_core_set_postexe, INT2FIX(0), block);
8956 break;
8958 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: {
8959 const pm_interpolated_regular_expression_node_t *cast = (const pm_interpolated_regular_expression_node_t *) scope_node->ast_node;
8960 pm_compile_regexp_dynamic(iseq, (const pm_node_t *) cast, &cast->parts, &location, ret, popped, scope_node);
8961 break;
8963 default:
8964 pm_compile_node(iseq, scope_node->body, ret, popped, scope_node);
8965 break;
8968 else {
8969 PUSH_INSN(ret, block_location, putnil);
8972 PUSH_LABEL(ret, end);
8973 PUSH_TRACE(ret, RUBY_EVENT_B_RETURN);
8974 ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
8976 /* wide range catch handler must put at last */
8977 PUSH_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, NULL, start);
8978 PUSH_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, NULL, end);
8979 break;
8981 case ISEQ_TYPE_ENSURE: {
8982 const pm_line_column_t statements_location = (scope_node->body != NULL ? PM_NODE_START_LINE_COLUMN(scope_node->parser, scope_node->body) : location);
8983 iseq_set_exception_local_table(iseq);
8985 if (scope_node->body != NULL) {
8986 PM_COMPILE_POPPED((const pm_node_t *) scope_node->body);
8989 PUSH_GETLOCAL(ret, statements_location, 1, 0);
8990 PUSH_INSN1(ret, statements_location, throw, INT2FIX(0));
8991 return;
8993 case ISEQ_TYPE_METHOD: {
8994 ISEQ_COMPILE_DATA(iseq)->root_node = (const void *) scope_node->body;
8995 PUSH_TRACE(ret, RUBY_EVENT_CALL);
8997 if (scope_node->body) {
8998 PM_COMPILE((const pm_node_t *) scope_node->body);
9000 else {
9001 PUSH_INSN(ret, location, putnil);
9004 ISEQ_COMPILE_DATA(iseq)->root_node = (const void *) scope_node->body;
9005 PUSH_TRACE(ret, RUBY_EVENT_RETURN);
9007 ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
9008 break;
9010 case ISEQ_TYPE_RESCUE: {
9011 iseq_set_exception_local_table(iseq);
9012 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_RESCUE_MODIFIER_NODE)) {
9013 LABEL *lab = NEW_LABEL(lineno);
9014 LABEL *rescue_end = NEW_LABEL(lineno);
9015 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
9016 PUSH_INSN1(ret, location, putobject, rb_eStandardError);
9017 PUSH_INSN1(ret, location, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
9018 PUSH_INSNL(ret, location, branchif, lab);
9019 PUSH_INSNL(ret, location, jump, rescue_end);
9020 PUSH_LABEL(ret, lab);
9021 PM_COMPILE((const pm_node_t *) scope_node->body);
9022 PUSH_INSN(ret, location, leave);
9023 PUSH_LABEL(ret, rescue_end);
9024 PUSH_GETLOCAL(ret, location, LVAR_ERRINFO, 0);
9026 else {
9027 PM_COMPILE((const pm_node_t *) scope_node->ast_node);
9029 PUSH_INSN1(ret, location, throw, INT2FIX(0));
9031 return;
9033 default:
9034 if (scope_node->body) {
9035 PM_COMPILE((const pm_node_t *) scope_node->body);
9037 else {
9038 PUSH_INSN(ret, location, putnil);
9040 break;
9043 if (PM_NODE_TYPE_P(scope_node->ast_node, PM_CLASS_NODE) || PM_NODE_TYPE_P(scope_node->ast_node, PM_MODULE_NODE)) {
9044 const pm_line_column_t end_location = PM_NODE_END_LINE_COLUMN(scope_node->parser, scope_node->ast_node);
9045 ADD_TRACE(ret, RUBY_EVENT_END);
9046 ISEQ_COMPILE_DATA(iseq)->last_line = end_location.line;
9049 if (!PM_NODE_TYPE_P(scope_node->ast_node, PM_ENSURE_NODE)) {
9050 const pm_line_column_t location = { .line = ISEQ_COMPILE_DATA(iseq)->last_line, .column = -1 };
9051 PUSH_INSN(ret, location, leave);
9054 return;
9056 case PM_SELF_NODE: {
9057 // self
9058 // ^^^^
9059 if (!popped) {
9060 PUSH_INSN(ret, location, putself);
9062 return;
9064 case PM_SHAREABLE_CONSTANT_NODE: {
9065 // A value that is being written to a constant that is being marked as
9066 // shared depending on the current lexical context.
9067 const pm_shareable_constant_node_t *cast = (const pm_shareable_constant_node_t *) node;
9069 switch (PM_NODE_TYPE(cast->write)) {
9070 case PM_CONSTANT_WRITE_NODE:
9071 pm_compile_constant_write_node(iseq, (const pm_constant_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node);
9072 break;
9073 case PM_CONSTANT_AND_WRITE_NODE:
9074 pm_compile_constant_and_write_node(iseq, (const pm_constant_and_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node);
9075 break;
9076 case PM_CONSTANT_OR_WRITE_NODE:
9077 pm_compile_constant_or_write_node(iseq, (const pm_constant_or_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node);
9078 break;
9079 case PM_CONSTANT_OPERATOR_WRITE_NODE:
9080 pm_compile_constant_operator_write_node(iseq, (const pm_constant_operator_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node);
9081 break;
9082 case PM_CONSTANT_PATH_WRITE_NODE:
9083 pm_compile_constant_path_write_node(iseq, (const pm_constant_path_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node);
9084 break;
9085 case PM_CONSTANT_PATH_AND_WRITE_NODE:
9086 pm_compile_constant_path_and_write_node(iseq, (const pm_constant_path_and_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node);
9087 break;
9088 case PM_CONSTANT_PATH_OR_WRITE_NODE:
9089 pm_compile_constant_path_or_write_node(iseq, (const pm_constant_path_or_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node);
9090 break;
9091 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE:
9092 pm_compile_constant_path_operator_write_node(iseq, (const pm_constant_path_operator_write_node_t *) cast->write, cast->base.flags, &location, ret, popped, scope_node);
9093 break;
9094 default:
9095 rb_bug("Unexpected node type for shareable constant write: %s", pm_node_type_to_str(PM_NODE_TYPE(cast->write)));
9096 break;
9099 return;
9101 case PM_SINGLETON_CLASS_NODE: {
9102 // class << self; end
9103 // ^^^^^^^^^^^^^^^^^^
9104 const pm_singleton_class_node_t *cast = (const pm_singleton_class_node_t *) node;
9106 pm_scope_node_t next_scope_node;
9107 pm_scope_node_init((const pm_node_t *) cast, &next_scope_node, scope_node);
9108 const rb_iseq_t *child_iseq = NEW_ISEQ(&next_scope_node, rb_fstring_lit("singleton class"), ISEQ_TYPE_CLASS, location.line);
9109 pm_scope_node_destroy(&next_scope_node);
9111 PM_COMPILE_NOT_POPPED(cast->expression);
9112 PUSH_INSN(ret, location, putnil);
9114 ID singletonclass;
9115 CONST_ID(singletonclass, "singletonclass");
9116 PUSH_INSN3(ret, location, defineclass, ID2SYM(singletonclass), child_iseq, INT2FIX(VM_DEFINECLASS_TYPE_SINGLETON_CLASS));
9118 if (popped) PUSH_INSN(ret, location, pop);
9119 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE) child_iseq);
9121 return;
9123 case PM_SOURCE_ENCODING_NODE: {
9124 // __ENCODING__
9125 // ^^^^^^^^^^^^
9126 if (!popped) {
9127 VALUE value = pm_static_literal_value(iseq, node, scope_node);
9128 PUSH_INSN1(ret, location, putobject, value);
9130 return;
9132 case PM_SOURCE_FILE_NODE: {
9133 // __FILE__
9134 // ^^^^^^^^
9135 if (!popped) {
9136 const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node;
9137 VALUE string = pm_source_file_value(cast, scope_node);
9139 if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_FROZEN)) {
9140 PUSH_INSN1(ret, location, putobject, string);
9142 else if (PM_NODE_FLAG_P(cast, PM_STRING_FLAGS_MUTABLE)) {
9143 PUSH_INSN1(ret, location, putstring, string);
9145 else {
9146 PUSH_INSN1(ret, location, putchilledstring, string);
9149 return;
9151 case PM_SOURCE_LINE_NODE: {
9152 // __LINE__
9153 // ^^^^^^^^
9154 if (!popped) {
9155 VALUE value = pm_static_literal_value(iseq, node, scope_node);
9156 PUSH_INSN1(ret, location, putobject, value);
9158 return;
9160 case PM_SPLAT_NODE: {
9161 // foo(*bar)
9162 // ^^^^
9163 const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
9164 if (cast->expression) {
9165 PM_COMPILE(cast->expression);
9168 if (!popped) {
9169 PUSH_INSN1(ret, location, splatarray, Qtrue);
9171 return;
9173 case PM_STATEMENTS_NODE: {
9174 // A list of statements.
9175 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
9176 const pm_node_list_t *body = &cast->body;
9178 if (body->size > 0) {
9179 for (size_t index = 0; index < body->size - 1; index++) {
9180 PM_COMPILE_POPPED(body->nodes[index]);
9182 PM_COMPILE(body->nodes[body->size - 1]);
9184 else {
9185 PUSH_INSN(ret, location, putnil);
9187 return;
9189 case PM_STRING_NODE: {
9190 // "foo"
9191 // ^^^^^
9192 if (!popped) {
9193 const pm_string_node_t *cast = (const pm_string_node_t *) node;
9194 VALUE value = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
9196 if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_FROZEN)) {
9197 PUSH_INSN1(ret, location, putobject, value);
9199 else if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_MUTABLE)) {
9200 PUSH_INSN1(ret, location, putstring, value);
9202 else {
9203 PUSH_INSN1(ret, location, putchilledstring, value);
9206 return;
9208 case PM_SUPER_NODE: {
9209 // super(foo)
9210 // ^^^^^^^^^^
9211 const pm_super_node_t *cast = (const pm_super_node_t *) node;
9213 DECL_ANCHOR(args);
9214 INIT_ANCHOR(args);
9216 LABEL *retry_label = NEW_LABEL(location.line);
9217 LABEL *retry_end_l = NEW_LABEL(location.line);
9219 const rb_iseq_t *previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
9220 const rb_iseq_t *current_block;
9221 ISEQ_COMPILE_DATA(iseq)->current_block = current_block = NULL;
9223 PUSH_LABEL(ret, retry_label);
9224 PUSH_INSN(ret, location, putself);
9226 int flags = 0;
9227 struct rb_callinfo_kwarg *keywords = NULL;
9228 int argc = pm_setup_args(cast->arguments, cast->block, &flags, &keywords, iseq, ret, scope_node, &location);
9229 flags |= VM_CALL_SUPER | VM_CALL_FCALL;
9231 if (cast->block && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE)) {
9232 pm_scope_node_t next_scope_node;
9233 pm_scope_node_init(cast->block, &next_scope_node, scope_node);
9235 ISEQ_COMPILE_DATA(iseq)->current_block = current_block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, lineno);
9236 pm_scope_node_destroy(&next_scope_node);
9239 if ((flags & VM_CALL_ARGS_BLOCKARG) && (flags & VM_CALL_KW_SPLAT) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
9240 PUSH_INSN(args, location, splatkw);
9243 PUSH_SEQ(ret, args);
9244 PUSH_INSN2(ret, location, invokesuper, new_callinfo(iseq, 0, argc, flags, keywords, current_block != NULL), current_block);
9245 pm_compile_retry_end_label(iseq, ret, retry_end_l);
9247 if (popped) PUSH_INSN(ret, location, pop);
9248 ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
9249 PUSH_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, current_block, retry_end_l);
9251 return;
9253 case PM_SYMBOL_NODE: {
9254 // :foo
9255 // ^^^^
9256 if (!popped) {
9257 VALUE value = pm_static_literal_value(iseq, node, scope_node);
9258 PUSH_INSN1(ret, location, putobject, value);
9260 return;
9262 case PM_TRUE_NODE: {
9263 // true
9264 // ^^^^
9265 if (!popped) {
9266 PUSH_INSN1(ret, location, putobject, Qtrue);
9268 return;
9270 case PM_UNDEF_NODE: {
9271 // undef foo
9272 // ^^^^^^^^^
9273 const pm_undef_node_t *cast = (const pm_undef_node_t *) node;
9274 const pm_node_list_t *names = &cast->names;
9276 for (size_t index = 0; index < names->size; index++) {
9277 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
9278 PUSH_INSN1(ret, location, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
9280 PM_COMPILE_NOT_POPPED(names->nodes[index]);
9281 PUSH_SEND(ret, location, id_core_undef_method, INT2NUM(2));
9283 if (index < names->size - 1) {
9284 PUSH_INSN(ret, location, pop);
9288 if (popped) PUSH_INSN(ret, location, pop);
9289 return;
9291 case PM_UNLESS_NODE: {
9292 // unless foo; bar end
9293 // ^^^^^^^^^^^^^^^^^^^
9295 // bar unless foo
9296 // ^^^^^^^^^^^^^^
9297 const pm_unless_node_t *cast = (const pm_unless_node_t *) node;
9298 const pm_statements_node_t *consequent = NULL;
9299 if (cast->consequent != NULL) {
9300 consequent = ((const pm_else_node_t *) cast->consequent)->statements;
9303 pm_compile_conditional(iseq, &location, PM_UNLESS_NODE, (const pm_node_t *) cast, consequent, (const pm_node_t *) cast->statements, cast->predicate, ret, popped, scope_node);
9304 return;
9306 case PM_UNTIL_NODE: {
9307 // until foo; bar end
9308 // ^^^^^^^^^^^^^^^^^
9310 // bar until foo
9311 // ^^^^^^^^^^^^^
9312 const pm_until_node_t *cast = (const pm_until_node_t *) node;
9313 pm_compile_loop(iseq, &location, cast->base.flags, PM_UNTIL_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node);
9314 return;
9316 case PM_WHILE_NODE: {
9317 // while foo; bar end
9318 // ^^^^^^^^^^^^^^^^^^
9320 // bar while foo
9321 // ^^^^^^^^^^^^^
9322 const pm_while_node_t *cast = (const pm_while_node_t *) node;
9323 pm_compile_loop(iseq, &location, cast->base.flags, PM_WHILE_NODE, (const pm_node_t *) cast, cast->statements, cast->predicate, ret, popped, scope_node);
9324 return;
9326 case PM_X_STRING_NODE: {
9327 // `foo`
9328 // ^^^^^
9329 const pm_x_string_node_t *cast = (const pm_x_string_node_t *) node;
9330 VALUE value = parse_static_literal_string(iseq, scope_node, node, &cast->unescaped);
9332 PUSH_INSN(ret, location, putself);
9333 PUSH_INSN1(ret, location, putobject, value);
9334 PUSH_SEND_WITH_FLAG(ret, location, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));
9335 if (popped) PUSH_INSN(ret, location, pop);
9337 return;
9339 case PM_YIELD_NODE: {
9340 // yield
9341 // ^^^^^
9343 // yield 1
9344 // ^^^^^^^
9345 const pm_yield_node_t *cast = (const pm_yield_node_t *) node;
9347 switch (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->type) {
9348 case ISEQ_TYPE_TOP:
9349 case ISEQ_TYPE_MAIN:
9350 case ISEQ_TYPE_CLASS:
9351 COMPILE_ERROR(iseq, location.line, "Invalid yield");
9352 return;
9353 default: /* valid */;
9356 int argc = 0;
9357 int flags = 0;
9358 struct rb_callinfo_kwarg *keywords = NULL;
9360 if (cast->arguments) {
9361 argc = pm_setup_args(cast->arguments, NULL, &flags, &keywords, iseq, ret, scope_node, &location);
9364 PUSH_INSN1(ret, location, invokeblock, new_callinfo(iseq, 0, argc, flags, keywords, FALSE));
9365 if (popped) PUSH_INSN(ret, location, pop);
9367 int level = 0;
9368 for (const rb_iseq_t *tmp_iseq = iseq; tmp_iseq != ISEQ_BODY(iseq)->local_iseq; level++) {
9369 tmp_iseq = ISEQ_BODY(tmp_iseq)->parent_iseq;
9372 if (level > 0) access_outer_variables(iseq, level, rb_intern("yield"), true);
9373 return;
9375 default: {
9376 rb_raise(rb_eNotImpError, "node type %s not implemented", pm_node_type_to_str(PM_NODE_TYPE(node)));
9377 return;
9382 /** True if the given iseq can have pre execution blocks. */
9383 static inline bool
9384 pm_iseq_pre_execution_p(rb_iseq_t *iseq)
9386 switch (ISEQ_BODY(iseq)->type) {
9387 case ISEQ_TYPE_TOP:
9388 case ISEQ_TYPE_EVAL:
9389 case ISEQ_TYPE_MAIN:
9390 return true;
9391 default:
9392 return false;
9397 * This is the main entry-point into the prism compiler. It accepts the iseq
9398 * that it should be compiling instruction into and a pointer to the scope node
9399 * that it should be compiling. It returns the established instruction sequence.
9400 * Note that this function could raise Ruby errors if it encounters compilation
9401 * errors or if there is a bug in the compiler.
9403 VALUE
9404 pm_iseq_compile_node(rb_iseq_t *iseq, pm_scope_node_t *node)
9406 DECL_ANCHOR(ret);
9407 INIT_ANCHOR(ret);
9409 if (pm_iseq_pre_execution_p(iseq)) {
9410 // Because these ISEQs can have BEGIN{}, we're going to create two
9411 // anchors to compile them, a "pre" and a "body". We'll mark the "pre"
9412 // on the scope node so that when BEGIN{} is found, its contents will be
9413 // added to the "pre" anchor.
9414 DECL_ANCHOR(pre);
9415 INIT_ANCHOR(pre);
9416 node->pre_execution_anchor = pre;
9418 // Now we'll compile the body as normal. We won't compile directly into
9419 // the "ret" anchor yet because we want to add the "pre" anchor to the
9420 // beginning of the "ret" anchor first.
9421 DECL_ANCHOR(body);
9422 INIT_ANCHOR(body);
9423 pm_compile_node(iseq, (const pm_node_t *) node, body, false, node);
9425 // Now we'll join both anchors together so that the content is in the
9426 // correct order.
9427 PUSH_SEQ(ret, pre);
9428 PUSH_SEQ(ret, body);
9430 else {
9431 // In other circumstances, we can just compile the node directly into
9432 // the "ret" anchor.
9433 pm_compile_node(iseq, (const pm_node_t *) node, ret, false, node);
9436 CHECK(iseq_setup_insn(iseq, ret));
9437 return iseq_setup(iseq, ret);
9441 * Free the internal memory associated with a pm_parse_result_t struct.
9442 * Importantly this does not free the struct itself.
9444 void
9445 pm_parse_result_free(pm_parse_result_t *result)
9447 if (result->parsed) {
9448 pm_node_destroy(&result->parser, result->node.ast_node);
9449 pm_scope_node_destroy(&result->node);
9452 pm_parser_free(&result->parser);
9453 pm_string_free(&result->input);
9454 pm_options_free(&result->options);
9457 /** An error that is going to be formatted into the output. */
9458 typedef struct {
9459 /** A pointer to the diagnostic that was generated during parsing. */
9460 pm_diagnostic_t *error;
9462 /** The start line of the diagnostic message. */
9463 int32_t line;
9465 /** The column start of the diagnostic message. */
9466 uint32_t column_start;
9468 /** The column end of the diagnostic message. */
9469 uint32_t column_end;
9470 } pm_parse_error_t;
9472 /** The format that will be used to format the errors into the output. */
9473 typedef struct {
9474 /** The prefix that will be used for line numbers. */
9475 const char *number_prefix;
9477 /** The prefix that will be used for blank lines. */
9478 const char *blank_prefix;
9480 /** The divider that will be used between sections of source code. */
9481 const char *divider;
9483 /** The length of the blank prefix. */
9484 size_t blank_prefix_length;
9486 /** The length of the divider. */
9487 size_t divider_length;
9488 } pm_parse_error_format_t;
9490 #define PM_COLOR_GRAY "\033[38;5;102m"
9491 #define PM_COLOR_RED "\033[1;31m"
9492 #define PM_COLOR_RESET "\033[m"
9493 #define PM_ERROR_TRUNCATE 30
9495 static inline pm_parse_error_t *
9496 pm_parse_errors_format_sort(const pm_parser_t *parser, const pm_list_t *error_list, const pm_newline_list_t *newline_list) {
9497 pm_parse_error_t *errors = xcalloc(error_list->size, sizeof(pm_parse_error_t));
9498 if (errors == NULL) return NULL;
9500 int32_t start_line = parser->start_line;
9501 for (pm_diagnostic_t *error = (pm_diagnostic_t *) error_list->head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
9502 pm_line_column_t start = pm_newline_list_line_column(newline_list, error->location.start, start_line);
9503 pm_line_column_t end = pm_newline_list_line_column(newline_list, error->location.end, start_line);
9505 // We're going to insert this error into the array in sorted order. We
9506 // do this by finding the first error that has a line number greater
9507 // than the current error and then inserting the current error before
9508 // that one.
9509 size_t index = 0;
9510 while (
9511 (index < error_list->size) &&
9512 (errors[index].error != NULL) &&
9514 (errors[index].line < start.line) ||
9515 ((errors[index].line == start.line) && (errors[index].column_start < start.column))
9517 ) index++;
9519 // Now we're going to shift all of the errors after this one down one
9520 // index to make room for the new error.
9521 if (index + 1 < error_list->size) {
9522 memmove(&errors[index + 1], &errors[index], sizeof(pm_parse_error_t) * (error_list->size - index - 1));
9525 // Finally, we'll insert the error into the array.
9526 uint32_t column_end;
9527 if (start.line == end.line) {
9528 column_end = end.column;
9529 } else {
9530 column_end = (uint32_t) (newline_list->offsets[start.line - start_line + 1] - newline_list->offsets[start.line - start_line] - 1);
9533 // Ensure we have at least one column of error.
9534 if (start.column == column_end) column_end++;
9536 errors[index] = (pm_parse_error_t) {
9537 .error = error,
9538 .line = start.line,
9539 .column_start = start.column,
9540 .column_end = column_end
9544 return errors;
9547 static inline void
9548 pm_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) {
9549 int32_t line_delta = line - parser->start_line;
9550 assert(line_delta >= 0);
9552 size_t index = (size_t) line_delta;
9553 assert(index < newline_list->size);
9555 const uint8_t *start = &parser->start[newline_list->offsets[index]];
9556 const uint8_t *end;
9558 if (index >= newline_list->size - 1) {
9559 end = parser->end;
9560 } else {
9561 end = &parser->start[newline_list->offsets[index + 1]];
9564 pm_buffer_append_format(buffer, number_prefix, line);
9566 // Here we determine if we should truncate the end of the line.
9567 bool truncate_end = false;
9568 if ((column_end != 0) && ((end - (start + column_end)) >= PM_ERROR_TRUNCATE)) {
9569 end = start + column_end + PM_ERROR_TRUNCATE;
9570 truncate_end = true;
9573 // Here we determine if we should truncate the start of the line.
9574 if (column_start >= PM_ERROR_TRUNCATE) {
9575 pm_buffer_append_string(buffer, "... ", 4);
9576 start += column_start;
9579 pm_buffer_append_string(buffer, (const char *) start, (size_t) (end - start));
9581 if (truncate_end) {
9582 pm_buffer_append_string(buffer, " ...\n", 5);
9583 } else if (end == parser->end && end[-1] != '\n') {
9584 pm_buffer_append_string(buffer, "\n", 1);
9589 * Format the errors on the parser into the given buffer.
9591 static void
9592 pm_parse_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, pm_buffer_t *buffer, bool colorize, bool inline_messages) {
9593 assert(error_list->size != 0);
9595 // First, we're going to sort all of the errors by line number using an
9596 // insertion sort into a newly allocated array.
9597 const int32_t start_line = parser->start_line;
9598 const pm_newline_list_t *newline_list = &parser->newline_list;
9600 pm_parse_error_t *errors = pm_parse_errors_format_sort(parser, error_list, newline_list);
9601 if (errors == NULL) return;
9603 // Now we're going to determine how we're going to format line numbers and
9604 // blank lines based on the maximum number of digits in the line numbers
9605 // that are going to be displaid.
9606 pm_parse_error_format_t error_format;
9607 int32_t first_line_number = errors[0].line;
9608 int32_t last_line_number = errors[error_list->size - 1].line;
9610 // If we have a maximum line number that is negative, then we're going to
9611 // use the absolute value for comparison but multiple by 10 to additionally
9612 // have a column for the negative sign.
9613 if (first_line_number < 0) first_line_number = (-first_line_number) * 10;
9614 if (last_line_number < 0) last_line_number = (-last_line_number) * 10;
9615 int32_t max_line_number = first_line_number > last_line_number ? first_line_number : last_line_number;
9617 if (max_line_number < 10) {
9618 if (colorize) {
9619 error_format = (pm_parse_error_format_t) {
9620 .number_prefix = PM_COLOR_GRAY "%1" PRIi32 " | " PM_COLOR_RESET,
9621 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
9622 .divider = PM_COLOR_GRAY " ~~~~~" PM_COLOR_RESET "\n"
9624 } else {
9625 error_format = (pm_parse_error_format_t) {
9626 .number_prefix = "%1" PRIi32 " | ",
9627 .blank_prefix = " | ",
9628 .divider = " ~~~~~\n"
9631 } else if (max_line_number < 100) {
9632 if (colorize) {
9633 error_format = (pm_parse_error_format_t) {
9634 .number_prefix = PM_COLOR_GRAY "%2" PRIi32 " | " PM_COLOR_RESET,
9635 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
9636 .divider = PM_COLOR_GRAY " ~~~~~~" PM_COLOR_RESET "\n"
9638 } else {
9639 error_format = (pm_parse_error_format_t) {
9640 .number_prefix = "%2" PRIi32 " | ",
9641 .blank_prefix = " | ",
9642 .divider = " ~~~~~~\n"
9645 } else if (max_line_number < 1000) {
9646 if (colorize) {
9647 error_format = (pm_parse_error_format_t) {
9648 .number_prefix = PM_COLOR_GRAY "%3" PRIi32 " | " PM_COLOR_RESET,
9649 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
9650 .divider = PM_COLOR_GRAY " ~~~~~~~" PM_COLOR_RESET "\n"
9652 } else {
9653 error_format = (pm_parse_error_format_t) {
9654 .number_prefix = "%3" PRIi32 " | ",
9655 .blank_prefix = " | ",
9656 .divider = " ~~~~~~~\n"
9659 } else if (max_line_number < 10000) {
9660 if (colorize) {
9661 error_format = (pm_parse_error_format_t) {
9662 .number_prefix = PM_COLOR_GRAY "%4" PRIi32 " | " PM_COLOR_RESET,
9663 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
9664 .divider = PM_COLOR_GRAY " ~~~~~~~~" PM_COLOR_RESET "\n"
9666 } else {
9667 error_format = (pm_parse_error_format_t) {
9668 .number_prefix = "%4" PRIi32 " | ",
9669 .blank_prefix = " | ",
9670 .divider = " ~~~~~~~~\n"
9673 } else {
9674 if (colorize) {
9675 error_format = (pm_parse_error_format_t) {
9676 .number_prefix = PM_COLOR_GRAY "%5" PRIi32 " | " PM_COLOR_RESET,
9677 .blank_prefix = PM_COLOR_GRAY " | " PM_COLOR_RESET,
9678 .divider = PM_COLOR_GRAY " ~~~~~~~~" PM_COLOR_RESET "\n"
9680 } else {
9681 error_format = (pm_parse_error_format_t) {
9682 .number_prefix = "%5" PRIi32 " | ",
9683 .blank_prefix = " | ",
9684 .divider = " ~~~~~~~~\n"
9689 error_format.blank_prefix_length = strlen(error_format.blank_prefix);
9690 error_format.divider_length = strlen(error_format.divider);
9692 // Now we're going to iterate through every error in our error list and
9693 // display it. While we're iterating, we will display some padding lines of
9694 // the source before the error to give some context. We'll be careful not to
9695 // display the same line twice in case the errors are close enough in the
9696 // source.
9697 int32_t last_line = parser->start_line - 1;
9698 uint32_t last_column_start = 0;
9699 const pm_encoding_t *encoding = parser->encoding;
9701 for (size_t index = 0; index < error_list->size; index++) {
9702 pm_parse_error_t *error = &errors[index];
9704 // Here we determine how many lines of padding of the source to display,
9705 // based on the difference from the last line that was displaid.
9706 if (error->line - last_line > 1) {
9707 if (error->line - last_line > 2) {
9708 if ((index != 0) && (error->line - last_line > 3)) {
9709 pm_buffer_append_string(buffer, error_format.divider, error_format.divider_length);
9712 pm_buffer_append_string(buffer, " ", 2);
9713 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 2, 0, 0, buffer);
9716 pm_buffer_append_string(buffer, " ", 2);
9717 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, error->line - 1, 0, 0, buffer);
9720 // If this is the first error or we're on a new line, then we'll display
9721 // the line that has the error in it.
9722 if ((index == 0) || (error->line != last_line)) {
9723 if (colorize) {
9724 pm_buffer_append_string(buffer, PM_COLOR_RED "> " PM_COLOR_RESET, 12);
9725 } else {
9726 pm_buffer_append_string(buffer, "> ", 2);
9729 last_column_start = error->column_start;
9731 // Find the maximum column end of all the errors on this line.
9732 uint32_t column_end = error->column_end;
9733 for (size_t next_index = index + 1; next_index < error_list->size; next_index++) {
9734 if (errors[next_index].line != error->line) break;
9735 if (errors[next_index].column_end > column_end) column_end = errors[next_index].column_end;
9738 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, error->line, error->column_start, column_end, buffer);
9741 const uint8_t *start = &parser->start[newline_list->offsets[error->line - start_line]];
9742 if (start == parser->end) pm_buffer_append_byte(buffer, '\n');
9744 // Now we'll display the actual error message. We'll do this by first
9745 // putting the prefix to the line, then a bunch of blank spaces
9746 // depending on the column, then as many carets as we need to display
9747 // the width of the error, then the error message itself.
9749 // Note that this doesn't take into account the width of the actual
9750 // character when displaid in the terminal. For some east-asian
9751 // languages or emoji, this means it can be thrown off pretty badly. We
9752 // will need to solve this eventually.
9753 pm_buffer_append_string(buffer, " ", 2);
9754 pm_buffer_append_string(buffer, error_format.blank_prefix, error_format.blank_prefix_length);
9756 size_t column = 0;
9757 if (last_column_start >= PM_ERROR_TRUNCATE) {
9758 pm_buffer_append_string(buffer, " ", 4);
9759 column = last_column_start;
9762 while (column < error->column_start) {
9763 pm_buffer_append_byte(buffer, ' ');
9765 size_t char_width = encoding->char_width(start + column, parser->end - (start + column));
9766 column += (char_width == 0 ? 1 : char_width);
9769 if (colorize) pm_buffer_append_string(buffer, PM_COLOR_RED, 7);
9770 pm_buffer_append_byte(buffer, '^');
9772 size_t char_width = encoding->char_width(start + column, parser->end - (start + column));
9773 column += (char_width == 0 ? 1 : char_width);
9775 while (column < error->column_end) {
9776 pm_buffer_append_byte(buffer, '~');
9778 size_t char_width = encoding->char_width(start + column, parser->end - (start + column));
9779 column += (char_width == 0 ? 1 : char_width);
9782 if (colorize) pm_buffer_append_string(buffer, PM_COLOR_RESET, 3);
9784 if (inline_messages) {
9785 pm_buffer_append_byte(buffer, ' ');
9786 assert(error->error != NULL);
9788 const char *message = error->error->message;
9789 pm_buffer_append_string(buffer, message, strlen(message));
9792 pm_buffer_append_byte(buffer, '\n');
9794 // Here we determine how many lines of padding to display after the
9795 // error, depending on where the next error is in source.
9796 last_line = error->line;
9797 int32_t next_line = (index == error_list->size - 1) ? (((int32_t) newline_list->size) + parser->start_line) : errors[index + 1].line;
9799 if (next_line - last_line > 1) {
9800 pm_buffer_append_string(buffer, " ", 2);
9801 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, 0, 0, buffer);
9804 if (next_line - last_line > 1) {
9805 pm_buffer_append_string(buffer, " ", 2);
9806 pm_parse_errors_format_line(parser, newline_list, error_format.number_prefix, ++last_line, 0, 0, buffer);
9810 // Finally, we'll free the array of errors that we allocated.
9811 xfree(errors);
9814 #undef PM_ERROR_TRUNCATE
9815 #undef PM_COLOR_GRAY
9816 #undef PM_COLOR_RED
9817 #undef PM_COLOR_RESET
9820 * Check if the given source slice is valid UTF-8. The location represents the
9821 * location of the error, but the slice of the source will include the content
9822 * of all of the lines that the error touches, so we need to check those parts
9823 * as well.
9825 static bool
9826 pm_parse_process_error_utf8_p(const pm_parser_t *parser, const pm_location_t *location)
9828 const size_t start_line = pm_newline_list_line_column(&parser->newline_list, location->start, 1).line;
9829 const size_t end_line = pm_newline_list_line_column(&parser->newline_list, location->end, 1).line;
9831 const uint8_t *start = parser->start + parser->newline_list.offsets[start_line - 1];
9832 const uint8_t *end = ((end_line == parser->newline_list.size) ? parser->end : (parser->start + parser->newline_list.offsets[end_line]));
9833 size_t width;
9835 while (start < end) {
9836 if ((width = pm_encoding_utf_8_char_width(start, end - start)) == 0) return false;
9837 start += width;
9840 return true;
9844 * Generate an error object from the given parser that contains as much
9845 * information as possible about the errors that were encountered.
9847 static VALUE
9848 pm_parse_process_error(const pm_parse_result_t *result)
9850 const pm_parser_t *parser = &result->parser;
9851 const pm_diagnostic_t *head = (const pm_diagnostic_t *) parser->error_list.head;
9852 bool valid_utf8 = true;
9854 pm_buffer_t buffer = { 0 };
9855 const pm_string_t *filepath = &parser->filepath;
9857 for (const pm_diagnostic_t *error = head; error != NULL; error = (const pm_diagnostic_t *) error->node.next) {
9858 switch (error->level) {
9859 case PM_ERROR_LEVEL_SYNTAX:
9860 // It is implicitly assumed that the error messages will be
9861 // encodeable as UTF-8. Because of this, we can't include source
9862 // examples that contain invalid byte sequences. So if any source
9863 // examples include invalid UTF-8 byte sequences, we will skip
9864 // showing source examples entirely.
9865 if (valid_utf8 && !pm_parse_process_error_utf8_p(parser, &error->location)) {
9866 valid_utf8 = false;
9868 break;
9869 case PM_ERROR_LEVEL_ARGUMENT: {
9870 // Any errors with the level PM_ERROR_LEVEL_ARGUMENT take over as
9871 // the only argument that gets raised. This is to allow priority
9872 // messages that should be handled before anything else.
9873 int32_t line_number = (int32_t) pm_location_line_number(parser, &error->location);
9875 pm_buffer_append_format(
9876 &buffer,
9877 "%.*s:%" PRIi32 ": %s",
9878 (int) pm_string_length(filepath),
9879 pm_string_source(filepath),
9880 line_number,
9881 error->message
9884 if (pm_parse_process_error_utf8_p(parser, &error->location)) {
9885 pm_buffer_append_byte(&buffer, '\n');
9887 pm_list_node_t *list_node = (pm_list_node_t *) error;
9888 pm_list_t error_list = { .size = 1, .head = list_node, .tail = list_node };
9890 pm_parse_errors_format(parser, &error_list, &buffer, rb_stderr_tty_p(), false);
9893 VALUE value = rb_exc_new(rb_eArgError, pm_buffer_value(&buffer), pm_buffer_length(&buffer));
9894 pm_buffer_free(&buffer);
9896 return value;
9898 case PM_ERROR_LEVEL_LOAD: {
9899 // Load errors are much simpler, because they don't include any of
9900 // the source in them. We create the error directly from the
9901 // message.
9902 VALUE message = rb_enc_str_new_cstr(error->message, rb_locale_encoding());
9903 VALUE value = rb_exc_new3(rb_eLoadError, message);
9904 rb_ivar_set(value, rb_intern_const("@path"), Qnil);
9905 return value;
9910 pm_buffer_append_format(
9911 &buffer,
9912 "%.*s:%" PRIi32 ": syntax error%s found\n",
9913 (int) pm_string_length(filepath),
9914 pm_string_source(filepath),
9915 (int32_t) pm_location_line_number(parser, &head->location),
9916 (parser->error_list.size > 1) ? "s" : ""
9919 if (valid_utf8) {
9920 pm_parse_errors_format(parser, &parser->error_list, &buffer, rb_stderr_tty_p(), true);
9922 else {
9923 for (const pm_diagnostic_t *error = head; error != NULL; error = (const pm_diagnostic_t *) error->node.next) {
9924 if (error != head) pm_buffer_append_byte(&buffer, '\n');
9925 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);
9929 VALUE message = rb_enc_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer), result->node.encoding);
9930 VALUE error = rb_exc_new_str(rb_eSyntaxError, message);
9932 rb_encoding *filepath_encoding = result->node.filepath_encoding != NULL ? result->node.filepath_encoding : rb_utf8_encoding();
9933 VALUE path = rb_enc_str_new((const char *) pm_string_source(filepath), pm_string_length(filepath), filepath_encoding);
9935 rb_ivar_set(error, rb_intern_const("@path"), path);
9936 pm_buffer_free(&buffer);
9938 return error;
9942 * Parse the parse result and raise a Ruby error if there are any syntax errors.
9943 * It returns an error if one should be raised. It is assumed that the parse
9944 * result object is zeroed out.
9946 static VALUE
9947 pm_parse_process(pm_parse_result_t *result, pm_node_t *node)
9949 pm_parser_t *parser = &result->parser;
9951 // First, set up the scope node so that the AST node is attached and can be
9952 // freed regardless of whether or we return an error.
9953 pm_scope_node_t *scope_node = &result->node;
9954 rb_encoding *filepath_encoding = scope_node->filepath_encoding;
9955 int coverage_enabled = scope_node->coverage_enabled;
9957 pm_scope_node_init(node, scope_node, NULL);
9958 scope_node->filepath_encoding = filepath_encoding;
9960 scope_node->encoding = rb_enc_find(parser->encoding->name);
9961 if (!scope_node->encoding) rb_bug("Encoding not found %s!", parser->encoding->name);
9963 scope_node->coverage_enabled = coverage_enabled;
9965 // Emit all of the various warnings from the parse.
9966 const pm_diagnostic_t *warning;
9967 const char *warning_filepath = (const char *) pm_string_source(&parser->filepath);
9969 for (warning = (const pm_diagnostic_t *) parser->warning_list.head; warning != NULL; warning = (const pm_diagnostic_t *) warning->node.next) {
9970 int line = pm_location_line_number(parser, &warning->location);
9972 if (warning->level == PM_WARNING_LEVEL_VERBOSE) {
9973 rb_enc_compile_warning(scope_node->encoding, warning_filepath, line, "%s", warning->message);
9975 else {
9976 rb_enc_compile_warn(scope_node->encoding, warning_filepath, line, "%s", warning->message);
9980 // If there are errors, raise an appropriate error and free the result.
9981 if (parser->error_list.size > 0) {
9982 VALUE error = pm_parse_process_error(result);
9984 // TODO: We need to set the backtrace.
9985 // rb_funcallv(error, rb_intern("set_backtrace"), 1, &path);
9986 return error;
9989 // Now set up the constant pool and intern all of the various constants into
9990 // their corresponding IDs.
9991 scope_node->parser = parser;
9992 scope_node->constants = calloc(parser->constant_pool.size, sizeof(ID));
9994 for (uint32_t index = 0; index < parser->constant_pool.size; index++) {
9995 pm_constant_t *constant = &parser->constant_pool.constants[index];
9996 scope_node->constants[index] = rb_intern3((const char *) constant->start, constant->length, scope_node->encoding);
9999 scope_node->index_lookup_table = st_init_numtable();
10000 pm_constant_id_list_t *locals = &scope_node->locals;
10001 for (size_t index = 0; index < locals->size; index++) {
10002 st_insert(scope_node->index_lookup_table, locals->ids[index], index);
10005 // If we got here, this is a success and we can return Qnil to indicate that
10006 // no error should be raised.
10007 result->parsed = true;
10008 return Qnil;
10012 * Set the frozen_string_literal option based on the default value used by the
10013 * CRuby compiler.
10015 static void
10016 pm_options_frozen_string_literal_init(pm_options_t *options)
10018 int frozen_string_literal = rb_iseq_opt_frozen_string_literal();
10020 switch (frozen_string_literal) {
10021 case ISEQ_FROZEN_STRING_LITERAL_UNSET:
10022 break;
10023 case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
10024 pm_options_frozen_string_literal_set(options, false);
10025 break;
10026 case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
10027 pm_options_frozen_string_literal_set(options, true);
10028 break;
10029 default:
10030 rb_bug("pm_options_frozen_string_literal_init: invalid frozen_string_literal=%d", frozen_string_literal);
10031 break;
10036 * Returns an array of ruby String objects that represent the lines of the
10037 * source file that the given parser parsed.
10039 static inline VALUE
10040 pm_parse_file_script_lines(const pm_scope_node_t *scope_node, const pm_parser_t *parser)
10042 const pm_newline_list_t *newline_list = &parser->newline_list;
10043 const char *start = (const char *) parser->start;
10044 const char *end = (const char *) parser->end;
10046 // If we end exactly on a newline, then there's no need to push on a final
10047 // segment. If we don't, then we need to push on the last offset up to the
10048 // end of the string.
10049 size_t last_offset = newline_list->offsets[newline_list->size - 1];
10050 bool last_push = start + last_offset != end;
10052 // Create the ruby strings that represent the lines of the source.
10053 VALUE lines = rb_ary_new_capa(newline_list->size - (last_push ? 0 : 1));
10055 for (size_t index = 0; index < newline_list->size - 1; index++) {
10056 size_t offset = newline_list->offsets[index];
10057 size_t length = newline_list->offsets[index + 1] - offset;
10059 rb_ary_push(lines, rb_enc_str_new(start + offset, length, scope_node->encoding));
10062 // Push on the last line if we need to.
10063 if (last_push) {
10064 rb_ary_push(lines, rb_enc_str_new(start + last_offset, end - (start + last_offset), scope_node->encoding));
10067 return lines;
10071 * Attempt to load the file into memory. Return a Ruby error if the file cannot
10072 * be read.
10074 VALUE
10075 pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error)
10077 if (!pm_string_mapped_init(&result->input, RSTRING_PTR(filepath))) {
10078 #ifdef _WIN32
10079 int e = rb_w32_map_errno(GetLastError());
10080 #else
10081 int e = errno;
10082 #endif
10084 VALUE error;
10086 if (load_error) {
10087 VALUE message = rb_str_buf_new_cstr(strerror(e));
10088 rb_str_cat2(message, " -- ");
10089 rb_str_append(message, filepath);
10091 error = rb_exc_new3(rb_eLoadError, message);
10092 rb_ivar_set(error, rb_intern_const("@path"), filepath);
10093 } else {
10094 error = rb_syserr_new(e, RSTRING_PTR(filepath));
10095 RB_GC_GUARD(filepath);
10098 return error;
10101 pm_options_frozen_string_literal_init(&result->options);
10102 return Qnil;
10106 * Parse the given filepath and store the resulting scope node in the given
10107 * parse result struct. It returns a Ruby error if the file cannot be read or
10108 * if it cannot be parsed properly. It is assumed that the parse result object
10109 * is zeroed out.
10111 VALUE
10112 pm_parse_file(pm_parse_result_t *result, VALUE filepath)
10114 result->node.filepath_encoding = rb_enc_get(filepath);
10115 pm_options_filepath_set(&result->options, RSTRING_PTR(filepath));
10116 RB_GC_GUARD(filepath);
10118 pm_parser_init(&result->parser, pm_string_source(&result->input), pm_string_length(&result->input), &result->options);
10119 pm_node_t *node = pm_parse(&result->parser);
10121 VALUE error = pm_parse_process(result, node);
10123 // If we're parsing a filepath, then we need to potentially support the
10124 // SCRIPT_LINES__ constant, which can be a hash that has an array of lines
10125 // of every read file.
10126 ID id_script_lines = rb_intern("SCRIPT_LINES__");
10128 if (rb_const_defined_at(rb_cObject, id_script_lines)) {
10129 VALUE script_lines = rb_const_get_at(rb_cObject, id_script_lines);
10131 if (RB_TYPE_P(script_lines, T_HASH)) {
10132 rb_hash_aset(script_lines, filepath, pm_parse_file_script_lines(&result->node, &result->parser));
10136 return error;
10140 * Load and then parse the given filepath. It returns a Ruby error if the file
10141 * cannot be read or if it cannot be parsed properly.
10143 VALUE
10144 pm_load_parse_file(pm_parse_result_t *result, VALUE filepath)
10146 VALUE error = pm_load_file(result, filepath, false);
10147 if (NIL_P(error)) {
10148 error = pm_parse_file(result, filepath);
10151 return error;
10155 * Parse the given source that corresponds to the given filepath and store the
10156 * resulting scope node in the given parse result struct. It is assumed that the
10157 * parse result object is zeroed out. If the string fails to parse, then a Ruby
10158 * error is returned.
10160 VALUE
10161 pm_parse_string(pm_parse_result_t *result, VALUE source, VALUE filepath)
10163 rb_encoding *encoding = rb_enc_get(source);
10164 if (!rb_enc_asciicompat(encoding)) {
10165 return rb_exc_new_cstr(rb_eArgError, "invalid source encoding");
10168 pm_options_frozen_string_literal_init(&result->options);
10169 pm_string_constant_init(&result->input, RSTRING_PTR(source), RSTRING_LEN(source));
10170 pm_options_encoding_set(&result->options, rb_enc_name(encoding));
10172 result->node.filepath_encoding = rb_enc_get(filepath);
10173 pm_options_filepath_set(&result->options, RSTRING_PTR(filepath));
10174 RB_GC_GUARD(filepath);
10176 pm_parser_init(&result->parser, pm_string_source(&result->input), pm_string_length(&result->input), &result->options);
10177 pm_node_t *node = pm_parse(&result->parser);
10179 return pm_parse_process(result, node);
10183 * An implementation of fgets that is suitable for use with Ruby IO objects.
10185 static char *
10186 pm_parse_stdin_fgets(char *string, int size, void *stream)
10188 RUBY_ASSERT(size > 0);
10190 VALUE line = rb_funcall((VALUE) stream, rb_intern("gets"), 1, INT2FIX(size - 1));
10191 if (NIL_P(line)) {
10192 return NULL;
10195 const char *cstr = StringValueCStr(line);
10196 size_t length = strlen(cstr);
10198 memcpy(string, cstr, length);
10199 string[length] = '\0';
10201 return string;
10205 * Parse the source off STDIN and store the resulting scope node in the given
10206 * parse result struct. It is assumed that the parse result object is zeroed
10207 * out. If the stream fails to parse, then a Ruby error is returned.
10209 VALUE
10210 pm_parse_stdin(pm_parse_result_t *result)
10212 pm_options_frozen_string_literal_init(&result->options);
10214 pm_buffer_t buffer;
10215 pm_node_t *node = pm_parse_stream(&result->parser, &buffer, (void *) rb_stdin, pm_parse_stdin_fgets, &result->options);
10217 // Copy the allocated buffer contents into the input string so that it gets
10218 // freed. At this point we've handed over ownership, so we don't need to
10219 // free the buffer itself.
10220 pm_string_owned_init(&result->input, (uint8_t *) pm_buffer_value(&buffer), pm_buffer_length(&buffer));
10222 return pm_parse_process(result, node);
10225 #undef NEW_ISEQ
10226 #define NEW_ISEQ OLD_ISEQ
10228 #undef NEW_CHILD_ISEQ
10229 #define NEW_CHILD_ISEQ OLD_CHILD_ISEQ