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) \
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); \
65 /******************************************************************************/
66 /* These functions compile getlocal/setlocal instructions but operate on */
67 /* prism locations instead of NODEs. */
68 /******************************************************************************/
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
)));
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
);
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
)));
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
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)
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
;
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.
158 parse_integer_value(const pm_integer_t
*integer
)
162 if (integer
->values
== NULL
) {
163 result
= UINT2NUM(integer
->value
);
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);
190 * Convert the value of an integer node into a Ruby Integer.
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.
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.
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.
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
);
236 case PM_INTEGER_NODE
: {
237 imaginary_part
= parse_integer((const pm_integer_node_t
*) node
->numeric
);
240 case PM_RATIONAL_NODE
: {
241 imaginary_part
= parse_rational((const pm_rational_node_t
*) node
->numeric
);
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
);
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.
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();
274 encoding
= default_encoding
;
277 return rb_enc_str_new((const char *) pm_string_source(string
), pm_string_length(string
), encoding
);
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();
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
);
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();
323 encoding
= scope_node
->encoding
;
326 return rb_intern3((const char *) pm_string_source(&symbol
->unescaped
), pm_string_length(&symbol
->unescaped
), encoding
);
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. */
337 parse_regexp_error(rb_iseq_t
*iseq
, int32_t line_number
, const char *fmt
, ...)
341 VALUE error
= rb_syntax_error_append(Qnil
, rb_iseq_path(iseq
), line_number
, -1, NULL
, "%" PRIsVALUE
, args
);
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();
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
));
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
];
382 switch (PM_NODE_TYPE(part
)) {
384 if (implicit_regexp_encoding
!= NULL
) {
386 string
= parse_regexp_string_part(iseq
, scope_node
, part
, &((const pm_string_node_t
*) part
)->unescaped
, implicit_regexp_encoding
, explicit_regexp_encoding
);
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
));
395 string
= parse_string_encoded(part
, &((const pm_string_node_t
*) part
)->unescaped
, scope_node
->encoding
);
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);
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);
407 RUBY_ASSERT(false && "unexpected node type in pm_static_literal_concat");
411 if (current
!= Qnil
) {
412 current
= rb_str_concat(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
428 #define ENC_Windows_31J 3
432 * Check the prism flags of a regular expression-like node and return the flags
433 * that are expected by the CRuby VM.
436 parse_regexp_flags(const pm_node_t
*node
)
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
;
474 #undef RE_OPTION_ENCODING_SHIFT
475 #undef RE_OPTION_ENCODING
476 #undef ARG_ENCODING_FIXED
477 #undef ARG_ENCODING_NONE
480 #undef ENC_Windows_31J
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
);
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
);
512 VALUE message
= rb_attr_get(rb_errinfo(), idMesg
);
513 rb_set_errinfo(errinfo
);
515 parse_regexp_error(iseq
, line_number
, "%" PRIsVALUE
, message
);
519 rb_obj_freeze(regexp
);
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
);
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
);
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
)
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
;
563 if (implicit_regexp_encoding
== NULL
) {
564 string_value
= parse_string_encoded(part
, &string_node
->unescaped
, scope_node
->encoding
);
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
);
574 current_string
= string_value
;
575 if (index
!= 0) current_location
= PM_NODE_END_LINE_COLUMN(scope_node
->parser
, part
);
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];
590 if (implicit_regexp_encoding
== NULL
) {
591 string_value
= parse_string_encoded(part
, &string_node
->unescaped
, scope_node
->encoding
);
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
);
601 current_string
= string_value
;
602 current_location
= PM_NODE_START_LINE_COLUMN(scope_node
->parser
, part
);
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();
617 encoding
= implicit_regexp_encoding
;
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
;
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
);
648 PUSH_INSN1(ret
, current_location
, putobject
, current_string
);
651 current_string
= Qnil
;
656 PUSH_INSN(ret
, *node_location
, putnil
);
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
));
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
);
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
);
683 return rb_fstring_lit("<compiled>");
688 * Return a static literal string, optionally with attached debugging
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
);
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.
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
));
732 return parse_float((const pm_float_node_t
*) 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
);
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
);
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
);
800 return ID2SYM(parse_string_symbol(scope_node
, (const pm_symbol_node_t
*) node
));
804 rb_bug("Don't have a literal value for node type %s", pm_node_type_to_str(PM_NODE_TYPE(node
)));
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))
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
);
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
);
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
);
858 PUSH_LABEL(seq
, label
);
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
);
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
);
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
);
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
);
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
);
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
);
931 switch (PM_NODE_TYPE(cond
)) {
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
);
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
);
948 PUSH_INSNL(ret
, location
, jump
, else_label
);
951 case PM_IMAGINARY_NODE
:
952 case PM_INTEGER_NODE
:
954 case PM_RATIONAL_NODE
:
955 case PM_REGULAR_EXPRESSION_NODE
:
959 PUSH_INSNL(ret
, location
, jump
, then_label
);
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
);
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);
970 pm_compile_node(iseq
, cond
, ret
, false, scope_node
);
975 PUSH_INSNL(ret
, location
, branchunless
, else_label
);
976 PUSH_INSNL(ret
, location
, jump
, then_label
);
980 * Compile an if or unless node.
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
);
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
}
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
);
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
);
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
);
1077 PUSH_LABEL(ret
, end_label
);
1084 * Compile a while or until loop.
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
);
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
;
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
;
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
)) {
1177 if (scope_node
->previous
) {
1178 scope_node
= scope_node
->previous
;
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
;
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
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];
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
),
1215 isolated_depth
? isolated_depth
+ 1 : 0,
1216 type
, ISEQ_COMPILE_DATA(iseq
)->option
);
1217 debugs("[new_child_iseq]< ---------------------------------------\n");
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
;
1230 return VM_DEFINECLASS_FLAG_SCOPED
;
1233 /* toplevel class ::Foo */
1234 PUSH_INSN1(ret
, *node_location
, putobject
, rb_cObject
);
1235 return VM_DEFINECLASS_FLAG_SCOPED
;
1239 /* class at cbase Foo */
1240 PUSH_INSN1(ret
, *node_location
, putspecialobject
, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE
));
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.
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
);
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
);
1272 PUSH_INSNL(ret
, location
, branchunless
, lcfin
);
1275 PUSH_INSNL(ret
, location
, branchif
, lcfin
);
1278 if (!popped
) PUSH_INSN(ret
, location
, pop
);
1279 PM_COMPILE_NOT_POPPED(value
);
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.
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
);
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) {
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
);
1340 // Here we are merging plain assoc nodes into the hash on
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
);
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.
1356 PUSH_INSN1(ret
, location
, putspecialobject
, INT2FIX(VM_SPECIAL_OBJECT_VMCORE
));
1357 PUSH_INSN1(ret
, location
, newhash
, INT2FIX(0));
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
);
1377 RUBY_ASSERT("Invalid node type for hash" && false);
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
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.
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
;
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
;
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
);
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
);
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
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
);
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
);
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
];
1479 if (rb_ary_entry(keyword_indices
, (long) element_index
) == Qtrue
) {
1480 keywords
[keyword_index
++] = pm_static_literal_value(iseq
, assoc
->key
, scope_node
);
1484 PM_COMPILE(assoc
->value
);
1487 RUBY_ASSERT(keyword_index
== size
);
1490 // If they aren't all symbol keys then we need to
1491 // construct a new hash and pass that as an argument.
1493 *flags
|= VM_CALL_KW_SPLAT
;
1495 size_t size
= elements
->size
;
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));
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
);
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
;
1529 // If this is the first splat array seen and it's not the
1530 // last parameter, we want splatarray to dup it.
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.
1544 PUSH_INSN1(ret
, location
, splatarray
, Qfalse
);
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.
1554 PUSH_INSN1(ret
, location
, splatarray
, Qfalse
);
1555 PUSH_INSN(ret
, location
, concatarray
);
1559 post_splat_counter
= 0;
1563 case PM_FORWARDING_ARGUMENTS_NODE
: {
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
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
);
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
);
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
);
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.
1594 // Stack items are turned into an array and concatenated in
1595 // the following cases:
1597 // If the next node is a splat:
1601 // If the next node is a kwarg or kwarg splat:
1603 // foo(*a, b, c: :d)
1606 // If the next node is NULL (we have hit the end):
1609 if (index
== arguments
->size
- 1) {
1610 RUBY_ASSERT(post_splat_counter
> 0);
1611 PUSH_INSN1(ret
, location
, pushtoarray
, INT2FIX(post_splat_counter
));
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
);
1623 case PM_SPLAT_NODE
: {
1624 PUSH_INSN1(ret
, location
, newarray
, INT2FIX(post_splat_counter
));
1625 PUSH_INSN(ret
, location
, concatarray
);
1641 if (has_splat
) orig_argc
++;
1642 if (has_keyword_splat
) orig_argc
++;
1646 // Compile the argument parts of a call
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
);
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:
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.
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
)) {
1708 PUSH_INSN(ret
, location
, splatkw
);
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;
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));
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
);
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));
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
) {
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
);
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
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
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
)) {
1824 PUSH_INSN(ret
, location
, splatkw
);
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;
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
);
1852 PUSH_INSNL(ret
, location
, branchif
, label
);
1855 PUSH_INSN(ret
, location
, pop
);
1856 PM_COMPILE_NOT_POPPED(value
);
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
);
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));
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
) {
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
);
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
);
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.
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
);
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.
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
);
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.
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
);
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
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
);
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.
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
);
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
);
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.
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
);
2134 * When matching fails, an appropriate error must be raised. This function is
2135 * responsible for compiling in those error raising instructions.
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.
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:
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
);
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
) {
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
);
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));
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
);
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
);
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
);
2492 PUSH_INSN(ret
, location
, putnil
);
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
);
2505 PUSH_SEND(ret
, location
, rb_intern("dup"), INT2FIX(0));
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
);
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
);
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
);
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);
2586 rb_bug("unreachable");
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
);
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
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
);
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
));
2653 PUSH_SETLOCAL(ret
, location
, index
.index
, index
.level
);
2654 PUSH_INSNL(ret
, location
, jump
, matched_label
);
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
));
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
;
2698 case PM_CLASS_VARIABLE_READ_NODE
:
2699 case PM_CONSTANT_PATH_NODE
:
2700 case PM_CONSTANT_READ_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
:
2714 case PM_SOURCE_ENCODING_NODE
:
2715 case PM_SOURCE_FILE_NODE
:
2716 case PM_SOURCE_LINE_NODE
:
2718 case PM_RATIONAL_NODE
:
2719 case PM_REGULAR_EXPRESSION_NODE
:
2721 case PM_STRING_NODE
:
2722 case PM_SYMBOL_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
);
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
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
));
2753 case PM_UNLESS_NODE
: {
2754 // If and unless nodes can show up here as guards on `in` clauses. This
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];
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
);
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
);
2812 PUSH_INSNL(ret
, location
, branchif
, unmatched_label
);
2815 PUSH_INSNL(ret
, location
, jump
, matched_label
);
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
)));
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.
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
;
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
;
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
;
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
;
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
;
2891 const pm_for_node_t
*cast
= (const pm_for_node_t
*) node
;
2892 scope
->body
= (pm_node_t
*) cast
->statements
;
2895 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE
: {
2896 RUBY_ASSERT(node
->flags
& PM_REGULAR_EXPRESSION_FLAGS_ONCE
);
2897 scope
->body
= (pm_node_t
*) node
;
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
;
2910 scope
->base
.location
.start
= cast
->operator_loc
.end
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
2953 rb_bug("unreachable");
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".
2978 pm_compile_retry_end_label(rb_iseq_t
*iseq
, LINK_ANCHOR
*const ret
, LABEL
*retry_end_l
)
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
;
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)) {
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")) {
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")) {
3029 // Compile Primitive.attr! :leaf, ...
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");
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
)));
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
);
3058 COMPILE_ERROR(iseq
, node_location
->line
, "unknown argument to attr!: %s", RSTRING_PTR(string
));
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");
3074 if (arguments
->arguments
.size
!= 1) {
3075 COMPILE_ERROR(iseq
, node_location
->line
, "arg!: too many argument");
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
)));
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
));
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");
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");
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");
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");
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
= ¶meters
,
3134 .body
= (pm_node_t
*) if_node
->statements
,
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(
3147 rb_iseq_base_label(iseq
),
3149 rb_iseq_realpath(iseq
),
3150 node_location
->line
,
3154 ISEQ_COMPILE_DATA(iseq
)->option
3157 pm_scope_node_destroy(&next_scope_node
);
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.");
3171 #define BUILTIN_INLINE_PREFIX "_bi"
3172 char inline_func
[sizeof(BUILTIN_INLINE_PREFIX
) + DECIMAL_SIZE_OF(int)];
3173 bool cconst
= false;
3175 const struct rb_builtin_function
*bf
= iseq_builtin_function_lookup(iseq
, builtin_func
);
3178 if (strcmp("cstmt!", builtin_func
) == 0 || strcmp("cexpr!", builtin_func
) == 0) {
3181 else if (strcmp("cconst!", builtin_func
) == 0) {
3184 else if (strcmp("cinit!", builtin_func
) == 0) {
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) {
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
);
3206 rb_bug("can't find builtin function:%s", builtin_func
);
3209 COMPILE_ERROR(iseq
, node_location
->line
, "can't find builtin function:%s", builtin_func
);
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
;
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
);
3227 // fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
3229 DECL_ANCHOR(args_seq
);
3230 INIT_ANCHOR(args_seq
);
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
);
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
));
3246 PUSH_SEQ(ret
, args_seq
);
3247 PUSH_INSN1(ret
, *node_location
, invokebuiltin
, bf
);
3250 if (popped
) PUSH_INSN(ret
, *node_location
, pop
);
3255 * Compile a call node into the given iseq.
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
);
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
;
3318 if (PM_NODE_FLAG_P(call_node
, PM_CALL_NODE_FLAGS_VARIABLE_CALL
)) {
3319 flags
|= VM_CALL_VCALL
;
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
);
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
;
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
);
3395 lfinish
[1] = NEW_LABEL(location
.line
);
3397 PUSH_INSNL(ret
, location
, branchunless
, lfinish
[1]);
3399 dtype
= DEFINED_TRUE
;
3403 dtype
= DEFINED_NIL
;
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
);
3419 // Otherwise, we have parentheses wrapping multiple statements, in
3420 // which case this is defined as "expression".
3421 dtype
= DEFINED_EXPR
;
3427 dtype
= DEFINED_SELF
;
3430 dtype
= DEFINED_TRUE
;
3433 dtype
= DEFINED_FALSE
;
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);
3443 lfinish
[1] = NEW_LABEL(location
.line
);
3446 PUSH_INSNL(ret
, location
, branchunless
, lfinish
[1]);
3454 case PM_CASE_MATCH_NODE
:
3457 case PM_DEFINED_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
:
3477 case PM_RATIONAL_NODE
:
3479 case PM_REGULAR_EXPRESSION_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
:
3491 case PM_X_STRING_NODE
:
3492 dtype
= DEFINED_EXPR
;
3494 case PM_LOCAL_VARIABLE_READ_NODE
:
3495 dtype
= DEFINED_LVAR
;
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
));
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
));
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
));
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
));
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
));
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
));
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
);
3564 PUSH_INSN1(ret
, location
, putobject
, rb_cObject
);
3567 PUSH_INSN3(ret
, location
, defined
, INT2FIX(DEFINED_CONST_FROM
), name
, PUSH_VAL(DEFINED_CONST
));
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
);
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
));
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
));
3611 PUSH_INSN(ret
, location
, putnil
);
3612 PUSH_INSN3(ret
, location
, defined
, INT2FIX(DEFINED_YIELD
), 0, PUSH_VAL(DEFINED_YIELD
));
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
));
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
;
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
));
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);
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(
3685 rb_str_concat(rb_str_new2("defined guard in "), ISEQ_BODY(iseq
)->location
.label
),
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]);
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
)
3704 LINK_ELEMENT
*last
= ret
->last
;
3706 lfinish
[0] = NEW_LABEL(node_location
->line
);
3711 pm_defined_expr(iseq
, node
, node_location
, ret
, popped
, scope_node
, in_condition
, lfinish
, false);
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
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
);
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
);
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
);
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
;
3774 pm_local_table_insert_func(st_data_t
*key
, st_data_t
*value
, st_data_t arg
, int 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
;
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.
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
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.
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
);
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
);
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
);
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
);
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.
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.
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
);
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
);
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
);
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
);
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.
3957 // The index of the stack from the base of the entire multi target at which
3958 // the parent expression is located.
3961 // The number of slots in the stack that this node occupies.
3964 // The position of the node in the list of targets.
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
3979 // The total number of slots in the stack that this multi target occupies.
3982 // The position of the current node being compiled. This is forwarded to
3983 // nodes when they are allocated.
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.
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
);
4001 node
->stack_index
= state
->stack_size
+ 1;
4002 node
->stack_size
= stack_size
;
4003 node
->position
= state
->position
;
4006 if (state
->head
== NULL
) {
4011 state
->tail
->next
= 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.
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
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
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
;
4056 current
= current
->next
;
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
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.
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.
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
);
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
));
4123 case PM_CONSTANT_TARGET_NODE
: {
4124 // Constant targets have no parent expression, so they only need to
4125 // compile the write.
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
));
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
));
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
));
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
);
4176 PUSH_INSN1(parents
, location
, putobject
, rb_cObject
);
4179 if (state
== NULL
) {
4180 PUSH_INSN(writes
, location
, swap
);
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
);
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
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
);
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 &
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
);
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);
4259 PUSH_INSN(writes
, location
, swap
);
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
) {
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
) {
4285 PUSH_INSN(writes
, location
, pop
);
4288 for (int index
= 0; index
< argc
+ 1; index
++) {
4289 PUSH_INSN(cleanup
, location
, pop
);
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
;
4308 pm_compile_multi_target_node(iseq
, node
, parents
, writes
, cleanup
, scope_node
, state
);
4309 if (state
!= NULL
) state
->position
= before_position
;
4314 rb_bug("Unexpected node type: %s", pm_node_type_to_str(PM_NODE_TYPE(node
)));
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
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
;
4337 rights
= &cast
->rights
;
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
;
4344 rights
= &cast
->rights
;
4348 rb_bug("Unsupported node %s", pm_node_type_to_str(PM_NODE_TYPE(node
)));
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
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.
4378 const pm_node_t
*target
= ((const pm_splat_node_t
*) rest
)->expression
;
4379 state
->position
= 1 + rights
->size
+ base_position
;
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.
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
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
);
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 };
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
);
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
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
);
4492 rb_bug("Unexpected node type for index in for node: %s", pm_node_type_to_str(PM_NODE_TYPE(node
)));
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(
4511 rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq
)->location
.label
),
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
);
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
);
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
;
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
);
4573 if (cast
->statements
) {
4574 PM_COMPILE((const pm_node_t
*) cast
->statements
);
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(
4589 rb_str_concat(rb_str_new2("ensure in "), ISEQ_BODY(iseq
)->location
.label
),
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
) {
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.
4617 pm_opt_str_freeze_p(const rb_iseq_t
*iseq
, const pm_call_node_t
*node
)
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.
4634 pm_opt_aref_with_p(const rb_iseq_t
*iseq
, const pm_call_node_t
*node
)
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.
4653 pm_opt_aset_with_p(const rb_iseq_t
*iseq
, const pm_call_node_t
*node
)
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.
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
);
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.
4693 pm_constant_path_parts(const pm_node_t
*node
, const pm_scope_node_t
*scope_node
)
4695 VALUE parts
= rb_ary_new();
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
);
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
));
4716 node
= cast
->parent
;
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
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
);
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
);
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
);
4762 PM_COMPILE_INTO_ANCHOR(prefix
, node
);
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.
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
)
4788 switch (PM_NODE_TYPE(node
)) {
4789 case PM_FLOAT_NODE
: {
4790 key
= pm_static_literal_value(iseq
, node
, scope_node
);
4793 if (modf(RFLOAT_VALUE(key
), &intptr
) == 0.0) {
4794 key
= (FIXABLE(intptr
) ? LONG2FIX((long) intptr
) : rb_dbl2big(intptr
));
4800 case PM_INTEGER_NODE
:
4802 case PM_SOURCE_FILE_NODE
:
4803 case PM_SOURCE_LINE_NODE
:
4804 case PM_SYMBOL_NODE
:
4806 key
= pm_static_literal_value(iseq
, node
, scope_node
);
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
);
4817 if (NIL_P(rb_hash_lookup(dispatch
, key
))) {
4818 rb_hash_aset(dispatch
, key
, ((VALUE
) label
) | 1);
4825 * Return the object that will be pushed onto the stack for the given node.
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
)) {
4834 case PM_SYMBOL_NODE
:
4835 case PM_REGULAR_EXPRESSION_NODE
:
4836 case PM_SOURCE_LINE_NODE
:
4837 case PM_INTEGER_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
);
4887 * Compile the instructions for pushing the value that will be written to a
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
);
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
;
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
));
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
));
4922 case PM_HASH_NODE
: {
4923 const pm_hash_node_t
*cast
= (const pm_hash_node_t
*) node
;
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));
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
));
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
));
4982 * Compile a constant write node, either in the context of a ractor pragma or
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);
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
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);
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
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);
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
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);
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.
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
)));
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
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
);
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);
5151 PM_COMPILE_NOT_POPPED(node
->value
);
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
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
);
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);
5196 PM_COMPILE_NOT_POPPED(node
->value
);
5200 PUSH_INSN1(ret
, location
, topn
, INT2FIX(1));
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
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
);
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);
5253 PM_COMPILE_NOT_POPPED(node
->value
);
5257 PUSH_INSN1(ret
, location
, topn
, INT2FIX(1));
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.
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
);
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);
5299 PM_COMPILE_NOT_POPPED(node
->value
);
5302 PUSH_CALL(ret
, location
, method_id
, INT2FIX(1));
5303 PUSH_INSN(ret
, location
, swap
);
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
5321 * scope_node - Stores parser and local information
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
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
: {
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
);
5376 case PM_ALIAS_METHOD_NODE
: {
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
);
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
);
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
) {
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]);
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.
5441 if (elements
->size
) {
5442 VALUE value
= pm_static_literal_value(iseq
, node
, scope_node
);
5443 PUSH_INSN1(ret
, location
, duparray
, value
);
5446 PUSH_INSN1(ret
, location
, newarray
, INT2FIX(0));
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
));
5480 // We don't want to emit a concat array in the case
5481 // where we're seeing our first splat, and already have
5483 if (need_to_concat_array
) PUSH_INSN(ret
, location
, concatarray
);
5486 if (splat_element
->expression
) {
5487 PM_COMPILE_NOT_POPPED(splat_element
->expression
);
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
);
5495 PUSH_INSN(ret
, location
, concatarray
);
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
)) {
5509 has_kw_splat
= true;
5510 pm_compile_hash_elements(iseq
, element
, &((const pm_keyword_hash_node_t
*) element
)->elements
, ret
, scope_node
);
5514 PM_COMPILE_NOT_POPPED(element
);
5518 if (new_array_size
) {
5520 PUSH_INSN1(ret
, location
, newarraykwsplat
, INT2FIX(new_array_size
));
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
);
5533 case PM_ASSOC_NODE
: {
5539 const pm_assoc_node_t
*cast
= (const pm_assoc_node_t
*) node
;
5541 PM_COMPILE(cast
->key
);
5542 PM_COMPILE(cast
->value
);
5546 case PM_ASSOC_SPLAT_NODE
: {
5550 // def foo(**); bar(**); end
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
);
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
);
5564 case PM_BACK_REFERENCE_READ_NODE
: {
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
);
5576 case PM_BEGIN_NODE
: {
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
);
5591 // If there is neither ensure or rescue, the just compile the
5593 if (cast
->statements
!= NULL
) {
5594 PM_COMPILE((const pm_node_t
*) cast
->statements
);
5597 PUSH_SYNTHETIC_PUTNIL(ret
, iseq
);
5602 case PM_BLOCK_ARGUMENT_NODE
: {
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
);
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
));
5617 case PM_BREAK_NODE
: {
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
)) {
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
);
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
);
5645 const rb_iseq_t
*ip
= iseq
;
5648 if (!ISEQ_COMPILE_DATA(ip
)) {
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
) {
5659 else if (ISEQ_BODY(ip
)->type
== ISEQ_TYPE_EVAL
) {
5660 COMPILE_ERROR(iseq
, location
.line
, "Can't escape from eval with break");
5664 ip
= ISEQ_BODY(ip
)->parent_iseq
;
5668 /* escape from block */
5669 if (cast
->arguments
!= NULL
) {
5670 PM_COMPILE_NOT_POPPED((const pm_node_t
*) cast
->arguments
);
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
);
5682 COMPILE_ERROR(iseq
, location
.line
, "Invalid break");
5686 case PM_CALL_NODE
: {
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
);
5709 LABEL
*start
= NEW_LABEL(location
.line
);
5710 if (cast
->block
) PUSH_LABEL(ret
, start
);
5712 switch (method_id
) {
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
));
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
));
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
));
5738 PUSH_INSN(ret
, location
, pop
);
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]);
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
);
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
);
5774 PM_COMPILE_NOT_POPPED(cast
->receiver
);
5777 pm_compile_call(iseq
, cast
, ret
, popped
, scope_node
, method_id
, start
);
5780 case PM_CALL_AND_WRITE_NODE
: {
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
);
5787 case PM_CALL_OR_WRITE_NODE
: {
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
);
5794 case PM_CALL_OPERATOR_WRITE_NODE
: {
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
5804 const pm_call_operator_write_node_t
*cast
= (const pm_call_operator_write_node_t
*) node
;
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));
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
);
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 };
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
);
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
);
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
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
);
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
);
5947 PUSH_SYNTHETIC_PUTNIL(cond_seq
, iseq
);
5950 // Finally, jump to the end label if none of the other conditions
5952 PUSH_INSNL(cond_seq
, location
, jump
, end_label
);
5953 PUSH_SEQ(ret
, cond_seq
);
5956 // Establish branch coverage for the case node.
5957 VALUE branches
= Qfalse
;
5958 rb_code_location_t case_location
= { 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
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
));
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
);
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
);
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
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
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
);
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
);
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
;
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
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
);
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
);
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
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
);
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
);
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
);
6264 case PM_CLASS_NODE
: {
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
);
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
);
6296 case PM_CLASS_VARIABLE_AND_WRITE_NODE
: {
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
);
6319 case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE
: {
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
));
6339 case PM_CLASS_VARIABLE_OR_WRITE_NODE
: {
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
);
6368 case PM_CLASS_VARIABLE_READ_NODE
: {
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
));
6378 case PM_CLASS_VARIABLE_WRITE_NODE
: {
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
));
6390 case PM_CONSTANT_PATH_NODE
: {
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
);
6400 DECL_ANCHOR(prefix
);
6401 INIT_ANCHOR(prefix
);
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
);
6411 PUSH_SEQ(ret
, prefix
);
6414 PUSH_SEQ(ret
, body
);
6417 if (popped
) PUSH_INSN(ret
, location
, pop
);
6420 case PM_CONSTANT_PATH_AND_WRITE_NODE
: {
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
);
6427 case PM_CONSTANT_PATH_OR_WRITE_NODE
: {
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
);
6434 case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE
: {
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
);
6441 case PM_CONSTANT_PATH_WRITE_NODE
: {
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
);
6448 case PM_CONSTANT_READ_NODE
: {
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
);
6459 case PM_CONSTANT_AND_WRITE_NODE
: {
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
);
6466 case PM_CONSTANT_OR_WRITE_NODE
: {
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
);
6473 case PM_CONSTANT_OPERATOR_WRITE_NODE
: {
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
);
6480 case PM_CONSTANT_WRITE_NODE
: {
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
);
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
);
6507 PUSH_INSN2(ret
, location
, definemethod
, ID2SYM(method_name
), method_iseq
);
6509 RB_OBJ_WRITTEN(iseq
, Qundef
, (VALUE
) method_iseq
);
6512 PUSH_INSN1(ret
, location
, putobject
, ID2SYM(method_name
));
6517 case PM_DEFINED_NODE
: {
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);
6524 case PM_EMBEDDED_STATEMENTS_NODE
: {
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
));
6533 PUSH_SYNTHETIC_PUTNIL(ret
, iseq
);
6536 if (popped
) PUSH_INSN(ret
, location
, pop
);
6539 case PM_EMBEDDED_VARIABLE_NODE
: {
6542 const pm_embedded_variable_node_t
*cast
= (const pm_embedded_variable_node_t
*) node
;
6543 PM_COMPILE(cast
->variable
);
6546 case PM_FALSE_NODE
: {
6550 PUSH_INSN1(ret
, location
, putobject
, Qfalse
);
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
);
6572 case PM_ELSE_NODE
: {
6573 // if foo then bar else baz end
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
);
6581 PUSH_SYNTHETIC_PUTNIL(ret
, iseq
);
6586 case PM_FLIP_FLOP_NODE
: {
6587 // if foo .. bar; end
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
);
6606 case PM_FLOAT_NODE
: {
6610 PUSH_INSN1(ret
, location
, putobject
, parse_float((const pm_float_node_t
*) 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
);
6647 case PM_FORWARDING_ARGUMENTS_NODE
: {
6648 rb_bug("Cannot compile a ForwardingArgumentsNode directly\n");
6651 case PM_FORWARDING_SUPER_NODE
: {
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
);
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
);
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
) {
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
;
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
);
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
;
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));
6759 PUSH_INSN1(args
, location
, newhash
, INT2FIX(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
);
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
);
6791 case PM_GLOBAL_VARIABLE_AND_WRITE_NODE
: {
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
);
6812 case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE
: {
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
);
6830 case PM_GLOBAL_VARIABLE_OR_WRITE_NODE
: {
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
);
6858 case PM_GLOBAL_VARIABLE_READ_NODE
: {
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
);
6869 case PM_GLOBAL_VARIABLE_WRITE_NODE
: {
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
));
6881 case PM_HASH_NODE
: {
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.
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
);
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
;
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
]);
6918 pm_compile_hash_elements(iseq
, node
, elements
, ret
, scope_node
);
6925 // if foo then bar end
6926 // ^^^^^^^^^^^^^^^^^^^
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
);
6937 case PM_IMAGINARY_NODE
: {
6941 PUSH_INSN1(ret
, location
, putobject
, parse_imaginary((const pm_imaginary_node_t
*) node
));
6945 case PM_IMPLICIT_NODE
: {
6946 // Implicit nodes mark places in the syntax tree where explicit syntax
6947 // was omitted, but implied. For example,
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
);
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");
6964 case PM_INDEX_OPERATOR_WRITE_NODE
: {
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
);
6971 case PM_INDEX_AND_WRITE_NODE
: {
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
);
6978 case PM_INDEX_OR_WRITE_NODE
: {
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
);
6985 case PM_INSTANCE_VARIABLE_AND_WRITE_NODE
: {
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
);
7008 case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE
: {
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
));
7028 case PM_INSTANCE_VARIABLE_OR_WRITE_NODE
: {
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
);
7051 case PM_INSTANCE_VARIABLE_READ_NODE
: {
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
));
7061 case PM_INSTANCE_VARIABLE_WRITE_NODE
: {
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
));
7073 case PM_INTEGER_NODE
: {
7077 PUSH_INSN1(ret
, location
, putobject
, parse_integer((const pm_integer_node_t
*) node
));
7081 case PM_INTERPOLATED_MATCH_LAST_LINE_NODE
: {
7082 // if /foo #{bar}/ then end
7084 if (PM_NODE_FLAG_P(node
, PM_NODE_FLAG_STATIC_LITERAL
)) {
7086 VALUE regexp
= pm_static_literal_value(iseq
, node
, scope_node
);
7087 PUSH_INSN1(ret
, location
, putobject
, regexp
);
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
);
7100 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE
: {
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
);
7122 if (PM_NODE_FLAG_P(node
, PM_NODE_FLAG_STATIC_LITERAL
)) {
7124 VALUE regexp
= pm_static_literal_value(iseq
, node
, scope_node
);
7125 PUSH_INSN1(ret
, location
, putobject
, regexp
);
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
);
7135 case PM_INTERPOLATED_STRING_NODE
: {
7138 if (PM_NODE_FLAG_P(node
, PM_NODE_FLAG_STATIC_LITERAL
)) {
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
);
7149 PUSH_INSN1(ret
, location
, putchilledstring
, string
);
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
);
7162 case PM_INTERPOLATED_SYMBOL_NODE
: {
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
)) {
7169 VALUE symbol
= pm_static_literal_value(iseq
, node
, scope_node
);
7170 PUSH_INSN1(ret
, location
, putobject
, symbol
);
7174 int length
= pm_interpolated_node_compile(iseq
, &cast
->parts
, &location
, ret
, popped
, scope_node
, NULL
, NULL
);
7176 PUSH_INSN1(ret
, location
, concatstrings
, INT2FIX(length
));
7180 PUSH_INSN(ret
, location
, intern
);
7183 PUSH_INSN(ret
, location
, pop
);
7189 case PM_INTERPOLATED_X_STRING_NODE
: {
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
);
7204 case PM_IT_LOCAL_VARIABLE_READ_NODE
: {
7208 PUSH_GETLOCAL(ret
, location
, scope_node
->local_table_for_iseq_size
, 0);
7213 case PM_KEYWORD_HASH_NODE
: {
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));
7227 case PM_LAMBDA_NODE
: {
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
);
7247 case PM_LOCAL_VARIABLE_AND_WRITE_NODE
: {
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
);
7268 case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE
: {
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
);
7286 case PM_LOCAL_VARIABLE_OR_WRITE_NODE
: {
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
);
7313 case PM_LOCAL_VARIABLE_READ_NODE
: {
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
);
7324 case PM_LOCAL_VARIABLE_WRITE_NODE
: {
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
);
7335 case PM_MATCH_LAST_LINE_NODE
: {
7336 // if /foo/ then end
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
);
7347 case PM_MATCH_PREDICATE_NODE
: {
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
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
);
7387 case PM_MATCH_REQUIRED_NODE
: {
7391 // A match required node represents pattern matching against a single
7392 // pattern using the => operator. For example,
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
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
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
);
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
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
);
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
);
7524 case PM_MISSING_NODE
: {
7525 rb_bug("A pm_missing_node_t should not exist in prism's AST.");
7528 case PM_MODULE_NODE
: {
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
);
7550 case PM_REQUIRED_PARAMETER_NODE
: {
7551 // def foo(bar); end
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
);
7559 case PM_MULTI_WRITE_NODE
: {
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
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
);
7597 case PM_NEXT_NODE
: {
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
);
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
);
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
);
7644 const rb_iseq_t
*ip
= iseq
;
7645 unsigned long throw_flag
= 0;
7648 if (!ISEQ_COMPILE_DATA(ip
)) {
7653 throw_flag
= VM_THROW_NO_ESCAPE_FLAG
;
7654 if (ISEQ_COMPILE_DATA(ip
)->redo_label
!= 0) {
7658 else if (ISEQ_BODY(ip
)->type
== ISEQ_TYPE_BLOCK
) {
7661 else if (ISEQ_BODY(ip
)->type
== ISEQ_TYPE_EVAL
) {
7662 COMPILE_ERROR(iseq
, location
.line
, "Can't escape from eval with next");
7666 ip
= ISEQ_BODY(ip
)->parent_iseq
;
7669 if (cast
->arguments
) {
7670 PM_COMPILE_NOT_POPPED((const pm_node_t
*) cast
->arguments
);
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
);
7680 COMPILE_ERROR(iseq
, location
.line
, "Invalid next");
7691 PUSH_INSN(ret
, location
, putnil
);
7696 case PM_NO_KEYWORDS_PARAMETER_NODE
: {
7697 // def foo(**nil); end
7699 ISEQ_BODY(iseq
)->param
.flags
.accepts_no_kwarg
= TRUE
;
7702 case PM_NUMBERED_REFERENCE_READ_NODE
: {
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));
7712 PUSH_INSN(ret
, location
, putnil
);
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
);
7735 case PM_OPTIONAL_PARAMETER_NODE
: {
7736 // def foo(bar = 1); end
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
);
7746 case PM_PARENTHESES_NODE
: {
7752 const pm_parentheses_node_t
*cast
= (const pm_parentheses_node_t
*) node
;
7754 if (cast
->body
!= NULL
) {
7755 PM_COMPILE(cast
->body
);
7758 PUSH_INSN(ret
, location
, putnil
);
7763 case PM_PRE_EXECUTION_NODE
: {
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
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
);
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
;
7803 case PM_POST_EXECUTION_NODE
: {
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
;
7825 case PM_RANGE_NODE
: {
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
)) {
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
,
7842 PUSH_INSN1(ret
, location
, putobject
, val
);
7846 if (cast
->left
== NULL
) {
7847 PUSH_INSN(ret
, location
, putnil
);
7850 PM_COMPILE(cast
->left
);
7853 if (cast
->right
== NULL
) {
7854 PUSH_INSN(ret
, location
, putnil
);
7857 PM_COMPILE(cast
->right
);
7861 PUSH_INSN1(ret
, location
, newrange
, INT2FIX(exclude_end
? 1 : 0));
7866 case PM_RATIONAL_NODE
: {
7870 PUSH_INSN1(ret
, location
, putobject
, parse_rational((const pm_rational_node_t
*) node
));
7874 case PM_REDO_NODE
: {
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
);
7900 const rb_iseq_t
*ip
= iseq
;
7903 if (!ISEQ_COMPILE_DATA(ip
)) {
7908 if (ISEQ_COMPILE_DATA(ip
)->redo_label
!= 0) {
7911 else if (ISEQ_BODY(ip
)->type
== ISEQ_TYPE_BLOCK
) {
7914 else if (ISEQ_BODY(ip
)->type
== ISEQ_TYPE_EVAL
) {
7915 COMPILE_ERROR(iseq
, location
.line
, "Can't escape from eval with redo");
7919 ip
= ISEQ_BODY(ip
)->parent_iseq
;
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
);
7928 COMPILE_ERROR(iseq
, location
.line
, "Invalid redo");
7934 case PM_REGULAR_EXPRESSION_NODE
: {
7938 VALUE regexp
= pm_static_literal_value(iseq
, node
, scope_node
);
7939 PUSH_INSN1(ret
, location
, putobject
, regexp
);
7943 case PM_RESCUE_NODE
: {
7944 // begin; rescue; end
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
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
);
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
;
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
);
8038 PUSH_GETLOCAL(ret
, location
, 1, 0);
8043 case PM_RESCUE_MODIFIER_NODE
: {
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(
8053 rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq
)->location
.label
),
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
);
8077 case PM_RETURN_NODE
: {
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
)) {
8088 PM_COMPILE_NOT_POPPED((const pm_node_t
*) arguments
);
8091 PUSH_INSN(ret
, location
, putnil
);
8095 enum rb_iseq_type type
= ISEQ_BODY(iseq
)->type
;
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
) {
8107 case ISEQ_TYPE_MAIN
:
8109 rb_warn("argument of top-level return is ignored");
8111 if (parent_iseq
== iseq
) {
8112 type
= ISEQ_TYPE_METHOD
;
8119 if (type
== ISEQ_TYPE_METHOD
) {
8120 splabel
= NEW_LABEL(0);
8121 PUSH_LABEL(ret
, splabel
);
8122 PUSH_ADJUST(ret
, location
, 0);
8126 PM_COMPILE_NOT_POPPED((const pm_node_t
*) arguments
);
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
);
8140 PUSH_INSN1(ret
, location
, throw, INT2FIX(TAG_RETURN
));
8141 if (popped
) PUSH_INSN(ret
, location
, pop
);
8147 case PM_RETRY_NODE
: {
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
);
8156 COMPILE_ERROR(iseq
, location
.line
, "Invalid retry");
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;
8193 case PM_PARAMETERS_NODE
: {
8194 parameters_node
= (pm_parameters_node_t
*) scope_node
->parameters
;
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;
8203 case PM_IT_PARAMETERS_NODE
:
8204 body
->param
.lead_num
= 1;
8205 body
->param
.flags
.ambiguous_param0
= true;
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
= ¶meters_node
->optionals
;
8216 requireds_list
= ¶meters_node
->requireds
;
8217 keywords_list
= ¶meters_node
->keywords
;
8218 posts_list
= ¶meters_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;
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
8240 if (PM_NODE_TYPE_P(scope_node
->ast_node
, PM_FOR_NODE
)) table_size
++;
8242 if (keywords_list
&& keywords_list
->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
)) {
8255 else if (PM_NODE_TYPE_P(required
, PM_REQUIRED_PARAMETER_NODE
)) {
8256 if (PM_NODE_FLAG_P(required
, PM_PARAMETER_FLAGS_REPEATED_PARAMETER
)) {
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
)) {
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
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
)) {
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)
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
)) {
8295 // def foo(_, **_); _; end
8297 if (parameters_node
->keyword_rest
) {
8298 // def foo(...); end
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
)) {
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
)) {
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
)) {
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
)) {
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
) {
8345 // We can create local_table_for_iseq with the correct size
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)
8375 if (requireds_list
&& requireds_list
->size
) {
8376 for (size_t i
= 0; i
< requireds_list
->size
; i
++, local_index
++) {
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)
8387 case PM_MULTI_TARGET_NODE
: {
8388 local
= rb_make_temporary_id(local_index
);
8389 local_table_for_iseq
->ids
[local_index
] = local
;
8392 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
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
;
8402 pm_insert_local_index(param
->name
, local_index
, index_lookup_table
, local_table_for_iseq
, scope_node
);
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)
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
;
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)
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
;
8456 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
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
;
8463 pm_insert_local_index(name
, local_index
, index_lookup_table
, local_table_for_iseq
, scope_node
);
8467 // def foo(a, (b, *c, d), e = 1, *, g, (h, *i, j), k:, l: 1, **m, &n)
8469 pm_insert_local_special(idMULT
, local_index
, index_lookup_table
, local_table_for_iseq
);
8476 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
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
++) {
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)
8494 case PM_MULTI_TARGET_NODE
: {
8495 local
= rb_make_temporary_id(local_index
);
8496 local_table_for_iseq
->ids
[local_index
] = local
;
8499 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
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
;
8509 pm_insert_local_index(param
->name
, local_index
, index_lookup_table
, local_table_for_iseq
, scope_node
);
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)
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)
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
;
8550 pm_insert_local_index(name
, local_index
, index_lookup_table
, local_table_for_iseq
, scope_node
);
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)
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
;
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
));
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
;
8581 pm_insert_local_index(name
, local_index
, index_lookup_table
, local_table_for_iseq
, scope_node
);
8583 ids
[kw_index
++] = local
;
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
);
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
;
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)
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)
8622 case PM_NO_KEYWORDS_PARAMETER_NODE
: {
8623 body
->param
.flags
.accepts_no_kwarg
= true;
8626 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
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
;
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
;
8644 pm_insert_local_index(constant_id
, local_index
, index_lookup_table
, local_table_for_iseq
, scope_node
);
8648 pm_insert_local_special(idPow
, local_index
, index_lookup_table
, local_table_for_iseq
);
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
);
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)
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
;
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
;
8703 pm_insert_local_index(name
, local_index
, index_lookup_table
, local_table_for_iseq
, scope_node
);
8707 pm_insert_local_special(idAnd
, local_index
, index_lookup_table
, local_table_for_iseq
);
8714 //********END OF STEP 2**********
8715 // The local table is now consistent with expected
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
++;
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
;
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 }
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
];
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);
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)
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
;
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
);
8875 PUSH_SETLOCAL(ret
, location
, index
.index
, index
.level
);
8876 PUSH_LABEL(ret
, end_label
);
8881 // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n)
8883 case PM_REQUIRED_KEYWORD_PARAMETER_NODE
: {
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
);
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
);
8964 pm_compile_node(iseq
, scope_node
->body
, ret
, popped
, scope_node
);
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
);
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));
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
);
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
;
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);
9027 PM_COMPILE((const pm_node_t
*) scope_node
->ast_node
);
9029 PUSH_INSN1(ret
, location
, throw, INT2FIX(0));
9034 if (scope_node
->body
) {
9035 PM_COMPILE((const pm_node_t
*) scope_node
->body
);
9038 PUSH_INSN(ret
, location
, putnil
);
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
);
9056 case PM_SELF_NODE
: {
9060 PUSH_INSN(ret
, location
, putself
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
9095 rb_bug("Unexpected node type for shareable constant write: %s", pm_node_type_to_str(PM_NODE_TYPE(cast
->write
)));
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
);
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
);
9123 case PM_SOURCE_ENCODING_NODE
: {
9127 VALUE value
= pm_static_literal_value(iseq
, node
, scope_node
);
9128 PUSH_INSN1(ret
, location
, putobject
, value
);
9132 case PM_SOURCE_FILE_NODE
: {
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
);
9146 PUSH_INSN1(ret
, location
, putchilledstring
, string
);
9151 case PM_SOURCE_LINE_NODE
: {
9155 VALUE value
= pm_static_literal_value(iseq
, node
, scope_node
);
9156 PUSH_INSN1(ret
, location
, putobject
, value
);
9160 case PM_SPLAT_NODE
: {
9163 const pm_splat_node_t
*cast
= (const pm_splat_node_t
*) node
;
9164 if (cast
->expression
) {
9165 PM_COMPILE(cast
->expression
);
9169 PUSH_INSN1(ret
, location
, splatarray
, Qtrue
);
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]);
9185 PUSH_INSN(ret
, location
, putnil
);
9189 case PM_STRING_NODE
: {
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
);
9203 PUSH_INSN1(ret
, location
, putchilledstring
, value
);
9208 case PM_SUPER_NODE
: {
9211 const pm_super_node_t
*cast
= (const pm_super_node_t
*) node
;
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
);
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
);
9253 case PM_SYMBOL_NODE
: {
9257 VALUE value
= pm_static_literal_value(iseq
, node
, scope_node
);
9258 PUSH_INSN1(ret
, location
, putobject
, value
);
9262 case PM_TRUE_NODE
: {
9266 PUSH_INSN1(ret
, location
, putobject
, Qtrue
);
9270 case PM_UNDEF_NODE
: {
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
);
9291 case PM_UNLESS_NODE
: {
9292 // unless foo; bar end
9293 // ^^^^^^^^^^^^^^^^^^^
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
);
9306 case PM_UNTIL_NODE
: {
9307 // until foo; bar end
9308 // ^^^^^^^^^^^^^^^^^
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
);
9316 case PM_WHILE_NODE
: {
9317 // while foo; bar end
9318 // ^^^^^^^^^^^^^^^^^^
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
);
9326 case PM_X_STRING_NODE
: {
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
);
9339 case PM_YIELD_NODE
: {
9345 const pm_yield_node_t
*cast
= (const pm_yield_node_t
*) node
;
9347 switch (ISEQ_BODY(ISEQ_BODY(iseq
)->local_iseq
)->type
) {
9349 case ISEQ_TYPE_MAIN
:
9350 case ISEQ_TYPE_CLASS
:
9351 COMPILE_ERROR(iseq
, location
.line
, "Invalid yield");
9353 default: /* valid */;
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
);
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);
9376 rb_raise(rb_eNotImpError
, "node type %s not implemented", pm_node_type_to_str(PM_NODE_TYPE(node
)));
9382 /** True if the given iseq can have pre execution blocks. */
9384 pm_iseq_pre_execution_p(rb_iseq_t
*iseq
)
9386 switch (ISEQ_BODY(iseq
)->type
) {
9388 case ISEQ_TYPE_EVAL
:
9389 case ISEQ_TYPE_MAIN
:
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.
9404 pm_iseq_compile_node(rb_iseq_t
*iseq
, pm_scope_node_t
*node
)
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.
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.
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
9428 PUSH_SEQ(ret
, body
);
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.
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. */
9459 /** A pointer to the diagnostic that was generated during parsing. */
9460 pm_diagnostic_t
*error
;
9462 /** The start line of the diagnostic message. */
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
;
9472 /** The format that will be used to format the errors into the output. */
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
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
))
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
;
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
) {
9539 .column_start
= start
.column
,
9540 .column_end
= column_end
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
]];
9558 if (index
>= newline_list
->size
- 1) {
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
));
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.
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) {
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"
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) {
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"
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) {
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"
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) {
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"
9667 error_format
= (pm_parse_error_format_t
) {
9668 .number_prefix
= "%4" PRIi32
" | ",
9669 .blank_prefix
= " | ",
9670 .divider
= " ~~~~~~~~\n"
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"
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
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
)) {
9724 pm_buffer_append_string(buffer
, PM_COLOR_RED
"> " PM_COLOR_RESET
, 12);
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
);
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.
9814 #undef PM_ERROR_TRUNCATE
9815 #undef PM_COLOR_GRAY
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
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
]));
9835 while (start
< end
) {
9836 if ((width
= pm_encoding_utf_8_char_width(start
, end
- start
)) == 0) return false;
9844 * Generate an error object from the given parser that contains as much
9845 * information as possible about the errors that were encountered.
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
)) {
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(
9877 "%.*s:%" PRIi32
": %s",
9878 (int) pm_string_length(filepath
),
9879 pm_string_source(filepath
),
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
);
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
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
);
9910 pm_buffer_append_format(
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" : ""
9920 pm_parse_errors_format(parser
, &parser
->error_list
, &buffer
, rb_stderr_tty_p(), true);
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
);
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.
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
);
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);
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;
10012 * Set the frozen_string_literal option based on the default value used by the
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
:
10023 case ISEQ_FROZEN_STRING_LITERAL_DISABLED
:
10024 pm_options_frozen_string_literal_set(options
, false);
10026 case ISEQ_FROZEN_STRING_LITERAL_ENABLED
:
10027 pm_options_frozen_string_literal_set(options
, true);
10030 rb_bug("pm_options_frozen_string_literal_init: invalid frozen_string_literal=%d", frozen_string_literal
);
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.
10064 rb_ary_push(lines
, rb_enc_str_new(start
+ last_offset
, end
- (start
+ last_offset
), scope_node
->encoding
));
10071 * Attempt to load the file into memory. Return a Ruby error if the file cannot
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
))) {
10079 int e
= rb_w32_map_errno(GetLastError());
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
);
10094 error
= rb_syserr_new(e
, RSTRING_PTR(filepath
));
10095 RB_GC_GUARD(filepath
);
10101 pm_options_frozen_string_literal_init(&result
->options
);
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
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
));
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.
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
);
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.
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.
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));
10195 const char *cstr
= StringValueCStr(line
);
10196 size_t length
= strlen(cstr
);
10198 memcpy(string
, cstr
, length
);
10199 string
[length
] = '\0';
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.
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
);
10226 #define NEW_ISEQ OLD_ISEQ
10228 #undef NEW_CHILD_ISEQ
10229 #define NEW_CHILD_ISEQ OLD_CHILD_ISEQ