Ruby 4.1.0dev (2026-03-06 revision d5d144c149d3beabbfb262e3994f60552469181b)
compile.c (d5d144c149d3beabbfb262e3994f60552469181b)
1/**********************************************************************
2
3 compile.c - ruby node tree -> VM instruction sequence
4
5 $Author$
6 created at: 04/01/01 03:42:15 JST
7
8 Copyright (C) 2004-2007 Koichi Sasada
9
10**********************************************************************/
11
12#include "ruby/internal/config.h"
13#include <math.h>
14
15#ifdef HAVE_DLADDR
16# include <dlfcn.h>
17#endif
18
19#include "encindex.h"
20#include "id_table.h"
21#include "internal.h"
22#include "internal/array.h"
23#include "internal/compile.h"
24#include "internal/complex.h"
25#include "internal/encoding.h"
26#include "internal/error.h"
27#include "internal/gc.h"
28#include "internal/hash.h"
29#include "internal/io.h"
30#include "internal/numeric.h"
31#include "internal/object.h"
32#include "internal/rational.h"
33#include "internal/re.h"
34#include "internal/ruby_parser.h"
35#include "internal/symbol.h"
36#include "internal/thread.h"
37#include "internal/variable.h"
38#include "iseq.h"
39#include "ruby/ractor.h"
40#include "ruby/re.h"
41#include "ruby/util.h"
42#include "vm_core.h"
43#include "vm_callinfo.h"
44#include "vm_debug.h"
45#include "yjit.h"
46
47#include "builtin.h"
48#include "insns.inc"
49#include "insns_info.inc"
50
51#define FIXNUM_INC(n, i) ((n)+(INT2FIX(i)&~FIXNUM_FLAG))
52
53typedef struct iseq_link_element {
54 enum {
55 ISEQ_ELEMENT_ANCHOR,
56 ISEQ_ELEMENT_LABEL,
57 ISEQ_ELEMENT_INSN,
58 ISEQ_ELEMENT_ADJUST,
59 ISEQ_ELEMENT_TRACE,
60 } type;
61 struct iseq_link_element *next;
62 struct iseq_link_element *prev;
64
65typedef struct iseq_link_anchor {
66 LINK_ELEMENT anchor;
67 LINK_ELEMENT *last;
69
70typedef enum {
71 LABEL_RESCUE_NONE,
72 LABEL_RESCUE_BEG,
73 LABEL_RESCUE_END,
74 LABEL_RESCUE_TYPE_MAX
75} LABEL_RESCUE_TYPE;
76
77typedef struct iseq_label_data {
78 LINK_ELEMENT link;
79 int label_no;
80 int position;
81 int sc_state;
82 int sp;
83 int refcnt;
84 unsigned int set: 1;
85 unsigned int rescued: 2;
86 unsigned int unremovable: 1;
87} LABEL;
88
89typedef struct iseq_insn_data {
90 LINK_ELEMENT link;
91 enum ruby_vminsn_type insn_id;
92 int operand_size;
93 int sc_state;
94 VALUE *operands;
95 struct {
96 int line_no;
97 int node_id;
98 rb_event_flag_t events;
99 } insn_info;
100} INSN;
101
102typedef struct iseq_adjust_data {
103 LINK_ELEMENT link;
104 LABEL *label;
105 int line_no;
106} ADJUST;
107
108typedef struct iseq_trace_data {
109 LINK_ELEMENT link;
110 rb_event_flag_t event;
111 long data;
112} TRACE;
113
115 LABEL *begin;
116 LABEL *end;
117 struct ensure_range *next;
118};
119
121 const void *ensure_node;
123 struct ensure_range *erange;
124};
125
126const ID rb_iseq_shared_exc_local_tbl[] = {idERROR_INFO};
127
141#ifndef CPDEBUG
142#define CPDEBUG 0
143#endif
144
145#if CPDEBUG >= 0
146#define compile_debug CPDEBUG
147#else
148#define compile_debug ISEQ_COMPILE_DATA(iseq)->option->debug_level
149#endif
150
151#if CPDEBUG
152
153#define compile_debug_print_indent(level) \
154 ruby_debug_print_indent((level), compile_debug, gl_node_level * 2)
155
156#define debugp(header, value) (void) \
157 (compile_debug_print_indent(1) && \
158 ruby_debug_print_value(1, compile_debug, (header), (value)))
159
160#define debugi(header, id) (void) \
161 (compile_debug_print_indent(1) && \
162 ruby_debug_print_id(1, compile_debug, (header), (id)))
163
164#define debugp_param(header, value) (void) \
165 (compile_debug_print_indent(1) && \
166 ruby_debug_print_value(1, compile_debug, (header), (value)))
167
168#define debugp_verbose(header, value) (void) \
169 (compile_debug_print_indent(2) && \
170 ruby_debug_print_value(2, compile_debug, (header), (value)))
171
172#define debugp_verbose_node(header, value) (void) \
173 (compile_debug_print_indent(10) && \
174 ruby_debug_print_value(10, compile_debug, (header), (value)))
175
176#define debug_node_start(node) ((void) \
177 (compile_debug_print_indent(1) && \
178 (ruby_debug_print_node(1, CPDEBUG, "", (const NODE *)(node)), gl_node_level)), \
179 gl_node_level++)
180
181#define debug_node_end() gl_node_level --
182
183#else
184
185#define debugi(header, id) ((void)0)
186#define debugp(header, value) ((void)0)
187#define debugp_verbose(header, value) ((void)0)
188#define debugp_verbose_node(header, value) ((void)0)
189#define debugp_param(header, value) ((void)0)
190#define debug_node_start(node) ((void)0)
191#define debug_node_end() ((void)0)
192#endif
193
194#if CPDEBUG > 1 || CPDEBUG < 0
195#undef printf
196#define printf ruby_debug_printf
197#define debugs if (compile_debug_print_indent(1)) ruby_debug_printf
198#define debug_compile(msg, v) ((void)(compile_debug_print_indent(1) && fputs((msg), stderr)), (v))
199#else
200#define debugs if(0)printf
201#define debug_compile(msg, v) (v)
202#endif
203
204#define LVAR_ERRINFO (1)
205
206/* create new label */
207#define NEW_LABEL(l) new_label_body(iseq, (l))
208#define LABEL_FORMAT "<L%03d>"
209
210#define NEW_ISEQ(node, name, type, line_no) \
211 new_child_iseq(iseq, (node), rb_fstring(name), 0, (type), (line_no))
212
213#define NEW_CHILD_ISEQ(node, name, type, line_no) \
214 new_child_iseq(iseq, (node), rb_fstring(name), iseq, (type), (line_no))
215
216#define NEW_CHILD_ISEQ_WITH_CALLBACK(callback_func, name, type, line_no) \
217 new_child_iseq_with_callback(iseq, (callback_func), (name), iseq, (type), (line_no))
218
219/* add instructions */
220#define ADD_SEQ(seq1, seq2) \
221 APPEND_LIST((seq1), (seq2))
222
223/* add an instruction */
224#define ADD_INSN(seq, line_node, insn) \
225 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 0))
226
227/* add an instruction with the given line number and node id */
228#define ADD_SYNTHETIC_INSN(seq, line_no, node_id, insn) \
229 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (line_no), (node_id), BIN(insn), 0))
230
231/* insert an instruction before next */
232#define INSERT_BEFORE_INSN(next, line_no, node_id, insn) \
233 ELEM_INSERT_PREV(&(next)->link, (LINK_ELEMENT *) new_insn_body(iseq, line_no, node_id, BIN(insn), 0))
234
235/* insert an instruction after prev */
236#define INSERT_AFTER_INSN(prev, line_no, node_id, insn) \
237 ELEM_INSERT_NEXT(&(prev)->link, (LINK_ELEMENT *) new_insn_body(iseq, line_no, node_id, BIN(insn), 0))
238
239/* add an instruction with some operands (1, 2, 3, 5) */
240#define ADD_INSN1(seq, line_node, insn, op1) \
241 ADD_ELEM((seq), (LINK_ELEMENT *) \
242 new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 1, (VALUE)(op1)))
243
244/* insert an instruction with some operands (1, 2, 3, 5) before next */
245#define INSERT_BEFORE_INSN1(next, line_no, node_id, insn, op1) \
246 ELEM_INSERT_PREV(&(next)->link, (LINK_ELEMENT *) \
247 new_insn_body(iseq, line_no, node_id, BIN(insn), 1, (VALUE)(op1)))
248
249/* insert an instruction with some operands (1, 2, 3, 5) after prev */
250#define INSERT_AFTER_INSN1(prev, line_no, node_id, insn, op1) \
251 ELEM_INSERT_NEXT(&(prev)->link, (LINK_ELEMENT *) \
252 new_insn_body(iseq, line_no, node_id, BIN(insn), 1, (VALUE)(op1)))
253
254#define LABEL_REF(label) ((label)->refcnt++)
255
256/* add an instruction with label operand (alias of ADD_INSN1) */
257#define ADD_INSNL(seq, line_node, insn, label) (ADD_INSN1(seq, line_node, insn, label), LABEL_REF(label))
258
259#define ADD_INSN2(seq, line_node, insn, op1, op2) \
260 ADD_ELEM((seq), (LINK_ELEMENT *) \
261 new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 2, (VALUE)(op1), (VALUE)(op2)))
262
263#define ADD_INSN3(seq, line_node, insn, op1, op2, op3) \
264 ADD_ELEM((seq), (LINK_ELEMENT *) \
265 new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3)))
266
267/* Specific Insn factory */
268#define ADD_SEND(seq, line_node, id, argc) \
269 ADD_SEND_R((seq), (line_node), (id), (argc), NULL, (VALUE)INT2FIX(0), NULL)
270
271#define ADD_SEND_WITH_FLAG(seq, line_node, id, argc, flag) \
272 ADD_SEND_R((seq), (line_node), (id), (argc), NULL, (VALUE)(flag), NULL)
273
274#define ADD_SEND_WITH_BLOCK(seq, line_node, id, argc, block) \
275 ADD_SEND_R((seq), (line_node), (id), (argc), (block), (VALUE)INT2FIX(0), NULL)
276
277#define ADD_CALL_RECEIVER(seq, line_node) \
278 ADD_INSN((seq), (line_node), putself)
279
280#define ADD_CALL(seq, line_node, id, argc) \
281 ADD_SEND_R((seq), (line_node), (id), (argc), NULL, (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
282
283#define ADD_CALL_WITH_BLOCK(seq, line_node, id, argc, block) \
284 ADD_SEND_R((seq), (line_node), (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
285
286#define ADD_SEND_R(seq, line_node, id, argc, block, flag, keywords) \
287 ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, nd_line(line_node), nd_node_id(line_node), (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords)))
288
289#define ADD_TRACE(seq, event) \
290 ADD_ELEM((seq), (LINK_ELEMENT *)new_trace_body(iseq, (event), 0))
291#define ADD_TRACE_WITH_DATA(seq, event, data) \
292 ADD_ELEM((seq), (LINK_ELEMENT *)new_trace_body(iseq, (event), (data)))
293
294static void iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level);
295static void iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level);
296
297#define ADD_GETLOCAL(seq, line_node, idx, level) iseq_add_getlocal(iseq, (seq), (line_node), (idx), (level))
298#define ADD_SETLOCAL(seq, line_node, idx, level) iseq_add_setlocal(iseq, (seq), (line_node), (idx), (level))
299
300/* add label */
301#define ADD_LABEL(seq, label) \
302 ADD_ELEM((seq), (LINK_ELEMENT *) (label))
303
304#define APPEND_LABEL(seq, before, label) \
305 APPEND_ELEM((seq), (before), (LINK_ELEMENT *) (label))
306
307#define ADD_ADJUST(seq, line_node, label) \
308 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), nd_line(line_node)))
309
310#define ADD_ADJUST_RESTORE(seq, label) \
311 ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), -1))
312
313#define LABEL_UNREMOVABLE(label) \
314 ((label) ? (LABEL_REF(label), (label)->unremovable=1) : 0)
315#define ADD_CATCH_ENTRY(type, ls, le, iseqv, lc) do { \
316 VALUE _e = rb_ary_new3(5, (type), \
317 (VALUE)(ls) | 1, (VALUE)(le) | 1, \
318 (VALUE)(iseqv), (VALUE)(lc) | 1); \
319 LABEL_UNREMOVABLE(ls); \
320 LABEL_REF(le); \
321 LABEL_REF(lc); \
322 if (NIL_P(ISEQ_COMPILE_DATA(iseq)->catch_table_ary)) \
323 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, rb_ary_hidden_new(3)); \
324 rb_ary_push(ISEQ_COMPILE_DATA(iseq)->catch_table_ary, freeze_hide_obj(_e)); \
325} while (0)
326
327/* compile node */
328#define COMPILE(anchor, desc, node) \
329 (debug_compile("== " desc "\n", \
330 iseq_compile_each(iseq, (anchor), (node), 0)))
331
332/* compile node, this node's value will be popped */
333#define COMPILE_POPPED(anchor, desc, node) \
334 (debug_compile("== " desc "\n", \
335 iseq_compile_each(iseq, (anchor), (node), 1)))
336
337/* compile node, which is popped when 'popped' is true */
338#define COMPILE_(anchor, desc, node, popped) \
339 (debug_compile("== " desc "\n", \
340 iseq_compile_each(iseq, (anchor), (node), (popped))))
341
342#define COMPILE_RECV(anchor, desc, node, recv) \
343 (private_recv_p(node) ? \
344 (ADD_INSN(anchor, node, putself), VM_CALL_FCALL) : \
345 COMPILE(anchor, desc, recv) ? 0 : -1)
346
347#define OPERAND_AT(insn, idx) \
348 (((INSN*)(insn))->operands[(idx)])
349
350#define INSN_OF(insn) \
351 (((INSN*)(insn))->insn_id)
352
353#define IS_INSN(link) ((link)->type == ISEQ_ELEMENT_INSN)
354#define IS_LABEL(link) ((link)->type == ISEQ_ELEMENT_LABEL)
355#define IS_ADJUST(link) ((link)->type == ISEQ_ELEMENT_ADJUST)
356#define IS_TRACE(link) ((link)->type == ISEQ_ELEMENT_TRACE)
357#define IS_INSN_ID(iobj, insn) (INSN_OF(iobj) == BIN(insn))
358#define IS_NEXT_INSN_ID(link, insn) \
359 ((link)->next && IS_INSN((link)->next) && IS_INSN_ID((link)->next, insn))
360
361/* error */
362#if CPDEBUG > 0
364#endif
365RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 4)
366static void
367append_compile_error(const rb_iseq_t *iseq, int line, const char *fmt, ...)
368{
369 VALUE err_info = ISEQ_COMPILE_DATA(iseq)->err_info;
370 VALUE file = rb_iseq_path(iseq);
371 VALUE err = err_info == Qtrue ? Qfalse : err_info;
372 va_list args;
373
374 va_start(args, fmt);
375 err = rb_syntax_error_append(err, file, line, -1, NULL, fmt, args);
376 va_end(args);
377 if (NIL_P(err_info)) {
378 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, err);
379 rb_set_errinfo(err);
380 }
381 else if (!err_info) {
382 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, Qtrue);
383 }
384 if (compile_debug) {
385 if (SPECIAL_CONST_P(err)) err = rb_eSyntaxError;
386 rb_exc_fatal(err);
387 }
388}
389
390#if 0
391static void
392compile_bug(rb_iseq_t *iseq, int line, const char *fmt, ...)
393{
394 va_list args;
395 va_start(args, fmt);
396 rb_report_bug_valist(rb_iseq_path(iseq), line, fmt, args);
397 va_end(args);
398 abort();
399}
400#endif
401
402#define COMPILE_ERROR append_compile_error
403
404#define ERROR_ARGS_AT(n) iseq, nd_line(n),
405#define ERROR_ARGS ERROR_ARGS_AT(node)
406
407#define EXPECT_NODE(prefix, node, ndtype, errval) \
408do { \
409 const NODE *error_node = (node); \
410 enum node_type error_type = nd_type(error_node); \
411 if (error_type != (ndtype)) { \
412 COMPILE_ERROR(ERROR_ARGS_AT(error_node) \
413 prefix ": " #ndtype " is expected, but %s", \
414 ruby_node_name(error_type)); \
415 return errval; \
416 } \
417} while (0)
418
419#define EXPECT_NODE_NONULL(prefix, parent, ndtype, errval) \
420do { \
421 COMPILE_ERROR(ERROR_ARGS_AT(parent) \
422 prefix ": must be " #ndtype ", but 0"); \
423 return errval; \
424} while (0)
425
426#define UNKNOWN_NODE(prefix, node, errval) \
427do { \
428 const NODE *error_node = (node); \
429 COMPILE_ERROR(ERROR_ARGS_AT(error_node) prefix ": unknown node (%s)", \
430 ruby_node_name(nd_type(error_node))); \
431 return errval; \
432} while (0)
433
434#define COMPILE_OK 1
435#define COMPILE_NG 0
436
437#define CHECK(sub) if (!(sub)) {BEFORE_RETURN;return COMPILE_NG;}
438#define NO_CHECK(sub) (void)(sub)
439#define BEFORE_RETURN
440
441#define DECL_ANCHOR(name) \
442 LINK_ANCHOR name[1] = {{{ISEQ_ELEMENT_ANCHOR,},&name[0].anchor}}
443#define INIT_ANCHOR(name) \
444 ((name->last = &name->anchor)->next = NULL) /* re-initialize */
445
446static inline VALUE
447freeze_hide_obj(VALUE obj)
448{
449 OBJ_FREEZE(obj);
450 RBASIC_CLEAR_CLASS(obj);
451 return obj;
452}
453
454#include "optinsn.inc"
455#if OPT_INSTRUCTIONS_UNIFICATION
456#include "optunifs.inc"
457#endif
458
459/* for debug */
460#if CPDEBUG < 0
461#define ISEQ_ARG iseq,
462#define ISEQ_ARG_DECLARE rb_iseq_t *iseq,
463#else
464#define ISEQ_ARG
465#define ISEQ_ARG_DECLARE
466#endif
467
468#if CPDEBUG
469#define gl_node_level ISEQ_COMPILE_DATA(iseq)->node_level
470#endif
471
472static void dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, const LABEL *dest);
473static void dump_disasm_list(const LINK_ELEMENT *elem);
474
475static int insn_data_length(INSN *iobj);
476static int calc_sp_depth(int depth, INSN *iobj);
477
478static INSN *new_insn_body(rb_iseq_t *iseq, int line_no, int node_id, enum ruby_vminsn_type insn_id, int argc, ...);
479static LABEL *new_label_body(rb_iseq_t *iseq, long line);
480static ADJUST *new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line);
481static TRACE *new_trace_body(rb_iseq_t *iseq, rb_event_flag_t event, long data);
482
483
484static int iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *anchor, const NODE *n, int);
485static int iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
486static int iseq_setup_insn(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
487static int iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
488static int iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
489
490static int iseq_set_local_table(rb_iseq_t *iseq, const rb_ast_id_table_t *tbl, const NODE *const node_args);
491static int iseq_set_exception_local_table(rb_iseq_t *iseq);
492static int iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, const NODE *const node);
493
494static int iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
495static int iseq_set_exception_table(rb_iseq_t *iseq);
496static int iseq_set_optargs_table(rb_iseq_t *iseq);
497static int iseq_set_parameters_lvar_state(const rb_iseq_t *iseq);
498
499static int compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr, bool ignore);
500static int compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int method_call_keywords, int popped);
501
502/*
503 * To make Array to LinkedList, use link_anchor
504 */
505
506static void
507verify_list(ISEQ_ARG_DECLARE const char *info, LINK_ANCHOR *const anchor)
508{
509#if CPDEBUG
510 int flag = 0;
511 LINK_ELEMENT *list, *plist;
512
513 if (!compile_debug) return;
514
515 list = anchor->anchor.next;
516 plist = &anchor->anchor;
517 while (list) {
518 if (plist != list->prev) {
519 flag += 1;
520 }
521 plist = list;
522 list = list->next;
523 }
524
525 if (anchor->last != plist && anchor->last != 0) {
526 flag |= 0x70000;
527 }
528
529 if (flag != 0) {
530 rb_bug("list verify error: %08x (%s)", flag, info);
531 }
532#endif
533}
534#if CPDEBUG < 0
535#define verify_list(info, anchor) verify_list(iseq, (info), (anchor))
536#endif
537
538static void
539verify_call_cache(rb_iseq_t *iseq)
540{
541#if CPDEBUG
542 VALUE *original = rb_iseq_original_iseq(iseq);
543 size_t i = 0;
544 while (i < ISEQ_BODY(iseq)->iseq_size) {
545 VALUE insn = original[i];
546 const char *types = insn_op_types(insn);
547
548 for (int j=0; types[j]; j++) {
549 if (types[j] == TS_CALLDATA) {
550 struct rb_call_data *cd = (struct rb_call_data *)original[i+j+1];
551 const struct rb_callinfo *ci = cd->ci;
552 const struct rb_callcache *cc = cd->cc;
553 if (cc != vm_cc_empty()) {
554 vm_ci_dump(ci);
555 rb_bug("call cache is not initialized by vm_cc_empty()");
556 }
557 }
558 }
559 i += insn_len(insn);
560 }
561
562 for (unsigned int i=0; i<ISEQ_BODY(iseq)->ci_size; i++) {
563 struct rb_call_data *cd = &ISEQ_BODY(iseq)->call_data[i];
564 const struct rb_callinfo *ci = cd->ci;
565 const struct rb_callcache *cc = cd->cc;
566 if (cc != NULL && cc != vm_cc_empty()) {
567 vm_ci_dump(ci);
568 rb_bug("call cache is not initialized by vm_cc_empty()");
569 }
570 }
571#endif
572}
573
574/*
575 * elem1, elem2 => elem1, elem2, elem
576 */
577static void
578ADD_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor, LINK_ELEMENT *elem)
579{
580 elem->prev = anchor->last;
581 anchor->last->next = elem;
582 anchor->last = elem;
583 verify_list("add", anchor);
584}
585
586/*
587 * elem1, before, elem2 => elem1, before, elem, elem2
588 */
589static void
590APPEND_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor, LINK_ELEMENT *before, LINK_ELEMENT *elem)
591{
592 elem->prev = before;
593 elem->next = before->next;
594 elem->next->prev = elem;
595 before->next = elem;
596 if (before == anchor->last) anchor->last = elem;
597 verify_list("add", anchor);
598}
599#if CPDEBUG < 0
600#define ADD_ELEM(anchor, elem) ADD_ELEM(iseq, (anchor), (elem))
601#define APPEND_ELEM(anchor, before, elem) APPEND_ELEM(iseq, (anchor), (before), (elem))
602#endif
603
604static int
605branch_coverage_valid_p(rb_iseq_t *iseq, int first_line)
606{
607 if (!ISEQ_COVERAGE(iseq)) return 0;
608 if (!ISEQ_BRANCH_COVERAGE(iseq)) return 0;
609 if (first_line <= 0) return 0;
610 return 1;
611}
612
613static VALUE
614setup_branch(const rb_code_location_t *loc, const char *type, VALUE structure, VALUE key)
615{
616 const int first_lineno = loc->beg_pos.lineno, first_column = loc->beg_pos.column;
617 const int last_lineno = loc->end_pos.lineno, last_column = loc->end_pos.column;
618 VALUE branch = rb_ary_hidden_new(6);
619
620 rb_hash_aset(structure, key, branch);
621 rb_ary_push(branch, ID2SYM(rb_intern(type)));
622 rb_ary_push(branch, INT2FIX(first_lineno));
623 rb_ary_push(branch, INT2FIX(first_column));
624 rb_ary_push(branch, INT2FIX(last_lineno));
625 rb_ary_push(branch, INT2FIX(last_column));
626 return branch;
627}
628
629static VALUE
630decl_branch_base(rb_iseq_t *iseq, VALUE key, const rb_code_location_t *loc, const char *type)
631{
632 if (!branch_coverage_valid_p(iseq, loc->beg_pos.lineno)) return Qundef;
633
634 /*
635 * if !structure[node]
636 * structure[node] = [type, first_lineno, first_column, last_lineno, last_column, branches = {}]
637 * else
638 * branches = structure[node][5]
639 * end
640 */
641
642 VALUE structure = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 0);
643 VALUE branch_base = rb_hash_aref(structure, key);
644 VALUE branches;
645
646 if (NIL_P(branch_base)) {
647 branch_base = setup_branch(loc, type, structure, key);
648 branches = rb_hash_new();
649 rb_obj_hide(branches);
650 rb_ary_push(branch_base, branches);
651 }
652 else {
653 branches = RARRAY_AREF(branch_base, 5);
654 }
655
656 return branches;
657}
658
659static NODE
660generate_dummy_line_node(int lineno, int node_id)
661{
662 NODE dummy = { 0 };
663 nd_set_line(&dummy, lineno);
664 nd_set_node_id(&dummy, node_id);
665 return dummy;
666}
667
668static void
669add_trace_branch_coverage(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const rb_code_location_t *loc, int node_id, int branch_id, const char *type, VALUE branches)
670{
671 if (!branch_coverage_valid_p(iseq, loc->beg_pos.lineno)) return;
672
673 /*
674 * if !branches[branch_id]
675 * branches[branch_id] = [type, first_lineno, first_column, last_lineno, last_column, counter_idx]
676 * else
677 * counter_idx= branches[branch_id][5]
678 * end
679 */
680
681 VALUE key = INT2FIX(branch_id);
682 VALUE branch = rb_hash_aref(branches, key);
683 long counter_idx;
684
685 if (NIL_P(branch)) {
686 branch = setup_branch(loc, type, branches, key);
687 VALUE counters = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 1);
688 counter_idx = RARRAY_LEN(counters);
689 rb_ary_push(branch, LONG2FIX(counter_idx));
690 rb_ary_push(counters, INT2FIX(0));
691 }
692 else {
693 counter_idx = FIX2LONG(RARRAY_AREF(branch, 5));
694 }
695
696 ADD_TRACE_WITH_DATA(seq, RUBY_EVENT_COVERAGE_BRANCH, counter_idx);
697 ADD_SYNTHETIC_INSN(seq, loc->end_pos.lineno, node_id, nop);
698}
699
700#define ISEQ_LAST_LINE(iseq) (ISEQ_COMPILE_DATA(iseq)->last_line)
701
702static int
703validate_label(st_data_t name, st_data_t label, st_data_t arg)
704{
705 rb_iseq_t *iseq = (rb_iseq_t *)arg;
706 LABEL *lobj = (LABEL *)label;
707 if (!lobj->link.next) {
708 do {
709 COMPILE_ERROR(iseq, lobj->position,
710 "%"PRIsVALUE": undefined label",
711 rb_sym2str((VALUE)name));
712 } while (0);
713 }
714 return ST_CONTINUE;
715}
716
717static void
718validate_labels(rb_iseq_t *iseq, st_table *labels_table)
719{
720 st_foreach(labels_table, validate_label, (st_data_t)iseq);
721 st_free_table(labels_table);
722}
723
724static NODE *
725get_nd_recv(const NODE *node)
726{
727 switch (nd_type(node)) {
728 case NODE_CALL:
729 return RNODE_CALL(node)->nd_recv;
730 case NODE_OPCALL:
731 return RNODE_OPCALL(node)->nd_recv;
732 case NODE_FCALL:
733 return 0;
734 case NODE_QCALL:
735 return RNODE_QCALL(node)->nd_recv;
736 case NODE_VCALL:
737 return 0;
738 case NODE_ATTRASGN:
739 return RNODE_ATTRASGN(node)->nd_recv;
740 case NODE_OP_ASGN1:
741 return RNODE_OP_ASGN1(node)->nd_recv;
742 case NODE_OP_ASGN2:
743 return RNODE_OP_ASGN2(node)->nd_recv;
744 default:
745 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
746 }
747}
748
749static ID
750get_node_call_nd_mid(const NODE *node)
751{
752 switch (nd_type(node)) {
753 case NODE_CALL:
754 return RNODE_CALL(node)->nd_mid;
755 case NODE_OPCALL:
756 return RNODE_OPCALL(node)->nd_mid;
757 case NODE_FCALL:
758 return RNODE_FCALL(node)->nd_mid;
759 case NODE_QCALL:
760 return RNODE_QCALL(node)->nd_mid;
761 case NODE_VCALL:
762 return RNODE_VCALL(node)->nd_mid;
763 case NODE_ATTRASGN:
764 return RNODE_ATTRASGN(node)->nd_mid;
765 default:
766 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
767 }
768}
769
770static NODE *
771get_nd_args(const NODE *node)
772{
773 switch (nd_type(node)) {
774 case NODE_CALL:
775 return RNODE_CALL(node)->nd_args;
776 case NODE_OPCALL:
777 return RNODE_OPCALL(node)->nd_args;
778 case NODE_FCALL:
779 return RNODE_FCALL(node)->nd_args;
780 case NODE_QCALL:
781 return RNODE_QCALL(node)->nd_args;
782 case NODE_VCALL:
783 return 0;
784 case NODE_ATTRASGN:
785 return RNODE_ATTRASGN(node)->nd_args;
786 default:
787 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
788 }
789}
790
791static ID
792get_node_colon_nd_mid(const NODE *node)
793{
794 switch (nd_type(node)) {
795 case NODE_COLON2:
796 return RNODE_COLON2(node)->nd_mid;
797 case NODE_COLON3:
798 return RNODE_COLON3(node)->nd_mid;
799 default:
800 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
801 }
802}
803
804static ID
805get_nd_vid(const NODE *node)
806{
807 switch (nd_type(node)) {
808 case NODE_LASGN:
809 return RNODE_LASGN(node)->nd_vid;
810 case NODE_DASGN:
811 return RNODE_DASGN(node)->nd_vid;
812 case NODE_IASGN:
813 return RNODE_IASGN(node)->nd_vid;
814 case NODE_CVASGN:
815 return RNODE_CVASGN(node)->nd_vid;
816 default:
817 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
818 }
819}
820
821static NODE *
822get_nd_value(const NODE *node)
823{
824 switch (nd_type(node)) {
825 case NODE_LASGN:
826 return RNODE_LASGN(node)->nd_value;
827 case NODE_DASGN:
828 return RNODE_DASGN(node)->nd_value;
829 default:
830 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
831 }
832}
833
834static VALUE
835get_string_value(const NODE *node)
836{
837 switch (nd_type(node)) {
838 case NODE_STR:
839 return RB_OBJ_SET_SHAREABLE(rb_node_str_string_val(node));
840 case NODE_FILE:
841 return RB_OBJ_SET_SHAREABLE(rb_node_file_path_val(node));
842 default:
843 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
844 }
845}
846
847VALUE
848rb_iseq_compile_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_callback_callback_func * ifunc)
849{
850 DECL_ANCHOR(ret);
851 INIT_ANCHOR(ret);
852
853 (*ifunc->func)(iseq, ret, ifunc->data);
854
855 ADD_SYNTHETIC_INSN(ret, ISEQ_COMPILE_DATA(iseq)->last_line, -1, leave);
856
857 CHECK(iseq_setup_insn(iseq, ret));
858 return iseq_setup(iseq, ret);
859}
860
861static bool drop_unreachable_return(LINK_ANCHOR *ret);
862
863VALUE
864rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
865{
866 DECL_ANCHOR(ret);
867 INIT_ANCHOR(ret);
868
869 if (node == 0) {
870 NO_CHECK(COMPILE(ret, "nil", node));
871 iseq_set_local_table(iseq, 0, 0);
872 }
873 /* assume node is T_NODE */
874 else if (nd_type_p(node, NODE_SCOPE)) {
875 /* iseq type of top, method, class, block */
876 iseq_set_local_table(iseq, RNODE_SCOPE(node)->nd_tbl, (NODE *)RNODE_SCOPE(node)->nd_args);
877 iseq_set_arguments(iseq, ret, (NODE *)RNODE_SCOPE(node)->nd_args);
878 iseq_set_parameters_lvar_state(iseq);
879
880 switch (ISEQ_BODY(iseq)->type) {
881 case ISEQ_TYPE_BLOCK:
882 {
883 LABEL *start = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(0);
884 LABEL *end = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(0);
885
886 start->rescued = LABEL_RESCUE_BEG;
887 end->rescued = LABEL_RESCUE_END;
888
889 ADD_TRACE(ret, RUBY_EVENT_B_CALL);
890 ADD_SYNTHETIC_INSN(ret, ISEQ_BODY(iseq)->location.first_lineno, -1, nop);
891 ADD_LABEL(ret, start);
892 CHECK(COMPILE(ret, "block body", RNODE_SCOPE(node)->nd_body));
893 ADD_LABEL(ret, end);
894 ADD_TRACE(ret, RUBY_EVENT_B_RETURN);
895 ISEQ_COMPILE_DATA(iseq)->last_line = ISEQ_BODY(iseq)->location.code_location.end_pos.lineno;
896
897 /* wide range catch handler must put at last */
898 ADD_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, NULL, start);
899 ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, NULL, end);
900 break;
901 }
902 case ISEQ_TYPE_CLASS:
903 {
904 ADD_TRACE(ret, RUBY_EVENT_CLASS);
905 CHECK(COMPILE(ret, "scoped node", RNODE_SCOPE(node)->nd_body));
906 ADD_TRACE(ret, RUBY_EVENT_END);
907 ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
908 break;
909 }
910 case ISEQ_TYPE_METHOD:
911 {
912 ISEQ_COMPILE_DATA(iseq)->root_node = RNODE_SCOPE(node)->nd_body;
913 ADD_TRACE(ret, RUBY_EVENT_CALL);
914 CHECK(COMPILE(ret, "scoped node", RNODE_SCOPE(node)->nd_body));
915 ISEQ_COMPILE_DATA(iseq)->root_node = RNODE_SCOPE(node)->nd_body;
916 ADD_TRACE(ret, RUBY_EVENT_RETURN);
917 ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
918 break;
919 }
920 default: {
921 CHECK(COMPILE(ret, "scoped node", RNODE_SCOPE(node)->nd_body));
922 break;
923 }
924 }
925 }
926 else {
927 const char *m;
928#define INVALID_ISEQ_TYPE(type) \
929 ISEQ_TYPE_##type: m = #type; goto invalid_iseq_type
930 switch (ISEQ_BODY(iseq)->type) {
931 case INVALID_ISEQ_TYPE(METHOD);
932 case INVALID_ISEQ_TYPE(CLASS);
933 case INVALID_ISEQ_TYPE(BLOCK);
934 case INVALID_ISEQ_TYPE(EVAL);
935 case INVALID_ISEQ_TYPE(MAIN);
936 case INVALID_ISEQ_TYPE(TOP);
937#undef INVALID_ISEQ_TYPE /* invalid iseq types end */
938 case ISEQ_TYPE_RESCUE:
939 iseq_set_exception_local_table(iseq);
940 CHECK(COMPILE(ret, "rescue", node));
941 break;
942 case ISEQ_TYPE_ENSURE:
943 iseq_set_exception_local_table(iseq);
944 CHECK(COMPILE_POPPED(ret, "ensure", node));
945 break;
946 case ISEQ_TYPE_PLAIN:
947 CHECK(COMPILE(ret, "ensure", node));
948 break;
949 default:
950 COMPILE_ERROR(ERROR_ARGS "unknown scope: %d", ISEQ_BODY(iseq)->type);
951 return COMPILE_NG;
952 invalid_iseq_type:
953 COMPILE_ERROR(ERROR_ARGS "compile/ISEQ_TYPE_%s should not be reached", m);
954 return COMPILE_NG;
955 }
956 }
957
958 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE || ISEQ_BODY(iseq)->type == ISEQ_TYPE_ENSURE) {
959 NODE dummy_line_node = generate_dummy_line_node(0, -1);
960 ADD_GETLOCAL(ret, &dummy_line_node, LVAR_ERRINFO, 0);
961 ADD_INSN1(ret, &dummy_line_node, throw, INT2FIX(0) /* continue throw */ );
962 }
963 else if (!drop_unreachable_return(ret)) {
964 ADD_SYNTHETIC_INSN(ret, ISEQ_COMPILE_DATA(iseq)->last_line, -1, leave);
965 }
966
967#if OPT_SUPPORT_JOKE
968 if (ISEQ_COMPILE_DATA(iseq)->labels_table) {
969 st_table *labels_table = ISEQ_COMPILE_DATA(iseq)->labels_table;
970 ISEQ_COMPILE_DATA(iseq)->labels_table = 0;
971 validate_labels(iseq, labels_table);
972 }
973#endif
974 CHECK(iseq_setup_insn(iseq, ret));
975 return iseq_setup(iseq, ret);
976}
977
978static int
979rb_iseq_translate_threaded_code(rb_iseq_t *iseq)
980{
981#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
982 const void * const *table = rb_vm_get_insns_address_table();
983 unsigned int i;
984 VALUE *encoded = (VALUE *)ISEQ_BODY(iseq)->iseq_encoded;
985
986 for (i = 0; i < ISEQ_BODY(iseq)->iseq_size; /* */ ) {
987 int insn = (int)ISEQ_BODY(iseq)->iseq_encoded[i];
988 int len = insn_len(insn);
989 encoded[i] = (VALUE)table[insn];
990 i += len;
991 }
992 FL_SET((VALUE)iseq, ISEQ_TRANSLATED);
993#endif
994
995#if USE_YJIT
996 rb_yjit_live_iseq_count++;
997 rb_yjit_iseq_alloc_count++;
998#endif
999
1000 return COMPILE_OK;
1001}
1002
1003VALUE *
1004rb_iseq_original_iseq(const rb_iseq_t *iseq) /* cold path */
1005{
1006 VALUE *original_code;
1007
1008 if (ISEQ_ORIGINAL_ISEQ(iseq)) return ISEQ_ORIGINAL_ISEQ(iseq);
1009 original_code = ISEQ_ORIGINAL_ISEQ_ALLOC(iseq, ISEQ_BODY(iseq)->iseq_size);
1010 MEMCPY(original_code, ISEQ_BODY(iseq)->iseq_encoded, VALUE, ISEQ_BODY(iseq)->iseq_size);
1011
1012#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
1013 {
1014 unsigned int i;
1015
1016 for (i = 0; i < ISEQ_BODY(iseq)->iseq_size; /* */ ) {
1017 const void *addr = (const void *)original_code[i];
1018 const int insn = rb_vm_insn_addr2insn(addr);
1019
1020 original_code[i] = insn;
1021 i += insn_len(insn);
1022 }
1023 }
1024#endif
1025 return original_code;
1026}
1027
1028/*********************************************/
1029/* definition of data structure for compiler */
1030/*********************************************/
1031
1032#if defined(HAVE_TRUE_LONG_LONG) && SIZEOF_LONG_LONG > SIZEOF_VALUE
1033# define ALIGNMENT_SIZE SIZEOF_LONG_LONG
1034#else
1035# define ALIGNMENT_SIZE SIZEOF_VALUE
1036#endif
1037#define PADDING_SIZE_MAX ((size_t)((ALIGNMENT_SIZE) - 1))
1038
1039#define ALIGNMENT_SIZE_OF(type) alignment_size_assert(RUBY_ALIGNOF(type), #type)
1040
1041static inline size_t
1042alignment_size_assert(size_t align, const char *type)
1043{
1044 RUBY_ASSERT((align & (align - 1)) == 0,
1045 "ALIGNMENT_SIZE_OF(%s):%zd == (2 ** N) is expected", type, align);
1046 return align;
1047}
1048
1049/* calculate padding size for aligned memory access */
1050static inline size_t
1051calc_padding(void *ptr, size_t align)
1052{
1053 size_t mis;
1054 size_t padding = 0;
1055
1056 mis = (size_t)ptr & (align - 1);
1057 if (mis > 0) {
1058 padding = align - mis;
1059 }
1060
1061 return padding;
1062}
1063
1064static void *
1065compile_data_alloc_with_arena(struct iseq_compile_data_storage **arena, size_t size, size_t align)
1066{
1067 void *ptr = 0;
1068 struct iseq_compile_data_storage *storage = *arena;
1069 size_t padding = calc_padding((void *)&storage->buff[storage->pos], align);
1070
1071 if (size >= INT_MAX - padding) rb_memerror();
1072 if (storage->pos + size + padding > storage->size) {
1073 unsigned int alloc_size = storage->size;
1074
1075 while (alloc_size < size + PADDING_SIZE_MAX) {
1076 if (alloc_size >= INT_MAX / 2) rb_memerror();
1077 alloc_size *= 2;
1078 }
1079 storage->next = (void *)ALLOC_N(char, alloc_size +
1080 offsetof(struct iseq_compile_data_storage, buff));
1081 storage = *arena = storage->next;
1082 storage->next = 0;
1083 storage->pos = 0;
1084 storage->size = alloc_size;
1085 padding = calc_padding((void *)&storage->buff[storage->pos], align);
1086 }
1087
1088 storage->pos += (int)padding;
1089
1090 ptr = (void *)&storage->buff[storage->pos];
1091 storage->pos += (int)size;
1092 return ptr;
1093}
1094
1095static void *
1096compile_data_alloc(rb_iseq_t *iseq, size_t size, size_t align)
1097{
1098 struct iseq_compile_data_storage ** arena = &ISEQ_COMPILE_DATA(iseq)->node.storage_current;
1099 return compile_data_alloc_with_arena(arena, size, align);
1100}
1101
1102#define compile_data_alloc_type(iseq, type) \
1103 (type *)compile_data_alloc(iseq, sizeof(type), ALIGNMENT_SIZE_OF(type))
1104
1105static inline void *
1106compile_data_alloc2(rb_iseq_t *iseq, size_t elsize, size_t num, size_t align)
1107{
1108 size_t size = rb_size_mul_or_raise(elsize, num, rb_eRuntimeError);
1109 return compile_data_alloc(iseq, size, align);
1110}
1111
1112#define compile_data_alloc2_type(iseq, type, num) \
1113 (type *)compile_data_alloc2(iseq, sizeof(type), num, ALIGNMENT_SIZE_OF(type))
1114
1115static inline void *
1116compile_data_calloc2(rb_iseq_t *iseq, size_t elsize, size_t num, size_t align)
1117{
1118 size_t size = rb_size_mul_or_raise(elsize, num, rb_eRuntimeError);
1119 void *p = compile_data_alloc(iseq, size, align);
1120 memset(p, 0, size);
1121 return p;
1122}
1123
1124#define compile_data_calloc2_type(iseq, type, num) \
1125 (type *)compile_data_calloc2(iseq, sizeof(type), num, ALIGNMENT_SIZE_OF(type))
1126
1127static INSN *
1128compile_data_alloc_insn(rb_iseq_t *iseq)
1129{
1130 struct iseq_compile_data_storage ** arena = &ISEQ_COMPILE_DATA(iseq)->insn.storage_current;
1131 return (INSN *)compile_data_alloc_with_arena(arena, sizeof(INSN), ALIGNMENT_SIZE_OF(INSN));
1132}
1133
1134static LABEL *
1135compile_data_alloc_label(rb_iseq_t *iseq)
1136{
1137 return compile_data_alloc_type(iseq, LABEL);
1138}
1139
1140static ADJUST *
1141compile_data_alloc_adjust(rb_iseq_t *iseq)
1142{
1143 return compile_data_alloc_type(iseq, ADJUST);
1144}
1145
1146static TRACE *
1147compile_data_alloc_trace(rb_iseq_t *iseq)
1148{
1149 return compile_data_alloc_type(iseq, TRACE);
1150}
1151
1152/*
1153 * elem1, elemX => elem1, elem2, elemX
1154 */
1155static void
1156ELEM_INSERT_NEXT(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
1157{
1158 elem2->next = elem1->next;
1159 elem2->prev = elem1;
1160 elem1->next = elem2;
1161 if (elem2->next) {
1162 elem2->next->prev = elem2;
1163 }
1164}
1165
1166/*
1167 * elem1, elemX => elemX, elem2, elem1
1168 */
1169static void
1170ELEM_INSERT_PREV(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
1171{
1172 elem2->prev = elem1->prev;
1173 elem2->next = elem1;
1174 elem1->prev = elem2;
1175 if (elem2->prev) {
1176 elem2->prev->next = elem2;
1177 }
1178}
1179
1180/*
1181 * elemX, elem1, elemY => elemX, elem2, elemY
1182 */
1183static void
1184ELEM_REPLACE(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
1185{
1186 elem2->prev = elem1->prev;
1187 elem2->next = elem1->next;
1188 if (elem1->prev) {
1189 elem1->prev->next = elem2;
1190 }
1191 if (elem1->next) {
1192 elem1->next->prev = elem2;
1193 }
1194}
1195
1196static void
1197ELEM_REMOVE(LINK_ELEMENT *elem)
1198{
1199 elem->prev->next = elem->next;
1200 if (elem->next) {
1201 elem->next->prev = elem->prev;
1202 }
1203}
1204
1205static LINK_ELEMENT *
1206FIRST_ELEMENT(const LINK_ANCHOR *const anchor)
1207{
1208 return anchor->anchor.next;
1209}
1210
1211static LINK_ELEMENT *
1212LAST_ELEMENT(LINK_ANCHOR *const anchor)
1213{
1214 return anchor->last;
1215}
1216
1217static LINK_ELEMENT *
1218ELEM_FIRST_INSN(LINK_ELEMENT *elem)
1219{
1220 while (elem) {
1221 switch (elem->type) {
1222 case ISEQ_ELEMENT_INSN:
1223 case ISEQ_ELEMENT_ADJUST:
1224 return elem;
1225 default:
1226 elem = elem->next;
1227 }
1228 }
1229 return NULL;
1230}
1231
1232static int
1233LIST_INSN_SIZE_ONE(const LINK_ANCHOR *const anchor)
1234{
1235 LINK_ELEMENT *first_insn = ELEM_FIRST_INSN(FIRST_ELEMENT(anchor));
1236 if (first_insn != NULL &&
1237 ELEM_FIRST_INSN(first_insn->next) == NULL) {
1238 return TRUE;
1239 }
1240 else {
1241 return FALSE;
1242 }
1243}
1244
1245static int
1246LIST_INSN_SIZE_ZERO(const LINK_ANCHOR *const anchor)
1247{
1248 if (ELEM_FIRST_INSN(FIRST_ELEMENT(anchor)) == NULL) {
1249 return TRUE;
1250 }
1251 else {
1252 return FALSE;
1253 }
1254}
1255
1256/*
1257 * anc1: e1, e2, e3
1258 * anc2: e4, e5
1259 *#=>
1260 * anc1: e1, e2, e3, e4, e5
1261 * anc2: e4, e5 (broken)
1262 */
1263static void
1264APPEND_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *const anc1, LINK_ANCHOR *const anc2)
1265{
1266 if (anc2->anchor.next) {
1267 /* LINK_ANCHOR must not loop */
1268 RUBY_ASSERT(anc2->last != &anc2->anchor);
1269 anc1->last->next = anc2->anchor.next;
1270 anc2->anchor.next->prev = anc1->last;
1271 anc1->last = anc2->last;
1272 }
1273 else {
1274 RUBY_ASSERT(anc2->last == &anc2->anchor);
1275 }
1276 verify_list("append", anc1);
1277}
1278#if CPDEBUG < 0
1279#define APPEND_LIST(anc1, anc2) APPEND_LIST(iseq, (anc1), (anc2))
1280#endif
1281
1282#if CPDEBUG && 0
1283static void
1284debug_list(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor, LINK_ELEMENT *cur)
1285{
1286 LINK_ELEMENT *list = FIRST_ELEMENT(anchor);
1287 printf("----\n");
1288 printf("anch: %p, frst: %p, last: %p\n", (void *)&anchor->anchor,
1289 (void *)anchor->anchor.next, (void *)anchor->last);
1290 while (list) {
1291 printf("curr: %p, next: %p, prev: %p, type: %d\n", (void *)list, (void *)list->next,
1292 (void *)list->prev, (int)list->type);
1293 list = list->next;
1294 }
1295 printf("----\n");
1296
1297 dump_disasm_list_with_cursor(anchor->anchor.next, cur, 0);
1298 verify_list("debug list", anchor);
1299}
1300#if CPDEBUG < 0
1301#define debug_list(anc, cur) debug_list(iseq, (anc), (cur))
1302#endif
1303#else
1304#define debug_list(anc, cur) ((void)0)
1305#endif
1306
1307static TRACE *
1308new_trace_body(rb_iseq_t *iseq, rb_event_flag_t event, long data)
1309{
1310 TRACE *trace = compile_data_alloc_trace(iseq);
1311
1312 trace->link.type = ISEQ_ELEMENT_TRACE;
1313 trace->link.next = NULL;
1314 trace->event = event;
1315 trace->data = data;
1316
1317 return trace;
1318}
1319
1320static LABEL *
1321new_label_body(rb_iseq_t *iseq, long line)
1322{
1323 LABEL *labelobj = compile_data_alloc_label(iseq);
1324
1325 labelobj->link.type = ISEQ_ELEMENT_LABEL;
1326 labelobj->link.next = 0;
1327
1328 labelobj->label_no = ISEQ_COMPILE_DATA(iseq)->label_no++;
1329 labelobj->sc_state = 0;
1330 labelobj->sp = -1;
1331 labelobj->refcnt = 0;
1332 labelobj->set = 0;
1333 labelobj->rescued = LABEL_RESCUE_NONE;
1334 labelobj->unremovable = 0;
1335 labelobj->position = -1;
1336 return labelobj;
1337}
1338
1339static ADJUST *
1340new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line)
1341{
1342 ADJUST *adjust = compile_data_alloc_adjust(iseq);
1343 adjust->link.type = ISEQ_ELEMENT_ADJUST;
1344 adjust->link.next = 0;
1345 adjust->label = label;
1346 adjust->line_no = line;
1347 LABEL_UNREMOVABLE(label);
1348 return adjust;
1349}
1350
1351static void
1352iseq_insn_each_markable_object(INSN *insn, void (*func)(VALUE *, VALUE), VALUE data)
1353{
1354 const char *types = insn_op_types(insn->insn_id);
1355 for (int j = 0; types[j]; j++) {
1356 char type = types[j];
1357 switch (type) {
1358 case TS_CDHASH:
1359 case TS_ISEQ:
1360 case TS_VALUE:
1361 case TS_IC: // constant path array
1362 case TS_CALLDATA: // ci is stored.
1363 func(&OPERAND_AT(insn, j), data);
1364 break;
1365 default:
1366 break;
1367 }
1368 }
1369}
1370
1371static void
1372iseq_insn_each_object_write_barrier(VALUE * obj, VALUE iseq)
1373{
1374 RB_OBJ_WRITTEN(iseq, Qundef, *obj);
1376 RBASIC_CLASS(*obj) == 0 || // hidden
1377 RB_OBJ_SHAREABLE_P(*obj));
1378}
1379
1380static INSN *
1381new_insn_core(rb_iseq_t *iseq, int line_no, int node_id, int insn_id, int argc, VALUE *argv)
1382{
1383 INSN *iobj = compile_data_alloc_insn(iseq);
1384
1385 /* printf("insn_id: %d, line: %d\n", insn_id, nd_line(line_node)); */
1386
1387 iobj->link.type = ISEQ_ELEMENT_INSN;
1388 iobj->link.next = 0;
1389 iobj->insn_id = insn_id;
1390 iobj->insn_info.line_no = line_no;
1391 iobj->insn_info.node_id = node_id;
1392 iobj->insn_info.events = 0;
1393 iobj->operands = argv;
1394 iobj->operand_size = argc;
1395 iobj->sc_state = 0;
1396
1397 iseq_insn_each_markable_object(iobj, iseq_insn_each_object_write_barrier, (VALUE)iseq);
1398
1399 return iobj;
1400}
1401
1402static INSN *
1403new_insn_body(rb_iseq_t *iseq, int line_no, int node_id, enum ruby_vminsn_type insn_id, int argc, ...)
1404{
1405 VALUE *operands = 0;
1406 va_list argv;
1407 if (argc > 0) {
1408 int i;
1409 va_start(argv, argc);
1410 operands = compile_data_alloc2_type(iseq, VALUE, argc);
1411 for (i = 0; i < argc; i++) {
1412 VALUE v = va_arg(argv, VALUE);
1413 operands[i] = v;
1414 }
1415 va_end(argv);
1416 }
1417 return new_insn_core(iseq, line_no, node_id, insn_id, argc, operands);
1418}
1419
1420static INSN *
1421insn_replace_with_operands(rb_iseq_t *iseq, INSN *iobj, enum ruby_vminsn_type insn_id, int argc, ...)
1422{
1423 VALUE *operands = 0;
1424 va_list argv;
1425 if (argc > 0) {
1426 int i;
1427 va_start(argv, argc);
1428 operands = compile_data_alloc2_type(iseq, VALUE, argc);
1429 for (i = 0; i < argc; i++) {
1430 VALUE v = va_arg(argv, VALUE);
1431 operands[i] = v;
1432 }
1433 va_end(argv);
1434 }
1435
1436 iobj->insn_id = insn_id;
1437 iobj->operand_size = argc;
1438 iobj->operands = operands;
1439 iseq_insn_each_markable_object(iobj, iseq_insn_each_object_write_barrier, (VALUE)iseq);
1440
1441 return iobj;
1442}
1443
1444static const struct rb_callinfo *
1445new_callinfo(rb_iseq_t *iseq, ID mid, int argc, unsigned int flag, struct rb_callinfo_kwarg *kw_arg, int has_blockiseq)
1446{
1447 VM_ASSERT(argc >= 0);
1448
1449 if (kw_arg) {
1450 flag |= VM_CALL_KWARG;
1451 argc += kw_arg->keyword_len;
1452 }
1453
1454 if (!(flag & (VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KWARG | VM_CALL_KW_SPLAT | VM_CALL_FORWARDING))
1455 && !has_blockiseq) {
1456 flag |= VM_CALL_ARGS_SIMPLE;
1457 }
1458
1459 ISEQ_BODY(iseq)->ci_size++;
1460 const struct rb_callinfo *ci = vm_ci_new(mid, flag, argc, kw_arg);
1461 RB_OBJ_WRITTEN(iseq, Qundef, ci);
1462 return ci;
1463}
1464
1465static INSN *
1466new_insn_send(rb_iseq_t *iseq, int line_no, int node_id, ID id, VALUE argc, const rb_iseq_t *blockiseq, VALUE flag, struct rb_callinfo_kwarg *keywords)
1467{
1468 VALUE *operands = compile_data_calloc2_type(iseq, VALUE, 2);
1469 VALUE ci = (VALUE)new_callinfo(iseq, id, FIX2INT(argc), FIX2INT(flag), keywords, blockiseq != NULL);
1470 operands[0] = ci;
1471 operands[1] = (VALUE)blockiseq;
1472 if (blockiseq) {
1473 RB_OBJ_WRITTEN(iseq, Qundef, blockiseq);
1474 }
1475
1476 INSN *insn;
1477
1478 if (vm_ci_flag((struct rb_callinfo *)ci) & VM_CALL_FORWARDING) {
1479 insn = new_insn_core(iseq, line_no, node_id, BIN(sendforward), 2, operands);
1480 }
1481 else {
1482 insn = new_insn_core(iseq, line_no, node_id, BIN(send), 2, operands);
1483 }
1484
1485 RB_OBJ_WRITTEN(iseq, Qundef, ci);
1486 RB_GC_GUARD(ci);
1487 return insn;
1488}
1489
1490static rb_iseq_t *
1491new_child_iseq(rb_iseq_t *iseq, const NODE *const node,
1492 VALUE name, const rb_iseq_t *parent, enum rb_iseq_type type, int line_no)
1493{
1494 rb_iseq_t *ret_iseq;
1495 VALUE ast_value = rb_ruby_ast_new(node);
1496
1497 debugs("[new_child_iseq]> ---------------------------------------\n");
1498 int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
1499 ret_iseq = rb_iseq_new_with_opt(ast_value, name,
1500 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
1501 line_no, parent,
1502 isolated_depth ? isolated_depth + 1 : 0,
1503 type, ISEQ_COMPILE_DATA(iseq)->option,
1504 ISEQ_BODY(iseq)->variable.script_lines);
1505 debugs("[new_child_iseq]< ---------------------------------------\n");
1506 return ret_iseq;
1507}
1508
1509static rb_iseq_t *
1510new_child_iseq_with_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_callback_callback_func *ifunc,
1511 VALUE name, const rb_iseq_t *parent, enum rb_iseq_type type, int line_no)
1512{
1513 rb_iseq_t *ret_iseq;
1514
1515 debugs("[new_child_iseq_with_callback]> ---------------------------------------\n");
1516 ret_iseq = rb_iseq_new_with_callback(ifunc, name,
1517 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
1518 line_no, parent, type, ISEQ_COMPILE_DATA(iseq)->option);
1519 debugs("[new_child_iseq_with_callback]< ---------------------------------------\n");
1520 return ret_iseq;
1521}
1522
1523static void
1524set_catch_except_p(rb_iseq_t *iseq)
1525{
1526 RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq));
1527 ISEQ_COMPILE_DATA(iseq)->catch_except_p = true;
1528 if (ISEQ_BODY(iseq)->parent_iseq != NULL) {
1529 if (ISEQ_COMPILE_DATA(ISEQ_BODY(iseq)->parent_iseq)) {
1530 set_catch_except_p((rb_iseq_t *) ISEQ_BODY(iseq)->parent_iseq);
1531 }
1532 }
1533}
1534
1535/* Set body->catch_except_p to true if the ISeq may catch an exception. If it is false,
1536 JIT-ed code may be optimized. If we are extremely conservative, we should set true
1537 if catch table exists. But we want to optimize while loop, which always has catch
1538 table entries for break/next/redo.
1539
1540 So this function sets true for limited ISeqs with break/next/redo catch table entries
1541 whose child ISeq would really raise an exception. */
1542static void
1543update_catch_except_flags(rb_iseq_t *iseq, struct rb_iseq_constant_body *body)
1544{
1545 unsigned int pos;
1546 size_t i;
1547 int insn;
1548 const struct iseq_catch_table *ct = body->catch_table;
1549
1550 /* This assumes that a block has parent_iseq which may catch an exception from the block, and that
1551 BREAK/NEXT/REDO catch table entries are used only when `throw` insn is used in the block. */
1552 pos = 0;
1553 while (pos < body->iseq_size) {
1554 insn = rb_vm_insn_decode(body->iseq_encoded[pos]);
1555 if (insn == BIN(throw)) {
1556 set_catch_except_p(iseq);
1557 break;
1558 }
1559 pos += insn_len(insn);
1560 }
1561
1562 if (ct == NULL)
1563 return;
1564
1565 for (i = 0; i < ct->size; i++) {
1566 const struct iseq_catch_table_entry *entry =
1567 UNALIGNED_MEMBER_PTR(ct, entries[i]);
1568 if (entry->type != CATCH_TYPE_BREAK
1569 && entry->type != CATCH_TYPE_NEXT
1570 && entry->type != CATCH_TYPE_REDO) {
1571 RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq));
1572 ISEQ_COMPILE_DATA(iseq)->catch_except_p = true;
1573 break;
1574 }
1575 }
1576}
1577
1578static void
1579iseq_insert_nop_between_end_and_cont(rb_iseq_t *iseq)
1580{
1581 VALUE catch_table_ary = ISEQ_COMPILE_DATA(iseq)->catch_table_ary;
1582 if (NIL_P(catch_table_ary)) return;
1583 unsigned int i, tlen = (unsigned int)RARRAY_LEN(catch_table_ary);
1584 const VALUE *tptr = RARRAY_CONST_PTR(catch_table_ary);
1585 for (i = 0; i < tlen; i++) {
1586 const VALUE *ptr = RARRAY_CONST_PTR(tptr[i]);
1587 LINK_ELEMENT *end = (LINK_ELEMENT *)(ptr[2] & ~1);
1588 LINK_ELEMENT *cont = (LINK_ELEMENT *)(ptr[4] & ~1);
1589 LINK_ELEMENT *e;
1590
1591 enum rb_catch_type ct = (enum rb_catch_type)(ptr[0] & 0xffff);
1592
1593 if (ct != CATCH_TYPE_BREAK
1594 && ct != CATCH_TYPE_NEXT
1595 && ct != CATCH_TYPE_REDO) {
1596
1597 for (e = end; e && (IS_LABEL(e) || IS_TRACE(e)); e = e->next) {
1598 if (e == cont) {
1599 INSN *nop = new_insn_core(iseq, 0, -1, BIN(nop), 0, 0);
1600 ELEM_INSERT_NEXT(end, &nop->link);
1601 break;
1602 }
1603 }
1604 }
1605 }
1606
1607 RB_GC_GUARD(catch_table_ary);
1608}
1609
1610static int
1611iseq_setup_insn(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
1612{
1613 if (RTEST(ISEQ_COMPILE_DATA(iseq)->err_info))
1614 return COMPILE_NG;
1615
1616 /* debugs("[compile step 2] (iseq_array_to_linkedlist)\n"); */
1617
1618 if (compile_debug > 5)
1619 dump_disasm_list(FIRST_ELEMENT(anchor));
1620
1621 debugs("[compile step 3.1 (iseq_optimize)]\n");
1622 iseq_optimize(iseq, anchor);
1623
1624 if (compile_debug > 5)
1625 dump_disasm_list(FIRST_ELEMENT(anchor));
1626
1627 if (ISEQ_COMPILE_DATA(iseq)->option->instructions_unification) {
1628 debugs("[compile step 3.2 (iseq_insns_unification)]\n");
1629 iseq_insns_unification(iseq, anchor);
1630 if (compile_debug > 5)
1631 dump_disasm_list(FIRST_ELEMENT(anchor));
1632 }
1633
1634 debugs("[compile step 3.4 (iseq_insert_nop_between_end_and_cont)]\n");
1635 iseq_insert_nop_between_end_and_cont(iseq);
1636 if (compile_debug > 5)
1637 dump_disasm_list(FIRST_ELEMENT(anchor));
1638
1639 return COMPILE_OK;
1640}
1641
1642static int
1643iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
1644{
1645 if (RTEST(ISEQ_COMPILE_DATA(iseq)->err_info))
1646 return COMPILE_NG;
1647
1648 debugs("[compile step 4.1 (iseq_set_sequence)]\n");
1649 if (!iseq_set_sequence(iseq, anchor)) return COMPILE_NG;
1650 if (compile_debug > 5)
1651 dump_disasm_list(FIRST_ELEMENT(anchor));
1652
1653 debugs("[compile step 4.2 (iseq_set_exception_table)]\n");
1654 if (!iseq_set_exception_table(iseq)) return COMPILE_NG;
1655
1656 debugs("[compile step 4.3 (set_optargs_table)] \n");
1657 if (!iseq_set_optargs_table(iseq)) return COMPILE_NG;
1658
1659 debugs("[compile step 5 (iseq_translate_threaded_code)] \n");
1660 if (!rb_iseq_translate_threaded_code(iseq)) return COMPILE_NG;
1661
1662 debugs("[compile step 6 (update_catch_except_flags)] \n");
1663 RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq));
1664 update_catch_except_flags(iseq, ISEQ_BODY(iseq));
1665
1666 debugs("[compile step 6.1 (remove unused catch tables)] \n");
1667 RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq));
1668 if (!ISEQ_COMPILE_DATA(iseq)->catch_except_p && ISEQ_BODY(iseq)->catch_table) {
1669 ruby_sized_xfree(ISEQ_BODY(iseq)->catch_table, iseq_catch_table_bytes(ISEQ_BODY(iseq)->catch_table->size));
1670 ISEQ_BODY(iseq)->catch_table = NULL;
1671 }
1672
1673#if VM_INSN_INFO_TABLE_IMPL == 2
1674 if (ISEQ_BODY(iseq)->insns_info.succ_index_table == NULL) {
1675 debugs("[compile step 7 (rb_iseq_insns_info_encode_positions)] \n");
1676 rb_iseq_insns_info_encode_positions(iseq);
1677 }
1678#endif
1679
1680 if (compile_debug > 1) {
1681 VALUE str = rb_iseq_disasm(iseq);
1682 printf("%s\n", StringValueCStr(str));
1683 }
1684 verify_call_cache(iseq);
1685 debugs("[compile step: finish]\n");
1686
1687 return COMPILE_OK;
1688}
1689
1690static int
1691iseq_set_exception_local_table(rb_iseq_t *iseq)
1692{
1693 ISEQ_BODY(iseq)->local_table_size = numberof(rb_iseq_shared_exc_local_tbl);
1694 ISEQ_BODY(iseq)->local_table = rb_iseq_shared_exc_local_tbl;
1695 ISEQ_BODY(iseq)->lvar_states = NULL; // $! is read-only, so don't need lvar_states
1696 return COMPILE_OK;
1697}
1698
1699static int
1700get_lvar_level(const rb_iseq_t *iseq)
1701{
1702 int lev = 0;
1703 while (iseq != ISEQ_BODY(iseq)->local_iseq) {
1704 lev++;
1705 iseq = ISEQ_BODY(iseq)->parent_iseq;
1706 }
1707 return lev;
1708}
1709
1710static int
1711get_dyna_var_idx_at_raw(const rb_iseq_t *iseq, ID id)
1712{
1713 unsigned int i;
1714
1715 for (i = 0; i < ISEQ_BODY(iseq)->local_table_size; i++) {
1716 if (ISEQ_BODY(iseq)->local_table[i] == id) {
1717 return (int)i;
1718 }
1719 }
1720 return -1;
1721}
1722
1723static int
1724get_local_var_idx(const rb_iseq_t *iseq, ID id)
1725{
1726 int idx = get_dyna_var_idx_at_raw(ISEQ_BODY(iseq)->local_iseq, id);
1727
1728 if (idx < 0) {
1729 COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq),
1730 "get_local_var_idx: %d", idx);
1731 }
1732
1733 return idx;
1734}
1735
1736static int
1737get_dyna_var_idx(const rb_iseq_t *iseq, ID id, int *level, int *ls)
1738{
1739 int lv = 0, idx = -1;
1740 const rb_iseq_t *const topmost_iseq = iseq;
1741
1742 while (iseq) {
1743 idx = get_dyna_var_idx_at_raw(iseq, id);
1744 if (idx >= 0) {
1745 break;
1746 }
1747 iseq = ISEQ_BODY(iseq)->parent_iseq;
1748 lv++;
1749 }
1750
1751 if (idx < 0) {
1752 COMPILE_ERROR(topmost_iseq, ISEQ_LAST_LINE(topmost_iseq),
1753 "get_dyna_var_idx: -1");
1754 }
1755
1756 *level = lv;
1757 *ls = ISEQ_BODY(iseq)->local_table_size;
1758 return idx;
1759}
1760
1761static int
1762iseq_local_block_param_p(const rb_iseq_t *iseq, unsigned int idx, unsigned int level)
1763{
1764 const struct rb_iseq_constant_body *body;
1765 while (level > 0) {
1766 iseq = ISEQ_BODY(iseq)->parent_iseq;
1767 level--;
1768 }
1769 body = ISEQ_BODY(iseq);
1770 if (body->local_iseq == iseq && /* local variables */
1771 body->param.flags.has_block &&
1772 body->local_table_size - body->param.block_start == idx) {
1773 return TRUE;
1774 }
1775 else {
1776 return FALSE;
1777 }
1778}
1779
1780static int
1781iseq_block_param_id_p(const rb_iseq_t *iseq, ID id, int *pidx, int *plevel)
1782{
1783 int level, ls;
1784 int idx = get_dyna_var_idx(iseq, id, &level, &ls);
1785 if (iseq_local_block_param_p(iseq, ls - idx, level)) {
1786 *pidx = ls - idx;
1787 *plevel = level;
1788 return TRUE;
1789 }
1790 else {
1791 return FALSE;
1792 }
1793}
1794
1795static void
1796access_outer_variables(const rb_iseq_t *iseq, int level, ID id, bool write)
1797{
1798 int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
1799
1800 if (isolated_depth && level >= isolated_depth) {
1801 if (id == rb_intern("yield")) {
1802 COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not yield from isolated Proc");
1803 }
1804 else {
1805 COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not access variable '%s' from isolated Proc", rb_id2name(id));
1806 }
1807 }
1808
1809 for (int i=0; i<level; i++) {
1810 VALUE val;
1811 struct rb_id_table *ovs = ISEQ_BODY(iseq)->outer_variables;
1812
1813 if (!ovs) {
1814 ovs = ISEQ_BODY(iseq)->outer_variables = rb_id_table_create(8);
1815 }
1816
1817 if (rb_id_table_lookup(ISEQ_BODY(iseq)->outer_variables, id, &val)) {
1818 if (write && !val) {
1819 rb_id_table_insert(ISEQ_BODY(iseq)->outer_variables, id, Qtrue);
1820 }
1821 }
1822 else {
1823 rb_id_table_insert(ISEQ_BODY(iseq)->outer_variables, id, RBOOL(write));
1824 }
1825
1826 iseq = ISEQ_BODY(iseq)->parent_iseq;
1827 }
1828}
1829
1830static ID
1831iseq_lvar_id(const rb_iseq_t *iseq, int idx, int level)
1832{
1833 for (int i=0; i<level; i++) {
1834 iseq = ISEQ_BODY(iseq)->parent_iseq;
1835 }
1836
1837 ID id = ISEQ_BODY(iseq)->local_table[ISEQ_BODY(iseq)->local_table_size - idx];
1838 // fprintf(stderr, "idx:%d level:%d ID:%s\n", idx, level, rb_id2name(id));
1839 return id;
1840}
1841
1842static void
1843update_lvar_state(const rb_iseq_t *iseq, int level, int idx)
1844{
1845 for (int i=0; i<level; i++) {
1846 iseq = ISEQ_BODY(iseq)->parent_iseq;
1847 }
1848
1849 enum lvar_state *states = ISEQ_BODY(iseq)->lvar_states;
1850 int table_idx = ISEQ_BODY(iseq)->local_table_size - idx;
1851 switch (states[table_idx]) {
1852 case lvar_uninitialized:
1853 states[table_idx] = lvar_initialized;
1854 break;
1855 case lvar_initialized:
1856 states[table_idx] = lvar_reassigned;
1857 break;
1858 case lvar_reassigned:
1859 /* nothing */
1860 break;
1861 default:
1862 rb_bug("unreachable");
1863 }
1864}
1865
1866static int
1867iseq_set_parameters_lvar_state(const rb_iseq_t *iseq)
1868{
1869 for (unsigned int i=0; i<ISEQ_BODY(iseq)->param.size; i++) {
1870 ISEQ_BODY(iseq)->lvar_states[i] = lvar_initialized;
1871 }
1872
1873 int lead_num = ISEQ_BODY(iseq)->param.lead_num;
1874 int opt_num = ISEQ_BODY(iseq)->param.opt_num;
1875 for (int i=0; i<opt_num; i++) {
1876 ISEQ_BODY(iseq)->lvar_states[lead_num + i] = lvar_uninitialized;
1877 }
1878
1879 return COMPILE_OK;
1880}
1881
1882static void
1883iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level)
1884{
1885 if (iseq_local_block_param_p(iseq, idx, level)) {
1886 ADD_INSN2(seq, line_node, getblockparam, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
1887 }
1888 else {
1889 ADD_INSN2(seq, line_node, getlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
1890 }
1891 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qfalse);
1892}
1893
1894static void
1895iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level)
1896{
1897 if (iseq_local_block_param_p(iseq, idx, level)) {
1898 ADD_INSN2(seq, line_node, setblockparam, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
1899 }
1900 else {
1901 ADD_INSN2(seq, line_node, setlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
1902 }
1903 update_lvar_state(iseq, level, idx);
1904 if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue);
1905}
1906
1907
1908
1909static void
1910iseq_calc_param_size(rb_iseq_t *iseq)
1911{
1912 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
1913 if (body->param.flags.has_opt ||
1914 body->param.flags.has_post ||
1915 body->param.flags.has_rest ||
1916 body->param.flags.has_block ||
1917 body->param.flags.has_kw ||
1918 body->param.flags.has_kwrest) {
1919
1920 if (body->param.flags.has_block) {
1921 body->param.size = body->param.block_start + 1;
1922 }
1923 else if (body->param.flags.has_kwrest) {
1924 body->param.size = body->param.keyword->rest_start + 1;
1925 }
1926 else if (body->param.flags.has_kw) {
1927 body->param.size = body->param.keyword->bits_start + 1;
1928 }
1929 else if (body->param.flags.has_post) {
1930 body->param.size = body->param.post_start + body->param.post_num;
1931 }
1932 else if (body->param.flags.has_rest) {
1933 body->param.size = body->param.rest_start + 1;
1934 }
1935 else if (body->param.flags.has_opt) {
1936 body->param.size = body->param.lead_num + body->param.opt_num;
1937 }
1938 else {
1940 }
1941 }
1942 else {
1943 body->param.size = body->param.lead_num;
1944 }
1945}
1946
1947static int
1948iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
1949 const struct rb_args_info *args, int arg_size)
1950{
1951 const rb_node_kw_arg_t *node = args->kw_args;
1952 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
1953 struct rb_iseq_param_keyword *keyword;
1954 const VALUE default_values = rb_ary_hidden_new(1);
1955 const VALUE complex_mark = rb_str_tmp_new(0);
1956 int kw = 0, rkw = 0, di = 0, i;
1957
1958 body->param.flags.has_kw = TRUE;
1959 body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
1960
1961 while (node) {
1962 kw++;
1963 node = node->nd_next;
1964 }
1965 arg_size += kw;
1966 keyword->bits_start = arg_size++;
1967
1968 node = args->kw_args;
1969 while (node) {
1970 const NODE *val_node = get_nd_value(node->nd_body);
1971 VALUE dv;
1972
1973 if (val_node == NODE_SPECIAL_REQUIRED_KEYWORD) {
1974 ++rkw;
1975 }
1976 else {
1977 switch (nd_type(val_node)) {
1978 case NODE_SYM:
1979 dv = rb_node_sym_string_val(val_node);
1980 break;
1981 case NODE_REGX:
1982 dv = rb_node_regx_string_val(val_node);
1983 break;
1984 case NODE_LINE:
1985 dv = rb_node_line_lineno_val(val_node);
1986 break;
1987 case NODE_INTEGER:
1988 dv = rb_node_integer_literal_val(val_node);
1989 break;
1990 case NODE_FLOAT:
1991 dv = rb_node_float_literal_val(val_node);
1992 break;
1993 case NODE_RATIONAL:
1994 dv = rb_node_rational_literal_val(val_node);
1995 break;
1996 case NODE_IMAGINARY:
1997 dv = rb_node_imaginary_literal_val(val_node);
1998 break;
1999 case NODE_ENCODING:
2000 dv = rb_node_encoding_val(val_node);
2001 break;
2002 case NODE_NIL:
2003 dv = Qnil;
2004 break;
2005 case NODE_TRUE:
2006 dv = Qtrue;
2007 break;
2008 case NODE_FALSE:
2009 dv = Qfalse;
2010 break;
2011 default:
2012 NO_CHECK(COMPILE_POPPED(optargs, "kwarg", RNODE(node))); /* nd_type_p(node, NODE_KW_ARG) */
2013 dv = complex_mark;
2014 }
2015
2016 keyword->num = ++di;
2017 rb_ary_push(default_values, dv);
2018 }
2019
2020 node = node->nd_next;
2021 }
2022
2023 keyword->num = kw;
2024
2025 if (RNODE_DVAR(args->kw_rest_arg)->nd_vid != 0) {
2026 ID kw_id = ISEQ_BODY(iseq)->local_table[arg_size];
2027 keyword->rest_start = arg_size++;
2028 body->param.flags.has_kwrest = TRUE;
2029
2030 if (kw_id == idPow) body->param.flags.anon_kwrest = TRUE;
2031 }
2032 keyword->required_num = rkw;
2033 keyword->table = &body->local_table[keyword->bits_start - keyword->num];
2034
2035 if (RARRAY_LEN(default_values)) {
2036 VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
2037
2038 for (i = 0; i < RARRAY_LEN(default_values); i++) {
2039 VALUE dv = RARRAY_AREF(default_values, i);
2040 if (dv == complex_mark) dv = Qundef;
2042 RB_OBJ_WRITE(iseq, &dvs[i], dv);
2043 }
2044
2045 keyword->default_values = dvs;
2046 }
2047 return arg_size;
2048}
2049
2050static void
2051iseq_set_use_block(rb_iseq_t *iseq)
2052{
2053 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
2054 if (!body->param.flags.use_block) {
2055 body->param.flags.use_block = 1;
2056
2057 rb_vm_t *vm = GET_VM();
2058
2059 if (!rb_warning_category_enabled_p(RB_WARN_CATEGORY_STRICT_UNUSED_BLOCK)) {
2060 st_data_t key = (st_data_t)rb_intern_str(body->location.label); // String -> ID
2061 set_insert(vm->unused_block_warning_table, key);
2062 }
2063 }
2064}
2065
2066static int
2067iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *const node_args)
2068{
2069 debugs("iseq_set_arguments: %s\n", node_args ? "" : "0");
2070
2071 if (node_args) {
2072 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
2073 const struct rb_args_info *const args = &RNODE_ARGS(node_args)->nd_ainfo;
2074 ID rest_id = 0;
2075 int last_comma = 0;
2076 ID block_id = 0;
2077 int arg_size;
2078
2079 EXPECT_NODE("iseq_set_arguments", node_args, NODE_ARGS, COMPILE_NG);
2080
2081 body->param.lead_num = arg_size = (int)args->pre_args_num;
2082 if (body->param.lead_num > 0) body->param.flags.has_lead = TRUE;
2083 debugs(" - argc: %d\n", body->param.lead_num);
2084
2085 rest_id = args->rest_arg;
2086 if (rest_id == NODE_SPECIAL_EXCESSIVE_COMMA) {
2087 last_comma = 1;
2088 rest_id = 0;
2089 }
2090 block_id = args->block_arg;
2091
2092 bool optimized_forward = (args->forwarding && args->pre_args_num == 0 && !args->opt_args);
2093
2094 if (optimized_forward) {
2095 rest_id = 0;
2096 block_id = 0;
2097 }
2098
2099 if (args->opt_args) {
2100 const rb_node_opt_arg_t *node = args->opt_args;
2101 LABEL *label;
2102 VALUE labels = rb_ary_hidden_new(1);
2103 VALUE *opt_table;
2104 int i = 0, j;
2105
2106 while (node) {
2107 label = NEW_LABEL(nd_line(RNODE(node)));
2108 rb_ary_push(labels, (VALUE)label | 1);
2109 ADD_LABEL(optargs, label);
2110 NO_CHECK(COMPILE_POPPED(optargs, "optarg", node->nd_body));
2111 node = node->nd_next;
2112 i += 1;
2113 }
2114
2115 /* last label */
2116 label = NEW_LABEL(nd_line(node_args));
2117 rb_ary_push(labels, (VALUE)label | 1);
2118 ADD_LABEL(optargs, label);
2119
2120 opt_table = ALLOC_N(VALUE, i+1);
2121
2122 MEMCPY(opt_table, RARRAY_CONST_PTR(labels), VALUE, i+1);
2123 for (j = 0; j < i+1; j++) {
2124 opt_table[j] &= ~1;
2125 }
2126 rb_ary_clear(labels);
2127
2128 body->param.flags.has_opt = TRUE;
2129 body->param.opt_num = i;
2130 body->param.opt_table = opt_table;
2131 arg_size += i;
2132 }
2133
2134 if (rest_id) {
2135 body->param.rest_start = arg_size++;
2136 body->param.flags.has_rest = TRUE;
2137 if (rest_id == '*') body->param.flags.anon_rest = TRUE;
2138 RUBY_ASSERT(body->param.rest_start != -1);
2139 }
2140
2141 if (args->first_post_arg) {
2142 body->param.post_start = arg_size;
2143 body->param.post_num = args->post_args_num;
2144 body->param.flags.has_post = TRUE;
2145 arg_size += args->post_args_num;
2146
2147 if (body->param.flags.has_rest) { /* TODO: why that? */
2148 body->param.post_start = body->param.rest_start + 1;
2149 }
2150 }
2151
2152 if (args->kw_args) {
2153 arg_size = iseq_set_arguments_keywords(iseq, optargs, args, arg_size);
2154 }
2155 else if (args->kw_rest_arg && !optimized_forward) {
2156 ID kw_id = ISEQ_BODY(iseq)->local_table[arg_size];
2157 struct rb_iseq_param_keyword *keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
2158 keyword->rest_start = arg_size++;
2159 body->param.keyword = keyword;
2160 body->param.flags.has_kwrest = TRUE;
2161
2162 static ID anon_kwrest = 0;
2163 if (!anon_kwrest) anon_kwrest = rb_intern("**");
2164 if (kw_id == anon_kwrest) body->param.flags.anon_kwrest = TRUE;
2165 }
2166 else if (args->no_kwarg) {
2167 body->param.flags.accepts_no_kwarg = TRUE;
2168 }
2169
2170 if (args->no_blockarg) {
2171 body->param.flags.accepts_no_block = TRUE;
2172 }
2173 else if (block_id) {
2174 body->param.block_start = arg_size++;
2175 body->param.flags.has_block = TRUE;
2176 iseq_set_use_block(iseq);
2177 }
2178
2179 // Only optimize specifically methods like this: `foo(...)`
2180 if (optimized_forward) {
2181 body->param.flags.use_block = 1;
2182 body->param.flags.forwardable = TRUE;
2183 arg_size = 1;
2184 }
2185
2186 iseq_calc_param_size(iseq);
2187 body->param.size = arg_size;
2188
2189 if (args->pre_init) { /* m_init */
2190 NO_CHECK(COMPILE_POPPED(optargs, "init arguments (m)", args->pre_init));
2191 }
2192 if (args->post_init) { /* p_init */
2193 NO_CHECK(COMPILE_POPPED(optargs, "init arguments (p)", args->post_init));
2194 }
2195
2196 if (body->type == ISEQ_TYPE_BLOCK) {
2197 if (body->param.flags.has_opt == FALSE &&
2198 body->param.flags.has_post == FALSE &&
2199 body->param.flags.has_rest == FALSE &&
2200 body->param.flags.has_kw == FALSE &&
2201 body->param.flags.has_kwrest == FALSE) {
2202
2203 if (body->param.lead_num == 1 && last_comma == 0) {
2204 /* {|a|} */
2205 body->param.flags.ambiguous_param0 = TRUE;
2206 }
2207 }
2208 }
2209 }
2210
2211 return COMPILE_OK;
2212}
2213
2214static int
2215iseq_set_local_table(rb_iseq_t *iseq, const rb_ast_id_table_t *tbl, const NODE *const node_args)
2216{
2217 unsigned int size = tbl ? tbl->size : 0;
2218 unsigned int offset = 0;
2219
2220 if (node_args) {
2221 struct rb_args_info *args = &RNODE_ARGS(node_args)->nd_ainfo;
2222
2223 // If we have a function that only has `...` as the parameter,
2224 // then its local table should only be `...`
2225 // FIXME: I think this should be fixed in the AST rather than special case here.
2226 if (args->forwarding && args->pre_args_num == 0 && !args->opt_args) {
2227 CHECK(size >= 3);
2228 size -= 3;
2229 offset += 3;
2230 }
2231 }
2232
2233 if (size > 0) {
2234 ID *ids = ALLOC_N(ID, size);
2235 MEMCPY(ids, tbl->ids + offset, ID, size);
2236 ISEQ_BODY(iseq)->local_table = ids;
2237
2238 enum lvar_state *states = ALLOC_N(enum lvar_state, size);
2239 // fprintf(stderr, "iseq:%p states:%p size:%d\n", iseq, states, (int)size);
2240 for (unsigned int i=0; i<size; i++) {
2241 states[i] = lvar_uninitialized;
2242 // fprintf(stderr, "id:%s\n", rb_id2name(ISEQ_BODY(iseq)->local_table[i]));
2243 }
2244 ISEQ_BODY(iseq)->lvar_states = states;
2245 }
2246 ISEQ_BODY(iseq)->local_table_size = size;
2247
2248 debugs("iseq_set_local_table: %u\n", ISEQ_BODY(iseq)->local_table_size);
2249 return COMPILE_OK;
2250}
2251
2252int
2253rb_iseq_cdhash_cmp(VALUE val, VALUE lit)
2254{
2255 int tval, tlit;
2256
2257 if (val == lit) {
2258 return 0;
2259 }
2260 else if ((tlit = OBJ_BUILTIN_TYPE(lit)) == -1) {
2261 return val != lit;
2262 }
2263 else if ((tval = OBJ_BUILTIN_TYPE(val)) == -1) {
2264 return -1;
2265 }
2266 else if (tlit != tval) {
2267 return -1;
2268 }
2269 else if (tlit == T_SYMBOL) {
2270 return val != lit;
2271 }
2272 else if (tlit == T_STRING) {
2273 return rb_str_hash_cmp(lit, val);
2274 }
2275 else if (tlit == T_BIGNUM) {
2276 long x = FIX2LONG(rb_big_cmp(lit, val));
2277
2278 /* Given lit and val are both Bignum, x must be -1, 0, 1.
2279 * There is no need to call rb_fix2int here. */
2280 RUBY_ASSERT((x == 1) || (x == 0) || (x == -1));
2281 return (int)x;
2282 }
2283 else if (tlit == T_FLOAT) {
2284 return rb_float_cmp(lit, val);
2285 }
2286 else if (tlit == T_RATIONAL) {
2287 const struct RRational *rat1 = RRATIONAL(val);
2288 const struct RRational *rat2 = RRATIONAL(lit);
2289 return rb_iseq_cdhash_cmp(rat1->num, rat2->num) || rb_iseq_cdhash_cmp(rat1->den, rat2->den);
2290 }
2291 else if (tlit == T_COMPLEX) {
2292 const struct RComplex *comp1 = RCOMPLEX(val);
2293 const struct RComplex *comp2 = RCOMPLEX(lit);
2294 return rb_iseq_cdhash_cmp(comp1->real, comp2->real) || rb_iseq_cdhash_cmp(comp1->imag, comp2->imag);
2295 }
2296 else if (tlit == T_REGEXP) {
2297 return rb_reg_equal(val, lit) ? 0 : -1;
2298 }
2299 else {
2301 }
2302}
2303
2304st_index_t
2305rb_iseq_cdhash_hash(VALUE a)
2306{
2307 switch (OBJ_BUILTIN_TYPE(a)) {
2308 case -1:
2309 case T_SYMBOL:
2310 return (st_index_t)a;
2311 case T_STRING:
2312 return rb_str_hash(a);
2313 case T_BIGNUM:
2314 return FIX2LONG(rb_big_hash(a));
2315 case T_FLOAT:
2316 return rb_dbl_long_hash(RFLOAT_VALUE(a));
2317 case T_RATIONAL:
2318 return rb_rational_hash(a);
2319 case T_COMPLEX:
2320 return rb_complex_hash(a);
2321 case T_REGEXP:
2322 return NUM2LONG(rb_reg_hash(a));
2323 default:
2325 }
2326}
2327
2328static const struct st_hash_type cdhash_type = {
2329 rb_iseq_cdhash_cmp,
2330 rb_iseq_cdhash_hash,
2331};
2332
2334 VALUE hash;
2335 int pos;
2336 int len;
2337};
2338
2339static int
2340cdhash_set_label_i(VALUE key, VALUE val, VALUE ptr)
2341{
2342 struct cdhash_set_label_struct *data = (struct cdhash_set_label_struct *)ptr;
2343 LABEL *lobj = (LABEL *)(val & ~1);
2344 rb_hash_aset(data->hash, key, INT2FIX(lobj->position - (data->pos+data->len)));
2345 return ST_CONTINUE;
2346}
2347
2348
2349static inline VALUE
2350get_ivar_ic_value(rb_iseq_t *iseq,ID id)
2351{
2352 return INT2FIX(ISEQ_BODY(iseq)->ivc_size++);
2353}
2354
2355static inline VALUE
2356get_cvar_ic_value(rb_iseq_t *iseq,ID id)
2357{
2358 VALUE val;
2359 struct rb_id_table *tbl = ISEQ_COMPILE_DATA(iseq)->ivar_cache_table;
2360 if (tbl) {
2361 if (rb_id_table_lookup(tbl,id,&val)) {
2362 return val;
2363 }
2364 }
2365 else {
2366 tbl = rb_id_table_create(1);
2367 ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = tbl;
2368 }
2369 val = INT2FIX(ISEQ_BODY(iseq)->icvarc_size++);
2370 rb_id_table_insert(tbl,id,val);
2371 return val;
2372}
2373
2374#define BADINSN_DUMP(anchor, list, dest) \
2375 dump_disasm_list_with_cursor(FIRST_ELEMENT(anchor), list, dest)
2376
2377#define BADINSN_ERROR \
2378 (SIZED_FREE_N(generated_iseq, generated_iseq_size), \
2379 SIZED_FREE_N(insns_info, insns_info_size), \
2380 BADINSN_DUMP(anchor, list, NULL), \
2381 COMPILE_ERROR)
2382
2383static int
2384fix_sp_depth(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
2385{
2386 int stack_max = 0, sp = 0, line = 0;
2387 LINK_ELEMENT *list;
2388
2389 for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
2390 if (IS_LABEL(list)) {
2391 LABEL *lobj = (LABEL *)list;
2392 lobj->set = TRUE;
2393 }
2394 }
2395
2396 for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
2397 switch (list->type) {
2398 case ISEQ_ELEMENT_INSN:
2399 {
2400 int j, len, insn;
2401 const char *types;
2402 VALUE *operands;
2403 INSN *iobj = (INSN *)list;
2404
2405 /* update sp */
2406 sp = calc_sp_depth(sp, iobj);
2407 if (sp < 0) {
2408 BADINSN_DUMP(anchor, list, NULL);
2409 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2410 "argument stack underflow (%d)", sp);
2411 return -1;
2412 }
2413 if (sp > stack_max) {
2414 stack_max = sp;
2415 }
2416
2417 line = iobj->insn_info.line_no;
2418 /* fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp); */
2419 operands = iobj->operands;
2420 insn = iobj->insn_id;
2421 types = insn_op_types(insn);
2422 len = insn_len(insn);
2423
2424 /* operand check */
2425 if (iobj->operand_size != len - 1) {
2426 /* printf("operand size miss! (%d, %d)\n", iobj->operand_size, len); */
2427 BADINSN_DUMP(anchor, list, NULL);
2428 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2429 "operand size miss! (%d for %d)",
2430 iobj->operand_size, len - 1);
2431 return -1;
2432 }
2433
2434 for (j = 0; types[j]; j++) {
2435 if (types[j] == TS_OFFSET) {
2436 /* label(destination position) */
2437 LABEL *lobj = (LABEL *)operands[j];
2438 if (!lobj->set) {
2439 BADINSN_DUMP(anchor, list, NULL);
2440 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2441 "unknown label: "LABEL_FORMAT, lobj->label_no);
2442 return -1;
2443 }
2444 if (lobj->sp == -1) {
2445 lobj->sp = sp;
2446 }
2447 else if (lobj->sp != sp) {
2448 debugs("%s:%d: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
2449 RSTRING_PTR(rb_iseq_path(iseq)), line,
2450 lobj->label_no, lobj->sp, sp);
2451 }
2452 }
2453 }
2454 break;
2455 }
2456 case ISEQ_ELEMENT_LABEL:
2457 {
2458 LABEL *lobj = (LABEL *)list;
2459 if (lobj->sp == -1) {
2460 lobj->sp = sp;
2461 }
2462 else {
2463 if (lobj->sp != sp) {
2464 debugs("%s:%d: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
2465 RSTRING_PTR(rb_iseq_path(iseq)), line,
2466 lobj->label_no, lobj->sp, sp);
2467 }
2468 sp = lobj->sp;
2469 }
2470 break;
2471 }
2472 case ISEQ_ELEMENT_TRACE:
2473 {
2474 /* ignore */
2475 break;
2476 }
2477 case ISEQ_ELEMENT_ADJUST:
2478 {
2479 ADJUST *adjust = (ADJUST *)list;
2480 int orig_sp = sp;
2481
2482 sp = adjust->label ? adjust->label->sp : 0;
2483 if (adjust->line_no != -1 && orig_sp - sp < 0) {
2484 BADINSN_DUMP(anchor, list, NULL);
2485 COMPILE_ERROR(iseq, adjust->line_no,
2486 "iseq_set_sequence: adjust bug %d < %d",
2487 orig_sp, sp);
2488 return -1;
2489 }
2490 break;
2491 }
2492 default:
2493 BADINSN_DUMP(anchor, list, NULL);
2494 COMPILE_ERROR(iseq, line, "unknown list type: %d", list->type);
2495 return -1;
2496 }
2497 }
2498 return stack_max;
2499}
2500
2501static int
2502add_insn_info(struct iseq_insn_info_entry *insns_info, unsigned int *positions,
2503 int insns_info_index, int code_index, const INSN *iobj)
2504{
2505 if (insns_info_index == 0 ||
2506 insns_info[insns_info_index-1].line_no != iobj->insn_info.line_no ||
2507#ifdef USE_ISEQ_NODE_ID
2508 insns_info[insns_info_index-1].node_id != iobj->insn_info.node_id ||
2509#endif
2510 insns_info[insns_info_index-1].events != iobj->insn_info.events) {
2511 insns_info[insns_info_index].line_no = iobj->insn_info.line_no;
2512#ifdef USE_ISEQ_NODE_ID
2513 insns_info[insns_info_index].node_id = iobj->insn_info.node_id;
2514#endif
2515 insns_info[insns_info_index].events = iobj->insn_info.events;
2516 positions[insns_info_index] = code_index;
2517 return TRUE;
2518 }
2519 return FALSE;
2520}
2521
2522static int
2523add_adjust_info(struct iseq_insn_info_entry *insns_info, unsigned int *positions,
2524 int insns_info_index, int code_index, const ADJUST *adjust)
2525{
2526 insns_info[insns_info_index].line_no = adjust->line_no;
2527 insns_info[insns_info_index].node_id = -1;
2528 insns_info[insns_info_index].events = 0;
2529 positions[insns_info_index] = code_index;
2530 return TRUE;
2531}
2532
2533static ID *
2534array_to_idlist(VALUE arr)
2535{
2537 long size = RARRAY_LEN(arr);
2538 ID *ids = (ID *)ALLOC_N(ID, size + 1);
2539 for (long i = 0; i < size; i++) {
2540 VALUE sym = RARRAY_AREF(arr, i);
2541 ids[i] = SYM2ID(sym);
2542 }
2543 ids[size] = 0;
2544 return ids;
2545}
2546
2547static VALUE
2548idlist_to_array(const ID *ids)
2549{
2550 VALUE arr = rb_ary_new();
2551 while (*ids) {
2552 rb_ary_push(arr, ID2SYM(*ids++));
2553 }
2554 return arr;
2555}
2556
2560static int
2561iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
2562{
2563 struct iseq_insn_info_entry *insns_info;
2564 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
2565 unsigned int *positions;
2566 LINK_ELEMENT *list;
2567 VALUE *generated_iseq;
2568 rb_event_flag_t events = 0;
2569 long data = 0;
2570
2571 int insn_num, code_index, insns_info_index, sp = 0;
2572 int stack_max = fix_sp_depth(iseq, anchor);
2573
2574 if (stack_max < 0) return COMPILE_NG;
2575
2576 /* fix label position */
2577 insn_num = code_index = 0;
2578 for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
2579 switch (list->type) {
2580 case ISEQ_ELEMENT_INSN:
2581 {
2582 INSN *iobj = (INSN *)list;
2583 /* update sp */
2584 sp = calc_sp_depth(sp, iobj);
2585 insn_num++;
2586 events = iobj->insn_info.events |= events;
2587 if (ISEQ_COVERAGE(iseq)) {
2588 if (ISEQ_LINE_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_LINE) &&
2589 !(rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES)) {
2590 int line = iobj->insn_info.line_no - 1;
2591 if (line >= 0 && line < RARRAY_LEN(ISEQ_LINE_COVERAGE(iseq))) {
2592 RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line, INT2FIX(0));
2593 }
2594 }
2595 if (ISEQ_BRANCH_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_BRANCH)) {
2596 while (RARRAY_LEN(ISEQ_PC2BRANCHINDEX(iseq)) <= code_index) {
2597 rb_ary_push(ISEQ_PC2BRANCHINDEX(iseq), Qnil);
2598 }
2599 RARRAY_ASET(ISEQ_PC2BRANCHINDEX(iseq), code_index, INT2FIX(data));
2600 }
2601 }
2602 code_index += insn_data_length(iobj);
2603 events = 0;
2604 data = 0;
2605 break;
2606 }
2607 case ISEQ_ELEMENT_LABEL:
2608 {
2609 LABEL *lobj = (LABEL *)list;
2610 lobj->position = code_index;
2611 if (lobj->sp != sp) {
2612 debugs("%s: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
2613 RSTRING_PTR(rb_iseq_path(iseq)),
2614 lobj->label_no, lobj->sp, sp);
2615 }
2616 sp = lobj->sp;
2617 break;
2618 }
2619 case ISEQ_ELEMENT_TRACE:
2620 {
2621 TRACE *trace = (TRACE *)list;
2622 events |= trace->event;
2623 if (trace->event & RUBY_EVENT_COVERAGE_BRANCH) data = trace->data;
2624 break;
2625 }
2626 case ISEQ_ELEMENT_ADJUST:
2627 {
2628 ADJUST *adjust = (ADJUST *)list;
2629 if (adjust->line_no != -1) {
2630 int orig_sp = sp;
2631 sp = adjust->label ? adjust->label->sp : 0;
2632 if (orig_sp - sp > 0) {
2633 if (orig_sp - sp > 1) code_index++; /* 1 operand */
2634 code_index++; /* insn */
2635 insn_num++;
2636 }
2637 }
2638 break;
2639 }
2640 default: break;
2641 }
2642 }
2643
2644 /* make instruction sequence */
2645 const int generated_iseq_size = code_index;
2646 generated_iseq = ALLOC_N(VALUE, code_index);
2647
2648 const int insns_info_size = insn_num;
2649 insns_info = ALLOC_N(struct iseq_insn_info_entry, insn_num);
2650
2651 const int positions_size = insn_num;
2652 positions = ALLOC_N(unsigned int, insn_num);
2653 if (ISEQ_IS_SIZE(body)) {
2654 body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(body));
2655 }
2656 else {
2657 body->is_entries = NULL;
2658 }
2659
2660 if (body->ci_size) {
2661 body->call_data = ZALLOC_N(struct rb_call_data, body->ci_size);
2662 }
2663 else {
2664 body->call_data = NULL;
2665 }
2666 ISEQ_COMPILE_DATA(iseq)->ci_index = 0;
2667
2668 // Calculate the bitmask buffer size.
2669 // Round the generated_iseq size up to the nearest multiple
2670 // of the number of bits in an unsigned long.
2671
2672 // Allocate enough room for the bitmask list
2673 iseq_bits_t * mark_offset_bits;
2674 int code_size = code_index;
2675
2676 bool needs_bitmap = false;
2677
2678 const size_t mark_offset_bits_size = ISEQ_MBITS_BUFLEN(code_index);
2679 if (mark_offset_bits_size == 1) {
2680 mark_offset_bits = &ISEQ_COMPILE_DATA(iseq)->mark_bits.single;
2681 ISEQ_COMPILE_DATA(iseq)->is_single_mark_bit = true;
2682 }
2683 else {
2684 mark_offset_bits = ZALLOC_N(iseq_bits_t, mark_offset_bits_size);
2685 ISEQ_COMPILE_DATA(iseq)->mark_bits.list = mark_offset_bits;
2686 ISEQ_COMPILE_DATA(iseq)->is_single_mark_bit = false;
2687 }
2688
2689 ISEQ_COMPILE_DATA(iseq)->iseq_encoded = (void *)generated_iseq;
2690 ISEQ_COMPILE_DATA(iseq)->iseq_size = code_index;
2691
2692 list = FIRST_ELEMENT(anchor);
2693 insns_info_index = code_index = sp = 0;
2694
2695 while (list) {
2696 switch (list->type) {
2697 case ISEQ_ELEMENT_INSN:
2698 {
2699 int j, len, insn;
2700 const char *types;
2701 VALUE *operands;
2702 INSN *iobj = (INSN *)list;
2703
2704 /* update sp */
2705 sp = calc_sp_depth(sp, iobj);
2706 /* fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp); */
2707 operands = iobj->operands;
2708 insn = iobj->insn_id;
2709 generated_iseq[code_index] = insn;
2710 types = insn_op_types(insn);
2711 len = insn_len(insn);
2712
2713 for (j = 0; types[j]; j++) {
2714 char type = types[j];
2715
2716 /* printf("--> [%c - (%d-%d)]\n", type, k, j); */
2717 switch (type) {
2718 case TS_OFFSET:
2719 {
2720 /* label(destination position) */
2721 LABEL *lobj = (LABEL *)operands[j];
2722 generated_iseq[code_index + 1 + j] = lobj->position - (code_index + len);
2723 break;
2724 }
2725 case TS_CDHASH:
2726 {
2727 VALUE map = operands[j];
2728 struct cdhash_set_label_struct data;
2729 data.hash = map;
2730 data.pos = code_index;
2731 data.len = len;
2732 rb_hash_foreach(map, cdhash_set_label_i, (VALUE)&data);
2733
2734 rb_hash_rehash(map);
2735 freeze_hide_obj(map);
2737 generated_iseq[code_index + 1 + j] = map;
2738 ISEQ_MBITS_SET(mark_offset_bits, code_index + 1 + j);
2739 RB_OBJ_WRITTEN(iseq, Qundef, map);
2740 needs_bitmap = true;
2741 break;
2742 }
2743 case TS_LINDEX:
2744 case TS_NUM: /* ulong */
2745 generated_iseq[code_index + 1 + j] = FIX2INT(operands[j]);
2746 break;
2747 case TS_ISEQ: /* iseq */
2748 case TS_VALUE: /* VALUE */
2749 {
2750 VALUE v = operands[j];
2751 generated_iseq[code_index + 1 + j] = v;
2752 /* to mark ruby object */
2753 if (!SPECIAL_CONST_P(v)) {
2754 RB_OBJ_WRITTEN(iseq, Qundef, v);
2755 ISEQ_MBITS_SET(mark_offset_bits, code_index + 1 + j);
2756 needs_bitmap = true;
2757 }
2758 break;
2759 }
2760 /* [ TS_IVC | TS_ICVARC | TS_ISE | TS_IC ] */
2761 case TS_IC: /* inline cache: constants */
2762 {
2763 unsigned int ic_index = ISEQ_COMPILE_DATA(iseq)->ic_index++;
2764 IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache;
2765 if (UNLIKELY(ic_index >= body->ic_size)) {
2766 BADINSN_DUMP(anchor, &iobj->link, 0);
2767 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2768 "iseq_set_sequence: ic_index overflow: index: %d, size: %d",
2769 ic_index, ISEQ_IS_SIZE(body));
2770 }
2771
2772 ic->segments = array_to_idlist(operands[j]);
2773
2774 generated_iseq[code_index + 1 + j] = (VALUE)ic;
2775 }
2776 break;
2777 case TS_IVC: /* inline ivar cache */
2778 {
2779 unsigned int ic_index = FIX2UINT(operands[j]);
2780
2781 IVC cache = ((IVC)&body->is_entries[ic_index]);
2782
2783 if (insn == BIN(setinstancevariable)) {
2784 cache->iv_set_name = SYM2ID(operands[j - 1]);
2785 }
2786 else {
2787 cache->iv_set_name = 0;
2788 }
2789
2790 vm_ic_attr_index_initialize(cache, INVALID_SHAPE_ID);
2791 }
2792 case TS_ISE: /* inline storage entry: `once` insn */
2793 case TS_ICVARC: /* inline cvar cache */
2794 {
2795 unsigned int ic_index = FIX2UINT(operands[j]);
2796 IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache;
2797 if (UNLIKELY(ic_index >= ISEQ_IS_SIZE(body))) {
2798 BADINSN_DUMP(anchor, &iobj->link, 0);
2799 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2800 "iseq_set_sequence: ic_index overflow: index: %d, size: %d",
2801 ic_index, ISEQ_IS_SIZE(body));
2802 }
2803 generated_iseq[code_index + 1 + j] = (VALUE)ic;
2804
2805 break;
2806 }
2807 case TS_CALLDATA:
2808 {
2809 const struct rb_callinfo *source_ci = (const struct rb_callinfo *)operands[j];
2810 RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq)->ci_index <= body->ci_size);
2811 struct rb_call_data *cd = &body->call_data[ISEQ_COMPILE_DATA(iseq)->ci_index++];
2812 cd->ci = source_ci;
2813 cd->cc = vm_cc_empty();
2814 generated_iseq[code_index + 1 + j] = (VALUE)cd;
2815 break;
2816 }
2817 case TS_ID: /* ID */
2818 generated_iseq[code_index + 1 + j] = SYM2ID(operands[j]);
2819 break;
2820 case TS_FUNCPTR:
2821 generated_iseq[code_index + 1 + j] = operands[j];
2822 break;
2823 case TS_BUILTIN:
2824 generated_iseq[code_index + 1 + j] = operands[j];
2825 break;
2826 default:
2827 BADINSN_ERROR(iseq, iobj->insn_info.line_no,
2828 "unknown operand type: %c", type);
2829 return COMPILE_NG;
2830 }
2831 }
2832 if (add_insn_info(insns_info, positions, insns_info_index, code_index, iobj)) insns_info_index++;
2833 code_index += len;
2834 break;
2835 }
2836 case ISEQ_ELEMENT_LABEL:
2837 {
2838 LABEL *lobj = (LABEL *)list;
2839 if (lobj->sp != sp) {
2840 debugs("%s: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
2841 RSTRING_PTR(rb_iseq_path(iseq)),
2842 lobj->label_no, lobj->sp, sp);
2843 }
2844 sp = lobj->sp;
2845 break;
2846 }
2847 case ISEQ_ELEMENT_ADJUST:
2848 {
2849 ADJUST *adjust = (ADJUST *)list;
2850 int orig_sp = sp;
2851
2852 if (adjust->label) {
2853 sp = adjust->label->sp;
2854 }
2855 else {
2856 sp = 0;
2857 }
2858
2859 if (adjust->line_no != -1) {
2860 const int diff = orig_sp - sp;
2861 if (diff > 0) {
2862 if (insns_info_index == 0) {
2863 COMPILE_ERROR(iseq, adjust->line_no,
2864 "iseq_set_sequence: adjust bug (ISEQ_ELEMENT_ADJUST must not be the first in iseq)");
2865 }
2866 if (add_adjust_info(insns_info, positions, insns_info_index, code_index, adjust)) insns_info_index++;
2867 }
2868 if (diff > 1) {
2869 generated_iseq[code_index++] = BIN(adjuststack);
2870 generated_iseq[code_index++] = orig_sp - sp;
2871 }
2872 else if (diff == 1) {
2873 generated_iseq[code_index++] = BIN(pop);
2874 }
2875 else if (diff < 0) {
2876 int label_no = adjust->label ? adjust->label->label_no : -1;
2877 SIZED_FREE_N(generated_iseq, generated_iseq_size);
2878 SIZED_FREE_N(insns_info, insns_info_size);
2879 SIZED_FREE_N(positions, positions_size);
2880 if (ISEQ_MBITS_BUFLEN(code_size) > 1) {
2881 SIZED_FREE_N(mark_offset_bits, ISEQ_MBITS_BUFLEN(code_index));
2882 }
2883 debug_list(anchor, list);
2884 COMPILE_ERROR(iseq, adjust->line_no,
2885 "iseq_set_sequence: adjust bug to %d %d < %d",
2886 label_no, orig_sp, sp);
2887 return COMPILE_NG;
2888 }
2889 }
2890 break;
2891 }
2892 default:
2893 /* ignore */
2894 break;
2895 }
2896 list = list->next;
2897 }
2898
2899 body->iseq_encoded = (void *)generated_iseq;
2900 body->iseq_size = code_index;
2901 body->stack_max = stack_max;
2902
2903 if (ISEQ_COMPILE_DATA(iseq)->is_single_mark_bit) {
2904 body->mark_bits.single = ISEQ_COMPILE_DATA(iseq)->mark_bits.single;
2905 }
2906 else {
2907 if (needs_bitmap) {
2908 body->mark_bits.list = mark_offset_bits;
2909 }
2910 else {
2911 body->mark_bits.list = NULL;
2912 ISEQ_COMPILE_DATA(iseq)->mark_bits.list = NULL;
2913 SIZED_FREE_N(mark_offset_bits, mark_offset_bits_size);
2914 }
2915 }
2916
2917 /* get rid of memory leak when REALLOC failed */
2918 body->insns_info.body = insns_info;
2919 body->insns_info.positions = positions;
2920
2921 SIZED_REALLOC_N(insns_info, struct iseq_insn_info_entry, insns_info_index, insns_info_size);
2922 body->insns_info.body = insns_info;
2923 SIZED_REALLOC_N(positions, unsigned int, insns_info_index, positions_size);
2924 body->insns_info.positions = positions;
2925 body->insns_info.size = insns_info_index;
2926
2927 return COMPILE_OK;
2928}
2929
2930static int
2931label_get_position(LABEL *lobj)
2932{
2933 return lobj->position;
2934}
2935
2936static int
2937label_get_sp(LABEL *lobj)
2938{
2939 return lobj->sp;
2940}
2941
2942static int
2943iseq_set_exception_table(rb_iseq_t *iseq)
2944{
2945 const VALUE *tptr, *ptr;
2946 unsigned int tlen, i;
2947 struct iseq_catch_table_entry *entry;
2948
2949 ISEQ_BODY(iseq)->catch_table = NULL;
2950
2951 VALUE catch_table_ary = ISEQ_COMPILE_DATA(iseq)->catch_table_ary;
2952 if (NIL_P(catch_table_ary)) return COMPILE_OK;
2953 tlen = (int)RARRAY_LEN(catch_table_ary);
2954 tptr = RARRAY_CONST_PTR(catch_table_ary);
2955
2956 if (tlen > 0) {
2957 struct iseq_catch_table *table = xmalloc(iseq_catch_table_bytes(tlen));
2958 table->size = tlen;
2959
2960 for (i = 0; i < table->size; i++) {
2961 int pos;
2962 ptr = RARRAY_CONST_PTR(tptr[i]);
2963 entry = UNALIGNED_MEMBER_PTR(table, entries[i]);
2964 entry->type = (enum rb_catch_type)(ptr[0] & 0xffff);
2965 pos = label_get_position((LABEL *)(ptr[1] & ~1));
2966 RUBY_ASSERT(pos >= 0);
2967 entry->start = (unsigned int)pos;
2968 pos = label_get_position((LABEL *)(ptr[2] & ~1));
2969 RUBY_ASSERT(pos >= 0);
2970 entry->end = (unsigned int)pos;
2971 entry->iseq = (rb_iseq_t *)ptr[3];
2972 RB_OBJ_WRITTEN(iseq, Qundef, entry->iseq);
2973
2974 /* stack depth */
2975 if (ptr[4]) {
2976 LABEL *lobj = (LABEL *)(ptr[4] & ~1);
2977 entry->cont = label_get_position(lobj);
2978 entry->sp = label_get_sp(lobj);
2979
2980 /* TODO: Dirty Hack! Fix me */
2981 if (entry->type == CATCH_TYPE_RESCUE ||
2982 entry->type == CATCH_TYPE_BREAK ||
2983 entry->type == CATCH_TYPE_NEXT) {
2984 RUBY_ASSERT(entry->sp > 0);
2985 entry->sp--;
2986 }
2987 }
2988 else {
2989 entry->cont = 0;
2990 }
2991 }
2992 ISEQ_BODY(iseq)->catch_table = table;
2993 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, 0); /* free */
2994 }
2995
2996 RB_GC_GUARD(catch_table_ary);
2997
2998 return COMPILE_OK;
2999}
3000
3001/*
3002 * set optional argument table
3003 * def foo(a, b=expr1, c=expr2)
3004 * =>
3005 * b:
3006 * expr1
3007 * c:
3008 * expr2
3009 */
3010static int
3011iseq_set_optargs_table(rb_iseq_t *iseq)
3012{
3013 int i;
3014 VALUE *opt_table = (VALUE *)ISEQ_BODY(iseq)->param.opt_table;
3015
3016 if (ISEQ_BODY(iseq)->param.flags.has_opt) {
3017 for (i = 0; i < ISEQ_BODY(iseq)->param.opt_num + 1; i++) {
3018 opt_table[i] = label_get_position((LABEL *)opt_table[i]);
3019 }
3020 }
3021 return COMPILE_OK;
3022}
3023
3024static LINK_ELEMENT *
3025get_destination_insn(INSN *iobj)
3026{
3027 LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
3028 LINK_ELEMENT *list;
3029 rb_event_flag_t events = 0;
3030
3031 list = lobj->link.next;
3032 while (list) {
3033 switch (list->type) {
3034 case ISEQ_ELEMENT_INSN:
3035 case ISEQ_ELEMENT_ADJUST:
3036 goto found;
3037 case ISEQ_ELEMENT_LABEL:
3038 /* ignore */
3039 break;
3040 case ISEQ_ELEMENT_TRACE:
3041 {
3042 TRACE *trace = (TRACE *)list;
3043 events |= trace->event;
3044 }
3045 break;
3046 default: break;
3047 }
3048 list = list->next;
3049 }
3050 found:
3051 if (list && IS_INSN(list)) {
3052 INSN *iobj = (INSN *)list;
3053 iobj->insn_info.events |= events;
3054 }
3055 return list;
3056}
3057
3058static LINK_ELEMENT *
3059get_next_insn(INSN *iobj)
3060{
3061 LINK_ELEMENT *list = iobj->link.next;
3062
3063 while (list) {
3064 if (IS_INSN(list) || IS_ADJUST(list)) {
3065 return list;
3066 }
3067 list = list->next;
3068 }
3069 return 0;
3070}
3071
3072static LINK_ELEMENT *
3073get_prev_insn(INSN *iobj)
3074{
3075 LINK_ELEMENT *list = iobj->link.prev;
3076
3077 while (list) {
3078 if (IS_INSN(list) || IS_ADJUST(list)) {
3079 return list;
3080 }
3081 list = list->prev;
3082 }
3083 return 0;
3084}
3085
3086static void
3087unref_destination(INSN *iobj, int pos)
3088{
3089 LABEL *lobj = (LABEL *)OPERAND_AT(iobj, pos);
3090 --lobj->refcnt;
3091 if (!lobj->refcnt) ELEM_REMOVE(&lobj->link);
3092}
3093
3094static bool
3095replace_destination(INSN *dobj, INSN *nobj)
3096{
3097 VALUE n = OPERAND_AT(nobj, 0);
3098 LABEL *dl = (LABEL *)OPERAND_AT(dobj, 0);
3099 LABEL *nl = (LABEL *)n;
3100 if (dl == nl) return false;
3101 --dl->refcnt;
3102 ++nl->refcnt;
3103 OPERAND_AT(dobj, 0) = n;
3104 if (!dl->refcnt) ELEM_REMOVE(&dl->link);
3105 return true;
3106}
3107
3108static LABEL*
3109find_destination(INSN *i)
3110{
3111 int pos, len = insn_len(i->insn_id);
3112 for (pos = 0; pos < len; ++pos) {
3113 if (insn_op_types(i->insn_id)[pos] == TS_OFFSET) {
3114 return (LABEL *)OPERAND_AT(i, pos);
3115 }
3116 }
3117 return 0;
3118}
3119
3120static int
3121remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i)
3122{
3123 LINK_ELEMENT *first = i, *end;
3124 int *unref_counts = 0, nlabels = ISEQ_COMPILE_DATA(iseq)->label_no;
3125
3126 if (!i) return 0;
3127 unref_counts = ALLOCA_N(int, nlabels);
3128 MEMZERO(unref_counts, int, nlabels);
3129 end = i;
3130 do {
3131 LABEL *lab;
3132 if (IS_INSN(i)) {
3133 if (IS_INSN_ID(i, leave)) {
3134 end = i;
3135 break;
3136 }
3137 else if ((lab = find_destination((INSN *)i)) != 0) {
3138 unref_counts[lab->label_no]++;
3139 }
3140 }
3141 else if (IS_LABEL(i)) {
3142 lab = (LABEL *)i;
3143 if (lab->unremovable) return 0;
3144 if (lab->refcnt > unref_counts[lab->label_no]) {
3145 if (i == first) return 0;
3146 break;
3147 }
3148 continue;
3149 }
3150 else if (IS_TRACE(i)) {
3151 /* do nothing */
3152 }
3153 else if (IS_ADJUST(i)) {
3154 return 0;
3155 }
3156 end = i;
3157 } while ((i = i->next) != 0);
3158 i = first;
3159 do {
3160 if (IS_INSN(i)) {
3161 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
3162 VALUE insn = INSN_OF(i);
3163 int pos, len = insn_len(insn);
3164 for (pos = 0; pos < len; ++pos) {
3165 switch (insn_op_types(insn)[pos]) {
3166 case TS_OFFSET:
3167 unref_destination((INSN *)i, pos);
3168 break;
3169 case TS_CALLDATA:
3170 --(body->ci_size);
3171 break;
3172 }
3173 }
3174 }
3175 ELEM_REMOVE(i);
3176 } while ((i != end) && (i = i->next) != 0);
3177 return 1;
3178}
3179
3180static int
3181iseq_pop_newarray(rb_iseq_t *iseq, INSN *iobj)
3182{
3183 switch (OPERAND_AT(iobj, 0)) {
3184 case INT2FIX(0): /* empty array */
3185 ELEM_REMOVE(&iobj->link);
3186 return TRUE;
3187 case INT2FIX(1): /* single element array */
3188 ELEM_REMOVE(&iobj->link);
3189 return FALSE;
3190 default:
3191 iobj->insn_id = BIN(adjuststack);
3192 return TRUE;
3193 }
3194}
3195
3196static int
3197is_frozen_putstring(INSN *insn, VALUE *op)
3198{
3199 if (IS_INSN_ID(insn, putstring) || IS_INSN_ID(insn, putchilledstring)) {
3200 *op = OPERAND_AT(insn, 0);
3201 return 1;
3202 }
3203 else if (IS_INSN_ID(insn, putobject)) { /* frozen_string_literal */
3204 *op = OPERAND_AT(insn, 0);
3205 return RB_TYPE_P(*op, T_STRING);
3206 }
3207 return 0;
3208}
3209
3210static int
3211insn_has_label_before(LINK_ELEMENT *elem)
3212{
3213 LINK_ELEMENT *prev = elem->prev;
3214 while (prev) {
3215 if (prev->type == ISEQ_ELEMENT_LABEL) {
3216 LABEL *label = (LABEL *)prev;
3217 if (label->refcnt > 0) {
3218 return 1;
3219 }
3220 }
3221 else if (prev->type == ISEQ_ELEMENT_INSN) {
3222 break;
3223 }
3224 prev = prev->prev;
3225 }
3226 return 0;
3227}
3228
3229static int
3230optimize_checktype(rb_iseq_t *iseq, INSN *iobj)
3231{
3232 /*
3233 * putobject obj
3234 * dup
3235 * checktype T_XXX
3236 * branchif l1
3237 * l2:
3238 * ...
3239 * l1:
3240 *
3241 * => obj is a T_XXX
3242 *
3243 * putobject obj (T_XXX)
3244 * jump L1
3245 * L1:
3246 *
3247 * => obj is not a T_XXX
3248 *
3249 * putobject obj (T_XXX)
3250 * jump L2
3251 * L2:
3252 */
3253 int line, node_id;
3254 INSN *niobj, *ciobj, *dup = 0;
3255 LABEL *dest = 0;
3256 VALUE type;
3257
3258 switch (INSN_OF(iobj)) {
3259 case BIN(putstring):
3260 case BIN(putchilledstring):
3262 break;
3263 case BIN(putnil):
3264 type = INT2FIX(T_NIL);
3265 break;
3266 case BIN(putobject):
3267 type = INT2FIX(TYPE(OPERAND_AT(iobj, 0)));
3268 break;
3269 default: return FALSE;
3270 }
3271
3272 ciobj = (INSN *)get_next_insn(iobj);
3273 if (IS_INSN_ID(ciobj, jump)) {
3274 ciobj = (INSN *)get_next_insn((INSN*)OPERAND_AT(ciobj, 0));
3275 }
3276 if (IS_INSN_ID(ciobj, dup)) {
3277 ciobj = (INSN *)get_next_insn(dup = ciobj);
3278 }
3279 if (!ciobj || !IS_INSN_ID(ciobj, checktype)) return FALSE;
3280 niobj = (INSN *)get_next_insn(ciobj);
3281 if (!niobj) {
3282 /* TODO: putobject true/false */
3283 return FALSE;
3284 }
3285 switch (INSN_OF(niobj)) {
3286 case BIN(branchif):
3287 if (OPERAND_AT(ciobj, 0) == type) {
3288 dest = (LABEL *)OPERAND_AT(niobj, 0);
3289 }
3290 break;
3291 case BIN(branchunless):
3292 if (OPERAND_AT(ciobj, 0) != type) {
3293 dest = (LABEL *)OPERAND_AT(niobj, 0);
3294 }
3295 break;
3296 default:
3297 return FALSE;
3298 }
3299 line = ciobj->insn_info.line_no;
3300 node_id = ciobj->insn_info.node_id;
3301 if (!dest) {
3302 if (niobj->link.next && IS_LABEL(niobj->link.next)) {
3303 dest = (LABEL *)niobj->link.next; /* reuse label */
3304 }
3305 else {
3306 dest = NEW_LABEL(line);
3307 ELEM_INSERT_NEXT(&niobj->link, &dest->link);
3308 }
3309 }
3310 INSERT_AFTER_INSN1(iobj, line, node_id, jump, dest);
3311 LABEL_REF(dest);
3312 if (!dup) INSERT_AFTER_INSN(iobj, line, node_id, pop);
3313 return TRUE;
3314}
3315
3316static const struct rb_callinfo *
3317ci_flag_set(const rb_iseq_t *iseq, const struct rb_callinfo *ci, unsigned int add)
3318{
3319 const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci),
3320 vm_ci_flag(ci) | add,
3321 vm_ci_argc(ci),
3322 vm_ci_kwarg(ci));
3323 RB_OBJ_WRITTEN(iseq, ci, nci);
3324 return nci;
3325}
3326
3327static const struct rb_callinfo *
3328ci_argc_set(const rb_iseq_t *iseq, const struct rb_callinfo *ci, int argc)
3329{
3330 const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci),
3331 vm_ci_flag(ci),
3332 argc,
3333 vm_ci_kwarg(ci));
3334 RB_OBJ_WRITTEN(iseq, ci, nci);
3335 return nci;
3336}
3337
3338#define vm_ci_simple(ci) (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE)
3339
3340static int
3341iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt)
3342{
3343 INSN *const iobj = (INSN *)list;
3344
3345 again:
3346 optimize_checktype(iseq, iobj);
3347
3348 if (IS_INSN_ID(iobj, jump)) {
3349 INSN *niobj, *diobj, *piobj;
3350 diobj = (INSN *)get_destination_insn(iobj);
3351 niobj = (INSN *)get_next_insn(iobj);
3352
3353 if (diobj == niobj) {
3354 /*
3355 * jump LABEL
3356 * LABEL:
3357 * =>
3358 * LABEL:
3359 */
3360 unref_destination(iobj, 0);
3361 ELEM_REMOVE(&iobj->link);
3362 return COMPILE_OK;
3363 }
3364 else if (iobj != diobj && IS_INSN(&diobj->link) &&
3365 IS_INSN_ID(diobj, jump) &&
3366 OPERAND_AT(iobj, 0) != OPERAND_AT(diobj, 0) &&
3367 diobj->insn_info.events == 0) {
3368 /*
3369 * useless jump elimination:
3370 * jump LABEL1
3371 * ...
3372 * LABEL1:
3373 * jump LABEL2
3374 *
3375 * => in this case, first jump instruction should jump to
3376 * LABEL2 directly
3377 */
3378 if (replace_destination(iobj, diobj)) {
3379 remove_unreachable_chunk(iseq, iobj->link.next);
3380 goto again;
3381 }
3382 }
3383 else if (IS_INSN_ID(diobj, leave)) {
3384 /*
3385 * jump LABEL
3386 * ...
3387 * LABEL:
3388 * leave
3389 * =>
3390 * leave
3391 * ...
3392 * LABEL:
3393 * leave
3394 */
3395 /* replace */
3396 unref_destination(iobj, 0);
3397 iobj->insn_id = BIN(leave);
3398 iobj->operand_size = 0;
3399 iobj->insn_info = diobj->insn_info;
3400 goto again;
3401 }
3402 else if (IS_INSN(iobj->link.prev) &&
3403 (piobj = (INSN *)iobj->link.prev) &&
3404 (IS_INSN_ID(piobj, branchif) ||
3405 IS_INSN_ID(piobj, branchunless))) {
3406 INSN *pdiobj = (INSN *)get_destination_insn(piobj);
3407 if (niobj == pdiobj) {
3408 int refcnt = IS_LABEL(piobj->link.next) ?
3409 ((LABEL *)piobj->link.next)->refcnt : 0;
3410 /*
3411 * useless jump elimination (if/unless destination):
3412 * if L1
3413 * jump L2
3414 * L1:
3415 * ...
3416 * L2:
3417 *
3418 * ==>
3419 * unless L2
3420 * L1:
3421 * ...
3422 * L2:
3423 */
3424 piobj->insn_id = (IS_INSN_ID(piobj, branchif))
3425 ? BIN(branchunless) : BIN(branchif);
3426 if (replace_destination(piobj, iobj) && refcnt <= 1) {
3427 ELEM_REMOVE(&iobj->link);
3428 }
3429 else {
3430 /* TODO: replace other branch destinations too */
3431 }
3432 return COMPILE_OK;
3433 }
3434 else if (diobj == pdiobj) {
3435 /*
3436 * useless jump elimination (if/unless before jump):
3437 * L1:
3438 * ...
3439 * if L1
3440 * jump L1
3441 *
3442 * ==>
3443 * L1:
3444 * ...
3445 * pop
3446 * jump L1
3447 */
3448 INSN *popiobj = new_insn_core(iseq, iobj->insn_info.line_no, iobj->insn_info.node_id, BIN(pop), 0, 0);
3449 ELEM_REPLACE(&piobj->link, &popiobj->link);
3450 }
3451 }
3452 if (remove_unreachable_chunk(iseq, iobj->link.next)) {
3453 goto again;
3454 }
3455 }
3456
3457 /*
3458 * putstring "beg"
3459 * putstring "end"
3460 * newrange excl
3461 *
3462 * ==>
3463 *
3464 * putobject "beg".."end"
3465 */
3466 if (IS_INSN_ID(iobj, newrange)) {
3467 INSN *const range = iobj;
3468 INSN *beg, *end;
3469 VALUE str_beg, str_end;
3470
3471 if ((end = (INSN *)get_prev_insn(range)) != 0 &&
3472 is_frozen_putstring(end, &str_end) &&
3473 (beg = (INSN *)get_prev_insn(end)) != 0 &&
3474 is_frozen_putstring(beg, &str_beg) &&
3475 !(insn_has_label_before(&beg->link) || insn_has_label_before(&end->link))) {
3476 int excl = FIX2INT(OPERAND_AT(range, 0));
3477 VALUE lit_range = RB_OBJ_SET_SHAREABLE(rb_range_new(str_beg, str_end, excl));
3478
3479 ELEM_REMOVE(&beg->link);
3480 ELEM_REMOVE(&end->link);
3481 range->insn_id = BIN(putobject);
3482 OPERAND_AT(range, 0) = lit_range;
3483 RB_OBJ_WRITTEN(iseq, Qundef, lit_range);
3484 }
3485 }
3486
3487 if (IS_INSN_ID(iobj, leave)) {
3488 remove_unreachable_chunk(iseq, iobj->link.next);
3489 }
3490
3491 /*
3492 * ...
3493 * duparray [...]
3494 * concatarray | concattoarray
3495 * =>
3496 * ...
3497 * putobject [...]
3498 * concatarray | concattoarray
3499 */
3500 if (IS_INSN_ID(iobj, duparray)) {
3501 LINK_ELEMENT *next = iobj->link.next;
3502 if (IS_INSN(next) && (IS_INSN_ID(next, concatarray) || IS_INSN_ID(next, concattoarray))) {
3503 iobj->insn_id = BIN(putobject);
3504 }
3505 }
3506
3507 /*
3508 * duparray [...]
3509 * send <calldata!mid:freeze, argc:0, ARGS_SIMPLE>, nil
3510 * =>
3511 * opt_ary_freeze [...], <calldata!mid:freeze, argc:0, ARGS_SIMPLE>
3512 */
3513 if (IS_INSN_ID(iobj, duparray)) {
3514 LINK_ELEMENT *next = iobj->link.next;
3515 if (IS_INSN(next) && (IS_INSN_ID(next, send))) {
3516 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(next, 0);
3517 const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1);
3518
3519 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) {
3520 VALUE ary = iobj->operands[0];
3522
3523 insn_replace_with_operands(iseq, iobj, BIN(opt_ary_freeze), 2, ary, (VALUE)ci);
3524 ELEM_REMOVE(next);
3525 }
3526 }
3527 }
3528
3529 /*
3530 * duphash {...}
3531 * send <calldata!mid:freeze, argc:0, ARGS_SIMPLE>, nil
3532 * =>
3533 * opt_hash_freeze {...}, <calldata!mid:freeze, argc:0, ARGS_SIMPLE>
3534 */
3535 if (IS_INSN_ID(iobj, duphash)) {
3536 LINK_ELEMENT *next = iobj->link.next;
3537 if (IS_INSN(next) && (IS_INSN_ID(next, send))) {
3538 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(next, 0);
3539 const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1);
3540
3541 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) {
3542 VALUE hash = iobj->operands[0];
3543 rb_obj_reveal(hash, rb_cHash);
3544 RB_OBJ_SET_SHAREABLE(hash);
3545
3546 insn_replace_with_operands(iseq, iobj, BIN(opt_hash_freeze), 2, hash, (VALUE)ci);
3547 ELEM_REMOVE(next);
3548 }
3549 }
3550 }
3551
3552 /*
3553 * newarray 0
3554 * send <calldata!mid:freeze, argc:0, ARGS_SIMPLE>, nil
3555 * =>
3556 * opt_ary_freeze [], <calldata!mid:freeze, argc:0, ARGS_SIMPLE>
3557 */
3558 if (IS_INSN_ID(iobj, newarray) && iobj->operands[0] == INT2FIX(0)) {
3559 LINK_ELEMENT *next = iobj->link.next;
3560 if (IS_INSN(next) && (IS_INSN_ID(next, send))) {
3561 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(next, 0);
3562 const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1);
3563
3564 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) {
3565 insn_replace_with_operands(iseq, iobj, BIN(opt_ary_freeze), 2, rb_cArray_empty_frozen, (VALUE)ci);
3566 ELEM_REMOVE(next);
3567 }
3568 }
3569 }
3570
3571 /*
3572 * newhash 0
3573 * send <calldata!mid:freeze, argc:0, ARGS_SIMPLE>, nil
3574 * =>
3575 * opt_hash_freeze {}, <calldata!mid:freeze, argc:0, ARGS_SIMPLE>
3576 */
3577 if (IS_INSN_ID(iobj, newhash) && iobj->operands[0] == INT2FIX(0)) {
3578 LINK_ELEMENT *next = iobj->link.next;
3579 if (IS_INSN(next) && (IS_INSN_ID(next, send))) {
3580 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(next, 0);
3581 const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1);
3582
3583 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) {
3584 insn_replace_with_operands(iseq, iobj, BIN(opt_hash_freeze), 2, rb_cHash_empty_frozen, (VALUE)ci);
3585 ELEM_REMOVE(next);
3586 }
3587 }
3588 }
3589
3590 if (IS_INSN_ID(iobj, branchif) ||
3591 IS_INSN_ID(iobj, branchnil) ||
3592 IS_INSN_ID(iobj, branchunless)) {
3593 /*
3594 * if L1
3595 * ...
3596 * L1:
3597 * jump L2
3598 * =>
3599 * if L2
3600 */
3601 INSN *nobj = (INSN *)get_destination_insn(iobj);
3602
3603 /* This is super nasty hack!!!
3604 *
3605 * This jump-jump optimization may ignore event flags of the jump
3606 * instruction being skipped. Actually, Line 2 TracePoint event
3607 * is never fired in the following code:
3608 *
3609 * 1: raise if 1 == 2
3610 * 2: while true
3611 * 3: break
3612 * 4: end
3613 *
3614 * This is critical for coverage measurement. [Bug #15980]
3615 *
3616 * This is a stopgap measure: stop the jump-jump optimization if
3617 * coverage measurement is enabled and if the skipped instruction
3618 * has any event flag.
3619 *
3620 * Note that, still, TracePoint Line event does not occur on Line 2.
3621 * This should be fixed in future.
3622 */
3623 int stop_optimization =
3624 ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq) &&
3625 nobj->link.type == ISEQ_ELEMENT_INSN &&
3626 nobj->insn_info.events;
3627 if (!stop_optimization) {
3628 INSN *pobj = (INSN *)iobj->link.prev;
3629 int prev_dup = 0;
3630 if (pobj) {
3631 if (!IS_INSN(&pobj->link))
3632 pobj = 0;
3633 else if (IS_INSN_ID(pobj, dup))
3634 prev_dup = 1;
3635 }
3636
3637 for (;;) {
3638 if (IS_INSN(&nobj->link) && IS_INSN_ID(nobj, jump)) {
3639 if (!replace_destination(iobj, nobj)) break;
3640 }
3641 else if (prev_dup && IS_INSN_ID(nobj, dup) &&
3642 !!(nobj = (INSN *)nobj->link.next) &&
3643 /* basic blocks, with no labels in the middle */
3644 nobj->insn_id == iobj->insn_id) {
3645 /*
3646 * dup
3647 * if L1
3648 * ...
3649 * L1:
3650 * dup
3651 * if L2
3652 * =>
3653 * dup
3654 * if L2
3655 * ...
3656 * L1:
3657 * dup
3658 * if L2
3659 */
3660 if (!replace_destination(iobj, nobj)) break;
3661 }
3662 else if (pobj) {
3663 /*
3664 * putnil
3665 * if L1
3666 * =>
3667 * # nothing
3668 *
3669 * putobject true
3670 * if L1
3671 * =>
3672 * jump L1
3673 *
3674 * putstring ".."
3675 * if L1
3676 * =>
3677 * jump L1
3678 *
3679 * putstring ".."
3680 * dup
3681 * if L1
3682 * =>
3683 * putstring ".."
3684 * jump L1
3685 *
3686 */
3687 int cond;
3688 if (prev_dup && IS_INSN(pobj->link.prev)) {
3689 pobj = (INSN *)pobj->link.prev;
3690 }
3691 if (IS_INSN_ID(pobj, putobject)) {
3692 cond = (IS_INSN_ID(iobj, branchif) ?
3693 OPERAND_AT(pobj, 0) != Qfalse :
3694 IS_INSN_ID(iobj, branchunless) ?
3695 OPERAND_AT(pobj, 0) == Qfalse :
3696 FALSE);
3697 }
3698 else if (IS_INSN_ID(pobj, putstring) ||
3699 IS_INSN_ID(pobj, duparray) ||
3700 IS_INSN_ID(pobj, newarray)) {
3701 cond = IS_INSN_ID(iobj, branchif);
3702 }
3703 else if (IS_INSN_ID(pobj, putnil)) {
3704 cond = !IS_INSN_ID(iobj, branchif);
3705 }
3706 else break;
3707 if (prev_dup || !IS_INSN_ID(pobj, newarray)) {
3708 ELEM_REMOVE(iobj->link.prev);
3709 }
3710 else if (!iseq_pop_newarray(iseq, pobj)) {
3711 pobj = new_insn_core(iseq, pobj->insn_info.line_no, pobj->insn_info.node_id, BIN(pop), 0, NULL);
3712 ELEM_INSERT_PREV(&iobj->link, &pobj->link);
3713 }
3714 if (cond) {
3715 if (prev_dup) {
3716 pobj = new_insn_core(iseq, pobj->insn_info.line_no, pobj->insn_info.node_id, BIN(putnil), 0, NULL);
3717 ELEM_INSERT_NEXT(&iobj->link, &pobj->link);
3718 }
3719 iobj->insn_id = BIN(jump);
3720 goto again;
3721 }
3722 else {
3723 unref_destination(iobj, 0);
3724 ELEM_REMOVE(&iobj->link);
3725 }
3726 break;
3727 }
3728 else break;
3729 nobj = (INSN *)get_destination_insn(nobj);
3730 }
3731 }
3732 }
3733
3734 if (IS_INSN_ID(iobj, pop)) {
3735 /*
3736 * putself / putnil / putobject obj / putstring "..."
3737 * pop
3738 * =>
3739 * # do nothing
3740 */
3741 LINK_ELEMENT *prev = iobj->link.prev;
3742 if (IS_INSN(prev)) {
3743 enum ruby_vminsn_type previ = ((INSN *)prev)->insn_id;
3744 if (previ == BIN(putobject) || previ == BIN(putnil) ||
3745 previ == BIN(putself) || previ == BIN(putstring) ||
3746 previ == BIN(putchilledstring) ||
3747 previ == BIN(dup) ||
3748 previ == BIN(getlocal) ||
3749 previ == BIN(getblockparam) ||
3750 previ == BIN(getblockparamproxy) ||
3751 previ == BIN(getinstancevariable) ||
3752 previ == BIN(duparray)) {
3753 /* just push operand or static value and pop soon, no
3754 * side effects */
3755 ELEM_REMOVE(prev);
3756 ELEM_REMOVE(&iobj->link);
3757 }
3758 else if (previ == BIN(newarray) && iseq_pop_newarray(iseq, (INSN*)prev)) {
3759 ELEM_REMOVE(&iobj->link);
3760 }
3761 else if (previ == BIN(concatarray)) {
3762 INSN *piobj = (INSN *)prev;
3763 INSERT_BEFORE_INSN1(piobj, piobj->insn_info.line_no, piobj->insn_info.node_id, splatarray, Qfalse);
3764 INSN_OF(piobj) = BIN(pop);
3765 }
3766 else if (previ == BIN(concatstrings)) {
3767 if (OPERAND_AT(prev, 0) == INT2FIX(1)) {
3768 ELEM_REMOVE(prev);
3769 }
3770 else {
3771 ELEM_REMOVE(&iobj->link);
3772 INSN_OF(prev) = BIN(adjuststack);
3773 }
3774 }
3775 }
3776 }
3777
3778 if (IS_INSN_ID(iobj, newarray) ||
3779 IS_INSN_ID(iobj, duparray) ||
3780 IS_INSN_ID(iobj, concatarray) ||
3781 IS_INSN_ID(iobj, splatarray) ||
3782 0) {
3783 /*
3784 * newarray N
3785 * splatarray
3786 * =>
3787 * newarray N
3788 * newarray always puts an array
3789 */
3790 LINK_ELEMENT *next = iobj->link.next;
3791 if (IS_INSN(next) && IS_INSN_ID(next, splatarray)) {
3792 /* remove splatarray following always-array insn */
3793 ELEM_REMOVE(next);
3794 }
3795 }
3796
3797 if (IS_INSN_ID(iobj, newarray)) {
3798 LINK_ELEMENT *next = iobj->link.next;
3799 if (IS_INSN(next) && IS_INSN_ID(next, expandarray) &&
3800 OPERAND_AT(next, 1) == INT2FIX(0)) {
3801 VALUE op1, op2;
3802 op1 = OPERAND_AT(iobj, 0);
3803 op2 = OPERAND_AT(next, 0);
3804 ELEM_REMOVE(next);
3805
3806 if (op1 == op2) {
3807 /*
3808 * newarray 2
3809 * expandarray 2, 0
3810 * =>
3811 * swap
3812 */
3813 if (op1 == INT2FIX(2)) {
3814 INSN_OF(iobj) = BIN(swap);
3815 iobj->operand_size = 0;
3816 }
3817 /*
3818 * newarray X
3819 * expandarray X, 0
3820 * =>
3821 * opt_reverse X
3822 */
3823 else {
3824 INSN_OF(iobj) = BIN(opt_reverse);
3825 }
3826 }
3827 else {
3828 long diff = FIX2LONG(op1) - FIX2LONG(op2);
3829 INSN_OF(iobj) = BIN(opt_reverse);
3830 OPERAND_AT(iobj, 0) = OPERAND_AT(next, 0);
3831
3832 if (op1 > op2) {
3833 /* X > Y
3834 * newarray X
3835 * expandarray Y, 0
3836 * =>
3837 * pop * (Y-X)
3838 * opt_reverse Y
3839 */
3840 for (; diff > 0; diff--) {
3841 INSERT_BEFORE_INSN(iobj, iobj->insn_info.line_no, iobj->insn_info.node_id, pop);
3842 }
3843 }
3844 else { /* (op1 < op2) */
3845 /* X < Y
3846 * newarray X
3847 * expandarray Y, 0
3848 * =>
3849 * putnil * (Y-X)
3850 * opt_reverse Y
3851 */
3852 for (; diff < 0; diff++) {
3853 INSERT_BEFORE_INSN(iobj, iobj->insn_info.line_no, iobj->insn_info.node_id, putnil);
3854 }
3855 }
3856 }
3857 }
3858 }
3859
3860 if (IS_INSN_ID(iobj, duparray)) {
3861 LINK_ELEMENT *next = iobj->link.next;
3862 /*
3863 * duparray obj
3864 * expandarray X, 0
3865 * =>
3866 * putobject obj
3867 * expandarray X, 0
3868 */
3869 if (IS_INSN(next) && IS_INSN_ID(next, expandarray)) {
3870 INSN_OF(iobj) = BIN(putobject);
3871 }
3872 }
3873
3874 if (IS_INSN_ID(iobj, anytostring)) {
3875 LINK_ELEMENT *next = iobj->link.next;
3876 /*
3877 * anytostring
3878 * concatstrings 1
3879 * =>
3880 * anytostring
3881 */
3882 if (IS_INSN(next) && IS_INSN_ID(next, concatstrings) &&
3883 OPERAND_AT(next, 0) == INT2FIX(1)) {
3884 ELEM_REMOVE(next);
3885 }
3886 }
3887
3888 if (IS_INSN_ID(iobj, putstring) || IS_INSN_ID(iobj, putchilledstring) ||
3889 (IS_INSN_ID(iobj, putobject) && RB_TYPE_P(OPERAND_AT(iobj, 0), T_STRING))) {
3890 /*
3891 * putstring ""
3892 * concatstrings N
3893 * =>
3894 * concatstrings N-1
3895 */
3896 if (IS_NEXT_INSN_ID(&iobj->link, concatstrings) &&
3897 RSTRING_LEN(OPERAND_AT(iobj, 0)) == 0) {
3898 INSN *next = (INSN *)iobj->link.next;
3899 if ((OPERAND_AT(next, 0) = FIXNUM_INC(OPERAND_AT(next, 0), -1)) == INT2FIX(1)) {
3900 ELEM_REMOVE(&next->link);
3901 }
3902 ELEM_REMOVE(&iobj->link);
3903 }
3904 if (IS_NEXT_INSN_ID(&iobj->link, toregexp)) {
3905 INSN *next = (INSN *)iobj->link.next;
3906 if (OPERAND_AT(next, 1) == INT2FIX(1)) {
3907 VALUE src = OPERAND_AT(iobj, 0);
3908 int opt = (int)FIX2LONG(OPERAND_AT(next, 0));
3909 VALUE path = rb_iseq_path(iseq);
3910 int line = iobj->insn_info.line_no;
3911 VALUE errinfo = rb_errinfo();
3912 VALUE re = rb_reg_compile(src, opt, RSTRING_PTR(path), line);
3913 if (NIL_P(re)) {
3914 VALUE message = rb_attr_get(rb_errinfo(), idMesg);
3915 rb_set_errinfo(errinfo);
3916 COMPILE_ERROR(iseq, line, "%" PRIsVALUE, message);
3917 }
3918 else {
3919 RB_OBJ_SET_SHAREABLE(re);
3920 }
3921 RB_OBJ_WRITE(iseq, &OPERAND_AT(iobj, 0), re);
3922 ELEM_REMOVE(iobj->link.next);
3923 }
3924 }
3925 }
3926
3927 if (IS_INSN_ID(iobj, concatstrings)) {
3928 /*
3929 * concatstrings N
3930 * concatstrings M
3931 * =>
3932 * concatstrings N+M-1
3933 */
3934 LINK_ELEMENT *next = iobj->link.next;
3935 INSN *jump = 0;
3936 if (IS_INSN(next) && IS_INSN_ID(next, jump))
3937 next = get_destination_insn(jump = (INSN *)next);
3938 if (IS_INSN(next) && IS_INSN_ID(next, concatstrings)) {
3939 int n = FIX2INT(OPERAND_AT(iobj, 0)) + FIX2INT(OPERAND_AT(next, 0)) - 1;
3940 OPERAND_AT(iobj, 0) = INT2FIX(n);
3941 if (jump) {
3942 LABEL *label = ((LABEL *)OPERAND_AT(jump, 0));
3943 if (!--label->refcnt) {
3944 ELEM_REMOVE(&label->link);
3945 }
3946 else {
3947 label = NEW_LABEL(0);
3948 OPERAND_AT(jump, 0) = (VALUE)label;
3949 }
3950 label->refcnt++;
3951 ELEM_INSERT_NEXT(next, &label->link);
3952 CHECK(iseq_peephole_optimize(iseq, get_next_insn(jump), do_tailcallopt));
3953 }
3954 else {
3955 ELEM_REMOVE(next);
3956 }
3957 }
3958 }
3959
3960 if (do_tailcallopt &&
3961 (IS_INSN_ID(iobj, send) ||
3962 IS_INSN_ID(iobj, invokesuper))) {
3963 /*
3964 * send ...
3965 * leave
3966 * =>
3967 * send ..., ... | VM_CALL_TAILCALL, ...
3968 * leave # unreachable
3969 */
3970 INSN *piobj = NULL;
3971 if (iobj->link.next) {
3972 LINK_ELEMENT *next = iobj->link.next;
3973 do {
3974 if (!IS_INSN(next)) {
3975 next = next->next;
3976 continue;
3977 }
3978 switch (INSN_OF(next)) {
3979 case BIN(nop):
3980 next = next->next;
3981 break;
3982 case BIN(jump):
3983 /* if cond
3984 * return tailcall
3985 * end
3986 */
3987 next = get_destination_insn((INSN *)next);
3988 break;
3989 case BIN(leave):
3990 piobj = iobj;
3991 /* fall through */
3992 default:
3993 next = NULL;
3994 break;
3995 }
3996 } while (next);
3997 }
3998
3999 if (piobj) {
4000 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(piobj, 0);
4001 if (IS_INSN_ID(piobj, send) ||
4002 IS_INSN_ID(piobj, invokesuper)) {
4003 if (OPERAND_AT(piobj, 1) == 0) { /* no blockiseq */
4004 ci = ci_flag_set(iseq, ci, VM_CALL_TAILCALL);
4005 OPERAND_AT(piobj, 0) = (VALUE)ci;
4006 RB_OBJ_WRITTEN(iseq, Qundef, ci);
4007 }
4008 }
4009 else {
4010 ci = ci_flag_set(iseq, ci, VM_CALL_TAILCALL);
4011 OPERAND_AT(piobj, 0) = (VALUE)ci;
4012 RB_OBJ_WRITTEN(iseq, Qundef, ci);
4013 }
4014 }
4015 }
4016
4017 if (IS_INSN_ID(iobj, dup)) {
4018 if (IS_NEXT_INSN_ID(&iobj->link, setlocal)) {
4019 LINK_ELEMENT *set1 = iobj->link.next, *set2 = NULL;
4020
4021 /*
4022 * dup
4023 * setlocal x, y
4024 * setlocal x, y
4025 * =>
4026 * dup
4027 * setlocal x, y
4028 */
4029 if (IS_NEXT_INSN_ID(set1, setlocal)) {
4030 set2 = set1->next;
4031 if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
4032 OPERAND_AT(set1, 1) == OPERAND_AT(set2, 1)) {
4033 ELEM_REMOVE(set1);
4034 ELEM_REMOVE(&iobj->link);
4035 }
4036 }
4037
4038 /*
4039 * dup
4040 * setlocal x, y
4041 * dup
4042 * setlocal x, y
4043 * =>
4044 * dup
4045 * setlocal x, y
4046 */
4047 else if (IS_NEXT_INSN_ID(set1, dup) &&
4048 IS_NEXT_INSN_ID(set1->next, setlocal)) {
4049 set2 = set1->next->next;
4050 if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
4051 OPERAND_AT(set1, 1) == OPERAND_AT(set2, 1)) {
4052 ELEM_REMOVE(set1->next);
4053 ELEM_REMOVE(set2);
4054 }
4055 }
4056 }
4057 }
4058
4059 /*
4060 * getlocal x, y
4061 * dup
4062 * setlocal x, y
4063 * =>
4064 * dup
4065 */
4066 if (IS_INSN_ID(iobj, getlocal)) {
4067 LINK_ELEMENT *niobj = &iobj->link;
4068 if (IS_NEXT_INSN_ID(niobj, dup)) {
4069 niobj = niobj->next;
4070 }
4071 if (IS_NEXT_INSN_ID(niobj, setlocal)) {
4072 LINK_ELEMENT *set1 = niobj->next;
4073 if (OPERAND_AT(iobj, 0) == OPERAND_AT(set1, 0) &&
4074 OPERAND_AT(iobj, 1) == OPERAND_AT(set1, 1)) {
4075 ELEM_REMOVE(set1);
4076 ELEM_REMOVE(niobj);
4077 }
4078 }
4079 }
4080
4081 /*
4082 * opt_invokebuiltin_delegate
4083 * trace
4084 * leave
4085 * =>
4086 * opt_invokebuiltin_delegate_leave
4087 * trace
4088 * leave
4089 */
4090 if (IS_INSN_ID(iobj, opt_invokebuiltin_delegate)) {
4091 if (IS_TRACE(iobj->link.next)) {
4092 if (IS_NEXT_INSN_ID(iobj->link.next, leave)) {
4093 iobj->insn_id = BIN(opt_invokebuiltin_delegate_leave);
4094 const struct rb_builtin_function *bf = (const struct rb_builtin_function *)iobj->operands[0];
4095 if (iobj == (INSN *)list && bf->argc == 0 && (ISEQ_BODY(iseq)->builtin_attrs & BUILTIN_ATTR_LEAF)) {
4096 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_SINGLE_NOARG_LEAF;
4097 }
4098 }
4099 }
4100 }
4101
4102 /*
4103 * getblockparam
4104 * branchif / branchunless
4105 * =>
4106 * getblockparamproxy
4107 * branchif / branchunless
4108 */
4109 if (IS_INSN_ID(iobj, getblockparam)) {
4110 if (IS_NEXT_INSN_ID(&iobj->link, branchif) || IS_NEXT_INSN_ID(&iobj->link, branchunless)) {
4111 iobj->insn_id = BIN(getblockparamproxy);
4112 }
4113 }
4114
4115 if (IS_INSN_ID(iobj, splatarray) && OPERAND_AT(iobj, 0) == false) {
4116 LINK_ELEMENT *niobj = &iobj->link;
4117 if (IS_NEXT_INSN_ID(niobj, duphash)) {
4118 niobj = niobj->next;
4119 LINK_ELEMENT *siobj;
4120 unsigned int set_flags = 0, unset_flags = 0;
4121
4122 /*
4123 * Eliminate hash allocation for f(*a, kw: 1)
4124 *
4125 * splatarray false
4126 * duphash
4127 * send ARGS_SPLAT|KW_SPLAT|KW_SPLAT_MUT and not ARGS_BLOCKARG
4128 * =>
4129 * splatarray false
4130 * putobject
4131 * send ARGS_SPLAT|KW_SPLAT
4132 */
4133 if (IS_NEXT_INSN_ID(niobj, send)) {
4134 siobj = niobj->next;
4135 set_flags = VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT;
4136 unset_flags = VM_CALL_ARGS_BLOCKARG;
4137 }
4138 /*
4139 * Eliminate hash allocation for f(*a, kw: 1, &{arg,lvar,@iv})
4140 *
4141 * splatarray false
4142 * duphash
4143 * getlocal / getinstancevariable / getblockparamproxy
4144 * send ARGS_SPLAT|KW_SPLAT|KW_SPLAT_MUT|ARGS_BLOCKARG
4145 * =>
4146 * splatarray false
4147 * putobject
4148 * getlocal / getinstancevariable / getblockparamproxy
4149 * send ARGS_SPLAT|KW_SPLAT|ARGS_BLOCKARG
4150 */
4151 else if ((IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable) ||
4152 IS_NEXT_INSN_ID(niobj, getblockparamproxy)) && (IS_NEXT_INSN_ID(niobj->next, send))) {
4153 siobj = niobj->next->next;
4154 set_flags = VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT|VM_CALL_ARGS_BLOCKARG;
4155 }
4156
4157 if (set_flags) {
4158 const struct rb_callinfo *ci = (const struct rb_callinfo *)OPERAND_AT(siobj, 0);
4159 unsigned int flags = vm_ci_flag(ci);
4160 if ((flags & set_flags) == set_flags && !(flags & unset_flags)) {
4161 ((INSN*)niobj)->insn_id = BIN(putobject);
4162 RB_OBJ_WRITE(iseq, &OPERAND_AT(niobj, 0), RB_OBJ_SET_SHAREABLE(rb_hash_freeze(rb_hash_resurrect(OPERAND_AT(niobj, 0)))));
4163
4164 const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci),
4165 flags & ~VM_CALL_KW_SPLAT_MUT, vm_ci_argc(ci), vm_ci_kwarg(ci));
4166 RB_OBJ_WRITTEN(iseq, ci, nci);
4167 OPERAND_AT(siobj, 0) = (VALUE)nci;
4168 }
4169 }
4170 }
4171 }
4172
4173 return COMPILE_OK;
4174}
4175
4176static int
4177insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id)
4178{
4179 if (insn_id == BIN(opt_neq)) {
4180 VALUE original_ci = iobj->operands[0];
4181 VALUE new_ci = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE);
4182 insn_replace_with_operands(iseq, iobj, insn_id, 2, new_ci, original_ci);
4183 }
4184 else {
4185 iobj->insn_id = insn_id;
4186 iobj->operand_size = insn_len(insn_id) - 1;
4187 }
4188 iobj->insn_info.events |= RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN;
4189
4190 return COMPILE_OK;
4191}
4192
4193static int
4194iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
4195{
4196 if (IS_INSN_ID(iobj, newarray) && iobj->link.next &&
4197 IS_INSN(iobj->link.next)) {
4198 /*
4199 * [a, b, ...].max/min -> a, b, c, opt_newarray_send max/min
4200 */
4201 INSN *niobj = (INSN *)iobj->link.next;
4202 if (IS_INSN_ID(niobj, send)) {
4203 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(niobj, 0);
4204 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0) {
4205 VALUE method = INT2FIX(0);
4206 switch (vm_ci_mid(ci)) {
4207 case idMax:
4208 method = INT2FIX(VM_OPT_NEWARRAY_SEND_MAX);
4209 break;
4210 case idMin:
4211 method = INT2FIX(VM_OPT_NEWARRAY_SEND_MIN);
4212 break;
4213 case idHash:
4214 method = INT2FIX(VM_OPT_NEWARRAY_SEND_HASH);
4215 break;
4216 }
4217
4218 if (method != INT2FIX(0)) {
4219 VALUE num = iobj->operands[0];
4220 insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, num, method);
4221 ELEM_REMOVE(&niobj->link);
4222 return COMPILE_OK;
4223 }
4224 }
4225 }
4226 else if ((IS_INSN_ID(niobj, putstring) || IS_INSN_ID(niobj, putchilledstring) ||
4227 (IS_INSN_ID(niobj, putobject) && RB_TYPE_P(OPERAND_AT(niobj, 0), T_STRING))) &&
4228 IS_NEXT_INSN_ID(&niobj->link, send)) {
4229 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT((INSN *)niobj->link.next, 0);
4230 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idPack) {
4231 VALUE num = iobj->operands[0];
4232 insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 1), INT2FIX(VM_OPT_NEWARRAY_SEND_PACK));
4233 ELEM_REMOVE(&iobj->link);
4234 ELEM_REMOVE(niobj->link.next);
4235 ELEM_INSERT_NEXT(&niobj->link, &iobj->link);
4236 return COMPILE_OK;
4237 }
4238 }
4239 // newarray n, putchilledstring "E", getlocal b, send :pack with {buffer: b}
4240 // -> putchilledstring "E", getlocal b, opt_newarray_send n+2, :pack, :buffer
4241 else if ((IS_INSN_ID(niobj, putstring) || IS_INSN_ID(niobj, putchilledstring) ||
4242 (IS_INSN_ID(niobj, putobject) && RB_TYPE_P(OPERAND_AT(niobj, 0), T_STRING))) &&
4243 IS_NEXT_INSN_ID(&niobj->link, getlocal) &&
4244 (niobj->link.next && IS_NEXT_INSN_ID(niobj->link.next, send))) {
4245 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT((INSN *)(niobj->link.next)->next, 0);
4246 const struct rb_callinfo_kwarg *kwarg = vm_ci_kwarg(ci);
4247 if (vm_ci_mid(ci) == idPack && vm_ci_argc(ci) == 2 &&
4248 (kwarg && kwarg->keyword_len == 1 && kwarg->keywords[0] == rb_id2sym(idBuffer))) {
4249 VALUE num = iobj->operands[0];
4250 insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 2), INT2FIX(VM_OPT_NEWARRAY_SEND_PACK_BUFFER));
4251 // Remove the "send" insn.
4252 ELEM_REMOVE((niobj->link.next)->next);
4253 // Remove the modified insn from its original "newarray" position...
4254 ELEM_REMOVE(&iobj->link);
4255 // and insert it after the buffer insn.
4256 ELEM_INSERT_NEXT(niobj->link.next, &iobj->link);
4257 return COMPILE_OK;
4258 }
4259 }
4260
4261 // Break the "else if" chain since some prior checks abort after sub-ifs.
4262 // We already found "newarray". To match `[...].include?(arg)` we look for
4263 // the instruction(s) representing the argument followed by a "send".
4264 if ((IS_INSN_ID(niobj, putstring) || IS_INSN_ID(niobj, putchilledstring) ||
4265 IS_INSN_ID(niobj, putobject) ||
4266 IS_INSN_ID(niobj, putself) ||
4267 IS_INSN_ID(niobj, getlocal) ||
4268 IS_INSN_ID(niobj, getinstancevariable)) &&
4269 IS_NEXT_INSN_ID(&niobj->link, send)) {
4270
4271 LINK_ELEMENT *sendobj = &(niobj->link); // Below we call ->next;
4272 const struct rb_callinfo *ci;
4273 // Allow any number (0 or more) of simple method calls on the argument
4274 // (as in `[...].include?(arg.method1.method2)`.
4275 do {
4276 sendobj = sendobj->next;
4277 ci = (struct rb_callinfo *)OPERAND_AT(sendobj, 0);
4278 } while (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && IS_NEXT_INSN_ID(sendobj, send));
4279
4280 // If this send is for .include? with one arg we can do our opt.
4281 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idIncludeP) {
4282 VALUE num = iobj->operands[0];
4283 INSN *sendins = (INSN *)sendobj;
4284 insn_replace_with_operands(iseq, sendins, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 1), INT2FIX(VM_OPT_NEWARRAY_SEND_INCLUDE_P));
4285 // Remove the original "newarray" insn.
4286 ELEM_REMOVE(&iobj->link);
4287 return COMPILE_OK;
4288 }
4289 }
4290 }
4291
4292 /*
4293 * duparray [...]
4294 * some insn for the arg...
4295 * send <calldata!mid:include?, argc:1, ARGS_SIMPLE>, nil
4296 * =>
4297 * arg insn...
4298 * opt_duparray_send [...], :include?, 1
4299 */
4300 if (IS_INSN_ID(iobj, duparray) && iobj->link.next && IS_INSN(iobj->link.next)) {
4301 INSN *niobj = (INSN *)iobj->link.next;
4302 if ((IS_INSN_ID(niobj, getlocal) ||
4303 IS_INSN_ID(niobj, getinstancevariable) ||
4304 IS_INSN_ID(niobj, putself)) &&
4305 IS_NEXT_INSN_ID(&niobj->link, send)) {
4306
4307 LINK_ELEMENT *sendobj = &(niobj->link); // Below we call ->next;
4308 const struct rb_callinfo *ci;
4309 // Allow any number (0 or more) of simple method calls on the argument
4310 // (as in `[...].include?(arg.method1.method2)`.
4311 do {
4312 sendobj = sendobj->next;
4313 ci = (struct rb_callinfo *)OPERAND_AT(sendobj, 0);
4314 } while (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && IS_NEXT_INSN_ID(sendobj, send));
4315
4316 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idIncludeP) {
4317 // Move the array arg from duparray to opt_duparray_send.
4318 VALUE ary = iobj->operands[0];
4320
4321 INSN *sendins = (INSN *)sendobj;
4322 insn_replace_with_operands(iseq, sendins, BIN(opt_duparray_send), 3, ary, rb_id2sym(idIncludeP), INT2FIX(1));
4323
4324 // Remove the duparray insn.
4325 ELEM_REMOVE(&iobj->link);
4326 return COMPILE_OK;
4327 }
4328 }
4329 }
4330
4331
4332 if (IS_INSN_ID(iobj, send)) {
4333 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
4334 const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(iobj, 1);
4335
4336#define SP_INSN(opt) insn_set_specialized_instruction(iseq, iobj, BIN(opt_##opt))
4337 if (vm_ci_simple(ci)) {
4338 switch (vm_ci_argc(ci)) {
4339 case 0:
4340 switch (vm_ci_mid(ci)) {
4341 case idLength: SP_INSN(length); return COMPILE_OK;
4342 case idSize: SP_INSN(size); return COMPILE_OK;
4343 case idEmptyP: SP_INSN(empty_p);return COMPILE_OK;
4344 case idNilP: SP_INSN(nil_p); return COMPILE_OK;
4345 case idSucc: SP_INSN(succ); return COMPILE_OK;
4346 case idNot: SP_INSN(not); return COMPILE_OK;
4347 }
4348 break;
4349 case 1:
4350 switch (vm_ci_mid(ci)) {
4351 case idPLUS: SP_INSN(plus); return COMPILE_OK;
4352 case idMINUS: SP_INSN(minus); return COMPILE_OK;
4353 case idMULT: SP_INSN(mult); return COMPILE_OK;
4354 case idDIV: SP_INSN(div); return COMPILE_OK;
4355 case idMOD: SP_INSN(mod); return COMPILE_OK;
4356 case idEq: SP_INSN(eq); return COMPILE_OK;
4357 case idNeq: SP_INSN(neq); return COMPILE_OK;
4358 case idEqTilde:SP_INSN(regexpmatch2);return COMPILE_OK;
4359 case idLT: SP_INSN(lt); return COMPILE_OK;
4360 case idLE: SP_INSN(le); return COMPILE_OK;
4361 case idGT: SP_INSN(gt); return COMPILE_OK;
4362 case idGE: SP_INSN(ge); return COMPILE_OK;
4363 case idLTLT: SP_INSN(ltlt); return COMPILE_OK;
4364 case idAREF: SP_INSN(aref); return COMPILE_OK;
4365 case idAnd: SP_INSN(and); return COMPILE_OK;
4366 case idOr: SP_INSN(or); return COMPILE_OK;
4367 }
4368 break;
4369 case 2:
4370 switch (vm_ci_mid(ci)) {
4371 case idASET: SP_INSN(aset); return COMPILE_OK;
4372 }
4373 break;
4374 }
4375 }
4376
4377 if ((vm_ci_flag(ci) & (VM_CALL_ARGS_BLOCKARG | VM_CALL_FORWARDING)) == 0 && blockiseq == NULL) {
4378 iobj->insn_id = BIN(opt_send_without_block);
4379 iobj->operand_size = insn_len(iobj->insn_id) - 1;
4380 }
4381 }
4382#undef SP_INSN
4383
4384 return COMPILE_OK;
4385}
4386
4387static inline int
4388tailcallable_p(rb_iseq_t *iseq)
4389{
4390 switch (ISEQ_BODY(iseq)->type) {
4391 case ISEQ_TYPE_TOP:
4392 case ISEQ_TYPE_EVAL:
4393 case ISEQ_TYPE_MAIN:
4394 /* not tail callable because cfp will be over popped */
4395 case ISEQ_TYPE_RESCUE:
4396 case ISEQ_TYPE_ENSURE:
4397 /* rescue block can't tail call because of errinfo */
4398 return FALSE;
4399 default:
4400 return TRUE;
4401 }
4402}
4403
4404static int
4405iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
4406{
4407 LINK_ELEMENT *list;
4408 const int do_peepholeopt = ISEQ_COMPILE_DATA(iseq)->option->peephole_optimization;
4409 const int do_tailcallopt = tailcallable_p(iseq) &&
4410 ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization;
4411 const int do_si = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction;
4412 const int do_ou = ISEQ_COMPILE_DATA(iseq)->option->operands_unification;
4413 const int do_without_ints = ISEQ_BODY(iseq)->builtin_attrs & BUILTIN_ATTR_WITHOUT_INTERRUPTS;
4414 int rescue_level = 0;
4415 int tailcallopt = do_tailcallopt;
4416
4417 list = FIRST_ELEMENT(anchor);
4418
4419 int do_block_optimization = 0;
4420 LABEL * block_loop_label = NULL;
4421
4422 // If we're optimizing a block
4423 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_BLOCK) {
4424 do_block_optimization = 1;
4425
4426 // If the block starts with a nop and a label,
4427 // record the label so we can detect if it's a jump target
4428 LINK_ELEMENT * le = FIRST_ELEMENT(anchor)->next;
4429 if (IS_INSN(le) && IS_INSN_ID((INSN *)le, nop) && IS_LABEL(le->next)) {
4430 block_loop_label = (LABEL *)le->next;
4431 }
4432 }
4433
4434 while (list) {
4435 if (IS_INSN(list)) {
4436 if (do_peepholeopt) {
4437 iseq_peephole_optimize(iseq, list, tailcallopt);
4438 }
4439 if (do_si) {
4440 iseq_specialized_instruction(iseq, (INSN *)list);
4441 }
4442 if (do_ou) {
4443 insn_operands_unification((INSN *)list);
4444 }
4445
4446 if (do_without_ints) {
4447 INSN *item = (INSN *)list;
4448 if (IS_INSN_ID(item, jump)) {
4449 item->insn_id = BIN(jump_without_ints);
4450 }
4451 else if (IS_INSN_ID(item, branchif)) {
4452 item->insn_id = BIN(branchif_without_ints);
4453 }
4454 else if (IS_INSN_ID(item, branchunless)) {
4455 item->insn_id = BIN(branchunless_without_ints);
4456 }
4457 else if (IS_INSN_ID(item, branchnil)) {
4458 item->insn_id = BIN(branchnil_without_ints);
4459 }
4460 }
4461
4462 if (do_block_optimization) {
4463 INSN * item = (INSN *)list;
4464 // Give up if there is a throw
4465 if (IS_INSN_ID(item, throw)) {
4466 do_block_optimization = 0;
4467 }
4468 else {
4469 // If the instruction has a jump target, check if the
4470 // jump target is the block loop label
4471 const char *types = insn_op_types(item->insn_id);
4472 for (int j = 0; types[j]; j++) {
4473 if (types[j] == TS_OFFSET) {
4474 // If the jump target is equal to the block loop
4475 // label, then we can't do the optimization because
4476 // the leading `nop` instruction fires the block
4477 // entry tracepoint
4478 LABEL * target = (LABEL *)OPERAND_AT(item, j);
4479 if (target == block_loop_label) {
4480 do_block_optimization = 0;
4481 }
4482 }
4483 }
4484 }
4485 }
4486 }
4487 if (IS_LABEL(list)) {
4488 switch (((LABEL *)list)->rescued) {
4489 case LABEL_RESCUE_BEG:
4490 rescue_level++;
4491 tailcallopt = FALSE;
4492 break;
4493 case LABEL_RESCUE_END:
4494 if (!--rescue_level) tailcallopt = do_tailcallopt;
4495 break;
4496 }
4497 }
4498 list = list->next;
4499 }
4500
4501 if (do_block_optimization) {
4502 LINK_ELEMENT * le = FIRST_ELEMENT(anchor)->next;
4503 if (IS_INSN(le) && IS_INSN_ID((INSN *)le, nop)) {
4504 ELEM_REMOVE(le);
4505 }
4506 }
4507 return COMPILE_OK;
4508}
4509
4510#if OPT_INSTRUCTIONS_UNIFICATION
4511static INSN *
4512new_unified_insn(rb_iseq_t *iseq,
4513 int insn_id, int size, LINK_ELEMENT *seq_list)
4514{
4515 INSN *iobj = 0;
4516 LINK_ELEMENT *list = seq_list;
4517 int i, argc = 0;
4518 VALUE *operands = 0, *ptr = 0;
4519
4520
4521 /* count argc */
4522 for (i = 0; i < size; i++) {
4523 iobj = (INSN *)list;
4524 argc += iobj->operand_size;
4525 list = list->next;
4526 }
4527
4528 if (argc > 0) {
4529 ptr = operands = compile_data_alloc2_type(iseq, VALUE, argc);
4530 }
4531
4532 /* copy operands */
4533 list = seq_list;
4534 for (i = 0; i < size; i++) {
4535 iobj = (INSN *)list;
4536 MEMCPY(ptr, iobj->operands, VALUE, iobj->operand_size);
4537 ptr += iobj->operand_size;
4538 list = list->next;
4539 }
4540
4541 return new_insn_core(iseq, iobj->insn_info.line_no, iobj->insn_info.node_id, insn_id, argc, operands);
4542}
4543#endif
4544
4545/*
4546 * This scheme can get more performance if do this optimize with
4547 * label address resolving.
4548 * It's future work (if compile time was bottle neck).
4549 */
4550static int
4551iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
4552{
4553#if OPT_INSTRUCTIONS_UNIFICATION
4554 LINK_ELEMENT *list;
4555 INSN *iobj, *niobj;
4556 int id, k;
4557 intptr_t j;
4558
4559 list = FIRST_ELEMENT(anchor);
4560 while (list) {
4561 if (IS_INSN(list)) {
4562 iobj = (INSN *)list;
4563 id = iobj->insn_id;
4564 if (unified_insns_data[id] != 0) {
4565 const int *const *entry = unified_insns_data[id];
4566 for (j = 1; j < (intptr_t)entry[0]; j++) {
4567 const int *unified = entry[j];
4568 LINK_ELEMENT *li = list->next;
4569 for (k = 2; k < unified[1]; k++) {
4570 if (!IS_INSN(li) ||
4571 ((INSN *)li)->insn_id != unified[k]) {
4572 goto miss;
4573 }
4574 li = li->next;
4575 }
4576 /* matched */
4577 niobj =
4578 new_unified_insn(iseq, unified[0], unified[1] - 1,
4579 list);
4580
4581 /* insert to list */
4582 niobj->link.prev = (LINK_ELEMENT *)iobj->link.prev;
4583 niobj->link.next = li;
4584 if (li) {
4585 li->prev = (LINK_ELEMENT *)niobj;
4586 }
4587
4588 list->prev->next = (LINK_ELEMENT *)niobj;
4589 list = (LINK_ELEMENT *)niobj;
4590 break;
4591 miss:;
4592 }
4593 }
4594 }
4595 list = list->next;
4596 }
4597#endif
4598 return COMPILE_OK;
4599}
4600
4601static int
4602all_string_result_p(const NODE *node)
4603{
4604 if (!node) return FALSE;
4605 switch (nd_type(node)) {
4606 case NODE_STR: case NODE_DSTR: case NODE_FILE:
4607 return TRUE;
4608 case NODE_IF: case NODE_UNLESS:
4609 if (!RNODE_IF(node)->nd_body || !RNODE_IF(node)->nd_else) return FALSE;
4610 if (all_string_result_p(RNODE_IF(node)->nd_body))
4611 return all_string_result_p(RNODE_IF(node)->nd_else);
4612 return FALSE;
4613 case NODE_AND: case NODE_OR:
4614 if (!RNODE_AND(node)->nd_2nd)
4615 return all_string_result_p(RNODE_AND(node)->nd_1st);
4616 if (!all_string_result_p(RNODE_AND(node)->nd_1st))
4617 return FALSE;
4618 return all_string_result_p(RNODE_AND(node)->nd_2nd);
4619 default:
4620 return FALSE;
4621 }
4622}
4623
4625 rb_iseq_t *const iseq;
4626 LINK_ANCHOR *const ret;
4627 VALUE lit;
4628 const NODE *lit_node;
4629 int cnt;
4630 int dregx;
4631};
4632
4633static int
4634append_dstr_fragment(struct dstr_ctxt *args, const NODE *const node, rb_parser_string_t *str)
4635{
4636 VALUE s = rb_str_new_mutable_parser_string(str);
4637 if (args->dregx) {
4638 VALUE error = rb_reg_check_preprocess(s);
4639 if (!NIL_P(error)) {
4640 COMPILE_ERROR(args->iseq, nd_line(node), "%" PRIsVALUE, error);
4641 return COMPILE_NG;
4642 }
4643 }
4644 if (NIL_P(args->lit)) {
4645 args->lit = s;
4646 args->lit_node = node;
4647 }
4648 else {
4649 rb_str_buf_append(args->lit, s);
4650 }
4651 return COMPILE_OK;
4652}
4653
4654static void
4655flush_dstr_fragment(struct dstr_ctxt *args)
4656{
4657 if (!NIL_P(args->lit)) {
4658 rb_iseq_t *iseq = args->iseq;
4659 VALUE lit = args->lit;
4660 args->lit = Qnil;
4661 lit = rb_fstring(lit);
4662 ADD_INSN1(args->ret, args->lit_node, putobject, lit);
4663 RB_OBJ_WRITTEN(args->iseq, Qundef, lit);
4664 args->cnt++;
4665 }
4666}
4667
4668static int
4669compile_dstr_fragments_0(struct dstr_ctxt *args, const NODE *const node)
4670{
4671 const struct RNode_LIST *list = RNODE_DSTR(node)->nd_next;
4672 rb_parser_string_t *str = RNODE_DSTR(node)->string;
4673
4674 if (str) {
4675 CHECK(append_dstr_fragment(args, node, str));
4676 }
4677
4678 while (list) {
4679 const NODE *const head = list->nd_head;
4680 if (nd_type_p(head, NODE_STR)) {
4681 CHECK(append_dstr_fragment(args, node, RNODE_STR(head)->string));
4682 }
4683 else if (nd_type_p(head, NODE_DSTR)) {
4684 CHECK(compile_dstr_fragments_0(args, head));
4685 }
4686 else {
4687 flush_dstr_fragment(args);
4688 rb_iseq_t *iseq = args->iseq;
4689 CHECK(COMPILE(args->ret, "each string", head));
4690 args->cnt++;
4691 }
4692 list = (struct RNode_LIST *)list->nd_next;
4693 }
4694 return COMPILE_OK;
4695}
4696
4697static int
4698compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int *cntp, int dregx)
4699{
4700 struct dstr_ctxt args = {
4701 .iseq = iseq, .ret = ret,
4702 .lit = Qnil, .lit_node = NULL,
4703 .cnt = 0, .dregx = dregx,
4704 };
4705 CHECK(compile_dstr_fragments_0(&args, node));
4706 flush_dstr_fragment(&args);
4707
4708 *cntp = args.cnt;
4709
4710 return COMPILE_OK;
4711}
4712
4713static int
4714compile_block(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped)
4715{
4716 while (node && nd_type_p(node, NODE_BLOCK)) {
4717 CHECK(COMPILE_(ret, "BLOCK body", RNODE_BLOCK(node)->nd_head,
4718 (RNODE_BLOCK(node)->nd_next ? 1 : popped)));
4719 node = RNODE_BLOCK(node)->nd_next;
4720 }
4721 if (node) {
4722 CHECK(COMPILE_(ret, "BLOCK next", RNODE_BLOCK(node)->nd_next, popped));
4723 }
4724 return COMPILE_OK;
4725}
4726
4727static int
4728compile_dstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
4729{
4730 int cnt;
4731 if (!RNODE_DSTR(node)->nd_next) {
4732 VALUE lit = rb_node_dstr_string_val(node);
4733 ADD_INSN1(ret, node, putstring, lit);
4734 RB_OBJ_SET_SHAREABLE(lit);
4735 RB_OBJ_WRITTEN(iseq, Qundef, lit);
4736 }
4737 else {
4738 CHECK(compile_dstr_fragments(iseq, ret, node, &cnt, FALSE));
4739 ADD_INSN1(ret, node, concatstrings, INT2FIX(cnt));
4740 }
4741 return COMPILE_OK;
4742}
4743
4744static int
4745compile_dregx(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
4746{
4747 int cnt;
4748 int cflag = (int)RNODE_DREGX(node)->as.nd_cflag;
4749
4750 if (!RNODE_DREGX(node)->nd_next) {
4751 if (!popped) {
4752 VALUE src = rb_node_dregx_string_val(node);
4753 VALUE match = rb_reg_compile(src, cflag, NULL, 0);
4754 RB_OBJ_SET_SHAREABLE(match);
4755 ADD_INSN1(ret, node, putobject, match);
4756 RB_OBJ_WRITTEN(iseq, Qundef, match);
4757 }
4758 return COMPILE_OK;
4759 }
4760
4761 CHECK(compile_dstr_fragments(iseq, ret, node, &cnt, TRUE));
4762 ADD_INSN2(ret, node, toregexp, INT2FIX(cflag), INT2FIX(cnt));
4763
4764 if (popped) {
4765 ADD_INSN(ret, node, pop);
4766 }
4767
4768 return COMPILE_OK;
4769}
4770
4771static int
4772compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int again,
4773 LABEL *then_label, LABEL *else_label)
4774{
4775 const int line = nd_line(node);
4776 LABEL *lend = NEW_LABEL(line);
4777 rb_num_t cnt = ISEQ_FLIP_CNT_INCREMENT(ISEQ_BODY(iseq)->local_iseq)
4778 + VM_SVAR_FLIPFLOP_START;
4779 VALUE key = INT2FIX(cnt);
4780
4781 ADD_INSN2(ret, node, getspecial, key, INT2FIX(0));
4782 ADD_INSNL(ret, node, branchif, lend);
4783
4784 /* *flip == 0 */
4785 CHECK(COMPILE(ret, "flip2 beg", RNODE_FLIP2(node)->nd_beg));
4786 ADD_INSNL(ret, node, branchunless, else_label);
4787 ADD_INSN1(ret, node, putobject, Qtrue);
4788 ADD_INSN1(ret, node, setspecial, key);
4789 if (!again) {
4790 ADD_INSNL(ret, node, jump, then_label);
4791 }
4792
4793 /* *flip == 1 */
4794 ADD_LABEL(ret, lend);
4795 CHECK(COMPILE(ret, "flip2 end", RNODE_FLIP2(node)->nd_end));
4796 ADD_INSNL(ret, node, branchunless, then_label);
4797 ADD_INSN1(ret, node, putobject, Qfalse);
4798 ADD_INSN1(ret, node, setspecial, key);
4799 ADD_INSNL(ret, node, jump, then_label);
4800
4801 return COMPILE_OK;
4802}
4803
4804static int
4805compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *cond,
4806 LABEL *then_label, LABEL *else_label);
4807
4808#define COMPILE_SINGLE 2
4809static int
4810compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cond,
4811 LABEL *then_label, LABEL *else_label)
4812{
4813 DECL_ANCHOR(seq);
4814 INIT_ANCHOR(seq);
4815 LABEL *label = NEW_LABEL(nd_line(cond));
4816 if (!then_label) then_label = label;
4817 else if (!else_label) else_label = label;
4818
4819 CHECK(compile_branch_condition(iseq, seq, cond, then_label, else_label));
4820
4821 if (LIST_INSN_SIZE_ONE(seq)) {
4822 INSN *insn = (INSN *)ELEM_FIRST_INSN(FIRST_ELEMENT(seq));
4823 if (insn->insn_id == BIN(jump) && (LABEL *)(insn->operands[0]) == label)
4824 return COMPILE_OK;
4825 }
4826 if (!label->refcnt) {
4827 return COMPILE_SINGLE;
4828 }
4829 ADD_LABEL(seq, label);
4830 ADD_SEQ(ret, seq);
4831 return COMPILE_OK;
4832}
4833
4834static int
4835compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *cond,
4836 LABEL *then_label, LABEL *else_label)
4837{
4838 int ok;
4839 DECL_ANCHOR(ignore);
4840
4841 again:
4842 switch (nd_type(cond)) {
4843 case NODE_AND:
4844 CHECK(ok = compile_logical(iseq, ret, RNODE_AND(cond)->nd_1st, NULL, else_label));
4845 cond = RNODE_AND(cond)->nd_2nd;
4846 if (ok == COMPILE_SINGLE) {
4847 INIT_ANCHOR(ignore);
4848 ret = ignore;
4849 then_label = NEW_LABEL(nd_line(cond));
4850 }
4851 goto again;
4852 case NODE_OR:
4853 CHECK(ok = compile_logical(iseq, ret, RNODE_OR(cond)->nd_1st, then_label, NULL));
4854 cond = RNODE_OR(cond)->nd_2nd;
4855 if (ok == COMPILE_SINGLE) {
4856 INIT_ANCHOR(ignore);
4857 ret = ignore;
4858 else_label = NEW_LABEL(nd_line(cond));
4859 }
4860 goto again;
4861 case NODE_SYM:
4862 case NODE_LINE:
4863 case NODE_FILE:
4864 case NODE_ENCODING:
4865 case NODE_INTEGER: /* NODE_INTEGER is always true */
4866 case NODE_FLOAT: /* NODE_FLOAT is always true */
4867 case NODE_RATIONAL: /* NODE_RATIONAL is always true */
4868 case NODE_IMAGINARY: /* NODE_IMAGINARY is always true */
4869 case NODE_TRUE:
4870 case NODE_STR:
4871 case NODE_REGX:
4872 case NODE_ZLIST:
4873 case NODE_LAMBDA:
4874 /* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
4875 ADD_INSNL(ret, cond, jump, then_label);
4876 return COMPILE_OK;
4877 case NODE_FALSE:
4878 case NODE_NIL:
4879 /* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
4880 ADD_INSNL(ret, cond, jump, else_label);
4881 return COMPILE_OK;
4882 case NODE_LIST:
4883 case NODE_ARGSCAT:
4884 case NODE_DREGX:
4885 case NODE_DSTR:
4886 CHECK(COMPILE_POPPED(ret, "branch condition", cond));
4887 ADD_INSNL(ret, cond, jump, then_label);
4888 return COMPILE_OK;
4889 case NODE_FLIP2:
4890 CHECK(compile_flip_flop(iseq, ret, cond, TRUE, then_label, else_label));
4891 return COMPILE_OK;
4892 case NODE_FLIP3:
4893 CHECK(compile_flip_flop(iseq, ret, cond, FALSE, then_label, else_label));
4894 return COMPILE_OK;
4895 case NODE_DEFINED:
4896 CHECK(compile_defined_expr(iseq, ret, cond, Qfalse, ret == ignore));
4897 break;
4898 default:
4899 {
4900 DECL_ANCHOR(cond_seq);
4901 INIT_ANCHOR(cond_seq);
4902
4903 CHECK(COMPILE(cond_seq, "branch condition", cond));
4904
4905 if (LIST_INSN_SIZE_ONE(cond_seq)) {
4906 INSN *insn = (INSN *)ELEM_FIRST_INSN(FIRST_ELEMENT(cond_seq));
4907 if (insn->insn_id == BIN(putobject)) {
4908 if (RTEST(insn->operands[0])) {
4909 ADD_INSNL(ret, cond, jump, then_label);
4910 // maybe unreachable
4911 return COMPILE_OK;
4912 }
4913 else {
4914 ADD_INSNL(ret, cond, jump, else_label);
4915 return COMPILE_OK;
4916 }
4917 }
4918 }
4919 ADD_SEQ(ret, cond_seq);
4920 }
4921 break;
4922 }
4923
4924 ADD_INSNL(ret, cond, branchunless, else_label);
4925 ADD_INSNL(ret, cond, jump, then_label);
4926 return COMPILE_OK;
4927}
4928
4929#define HASH_BRACE 1
4930
4931static int
4932keyword_node_p(const NODE *const node)
4933{
4934 return nd_type_p(node, NODE_HASH) && (RNODE_HASH(node)->nd_brace & HASH_BRACE) != HASH_BRACE;
4935}
4936
4937static VALUE
4938get_symbol_value(rb_iseq_t *iseq, const NODE *node)
4939{
4940 switch (nd_type(node)) {
4941 case NODE_SYM:
4942 return rb_node_sym_string_val(node);
4943 default:
4944 UNKNOWN_NODE("get_symbol_value", node, Qnil);
4945 }
4946}
4947
4948static VALUE
4949node_hash_unique_key_index(rb_iseq_t *iseq, rb_node_hash_t *node_hash, int *count_ptr)
4950{
4951 NODE *node = node_hash->nd_head;
4952 VALUE hash = rb_hash_new();
4953 VALUE ary = rb_ary_new();
4954
4955 for (int i = 0; node != NULL; i++, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
4956 VALUE key = get_symbol_value(iseq, RNODE_LIST(node)->nd_head);
4957 VALUE idx = rb_hash_aref(hash, key);
4958 if (!NIL_P(idx)) {
4959 rb_ary_store(ary, FIX2INT(idx), Qfalse);
4960 (*count_ptr)--;
4961 }
4962 rb_hash_aset(hash, key, INT2FIX(i));
4963 rb_ary_store(ary, i, Qtrue);
4964 (*count_ptr)++;
4965 }
4966
4967 return ary;
4968}
4969
4970static int
4971compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
4972 const NODE *const root_node,
4973 struct rb_callinfo_kwarg **const kw_arg_ptr,
4974 unsigned int *flag)
4975{
4976 RUBY_ASSERT(nd_type_p(root_node, NODE_HASH));
4977 RUBY_ASSERT(kw_arg_ptr != NULL);
4978 RUBY_ASSERT(flag != NULL);
4979
4980 if (RNODE_HASH(root_node)->nd_head && nd_type_p(RNODE_HASH(root_node)->nd_head, NODE_LIST)) {
4981 const NODE *node = RNODE_HASH(root_node)->nd_head;
4982 int seen_nodes = 0;
4983
4984 while (node) {
4985 const NODE *key_node = RNODE_LIST(node)->nd_head;
4986 seen_nodes++;
4987
4988 RUBY_ASSERT(nd_type_p(node, NODE_LIST));
4989 if (key_node && nd_type_p(key_node, NODE_SYM)) {
4990 /* can be keywords */
4991 }
4992 else {
4993 if (flag) {
4994 *flag |= VM_CALL_KW_SPLAT;
4995 if (seen_nodes > 1 || RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
4996 /* A new hash will be created for the keyword arguments
4997 * in this case, so mark the method as passing mutable
4998 * keyword splat.
4999 */
5000 *flag |= VM_CALL_KW_SPLAT_MUT;
5001 }
5002 }
5003 return FALSE;
5004 }
5005 node = RNODE_LIST(node)->nd_next; /* skip value node */
5006 node = RNODE_LIST(node)->nd_next;
5007 }
5008
5009 /* may be keywords */
5010 node = RNODE_HASH(root_node)->nd_head;
5011 {
5012 int len = 0;
5013 VALUE key_index = node_hash_unique_key_index(iseq, RNODE_HASH(root_node), &len);
5014 struct rb_callinfo_kwarg *kw_arg =
5015 rb_xmalloc_mul_add(len, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
5016 VALUE *keywords = kw_arg->keywords;
5017 int i = 0;
5018 int j = 0;
5019 kw_arg->references = 0;
5020 kw_arg->keyword_len = len;
5021
5022 *kw_arg_ptr = kw_arg;
5023
5024 for (i=0; node != NULL; i++, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
5025 const NODE *key_node = RNODE_LIST(node)->nd_head;
5026 const NODE *val_node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head;
5027 int popped = TRUE;
5028 if (rb_ary_entry(key_index, i)) {
5029 keywords[j] = get_symbol_value(iseq, key_node);
5030 j++;
5031 popped = FALSE;
5032 }
5033 NO_CHECK(COMPILE_(ret, "keyword values", val_node, popped));
5034 }
5035 RUBY_ASSERT(j == len);
5036 return TRUE;
5037 }
5038 }
5039 return FALSE;
5040}
5041
5042static int
5043compile_args(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, NODE **kwnode_ptr)
5044{
5045 int len = 0;
5046
5047 for (; node; len++, node = RNODE_LIST(node)->nd_next) {
5048 if (CPDEBUG > 0) {
5049 EXPECT_NODE("compile_args", node, NODE_LIST, -1);
5050 }
5051
5052 if (RNODE_LIST(node)->nd_next == NULL && keyword_node_p(RNODE_LIST(node)->nd_head)) { /* last node is kwnode */
5053 *kwnode_ptr = RNODE_LIST(node)->nd_head;
5054 }
5055 else {
5056 RUBY_ASSERT(!keyword_node_p(RNODE_LIST(node)->nd_head));
5057 NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, FALSE));
5058 }
5059 }
5060
5061 return len;
5062}
5063
5064static inline bool
5065frozen_string_literal_p(const rb_iseq_t *iseq)
5066{
5067 return ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal > 0;
5068}
5069
5070static inline bool
5071static_literal_node_p(const NODE *node, const rb_iseq_t *iseq, bool hash_key)
5072{
5073 switch (nd_type(node)) {
5074 case NODE_SYM:
5075 case NODE_REGX:
5076 case NODE_LINE:
5077 case NODE_ENCODING:
5078 case NODE_INTEGER:
5079 case NODE_FLOAT:
5080 case NODE_RATIONAL:
5081 case NODE_IMAGINARY:
5082 case NODE_NIL:
5083 case NODE_TRUE:
5084 case NODE_FALSE:
5085 return TRUE;
5086 case NODE_STR:
5087 case NODE_FILE:
5088 return hash_key || frozen_string_literal_p(iseq);
5089 default:
5090 return FALSE;
5091 }
5092}
5093
5094static inline VALUE
5095static_literal_value(const NODE *node, rb_iseq_t *iseq)
5096{
5097 switch (nd_type(node)) {
5098 case NODE_INTEGER:
5099 {
5100 VALUE lit = rb_node_integer_literal_val(node);
5101 if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit);
5102 return lit;
5103 }
5104 case NODE_FLOAT:
5105 {
5106 VALUE lit = rb_node_float_literal_val(node);
5107 if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit);
5108 return lit;
5109 }
5110 case NODE_RATIONAL:
5111 return rb_ractor_make_shareable(rb_node_rational_literal_val(node));
5112 case NODE_IMAGINARY:
5113 return rb_ractor_make_shareable(rb_node_imaginary_literal_val(node));
5114 case NODE_NIL:
5115 return Qnil;
5116 case NODE_TRUE:
5117 return Qtrue;
5118 case NODE_FALSE:
5119 return Qfalse;
5120 case NODE_SYM:
5121 return rb_node_sym_string_val(node);
5122 case NODE_REGX:
5123 return RB_OBJ_SET_SHAREABLE(rb_node_regx_string_val(node));
5124 case NODE_LINE:
5125 return rb_node_line_lineno_val(node);
5126 case NODE_ENCODING:
5127 return rb_node_encoding_val(node);
5128 case NODE_FILE:
5129 case NODE_STR:
5130 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
5131 VALUE lit = get_string_value(node);
5132 VALUE str = rb_str_with_debug_created_info(lit, rb_iseq_path(iseq), (int)nd_line(node));
5133 RB_OBJ_SET_SHAREABLE(str);
5134 return str;
5135 }
5136 else {
5137 return get_string_value(node);
5138 }
5139 default:
5140 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
5141 }
5142}
5143
5144static int
5145compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped, bool first_chunk)
5146{
5147 const NODE *line_node = node;
5148
5149 if (nd_type_p(node, NODE_ZLIST)) {
5150 if (!popped) {
5151 ADD_INSN1(ret, line_node, newarray, INT2FIX(0));
5152 }
5153 return 0;
5154 }
5155
5156 EXPECT_NODE("compile_array", node, NODE_LIST, -1);
5157
5158 if (popped) {
5159 for (; node; node = RNODE_LIST(node)->nd_next) {
5160 NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, popped));
5161 }
5162 return 1;
5163 }
5164
5165 /* Compilation of an array literal.
5166 * The following code is essentially the same as:
5167 *
5168 * for (int count = 0; node; count++; node->nd_next) {
5169 * compile(node->nd_head);
5170 * }
5171 * ADD_INSN(newarray, count);
5172 *
5173 * However, there are three points.
5174 *
5175 * - The code above causes stack overflow for a big string literal.
5176 * The following limits the stack length up to max_stack_len.
5177 *
5178 * [x1,x2,...,x10000] =>
5179 * push x1 ; push x2 ; ...; push x256; newarray 256;
5180 * push x257; push x258; ...; push x512; pushtoarray 256;
5181 * push x513; push x514; ...; push x768; pushtoarray 256;
5182 * ...
5183 *
5184 * - Long subarray can be optimized by pre-allocating a hidden array.
5185 *
5186 * [1,2,3,...,100] =>
5187 * duparray [1,2,3,...,100]
5188 *
5189 * [x, 1,2,3,...,100, z] =>
5190 * push x; newarray 1;
5191 * putobject [1,2,3,...,100] (<- hidden array); concattoarray;
5192 * push z; pushtoarray 1;
5193 *
5194 * - If the last element is a keyword, pushtoarraykwsplat should be emitted
5195 * to only push it onto the array if it is not empty
5196 * (Note: a keyword is NODE_HASH which is not static_literal_node_p.)
5197 *
5198 * [1,2,3,**kw] =>
5199 * putobject 1; putobject 2; putobject 3; newarray 3; ...; pushtoarraykwsplat kw
5200 */
5201
5202 const int max_stack_len = 0x100;
5203 const int min_tmp_ary_len = 0x40;
5204 int stack_len = 0;
5205
5206 /* Either create a new array, or push to the existing array */
5207#define FLUSH_CHUNK \
5208 if (stack_len) { \
5209 if (first_chunk) ADD_INSN1(ret, line_node, newarray, INT2FIX(stack_len)); \
5210 else ADD_INSN1(ret, line_node, pushtoarray, INT2FIX(stack_len)); \
5211 first_chunk = FALSE; \
5212 stack_len = 0; \
5213 }
5214
5215 while (node) {
5216 int count = 1;
5217
5218 /* pre-allocation check (this branch can be omittable) */
5219 if (static_literal_node_p(RNODE_LIST(node)->nd_head, iseq, false)) {
5220 /* count the elements that are optimizable */
5221 const NODE *node_tmp = RNODE_LIST(node)->nd_next;
5222 for (; node_tmp && static_literal_node_p(RNODE_LIST(node_tmp)->nd_head, iseq, false); node_tmp = RNODE_LIST(node_tmp)->nd_next)
5223 count++;
5224
5225 if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_ary_len) {
5226 /* The literal contains only optimizable elements, or the subarray is long enough */
5227 VALUE ary = rb_ary_hidden_new(count);
5228
5229 /* Create a hidden array */
5230 for (; count; count--, node = RNODE_LIST(node)->nd_next)
5231 rb_ary_push(ary, static_literal_value(RNODE_LIST(node)->nd_head, iseq));
5232 RB_OBJ_SET_FROZEN_SHAREABLE(ary);
5233
5234 /* Emit optimized code */
5235 FLUSH_CHUNK;
5236 if (first_chunk) {
5237 ADD_INSN1(ret, line_node, duparray, ary);
5238 first_chunk = FALSE;
5239 }
5240 else {
5241 ADD_INSN1(ret, line_node, putobject, ary);
5242 ADD_INSN(ret, line_node, concattoarray);
5243 }
5244 RB_OBJ_SET_SHAREABLE(ary);
5245 RB_OBJ_WRITTEN(iseq, Qundef, ary);
5246 }
5247 }
5248
5249 /* Base case: Compile "count" elements */
5250 for (; count; count--, node = RNODE_LIST(node)->nd_next) {
5251 if (CPDEBUG > 0) {
5252 EXPECT_NODE("compile_array", node, NODE_LIST, -1);
5253 }
5254
5255 if (!RNODE_LIST(node)->nd_next && keyword_node_p(RNODE_LIST(node)->nd_head)) {
5256 /* Create array or push existing non-keyword elements onto array */
5257 if (stack_len == 0 && first_chunk) {
5258 ADD_INSN1(ret, line_node, newarray, INT2FIX(0));
5259 }
5260 else {
5261 FLUSH_CHUNK;
5262 }
5263 NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, 0));
5264 ADD_INSN(ret, line_node, pushtoarraykwsplat);
5265 return 1;
5266 }
5267 else {
5268 NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, 0));
5269 stack_len++;
5270 }
5271
5272 /* If there are many pushed elements, flush them to avoid stack overflow */
5273 if (stack_len >= max_stack_len) FLUSH_CHUNK;
5274 }
5275 }
5276
5277 FLUSH_CHUNK;
5278#undef FLUSH_CHUNK
5279 return 1;
5280}
5281
5282static inline int
5283static_literal_node_pair_p(const NODE *node, const rb_iseq_t *iseq)
5284{
5285 return RNODE_LIST(node)->nd_head && static_literal_node_p(RNODE_LIST(node)->nd_head, iseq, true) && static_literal_node_p(RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, iseq, false);
5286}
5287
5288static int
5289compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int method_call_keywords, int popped)
5290{
5291 const NODE *line_node = node;
5292
5293 node = RNODE_HASH(node)->nd_head;
5294
5295 if (!node || nd_type_p(node, NODE_ZLIST)) {
5296 if (!popped) {
5297 ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
5298 }
5299 return 0;
5300 }
5301
5302 EXPECT_NODE("compile_hash", node, NODE_LIST, -1);
5303
5304 if (popped) {
5305 for (; node; node = RNODE_LIST(node)->nd_next) {
5306 NO_CHECK(COMPILE_(ret, "hash element", RNODE_LIST(node)->nd_head, popped));
5307 }
5308 return 1;
5309 }
5310
5311 /* Compilation of a hash literal (or keyword arguments).
5312 * This is very similar to compile_array, but there are some differences:
5313 *
5314 * - It contains key-value pairs. So we need to take every two elements.
5315 * We can assume that the length is always even.
5316 *
5317 * - Merging is done by a method call (id_core_hash_merge_ptr).
5318 * Sometimes we need to insert the receiver, so "anchor" is needed.
5319 * In addition, a method call is much slower than concatarray.
5320 * So it pays only when the subsequence is really long.
5321 * (min_tmp_hash_len must be much larger than min_tmp_ary_len.)
5322 *
5323 * - We need to handle keyword splat: **kw.
5324 * For **kw, the key part (node->nd_head) is NULL, and the value part
5325 * (node->nd_next->nd_head) is "kw".
5326 * The code is a bit difficult to avoid hash allocation for **{}.
5327 */
5328
5329 const int max_stack_len = 0x100;
5330 const int min_tmp_hash_len = 0x800;
5331 int stack_len = 0;
5332 int first_chunk = 1;
5333 DECL_ANCHOR(anchor);
5334 INIT_ANCHOR(anchor);
5335
5336 /* Convert pushed elements to a hash, and merge if needed */
5337#define FLUSH_CHUNK() \
5338 if (stack_len) { \
5339 if (first_chunk) { \
5340 APPEND_LIST(ret, anchor); \
5341 ADD_INSN1(ret, line_node, newhash, INT2FIX(stack_len)); \
5342 } \
5343 else { \
5344 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); \
5345 ADD_INSN(ret, line_node, swap); \
5346 APPEND_LIST(ret, anchor); \
5347 ADD_SEND(ret, line_node, id_core_hash_merge_ptr, INT2FIX(stack_len + 1)); \
5348 } \
5349 INIT_ANCHOR(anchor); \
5350 first_chunk = stack_len = 0; \
5351 }
5352
5353 while (node) {
5354 int count = 1;
5355
5356 /* pre-allocation check (this branch can be omittable) */
5357 if (static_literal_node_pair_p(node, iseq)) {
5358 /* count the elements that are optimizable */
5359 const NODE *node_tmp = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next;
5360 for (; node_tmp && static_literal_node_pair_p(node_tmp, iseq); node_tmp = RNODE_LIST(RNODE_LIST(node_tmp)->nd_next)->nd_next)
5361 count++;
5362
5363 if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_hash_len) {
5364 /* The literal contains only optimizable elements, or the subsequence is long enough */
5365 VALUE ary = rb_ary_hidden_new(count);
5366
5367 /* Create a hidden hash */
5368 for (; count; count--, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
5369 VALUE elem[2];
5370 elem[0] = static_literal_value(RNODE_LIST(node)->nd_head, iseq);
5371 if (!RB_SPECIAL_CONST_P(elem[0])) RB_OBJ_SET_FROZEN_SHAREABLE(elem[0]);
5372 elem[1] = static_literal_value(RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, iseq);
5373 if (!RB_SPECIAL_CONST_P(elem[1])) RB_OBJ_SET_FROZEN_SHAREABLE(elem[1]);
5374 rb_ary_cat(ary, elem, 2);
5375 }
5376 VALUE hash = rb_hash_new_with_size(RARRAY_LEN(ary) / 2);
5377 rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary), hash);
5378 hash = RB_OBJ_SET_FROZEN_SHAREABLE(rb_obj_hide(hash));
5379
5380 /* Emit optimized code */
5381 FLUSH_CHUNK();
5382 if (first_chunk) {
5383 ADD_INSN1(ret, line_node, duphash, hash);
5384 first_chunk = 0;
5385 }
5386 else {
5387 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5388 ADD_INSN(ret, line_node, swap);
5389
5390 ADD_INSN1(ret, line_node, putobject, hash);
5391
5392 ADD_SEND(ret, line_node, id_core_hash_merge_kwd, INT2FIX(2));
5393 }
5394 RB_OBJ_WRITTEN(iseq, Qundef, hash);
5395 }
5396 }
5397
5398 /* Base case: Compile "count" elements */
5399 for (; count; count--, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
5400
5401 if (CPDEBUG > 0) {
5402 EXPECT_NODE("compile_hash", node, NODE_LIST, -1);
5403 }
5404
5405 if (RNODE_LIST(node)->nd_head) {
5406 /* Normal key-value pair */
5407 NO_CHECK(COMPILE_(anchor, "hash key element", RNODE_LIST(node)->nd_head, 0));
5408 NO_CHECK(COMPILE_(anchor, "hash value element", RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, 0));
5409 stack_len += 2;
5410
5411 /* If there are many pushed elements, flush them to avoid stack overflow */
5412 if (stack_len >= max_stack_len) FLUSH_CHUNK();
5413 }
5414 else {
5415 /* kwsplat case: foo(..., **kw, ...) */
5416 FLUSH_CHUNK();
5417
5418 const NODE *kw = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head;
5419 int empty_kw = nd_type_p(kw, NODE_HASH) && (!RNODE_HASH(kw)->nd_head); /* foo( ..., **{}, ...) */
5420 int first_kw = first_chunk && stack_len == 0; /* foo(1,2,3, **kw, ...) */
5421 int last_kw = !RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next; /* foo( ..., **kw) */
5422 int only_kw = last_kw && first_kw; /* foo(1,2,3, **kw) */
5423
5424 empty_kw = empty_kw || nd_type_p(kw, NODE_NIL); /* foo( ..., **nil, ...) */
5425 if (empty_kw) {
5426 if (only_kw && method_call_keywords) {
5427 /* **{} appears at the only keyword argument in method call,
5428 * so it won't be modified.
5429 * kw is a special NODE_LIT that contains a special empty hash,
5430 * so this emits: putobject {}.
5431 * This is only done for method calls and not for literal hashes,
5432 * because literal hashes should always result in a new hash.
5433 */
5434 NO_CHECK(COMPILE(ret, "keyword splat", kw));
5435 }
5436 else if (first_kw) {
5437 /* **{} appears as the first keyword argument, so it may be modified.
5438 * We need to create a fresh hash object.
5439 */
5440 ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
5441 }
5442 /* Any empty keyword splats that are not the first can be ignored.
5443 * since merging an empty hash into the existing hash is the same
5444 * as not merging it. */
5445 }
5446 else {
5447 if (only_kw && method_call_keywords) {
5448 /* **kw is only keyword argument in method call.
5449 * Use directly. This will be not be flagged as mutable.
5450 * This is only done for method calls and not for literal hashes,
5451 * because literal hashes should always result in a new hash.
5452 */
5453 NO_CHECK(COMPILE(ret, "keyword splat", kw));
5454 }
5455 else {
5456 /* There is more than one keyword argument, or this is not a method
5457 * call. In that case, we need to add an empty hash (if first keyword),
5458 * or merge the hash to the accumulated hash (if not the first keyword).
5459 */
5460 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5461 if (first_kw) ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
5462 else ADD_INSN(ret, line_node, swap);
5463
5464 NO_CHECK(COMPILE(ret, "keyword splat", kw));
5465
5466 ADD_SEND(ret, line_node, id_core_hash_merge_kwd, INT2FIX(2));
5467 }
5468 }
5469
5470 first_chunk = 0;
5471 }
5472 }
5473 }
5474
5475 FLUSH_CHUNK();
5476#undef FLUSH_CHUNK
5477 return 1;
5478}
5479
5480VALUE
5481rb_node_case_when_optimizable_literal(const NODE *const node)
5482{
5483 switch (nd_type(node)) {
5484 case NODE_INTEGER:
5485 return rb_node_integer_literal_val(node);
5486 case NODE_FLOAT: {
5487 VALUE v = rb_node_float_literal_val(node);
5488 double ival;
5489
5490 if (modf(RFLOAT_VALUE(v), &ival) == 0.0) {
5491 return FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival);
5492 }
5493 return v;
5494 }
5495 case NODE_RATIONAL:
5496 case NODE_IMAGINARY:
5497 return Qundef;
5498 case NODE_NIL:
5499 return Qnil;
5500 case NODE_TRUE:
5501 return Qtrue;
5502 case NODE_FALSE:
5503 return Qfalse;
5504 case NODE_SYM:
5505 return rb_node_sym_string_val(node);
5506 case NODE_LINE:
5507 return rb_node_line_lineno_val(node);
5508 case NODE_STR:
5509 return rb_node_str_string_val(node);
5510 case NODE_FILE:
5511 return rb_node_file_path_val(node);
5512 }
5513 return Qundef;
5514}
5515
5516static int
5517when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
5518 LABEL *l1, int only_special_literals, VALUE literals)
5519{
5520 while (vals) {
5521 const NODE *val = RNODE_LIST(vals)->nd_head;
5522 VALUE lit = rb_node_case_when_optimizable_literal(val);
5523
5524 if (UNDEF_P(lit)) {
5525 only_special_literals = 0;
5526 }
5527 else if (NIL_P(rb_hash_lookup(literals, lit))) {
5528 rb_hash_aset(literals, lit, (VALUE)(l1) | 1);
5529 }
5530
5531 if (nd_type_p(val, NODE_STR) || nd_type_p(val, NODE_FILE)) {
5532 debugp_param("nd_lit", get_string_value(val));
5533 lit = get_string_value(val);
5534 ADD_INSN1(cond_seq, val, putobject, lit);
5535 RB_OBJ_WRITTEN(iseq, Qundef, lit);
5536 }
5537 else {
5538 if (!COMPILE(cond_seq, "when cond", val)) return -1;
5539 }
5540
5541 // Emit pattern === target
5542 ADD_INSN1(cond_seq, vals, topn, INT2FIX(1));
5543 ADD_CALL(cond_seq, vals, idEqq, INT2FIX(1));
5544 ADD_INSNL(cond_seq, val, branchif, l1);
5545 vals = RNODE_LIST(vals)->nd_next;
5546 }
5547 return only_special_literals;
5548}
5549
5550static int
5551when_splat_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
5552 LABEL *l1, int only_special_literals, VALUE literals)
5553{
5554 const NODE *line_node = vals;
5555
5556 switch (nd_type(vals)) {
5557 case NODE_LIST:
5558 if (when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals) < 0)
5559 return COMPILE_NG;
5560 break;
5561 case NODE_SPLAT:
5562 ADD_INSN (cond_seq, line_node, dup);
5563 CHECK(COMPILE(cond_seq, "when splat", RNODE_SPLAT(vals)->nd_head));
5564 ADD_INSN1(cond_seq, line_node, splatarray, Qfalse);
5565 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
5566 ADD_INSNL(cond_seq, line_node, branchif, l1);
5567 break;
5568 case NODE_ARGSCAT:
5569 CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSCAT(vals)->nd_head, l1, only_special_literals, literals));
5570 CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSCAT(vals)->nd_body, l1, only_special_literals, literals));
5571 break;
5572 case NODE_ARGSPUSH:
5573 CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSPUSH(vals)->nd_head, l1, only_special_literals, literals));
5574 ADD_INSN (cond_seq, line_node, dup);
5575 CHECK(COMPILE(cond_seq, "when argspush body", RNODE_ARGSPUSH(vals)->nd_body));
5576 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
5577 ADD_INSNL(cond_seq, line_node, branchif, l1);
5578 break;
5579 default:
5580 ADD_INSN (cond_seq, line_node, dup);
5581 CHECK(COMPILE(cond_seq, "when val", vals));
5582 ADD_INSN1(cond_seq, line_node, splatarray, Qfalse);
5583 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
5584 ADD_INSNL(cond_seq, line_node, branchif, l1);
5585 break;
5586 }
5587 return COMPILE_OK;
5588}
5589
5590/* Multiple Assignment Handling
5591 *
5592 * In order to handle evaluation of multiple assignment such that the left hand side
5593 * is evaluated before the right hand side, we need to process the left hand side
5594 * and see if there are any attributes that need to be assigned, or constants set
5595 * on explicit objects. If so, we add instructions to evaluate the receiver of
5596 * any assigned attributes or constants before we process the right hand side.
5597 *
5598 * For a multiple assignment such as:
5599 *
5600 * l1.m1, l2[0] = r3, r4
5601 *
5602 * We start off evaluating l1 and l2, then we evaluate r3 and r4, then we
5603 * assign the result of r3 to l1.m1, and then the result of r4 to l2.m2.
5604 * On the VM stack, this looks like:
5605 *
5606 * self # putself
5607 * l1 # send
5608 * l1, self # putself
5609 * l1, l2 # send
5610 * l1, l2, 0 # putobject 0
5611 * l1, l2, 0, [r3, r4] # after evaluation of RHS
5612 * l1, l2, 0, [r3, r4], r4, r3 # expandarray
5613 * l1, l2, 0, [r3, r4], r4, r3, l1 # topn 5
5614 * l1, l2, 0, [r3, r4], r4, l1, r3 # swap
5615 * l1, l2, 0, [r3, r4], r4, m1= # send
5616 * l1, l2, 0, [r3, r4], r4 # pop
5617 * l1, l2, 0, [r3, r4], r4, l2 # topn 3
5618 * l1, l2, 0, [r3, r4], r4, l2, 0 # topn 3
5619 * l1, l2, 0, [r3, r4], r4, l2, 0, r4 # topn 2
5620 * l1, l2, 0, [r3, r4], r4, []= # send
5621 * l1, l2, 0, [r3, r4], r4 # pop
5622 * l1, l2, 0, [r3, r4] # pop
5623 * [r3, r4], l2, 0, [r3, r4] # setn 3
5624 * [r3, r4], l2, 0 # pop
5625 * [r3, r4], l2 # pop
5626 * [r3, r4] # pop
5627 *
5628 * This is made more complex when you have to handle splats, post args,
5629 * and arbitrary levels of nesting. You need to keep track of the total
5630 * number of attributes to set, and for each attribute, how many entries
5631 * are on the stack before the final attribute, in order to correctly
5632 * calculate the topn value to use to get the receiver of the attribute
5633 * setter method.
5634 *
5635 * A brief description of the VM stack for simple multiple assignment
5636 * with no splat (rhs_array will not be present if the return value of
5637 * the multiple assignment is not needed):
5638 *
5639 * lhs_attr1, lhs_attr2, ..., rhs_array, ..., rhs_arg2, rhs_arg1
5640 *
5641 * For multiple assignment with splats, while processing the part before
5642 * the splat (splat+post here is an array of the splat and the post arguments):
5643 *
5644 * lhs_attr1, lhs_attr2, ..., rhs_array, splat+post, ..., rhs_arg2, rhs_arg1
5645 *
5646 * When processing the splat and post arguments:
5647 *
5648 * lhs_attr1, lhs_attr2, ..., rhs_array, ..., post_arg2, post_arg1, splat
5649 *
5650 * When processing nested multiple assignment, existing values on the stack
5651 * are kept. So for:
5652 *
5653 * (l1.m1, l2.m2), l3.m3, l4* = [r1, r2], r3, r4
5654 *
5655 * The stack layout would be the following before processing the nested
5656 * multiple assignment:
5657 *
5658 * l1, l2, [[r1, r2], r3, r4], [r4], r3, [r1, r2]
5659 *
5660 * In order to handle this correctly, we need to keep track of the nesting
5661 * level for each attribute assignment, as well as the attribute number
5662 * (left hand side attributes are processed left to right) and number of
5663 * arguments to pass to the setter method. struct masgn_lhs_node tracks
5664 * this information.
5665 *
5666 * We also need to track information for the entire multiple assignment, such
5667 * as the total number of arguments, and the current nesting level, to
5668 * handle both nested multiple assignment as well as cases where the
5669 * rhs is not needed. We also need to keep track of all attribute
5670 * assignments in this, which we do using a linked listed. struct masgn_state
5671 * tracks this information.
5672 */
5673
5675 INSN *before_insn;
5676 struct masgn_lhs_node *next;
5677 const NODE *line_node;
5678 int argn;
5679 int num_args;
5680 int lhs_pos;
5681};
5682
5684 struct masgn_lhs_node *first_memo;
5685 struct masgn_lhs_node *last_memo;
5686 int lhs_level;
5687 int num_args;
5688 bool nested;
5689};
5690
5691static int
5692add_masgn_lhs_node(struct masgn_state *state, int lhs_pos, const NODE *line_node, int argc, INSN *before_insn)
5693{
5694 if (!state) {
5695 rb_bug("no masgn_state");
5696 }
5697
5698 struct masgn_lhs_node *memo;
5699 memo = malloc(sizeof(struct masgn_lhs_node));
5700 if (!memo) {
5701 return COMPILE_NG;
5702 }
5703
5704 memo->before_insn = before_insn;
5705 memo->line_node = line_node;
5706 memo->argn = state->num_args + 1;
5707 memo->num_args = argc;
5708 state->num_args += argc;
5709 memo->lhs_pos = lhs_pos;
5710 memo->next = NULL;
5711 if (!state->first_memo) {
5712 state->first_memo = memo;
5713 }
5714 else {
5715 state->last_memo->next = memo;
5716 }
5717 state->last_memo = memo;
5718
5719 return COMPILE_OK;
5720}
5721
5722static int compile_massign0(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const rhs, LINK_ANCHOR *const lhs, LINK_ANCHOR *const post, const NODE *const node, struct masgn_state *state, int popped);
5723
5724static int
5725compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const rhs, LINK_ANCHOR *const lhs, LINK_ANCHOR *const post, const NODE *const node, struct masgn_state *state, int lhs_pos)
5726{
5727 switch (nd_type(node)) {
5728 case NODE_ATTRASGN: {
5729 INSN *iobj;
5730 const NODE *line_node = node;
5731
5732 CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_ATTRASGN)", node));
5733
5734 bool safenav_call = false;
5735 LINK_ELEMENT *insn_element = LAST_ELEMENT(pre);
5736 iobj = (INSN *)get_prev_insn((INSN *)insn_element); /* send insn */
5737 ASSUME(iobj);
5738 ELEM_REMOVE(insn_element);
5739 if (!IS_INSN_ID(iobj, send)) {
5740 safenav_call = true;
5741 iobj = (INSN *)get_prev_insn(iobj);
5742 ELEM_INSERT_NEXT(&iobj->link, insn_element);
5743 }
5744 (pre->last = iobj->link.prev)->next = 0;
5745
5746 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
5747 int argc = vm_ci_argc(ci) + 1;
5748 ci = ci_argc_set(iseq, ci, argc);
5749 OPERAND_AT(iobj, 0) = (VALUE)ci;
5750 RB_OBJ_WRITTEN(iseq, Qundef, ci);
5751
5752 if (argc == 1) {
5753 ADD_INSN(lhs, line_node, swap);
5754 }
5755 else {
5756 ADD_INSN1(lhs, line_node, topn, INT2FIX(argc));
5757 }
5758
5759 if (!add_masgn_lhs_node(state, lhs_pos, line_node, argc, (INSN *)LAST_ELEMENT(lhs))) {
5760 return COMPILE_NG;
5761 }
5762
5763 iobj->link.prev = lhs->last;
5764 lhs->last->next = &iobj->link;
5765 for (lhs->last = &iobj->link; lhs->last->next; lhs->last = lhs->last->next);
5766 if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) {
5767 int argc = vm_ci_argc(ci);
5768 bool dupsplat = false;
5769 ci = ci_argc_set(iseq, ci, argc - 1);
5770 if (!(vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT_MUT)) {
5771 /* Given h[*a], _ = ary
5772 * setup_args sets VM_CALL_ARGS_SPLAT and not VM_CALL_ARGS_SPLAT_MUT
5773 * `a` must be dupped, because it will be appended with ary[0]
5774 * Since you are dupping `a`, you can set VM_CALL_ARGS_SPLAT_MUT
5775 */
5776 dupsplat = true;
5777 ci = ci_flag_set(iseq, ci, VM_CALL_ARGS_SPLAT_MUT);
5778 }
5779 OPERAND_AT(iobj, 0) = (VALUE)ci;
5780 RB_OBJ_WRITTEN(iseq, Qundef, iobj);
5781
5782 /* Given: h[*a], h[*b, 1] = ary
5783 * h[*a] uses splatarray false and does not set VM_CALL_ARGS_SPLAT_MUT,
5784 * so this uses splatarray true on a to dup it before using pushtoarray
5785 * h[*b, 1] uses splatarray true and sets VM_CALL_ARGS_SPLAT_MUT,
5786 * so you can use pushtoarray directly
5787 */
5788 int line_no = nd_line(line_node);
5789 int node_id = nd_node_id(line_node);
5790
5791 if (dupsplat) {
5792 INSERT_BEFORE_INSN(iobj, line_no, node_id, swap);
5793 INSERT_BEFORE_INSN1(iobj, line_no, node_id, splatarray, Qtrue);
5794 INSERT_BEFORE_INSN(iobj, line_no, node_id, swap);
5795 }
5796 INSERT_BEFORE_INSN1(iobj, line_no, node_id, pushtoarray, INT2FIX(1));
5797 }
5798 if (!safenav_call) {
5799 ADD_INSN(lhs, line_node, pop);
5800 if (argc != 1) {
5801 ADD_INSN(lhs, line_node, pop);
5802 }
5803 }
5804 for (int i=0; i < argc; i++) {
5805 ADD_INSN(post, line_node, pop);
5806 }
5807 break;
5808 }
5809 case NODE_MASGN: {
5810 DECL_ANCHOR(nest_rhs);
5811 INIT_ANCHOR(nest_rhs);
5812 DECL_ANCHOR(nest_lhs);
5813 INIT_ANCHOR(nest_lhs);
5814
5815 int prev_level = state->lhs_level;
5816 bool prev_nested = state->nested;
5817 state->nested = 1;
5818 state->lhs_level = lhs_pos - 1;
5819 CHECK(compile_massign0(iseq, pre, nest_rhs, nest_lhs, post, node, state, 1));
5820 state->lhs_level = prev_level;
5821 state->nested = prev_nested;
5822
5823 ADD_SEQ(lhs, nest_rhs);
5824 ADD_SEQ(lhs, nest_lhs);
5825 break;
5826 }
5827 case NODE_CDECL:
5828 if (!RNODE_CDECL(node)->nd_vid) {
5829 /* Special handling only needed for expr::C, not for C */
5830 INSN *iobj;
5831
5832 CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_CDECL)", node));
5833
5834 LINK_ELEMENT *insn_element = LAST_ELEMENT(pre);
5835 iobj = (INSN *)insn_element; /* setconstant insn */
5836 ELEM_REMOVE((LINK_ELEMENT *)get_prev_insn((INSN *)get_prev_insn(iobj)));
5837 ELEM_REMOVE((LINK_ELEMENT *)get_prev_insn(iobj));
5838 ELEM_REMOVE(insn_element);
5839 pre->last = iobj->link.prev;
5840 ADD_ELEM(lhs, (LINK_ELEMENT *)iobj);
5841
5842 if (!add_masgn_lhs_node(state, lhs_pos, node, 1, (INSN *)LAST_ELEMENT(lhs))) {
5843 return COMPILE_NG;
5844 }
5845
5846 ADD_INSN(post, node, pop);
5847 break;
5848 }
5849 /* Fallthrough */
5850 default: {
5851 DECL_ANCHOR(anchor);
5852 INIT_ANCHOR(anchor);
5853 CHECK(COMPILE_POPPED(anchor, "masgn lhs", node));
5854 ELEM_REMOVE(FIRST_ELEMENT(anchor));
5855 ADD_SEQ(lhs, anchor);
5856 }
5857 }
5858
5859 return COMPILE_OK;
5860}
5861
5862static int
5863compile_massign_opt_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *lhsn)
5864{
5865 if (lhsn) {
5866 CHECK(compile_massign_opt_lhs(iseq, ret, RNODE_LIST(lhsn)->nd_next));
5867 CHECK(compile_massign_lhs(iseq, ret, ret, ret, ret, RNODE_LIST(lhsn)->nd_head, NULL, 0));
5868 }
5869 return COMPILE_OK;
5870}
5871
5872static int
5873compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
5874 const NODE *rhsn, const NODE *orig_lhsn)
5875{
5876 VALUE mem[64];
5877 const int memsize = numberof(mem);
5878 int memindex = 0;
5879 int llen = 0, rlen = 0;
5880 int i;
5881 const NODE *lhsn = orig_lhsn;
5882
5883#define MEMORY(v) { \
5884 int i; \
5885 if (memindex == memsize) return 0; \
5886 for (i=0; i<memindex; i++) { \
5887 if (mem[i] == (v)) return 0; \
5888 } \
5889 mem[memindex++] = (v); \
5890}
5891
5892 if (rhsn == 0 || !nd_type_p(rhsn, NODE_LIST)) {
5893 return 0;
5894 }
5895
5896 while (lhsn) {
5897 const NODE *ln = RNODE_LIST(lhsn)->nd_head;
5898 switch (nd_type(ln)) {
5899 case NODE_LASGN:
5900 case NODE_DASGN:
5901 case NODE_IASGN:
5902 case NODE_CVASGN:
5903 MEMORY(get_nd_vid(ln));
5904 break;
5905 default:
5906 return 0;
5907 }
5908 lhsn = RNODE_LIST(lhsn)->nd_next;
5909 llen++;
5910 }
5911
5912 while (rhsn) {
5913 if (llen <= rlen) {
5914 NO_CHECK(COMPILE_POPPED(ret, "masgn val (popped)", RNODE_LIST(rhsn)->nd_head));
5915 }
5916 else {
5917 NO_CHECK(COMPILE(ret, "masgn val", RNODE_LIST(rhsn)->nd_head));
5918 }
5919 rhsn = RNODE_LIST(rhsn)->nd_next;
5920 rlen++;
5921 }
5922
5923 if (llen > rlen) {
5924 for (i=0; i<llen-rlen; i++) {
5925 ADD_INSN(ret, orig_lhsn, putnil);
5926 }
5927 }
5928
5929 compile_massign_opt_lhs(iseq, ret, orig_lhsn);
5930 return 1;
5931}
5932
5933static int
5934compile_massign0(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const rhs, LINK_ANCHOR *const lhs, LINK_ANCHOR *const post, const NODE *const node, struct masgn_state *state, int popped)
5935{
5936 const NODE *rhsn = RNODE_MASGN(node)->nd_value;
5937 const NODE *splatn = RNODE_MASGN(node)->nd_args;
5938 const NODE *lhsn = RNODE_MASGN(node)->nd_head;
5939 const NODE *lhsn_count = lhsn;
5940 int lhs_splat = (splatn && NODE_NAMED_REST_P(splatn)) ? 1 : 0;
5941
5942 int llen = 0;
5943 int lpos = 0;
5944
5945 while (lhsn_count) {
5946 llen++;
5947 lhsn_count = RNODE_LIST(lhsn_count)->nd_next;
5948 }
5949 while (lhsn) {
5950 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, RNODE_LIST(lhsn)->nd_head, state, (llen - lpos) + lhs_splat + state->lhs_level));
5951 lpos++;
5952 lhsn = RNODE_LIST(lhsn)->nd_next;
5953 }
5954
5955 if (lhs_splat) {
5956 if (nd_type_p(splatn, NODE_POSTARG)) {
5957 /*a, b, *r, p1, p2 */
5958 const NODE *postn = RNODE_POSTARG(splatn)->nd_2nd;
5959 const NODE *restn = RNODE_POSTARG(splatn)->nd_1st;
5960 int plen = (int)RNODE_LIST(postn)->as.nd_alen;
5961 int ppos = 0;
5962 int flag = 0x02 | (NODE_NAMED_REST_P(restn) ? 0x01 : 0x00);
5963
5964 ADD_INSN2(lhs, splatn, expandarray, INT2FIX(plen), INT2FIX(flag));
5965
5966 if (NODE_NAMED_REST_P(restn)) {
5967 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, restn, state, 1 + plen + state->lhs_level));
5968 }
5969 while (postn) {
5970 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, RNODE_LIST(postn)->nd_head, state, (plen - ppos) + state->lhs_level));
5971 ppos++;
5972 postn = RNODE_LIST(postn)->nd_next;
5973 }
5974 }
5975 else {
5976 /* a, b, *r */
5977 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, splatn, state, 1 + state->lhs_level));
5978 }
5979 }
5980
5981 if (!state->nested) {
5982 NO_CHECK(COMPILE(rhs, "normal masgn rhs", rhsn));
5983 }
5984
5985 if (!popped) {
5986 ADD_INSN(rhs, node, dup);
5987 }
5988 ADD_INSN2(rhs, node, expandarray, INT2FIX(llen), INT2FIX(lhs_splat));
5989 return COMPILE_OK;
5990}
5991
5992static int
5993compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
5994{
5995 if (!popped || RNODE_MASGN(node)->nd_args || !compile_massign_opt(iseq, ret, RNODE_MASGN(node)->nd_value, RNODE_MASGN(node)->nd_head)) {
5996 struct masgn_state state;
5997 state.lhs_level = popped ? 0 : 1;
5998 state.nested = 0;
5999 state.num_args = 0;
6000 state.first_memo = NULL;
6001 state.last_memo = NULL;
6002
6003 DECL_ANCHOR(pre);
6004 INIT_ANCHOR(pre);
6005 DECL_ANCHOR(rhs);
6006 INIT_ANCHOR(rhs);
6007 DECL_ANCHOR(lhs);
6008 INIT_ANCHOR(lhs);
6009 DECL_ANCHOR(post);
6010 INIT_ANCHOR(post);
6011 int ok = compile_massign0(iseq, pre, rhs, lhs, post, node, &state, popped);
6012
6013 struct masgn_lhs_node *memo = state.first_memo, *tmp_memo;
6014 while (memo) {
6015 VALUE topn_arg = INT2FIX((state.num_args - memo->argn) + memo->lhs_pos);
6016 for (int i = 0; i < memo->num_args; i++) {
6017 INSERT_BEFORE_INSN1(memo->before_insn, nd_line(memo->line_node), nd_node_id(memo->line_node), topn, topn_arg);
6018 }
6019 tmp_memo = memo->next;
6020 free(memo);
6021 memo = tmp_memo;
6022 }
6023 CHECK(ok);
6024
6025 ADD_SEQ(ret, pre);
6026 ADD_SEQ(ret, rhs);
6027 ADD_SEQ(ret, lhs);
6028 if (!popped && state.num_args >= 1) {
6029 /* make sure rhs array is returned before popping */
6030 ADD_INSN1(ret, node, setn, INT2FIX(state.num_args));
6031 }
6032 ADD_SEQ(ret, post);
6033 }
6034 return COMPILE_OK;
6035}
6036
6037static VALUE
6038collect_const_segments(rb_iseq_t *iseq, const NODE *node)
6039{
6040 VALUE arr = rb_ary_new();
6041 for (;;) {
6042 switch (nd_type(node)) {
6043 case NODE_CONST:
6044 rb_ary_unshift(arr, ID2SYM(RNODE_CONST(node)->nd_vid));
6045 RB_OBJ_SET_SHAREABLE(arr);
6046 return arr;
6047 case NODE_COLON3:
6048 rb_ary_unshift(arr, ID2SYM(RNODE_COLON3(node)->nd_mid));
6049 rb_ary_unshift(arr, ID2SYM(idNULL));
6050 RB_OBJ_SET_SHAREABLE(arr);
6051 return arr;
6052 case NODE_COLON2:
6053 rb_ary_unshift(arr, ID2SYM(RNODE_COLON2(node)->nd_mid));
6054 node = RNODE_COLON2(node)->nd_head;
6055 break;
6056 default:
6057 return Qfalse;
6058 }
6059 }
6060}
6061
6062static int
6063compile_const_prefix(rb_iseq_t *iseq, const NODE *const node,
6064 LINK_ANCHOR *const pref, LINK_ANCHOR *const body)
6065{
6066 switch (nd_type(node)) {
6067 case NODE_CONST:
6068 debugi("compile_const_prefix - colon", RNODE_CONST(node)->nd_vid);
6069 ADD_INSN1(body, node, putobject, Qtrue);
6070 ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_CONST(node)->nd_vid));
6071 break;
6072 case NODE_COLON3:
6073 debugi("compile_const_prefix - colon3", RNODE_COLON3(node)->nd_mid);
6074 ADD_INSN(body, node, pop);
6075 ADD_INSN1(body, node, putobject, rb_cObject);
6076 ADD_INSN1(body, node, putobject, Qtrue);
6077 ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_COLON3(node)->nd_mid));
6078 break;
6079 case NODE_COLON2:
6080 CHECK(compile_const_prefix(iseq, RNODE_COLON2(node)->nd_head, pref, body));
6081 debugi("compile_const_prefix - colon2", RNODE_COLON2(node)->nd_mid);
6082 ADD_INSN1(body, node, putobject, Qfalse);
6083 ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_COLON2(node)->nd_mid));
6084 break;
6085 default:
6086 CHECK(COMPILE(pref, "const colon2 prefix", node));
6087 break;
6088 }
6089 return COMPILE_OK;
6090}
6091
6092static int
6093compile_cpath(LINK_ANCHOR *const ret, rb_iseq_t *iseq, const NODE *cpath)
6094{
6095 if (nd_type_p(cpath, NODE_COLON3)) {
6096 /* toplevel class ::Foo */
6097 ADD_INSN1(ret, cpath, putobject, rb_cObject);
6098 return VM_DEFINECLASS_FLAG_SCOPED;
6099 }
6100 else if (nd_type_p(cpath, NODE_COLON2) && RNODE_COLON2(cpath)->nd_head) {
6101 /* Bar::Foo */
6102 NO_CHECK(COMPILE(ret, "nd_else->nd_head", RNODE_COLON2(cpath)->nd_head));
6103 return VM_DEFINECLASS_FLAG_SCOPED;
6104 }
6105 else {
6106 /* class at cbase Foo */
6107 ADD_INSN1(ret, cpath, putspecialobject,
6108 INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
6109 return 0;
6110 }
6111}
6112
6113static inline int
6114private_recv_p(const NODE *node)
6115{
6116 NODE *recv = get_nd_recv(node);
6117 if (recv && nd_type_p(recv, NODE_SELF)) {
6118 return RNODE_SELF(recv)->nd_state != 0;
6119 }
6120 return 0;
6121}
6122
6123static void
6124defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
6125 const NODE *const node, LABEL **lfinish, VALUE needstr, bool ignore);
6126
6127static int
6128compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const enum node_type type, const NODE *const line_node, int popped, bool assume_receiver);
6129
6130static void
6131defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
6132 const NODE *const node, LABEL **lfinish, VALUE needstr,
6133 bool keep_result)
6134{
6135 enum defined_type expr_type = DEFINED_NOT_DEFINED;
6136 enum node_type type;
6137 const int line = nd_line(node);
6138 const NODE *line_node = node;
6139
6140 switch (type = nd_type(node)) {
6141
6142 /* easy literals */
6143 case NODE_NIL:
6144 expr_type = DEFINED_NIL;
6145 break;
6146 case NODE_SELF:
6147 expr_type = DEFINED_SELF;
6148 break;
6149 case NODE_TRUE:
6150 expr_type = DEFINED_TRUE;
6151 break;
6152 case NODE_FALSE:
6153 expr_type = DEFINED_FALSE;
6154 break;
6155
6156 case NODE_HASH:
6157 case NODE_LIST:{
6158 const NODE *vals = (nd_type(node) == NODE_HASH) ? RNODE_HASH(node)->nd_head : node;
6159
6160 if (vals) {
6161 do {
6162 if (RNODE_LIST(vals)->nd_head) {
6163 defined_expr0(iseq, ret, RNODE_LIST(vals)->nd_head, lfinish, Qfalse, false);
6164
6165 if (!lfinish[1]) {
6166 lfinish[1] = NEW_LABEL(line);
6167 }
6168 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6169 }
6170 } while ((vals = RNODE_LIST(vals)->nd_next) != NULL);
6171 }
6172 }
6173 /* fall through */
6174 case NODE_STR:
6175 case NODE_SYM:
6176 case NODE_REGX:
6177 case NODE_LINE:
6178 case NODE_FILE:
6179 case NODE_ENCODING:
6180 case NODE_INTEGER:
6181 case NODE_FLOAT:
6182 case NODE_RATIONAL:
6183 case NODE_IMAGINARY:
6184 case NODE_ZLIST:
6185 case NODE_AND:
6186 case NODE_OR:
6187 default:
6188 expr_type = DEFINED_EXPR;
6189 break;
6190
6191 case NODE_SPLAT:
6192 defined_expr0(iseq, ret, RNODE_LIST(node)->nd_head, lfinish, Qfalse, false);
6193 if (!lfinish[1]) {
6194 lfinish[1] = NEW_LABEL(line);
6195 }
6196 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6197 expr_type = DEFINED_EXPR;
6198 break;
6199
6200 /* variables */
6201 case NODE_LVAR:
6202 case NODE_DVAR:
6203 expr_type = DEFINED_LVAR;
6204 break;
6205
6206#define PUSH_VAL(type) (needstr == Qfalse ? Qtrue : rb_iseq_defined_string(type))
6207 case NODE_IVAR:
6208 ADD_INSN3(ret, line_node, definedivar,
6209 ID2SYM(RNODE_IVAR(node)->nd_vid), get_ivar_ic_value(iseq,RNODE_IVAR(node)->nd_vid), PUSH_VAL(DEFINED_IVAR));
6210 return;
6211
6212 case NODE_GVAR:
6213 ADD_INSN(ret, line_node, putnil);
6214 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_GVAR),
6215 ID2SYM(RNODE_GVAR(node)->nd_vid), PUSH_VAL(DEFINED_GVAR));
6216 return;
6217
6218 case NODE_CVAR:
6219 ADD_INSN(ret, line_node, putnil);
6220 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CVAR),
6221 ID2SYM(RNODE_CVAR(node)->nd_vid), PUSH_VAL(DEFINED_CVAR));
6222 return;
6223
6224 case NODE_CONST:
6225 ADD_INSN(ret, line_node, putnil);
6226 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST),
6227 ID2SYM(RNODE_CONST(node)->nd_vid), PUSH_VAL(DEFINED_CONST));
6228 return;
6229 case NODE_COLON2:
6230 if (!lfinish[1]) {
6231 lfinish[1] = NEW_LABEL(line);
6232 }
6233 defined_expr0(iseq, ret, RNODE_COLON2(node)->nd_head, lfinish, Qfalse, false);
6234 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6235 NO_CHECK(COMPILE(ret, "defined/colon2#nd_head", RNODE_COLON2(node)->nd_head));
6236
6237 if (rb_is_const_id(RNODE_COLON2(node)->nd_mid)) {
6238 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST_FROM),
6239 ID2SYM(RNODE_COLON2(node)->nd_mid), PUSH_VAL(DEFINED_CONST));
6240 }
6241 else {
6242 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD),
6243 ID2SYM(RNODE_COLON2(node)->nd_mid), PUSH_VAL(DEFINED_METHOD));
6244 }
6245 return;
6246 case NODE_COLON3:
6247 ADD_INSN1(ret, line_node, putobject, rb_cObject);
6248 ADD_INSN3(ret, line_node, defined,
6249 INT2FIX(DEFINED_CONST_FROM), ID2SYM(RNODE_COLON3(node)->nd_mid), PUSH_VAL(DEFINED_CONST));
6250 return;
6251
6252 /* method dispatch */
6253 case NODE_CALL:
6254 case NODE_OPCALL:
6255 case NODE_VCALL:
6256 case NODE_FCALL:
6257 case NODE_ATTRASGN:{
6258 const int explicit_receiver =
6259 (type == NODE_CALL || type == NODE_OPCALL ||
6260 (type == NODE_ATTRASGN && !private_recv_p(node)));
6261
6262 if (get_nd_args(node) || explicit_receiver) {
6263 if (!lfinish[1]) {
6264 lfinish[1] = NEW_LABEL(line);
6265 }
6266 if (!lfinish[2]) {
6267 lfinish[2] = NEW_LABEL(line);
6268 }
6269 }
6270 if (get_nd_args(node)) {
6271 defined_expr0(iseq, ret, get_nd_args(node), lfinish, Qfalse, false);
6272 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6273 }
6274 if (explicit_receiver) {
6275 defined_expr0(iseq, ret, get_nd_recv(node), lfinish, Qfalse, true);
6276 switch (nd_type(get_nd_recv(node))) {
6277 case NODE_CALL:
6278 case NODE_OPCALL:
6279 case NODE_VCALL:
6280 case NODE_FCALL:
6281 case NODE_ATTRASGN:
6282 ADD_INSNL(ret, line_node, branchunless, lfinish[2]);
6283 compile_call(iseq, ret, get_nd_recv(node), nd_type(get_nd_recv(node)), line_node, 0, true);
6284 break;
6285 default:
6286 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6287 NO_CHECK(COMPILE(ret, "defined/recv", get_nd_recv(node)));
6288 break;
6289 }
6290 if (keep_result) {
6291 ADD_INSN(ret, line_node, dup);
6292 }
6293 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD),
6294 ID2SYM(get_node_call_nd_mid(node)), PUSH_VAL(DEFINED_METHOD));
6295 }
6296 else {
6297 ADD_INSN(ret, line_node, putself);
6298 if (keep_result) {
6299 ADD_INSN(ret, line_node, dup);
6300 }
6301 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_FUNC),
6302 ID2SYM(get_node_call_nd_mid(node)), PUSH_VAL(DEFINED_METHOD));
6303 }
6304 return;
6305 }
6306
6307 case NODE_YIELD:
6308 ADD_INSN(ret, line_node, putnil);
6309 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_YIELD), 0,
6310 PUSH_VAL(DEFINED_YIELD));
6311 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
6312 return;
6313
6314 case NODE_BACK_REF:
6315 case NODE_NTH_REF:
6316 ADD_INSN(ret, line_node, putnil);
6317 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_REF),
6318 INT2FIX((RNODE_BACK_REF(node)->nd_nth << 1) | (type == NODE_BACK_REF)),
6319 PUSH_VAL(DEFINED_GVAR));
6320 return;
6321
6322 case NODE_SUPER:
6323 case NODE_ZSUPER:
6324 ADD_INSN(ret, line_node, putnil);
6325 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_ZSUPER), 0,
6326 PUSH_VAL(DEFINED_ZSUPER));
6327 return;
6328
6329#undef PUSH_VAL
6330 case NODE_OP_ASGN1:
6331 case NODE_OP_ASGN2:
6332 case NODE_OP_ASGN_OR:
6333 case NODE_OP_ASGN_AND:
6334 case NODE_MASGN:
6335 case NODE_LASGN:
6336 case NODE_DASGN:
6337 case NODE_GASGN:
6338 case NODE_IASGN:
6339 case NODE_CDECL:
6340 case NODE_CVASGN:
6341 case NODE_OP_CDECL:
6342 expr_type = DEFINED_ASGN;
6343 break;
6344 }
6345
6346 RUBY_ASSERT(expr_type != DEFINED_NOT_DEFINED);
6347
6348 if (needstr != Qfalse) {
6349 VALUE str = rb_iseq_defined_string(expr_type);
6350 ADD_INSN1(ret, line_node, putobject, str);
6351 }
6352 else {
6353 ADD_INSN1(ret, line_node, putobject, Qtrue);
6354 }
6355}
6356
6357static void
6358build_defined_rescue_iseq(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const void *unused)
6359{
6360 ADD_SYNTHETIC_INSN(ret, 0, -1, putnil);
6361 iseq_set_exception_local_table(iseq);
6362}
6363
6364static void
6365defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
6366 const NODE *const node, LABEL **lfinish, VALUE needstr, bool ignore)
6367{
6368 LINK_ELEMENT *lcur = ret->last;
6369 defined_expr0(iseq, ret, node, lfinish, needstr, false);
6370 if (lfinish[1]) {
6371 int line = nd_line(node);
6372 LABEL *lstart = NEW_LABEL(line);
6373 LABEL *lend = NEW_LABEL(line);
6374 const rb_iseq_t *rescue;
6376 rb_iseq_new_with_callback_new_callback(build_defined_rescue_iseq, NULL);
6377 rescue = NEW_CHILD_ISEQ_WITH_CALLBACK(ifunc,
6378 rb_str_concat(rb_str_new2("defined guard in "),
6379 ISEQ_BODY(iseq)->location.label),
6380 ISEQ_TYPE_RESCUE, 0);
6381 lstart->rescued = LABEL_RESCUE_BEG;
6382 lend->rescued = LABEL_RESCUE_END;
6383 APPEND_LABEL(ret, lcur, lstart);
6384 ADD_LABEL(ret, lend);
6385 if (!ignore) {
6386 ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
6387 }
6388 }
6389}
6390
6391static int
6392compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr, bool ignore)
6393{
6394 const int line = nd_line(node);
6395 const NODE *line_node = node;
6396 if (!RNODE_DEFINED(node)->nd_head) {
6397 VALUE str = rb_iseq_defined_string(DEFINED_NIL);
6398 ADD_INSN1(ret, line_node, putobject, str);
6399 }
6400 else {
6401 LABEL *lfinish[3];
6402 LINK_ELEMENT *last = ret->last;
6403 lfinish[0] = NEW_LABEL(line);
6404 lfinish[1] = 0;
6405 lfinish[2] = 0;
6406 defined_expr(iseq, ret, RNODE_DEFINED(node)->nd_head, lfinish, needstr, ignore);
6407 if (lfinish[1]) {
6408 ELEM_INSERT_NEXT(last, &new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(putnil), 0)->link);
6409 ADD_INSN(ret, line_node, swap);
6410 if (lfinish[2]) {
6411 ADD_LABEL(ret, lfinish[2]);
6412 }
6413 ADD_INSN(ret, line_node, pop);
6414 ADD_LABEL(ret, lfinish[1]);
6415 }
6416 ADD_LABEL(ret, lfinish[0]);
6417 }
6418 return COMPILE_OK;
6419}
6420
6421static VALUE
6422make_name_for_block(const rb_iseq_t *orig_iseq)
6423{
6424 int level = 1;
6425 const rb_iseq_t *iseq = orig_iseq;
6426
6427 if (ISEQ_BODY(orig_iseq)->parent_iseq != 0) {
6428 while (ISEQ_BODY(orig_iseq)->local_iseq != iseq) {
6429 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_BLOCK) {
6430 level++;
6431 }
6432 iseq = ISEQ_BODY(iseq)->parent_iseq;
6433 }
6434 }
6435
6436 if (level == 1) {
6437 return rb_sprintf("block in %"PRIsVALUE, ISEQ_BODY(iseq)->location.label);
6438 }
6439 else {
6440 return rb_sprintf("block (%d levels) in %"PRIsVALUE, level, ISEQ_BODY(iseq)->location.label);
6441 }
6442}
6443
6444static void
6445push_ensure_entry(rb_iseq_t *iseq,
6447 struct ensure_range *er, const void *const node)
6448{
6449 enl->ensure_node = node;
6450 enl->prev = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack; /* prev */
6451 enl->erange = er;
6452 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl;
6453}
6454
6455static void
6456add_ensure_range(rb_iseq_t *iseq, struct ensure_range *erange,
6457 LABEL *lstart, LABEL *lend)
6458{
6459 struct ensure_range *ne =
6460 compile_data_alloc_type(iseq, struct ensure_range);
6461
6462 while (erange->next != 0) {
6463 erange = erange->next;
6464 }
6465 ne->next = 0;
6466 ne->begin = lend;
6467 ne->end = erange->end;
6468 erange->end = lstart;
6469
6470 erange->next = ne;
6471}
6472
6473static bool
6474can_add_ensure_iseq(const rb_iseq_t *iseq)
6475{
6477 if (ISEQ_COMPILE_DATA(iseq)->in_rescue && (e = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack) != NULL) {
6478 while (e) {
6479 if (e->ensure_node) return false;
6480 e = e->prev;
6481 }
6482 }
6483 return true;
6484}
6485
6486static void
6487add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return)
6488{
6489 RUBY_ASSERT(can_add_ensure_iseq(iseq));
6490
6492 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
6493 struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
6494 DECL_ANCHOR(ensure);
6495
6496 INIT_ANCHOR(ensure);
6497 while (enlp) {
6498 if (enlp->erange != NULL) {
6499 DECL_ANCHOR(ensure_part);
6500 LABEL *lstart = NEW_LABEL(0);
6501 LABEL *lend = NEW_LABEL(0);
6502 INIT_ANCHOR(ensure_part);
6503
6504 add_ensure_range(iseq, enlp->erange, lstart, lend);
6505
6506 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
6507 ADD_LABEL(ensure_part, lstart);
6508 NO_CHECK(COMPILE_POPPED(ensure_part, "ensure part", enlp->ensure_node));
6509 ADD_LABEL(ensure_part, lend);
6510 ADD_SEQ(ensure, ensure_part);
6511 }
6512 else {
6513 if (!is_return) {
6514 break;
6515 }
6516 }
6517 enlp = enlp->prev;
6518 }
6519 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = prev_enlp;
6520 ADD_SEQ(ret, ensure);
6521}
6522
6523#if RUBY_DEBUG
6524static int
6525check_keyword(const NODE *node)
6526{
6527 /* This check is essentially a code clone of compile_keyword_arg. */
6528
6529 if (nd_type_p(node, NODE_LIST)) {
6530 while (RNODE_LIST(node)->nd_next) {
6531 node = RNODE_LIST(node)->nd_next;
6532 }
6533 node = RNODE_LIST(node)->nd_head;
6534 }
6535
6536 return keyword_node_p(node);
6537}
6538#endif
6539
6540static bool
6541keyword_node_single_splat_p(NODE *kwnode)
6542{
6543 RUBY_ASSERT(keyword_node_p(kwnode));
6544
6545 NODE *node = RNODE_HASH(kwnode)->nd_head;
6546 return RNODE_LIST(node)->nd_head == NULL &&
6547 RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next == NULL;
6548}
6549
6550static void
6551compile_single_keyword_splat_mutable(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
6552 NODE *kwnode, unsigned int *flag_ptr)
6553{
6554 *flag_ptr |= VM_CALL_KW_SPLAT_MUT;
6555 ADD_INSN1(args, argn, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6556 ADD_INSN1(args, argn, newhash, INT2FIX(0));
6557 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6558 ADD_SEND(args, argn, id_core_hash_merge_kwd, INT2FIX(2));
6559}
6560
6561#define SPLATARRAY_FALSE 0
6562#define SPLATARRAY_TRUE 1
6563#define DUP_SINGLE_KW_SPLAT 2
6564
6565static int
6566setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
6567 unsigned int *dup_rest, unsigned int *flag_ptr, struct rb_callinfo_kwarg **kwarg_ptr)
6568{
6569 if (!argn) return 0;
6570
6571 NODE *kwnode = NULL;
6572
6573 switch (nd_type(argn)) {
6574 case NODE_LIST: {
6575 // f(x, y, z)
6576 int len = compile_args(iseq, args, argn, &kwnode);
6577 RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_ARGS_SPLAT) == 0);
6578
6579 if (kwnode) {
6580 if (compile_keyword_arg(iseq, args, kwnode, kwarg_ptr, flag_ptr)) {
6581 len -= 1;
6582 }
6583 else {
6584 if (keyword_node_single_splat_p(kwnode) && (*dup_rest & DUP_SINGLE_KW_SPLAT)) {
6585 compile_single_keyword_splat_mutable(iseq, args, argn, kwnode, flag_ptr);
6586 }
6587 else {
6588 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6589 }
6590 }
6591 }
6592
6593 return len;
6594 }
6595 case NODE_SPLAT: {
6596 // f(*a)
6597 NO_CHECK(COMPILE(args, "args (splat)", RNODE_SPLAT(argn)->nd_head));
6598 ADD_INSN1(args, argn, splatarray, RBOOL(*dup_rest & SPLATARRAY_TRUE));
6599 if (*dup_rest & SPLATARRAY_TRUE) *dup_rest &= ~SPLATARRAY_TRUE;
6600 if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT;
6601 RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_KW_SPLAT) == 0);
6602 return 1;
6603 }
6604 case NODE_ARGSCAT: {
6605 if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT;
6606 int argc = setup_args_core(iseq, args, RNODE_ARGSCAT(argn)->nd_head, dup_rest, NULL, NULL);
6607 bool args_pushed = false;
6608
6609 if (nd_type_p(RNODE_ARGSCAT(argn)->nd_body, NODE_LIST)) {
6610 int rest_len = compile_args(iseq, args, RNODE_ARGSCAT(argn)->nd_body, &kwnode);
6611 if (kwnode) rest_len--;
6612 ADD_INSN1(args, argn, pushtoarray, INT2FIX(rest_len));
6613 args_pushed = true;
6614 }
6615 else {
6616 RUBY_ASSERT(!check_keyword(RNODE_ARGSCAT(argn)->nd_body));
6617 NO_CHECK(COMPILE(args, "args (cat: splat)", RNODE_ARGSCAT(argn)->nd_body));
6618 }
6619
6620 if (nd_type_p(RNODE_ARGSCAT(argn)->nd_head, NODE_LIST)) {
6621 ADD_INSN1(args, argn, splatarray, RBOOL(*dup_rest & SPLATARRAY_TRUE));
6622 if (*dup_rest & SPLATARRAY_TRUE) *dup_rest &= ~SPLATARRAY_TRUE;
6623 argc += 1;
6624 }
6625 else if (!args_pushed) {
6626 ADD_INSN(args, argn, concattoarray);
6627 }
6628
6629 // f(..., *a, ..., k1:1, ...) #=> f(..., *[*a, ...], **{k1:1, ...})
6630 if (kwnode) {
6631 // kwsplat
6632 *flag_ptr |= VM_CALL_KW_SPLAT;
6633 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6634 argc += 1;
6635 }
6636
6637 return argc;
6638 }
6639 case NODE_ARGSPUSH: {
6640 if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT;
6641 int argc = setup_args_core(iseq, args, RNODE_ARGSPUSH(argn)->nd_head, dup_rest, NULL, NULL);
6642
6643 if (nd_type_p(RNODE_ARGSPUSH(argn)->nd_body, NODE_LIST)) {
6644 int rest_len = compile_args(iseq, args, RNODE_ARGSPUSH(argn)->nd_body, &kwnode);
6645 if (kwnode) rest_len--;
6646 ADD_INSN1(args, argn, newarray, INT2FIX(rest_len));
6647 ADD_INSN1(args, argn, pushtoarray, INT2FIX(1));
6648 }
6649 else {
6650 if (keyword_node_p(RNODE_ARGSPUSH(argn)->nd_body)) {
6651 kwnode = RNODE_ARGSPUSH(argn)->nd_body;
6652 }
6653 else {
6654 NO_CHECK(COMPILE(args, "args (cat: splat)", RNODE_ARGSPUSH(argn)->nd_body));
6655 ADD_INSN1(args, argn, pushtoarray, INT2FIX(1));
6656 }
6657 }
6658
6659 if (kwnode) {
6660 // f(*a, k:1)
6661 *flag_ptr |= VM_CALL_KW_SPLAT;
6662 if (!keyword_node_single_splat_p(kwnode)) {
6663 *flag_ptr |= VM_CALL_KW_SPLAT_MUT;
6664 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6665 }
6666 else if (*dup_rest & DUP_SINGLE_KW_SPLAT) {
6667 compile_single_keyword_splat_mutable(iseq, args, argn, kwnode, flag_ptr);
6668 }
6669 else {
6670 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6671 }
6672 argc += 1;
6673 }
6674
6675 return argc;
6676 }
6677 default: {
6678 UNKNOWN_NODE("setup_arg", argn, Qnil);
6679 }
6680 }
6681}
6682
6683static void
6684setup_args_splat_mut(unsigned int *flag, int dup_rest, int initial_dup_rest)
6685{
6686 if ((*flag & VM_CALL_ARGS_SPLAT) && dup_rest != initial_dup_rest) {
6687 *flag |= VM_CALL_ARGS_SPLAT_MUT;
6688 }
6689}
6690
6691static bool
6692setup_args_dup_rest_p(const NODE *argn)
6693{
6694 switch(nd_type(argn)) {
6695 case NODE_LVAR:
6696 case NODE_DVAR:
6697 case NODE_GVAR:
6698 case NODE_IVAR:
6699 case NODE_CVAR:
6700 case NODE_CONST:
6701 case NODE_COLON3:
6702 case NODE_INTEGER:
6703 case NODE_FLOAT:
6704 case NODE_RATIONAL:
6705 case NODE_IMAGINARY:
6706 case NODE_STR:
6707 case NODE_SYM:
6708 case NODE_REGX:
6709 case NODE_SELF:
6710 case NODE_NIL:
6711 case NODE_TRUE:
6712 case NODE_FALSE:
6713 case NODE_LAMBDA:
6714 case NODE_NTH_REF:
6715 case NODE_BACK_REF:
6716 return false;
6717 case NODE_COLON2:
6718 return setup_args_dup_rest_p(RNODE_COLON2(argn)->nd_head);
6719 case NODE_LIST:
6720 while (argn) {
6721 if (setup_args_dup_rest_p(RNODE_LIST(argn)->nd_head)) {
6722 return true;
6723 }
6724 argn = RNODE_LIST(argn)->nd_next;
6725 }
6726 return false;
6727 default:
6728 return true;
6729 }
6730}
6731
6732static VALUE
6733setup_args(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
6734 unsigned int *flag, struct rb_callinfo_kwarg **keywords)
6735{
6736 VALUE ret;
6737 unsigned int dup_rest = SPLATARRAY_TRUE, initial_dup_rest;
6738
6739 if (argn) {
6740 const NODE *check_arg = nd_type_p(argn, NODE_BLOCK_PASS) ?
6741 RNODE_BLOCK_PASS(argn)->nd_head : argn;
6742
6743 if (check_arg) {
6744 switch(nd_type(check_arg)) {
6745 case(NODE_SPLAT):
6746 // avoid caller side array allocation for f(*arg)
6747 dup_rest = SPLATARRAY_FALSE;
6748 break;
6749 case(NODE_ARGSCAT):
6750 // avoid caller side array allocation for f(1, *arg)
6751 dup_rest = !nd_type_p(RNODE_ARGSCAT(check_arg)->nd_head, NODE_LIST);
6752 break;
6753 case(NODE_ARGSPUSH):
6754 // avoid caller side array allocation for f(*arg, **hash) and f(1, *arg, **hash)
6755 dup_rest = !((nd_type_p(RNODE_ARGSPUSH(check_arg)->nd_head, NODE_SPLAT) ||
6756 (nd_type_p(RNODE_ARGSPUSH(check_arg)->nd_head, NODE_ARGSCAT) &&
6757 nd_type_p(RNODE_ARGSCAT(RNODE_ARGSPUSH(check_arg)->nd_head)->nd_head, NODE_LIST))) &&
6758 nd_type_p(RNODE_ARGSPUSH(check_arg)->nd_body, NODE_HASH) &&
6759 !RNODE_HASH(RNODE_ARGSPUSH(check_arg)->nd_body)->nd_brace);
6760
6761 if (dup_rest == SPLATARRAY_FALSE) {
6762 // require allocation for keyword key/value/splat that may modify splatted argument
6763 NODE *node = RNODE_HASH(RNODE_ARGSPUSH(check_arg)->nd_body)->nd_head;
6764 while (node) {
6765 NODE *key_node = RNODE_LIST(node)->nd_head;
6766 if (key_node && setup_args_dup_rest_p(key_node)) {
6767 dup_rest = SPLATARRAY_TRUE;
6768 break;
6769 }
6770
6771 node = RNODE_LIST(node)->nd_next;
6772 NODE *value_node = RNODE_LIST(node)->nd_head;
6773 if (setup_args_dup_rest_p(value_node)) {
6774 dup_rest = SPLATARRAY_TRUE;
6775 break;
6776 }
6777
6778 node = RNODE_LIST(node)->nd_next;
6779 }
6780 }
6781 break;
6782 default:
6783 break;
6784 }
6785 }
6786
6787 if (check_arg != argn && setup_args_dup_rest_p(RNODE_BLOCK_PASS(argn)->nd_body)) {
6788 // for block pass that may modify splatted argument, dup rest and kwrest if given
6789 dup_rest = SPLATARRAY_TRUE | DUP_SINGLE_KW_SPLAT;
6790 }
6791 }
6792 initial_dup_rest = dup_rest;
6793
6794 if (argn && nd_type_p(argn, NODE_BLOCK_PASS)) {
6795 DECL_ANCHOR(arg_block);
6796 INIT_ANCHOR(arg_block);
6797
6798 if (RNODE_BLOCK_PASS(argn)->forwarding && ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
6799 int idx = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size;// - get_local_var_idx(iseq, idDot3);
6800
6801 RUBY_ASSERT(nd_type_p(RNODE_BLOCK_PASS(argn)->nd_head, NODE_ARGSPUSH));
6802 const NODE * arg_node =
6803 RNODE_ARGSPUSH(RNODE_BLOCK_PASS(argn)->nd_head)->nd_head;
6804
6805 int argc = 0;
6806
6807 // Only compile leading args:
6808 // foo(x, y, ...)
6809 // ^^^^
6810 if (nd_type_p(arg_node, NODE_ARGSCAT)) {
6811 argc += setup_args_core(iseq, args, RNODE_ARGSCAT(arg_node)->nd_head, &dup_rest, flag, keywords);
6812 }
6813
6814 *flag |= VM_CALL_FORWARDING;
6815
6816 ADD_GETLOCAL(args, argn, idx, get_lvar_level(iseq));
6817 setup_args_splat_mut(flag, dup_rest, initial_dup_rest);
6818 return INT2FIX(argc);
6819 }
6820 else {
6821 *flag |= VM_CALL_ARGS_BLOCKARG;
6822
6823 NO_CHECK(COMPILE(arg_block, "block", RNODE_BLOCK_PASS(argn)->nd_body));
6824 }
6825
6826 if (LIST_INSN_SIZE_ONE(arg_block)) {
6827 LINK_ELEMENT *elem = FIRST_ELEMENT(arg_block);
6828 if (IS_INSN(elem)) {
6829 INSN *iobj = (INSN *)elem;
6830 if (iobj->insn_id == BIN(getblockparam)) {
6831 iobj->insn_id = BIN(getblockparamproxy);
6832 }
6833 }
6834 }
6835 ret = INT2FIX(setup_args_core(iseq, args, RNODE_BLOCK_PASS(argn)->nd_head, &dup_rest, flag, keywords));
6836 ADD_SEQ(args, arg_block);
6837 }
6838 else {
6839 ret = INT2FIX(setup_args_core(iseq, args, argn, &dup_rest, flag, keywords));
6840 }
6841 setup_args_splat_mut(flag, dup_rest, initial_dup_rest);
6842 return ret;
6843}
6844
6845static void
6846build_postexe_iseq(rb_iseq_t *iseq, LINK_ANCHOR *ret, const void *ptr)
6847{
6848 const NODE *body = ptr;
6849 int line = nd_line(body);
6850 VALUE argc = INT2FIX(0);
6851 const rb_iseq_t *block = NEW_CHILD_ISEQ(body, make_name_for_block(ISEQ_BODY(iseq)->parent_iseq), ISEQ_TYPE_BLOCK, line);
6852
6853 ADD_INSN1(ret, body, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6854 ADD_CALL_WITH_BLOCK(ret, body, id_core_set_postexe, argc, block);
6855 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block);
6856 iseq_set_local_table(iseq, 0, 0);
6857}
6858
6859static void
6860compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
6861{
6862 const NODE *vars;
6863 LINK_ELEMENT *last;
6864 int line = nd_line(node);
6865 const NODE *line_node = node;
6866 LABEL *fail_label = NEW_LABEL(line), *end_label = NEW_LABEL(line);
6867
6868#if !(defined(NAMED_CAPTURE_BY_SVAR) && NAMED_CAPTURE_BY_SVAR-0)
6869 ADD_INSN1(ret, line_node, getglobal, ID2SYM(idBACKREF));
6870#else
6871 ADD_INSN2(ret, line_node, getspecial, INT2FIX(1) /* '~' */, INT2FIX(0));
6872#endif
6873 ADD_INSN(ret, line_node, dup);
6874 ADD_INSNL(ret, line_node, branchunless, fail_label);
6875
6876 for (vars = node; vars; vars = RNODE_BLOCK(vars)->nd_next) {
6877 INSN *cap;
6878 if (RNODE_BLOCK(vars)->nd_next) {
6879 ADD_INSN(ret, line_node, dup);
6880 }
6881 last = ret->last;
6882 NO_CHECK(COMPILE_POPPED(ret, "capture", RNODE_BLOCK(vars)->nd_head));
6883 last = last->next; /* putobject :var */
6884 cap = new_insn_send(iseq, nd_line(line_node), nd_node_id(line_node), idAREF, INT2FIX(1),
6885 NULL, INT2FIX(0), NULL);
6886 ELEM_INSERT_PREV(last->next, (LINK_ELEMENT *)cap);
6887#if !defined(NAMED_CAPTURE_SINGLE_OPT) || NAMED_CAPTURE_SINGLE_OPT-0
6888 if (!RNODE_BLOCK(vars)->nd_next && vars == node) {
6889 /* only one name */
6890 DECL_ANCHOR(nom);
6891
6892 INIT_ANCHOR(nom);
6893 ADD_INSNL(nom, line_node, jump, end_label);
6894 ADD_LABEL(nom, fail_label);
6895# if 0 /* $~ must be MatchData or nil */
6896 ADD_INSN(nom, line_node, pop);
6897 ADD_INSN(nom, line_node, putnil);
6898# endif
6899 ADD_LABEL(nom, end_label);
6900 (nom->last->next = cap->link.next)->prev = nom->last;
6901 (cap->link.next = nom->anchor.next)->prev = &cap->link;
6902 return;
6903 }
6904#endif
6905 }
6906 ADD_INSNL(ret, line_node, jump, end_label);
6907 ADD_LABEL(ret, fail_label);
6908 ADD_INSN(ret, line_node, pop);
6909 for (vars = node; vars; vars = RNODE_BLOCK(vars)->nd_next) {
6910 last = ret->last;
6911 NO_CHECK(COMPILE_POPPED(ret, "capture", RNODE_BLOCK(vars)->nd_head));
6912 last = last->next; /* putobject :var */
6913 ((INSN*)last)->insn_id = BIN(putnil);
6914 ((INSN*)last)->operand_size = 0;
6915 }
6916 ADD_LABEL(ret, end_label);
6917}
6918
6919static int
6920optimizable_range_item_p(const NODE *n)
6921{
6922 if (!n) return FALSE;
6923 switch (nd_type(n)) {
6924 case NODE_LINE:
6925 return TRUE;
6926 case NODE_INTEGER:
6927 return TRUE;
6928 case NODE_NIL:
6929 return TRUE;
6930 default:
6931 return FALSE;
6932 }
6933}
6934
6935static VALUE
6936optimized_range_item(const NODE *n)
6937{
6938 switch (nd_type(n)) {
6939 case NODE_LINE:
6940 return rb_node_line_lineno_val(n);
6941 case NODE_INTEGER:
6942 return rb_node_integer_literal_val(n);
6943 case NODE_FLOAT:
6944 return rb_node_float_literal_val(n);
6945 case NODE_RATIONAL:
6946 return rb_node_rational_literal_val(n);
6947 case NODE_IMAGINARY:
6948 return rb_node_imaginary_literal_val(n);
6949 case NODE_NIL:
6950 return Qnil;
6951 default:
6952 rb_bug("unexpected node: %s", ruby_node_name(nd_type(n)));
6953 }
6954}
6955
6956static int
6957compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
6958{
6959 const NODE *const node_body = type == NODE_IF ? RNODE_IF(node)->nd_body : RNODE_UNLESS(node)->nd_else;
6960 const NODE *const node_else = type == NODE_IF ? RNODE_IF(node)->nd_else : RNODE_UNLESS(node)->nd_body;
6961
6962 const int line = nd_line(node);
6963 const NODE *line_node = node;
6964 DECL_ANCHOR(cond_seq);
6965 LABEL *then_label, *else_label, *end_label;
6966 VALUE branches = Qfalse;
6967
6968 INIT_ANCHOR(cond_seq);
6969 then_label = NEW_LABEL(line);
6970 else_label = NEW_LABEL(line);
6971 end_label = 0;
6972
6973 NODE *cond = RNODE_IF(node)->nd_cond;
6974 if (nd_type(cond) == NODE_BLOCK) {
6975 cond = RNODE_BLOCK(cond)->nd_head;
6976 }
6977
6978 CHECK(compile_branch_condition(iseq, cond_seq, cond, then_label, else_label));
6979 ADD_SEQ(ret, cond_seq);
6980
6981 if (then_label->refcnt && else_label->refcnt) {
6982 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_IF ? "if" : "unless");
6983 }
6984
6985 if (then_label->refcnt) {
6986 ADD_LABEL(ret, then_label);
6987
6988 DECL_ANCHOR(then_seq);
6989 INIT_ANCHOR(then_seq);
6990 CHECK(COMPILE_(then_seq, "then", node_body, popped));
6991
6992 if (else_label->refcnt) {
6993 const NODE *const coverage_node = node_body ? node_body : node;
6994 add_trace_branch_coverage(
6995 iseq,
6996 ret,
6997 nd_code_loc(coverage_node),
6998 nd_node_id(coverage_node),
6999 0,
7000 type == NODE_IF ? "then" : "else",
7001 branches);
7002 end_label = NEW_LABEL(line);
7003 ADD_INSNL(then_seq, line_node, jump, end_label);
7004 if (!popped) {
7005 ADD_INSN(then_seq, line_node, pop);
7006 }
7007 }
7008 ADD_SEQ(ret, then_seq);
7009 }
7010
7011 if (else_label->refcnt) {
7012 ADD_LABEL(ret, else_label);
7013
7014 DECL_ANCHOR(else_seq);
7015 INIT_ANCHOR(else_seq);
7016 CHECK(COMPILE_(else_seq, "else", node_else, popped));
7017
7018 if (then_label->refcnt) {
7019 const NODE *const coverage_node = node_else ? node_else : node;
7020 add_trace_branch_coverage(
7021 iseq,
7022 ret,
7023 nd_code_loc(coverage_node),
7024 nd_node_id(coverage_node),
7025 1,
7026 type == NODE_IF ? "else" : "then",
7027 branches);
7028 }
7029 ADD_SEQ(ret, else_seq);
7030 }
7031
7032 if (end_label) {
7033 ADD_LABEL(ret, end_label);
7034 }
7035
7036 return COMPILE_OK;
7037}
7038
7039static int
7040compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
7041{
7042 const NODE *vals;
7043 const NODE *node = orig_node;
7044 LABEL *endlabel, *elselabel;
7045 DECL_ANCHOR(head);
7046 DECL_ANCHOR(body_seq);
7047 DECL_ANCHOR(cond_seq);
7048 int only_special_literals = 1;
7049 VALUE literals = rb_hash_new();
7050 int line;
7051 enum node_type type;
7052 const NODE *line_node;
7053 VALUE branches = Qfalse;
7054 int branch_id = 0;
7055
7056 INIT_ANCHOR(head);
7057 INIT_ANCHOR(body_seq);
7058 INIT_ANCHOR(cond_seq);
7059
7060 RHASH_TBL_RAW(literals)->type = &cdhash_type;
7061
7062 CHECK(COMPILE(head, "case base", RNODE_CASE(node)->nd_head));
7063
7064 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case");
7065
7066 node = RNODE_CASE(node)->nd_body;
7067 EXPECT_NODE("NODE_CASE", node, NODE_WHEN, COMPILE_NG);
7068 type = nd_type(node);
7069 line = nd_line(node);
7070 line_node = node;
7071
7072 endlabel = NEW_LABEL(line);
7073 elselabel = NEW_LABEL(line);
7074
7075 ADD_SEQ(ret, head); /* case VAL */
7076
7077 while (type == NODE_WHEN) {
7078 LABEL *l1;
7079
7080 l1 = NEW_LABEL(line);
7081 ADD_LABEL(body_seq, l1);
7082 ADD_INSN(body_seq, line_node, pop);
7083
7084 const NODE *const coverage_node = RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node;
7085 add_trace_branch_coverage(
7086 iseq,
7087 body_seq,
7088 nd_code_loc(coverage_node),
7089 nd_node_id(coverage_node),
7090 branch_id++,
7091 "when",
7092 branches);
7093
7094 CHECK(COMPILE_(body_seq, "when body", RNODE_WHEN(node)->nd_body, popped));
7095 ADD_INSNL(body_seq, line_node, jump, endlabel);
7096
7097 vals = RNODE_WHEN(node)->nd_head;
7098 if (vals) {
7099 switch (nd_type(vals)) {
7100 case NODE_LIST:
7101 only_special_literals = when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals);
7102 if (only_special_literals < 0) return COMPILE_NG;
7103 break;
7104 case NODE_SPLAT:
7105 case NODE_ARGSCAT:
7106 case NODE_ARGSPUSH:
7107 only_special_literals = 0;
7108 CHECK(when_splat_vals(iseq, cond_seq, vals, l1, only_special_literals, literals));
7109 break;
7110 default:
7111 UNKNOWN_NODE("NODE_CASE", vals, COMPILE_NG);
7112 }
7113 }
7114 else {
7115 EXPECT_NODE_NONULL("NODE_CASE", node, NODE_LIST, COMPILE_NG);
7116 }
7117
7118 node = RNODE_WHEN(node)->nd_next;
7119 if (!node) {
7120 break;
7121 }
7122 type = nd_type(node);
7123 line = nd_line(node);
7124 line_node = node;
7125 }
7126 /* else */
7127 if (node) {
7128 ADD_LABEL(cond_seq, elselabel);
7129 ADD_INSN(cond_seq, line_node, pop);
7130 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(node), nd_node_id(node), branch_id, "else", branches);
7131 CHECK(COMPILE_(cond_seq, "else", node, popped));
7132 ADD_INSNL(cond_seq, line_node, jump, endlabel);
7133 }
7134 else {
7135 debugs("== else (implicit)\n");
7136 ADD_LABEL(cond_seq, elselabel);
7137 ADD_INSN(cond_seq, orig_node, pop);
7138 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(orig_node), nd_node_id(orig_node), branch_id, "else", branches);
7139 if (!popped) {
7140 ADD_INSN(cond_seq, orig_node, putnil);
7141 }
7142 ADD_INSNL(cond_seq, orig_node, jump, endlabel);
7143 }
7144
7145 if (only_special_literals && ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
7146 ADD_INSN(ret, orig_node, dup);
7147 rb_obj_hide(literals);
7148 ADD_INSN2(ret, orig_node, opt_case_dispatch, literals, elselabel);
7149 RB_OBJ_WRITTEN(iseq, Qundef, literals);
7150 LABEL_REF(elselabel);
7151 }
7152
7153 ADD_SEQ(ret, cond_seq);
7154 ADD_SEQ(ret, body_seq);
7155 ADD_LABEL(ret, endlabel);
7156 return COMPILE_OK;
7157}
7158
7159static int
7160compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
7161{
7162 const NODE *vals;
7163 const NODE *val;
7164 const NODE *node = RNODE_CASE2(orig_node)->nd_body;
7165 LABEL *endlabel;
7166 DECL_ANCHOR(body_seq);
7167 VALUE branches = Qfalse;
7168 int branch_id = 0;
7169
7170 branches = decl_branch_base(iseq, PTR2NUM(orig_node), nd_code_loc(orig_node), "case");
7171
7172 INIT_ANCHOR(body_seq);
7173 endlabel = NEW_LABEL(nd_line(node));
7174
7175 while (node && nd_type_p(node, NODE_WHEN)) {
7176 const int line = nd_line(node);
7177 LABEL *l1 = NEW_LABEL(line);
7178 ADD_LABEL(body_seq, l1);
7179
7180 const NODE *const coverage_node = RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node;
7181 add_trace_branch_coverage(
7182 iseq,
7183 body_seq,
7184 nd_code_loc(coverage_node),
7185 nd_node_id(coverage_node),
7186 branch_id++,
7187 "when",
7188 branches);
7189
7190 CHECK(COMPILE_(body_seq, "when", RNODE_WHEN(node)->nd_body, popped));
7191 ADD_INSNL(body_seq, node, jump, endlabel);
7192
7193 vals = RNODE_WHEN(node)->nd_head;
7194 if (!vals) {
7195 EXPECT_NODE_NONULL("NODE_WHEN", node, NODE_LIST, COMPILE_NG);
7196 }
7197 switch (nd_type(vals)) {
7198 case NODE_LIST:
7199 while (vals) {
7200 LABEL *lnext;
7201 val = RNODE_LIST(vals)->nd_head;
7202 lnext = NEW_LABEL(nd_line(val));
7203 debug_compile("== when2\n", (void)0);
7204 CHECK(compile_branch_condition(iseq, ret, val, l1, lnext));
7205 ADD_LABEL(ret, lnext);
7206 vals = RNODE_LIST(vals)->nd_next;
7207 }
7208 break;
7209 case NODE_SPLAT:
7210 case NODE_ARGSCAT:
7211 case NODE_ARGSPUSH:
7212 ADD_INSN(ret, vals, putnil);
7213 CHECK(COMPILE(ret, "when2/cond splat", vals));
7214 ADD_INSN1(ret, vals, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
7215 ADD_INSNL(ret, vals, branchif, l1);
7216 break;
7217 default:
7218 UNKNOWN_NODE("NODE_WHEN", vals, COMPILE_NG);
7219 }
7220 node = RNODE_WHEN(node)->nd_next;
7221 }
7222 /* else */
7223 const NODE *const coverage_node = node ? node : orig_node;
7224 add_trace_branch_coverage(
7225 iseq,
7226 ret,
7227 nd_code_loc(coverage_node),
7228 nd_node_id(coverage_node),
7229 branch_id,
7230 "else",
7231 branches);
7232 CHECK(COMPILE_(ret, "else", node, popped));
7233 ADD_INSNL(ret, orig_node, jump, endlabel);
7234
7235 ADD_SEQ(ret, body_seq);
7236 ADD_LABEL(ret, endlabel);
7237 return COMPILE_OK;
7238}
7239
7240static int iseq_compile_pattern_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *unmatched, bool in_single_pattern, bool in_alt_pattern, int base_index, bool use_deconstructed_cache);
7241
7242static int iseq_compile_pattern_constant(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *match_failed, bool in_single_pattern, int base_index);
7243static int iseq_compile_array_deconstruct(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *deconstruct, LABEL *deconstructed, LABEL *match_failed, LABEL *type_error, bool in_single_pattern, int base_index, bool use_deconstructed_cache);
7244static int iseq_compile_pattern_set_general_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE errmsg, int base_index);
7245static int iseq_compile_pattern_set_length_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE errmsg, VALUE pattern_length, int base_index);
7246static int iseq_compile_pattern_set_eqq_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int base_index);
7247
7248#define CASE3_BI_OFFSET_DECONSTRUCTED_CACHE 0
7249#define CASE3_BI_OFFSET_ERROR_STRING 1
7250#define CASE3_BI_OFFSET_KEY_ERROR_P 2
7251#define CASE3_BI_OFFSET_KEY_ERROR_MATCHEE 3
7252#define CASE3_BI_OFFSET_KEY_ERROR_KEY 4
7253
7254static int
7255iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *matched, LABEL *unmatched, bool in_single_pattern, bool in_alt_pattern, int base_index, bool use_deconstructed_cache)
7256{
7257 const int line = nd_line(node);
7258 const NODE *line_node = node;
7259
7260 switch (nd_type(node)) {
7261 case NODE_ARYPTN: {
7262 /*
7263 * if pattern.use_rest_num?
7264 * rest_num = 0
7265 * end
7266 * if pattern.has_constant_node?
7267 * unless pattern.constant === obj
7268 * goto match_failed
7269 * end
7270 * end
7271 * unless obj.respond_to?(:deconstruct)
7272 * goto match_failed
7273 * end
7274 * d = obj.deconstruct
7275 * unless Array === d
7276 * goto type_error
7277 * end
7278 * min_argc = pattern.pre_args_num + pattern.post_args_num
7279 * if pattern.has_rest_arg?
7280 * unless d.length >= min_argc
7281 * goto match_failed
7282 * end
7283 * else
7284 * unless d.length == min_argc
7285 * goto match_failed
7286 * end
7287 * end
7288 * pattern.pre_args_num.each do |i|
7289 * unless pattern.pre_args[i].match?(d[i])
7290 * goto match_failed
7291 * end
7292 * end
7293 * if pattern.use_rest_num?
7294 * rest_num = d.length - min_argc
7295 * if pattern.has_rest_arg? && pattern.has_rest_arg_id # not `*`, but `*rest`
7296 * unless pattern.rest_arg.match?(d[pattern.pre_args_num, rest_num])
7297 * goto match_failed
7298 * end
7299 * end
7300 * end
7301 * pattern.post_args_num.each do |i|
7302 * j = pattern.pre_args_num + i
7303 * j += rest_num
7304 * unless pattern.post_args[i].match?(d[j])
7305 * goto match_failed
7306 * end
7307 * end
7308 * goto matched
7309 * type_error:
7310 * FrozenCore.raise TypeError
7311 * match_failed:
7312 * goto unmatched
7313 */
7314 const NODE *args = RNODE_ARYPTN(node)->pre_args;
7315 const int pre_args_num = RNODE_ARYPTN(node)->pre_args ? rb_long2int(RNODE_LIST(RNODE_ARYPTN(node)->pre_args)->as.nd_alen) : 0;
7316 const int post_args_num = RNODE_ARYPTN(node)->post_args ? rb_long2int(RNODE_LIST(RNODE_ARYPTN(node)->post_args)->as.nd_alen) : 0;
7317
7318 const int min_argc = pre_args_num + post_args_num;
7319 const int use_rest_num = RNODE_ARYPTN(node)->rest_arg && (NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg) ||
7320 (!NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg) && post_args_num > 0));
7321
7322 LABEL *match_failed, *type_error, *deconstruct, *deconstructed;
7323 int i;
7324 match_failed = NEW_LABEL(line);
7325 type_error = NEW_LABEL(line);
7326 deconstruct = NEW_LABEL(line);
7327 deconstructed = NEW_LABEL(line);
7328
7329 if (use_rest_num) {
7330 ADD_INSN1(ret, line_node, putobject, INT2FIX(0)); /* allocate stack for rest_num */
7331 ADD_INSN(ret, line_node, swap);
7332 if (base_index) {
7333 base_index++;
7334 }
7335 }
7336
7337 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
7338
7339 CHECK(iseq_compile_array_deconstruct(iseq, ret, node, deconstruct, deconstructed, match_failed, type_error, in_single_pattern, base_index, use_deconstructed_cache));
7340
7341 ADD_INSN(ret, line_node, dup);
7342 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
7343 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
7344 ADD_SEND(ret, line_node, RNODE_ARYPTN(node)->rest_arg ? idGE : idEq, INT2FIX(1)); // (1)
7345 if (in_single_pattern) {
7346 CHECK(iseq_compile_pattern_set_length_errmsg(iseq, ret, node,
7347 RNODE_ARYPTN(node)->rest_arg ? rb_fstring_lit("%p length mismatch (given %p, expected %p+)") :
7348 rb_fstring_lit("%p length mismatch (given %p, expected %p)"),
7349 INT2FIX(min_argc), base_index + 1 /* (1) */));
7350 }
7351 ADD_INSNL(ret, line_node, branchunless, match_failed);
7352
7353 for (i = 0; i < pre_args_num; i++) {
7354 ADD_INSN(ret, line_node, dup);
7355 ADD_INSN1(ret, line_node, putobject, INT2FIX(i));
7356 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (2)
7357 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(args)->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (2) */, false));
7358 args = RNODE_LIST(args)->nd_next;
7359 }
7360
7361 if (RNODE_ARYPTN(node)->rest_arg) {
7362 if (NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg)) {
7363 ADD_INSN(ret, line_node, dup);
7364 ADD_INSN1(ret, line_node, putobject, INT2FIX(pre_args_num));
7365 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
7366 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
7367 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
7368 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1));
7369 ADD_INSN1(ret, line_node, setn, INT2FIX(4));
7370 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (3)
7371
7372 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_ARYPTN(node)->rest_arg, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (3) */, false));
7373 }
7374 else {
7375 if (post_args_num > 0) {
7376 ADD_INSN(ret, line_node, dup);
7377 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
7378 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
7379 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1));
7380 ADD_INSN1(ret, line_node, setn, INT2FIX(2));
7381 ADD_INSN(ret, line_node, pop);
7382 }
7383 }
7384 }
7385
7386 args = RNODE_ARYPTN(node)->post_args;
7387 for (i = 0; i < post_args_num; i++) {
7388 ADD_INSN(ret, line_node, dup);
7389
7390 ADD_INSN1(ret, line_node, putobject, INT2FIX(pre_args_num + i));
7391 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7392 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
7393
7394 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (4)
7395 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(args)->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (4) */, false));
7396 args = RNODE_LIST(args)->nd_next;
7397 }
7398
7399 ADD_INSN(ret, line_node, pop);
7400 if (use_rest_num) {
7401 ADD_INSN(ret, line_node, pop);
7402 }
7403 ADD_INSNL(ret, line_node, jump, matched);
7404 ADD_INSN(ret, line_node, putnil);
7405 if (use_rest_num) {
7406 ADD_INSN(ret, line_node, putnil);
7407 }
7408
7409 ADD_LABEL(ret, type_error);
7410 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7411 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
7412 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct must return Array"));
7413 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
7414 ADD_INSN(ret, line_node, pop);
7415
7416 ADD_LABEL(ret, match_failed);
7417 ADD_INSN(ret, line_node, pop);
7418 if (use_rest_num) {
7419 ADD_INSN(ret, line_node, pop);
7420 }
7421 ADD_INSNL(ret, line_node, jump, unmatched);
7422
7423 break;
7424 }
7425 case NODE_FNDPTN: {
7426 /*
7427 * if pattern.has_constant_node?
7428 * unless pattern.constant === obj
7429 * goto match_failed
7430 * end
7431 * end
7432 * unless obj.respond_to?(:deconstruct)
7433 * goto match_failed
7434 * end
7435 * d = obj.deconstruct
7436 * unless Array === d
7437 * goto type_error
7438 * end
7439 * unless d.length >= pattern.args_num
7440 * goto match_failed
7441 * end
7442 *
7443 * begin
7444 * len = d.length
7445 * limit = d.length - pattern.args_num
7446 * i = 0
7447 * while i <= limit
7448 * if pattern.args_num.times.all? {|j| pattern.args[j].match?(d[i+j]) }
7449 * if pattern.has_pre_rest_arg_id
7450 * unless pattern.pre_rest_arg.match?(d[0, i])
7451 * goto find_failed
7452 * end
7453 * end
7454 * if pattern.has_post_rest_arg_id
7455 * unless pattern.post_rest_arg.match?(d[i+pattern.args_num, len])
7456 * goto find_failed
7457 * end
7458 * end
7459 * goto find_succeeded
7460 * end
7461 * i+=1
7462 * end
7463 * find_failed:
7464 * goto match_failed
7465 * find_succeeded:
7466 * end
7467 *
7468 * goto matched
7469 * type_error:
7470 * FrozenCore.raise TypeError
7471 * match_failed:
7472 * goto unmatched
7473 */
7474 const NODE *args = RNODE_FNDPTN(node)->args;
7475 const int args_num = RNODE_FNDPTN(node)->args ? rb_long2int(RNODE_LIST(RNODE_FNDPTN(node)->args)->as.nd_alen) : 0;
7476
7477 LABEL *match_failed, *type_error, *deconstruct, *deconstructed;
7478 match_failed = NEW_LABEL(line);
7479 type_error = NEW_LABEL(line);
7480 deconstruct = NEW_LABEL(line);
7481 deconstructed = NEW_LABEL(line);
7482
7483 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
7484
7485 CHECK(iseq_compile_array_deconstruct(iseq, ret, node, deconstruct, deconstructed, match_failed, type_error, in_single_pattern, base_index, use_deconstructed_cache));
7486
7487 ADD_INSN(ret, line_node, dup);
7488 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
7489 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
7490 ADD_SEND(ret, line_node, idGE, INT2FIX(1)); // (1)
7491 if (in_single_pattern) {
7492 CHECK(iseq_compile_pattern_set_length_errmsg(iseq, ret, node, rb_fstring_lit("%p length mismatch (given %p, expected %p+)"), INT2FIX(args_num), base_index + 1 /* (1) */));
7493 }
7494 ADD_INSNL(ret, line_node, branchunless, match_failed);
7495
7496 {
7497 LABEL *while_begin = NEW_LABEL(nd_line(node));
7498 LABEL *next_loop = NEW_LABEL(nd_line(node));
7499 LABEL *find_succeeded = NEW_LABEL(line);
7500 LABEL *find_failed = NEW_LABEL(nd_line(node));
7501 int j;
7502
7503 ADD_INSN(ret, line_node, dup); /* allocate stack for len */
7504 ADD_SEND(ret, line_node, idLength, INT2FIX(0)); // (2)
7505
7506 ADD_INSN(ret, line_node, dup); /* allocate stack for limit */
7507 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
7508 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1)); // (3)
7509
7510 ADD_INSN1(ret, line_node, putobject, INT2FIX(0)); /* allocate stack for i */ // (4)
7511
7512 ADD_LABEL(ret, while_begin);
7513
7514 ADD_INSN(ret, line_node, dup);
7515 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
7516 ADD_SEND(ret, line_node, idLE, INT2FIX(1));
7517 ADD_INSNL(ret, line_node, branchunless, find_failed);
7518
7519 for (j = 0; j < args_num; j++) {
7520 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7521 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
7522 if (j != 0) {
7523 ADD_INSN1(ret, line_node, putobject, INT2FIX(j));
7524 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
7525 }
7526 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (5)
7527
7528 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(args)->nd_head, next_loop, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3), (4), (5) */, false));
7529 args = RNODE_LIST(args)->nd_next;
7530 }
7531
7532 if (NODE_NAMED_REST_P(RNODE_FNDPTN(node)->pre_rest_arg)) {
7533 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7534 ADD_INSN1(ret, line_node, putobject, INT2FIX(0));
7535 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
7536 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (6)
7537 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_FNDPTN(node)->pre_rest_arg, find_failed, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3), (4), (6) */, false));
7538 }
7539 if (NODE_NAMED_REST_P(RNODE_FNDPTN(node)->post_rest_arg)) {
7540 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7541 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
7542 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
7543 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
7544 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7545 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (7)
7546 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_FNDPTN(node)->post_rest_arg, find_failed, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3),(4), (7) */, false));
7547 }
7548 ADD_INSNL(ret, line_node, jump, find_succeeded);
7549
7550 ADD_LABEL(ret, next_loop);
7551 ADD_INSN1(ret, line_node, putobject, INT2FIX(1));
7552 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
7553 ADD_INSNL(ret, line_node, jump, while_begin);
7554
7555 ADD_LABEL(ret, find_failed);
7556 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(3));
7557 if (in_single_pattern) {
7558 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7559 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("%p does not match to find pattern"));
7560 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
7561 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(2)); // (8)
7562 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (8) */)); // (9)
7563
7564 ADD_INSN1(ret, line_node, putobject, Qfalse);
7565 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (8), (9) */));
7566
7567 ADD_INSN(ret, line_node, pop);
7568 ADD_INSN(ret, line_node, pop);
7569 }
7570 ADD_INSNL(ret, line_node, jump, match_failed);
7571 ADD_INSN1(ret, line_node, dupn, INT2FIX(3));
7572
7573 ADD_LABEL(ret, find_succeeded);
7574 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(3));
7575 }
7576
7577 ADD_INSN(ret, line_node, pop);
7578 ADD_INSNL(ret, line_node, jump, matched);
7579 ADD_INSN(ret, line_node, putnil);
7580
7581 ADD_LABEL(ret, type_error);
7582 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7583 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
7584 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct must return Array"));
7585 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
7586 ADD_INSN(ret, line_node, pop);
7587
7588 ADD_LABEL(ret, match_failed);
7589 ADD_INSN(ret, line_node, pop);
7590 ADD_INSNL(ret, line_node, jump, unmatched);
7591
7592 break;
7593 }
7594 case NODE_HSHPTN: {
7595 /*
7596 * keys = nil
7597 * if pattern.has_kw_args_node? && !pattern.has_kw_rest_arg_node?
7598 * keys = pattern.kw_args_node.keys
7599 * end
7600 * if pattern.has_constant_node?
7601 * unless pattern.constant === obj
7602 * goto match_failed
7603 * end
7604 * end
7605 * unless obj.respond_to?(:deconstruct_keys)
7606 * goto match_failed
7607 * end
7608 * d = obj.deconstruct_keys(keys)
7609 * unless Hash === d
7610 * goto type_error
7611 * end
7612 * if pattern.has_kw_rest_arg_node?
7613 * d = d.dup
7614 * end
7615 * if pattern.has_kw_args_node?
7616 * pattern.kw_args_node.each |k,|
7617 * unless d.key?(k)
7618 * goto match_failed
7619 * end
7620 * end
7621 * pattern.kw_args_node.each |k, pat|
7622 * if pattern.has_kw_rest_arg_node?
7623 * unless pat.match?(d.delete(k))
7624 * goto match_failed
7625 * end
7626 * else
7627 * unless pat.match?(d[k])
7628 * goto match_failed
7629 * end
7630 * end
7631 * end
7632 * else
7633 * unless d.empty?
7634 * goto match_failed
7635 * end
7636 * end
7637 * if pattern.has_kw_rest_arg_node?
7638 * if pattern.no_rest_keyword?
7639 * unless d.empty?
7640 * goto match_failed
7641 * end
7642 * else
7643 * unless pattern.kw_rest_arg_node.match?(d)
7644 * goto match_failed
7645 * end
7646 * end
7647 * end
7648 * goto matched
7649 * type_error:
7650 * FrozenCore.raise TypeError
7651 * match_failed:
7652 * goto unmatched
7653 */
7654 LABEL *match_failed, *type_error;
7655 VALUE keys = Qnil;
7656
7657 match_failed = NEW_LABEL(line);
7658 type_error = NEW_LABEL(line);
7659
7660 if (RNODE_HSHPTN(node)->nd_pkwargs && !RNODE_HSHPTN(node)->nd_pkwrestarg) {
7661 const NODE *kw_args = RNODE_HASH(RNODE_HSHPTN(node)->nd_pkwargs)->nd_head;
7662 keys = rb_ary_new_capa(kw_args ? RNODE_LIST(kw_args)->as.nd_alen/2 : 0);
7663 while (kw_args) {
7664 rb_ary_push(keys, get_symbol_value(iseq, RNODE_LIST(kw_args)->nd_head));
7665 kw_args = RNODE_LIST(RNODE_LIST(kw_args)->nd_next)->nd_next;
7666 }
7667 }
7668
7669 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
7670
7671 ADD_INSN(ret, line_node, dup);
7672 ADD_INSN1(ret, line_node, putobject, ID2SYM(rb_intern("deconstruct_keys")));
7673 ADD_SEND(ret, line_node, idRespond_to, INT2FIX(1)); // (1)
7674 if (in_single_pattern) {
7675 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p does not respond to #deconstruct_keys"), base_index + 1 /* (1) */));
7676 }
7677 ADD_INSNL(ret, line_node, branchunless, match_failed);
7678
7679 if (NIL_P(keys)) {
7680 ADD_INSN(ret, line_node, putnil);
7681 }
7682 else {
7683 RB_OBJ_SET_FROZEN_SHAREABLE(keys);
7684 ADD_INSN1(ret, line_node, duparray, keys);
7685 RB_OBJ_WRITTEN(iseq, Qundef, rb_obj_hide(keys));
7686 }
7687 ADD_SEND(ret, line_node, rb_intern("deconstruct_keys"), INT2FIX(1)); // (2)
7688
7689 ADD_INSN(ret, line_node, dup);
7690 ADD_INSN1(ret, line_node, checktype, INT2FIX(T_HASH));
7691 ADD_INSNL(ret, line_node, branchunless, type_error);
7692
7693 if (RNODE_HSHPTN(node)->nd_pkwrestarg) {
7694 ADD_SEND(ret, line_node, rb_intern("dup"), INT2FIX(0));
7695 }
7696
7697 if (RNODE_HSHPTN(node)->nd_pkwargs) {
7698 int i;
7699 int keys_num;
7700 const NODE *args;
7701 args = RNODE_HASH(RNODE_HSHPTN(node)->nd_pkwargs)->nd_head;
7702 if (args) {
7703 DECL_ANCHOR(match_values);
7704 INIT_ANCHOR(match_values);
7705 keys_num = rb_long2int(RNODE_LIST(args)->as.nd_alen) / 2;
7706 for (i = 0; i < keys_num; i++) {
7707 NODE *key_node = RNODE_LIST(args)->nd_head;
7708 NODE *value_node = RNODE_LIST(RNODE_LIST(args)->nd_next)->nd_head;
7709 VALUE key = get_symbol_value(iseq, key_node);
7710
7711 ADD_INSN(ret, line_node, dup);
7712 ADD_INSN1(ret, line_node, putobject, key);
7713 ADD_SEND(ret, line_node, rb_intern("key?"), INT2FIX(1)); // (3)
7714 if (in_single_pattern) {
7715 LABEL *match_succeeded;
7716 match_succeeded = NEW_LABEL(line);
7717
7718 ADD_INSN(ret, line_node, dup);
7719 ADD_INSNL(ret, line_node, branchif, match_succeeded);
7720
7721 VALUE str = rb_str_freeze(rb_sprintf("key not found: %+"PRIsVALUE, key));
7722 ADD_INSN1(ret, line_node, putobject, RB_OBJ_SET_SHAREABLE(str)); // (4)
7723 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 2 /* (3), (4) */));
7724 ADD_INSN1(ret, line_node, putobject, Qtrue); // (5)
7725 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 3 /* (3), (4), (5) */));
7726 ADD_INSN1(ret, line_node, topn, INT2FIX(3)); // (6)
7727 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_MATCHEE + 4 /* (3), (4), (5), (6) */));
7728 ADD_INSN1(ret, line_node, putobject, key); // (7)
7729 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_KEY + 5 /* (3), (4), (5), (6), (7) */));
7730
7731 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(4));
7732
7733 ADD_LABEL(ret, match_succeeded);
7734 }
7735 ADD_INSNL(ret, line_node, branchunless, match_failed);
7736
7737 ADD_INSN(match_values, line_node, dup);
7738 ADD_INSN1(match_values, line_node, putobject, key);
7739 ADD_SEND(match_values, line_node, RNODE_HSHPTN(node)->nd_pkwrestarg ? rb_intern("delete") : idAREF, INT2FIX(1)); // (8)
7740 CHECK(iseq_compile_pattern_match(iseq, match_values, value_node, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (8) */, false));
7741 args = RNODE_LIST(RNODE_LIST(args)->nd_next)->nd_next;
7742 }
7743 ADD_SEQ(ret, match_values);
7744 }
7745 }
7746 else {
7747 ADD_INSN(ret, line_node, dup);
7748 ADD_SEND(ret, line_node, idEmptyP, INT2FIX(0)); // (9)
7749 if (in_single_pattern) {
7750 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p is not empty"), base_index + 1 /* (9) */));
7751 }
7752 ADD_INSNL(ret, line_node, branchunless, match_failed);
7753 }
7754
7755 if (RNODE_HSHPTN(node)->nd_pkwrestarg) {
7756 if (RNODE_HSHPTN(node)->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD) {
7757 ADD_INSN(ret, line_node, dup);
7758 ADD_SEND(ret, line_node, idEmptyP, INT2FIX(0)); // (10)
7759 if (in_single_pattern) {
7760 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("rest of %p is not empty"), base_index + 1 /* (10) */));
7761 }
7762 ADD_INSNL(ret, line_node, branchunless, match_failed);
7763 }
7764 else {
7765 ADD_INSN(ret, line_node, dup); // (11)
7766 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_HSHPTN(node)->nd_pkwrestarg, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (11) */, false));
7767 }
7768 }
7769
7770 ADD_INSN(ret, line_node, pop);
7771 ADD_INSNL(ret, line_node, jump, matched);
7772 ADD_INSN(ret, line_node, putnil);
7773
7774 ADD_LABEL(ret, type_error);
7775 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7776 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
7777 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct_keys must return Hash"));
7778 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
7779 ADD_INSN(ret, line_node, pop);
7780
7781 ADD_LABEL(ret, match_failed);
7782 ADD_INSN(ret, line_node, pop);
7783 ADD_INSNL(ret, line_node, jump, unmatched);
7784 break;
7785 }
7786 case NODE_SYM:
7787 case NODE_REGX:
7788 case NODE_LINE:
7789 case NODE_INTEGER:
7790 case NODE_FLOAT:
7791 case NODE_RATIONAL:
7792 case NODE_IMAGINARY:
7793 case NODE_FILE:
7794 case NODE_ENCODING:
7795 case NODE_STR:
7796 case NODE_XSTR:
7797 case NODE_DSTR:
7798 case NODE_DSYM:
7799 case NODE_DREGX:
7800 case NODE_LIST:
7801 case NODE_ZLIST:
7802 case NODE_LAMBDA:
7803 case NODE_DOT2:
7804 case NODE_DOT3:
7805 case NODE_CONST:
7806 case NODE_LVAR:
7807 case NODE_DVAR:
7808 case NODE_IVAR:
7809 case NODE_CVAR:
7810 case NODE_GVAR:
7811 case NODE_TRUE:
7812 case NODE_FALSE:
7813 case NODE_SELF:
7814 case NODE_NIL:
7815 case NODE_COLON2:
7816 case NODE_COLON3:
7817 case NODE_BEGIN:
7818 case NODE_BLOCK:
7819 case NODE_ONCE:
7820 CHECK(COMPILE(ret, "case in literal", node)); // (1)
7821 if (in_single_pattern) {
7822 ADD_INSN1(ret, line_node, dupn, INT2FIX(2));
7823 }
7824 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE)); // (2)
7825 if (in_single_pattern) {
7826 CHECK(iseq_compile_pattern_set_eqq_errmsg(iseq, ret, node, base_index + 2 /* (1), (2) */));
7827 }
7828 ADD_INSNL(ret, line_node, branchif, matched);
7829 ADD_INSNL(ret, line_node, jump, unmatched);
7830 break;
7831 case NODE_LASGN: {
7832 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
7833 ID id = RNODE_LASGN(node)->nd_vid;
7834 int idx = ISEQ_BODY(body->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
7835
7836 if (in_alt_pattern) {
7837 const char *name = rb_id2name(id);
7838 if (name && strlen(name) > 0 && name[0] != '_') {
7839 COMPILE_ERROR(ERROR_ARGS "illegal variable in alternative pattern (%"PRIsVALUE")",
7840 rb_id2str(id));
7841 return COMPILE_NG;
7842 }
7843 }
7844
7845 ADD_SETLOCAL(ret, line_node, idx, get_lvar_level(iseq));
7846 ADD_INSNL(ret, line_node, jump, matched);
7847 break;
7848 }
7849 case NODE_DASGN: {
7850 int idx, lv, ls;
7851 ID id = RNODE_DASGN(node)->nd_vid;
7852
7853 idx = get_dyna_var_idx(iseq, id, &lv, &ls);
7854
7855 if (in_alt_pattern) {
7856 const char *name = rb_id2name(id);
7857 if (name && strlen(name) > 0 && name[0] != '_') {
7858 COMPILE_ERROR(ERROR_ARGS "illegal variable in alternative pattern (%"PRIsVALUE")",
7859 rb_id2str(id));
7860 return COMPILE_NG;
7861 }
7862 }
7863
7864 if (idx < 0) {
7865 COMPILE_ERROR(ERROR_ARGS "NODE_DASGN: unknown id (%"PRIsVALUE")",
7866 rb_id2str(id));
7867 return COMPILE_NG;
7868 }
7869 ADD_SETLOCAL(ret, line_node, ls - idx, lv);
7870 ADD_INSNL(ret, line_node, jump, matched);
7871 break;
7872 }
7873 case NODE_IF:
7874 case NODE_UNLESS: {
7875 LABEL *match_failed;
7876 match_failed = unmatched;
7877 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_IF(node)->nd_body, unmatched, in_single_pattern, in_alt_pattern, base_index, use_deconstructed_cache));
7878 CHECK(COMPILE(ret, "case in if", RNODE_IF(node)->nd_cond));
7879 if (in_single_pattern) {
7880 LABEL *match_succeeded;
7881 match_succeeded = NEW_LABEL(line);
7882
7883 ADD_INSN(ret, line_node, dup);
7884 if (nd_type_p(node, NODE_IF)) {
7885 ADD_INSNL(ret, line_node, branchif, match_succeeded);
7886 }
7887 else {
7888 ADD_INSNL(ret, line_node, branchunless, match_succeeded);
7889 }
7890
7891 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("guard clause does not return true")); // (1)
7892 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
7893 ADD_INSN1(ret, line_node, putobject, Qfalse);
7894 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
7895
7896 ADD_INSN(ret, line_node, pop);
7897 ADD_INSN(ret, line_node, pop);
7898
7899 ADD_LABEL(ret, match_succeeded);
7900 }
7901 if (nd_type_p(node, NODE_IF)) {
7902 ADD_INSNL(ret, line_node, branchunless, match_failed);
7903 }
7904 else {
7905 ADD_INSNL(ret, line_node, branchif, match_failed);
7906 }
7907 ADD_INSNL(ret, line_node, jump, matched);
7908 break;
7909 }
7910 case NODE_HASH: {
7911 NODE *n;
7912 LABEL *match_failed;
7913 match_failed = NEW_LABEL(line);
7914
7915 n = RNODE_HASH(node)->nd_head;
7916 if (! (nd_type_p(n, NODE_LIST) && RNODE_LIST(n)->as.nd_alen == 2)) {
7917 COMPILE_ERROR(ERROR_ARGS "unexpected node");
7918 return COMPILE_NG;
7919 }
7920
7921 ADD_INSN(ret, line_node, dup); // (1)
7922 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(n)->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (1) */, use_deconstructed_cache));
7923 CHECK(iseq_compile_pattern_each(iseq, ret, RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head, matched, match_failed, in_single_pattern, in_alt_pattern, base_index, false));
7924 ADD_INSN(ret, line_node, putnil);
7925
7926 ADD_LABEL(ret, match_failed);
7927 ADD_INSN(ret, line_node, pop);
7928 ADD_INSNL(ret, line_node, jump, unmatched);
7929 break;
7930 }
7931 case NODE_OR: {
7932 LABEL *match_succeeded, *fin;
7933 match_succeeded = NEW_LABEL(line);
7934 fin = NEW_LABEL(line);
7935
7936 ADD_INSN(ret, line_node, dup); // (1)
7937 CHECK(iseq_compile_pattern_each(iseq, ret, RNODE_OR(node)->nd_1st, match_succeeded, fin, in_single_pattern, true, base_index + 1 /* (1) */, use_deconstructed_cache));
7938 ADD_LABEL(ret, match_succeeded);
7939 ADD_INSN(ret, line_node, pop);
7940 ADD_INSNL(ret, line_node, jump, matched);
7941 ADD_INSN(ret, line_node, putnil);
7942 ADD_LABEL(ret, fin);
7943 CHECK(iseq_compile_pattern_each(iseq, ret, RNODE_OR(node)->nd_2nd, matched, unmatched, in_single_pattern, true, base_index, use_deconstructed_cache));
7944 break;
7945 }
7946 default:
7947 UNKNOWN_NODE("NODE_IN", node, COMPILE_NG);
7948 }
7949 return COMPILE_OK;
7950}
7951
7952static int
7953iseq_compile_pattern_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *unmatched, bool in_single_pattern, bool in_alt_pattern, int base_index, bool use_deconstructed_cache)
7954{
7955 LABEL *fin = NEW_LABEL(nd_line(node));
7956 CHECK(iseq_compile_pattern_each(iseq, ret, node, fin, unmatched, in_single_pattern, in_alt_pattern, base_index, use_deconstructed_cache));
7957 ADD_LABEL(ret, fin);
7958 return COMPILE_OK;
7959}
7960
7961static int
7962iseq_compile_pattern_constant(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *match_failed, bool in_single_pattern, int base_index)
7963{
7964 const NODE *line_node = node;
7965
7966 if (RNODE_ARYPTN(node)->nd_pconst) {
7967 ADD_INSN(ret, line_node, dup); // (1)
7968 CHECK(COMPILE(ret, "constant", RNODE_ARYPTN(node)->nd_pconst)); // (2)
7969 if (in_single_pattern) {
7970 ADD_INSN1(ret, line_node, dupn, INT2FIX(2));
7971 }
7972 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE)); // (3)
7973 if (in_single_pattern) {
7974 CHECK(iseq_compile_pattern_set_eqq_errmsg(iseq, ret, node, base_index + 3 /* (1), (2), (3) */));
7975 }
7976 ADD_INSNL(ret, line_node, branchunless, match_failed);
7977 }
7978 return COMPILE_OK;
7979}
7980
7981
7982static int
7983iseq_compile_array_deconstruct(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *deconstruct, LABEL *deconstructed, LABEL *match_failed, LABEL *type_error, bool in_single_pattern, int base_index, bool use_deconstructed_cache)
7984{
7985 const NODE *line_node = node;
7986
7987 // NOTE: this optimization allows us to re-use the #deconstruct value
7988 // (or its absence).
7989 if (use_deconstructed_cache) {
7990 // If value is nil then we haven't tried to deconstruct
7991 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
7992 ADD_INSNL(ret, line_node, branchnil, deconstruct);
7993
7994 // If false then the value is not deconstructable
7995 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
7996 ADD_INSNL(ret, line_node, branchunless, match_failed);
7997
7998 // Drop value, add deconstructed to the stack and jump
7999 ADD_INSN(ret, line_node, pop); // (1)
8000 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE - 1 /* (1) */));
8001 ADD_INSNL(ret, line_node, jump, deconstructed);
8002 }
8003 else {
8004 ADD_INSNL(ret, line_node, jump, deconstruct);
8005 }
8006
8007 ADD_LABEL(ret, deconstruct);
8008 ADD_INSN(ret, line_node, dup);
8009 ADD_INSN1(ret, line_node, putobject, ID2SYM(rb_intern("deconstruct")));
8010 ADD_SEND(ret, line_node, idRespond_to, INT2FIX(1)); // (2)
8011
8012 // Cache the result of respond_to? (in case it's false is stays there, if true - it's overwritten after #deconstruct)
8013 if (use_deconstructed_cache) {
8014 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE + 1 /* (2) */));
8015 }
8016
8017 if (in_single_pattern) {
8018 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p does not respond to #deconstruct"), base_index + 1 /* (2) */));
8019 }
8020
8021 ADD_INSNL(ret, line_node, branchunless, match_failed);
8022
8023 ADD_SEND(ret, line_node, rb_intern("deconstruct"), INT2FIX(0));
8024
8025 // Cache the result (if it's cacheable - currently, only top-level array patterns)
8026 if (use_deconstructed_cache) {
8027 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
8028 }
8029
8030 ADD_INSN(ret, line_node, dup);
8031 ADD_INSN1(ret, line_node, checktype, INT2FIX(T_ARRAY));
8032 ADD_INSNL(ret, line_node, branchunless, type_error);
8033
8034 ADD_LABEL(ret, deconstructed);
8035
8036 return COMPILE_OK;
8037}
8038
8039static int
8040iseq_compile_pattern_set_general_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE errmsg, int base_index)
8041{
8042 /*
8043 * if match_succeeded?
8044 * goto match_succeeded
8045 * end
8046 * error_string = FrozenCore.sprintf(errmsg, matchee)
8047 * key_error_p = false
8048 * match_succeeded:
8049 */
8050 const int line = nd_line(node);
8051 const NODE *line_node = node;
8052 LABEL *match_succeeded = NEW_LABEL(line);
8053
8054 ADD_INSN(ret, line_node, dup);
8055 ADD_INSNL(ret, line_node, branchif, match_succeeded);
8056
8057 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8058 ADD_INSN1(ret, line_node, putobject, errmsg);
8059 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
8060 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(2)); // (1)
8061 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
8062
8063 ADD_INSN1(ret, line_node, putobject, Qfalse);
8064 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
8065
8066 ADD_INSN(ret, line_node, pop);
8067 ADD_INSN(ret, line_node, pop);
8068 ADD_LABEL(ret, match_succeeded);
8069
8070 return COMPILE_OK;
8071}
8072
8073static int
8074iseq_compile_pattern_set_length_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE errmsg, VALUE pattern_length, int base_index)
8075{
8076 /*
8077 * if match_succeeded?
8078 * goto match_succeeded
8079 * end
8080 * error_string = FrozenCore.sprintf(errmsg, matchee, matchee.length, pat.length)
8081 * key_error_p = false
8082 * match_succeeded:
8083 */
8084 const int line = nd_line(node);
8085 const NODE *line_node = node;
8086 LABEL *match_succeeded = NEW_LABEL(line);
8087
8088 ADD_INSN(ret, line_node, dup);
8089 ADD_INSNL(ret, line_node, branchif, match_succeeded);
8090
8091 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8092 ADD_INSN1(ret, line_node, putobject, errmsg);
8093 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
8094 ADD_INSN(ret, line_node, dup);
8095 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
8096 ADD_INSN1(ret, line_node, putobject, pattern_length);
8097 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(4)); // (1)
8098 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
8099
8100 ADD_INSN1(ret, line_node, putobject, Qfalse);
8101 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2/* (1), (2) */));
8102
8103 ADD_INSN(ret, line_node, pop);
8104 ADD_INSN(ret, line_node, pop);
8105 ADD_LABEL(ret, match_succeeded);
8106
8107 return COMPILE_OK;
8108}
8109
8110static int
8111iseq_compile_pattern_set_eqq_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int base_index)
8112{
8113 /*
8114 * if match_succeeded?
8115 * goto match_succeeded
8116 * end
8117 * error_string = FrozenCore.sprintf("%p === %p does not return true", pat, matchee)
8118 * key_error_p = false
8119 * match_succeeded:
8120 */
8121 const int line = nd_line(node);
8122 const NODE *line_node = node;
8123 LABEL *match_succeeded = NEW_LABEL(line);
8124
8125 ADD_INSN(ret, line_node, dup);
8126 ADD_INSNL(ret, line_node, branchif, match_succeeded);
8127
8128 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8129 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("%p === %p does not return true"));
8130 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
8131 ADD_INSN1(ret, line_node, topn, INT2FIX(5));
8132 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(3)); // (1)
8133 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
8134
8135 ADD_INSN1(ret, line_node, putobject, Qfalse);
8136 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
8137
8138 ADD_INSN(ret, line_node, pop);
8139 ADD_INSN(ret, line_node, pop);
8140
8141 ADD_LABEL(ret, match_succeeded);
8142 ADD_INSN1(ret, line_node, setn, INT2FIX(2));
8143 ADD_INSN(ret, line_node, pop);
8144 ADD_INSN(ret, line_node, pop);
8145
8146 return COMPILE_OK;
8147}
8148
8149static int
8150compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
8151{
8152 const NODE *pattern;
8153 const NODE *node = orig_node;
8154 LABEL *endlabel, *elselabel;
8155 DECL_ANCHOR(head);
8156 DECL_ANCHOR(body_seq);
8157 DECL_ANCHOR(cond_seq);
8158 int line;
8159 enum node_type type;
8160 const NODE *line_node;
8161 VALUE branches = 0;
8162 int branch_id = 0;
8163 bool single_pattern;
8164
8165 INIT_ANCHOR(head);
8166 INIT_ANCHOR(body_seq);
8167 INIT_ANCHOR(cond_seq);
8168
8169 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case");
8170
8171 node = RNODE_CASE3(node)->nd_body;
8172 EXPECT_NODE("NODE_CASE3", node, NODE_IN, COMPILE_NG);
8173 type = nd_type(node);
8174 line = nd_line(node);
8175 line_node = node;
8176 single_pattern = !RNODE_IN(node)->nd_next;
8177
8178 endlabel = NEW_LABEL(line);
8179 elselabel = NEW_LABEL(line);
8180
8181 if (single_pattern) {
8182 /* allocate stack for ... */
8183 ADD_INSN(head, line_node, putnil); /* key_error_key */
8184 ADD_INSN(head, line_node, putnil); /* key_error_matchee */
8185 ADD_INSN1(head, line_node, putobject, Qfalse); /* key_error_p */
8186 ADD_INSN(head, line_node, putnil); /* error_string */
8187 }
8188 ADD_INSN(head, line_node, putnil); /* allocate stack for cached #deconstruct value */
8189
8190 CHECK(COMPILE(head, "case base", RNODE_CASE3(orig_node)->nd_head));
8191
8192 ADD_SEQ(ret, head); /* case VAL */
8193
8194 while (type == NODE_IN) {
8195 LABEL *l1;
8196
8197 if (branch_id) {
8198 ADD_INSN(body_seq, line_node, putnil);
8199 }
8200 l1 = NEW_LABEL(line);
8201 ADD_LABEL(body_seq, l1);
8202 ADD_INSN1(body_seq, line_node, adjuststack, INT2FIX(single_pattern ? 6 : 2));
8203
8204 const NODE *const coverage_node = RNODE_IN(node)->nd_body ? RNODE_IN(node)->nd_body : node;
8205 add_trace_branch_coverage(
8206 iseq,
8207 body_seq,
8208 nd_code_loc(coverage_node),
8209 nd_node_id(coverage_node),
8210 branch_id++,
8211 "in",
8212 branches);
8213
8214 CHECK(COMPILE_(body_seq, "in body", RNODE_IN(node)->nd_body, popped));
8215 ADD_INSNL(body_seq, line_node, jump, endlabel);
8216
8217 pattern = RNODE_IN(node)->nd_head;
8218 if (pattern) {
8219 int pat_line = nd_line(pattern);
8220 LABEL *next_pat = NEW_LABEL(pat_line);
8221 ADD_INSN (cond_seq, pattern, dup); /* dup case VAL */
8222 // NOTE: set base_index (it's "under" the matchee value, so it's position is 2)
8223 CHECK(iseq_compile_pattern_each(iseq, cond_seq, pattern, l1, next_pat, single_pattern, false, 2, true));
8224 ADD_LABEL(cond_seq, next_pat);
8225 LABEL_UNREMOVABLE(next_pat);
8226 }
8227 else {
8228 COMPILE_ERROR(ERROR_ARGS "unexpected node");
8229 return COMPILE_NG;
8230 }
8231
8232 node = RNODE_IN(node)->nd_next;
8233 if (!node) {
8234 break;
8235 }
8236 type = nd_type(node);
8237 line = nd_line(node);
8238 line_node = node;
8239 }
8240 /* else */
8241 if (node) {
8242 ADD_LABEL(cond_seq, elselabel);
8243 ADD_INSN(cond_seq, line_node, pop);
8244 ADD_INSN(cond_seq, line_node, pop); /* discard cached #deconstruct value */
8245 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(node), nd_node_id(node), branch_id, "else", branches);
8246 CHECK(COMPILE_(cond_seq, "else", node, popped));
8247 ADD_INSNL(cond_seq, line_node, jump, endlabel);
8248 ADD_INSN(cond_seq, line_node, putnil);
8249 if (popped) {
8250 ADD_INSN(cond_seq, line_node, putnil);
8251 }
8252 }
8253 else {
8254 debugs("== else (implicit)\n");
8255 ADD_LABEL(cond_seq, elselabel);
8256 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(orig_node), nd_node_id(orig_node), branch_id, "else", branches);
8257 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8258
8259 if (single_pattern) {
8260 /*
8261 * if key_error_p
8262 * FrozenCore.raise NoMatchingPatternKeyError.new(FrozenCore.sprintf("%p: %s", case_val, error_string), matchee: key_error_matchee, key: key_error_key)
8263 * else
8264 * FrozenCore.raise NoMatchingPatternError, FrozenCore.sprintf("%p: %s", case_val, error_string)
8265 * end
8266 */
8267 LABEL *key_error, *fin;
8268 struct rb_callinfo_kwarg *kw_arg;
8269
8270 key_error = NEW_LABEL(line);
8271 fin = NEW_LABEL(line);
8272
8273 kw_arg = rb_xmalloc_mul_add(2, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
8274 kw_arg->references = 0;
8275 kw_arg->keyword_len = 2;
8276 kw_arg->keywords[0] = ID2SYM(rb_intern("matchee"));
8277 kw_arg->keywords[1] = ID2SYM(rb_intern("key"));
8278
8279 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_P + 2));
8280 ADD_INSNL(cond_seq, orig_node, branchif, key_error);
8281 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternError);
8282 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8283 ADD_INSN1(cond_seq, orig_node, putobject, rb_fstring_lit("%p: %s"));
8284 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(4)); /* case VAL */
8285 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_ERROR_STRING + 6));
8286 ADD_SEND(cond_seq, orig_node, id_core_sprintf, INT2FIX(3));
8287 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(2));
8288 ADD_INSNL(cond_seq, orig_node, jump, fin);
8289
8290 ADD_LABEL(cond_seq, key_error);
8291 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternKeyError);
8292 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8293 ADD_INSN1(cond_seq, orig_node, putobject, rb_fstring_lit("%p: %s"));
8294 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(4)); /* case VAL */
8295 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_ERROR_STRING + 6));
8296 ADD_SEND(cond_seq, orig_node, id_core_sprintf, INT2FIX(3));
8297 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_MATCHEE + 4));
8298 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_KEY + 5));
8299 ADD_SEND_R(cond_seq, orig_node, rb_intern("new"), INT2FIX(1), NULL, INT2FIX(VM_CALL_KWARG), kw_arg);
8300 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(1));
8301
8302 ADD_LABEL(cond_seq, fin);
8303 }
8304 else {
8305 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternError);
8306 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(2));
8307 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(2));
8308 }
8309 ADD_INSN1(cond_seq, orig_node, adjuststack, INT2FIX(single_pattern ? 7 : 3));
8310 if (!popped) {
8311 ADD_INSN(cond_seq, orig_node, putnil);
8312 }
8313 ADD_INSNL(cond_seq, orig_node, jump, endlabel);
8314 ADD_INSN1(cond_seq, orig_node, dupn, INT2FIX(single_pattern ? 5 : 1));
8315 if (popped) {
8316 ADD_INSN(cond_seq, line_node, putnil);
8317 }
8318 }
8319
8320 ADD_SEQ(ret, cond_seq);
8321 ADD_SEQ(ret, body_seq);
8322 ADD_LABEL(ret, endlabel);
8323 return COMPILE_OK;
8324}
8325
8326#undef CASE3_BI_OFFSET_DECONSTRUCTED_CACHE
8327#undef CASE3_BI_OFFSET_ERROR_STRING
8328#undef CASE3_BI_OFFSET_KEY_ERROR_P
8329#undef CASE3_BI_OFFSET_KEY_ERROR_MATCHEE
8330#undef CASE3_BI_OFFSET_KEY_ERROR_KEY
8331
8332static int
8333compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
8334{
8335 const int line = (int)nd_line(node);
8336 const NODE *line_node = node;
8337
8338 LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
8339 LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
8340 LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
8341 int prev_loopval_popped = ISEQ_COMPILE_DATA(iseq)->loopval_popped;
8342 VALUE branches = Qfalse;
8343
8345
8346 LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(line); /* next */
8347 LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(line); /* redo */
8348 LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(line); /* break */
8349 LABEL *end_label = NEW_LABEL(line);
8350 LABEL *adjust_label = NEW_LABEL(line);
8351
8352 LABEL *next_catch_label = NEW_LABEL(line);
8353 LABEL *tmp_label = NULL;
8354
8355 ISEQ_COMPILE_DATA(iseq)->loopval_popped = 0;
8356 push_ensure_entry(iseq, &enl, NULL, NULL);
8357
8358 if (RNODE_WHILE(node)->nd_state == 1) {
8359 ADD_INSNL(ret, line_node, jump, next_label);
8360 }
8361 else {
8362 tmp_label = NEW_LABEL(line);
8363 ADD_INSNL(ret, line_node, jump, tmp_label);
8364 }
8365 ADD_LABEL(ret, adjust_label);
8366 ADD_INSN(ret, line_node, putnil);
8367 ADD_LABEL(ret, next_catch_label);
8368 ADD_INSN(ret, line_node, pop);
8369 ADD_INSNL(ret, line_node, jump, next_label);
8370 if (tmp_label) ADD_LABEL(ret, tmp_label);
8371
8372 ADD_LABEL(ret, redo_label);
8373 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_WHILE ? "while" : "until");
8374
8375 const NODE *const coverage_node = RNODE_WHILE(node)->nd_body ? RNODE_WHILE(node)->nd_body : node;
8376 add_trace_branch_coverage(
8377 iseq,
8378 ret,
8379 nd_code_loc(coverage_node),
8380 nd_node_id(coverage_node),
8381 0,
8382 "body",
8383 branches);
8384
8385 CHECK(COMPILE_POPPED(ret, "while body", RNODE_WHILE(node)->nd_body));
8386 ADD_LABEL(ret, next_label); /* next */
8387
8388 if (type == NODE_WHILE) {
8389 CHECK(compile_branch_condition(iseq, ret, RNODE_WHILE(node)->nd_cond,
8390 redo_label, end_label));
8391 }
8392 else {
8393 /* until */
8394 CHECK(compile_branch_condition(iseq, ret, RNODE_WHILE(node)->nd_cond,
8395 end_label, redo_label));
8396 }
8397
8398 ADD_LABEL(ret, end_label);
8399 ADD_ADJUST_RESTORE(ret, adjust_label);
8400
8401 if (UNDEF_P(RNODE_WHILE(node)->nd_state)) {
8402 /* ADD_INSN(ret, line_node, putundef); */
8403 COMPILE_ERROR(ERROR_ARGS "unsupported: putundef");
8404 return COMPILE_NG;
8405 }
8406 else {
8407 ADD_INSN(ret, line_node, putnil);
8408 }
8409
8410 ADD_LABEL(ret, break_label); /* break */
8411
8412 if (popped) {
8413 ADD_INSN(ret, line_node, pop);
8414 }
8415
8416 ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL,
8417 break_label);
8418 ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL,
8419 next_catch_label);
8420 ADD_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL,
8421 ISEQ_COMPILE_DATA(iseq)->redo_label);
8422
8423 ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
8424 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
8425 ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
8426 ISEQ_COMPILE_DATA(iseq)->loopval_popped = prev_loopval_popped;
8427 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->prev;
8428 return COMPILE_OK;
8429}
8430
8431static int
8432compile_iter(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8433{
8434 const int line = nd_line(node);
8435 const NODE *line_node = node;
8436 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
8437 LABEL *retry_label = NEW_LABEL(line);
8438 LABEL *retry_end_l = NEW_LABEL(line);
8439 const rb_iseq_t *child_iseq;
8440
8441 ADD_LABEL(ret, retry_label);
8442 if (nd_type_p(node, NODE_FOR)) {
8443 CHECK(COMPILE(ret, "iter caller (for)", RNODE_FOR(node)->nd_iter));
8444
8445 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
8446 NEW_CHILD_ISEQ(RNODE_FOR(node)->nd_body, make_name_for_block(iseq),
8447 ISEQ_TYPE_BLOCK, line);
8448 ADD_SEND_WITH_BLOCK(ret, line_node, idEach, INT2FIX(0), child_iseq);
8449 }
8450 else {
8451 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
8452 NEW_CHILD_ISEQ(RNODE_ITER(node)->nd_body, make_name_for_block(iseq),
8453 ISEQ_TYPE_BLOCK, line);
8454 CHECK(COMPILE(ret, "iter caller", RNODE_ITER(node)->nd_iter));
8455 }
8456
8457 {
8458 // We need to put the label "retry_end_l" immediately after the last "send" instruction.
8459 // This because vm_throw checks if the break cont is equal to the index of next insn of the "send".
8460 // (Otherwise, it is considered "break from proc-closure". See "TAG_BREAK" handling in "vm_throw_start".)
8461 //
8462 // Normally, "send" instruction is at the last.
8463 // However, qcall under branch coverage measurement adds some instructions after the "send".
8464 //
8465 // Note that "invokesuper", "invokesuperforward" appears instead of "send".
8466 INSN *iobj;
8467 LINK_ELEMENT *last_elem = LAST_ELEMENT(ret);
8468 iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem);
8469 while (!IS_INSN_ID(iobj, send) && !IS_INSN_ID(iobj, invokesuper) && !IS_INSN_ID(iobj, sendforward) && !IS_INSN_ID(iobj, invokesuperforward)) {
8470 iobj = (INSN*) get_prev_insn(iobj);
8471 }
8472 ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l);
8473
8474 // LINK_ANCHOR has a pointer to the last element, but ELEM_INSERT_NEXT does not update it
8475 // even if we add an insn to the last of LINK_ANCHOR. So this updates it manually.
8476 if (&iobj->link == LAST_ELEMENT(ret)) {
8477 ret->last = (LINK_ELEMENT*) retry_end_l;
8478 }
8479 }
8480
8481 if (popped) {
8482 ADD_INSN(ret, line_node, pop);
8483 }
8484
8485 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
8486
8487 ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
8488 return COMPILE_OK;
8489}
8490
8491static int
8492compile_for_masgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8493{
8494 /* massign to var in "for"
8495 * (args.length == 1 && Array.try_convert(args[0])) || args
8496 */
8497 const NODE *line_node = node;
8498 const NODE *var = RNODE_FOR_MASGN(node)->nd_var;
8499 LABEL *not_single = NEW_LABEL(nd_line(var));
8500 LABEL *not_ary = NEW_LABEL(nd_line(var));
8501 CHECK(COMPILE(ret, "for var", var));
8502 ADD_INSN(ret, line_node, dup);
8503 ADD_CALL(ret, line_node, idLength, INT2FIX(0));
8504 ADD_INSN1(ret, line_node, putobject, INT2FIX(1));
8505 ADD_CALL(ret, line_node, idEq, INT2FIX(1));
8506 ADD_INSNL(ret, line_node, branchunless, not_single);
8507 ADD_INSN(ret, line_node, dup);
8508 ADD_INSN1(ret, line_node, putobject, INT2FIX(0));
8509 ADD_CALL(ret, line_node, idAREF, INT2FIX(1));
8510 ADD_INSN1(ret, line_node, putobject, rb_cArray);
8511 ADD_INSN(ret, line_node, swap);
8512 ADD_CALL(ret, line_node, rb_intern("try_convert"), INT2FIX(1));
8513 ADD_INSN(ret, line_node, dup);
8514 ADD_INSNL(ret, line_node, branchunless, not_ary);
8515 ADD_INSN(ret, line_node, swap);
8516 ADD_LABEL(ret, not_ary);
8517 ADD_INSN(ret, line_node, pop);
8518 ADD_LABEL(ret, not_single);
8519 return COMPILE_OK;
8520}
8521
8522static int
8523compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8524{
8525 const NODE *line_node = node;
8526 unsigned long throw_flag = 0;
8527
8528 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
8529 /* while/until */
8530 LABEL *splabel = NEW_LABEL(0);
8531 ADD_LABEL(ret, splabel);
8532 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
8533 CHECK(COMPILE_(ret, "break val (while/until)", RNODE_BREAK(node)->nd_stts,
8534 ISEQ_COMPILE_DATA(iseq)->loopval_popped));
8535 add_ensure_iseq(ret, iseq, 0);
8536 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
8537 ADD_ADJUST_RESTORE(ret, splabel);
8538
8539 if (!popped) {
8540 ADD_INSN(ret, line_node, putnil);
8541 }
8542 }
8543 else {
8544 const rb_iseq_t *ip = iseq;
8545
8546 while (ip) {
8547 if (!ISEQ_COMPILE_DATA(ip)) {
8548 ip = 0;
8549 break;
8550 }
8551
8552 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8553 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
8554 }
8555 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8556 throw_flag = 0;
8557 }
8558 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8559 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with break");
8560 return COMPILE_NG;
8561 }
8562 else {
8563 ip = ISEQ_BODY(ip)->parent_iseq;
8564 continue;
8565 }
8566
8567 /* escape from block */
8568 CHECK(COMPILE(ret, "break val (block)", RNODE_BREAK(node)->nd_stts));
8569 ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_BREAK));
8570 if (popped) {
8571 ADD_INSN(ret, line_node, pop);
8572 }
8573 return COMPILE_OK;
8574 }
8575 COMPILE_ERROR(ERROR_ARGS "Invalid break");
8576 return COMPILE_NG;
8577 }
8578 return COMPILE_OK;
8579}
8580
8581static int
8582compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8583{
8584 const NODE *line_node = node;
8585 unsigned long throw_flag = 0;
8586
8587 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
8588 LABEL *splabel = NEW_LABEL(0);
8589 debugs("next in while loop\n");
8590 ADD_LABEL(ret, splabel);
8591 CHECK(COMPILE(ret, "next val/valid syntax?", RNODE_NEXT(node)->nd_stts));
8592 add_ensure_iseq(ret, iseq, 0);
8593 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
8594 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8595 ADD_ADJUST_RESTORE(ret, splabel);
8596 if (!popped) {
8597 ADD_INSN(ret, line_node, putnil);
8598 }
8599 }
8600 else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
8601 LABEL *splabel = NEW_LABEL(0);
8602 debugs("next in block\n");
8603 ADD_LABEL(ret, splabel);
8604 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
8605 CHECK(COMPILE(ret, "next val", RNODE_NEXT(node)->nd_stts));
8606 add_ensure_iseq(ret, iseq, 0);
8607 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
8608 ADD_ADJUST_RESTORE(ret, splabel);
8609
8610 if (!popped) {
8611 ADD_INSN(ret, line_node, putnil);
8612 }
8613 }
8614 else {
8615 const rb_iseq_t *ip = iseq;
8616
8617 while (ip) {
8618 if (!ISEQ_COMPILE_DATA(ip)) {
8619 ip = 0;
8620 break;
8621 }
8622
8623 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
8624 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8625 /* while loop */
8626 break;
8627 }
8628 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8629 break;
8630 }
8631 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8632 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with next");
8633 return COMPILE_NG;
8634 }
8635
8636 ip = ISEQ_BODY(ip)->parent_iseq;
8637 }
8638 if (ip != 0) {
8639 CHECK(COMPILE(ret, "next val", RNODE_NEXT(node)->nd_stts));
8640 ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_NEXT));
8641
8642 if (popped) {
8643 ADD_INSN(ret, line_node, pop);
8644 }
8645 }
8646 else {
8647 COMPILE_ERROR(ERROR_ARGS "Invalid next");
8648 return COMPILE_NG;
8649 }
8650 }
8651 return COMPILE_OK;
8652}
8653
8654static int
8655compile_redo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8656{
8657 const NODE *line_node = node;
8658
8659 if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
8660 LABEL *splabel = NEW_LABEL(0);
8661 debugs("redo in while");
8662 ADD_LABEL(ret, splabel);
8663 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
8664 add_ensure_iseq(ret, iseq, 0);
8665 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
8666 ADD_ADJUST_RESTORE(ret, splabel);
8667 if (!popped) {
8668 ADD_INSN(ret, line_node, putnil);
8669 }
8670 }
8671 else if (ISEQ_BODY(iseq)->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
8672 LABEL *splabel = NEW_LABEL(0);
8673
8674 debugs("redo in block");
8675 ADD_LABEL(ret, splabel);
8676 add_ensure_iseq(ret, iseq, 0);
8677 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
8678 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8679 ADD_ADJUST_RESTORE(ret, splabel);
8680
8681 if (!popped) {
8682 ADD_INSN(ret, line_node, putnil);
8683 }
8684 }
8685 else {
8686 const rb_iseq_t *ip = iseq;
8687
8688 while (ip) {
8689 if (!ISEQ_COMPILE_DATA(ip)) {
8690 ip = 0;
8691 break;
8692 }
8693
8694 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8695 break;
8696 }
8697 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8698 break;
8699 }
8700 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8701 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with redo");
8702 return COMPILE_NG;
8703 }
8704
8705 ip = ISEQ_BODY(ip)->parent_iseq;
8706 }
8707 if (ip != 0) {
8708 ADD_INSN(ret, line_node, putnil);
8709 ADD_INSN1(ret, line_node, throw, INT2FIX(VM_THROW_NO_ESCAPE_FLAG | TAG_REDO));
8710
8711 if (popped) {
8712 ADD_INSN(ret, line_node, pop);
8713 }
8714 }
8715 else {
8716 COMPILE_ERROR(ERROR_ARGS "Invalid redo");
8717 return COMPILE_NG;
8718 }
8719 }
8720 return COMPILE_OK;
8721}
8722
8723static int
8724compile_retry(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8725{
8726 const NODE *line_node = node;
8727
8728 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
8729 ADD_INSN(ret, line_node, putnil);
8730 ADD_INSN1(ret, line_node, throw, INT2FIX(TAG_RETRY));
8731
8732 if (popped) {
8733 ADD_INSN(ret, line_node, pop);
8734 }
8735 }
8736 else {
8737 COMPILE_ERROR(ERROR_ARGS "Invalid retry");
8738 return COMPILE_NG;
8739 }
8740 return COMPILE_OK;
8741}
8742
8743static int
8744compile_rescue(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8745{
8746 const int line = nd_line(node);
8747 const NODE *line_node = node;
8748 LABEL *lstart = NEW_LABEL(line);
8749 LABEL *lend = NEW_LABEL(line);
8750 LABEL *lcont = NEW_LABEL(line);
8751 const rb_iseq_t *rescue = NEW_CHILD_ISEQ(RNODE_RESCUE(node)->nd_resq,
8752 rb_str_concat(rb_str_new2("rescue in "),
8753 ISEQ_BODY(iseq)->location.label),
8754 ISEQ_TYPE_RESCUE, line);
8755
8756 lstart->rescued = LABEL_RESCUE_BEG;
8757 lend->rescued = LABEL_RESCUE_END;
8758 ADD_LABEL(ret, lstart);
8759
8760 bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
8761 ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
8762 {
8763 CHECK(COMPILE(ret, "rescue head", RNODE_RESCUE(node)->nd_head));
8764 }
8765 ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
8766
8767 ADD_LABEL(ret, lend);
8768 if (RNODE_RESCUE(node)->nd_else) {
8769 ADD_INSN(ret, line_node, pop);
8770 CHECK(COMPILE(ret, "rescue else", RNODE_RESCUE(node)->nd_else));
8771 }
8772 ADD_INSN(ret, line_node, nop);
8773 ADD_LABEL(ret, lcont);
8774
8775 if (popped) {
8776 ADD_INSN(ret, line_node, pop);
8777 }
8778
8779 /* register catch entry */
8780 ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lcont);
8781 ADD_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
8782 return COMPILE_OK;
8783}
8784
8785static int
8786compile_resbody(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8787{
8788 const int line = nd_line(node);
8789 const NODE *line_node = node;
8790 const NODE *resq = node;
8791 const NODE *narg;
8792 LABEL *label_miss, *label_hit;
8793
8794 while (resq) {
8795 label_miss = NEW_LABEL(line);
8796 label_hit = NEW_LABEL(line);
8797
8798 narg = RNODE_RESBODY(resq)->nd_args;
8799 if (narg) {
8800 switch (nd_type(narg)) {
8801 case NODE_LIST:
8802 while (narg) {
8803 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
8804 CHECK(COMPILE(ret, "rescue arg", RNODE_LIST(narg)->nd_head));
8805 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
8806 ADD_INSNL(ret, line_node, branchif, label_hit);
8807 narg = RNODE_LIST(narg)->nd_next;
8808 }
8809 break;
8810 case NODE_SPLAT:
8811 case NODE_ARGSCAT:
8812 case NODE_ARGSPUSH:
8813 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
8814 CHECK(COMPILE(ret, "rescue/cond splat", narg));
8815 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE | VM_CHECKMATCH_ARRAY));
8816 ADD_INSNL(ret, line_node, branchif, label_hit);
8817 break;
8818 default:
8819 UNKNOWN_NODE("NODE_RESBODY", narg, COMPILE_NG);
8820 }
8821 }
8822 else {
8823 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
8824 ADD_INSN1(ret, line_node, putobject, rb_eStandardError);
8825 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
8826 ADD_INSNL(ret, line_node, branchif, label_hit);
8827 }
8828 ADD_INSNL(ret, line_node, jump, label_miss);
8829 ADD_LABEL(ret, label_hit);
8830 ADD_TRACE(ret, RUBY_EVENT_RESCUE);
8831
8832 if (RNODE_RESBODY(resq)->nd_exc_var) {
8833 CHECK(COMPILE_POPPED(ret, "resbody exc_var", RNODE_RESBODY(resq)->nd_exc_var));
8834 }
8835
8836 if (nd_type(RNODE_RESBODY(resq)->nd_body) == NODE_BEGIN && RNODE_BEGIN(RNODE_RESBODY(resq)->nd_body)->nd_body == NULL && !RNODE_RESBODY(resq)->nd_exc_var) {
8837 // empty body
8838 ADD_SYNTHETIC_INSN(ret, nd_line(RNODE_RESBODY(resq)->nd_body), -1, putnil);
8839 }
8840 else {
8841 CHECK(COMPILE(ret, "resbody body", RNODE_RESBODY(resq)->nd_body));
8842 }
8843
8844 if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) {
8845 ADD_INSN(ret, line_node, nop);
8846 }
8847 ADD_INSN(ret, line_node, leave);
8848 ADD_LABEL(ret, label_miss);
8849 resq = RNODE_RESBODY(resq)->nd_next;
8850 }
8851 return COMPILE_OK;
8852}
8853
8854static int
8855compile_ensure(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8856{
8857 const int line = nd_line(RNODE_ENSURE(node)->nd_ensr);
8858 const NODE *line_node = node;
8859 DECL_ANCHOR(ensr);
8860 const rb_iseq_t *ensure = NEW_CHILD_ISEQ(RNODE_ENSURE(node)->nd_ensr,
8861 rb_str_concat(rb_str_new2 ("ensure in "), ISEQ_BODY(iseq)->location.label),
8862 ISEQ_TYPE_ENSURE, line);
8863 LABEL *lstart = NEW_LABEL(line);
8864 LABEL *lend = NEW_LABEL(line);
8865 LABEL *lcont = NEW_LABEL(line);
8866 LINK_ELEMENT *last;
8867 int last_leave = 0;
8868 struct ensure_range er;
8870 struct ensure_range *erange;
8871
8872 INIT_ANCHOR(ensr);
8873 CHECK(COMPILE_POPPED(ensr, "ensure ensr", RNODE_ENSURE(node)->nd_ensr));
8874 last = ensr->last;
8875 last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave);
8876
8877 er.begin = lstart;
8878 er.end = lend;
8879 er.next = 0;
8880 push_ensure_entry(iseq, &enl, &er, RNODE_ENSURE(node)->nd_ensr);
8881
8882 ADD_LABEL(ret, lstart);
8883 CHECK(COMPILE_(ret, "ensure head", RNODE_ENSURE(node)->nd_head, (popped | last_leave)));
8884 ADD_LABEL(ret, lend);
8885 ADD_SEQ(ret, ensr);
8886 if (!popped && last_leave) ADD_INSN(ret, line_node, putnil);
8887 ADD_LABEL(ret, lcont);
8888 if (last_leave) ADD_INSN(ret, line_node, pop);
8889
8890 erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
8891 if (lstart->link.next != &lend->link) {
8892 while (erange) {
8893 ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end,
8894 ensure, lcont);
8895 erange = erange->next;
8896 }
8897 }
8898
8899 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
8900 return COMPILE_OK;
8901}
8902
8903static int
8904compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8905{
8906 const NODE *line_node = node;
8907
8908 if (iseq) {
8909 enum rb_iseq_type type = ISEQ_BODY(iseq)->type;
8910 const rb_iseq_t *is = iseq;
8911 enum rb_iseq_type t = type;
8912 const NODE *retval = RNODE_RETURN(node)->nd_stts;
8913 LABEL *splabel = 0;
8914
8915 while (t == ISEQ_TYPE_RESCUE || t == ISEQ_TYPE_ENSURE) {
8916 if (!(is = ISEQ_BODY(is)->parent_iseq)) break;
8917 t = ISEQ_BODY(is)->type;
8918 }
8919 switch (t) {
8920 case ISEQ_TYPE_TOP:
8921 case ISEQ_TYPE_MAIN:
8922 if (retval) {
8923 rb_warn("argument of top-level return is ignored");
8924 }
8925 if (is == iseq) {
8926 /* plain top-level, leave directly */
8927 type = ISEQ_TYPE_METHOD;
8928 }
8929 break;
8930 default:
8931 break;
8932 }
8933
8934 if (type == ISEQ_TYPE_METHOD) {
8935 splabel = NEW_LABEL(0);
8936 ADD_LABEL(ret, splabel);
8937 ADD_ADJUST(ret, line_node, 0);
8938 }
8939
8940 CHECK(COMPILE(ret, "return nd_stts (return val)", retval));
8941
8942 if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
8943 add_ensure_iseq(ret, iseq, 1);
8944 ADD_TRACE(ret, RUBY_EVENT_RETURN);
8945 ADD_INSN(ret, line_node, leave);
8946 ADD_ADJUST_RESTORE(ret, splabel);
8947
8948 if (!popped) {
8949 ADD_INSN(ret, line_node, putnil);
8950 }
8951 }
8952 else {
8953 ADD_INSN1(ret, line_node, throw, INT2FIX(TAG_RETURN));
8954 if (popped) {
8955 ADD_INSN(ret, line_node, pop);
8956 }
8957 }
8958 }
8959 return COMPILE_OK;
8960}
8961
8962static bool
8963drop_unreachable_return(LINK_ANCHOR *ret)
8964{
8965 LINK_ELEMENT *i = ret->last, *last;
8966 if (!i) return false;
8967 if (IS_TRACE(i)) i = i->prev;
8968 if (!IS_INSN(i) || !IS_INSN_ID(i, putnil)) return false;
8969 last = i = i->prev;
8970 if (IS_ADJUST(i)) i = i->prev;
8971 if (!IS_INSN(i)) return false;
8972 switch (INSN_OF(i)) {
8973 case BIN(leave):
8974 case BIN(jump):
8975 break;
8976 default:
8977 return false;
8978 }
8979 (ret->last = last->prev)->next = NULL;
8980 return true;
8981}
8982
8983static int
8984compile_evstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8985{
8986 CHECK(COMPILE_(ret, "nd_body", node, popped));
8987
8988 if (!popped && !all_string_result_p(node)) {
8989 const NODE *line_node = node;
8990 const unsigned int flag = VM_CALL_FCALL;
8991
8992 // Note, this dup could be removed if we are willing to change anytostring. It pops
8993 // two VALUEs off the stack when it could work by replacing the top most VALUE.
8994 ADD_INSN(ret, line_node, dup);
8995 ADD_INSN1(ret, line_node, objtostring, new_callinfo(iseq, idTo_s, 0, flag, NULL, FALSE));
8996 ADD_INSN(ret, line_node, anytostring);
8997 }
8998 return COMPILE_OK;
8999}
9000
9001static void
9002compile_lvar(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *line_node, ID id)
9003{
9004 int idx = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
9005
9006 debugs("id: %s idx: %d\n", rb_id2name(id), idx);
9007 ADD_GETLOCAL(ret, line_node, idx, get_lvar_level(iseq));
9008}
9009
9010static LABEL *
9011qcall_branch_start(rb_iseq_t *iseq, LINK_ANCHOR *const recv, VALUE *branches, const NODE *node, const NODE *line_node)
9012{
9013 LABEL *else_label = NEW_LABEL(nd_line(line_node));
9014 VALUE br = 0;
9015
9016 br = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "&.");
9017 *branches = br;
9018 ADD_INSN(recv, line_node, dup);
9019 ADD_INSNL(recv, line_node, branchnil, else_label);
9020 add_trace_branch_coverage(iseq, recv, nd_code_loc(node), nd_node_id(node), 0, "then", br);
9021 return else_label;
9022}
9023
9024static void
9025qcall_branch_end(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *else_label, VALUE branches, const NODE *node, const NODE *line_node)
9026{
9027 LABEL *end_label;
9028 if (!else_label) return;
9029 end_label = NEW_LABEL(nd_line(line_node));
9030 ADD_INSNL(ret, line_node, jump, end_label);
9031 ADD_LABEL(ret, else_label);
9032 add_trace_branch_coverage(iseq, ret, nd_code_loc(node), nd_node_id(node), 1, "else", branches);
9033 ADD_LABEL(ret, end_label);
9034}
9035
9036static int
9037compile_call_precheck_freeze(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const NODE *line_node, int popped)
9038{
9039 /* optimization shortcut
9040 * "literal".freeze -> opt_str_freeze("literal")
9041 */
9042 if (get_nd_recv(node) &&
9043 (nd_type_p(get_nd_recv(node), NODE_STR) || nd_type_p(get_nd_recv(node), NODE_FILE)) &&
9044 (get_node_call_nd_mid(node) == idFreeze || get_node_call_nd_mid(node) == idUMinus) &&
9045 get_nd_args(node) == NULL &&
9046 ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
9047 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
9048 VALUE str = get_string_value(get_nd_recv(node));
9049 if (get_node_call_nd_mid(node) == idUMinus) {
9050 ADD_INSN2(ret, line_node, opt_str_uminus, str,
9051 new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE));
9052 }
9053 else {
9054 ADD_INSN2(ret, line_node, opt_str_freeze, str,
9055 new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE));
9056 }
9057 RB_OBJ_WRITTEN(iseq, Qundef, str);
9058 if (popped) {
9059 ADD_INSN(ret, line_node, pop);
9060 }
9061 return TRUE;
9062 }
9063 return FALSE;
9064}
9065
9066static int
9067iseq_has_builtin_function_table(const rb_iseq_t *iseq)
9068{
9069 return ISEQ_COMPILE_DATA(iseq)->builtin_function_table != NULL;
9070}
9071
9072static const struct rb_builtin_function *
9073iseq_builtin_function_lookup(const rb_iseq_t *iseq, const char *name)
9074{
9075 int i;
9076 const struct rb_builtin_function *table = ISEQ_COMPILE_DATA(iseq)->builtin_function_table;
9077 for (i=0; table[i].index != -1; i++) {
9078 if (strcmp(table[i].name, name) == 0) {
9079 return &table[i];
9080 }
9081 }
9082 return NULL;
9083}
9084
9085static const char *
9086iseq_builtin_function_name(const enum node_type type, const NODE *recv, ID mid)
9087{
9088 const char *name = rb_id2name(mid);
9089 static const char prefix[] = "__builtin_";
9090 const size_t prefix_len = sizeof(prefix) - 1;
9091
9092 switch (type) {
9093 case NODE_CALL:
9094 if (recv) {
9095 switch (nd_type(recv)) {
9096 case NODE_VCALL:
9097 if (RNODE_VCALL(recv)->nd_mid == rb_intern("__builtin")) {
9098 return name;
9099 }
9100 break;
9101 case NODE_CONST:
9102 if (RNODE_CONST(recv)->nd_vid == rb_intern("Primitive")) {
9103 return name;
9104 }
9105 break;
9106 default: break;
9107 }
9108 }
9109 break;
9110 case NODE_VCALL:
9111 case NODE_FCALL:
9112 if (UNLIKELY(strncmp(prefix, name, prefix_len) == 0)) {
9113 return &name[prefix_len];
9114 }
9115 break;
9116 default: break;
9117 }
9118 return NULL;
9119}
9120
9121static int
9122delegate_call_p(const rb_iseq_t *iseq, unsigned int argc, const LINK_ANCHOR *args, unsigned int *pstart_index)
9123{
9124
9125 if (argc == 0) {
9126 *pstart_index = 0;
9127 return TRUE;
9128 }
9129 else if (argc <= ISEQ_BODY(iseq)->local_table_size) {
9130 unsigned int start=0;
9131
9132 // local_table: [p1, p2, p3, l1, l2, l3]
9133 // arguments: [p3, l1, l2] -> 2
9134 for (start = 0;
9135 argc + start <= ISEQ_BODY(iseq)->local_table_size;
9136 start++) {
9137 const LINK_ELEMENT *elem = FIRST_ELEMENT(args);
9138
9139 for (unsigned int i=start; i-start<argc; i++) {
9140 if (IS_INSN(elem) &&
9141 INSN_OF(elem) == BIN(getlocal)) {
9142 int local_index = FIX2INT(OPERAND_AT(elem, 0));
9143 int local_level = FIX2INT(OPERAND_AT(elem, 1));
9144
9145 if (local_level == 0) {
9146 unsigned int index = ISEQ_BODY(iseq)->local_table_size - (local_index - VM_ENV_DATA_SIZE + 1);
9147 if (0) { // for debug
9148 fprintf(stderr, "lvar:%s (%d), id:%s (%d) local_index:%d, local_size:%d\n",
9149 rb_id2name(ISEQ_BODY(iseq)->local_table[i]), i,
9150 rb_id2name(ISEQ_BODY(iseq)->local_table[index]), index,
9151 local_index, (int)ISEQ_BODY(iseq)->local_table_size);
9152 }
9153 if (i == index) {
9154 elem = elem->next;
9155 continue; /* for */
9156 }
9157 else {
9158 goto next;
9159 }
9160 }
9161 else {
9162 goto fail; // level != 0 is unsupported
9163 }
9164 }
9165 else {
9166 goto fail; // insn is not a getlocal
9167 }
9168 }
9169 goto success;
9170 next:;
9171 }
9172 fail:
9173 return FALSE;
9174 success:
9175 *pstart_index = start;
9176 return TRUE;
9177 }
9178 else {
9179 return FALSE;
9180 }
9181}
9182
9183// Compile Primitive.attr! :leaf, ...
9184static int
9185compile_builtin_attr(rb_iseq_t *iseq, const NODE *node)
9186{
9187 VALUE symbol;
9188 VALUE string;
9189 if (!node) goto no_arg;
9190 while (node) {
9191 if (!nd_type_p(node, NODE_LIST)) goto bad_arg;
9192 const NODE *next = RNODE_LIST(node)->nd_next;
9193
9194 node = RNODE_LIST(node)->nd_head;
9195 if (!node) goto no_arg;
9196 switch (nd_type(node)) {
9197 case NODE_SYM:
9198 symbol = rb_node_sym_string_val(node);
9199 break;
9200 default:
9201 goto bad_arg;
9202 }
9203
9204 if (!SYMBOL_P(symbol)) goto non_symbol_arg;
9205
9206 string = rb_sym2str(symbol);
9207 if (strcmp(RSTRING_PTR(string), "leaf") == 0) {
9208 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_LEAF;
9209 }
9210 else if (strcmp(RSTRING_PTR(string), "inline_block") == 0) {
9211 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_INLINE_BLOCK;
9212 }
9213 else if (strcmp(RSTRING_PTR(string), "use_block") == 0) {
9214 iseq_set_use_block(iseq);
9215 }
9216 else if (strcmp(RSTRING_PTR(string), "c_trace") == 0) {
9217 // Let the iseq act like a C method in backtraces
9218 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_C_TRACE;
9219 }
9220 else if (strcmp(RSTRING_PTR(string), "without_interrupts") == 0) {
9221 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_WITHOUT_INTERRUPTS;
9222 }
9223 else {
9224 goto unknown_arg;
9225 }
9226 node = next;
9227 }
9228 return COMPILE_OK;
9229 no_arg:
9230 COMPILE_ERROR(ERROR_ARGS "attr!: no argument");
9231 return COMPILE_NG;
9232 non_symbol_arg:
9233 COMPILE_ERROR(ERROR_ARGS "non symbol argument to attr!: %s", rb_builtin_class_name(symbol));
9234 return COMPILE_NG;
9235 unknown_arg:
9236 COMPILE_ERROR(ERROR_ARGS "unknown argument to attr!: %s", RSTRING_PTR(string));
9237 return COMPILE_NG;
9238 bad_arg:
9239 UNKNOWN_NODE("attr!", node, COMPILE_NG);
9240}
9241
9242static int
9243compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, const NODE *line_node, int popped)
9244{
9245 VALUE name;
9246
9247 if (!node) goto no_arg;
9248 if (!nd_type_p(node, NODE_LIST)) goto bad_arg;
9249 if (RNODE_LIST(node)->nd_next) goto too_many_arg;
9250 node = RNODE_LIST(node)->nd_head;
9251 if (!node) goto no_arg;
9252 switch (nd_type(node)) {
9253 case NODE_SYM:
9254 name = rb_node_sym_string_val(node);
9255 break;
9256 default:
9257 goto bad_arg;
9258 }
9259 if (!SYMBOL_P(name)) goto non_symbol_arg;
9260 if (!popped) {
9261 compile_lvar(iseq, ret, line_node, SYM2ID(name));
9262 }
9263 return COMPILE_OK;
9264 no_arg:
9265 COMPILE_ERROR(ERROR_ARGS "arg!: no argument");
9266 return COMPILE_NG;
9267 too_many_arg:
9268 COMPILE_ERROR(ERROR_ARGS "arg!: too many argument");
9269 return COMPILE_NG;
9270 non_symbol_arg:
9271 COMPILE_ERROR(ERROR_ARGS "non symbol argument to arg!: %s",
9272 rb_builtin_class_name(name));
9273 return COMPILE_NG;
9274 bad_arg:
9275 UNKNOWN_NODE("arg!", node, COMPILE_NG);
9276}
9277
9278static NODE *
9279mandatory_node(const rb_iseq_t *iseq, const NODE *cond_node)
9280{
9281 const NODE *node = ISEQ_COMPILE_DATA(iseq)->root_node;
9282 if (nd_type(node) == NODE_IF && RNODE_IF(node)->nd_cond == cond_node) {
9283 return RNODE_IF(node)->nd_body;
9284 }
9285 else {
9286 rb_bug("mandatory_node: can't find mandatory node");
9287 }
9288}
9289
9290static int
9291compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const NODE *line_node)
9292{
9293 // arguments
9294 struct rb_args_info args = {
9295 .pre_args_num = ISEQ_BODY(iseq)->param.lead_num,
9296 };
9297 rb_node_args_t args_node;
9298 rb_node_init(RNODE(&args_node), NODE_ARGS);
9299 args_node.nd_ainfo = args;
9300
9301 // local table without non-mandatory parameters
9302 const int skip_local_size = ISEQ_BODY(iseq)->param.size - ISEQ_BODY(iseq)->param.lead_num;
9303 const int table_size = ISEQ_BODY(iseq)->local_table_size - skip_local_size;
9304
9305 VALUE idtmp = 0;
9306 rb_ast_id_table_t *tbl = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
9307 tbl->size = table_size;
9308
9309 int i;
9310
9311 // lead parameters
9312 for (i=0; i<ISEQ_BODY(iseq)->param.lead_num; i++) {
9313 tbl->ids[i] = ISEQ_BODY(iseq)->local_table[i];
9314 }
9315 // local variables
9316 for (; i<table_size; i++) {
9317 tbl->ids[i] = ISEQ_BODY(iseq)->local_table[i + skip_local_size];
9318 }
9319
9320 rb_node_scope_t scope_node;
9321 rb_node_init(RNODE(&scope_node), NODE_SCOPE);
9322 scope_node.nd_tbl = tbl;
9323 scope_node.nd_body = mandatory_node(iseq, node);
9324 scope_node.nd_parent = NULL;
9325 scope_node.nd_args = &args_node;
9326
9327 VALUE ast_value = rb_ruby_ast_new(RNODE(&scope_node));
9328
9329 const rb_iseq_t *mandatory_only_iseq =
9330 rb_iseq_new_with_opt(ast_value, rb_iseq_base_label(iseq),
9331 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
9332 nd_line(line_node), NULL, 0,
9333 ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option,
9334 ISEQ_BODY(iseq)->variable.script_lines);
9335 RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->mandatory_only_iseq, (VALUE)mandatory_only_iseq);
9336
9337 ALLOCV_END(idtmp);
9338 return COMPILE_OK;
9339}
9340
9341static int
9342compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const NODE *line_node, int popped,
9343 const rb_iseq_t *parent_block, LINK_ANCHOR *args, const char *builtin_func)
9344{
9345 NODE *args_node = get_nd_args(node);
9346
9347 if (parent_block != NULL) {
9348 COMPILE_ERROR(ERROR_ARGS_AT(line_node) "should not call builtins here.");
9349 return COMPILE_NG;
9350 }
9351 else {
9352# define BUILTIN_INLINE_PREFIX "_bi"
9353 char inline_func[sizeof(BUILTIN_INLINE_PREFIX) + DECIMAL_SIZE_OF(int)];
9354 bool cconst = false;
9355 retry:;
9356 const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
9357
9358 if (bf == NULL) {
9359 if (strcmp("cstmt!", builtin_func) == 0 ||
9360 strcmp("cexpr!", builtin_func) == 0) {
9361 // ok
9362 }
9363 else if (strcmp("cconst!", builtin_func) == 0) {
9364 cconst = true;
9365 }
9366 else if (strcmp("cinit!", builtin_func) == 0) {
9367 // ignore
9368 return COMPILE_OK;
9369 }
9370 else if (strcmp("attr!", builtin_func) == 0) {
9371 return compile_builtin_attr(iseq, args_node);
9372 }
9373 else if (strcmp("arg!", builtin_func) == 0) {
9374 return compile_builtin_arg(iseq, ret, args_node, line_node, popped);
9375 }
9376 else if (strcmp("mandatory_only?", builtin_func) == 0) {
9377 if (popped) {
9378 rb_bug("mandatory_only? should be in if condition");
9379 }
9380 else if (!LIST_INSN_SIZE_ZERO(ret)) {
9381 rb_bug("mandatory_only? should be put on top");
9382 }
9383
9384 ADD_INSN1(ret, line_node, putobject, Qfalse);
9385 return compile_builtin_mandatory_only_method(iseq, node, line_node);
9386 }
9387 else if (1) {
9388 rb_bug("can't find builtin function:%s", builtin_func);
9389 }
9390 else {
9391 COMPILE_ERROR(ERROR_ARGS "can't find builtin function:%s", builtin_func);
9392 return COMPILE_NG;
9393 }
9394
9395 int inline_index = nd_line(node);
9396 snprintf(inline_func, sizeof(inline_func), BUILTIN_INLINE_PREFIX "%d", inline_index);
9397 builtin_func = inline_func;
9398 args_node = NULL;
9399 goto retry;
9400 }
9401
9402 if (cconst) {
9403 typedef VALUE(*builtin_func0)(void *, VALUE);
9404 VALUE const_val = (*(builtin_func0)(uintptr_t)bf->func_ptr)(NULL, Qnil);
9405 ADD_INSN1(ret, line_node, putobject, const_val);
9406 return COMPILE_OK;
9407 }
9408
9409 // fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
9410
9411 unsigned int flag = 0;
9412 struct rb_callinfo_kwarg *keywords = NULL;
9413 VALUE argc = setup_args(iseq, args, args_node, &flag, &keywords);
9414
9415 if (FIX2INT(argc) != bf->argc) {
9416 COMPILE_ERROR(ERROR_ARGS "argc is not match for builtin function:%s (expect %d but %d)",
9417 builtin_func, bf->argc, FIX2INT(argc));
9418 return COMPILE_NG;
9419 }
9420
9421 unsigned int start_index;
9422 if (delegate_call_p(iseq, FIX2INT(argc), args, &start_index)) {
9423 ADD_INSN2(ret, line_node, opt_invokebuiltin_delegate, bf, INT2FIX(start_index));
9424 }
9425 else {
9426 ADD_SEQ(ret, args);
9427 ADD_INSN1(ret, line_node, invokebuiltin, bf);
9428 }
9429
9430 if (popped) ADD_INSN(ret, line_node, pop);
9431 return COMPILE_OK;
9432 }
9433}
9434
9435static int
9436compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const enum node_type type, const NODE *const line_node, int popped, bool assume_receiver)
9437{
9438 /* call: obj.method(...)
9439 * fcall: func(...)
9440 * vcall: func
9441 */
9442 DECL_ANCHOR(recv);
9443 DECL_ANCHOR(args);
9444 ID mid = get_node_call_nd_mid(node);
9445 VALUE argc;
9446 unsigned int flag = 0;
9447 struct rb_callinfo_kwarg *keywords = NULL;
9448 const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
9449 LABEL *else_label = NULL;
9450 VALUE branches = Qfalse;
9451
9452 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
9453
9454 INIT_ANCHOR(recv);
9455 INIT_ANCHOR(args);
9456
9457#if OPT_SUPPORT_JOKE
9458 if (nd_type_p(node, NODE_VCALL)) {
9459 ID id_bitblt;
9460 ID id_answer;
9461
9462 CONST_ID(id_bitblt, "bitblt");
9463 CONST_ID(id_answer, "the_answer_to_life_the_universe_and_everything");
9464
9465 if (mid == id_bitblt) {
9466 ADD_INSN(ret, line_node, bitblt);
9467 return COMPILE_OK;
9468 }
9469 else if (mid == id_answer) {
9470 ADD_INSN(ret, line_node, answer);
9471 return COMPILE_OK;
9472 }
9473 }
9474 /* only joke */
9475 {
9476 ID goto_id;
9477 ID label_id;
9478
9479 CONST_ID(goto_id, "__goto__");
9480 CONST_ID(label_id, "__label__");
9481
9482 if (nd_type_p(node, NODE_FCALL) &&
9483 (mid == goto_id || mid == label_id)) {
9484 LABEL *label;
9485 st_data_t data;
9486 st_table *labels_table = ISEQ_COMPILE_DATA(iseq)->labels_table;
9487 VALUE label_name;
9488
9489 if (!labels_table) {
9490 labels_table = st_init_numtable();
9491 ISEQ_COMPILE_DATA(iseq)->labels_table = labels_table;
9492 }
9493 {
9494 COMPILE_ERROR(ERROR_ARGS "invalid goto/label format");
9495 return COMPILE_NG;
9496 }
9497
9498 if (mid == goto_id) {
9499 ADD_INSNL(ret, line_node, jump, label);
9500 }
9501 else {
9502 ADD_LABEL(ret, label);
9503 }
9504 return COMPILE_OK;
9505 }
9506 }
9507#endif
9508
9509 const char *builtin_func;
9510 if (UNLIKELY(iseq_has_builtin_function_table(iseq)) &&
9511 (builtin_func = iseq_builtin_function_name(type, get_nd_recv(node), mid)) != NULL) {
9512 return compile_builtin_function_call(iseq, ret, node, line_node, popped, parent_block, args, builtin_func);
9513 }
9514
9515 /* receiver */
9516 if (!assume_receiver) {
9517 if (type == NODE_CALL || type == NODE_OPCALL || type == NODE_QCALL) {
9518 int idx, level;
9519
9520 if (mid == idCall &&
9521 nd_type_p(get_nd_recv(node), NODE_LVAR) &&
9522 iseq_block_param_id_p(iseq, RNODE_LVAR(get_nd_recv(node))->nd_vid, &idx, &level)) {
9523 ADD_INSN2(recv, get_nd_recv(node), getblockparamproxy, INT2FIX(idx + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
9524 }
9525 else if (private_recv_p(node)) {
9526 ADD_INSN(recv, node, putself);
9527 flag |= VM_CALL_FCALL;
9528 }
9529 else {
9530 CHECK(COMPILE(recv, "recv", get_nd_recv(node)));
9531 }
9532
9533 if (type == NODE_QCALL) {
9534 else_label = qcall_branch_start(iseq, recv, &branches, node, line_node);
9535 }
9536 }
9537 else if (type == NODE_FCALL || type == NODE_VCALL) {
9538 ADD_CALL_RECEIVER(recv, line_node);
9539 }
9540 }
9541
9542 /* args */
9543 if (type != NODE_VCALL) {
9544 argc = setup_args(iseq, args, get_nd_args(node), &flag, &keywords);
9545 CHECK(!NIL_P(argc));
9546 }
9547 else {
9548 argc = INT2FIX(0);
9549 }
9550
9551 ADD_SEQ(ret, recv);
9552
9553 bool inline_new = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction &&
9554 mid == rb_intern("new") &&
9555 parent_block == NULL &&
9556 !(flag & VM_CALL_ARGS_BLOCKARG);
9557
9558 if (inline_new) {
9559 ADD_INSN(ret, node, putnil);
9560 ADD_INSN(ret, node, swap);
9561 }
9562
9563 ADD_SEQ(ret, args);
9564
9565 debugp_param("call args argc", argc);
9566 debugp_param("call method", ID2SYM(mid));
9567
9568 switch ((int)type) {
9569 case NODE_VCALL:
9570 flag |= VM_CALL_VCALL;
9571 /* VCALL is funcall, so fall through */
9572 case NODE_FCALL:
9573 flag |= VM_CALL_FCALL;
9574 }
9575
9576 if ((flag & VM_CALL_ARGS_BLOCKARG) && (flag & VM_CALL_KW_SPLAT) && !(flag & VM_CALL_KW_SPLAT_MUT)) {
9577 ADD_INSN(ret, line_node, splatkw);
9578 }
9579
9580 LABEL *not_basic_new = NEW_LABEL(nd_line(node));
9581 LABEL *not_basic_new_finish = NEW_LABEL(nd_line(node));
9582
9583 if (inline_new) {
9584 // Jump unless the receiver uses the "basic" implementation of "new"
9585 VALUE ci;
9586 if (flag & VM_CALL_FORWARDING) {
9587 ci = (VALUE)new_callinfo(iseq, mid, NUM2INT(argc) + 1, flag, keywords, 0);
9588 }
9589 else {
9590 ci = (VALUE)new_callinfo(iseq, mid, NUM2INT(argc), flag, keywords, 0);
9591 }
9592 ADD_INSN2(ret, node, opt_new, ci, not_basic_new);
9593 LABEL_REF(not_basic_new);
9594
9595 // optimized path
9596 ADD_SEND_R(ret, line_node, rb_intern("initialize"), argc, parent_block, INT2FIX(flag | VM_CALL_FCALL), keywords);
9597 ADD_INSNL(ret, line_node, jump, not_basic_new_finish);
9598
9599 ADD_LABEL(ret, not_basic_new);
9600 // Fall back to normal send
9601 ADD_SEND_R(ret, line_node, mid, argc, parent_block, INT2FIX(flag), keywords);
9602 ADD_INSN(ret, line_node, swap);
9603
9604 ADD_LABEL(ret, not_basic_new_finish);
9605 ADD_INSN(ret, line_node, pop);
9606 }
9607 else {
9608 ADD_SEND_R(ret, line_node, mid, argc, parent_block, INT2FIX(flag), keywords);
9609 }
9610
9611 qcall_branch_end(iseq, ret, else_label, branches, node, line_node);
9612 if (popped) {
9613 ADD_INSN(ret, line_node, pop);
9614 }
9615 return COMPILE_OK;
9616}
9617
9618static int
9619compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9620{
9621 const int line = nd_line(node);
9622 VALUE argc;
9623 unsigned int flag = 0;
9624 int asgnflag = 0;
9625 ID id = RNODE_OP_ASGN1(node)->nd_mid;
9626
9627 /*
9628 * a[x] (op)= y
9629 *
9630 * nil # nil
9631 * eval a # nil a
9632 * eval x # nil a x
9633 * dupn 2 # nil a x a x
9634 * send :[] # nil a x a[x]
9635 * eval y # nil a x a[x] y
9636 * send op # nil a x ret
9637 * setn 3 # ret a x ret
9638 * send []= # ret ?
9639 * pop # ret
9640 */
9641
9642 /*
9643 * nd_recv[nd_args->nd_body] (nd_mid)= nd_args->nd_head;
9644 * NODE_OP_ASGN nd_recv
9645 * nd_args->nd_head
9646 * nd_args->nd_body
9647 * nd_mid
9648 */
9649
9650 if (!popped) {
9651 ADD_INSN(ret, node, putnil);
9652 }
9653 asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN1 recv", node, RNODE_OP_ASGN1(node)->nd_recv);
9654 CHECK(asgnflag != -1);
9655 switch (nd_type(RNODE_OP_ASGN1(node)->nd_index)) {
9656 case NODE_ZLIST:
9657 argc = INT2FIX(0);
9658 break;
9659 default:
9660 argc = setup_args(iseq, ret, RNODE_OP_ASGN1(node)->nd_index, &flag, NULL);
9661 CHECK(!NIL_P(argc));
9662 }
9663 int dup_argn = FIX2INT(argc) + 1;
9664 ADD_INSN1(ret, node, dupn, INT2FIX(dup_argn));
9665 flag |= asgnflag;
9666 ADD_SEND_R(ret, node, idAREF, argc, NULL, INT2FIX(flag & ~VM_CALL_ARGS_SPLAT_MUT), NULL);
9667
9668 if (id == idOROP || id == idANDOP) {
9669 /* a[x] ||= y or a[x] &&= y
9670
9671 unless/if a[x]
9672 a[x]= y
9673 else
9674 nil
9675 end
9676 */
9677 LABEL *label = NEW_LABEL(line);
9678 LABEL *lfin = NEW_LABEL(line);
9679
9680 ADD_INSN(ret, node, dup);
9681 if (id == idOROP) {
9682 ADD_INSNL(ret, node, branchif, label);
9683 }
9684 else { /* idANDOP */
9685 ADD_INSNL(ret, node, branchunless, label);
9686 }
9687 ADD_INSN(ret, node, pop);
9688
9689 CHECK(COMPILE(ret, "NODE_OP_ASGN1 nd_rvalue: ", RNODE_OP_ASGN1(node)->nd_rvalue));
9690 if (!popped) {
9691 ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
9692 }
9693 if (flag & VM_CALL_ARGS_SPLAT) {
9694 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
9695 ADD_INSN(ret, node, swap);
9696 ADD_INSN1(ret, node, splatarray, Qtrue);
9697 ADD_INSN(ret, node, swap);
9698 flag |= VM_CALL_ARGS_SPLAT_MUT;
9699 }
9700 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
9701 ADD_SEND_R(ret, node, idASET, argc, NULL, INT2FIX(flag), NULL);
9702 }
9703 else {
9704 ADD_SEND_R(ret, node, idASET, FIXNUM_INC(argc, 1), NULL, INT2FIX(flag), NULL);
9705 }
9706 ADD_INSN(ret, node, pop);
9707 ADD_INSNL(ret, node, jump, lfin);
9708 ADD_LABEL(ret, label);
9709 if (!popped) {
9710 ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
9711 }
9712 ADD_INSN1(ret, node, adjuststack, INT2FIX(dup_argn+1));
9713 ADD_LABEL(ret, lfin);
9714 }
9715 else {
9716 CHECK(COMPILE(ret, "NODE_OP_ASGN1 nd_rvalue: ", RNODE_OP_ASGN1(node)->nd_rvalue));
9717 ADD_SEND(ret, node, id, INT2FIX(1));
9718 if (!popped) {
9719 ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
9720 }
9721 if (flag & VM_CALL_ARGS_SPLAT) {
9722 if (flag & VM_CALL_KW_SPLAT) {
9723 ADD_INSN1(ret, node, topn, INT2FIX(2));
9724 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
9725 ADD_INSN1(ret, node, splatarray, Qtrue);
9726 flag |= VM_CALL_ARGS_SPLAT_MUT;
9727 }
9728 ADD_INSN(ret, node, swap);
9729 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
9730 ADD_INSN1(ret, node, setn, INT2FIX(2));
9731 ADD_INSN(ret, node, pop);
9732 }
9733 else {
9734 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
9735 ADD_INSN(ret, node, swap);
9736 ADD_INSN1(ret, node, splatarray, Qtrue);
9737 ADD_INSN(ret, node, swap);
9738 flag |= VM_CALL_ARGS_SPLAT_MUT;
9739 }
9740 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
9741 }
9742 ADD_SEND_R(ret, node, idASET, argc, NULL, INT2FIX(flag), NULL);
9743 }
9744 else {
9745 ADD_SEND_R(ret, node, idASET, FIXNUM_INC(argc, 1), NULL, INT2FIX(flag), NULL);
9746 }
9747 ADD_INSN(ret, node, pop);
9748 }
9749 return COMPILE_OK;
9750}
9751
9752static int
9753compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9754{
9755 const int line = nd_line(node);
9756 ID atype = RNODE_OP_ASGN2(node)->nd_mid;
9757 ID vid = RNODE_OP_ASGN2(node)->nd_vid, aid = rb_id_attrset(vid);
9758 int asgnflag;
9759 LABEL *lfin = NEW_LABEL(line);
9760 LABEL *lcfin = NEW_LABEL(line);
9761 LABEL *lskip = 0;
9762 /*
9763 class C; attr_accessor :c; end
9764 r = C.new
9765 r.a &&= v # asgn2
9766
9767 eval r # r
9768 dup # r r
9769 eval r.a # r o
9770
9771 # or
9772 dup # r o o
9773 if lcfin # r o
9774 pop # r
9775 eval v # r v
9776 swap # v r
9777 topn 1 # v r v
9778 send a= # v ?
9779 jump lfin # v ?
9780
9781 lcfin: # r o
9782 swap # o r
9783
9784 lfin: # o ?
9785 pop # o
9786
9787 # or (popped)
9788 if lcfin # r
9789 eval v # r v
9790 send a= # ?
9791 jump lfin # ?
9792
9793 lcfin: # r
9794
9795 lfin: # ?
9796 pop #
9797
9798 # and
9799 dup # r o o
9800 unless lcfin
9801 pop # r
9802 eval v # r v
9803 swap # v r
9804 topn 1 # v r v
9805 send a= # v ?
9806 jump lfin # v ?
9807
9808 # others
9809 eval v # r o v
9810 send ?? # r w
9811 send a= # w
9812
9813 */
9814
9815 asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN2#recv", node, RNODE_OP_ASGN2(node)->nd_recv);
9816 CHECK(asgnflag != -1);
9817 if (RNODE_OP_ASGN2(node)->nd_aid) {
9818 lskip = NEW_LABEL(line);
9819 ADD_INSN(ret, node, dup);
9820 ADD_INSNL(ret, node, branchnil, lskip);
9821 }
9822 ADD_INSN(ret, node, dup);
9823 ADD_SEND_WITH_FLAG(ret, node, vid, INT2FIX(0), INT2FIX(asgnflag));
9824
9825 if (atype == idOROP || atype == idANDOP) {
9826 if (!popped) {
9827 ADD_INSN(ret, node, dup);
9828 }
9829 if (atype == idOROP) {
9830 ADD_INSNL(ret, node, branchif, lcfin);
9831 }
9832 else { /* idANDOP */
9833 ADD_INSNL(ret, node, branchunless, lcfin);
9834 }
9835 if (!popped) {
9836 ADD_INSN(ret, node, pop);
9837 }
9838 CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", RNODE_OP_ASGN2(node)->nd_value));
9839 if (!popped) {
9840 ADD_INSN(ret, node, swap);
9841 ADD_INSN1(ret, node, topn, INT2FIX(1));
9842 }
9843 ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
9844 ADD_INSNL(ret, node, jump, lfin);
9845
9846 ADD_LABEL(ret, lcfin);
9847 if (!popped) {
9848 ADD_INSN(ret, node, swap);
9849 }
9850
9851 ADD_LABEL(ret, lfin);
9852 }
9853 else {
9854 CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", RNODE_OP_ASGN2(node)->nd_value));
9855 ADD_SEND(ret, node, atype, INT2FIX(1));
9856 if (!popped) {
9857 ADD_INSN(ret, node, swap);
9858 ADD_INSN1(ret, node, topn, INT2FIX(1));
9859 }
9860 ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
9861 }
9862 if (lskip && popped) {
9863 ADD_LABEL(ret, lskip);
9864 }
9865 ADD_INSN(ret, node, pop);
9866 if (lskip && !popped) {
9867 ADD_LABEL(ret, lskip);
9868 }
9869 return COMPILE_OK;
9870}
9871
9872static int compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value);
9873
9874static int
9875compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9876{
9877 const int line = nd_line(node);
9878 LABEL *lfin = 0;
9879 LABEL *lassign = 0;
9880 ID mid;
9881
9882 switch (nd_type(RNODE_OP_CDECL(node)->nd_head)) {
9883 case NODE_COLON3:
9884 ADD_INSN1(ret, node, putobject, rb_cObject);
9885 break;
9886 case NODE_COLON2:
9887 CHECK(COMPILE(ret, "NODE_OP_CDECL/colon2#nd_head", RNODE_COLON2(RNODE_OP_CDECL(node)->nd_head)->nd_head));
9888 break;
9889 default:
9890 COMPILE_ERROR(ERROR_ARGS "%s: invalid node in NODE_OP_CDECL",
9891 ruby_node_name(nd_type(RNODE_OP_CDECL(node)->nd_head)));
9892 return COMPILE_NG;
9893 }
9894 mid = get_node_colon_nd_mid(RNODE_OP_CDECL(node)->nd_head);
9895 /* cref */
9896 if (RNODE_OP_CDECL(node)->nd_aid == idOROP) {
9897 lassign = NEW_LABEL(line);
9898 ADD_INSN(ret, node, dup); /* cref cref */
9899 ADD_INSN3(ret, node, defined, INT2FIX(DEFINED_CONST_FROM),
9900 ID2SYM(mid), Qtrue); /* cref bool */
9901 ADD_INSNL(ret, node, branchunless, lassign); /* cref */
9902 }
9903 ADD_INSN(ret, node, dup); /* cref cref */
9904 ADD_INSN1(ret, node, putobject, Qtrue);
9905 ADD_INSN1(ret, node, getconstant, ID2SYM(mid)); /* cref obj */
9906
9907 if (RNODE_OP_CDECL(node)->nd_aid == idOROP || RNODE_OP_CDECL(node)->nd_aid == idANDOP) {
9908 lfin = NEW_LABEL(line);
9909 if (!popped) ADD_INSN(ret, node, dup); /* cref [obj] obj */
9910 if (RNODE_OP_CDECL(node)->nd_aid == idOROP)
9911 ADD_INSNL(ret, node, branchif, lfin);
9912 else /* idANDOP */
9913 ADD_INSNL(ret, node, branchunless, lfin);
9914 /* cref [obj] */
9915 if (!popped) ADD_INSN(ret, node, pop); /* cref */
9916 if (lassign) ADD_LABEL(ret, lassign);
9917 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value));
9918 /* cref value */
9919 if (popped)
9920 ADD_INSN1(ret, node, topn, INT2FIX(1)); /* cref value cref */
9921 else {
9922 ADD_INSN1(ret, node, dupn, INT2FIX(2)); /* cref value cref value */
9923 ADD_INSN(ret, node, swap); /* cref value value cref */
9924 }
9925 ADD_INSN1(ret, node, setconstant, ID2SYM(mid)); /* cref [value] */
9926 ADD_LABEL(ret, lfin); /* cref [value] */
9927 if (!popped) ADD_INSN(ret, node, swap); /* [value] cref */
9928 ADD_INSN(ret, node, pop); /* [value] */
9929 }
9930 else {
9931 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value));
9932 /* cref obj value */
9933 ADD_CALL(ret, node, RNODE_OP_CDECL(node)->nd_aid, INT2FIX(1));
9934 /* cref value */
9935 ADD_INSN(ret, node, swap); /* value cref */
9936 if (!popped) {
9937 ADD_INSN1(ret, node, topn, INT2FIX(1)); /* value cref value */
9938 ADD_INSN(ret, node, swap); /* value value cref */
9939 }
9940 ADD_INSN1(ret, node, setconstant, ID2SYM(mid));
9941 }
9942 return COMPILE_OK;
9943}
9944
9945static int
9946compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
9947{
9948 const int line = nd_line(node);
9949 LABEL *lfin = NEW_LABEL(line);
9950 LABEL *lassign;
9951
9952 if (type == NODE_OP_ASGN_OR && !nd_type_p(RNODE_OP_ASGN_OR(node)->nd_head, NODE_IVAR)) {
9953 LABEL *lfinish[2];
9954 lfinish[0] = lfin;
9955 lfinish[1] = 0;
9956 defined_expr(iseq, ret, RNODE_OP_ASGN_OR(node)->nd_head, lfinish, Qfalse, false);
9957 lassign = lfinish[1];
9958 if (!lassign) {
9959 lassign = NEW_LABEL(line);
9960 }
9961 ADD_INSNL(ret, node, branchunless, lassign);
9962 }
9963 else {
9964 lassign = NEW_LABEL(line);
9965 }
9966
9967 CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", RNODE_OP_ASGN_OR(node)->nd_head));
9968
9969 if (!popped) {
9970 ADD_INSN(ret, node, dup);
9971 }
9972
9973 if (type == NODE_OP_ASGN_AND) {
9974 ADD_INSNL(ret, node, branchunless, lfin);
9975 }
9976 else {
9977 ADD_INSNL(ret, node, branchif, lfin);
9978 }
9979
9980 if (!popped) {
9981 ADD_INSN(ret, node, pop);
9982 }
9983
9984 ADD_LABEL(ret, lassign);
9985 CHECK(COMPILE_(ret, "NODE_OP_ASGN_AND/OR#nd_value", RNODE_OP_ASGN_OR(node)->nd_value, popped));
9986 ADD_LABEL(ret, lfin);
9987 return COMPILE_OK;
9988}
9989
9990static int
9991compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
9992{
9993 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
9994 DECL_ANCHOR(args);
9995 int argc;
9996 unsigned int flag = 0;
9997 struct rb_callinfo_kwarg *keywords = NULL;
9998 const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
9999 int use_block = 1;
10000
10001 INIT_ANCHOR(args);
10002 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
10003
10004 if (type == NODE_SUPER) {
10005 VALUE vargc = setup_args(iseq, args, RNODE_SUPER(node)->nd_args, &flag, &keywords);
10006 CHECK(!NIL_P(vargc));
10007 argc = FIX2INT(vargc);
10008 if ((flag & VM_CALL_ARGS_BLOCKARG) && (flag & VM_CALL_KW_SPLAT) && !(flag & VM_CALL_KW_SPLAT_MUT)) {
10009 ADD_INSN(args, node, splatkw);
10010 }
10011
10012 if (flag & VM_CALL_ARGS_BLOCKARG) {
10013 use_block = 0;
10014 }
10015 }
10016 else {
10017 /* NODE_ZSUPER */
10018 int i;
10019 const rb_iseq_t *liseq = body->local_iseq;
10020 const struct rb_iseq_constant_body *const local_body = ISEQ_BODY(liseq);
10021 const struct rb_iseq_param_keyword *const local_kwd = local_body->param.keyword;
10022 int lvar_level = get_lvar_level(iseq);
10023
10024 argc = local_body->param.lead_num;
10025
10026 /* normal arguments */
10027 for (i = 0; i < local_body->param.lead_num; i++) {
10028 int idx = local_body->local_table_size - i;
10029 ADD_GETLOCAL(args, node, idx, lvar_level);
10030 }
10031
10032 /* forward ... */
10033 if (local_body->param.flags.forwardable) {
10034 flag |= VM_CALL_FORWARDING;
10035 int idx = local_body->local_table_size - get_local_var_idx(liseq, idDot3);
10036 ADD_GETLOCAL(args, node, idx, lvar_level);
10037 }
10038
10039 if (local_body->param.flags.has_opt) {
10040 /* optional arguments */
10041 int j;
10042 for (j = 0; j < local_body->param.opt_num; j++) {
10043 int idx = local_body->local_table_size - (i + j);
10044 ADD_GETLOCAL(args, node, idx, lvar_level);
10045 }
10046 i += j;
10047 argc = i;
10048 }
10049 if (local_body->param.flags.has_rest) {
10050 /* rest argument */
10051 int idx = local_body->local_table_size - local_body->param.rest_start;
10052 ADD_GETLOCAL(args, node, idx, lvar_level);
10053 ADD_INSN1(args, node, splatarray, RBOOL(local_body->param.flags.has_post));
10054
10055 argc = local_body->param.rest_start + 1;
10056 flag |= VM_CALL_ARGS_SPLAT;
10057 }
10058 if (local_body->param.flags.has_post) {
10059 /* post arguments */
10060 int post_len = local_body->param.post_num;
10061 int post_start = local_body->param.post_start;
10062
10063 if (local_body->param.flags.has_rest) {
10064 int j;
10065 for (j=0; j<post_len; j++) {
10066 int idx = local_body->local_table_size - (post_start + j);
10067 ADD_GETLOCAL(args, node, idx, lvar_level);
10068 }
10069 ADD_INSN1(args, node, pushtoarray, INT2FIX(j));
10070 flag |= VM_CALL_ARGS_SPLAT_MUT;
10071 /* argc is settled at above */
10072 }
10073 else {
10074 int j;
10075 for (j=0; j<post_len; j++) {
10076 int idx = local_body->local_table_size - (post_start + j);
10077 ADD_GETLOCAL(args, node, idx, lvar_level);
10078 }
10079 argc = post_len + post_start;
10080 }
10081 }
10082
10083 if (local_body->param.flags.has_kw) { /* TODO: support keywords */
10084 int local_size = local_body->local_table_size;
10085 argc++;
10086
10087 ADD_INSN1(args, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10088
10089 if (local_body->param.flags.has_kwrest) {
10090 int idx = local_body->local_table_size - local_kwd->rest_start;
10091 ADD_GETLOCAL(args, node, idx, lvar_level);
10092 RUBY_ASSERT(local_kwd->num > 0);
10093 ADD_SEND (args, node, rb_intern("dup"), INT2FIX(0));
10094 }
10095 else {
10096 ADD_INSN1(args, node, newhash, INT2FIX(0));
10097 }
10098 for (i = 0; i < local_kwd->num; ++i) {
10099 ID id = local_kwd->table[i];
10100 int idx = local_size - get_local_var_idx(liseq, id);
10101 ADD_INSN1(args, node, putobject, ID2SYM(id));
10102 ADD_GETLOCAL(args, node, idx, lvar_level);
10103 }
10104 ADD_SEND(args, node, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
10105 flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT;
10106 }
10107 else if (local_body->param.flags.has_kwrest) {
10108 int idx = local_body->local_table_size - local_kwd->rest_start;
10109 ADD_GETLOCAL(args, node, idx, lvar_level);
10110 argc++;
10111 flag |= VM_CALL_KW_SPLAT;
10112 }
10113 }
10114
10115 if (use_block && parent_block == NULL) {
10116 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
10117 }
10118
10119 flag |= VM_CALL_SUPER | VM_CALL_FCALL;
10120 if (type == NODE_ZSUPER) flag |= VM_CALL_ZSUPER;
10121 ADD_INSN(ret, node, putself);
10122 ADD_SEQ(ret, args);
10123
10124 const struct rb_callinfo * ci = new_callinfo(iseq, 0, argc, flag, keywords, parent_block != NULL);
10125
10126 if (vm_ci_flag(ci) & VM_CALL_FORWARDING) {
10127 ADD_INSN2(ret, node, invokesuperforward, ci, parent_block);
10128 }
10129 else {
10130 ADD_INSN2(ret, node, invokesuper, ci, parent_block);
10131 }
10132
10133 if (popped) {
10134 ADD_INSN(ret, node, pop);
10135 }
10136 return COMPILE_OK;
10137}
10138
10139static int
10140compile_yield(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10141{
10142 DECL_ANCHOR(args);
10143 VALUE argc;
10144 unsigned int flag = 0;
10145 struct rb_callinfo_kwarg *keywords = NULL;
10146
10147 INIT_ANCHOR(args);
10148
10149 switch (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->type) {
10150 case ISEQ_TYPE_TOP:
10151 case ISEQ_TYPE_MAIN:
10152 case ISEQ_TYPE_CLASS:
10153 COMPILE_ERROR(ERROR_ARGS "Invalid yield");
10154 return COMPILE_NG;
10155 default: /* valid */;
10156 }
10157
10158 if (RNODE_YIELD(node)->nd_head) {
10159 argc = setup_args(iseq, args, RNODE_YIELD(node)->nd_head, &flag, &keywords);
10160 CHECK(!NIL_P(argc));
10161 }
10162 else {
10163 argc = INT2FIX(0);
10164 }
10165
10166 ADD_SEQ(ret, args);
10167 ADD_INSN1(ret, node, invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), flag, keywords, FALSE));
10168 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
10169
10170 if (popped) {
10171 ADD_INSN(ret, node, pop);
10172 }
10173
10174 int level = 0;
10175 const rb_iseq_t *tmp_iseq = iseq;
10176 for (; tmp_iseq != ISEQ_BODY(iseq)->local_iseq; level++ ) {
10177 tmp_iseq = ISEQ_BODY(tmp_iseq)->parent_iseq;
10178 }
10179 if (level > 0) access_outer_variables(iseq, level, rb_intern("yield"), true);
10180
10181 return COMPILE_OK;
10182}
10183
10184static int
10185compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
10186{
10187 DECL_ANCHOR(recv);
10188 DECL_ANCHOR(val);
10189
10190 INIT_ANCHOR(recv);
10191 INIT_ANCHOR(val);
10192 switch ((int)type) {
10193 case NODE_MATCH:
10194 {
10195 VALUE re = rb_node_regx_string_val(node);
10196 RB_OBJ_SET_FROZEN_SHAREABLE(re);
10197 ADD_INSN1(recv, node, putobject, re);
10198 ADD_INSN2(val, node, getspecial, INT2FIX(0),
10199 INT2FIX(0));
10200 }
10201 break;
10202 case NODE_MATCH2:
10203 CHECK(COMPILE(recv, "receiver", RNODE_MATCH2(node)->nd_recv));
10204 CHECK(COMPILE(val, "value", RNODE_MATCH2(node)->nd_value));
10205 break;
10206 case NODE_MATCH3:
10207 CHECK(COMPILE(recv, "receiver", RNODE_MATCH3(node)->nd_value));
10208 CHECK(COMPILE(val, "value", RNODE_MATCH3(node)->nd_recv));
10209 break;
10210 }
10211
10212 ADD_SEQ(ret, recv);
10213 ADD_SEQ(ret, val);
10214 ADD_SEND(ret, node, idEqTilde, INT2FIX(1));
10215
10216 if (nd_type_p(node, NODE_MATCH2) && RNODE_MATCH2(node)->nd_args) {
10217 compile_named_capture_assign(iseq, ret, RNODE_MATCH2(node)->nd_args);
10218 }
10219
10220 if (popped) {
10221 ADD_INSN(ret, node, pop);
10222 }
10223 return COMPILE_OK;
10224}
10225
10226static int
10227compile_colon2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10228{
10229 if (rb_is_const_id(RNODE_COLON2(node)->nd_mid)) {
10230 /* constant */
10231 VALUE segments;
10232 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache &&
10233 (segments = collect_const_segments(iseq, node))) {
10234 ISEQ_BODY(iseq)->ic_size++;
10235 ADD_INSN1(ret, node, opt_getconstant_path, segments);
10236 RB_OBJ_WRITTEN(iseq, Qundef, segments);
10237 }
10238 else {
10239 /* constant */
10240 DECL_ANCHOR(pref);
10241 DECL_ANCHOR(body);
10242
10243 INIT_ANCHOR(pref);
10244 INIT_ANCHOR(body);
10245 CHECK(compile_const_prefix(iseq, node, pref, body));
10246 if (LIST_INSN_SIZE_ZERO(pref)) {
10247 ADD_INSN(ret, node, putnil);
10248 ADD_SEQ(ret, body);
10249 }
10250 else {
10251 ADD_SEQ(ret, pref);
10252 ADD_SEQ(ret, body);
10253 }
10254 }
10255 }
10256 else {
10257 /* function call */
10258 ADD_CALL_RECEIVER(ret, node);
10259 CHECK(COMPILE(ret, "colon2#nd_head", RNODE_COLON2(node)->nd_head));
10260 ADD_CALL(ret, node, RNODE_COLON2(node)->nd_mid, INT2FIX(1));
10261 }
10262 if (popped) {
10263 ADD_INSN(ret, node, pop);
10264 }
10265 return COMPILE_OK;
10266}
10267
10268static int
10269compile_colon3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10270{
10271 debugi("colon3#nd_mid", RNODE_COLON3(node)->nd_mid);
10272
10273 /* add cache insn */
10274 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
10275 ISEQ_BODY(iseq)->ic_size++;
10276 VALUE segments = rb_ary_new_from_args(2, ID2SYM(idNULL), ID2SYM(RNODE_COLON3(node)->nd_mid));
10277 RB_OBJ_SET_FROZEN_SHAREABLE(segments);
10278 ADD_INSN1(ret, node, opt_getconstant_path, segments);
10279 RB_OBJ_WRITTEN(iseq, Qundef, segments);
10280 }
10281 else {
10282 ADD_INSN1(ret, node, putobject, rb_cObject);
10283 ADD_INSN1(ret, node, putobject, Qtrue);
10284 ADD_INSN1(ret, node, getconstant, ID2SYM(RNODE_COLON3(node)->nd_mid));
10285 }
10286
10287 if (popped) {
10288 ADD_INSN(ret, node, pop);
10289 }
10290 return COMPILE_OK;
10291}
10292
10293static int
10294compile_dots(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const int excl)
10295{
10296 VALUE flag = INT2FIX(excl);
10297 const NODE *b = RNODE_DOT2(node)->nd_beg;
10298 const NODE *e = RNODE_DOT2(node)->nd_end;
10299
10300 if (optimizable_range_item_p(b) && optimizable_range_item_p(e)) {
10301 if (!popped) {
10302 VALUE bv = optimized_range_item(b);
10303 VALUE ev = optimized_range_item(e);
10304 VALUE val = rb_range_new(bv, ev, excl);
10306 ADD_INSN1(ret, node, putobject, val);
10307 RB_OBJ_WRITTEN(iseq, Qundef, val);
10308 }
10309 }
10310 else {
10311 CHECK(COMPILE_(ret, "min", b, popped));
10312 CHECK(COMPILE_(ret, "max", e, popped));
10313 if (!popped) {
10314 ADD_INSN1(ret, node, newrange, flag);
10315 }
10316 }
10317 return COMPILE_OK;
10318}
10319
10320static int
10321compile_errinfo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10322{
10323 if (!popped) {
10324 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
10325 ADD_GETLOCAL(ret, node, LVAR_ERRINFO, 0);
10326 }
10327 else {
10328 const rb_iseq_t *ip = iseq;
10329 int level = 0;
10330 while (ip) {
10331 if (ISEQ_BODY(ip)->type == ISEQ_TYPE_RESCUE) {
10332 break;
10333 }
10334 ip = ISEQ_BODY(ip)->parent_iseq;
10335 level++;
10336 }
10337 if (ip) {
10338 ADD_GETLOCAL(ret, node, LVAR_ERRINFO, level);
10339 }
10340 else {
10341 ADD_INSN(ret, node, putnil);
10342 }
10343 }
10344 }
10345 return COMPILE_OK;
10346}
10347
10348static int
10349compile_kw_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10350{
10351 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
10352 LABEL *end_label = NEW_LABEL(nd_line(node));
10353 const NODE *default_value = get_nd_value(RNODE_KW_ARG(node)->nd_body);
10354
10355 if (default_value == NODE_SPECIAL_REQUIRED_KEYWORD) {
10356 /* required argument. do nothing */
10357 COMPILE_ERROR(ERROR_ARGS "unreachable");
10358 return COMPILE_NG;
10359 }
10360 else if (nd_type_p(default_value, NODE_SYM) ||
10361 nd_type_p(default_value, NODE_REGX) ||
10362 nd_type_p(default_value, NODE_LINE) ||
10363 nd_type_p(default_value, NODE_INTEGER) ||
10364 nd_type_p(default_value, NODE_FLOAT) ||
10365 nd_type_p(default_value, NODE_RATIONAL) ||
10366 nd_type_p(default_value, NODE_IMAGINARY) ||
10367 nd_type_p(default_value, NODE_NIL) ||
10368 nd_type_p(default_value, NODE_TRUE) ||
10369 nd_type_p(default_value, NODE_FALSE)) {
10370 COMPILE_ERROR(ERROR_ARGS "unreachable");
10371 return COMPILE_NG;
10372 }
10373 else {
10374 /* if keywordcheck(_kw_bits, nth_keyword)
10375 * kw = default_value
10376 * end
10377 */
10378 int kw_bits_idx = body->local_table_size - body->param.keyword->bits_start;
10379 int keyword_idx = body->param.keyword->num;
10380
10381 ADD_INSN2(ret, node, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(keyword_idx));
10382 ADD_INSNL(ret, node, branchif, end_label);
10383 CHECK(COMPILE_POPPED(ret, "keyword default argument", RNODE_KW_ARG(node)->nd_body));
10384 ADD_LABEL(ret, end_label);
10385 }
10386 return COMPILE_OK;
10387}
10388
10389static int
10390compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10391{
10392 DECL_ANCHOR(recv);
10393 DECL_ANCHOR(args);
10394 unsigned int flag = 0;
10395 ID mid = RNODE_ATTRASGN(node)->nd_mid;
10396 VALUE argc;
10397 LABEL *else_label = NULL;
10398 VALUE branches = Qfalse;
10399
10400 INIT_ANCHOR(recv);
10401 INIT_ANCHOR(args);
10402 argc = setup_args(iseq, args, RNODE_ATTRASGN(node)->nd_args, &flag, NULL);
10403 CHECK(!NIL_P(argc));
10404
10405 int asgnflag = COMPILE_RECV(recv, "recv", node, RNODE_ATTRASGN(node)->nd_recv);
10406 CHECK(asgnflag != -1);
10407 flag |= (unsigned int)asgnflag;
10408
10409 debugp_param("argc", argc);
10410 debugp_param("nd_mid", ID2SYM(mid));
10411
10412 if (!rb_is_attrset_id(mid)) {
10413 /* safe nav attr */
10414 mid = rb_id_attrset(mid);
10415 else_label = qcall_branch_start(iseq, recv, &branches, node, node);
10416 }
10417 if (!popped) {
10418 ADD_INSN(ret, node, putnil);
10419 ADD_SEQ(ret, recv);
10420 ADD_SEQ(ret, args);
10421
10422 if (flag & VM_CALL_ARGS_SPLAT) {
10423 ADD_INSN(ret, node, dup);
10424 ADD_INSN1(ret, node, putobject, INT2FIX(-1));
10425 ADD_SEND_WITH_FLAG(ret, node, idAREF, INT2FIX(1), INT2FIX(asgnflag));
10426 ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2));
10427 ADD_INSN (ret, node, pop);
10428 }
10429 else {
10430 ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 1));
10431 }
10432 }
10433 else {
10434 ADD_SEQ(ret, recv);
10435 ADD_SEQ(ret, args);
10436 }
10437 ADD_SEND_WITH_FLAG(ret, node, mid, argc, INT2FIX(flag));
10438 qcall_branch_end(iseq, ret, else_label, branches, node, node);
10439 ADD_INSN(ret, node, pop);
10440 return COMPILE_OK;
10441}
10442
10443static int
10444compile_make_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, LINK_ANCHOR *sub, const NODE *value, bool copy)
10445{
10446 ADD_INSN1(ret, value, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10447 ADD_SEQ(ret, sub);
10448
10449 if (copy) {
10450 /*
10451 * NEW_CALL(fcore, rb_intern("make_shareable_copy"),
10452 * NEW_LIST(value, loc), loc);
10453 */
10454 ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
10455 }
10456 else {
10457 /*
10458 * NEW_CALL(fcore, rb_intern("make_shareable"),
10459 * NEW_LIST(value, loc), loc);
10460 */
10461 ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
10462 }
10463
10464 return COMPILE_OK;
10465}
10466
10467static VALUE
10468node_const_decl_val(const NODE *node)
10469{
10470 VALUE path;
10471 switch (nd_type(node)) {
10472 case NODE_CDECL:
10473 if (RNODE_CDECL(node)->nd_vid) {
10474 path = rb_id2str(RNODE_CDECL(node)->nd_vid);
10475 goto end;
10476 }
10477 else {
10478 node = RNODE_CDECL(node)->nd_else;
10479 }
10480 break;
10481 case NODE_COLON2:
10482 break;
10483 case NODE_COLON3:
10484 // ::Const
10485 path = rb_str_new_cstr("::");
10486 rb_str_append(path, rb_id2str(RNODE_COLON3(node)->nd_mid));
10487 goto end;
10488 default:
10489 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
10491 }
10492
10493 path = rb_ary_new();
10494 if (node) {
10495 for (; node && nd_type_p(node, NODE_COLON2); node = RNODE_COLON2(node)->nd_head) {
10496 rb_ary_push(path, rb_id2str(RNODE_COLON2(node)->nd_mid));
10497 }
10498 if (node && nd_type_p(node, NODE_CONST)) {
10499 // Const::Name
10500 rb_ary_push(path, rb_id2str(RNODE_CONST(node)->nd_vid));
10501 }
10502 else if (node && nd_type_p(node, NODE_COLON3)) {
10503 // ::Const::Name
10504 rb_ary_push(path, rb_id2str(RNODE_COLON3(node)->nd_mid));
10505 rb_ary_push(path, rb_str_new(0, 0));
10506 }
10507 else {
10508 // expression::Name
10509 rb_ary_push(path, rb_str_new_cstr("..."));
10510 }
10511 path = rb_ary_join(rb_ary_reverse(path), rb_str_new_cstr("::"));
10512 }
10513 end:
10514 path = rb_fstring(path);
10515 return path;
10516}
10517
10518static VALUE
10519const_decl_path(NODE *dest)
10520{
10521 VALUE path = Qnil;
10522 if (!nd_type_p(dest, NODE_CALL)) {
10523 path = node_const_decl_val(dest);
10524 }
10525 return path;
10526}
10527
10528static int
10529compile_ensure_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *dest, const NODE *value)
10530{
10531 /*
10532 *. RubyVM::FrozenCore.ensure_shareable(value, const_decl_path(dest))
10533 */
10534 VALUE path = const_decl_path(dest);
10535 ADD_INSN1(ret, value, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10536 CHECK(COMPILE(ret, "compile_ensure_shareable_node", value));
10537 ADD_INSN1(ret, value, putobject, path);
10538 RB_OBJ_WRITTEN(iseq, Qundef, path);
10539 ADD_SEND_WITH_FLAG(ret, value, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE));
10540
10541 return COMPILE_OK;
10542}
10543
10544#ifndef SHAREABLE_BARE_EXPRESSION
10545#define SHAREABLE_BARE_EXPRESSION 1
10546#endif
10547
10548static int
10549compile_shareable_literal_constant(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, NODE *dest, const NODE *node, size_t level, VALUE *value_p, int *shareable_literal_p)
10550{
10551# define compile_shareable_literal_constant_next(node, anchor, value_p, shareable_literal_p) \
10552 compile_shareable_literal_constant(iseq, anchor, shareable, dest, node, level+1, value_p, shareable_literal_p)
10553 VALUE lit = Qnil;
10554 DECL_ANCHOR(anchor);
10555
10556 enum node_type type = node ? nd_type(node) : NODE_NIL;
10557 switch (type) {
10558 case NODE_TRUE:
10559 *value_p = Qtrue;
10560 goto compile;
10561 case NODE_FALSE:
10562 *value_p = Qfalse;
10563 goto compile;
10564 case NODE_NIL:
10565 *value_p = Qnil;
10566 goto compile;
10567 case NODE_SYM:
10568 *value_p = rb_node_sym_string_val(node);
10569 goto compile;
10570 case NODE_REGX:
10571 *value_p = rb_node_regx_string_val(node);
10572 goto compile;
10573 case NODE_LINE:
10574 *value_p = rb_node_line_lineno_val(node);
10575 goto compile;
10576 case NODE_INTEGER:
10577 *value_p = rb_node_integer_literal_val(node);
10578 goto compile;
10579 case NODE_FLOAT:
10580 *value_p = rb_node_float_literal_val(node);
10581 goto compile;
10582 case NODE_RATIONAL:
10583 *value_p = rb_node_rational_literal_val(node);
10584 goto compile;
10585 case NODE_IMAGINARY:
10586 *value_p = rb_node_imaginary_literal_val(node);
10587 goto compile;
10588 case NODE_ENCODING:
10589 *value_p = rb_node_encoding_val(node);
10590
10591 compile:
10592 CHECK(COMPILE(ret, "shareable_literal_constant", node));
10593 *shareable_literal_p = 1;
10594 return COMPILE_OK;
10595
10596 case NODE_DSTR:
10597 CHECK(COMPILE(ret, "shareable_literal_constant", node));
10598 if (shareable == rb_parser_shareable_literal) {
10599 /*
10600 * NEW_CALL(node, idUMinus, 0, loc);
10601 *
10602 * -"#{var}"
10603 */
10604 ADD_SEND_WITH_FLAG(ret, node, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE));
10605 }
10606 *value_p = Qundef;
10607 *shareable_literal_p = 1;
10608 return COMPILE_OK;
10609
10610 case NODE_STR:{
10611 VALUE lit = rb_node_str_string_val(node);
10612 ADD_INSN1(ret, node, putobject, lit);
10613 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10614 *value_p = lit;
10615 *shareable_literal_p = 1;
10616
10617 return COMPILE_OK;
10618 }
10619
10620 case NODE_FILE:{
10621 VALUE lit = rb_node_file_path_val(node);
10622 ADD_INSN1(ret, node, putobject, lit);
10623 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10624 *value_p = lit;
10625 *shareable_literal_p = 1;
10626
10627 return COMPILE_OK;
10628 }
10629
10630 case NODE_ZLIST:{
10631 VALUE lit = rb_ary_new();
10632 OBJ_FREEZE(lit);
10633 ADD_INSN1(ret, node, putobject, lit);
10634 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10635 *value_p = lit;
10636 *shareable_literal_p = 1;
10637
10638 return COMPILE_OK;
10639 }
10640
10641 case NODE_LIST:{
10642 INIT_ANCHOR(anchor);
10643 lit = rb_ary_new();
10644 for (NODE *n = (NODE *)node; n; n = RNODE_LIST(n)->nd_next) {
10645 VALUE val;
10646 int shareable_literal_p2;
10647 NODE *elt = RNODE_LIST(n)->nd_head;
10648 if (elt) {
10649 CHECK(compile_shareable_literal_constant_next(elt, anchor, &val, &shareable_literal_p2));
10650 if (shareable_literal_p2) {
10651 /* noop */
10652 }
10653 else if (RTEST(lit)) {
10654 rb_ary_clear(lit);
10655 lit = Qfalse;
10656 }
10657 }
10658 if (RTEST(lit)) {
10659 if (!UNDEF_P(val)) {
10660 rb_ary_push(lit, val);
10661 }
10662 else {
10663 rb_ary_clear(lit);
10664 lit = Qnil; /* make shareable at runtime */
10665 }
10666 }
10667 }
10668 break;
10669 }
10670 case NODE_HASH:{
10671 if (!RNODE_HASH(node)->nd_brace) {
10672 *value_p = Qundef;
10673 *shareable_literal_p = 0;
10674 return COMPILE_OK;
10675 }
10676 for (NODE *n = RNODE_HASH(node)->nd_head; n; n = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_next) {
10677 if (!RNODE_LIST(n)->nd_head) {
10678 // If the hash node have a keyword splat, fall back to the default case.
10679 goto compile_shareable;
10680 }
10681 }
10682
10683 INIT_ANCHOR(anchor);
10684 lit = rb_hash_new();
10685 for (NODE *n = RNODE_HASH(node)->nd_head; n; n = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_next) {
10686 VALUE key_val = 0;
10687 VALUE value_val = 0;
10688 int shareable_literal_p2;
10689 NODE *key = RNODE_LIST(n)->nd_head;
10690 NODE *val = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head;
10691 CHECK(compile_shareable_literal_constant_next(key, anchor, &key_val, &shareable_literal_p2));
10692 if (shareable_literal_p2) {
10693 /* noop */
10694 }
10695 else if (RTEST(lit)) {
10696 rb_hash_clear(lit);
10697 lit = Qfalse;
10698 }
10699 CHECK(compile_shareable_literal_constant_next(val, anchor, &value_val, &shareable_literal_p2));
10700 if (shareable_literal_p2) {
10701 /* noop */
10702 }
10703 else if (RTEST(lit)) {
10704 rb_hash_clear(lit);
10705 lit = Qfalse;
10706 }
10707 if (RTEST(lit)) {
10708 if (!UNDEF_P(key_val) && !UNDEF_P(value_val)) {
10709 rb_hash_aset(lit, key_val, value_val);
10710 }
10711 else {
10712 rb_hash_clear(lit);
10713 lit = Qnil; /* make shareable at runtime */
10714 }
10715 }
10716 }
10717 break;
10718 }
10719
10720 default:
10721
10722 compile_shareable:
10723 if (shareable == rb_parser_shareable_literal &&
10724 (SHAREABLE_BARE_EXPRESSION || level > 0)) {
10725 CHECK(compile_ensure_shareable_node(iseq, ret, dest, node));
10726 *value_p = Qundef;
10727 *shareable_literal_p = 1;
10728 return COMPILE_OK;
10729 }
10730 CHECK(COMPILE(ret, "shareable_literal_constant", node));
10731 *value_p = Qundef;
10732 *shareable_literal_p = 0;
10733 return COMPILE_OK;
10734 }
10735
10736 /* Array or Hash that does not have keyword splat */
10737 if (!lit) {
10738 if (nd_type(node) == NODE_LIST) {
10739 ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen));
10740 }
10741 else if (nd_type(node) == NODE_HASH) {
10742 long len = RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen;
10745 ADD_INSN1(anchor, node, newhash, LONG2FIX(len));
10746 }
10747 *value_p = Qundef;
10748 *shareable_literal_p = 0;
10749 ADD_SEQ(ret, anchor);
10750 return COMPILE_OK;
10751 }
10752 if (NIL_P(lit)) {
10753 // if shareable_literal, all elements should have been ensured
10754 // as shareable
10755 if (nd_type(node) == NODE_LIST) {
10756 ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen));
10757 }
10758 else if (nd_type(node) == NODE_HASH) {
10759 long len = RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen;
10762 ADD_INSN1(anchor, node, newhash, LONG2FIX(len));
10763 }
10764 CHECK(compile_make_shareable_node(iseq, ret, anchor, node, false));
10765 *value_p = Qundef;
10766 *shareable_literal_p = 1;
10767 }
10768 else {
10770 ADD_INSN1(ret, node, putobject, val);
10771 RB_OBJ_WRITTEN(iseq, Qundef, val);
10772 *value_p = val;
10773 *shareable_literal_p = 1;
10774 }
10775
10776 return COMPILE_OK;
10777}
10778
10779static int
10780compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value)
10781{
10782 int literal_p = 0;
10783 VALUE val;
10784 DECL_ANCHOR(anchor);
10785 INIT_ANCHOR(anchor);
10786
10787 switch (shareable) {
10788 case rb_parser_shareable_none:
10789 CHECK(COMPILE(ret, "compile_shareable_constant_value", value));
10790 return COMPILE_OK;
10791
10792 case rb_parser_shareable_literal:
10793 CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p));
10794 ADD_SEQ(ret, anchor);
10795 return COMPILE_OK;
10796
10797 case rb_parser_shareable_copy:
10798 case rb_parser_shareable_everything:
10799 CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p));
10800 if (!literal_p) {
10801 CHECK(compile_make_shareable_node(iseq, ret, anchor, value, shareable == rb_parser_shareable_copy));
10802 }
10803 else {
10804 ADD_SEQ(ret, anchor);
10805 }
10806 return COMPILE_OK;
10807 default:
10808 rb_bug("unexpected rb_parser_shareability: %d", shareable);
10809 }
10810}
10811
10812static int iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped);
10820static int
10821iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *node, int popped)
10822{
10823 if (node == 0) {
10824 if (!popped) {
10825 int lineno = ISEQ_COMPILE_DATA(iseq)->last_line;
10826 if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq));
10827 debugs("node: NODE_NIL(implicit)\n");
10828 ADD_SYNTHETIC_INSN(ret, lineno, -1, putnil);
10829 }
10830 return COMPILE_OK;
10831 }
10832 return iseq_compile_each0(iseq, ret, node, popped);
10833}
10834
10835static int
10836iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10837{
10838 const int line = (int)nd_line(node);
10839 const enum node_type type = nd_type(node);
10840 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
10841
10842 if (ISEQ_COMPILE_DATA(iseq)->last_line == line) {
10843 /* ignore */
10844 }
10845 else {
10846 if (nd_fl_newline(node)) {
10847 int event = RUBY_EVENT_LINE;
10848 ISEQ_COMPILE_DATA(iseq)->last_line = line;
10849 if (line > 0 && ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
10850 event |= RUBY_EVENT_COVERAGE_LINE;
10851 }
10852 ADD_TRACE(ret, event);
10853 }
10854 }
10855
10856 debug_node_start(node);
10857#undef BEFORE_RETURN
10858#define BEFORE_RETURN debug_node_end()
10859
10860 switch (type) {
10861 case NODE_BLOCK:
10862 CHECK(compile_block(iseq, ret, node, popped));
10863 break;
10864 case NODE_IF:
10865 case NODE_UNLESS:
10866 CHECK(compile_if(iseq, ret, node, popped, type));
10867 break;
10868 case NODE_CASE:
10869 CHECK(compile_case(iseq, ret, node, popped));
10870 break;
10871 case NODE_CASE2:
10872 CHECK(compile_case2(iseq, ret, node, popped));
10873 break;
10874 case NODE_CASE3:
10875 CHECK(compile_case3(iseq, ret, node, popped));
10876 break;
10877 case NODE_WHILE:
10878 case NODE_UNTIL:
10879 CHECK(compile_loop(iseq, ret, node, popped, type));
10880 break;
10881 case NODE_FOR:
10882 case NODE_ITER:
10883 CHECK(compile_iter(iseq, ret, node, popped));
10884 break;
10885 case NODE_FOR_MASGN:
10886 CHECK(compile_for_masgn(iseq, ret, node, popped));
10887 break;
10888 case NODE_BREAK:
10889 CHECK(compile_break(iseq, ret, node, popped));
10890 break;
10891 case NODE_NEXT:
10892 CHECK(compile_next(iseq, ret, node, popped));
10893 break;
10894 case NODE_REDO:
10895 CHECK(compile_redo(iseq, ret, node, popped));
10896 break;
10897 case NODE_RETRY:
10898 CHECK(compile_retry(iseq, ret, node, popped));
10899 break;
10900 case NODE_BEGIN:{
10901 CHECK(COMPILE_(ret, "NODE_BEGIN", RNODE_BEGIN(node)->nd_body, popped));
10902 break;
10903 }
10904 case NODE_RESCUE:
10905 CHECK(compile_rescue(iseq, ret, node, popped));
10906 break;
10907 case NODE_RESBODY:
10908 CHECK(compile_resbody(iseq, ret, node, popped));
10909 break;
10910 case NODE_ENSURE:
10911 CHECK(compile_ensure(iseq, ret, node, popped));
10912 break;
10913
10914 case NODE_AND:
10915 case NODE_OR:{
10916 LABEL *end_label = NEW_LABEL(line);
10917 CHECK(COMPILE(ret, "nd_1st", RNODE_OR(node)->nd_1st));
10918 if (!popped) {
10919 ADD_INSN(ret, node, dup);
10920 }
10921 if (type == NODE_AND) {
10922 ADD_INSNL(ret, node, branchunless, end_label);
10923 }
10924 else {
10925 ADD_INSNL(ret, node, branchif, end_label);
10926 }
10927 if (!popped) {
10928 ADD_INSN(ret, node, pop);
10929 }
10930 CHECK(COMPILE_(ret, "nd_2nd", RNODE_OR(node)->nd_2nd, popped));
10931 ADD_LABEL(ret, end_label);
10932 break;
10933 }
10934
10935 case NODE_MASGN:{
10936 compile_massign(iseq, ret, node, popped);
10937 break;
10938 }
10939
10940 case NODE_LASGN:{
10941 ID id = RNODE_LASGN(node)->nd_vid;
10942 int idx = ISEQ_BODY(body->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
10943
10944 debugs("lvar: %s idx: %d\n", rb_id2name(id), idx);
10945 CHECK(COMPILE(ret, "rvalue", RNODE_LASGN(node)->nd_value));
10946
10947 if (!popped) {
10948 ADD_INSN(ret, node, dup);
10949 }
10950 ADD_SETLOCAL(ret, node, idx, get_lvar_level(iseq));
10951 break;
10952 }
10953 case NODE_DASGN: {
10954 int idx, lv, ls;
10955 ID id = RNODE_DASGN(node)->nd_vid;
10956 CHECK(COMPILE(ret, "dvalue", RNODE_DASGN(node)->nd_value));
10957 debugi("dassn id", rb_id2str(id) ? id : '*');
10958
10959 if (!popped) {
10960 ADD_INSN(ret, node, dup);
10961 }
10962
10963 idx = get_dyna_var_idx(iseq, id, &lv, &ls);
10964
10965 if (idx < 0) {
10966 COMPILE_ERROR(ERROR_ARGS "NODE_DASGN: unknown id (%"PRIsVALUE")",
10967 rb_id2str(id));
10968 goto ng;
10969 }
10970 ADD_SETLOCAL(ret, node, ls - idx, lv);
10971 break;
10972 }
10973 case NODE_GASGN:{
10974 CHECK(COMPILE(ret, "lvalue", RNODE_GASGN(node)->nd_value));
10975
10976 if (!popped) {
10977 ADD_INSN(ret, node, dup);
10978 }
10979 ADD_INSN1(ret, node, setglobal, ID2SYM(RNODE_GASGN(node)->nd_vid));
10980 break;
10981 }
10982 case NODE_IASGN:{
10983 CHECK(COMPILE(ret, "lvalue", RNODE_IASGN(node)->nd_value));
10984 if (!popped) {
10985 ADD_INSN(ret, node, dup);
10986 }
10987 ADD_INSN2(ret, node, setinstancevariable,
10988 ID2SYM(RNODE_IASGN(node)->nd_vid),
10989 get_ivar_ic_value(iseq,RNODE_IASGN(node)->nd_vid));
10990 break;
10991 }
10992 case NODE_CDECL:{
10993 if (RNODE_CDECL(node)->nd_vid) {
10994 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value));
10995
10996 if (!popped) {
10997 ADD_INSN(ret, node, dup);
10998 }
10999
11000 ADD_INSN1(ret, node, putspecialobject,
11001 INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
11002 ADD_INSN1(ret, node, setconstant, ID2SYM(RNODE_CDECL(node)->nd_vid));
11003 }
11004 else {
11005 compile_cpath(ret, iseq, RNODE_CDECL(node)->nd_else);
11006 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value));
11007 ADD_INSN(ret, node, swap);
11008
11009 if (!popped) {
11010 ADD_INSN1(ret, node, topn, INT2FIX(1));
11011 ADD_INSN(ret, node, swap);
11012 }
11013
11014 ADD_INSN1(ret, node, setconstant, ID2SYM(get_node_colon_nd_mid(RNODE_CDECL(node)->nd_else)));
11015 }
11016 break;
11017 }
11018 case NODE_CVASGN:{
11019 CHECK(COMPILE(ret, "cvasgn val", RNODE_CVASGN(node)->nd_value));
11020 if (!popped) {
11021 ADD_INSN(ret, node, dup);
11022 }
11023 ADD_INSN2(ret, node, setclassvariable,
11024 ID2SYM(RNODE_CVASGN(node)->nd_vid),
11025 get_cvar_ic_value(iseq, RNODE_CVASGN(node)->nd_vid));
11026 break;
11027 }
11028 case NODE_OP_ASGN1:
11029 CHECK(compile_op_asgn1(iseq, ret, node, popped));
11030 break;
11031 case NODE_OP_ASGN2:
11032 CHECK(compile_op_asgn2(iseq, ret, node, popped));
11033 break;
11034 case NODE_OP_CDECL:
11035 CHECK(compile_op_cdecl(iseq, ret, node, popped));
11036 break;
11037 case NODE_OP_ASGN_AND:
11038 case NODE_OP_ASGN_OR:
11039 CHECK(compile_op_log(iseq, ret, node, popped, type));
11040 break;
11041 case NODE_CALL: /* obj.foo */
11042 case NODE_OPCALL: /* foo[] */
11043 if (compile_call_precheck_freeze(iseq, ret, node, node, popped) == TRUE) {
11044 break;
11045 }
11046 case NODE_QCALL: /* obj&.foo */
11047 case NODE_FCALL: /* foo() */
11048 case NODE_VCALL: /* foo (variable or call) */
11049 if (compile_call(iseq, ret, node, type, node, popped, false) == COMPILE_NG) {
11050 goto ng;
11051 }
11052 break;
11053 case NODE_SUPER:
11054 case NODE_ZSUPER:
11055 CHECK(compile_super(iseq, ret, node, popped, type));
11056 break;
11057 case NODE_LIST:{
11058 CHECK(compile_array(iseq, ret, node, popped, TRUE) >= 0);
11059 break;
11060 }
11061 case NODE_ZLIST:{
11062 if (!popped) {
11063 ADD_INSN1(ret, node, newarray, INT2FIX(0));
11064 }
11065 break;
11066 }
11067 case NODE_HASH:
11068 CHECK(compile_hash(iseq, ret, node, FALSE, popped) >= 0);
11069 break;
11070 case NODE_RETURN:
11071 CHECK(compile_return(iseq, ret, node, popped));
11072 break;
11073 case NODE_YIELD:
11074 CHECK(compile_yield(iseq, ret, node, popped));
11075 break;
11076 case NODE_LVAR:{
11077 if (!popped) {
11078 compile_lvar(iseq, ret, node, RNODE_LVAR(node)->nd_vid);
11079 }
11080 break;
11081 }
11082 case NODE_DVAR:{
11083 int lv, idx, ls;
11084 debugi("nd_vid", RNODE_DVAR(node)->nd_vid);
11085 if (!popped) {
11086 idx = get_dyna_var_idx(iseq, RNODE_DVAR(node)->nd_vid, &lv, &ls);
11087 if (idx < 0) {
11088 COMPILE_ERROR(ERROR_ARGS "unknown dvar (%"PRIsVALUE")",
11089 rb_id2str(RNODE_DVAR(node)->nd_vid));
11090 goto ng;
11091 }
11092 ADD_GETLOCAL(ret, node, ls - idx, lv);
11093 }
11094 break;
11095 }
11096 case NODE_GVAR:{
11097 ADD_INSN1(ret, node, getglobal, ID2SYM(RNODE_GVAR(node)->nd_vid));
11098 if (popped) {
11099 ADD_INSN(ret, node, pop);
11100 }
11101 break;
11102 }
11103 case NODE_IVAR:{
11104 debugi("nd_vid", RNODE_IVAR(node)->nd_vid);
11105 if (!popped) {
11106 ADD_INSN2(ret, node, getinstancevariable,
11107 ID2SYM(RNODE_IVAR(node)->nd_vid),
11108 get_ivar_ic_value(iseq, RNODE_IVAR(node)->nd_vid));
11109 }
11110 break;
11111 }
11112 case NODE_CONST:{
11113 debugi("nd_vid", RNODE_CONST(node)->nd_vid);
11114
11115 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
11116 body->ic_size++;
11117 VALUE segments = rb_ary_new_from_args(1, ID2SYM(RNODE_CONST(node)->nd_vid));
11118 RB_OBJ_SET_FROZEN_SHAREABLE(segments);
11119 ADD_INSN1(ret, node, opt_getconstant_path, segments);
11120 RB_OBJ_WRITTEN(iseq, Qundef, segments);
11121 }
11122 else {
11123 ADD_INSN(ret, node, putnil);
11124 ADD_INSN1(ret, node, putobject, Qtrue);
11125 ADD_INSN1(ret, node, getconstant, ID2SYM(RNODE_CONST(node)->nd_vid));
11126 }
11127
11128 if (popped) {
11129 ADD_INSN(ret, node, pop);
11130 }
11131 break;
11132 }
11133 case NODE_CVAR:{
11134 if (!popped) {
11135 ADD_INSN2(ret, node, getclassvariable,
11136 ID2SYM(RNODE_CVAR(node)->nd_vid),
11137 get_cvar_ic_value(iseq, RNODE_CVAR(node)->nd_vid));
11138 }
11139 break;
11140 }
11141 case NODE_NTH_REF:{
11142 if (!popped) {
11143 if (!RNODE_NTH_REF(node)->nd_nth) {
11144 ADD_INSN(ret, node, putnil);
11145 break;
11146 }
11147 ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
11148 INT2FIX(RNODE_NTH_REF(node)->nd_nth << 1));
11149 }
11150 break;
11151 }
11152 case NODE_BACK_REF:{
11153 if (!popped) {
11154 ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
11155 INT2FIX(0x01 | (RNODE_BACK_REF(node)->nd_nth << 1)));
11156 }
11157 break;
11158 }
11159 case NODE_MATCH:
11160 case NODE_MATCH2:
11161 case NODE_MATCH3:
11162 CHECK(compile_match(iseq, ret, node, popped, type));
11163 break;
11164 case NODE_SYM:{
11165 if (!popped) {
11166 ADD_INSN1(ret, node, putobject, rb_node_sym_string_val(node));
11167 }
11168 break;
11169 }
11170 case NODE_LINE:{
11171 if (!popped) {
11172 ADD_INSN1(ret, node, putobject, rb_node_line_lineno_val(node));
11173 }
11174 break;
11175 }
11176 case NODE_ENCODING:{
11177 if (!popped) {
11178 ADD_INSN1(ret, node, putobject, rb_node_encoding_val(node));
11179 }
11180 break;
11181 }
11182 case NODE_INTEGER:{
11183 VALUE lit = rb_node_integer_literal_val(node);
11184 if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit);
11185 debugp_param("integer", lit);
11186 if (!popped) {
11187 ADD_INSN1(ret, node, putobject, lit);
11188 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11189 }
11190 break;
11191 }
11192 case NODE_FLOAT:{
11193 VALUE lit = rb_node_float_literal_val(node);
11194 if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit);
11195 debugp_param("float", lit);
11196 if (!popped) {
11197 ADD_INSN1(ret, node, putobject, lit);
11198 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11199 }
11200 break;
11201 }
11202 case NODE_RATIONAL:{
11203 VALUE lit = rb_node_rational_literal_val(node);
11205 debugp_param("rational", lit);
11206 if (!popped) {
11207 ADD_INSN1(ret, node, putobject, lit);
11208 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11209 }
11210 break;
11211 }
11212 case NODE_IMAGINARY:{
11213 VALUE lit = rb_node_imaginary_literal_val(node);
11215 debugp_param("imaginary", lit);
11216 if (!popped) {
11217 ADD_INSN1(ret, node, putobject, lit);
11218 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11219 }
11220 break;
11221 }
11222 case NODE_FILE:
11223 case NODE_STR:{
11224 debugp_param("nd_lit", get_string_value(node));
11225 if (!popped) {
11226 VALUE lit = get_string_value(node);
11227 const rb_compile_option_t *option = ISEQ_COMPILE_DATA(iseq)->option;
11228 if ((option->debug_frozen_string_literal || RTEST(ruby_debug)) &&
11229 option->frozen_string_literal != ISEQ_FROZEN_STRING_LITERAL_DISABLED) {
11230 lit = rb_str_with_debug_created_info(lit, rb_iseq_path(iseq), line);
11231 RB_OBJ_SET_SHAREABLE(lit);
11232 }
11233 switch (option->frozen_string_literal) {
11234 case ISEQ_FROZEN_STRING_LITERAL_UNSET:
11235 ADD_INSN1(ret, node, putchilledstring, lit);
11236 break;
11237 case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
11238 ADD_INSN1(ret, node, putstring, lit);
11239 break;
11240 case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
11241 ADD_INSN1(ret, node, putobject, lit);
11242 break;
11243 default:
11244 rb_bug("invalid frozen_string_literal");
11245 }
11246 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11247 }
11248 break;
11249 }
11250 case NODE_DSTR:{
11251 compile_dstr(iseq, ret, node);
11252
11253 if (popped) {
11254 ADD_INSN(ret, node, pop);
11255 }
11256 break;
11257 }
11258 case NODE_XSTR:{
11259 ADD_CALL_RECEIVER(ret, node);
11260 VALUE str = rb_node_str_string_val(node);
11261 ADD_INSN1(ret, node, putobject, str);
11262 RB_OBJ_WRITTEN(iseq, Qundef, str);
11263 ADD_CALL(ret, node, idBackquote, INT2FIX(1));
11264
11265 if (popped) {
11266 ADD_INSN(ret, node, pop);
11267 }
11268 break;
11269 }
11270 case NODE_DXSTR:{
11271 ADD_CALL_RECEIVER(ret, node);
11272 compile_dstr(iseq, ret, node);
11273 ADD_CALL(ret, node, idBackquote, INT2FIX(1));
11274
11275 if (popped) {
11276 ADD_INSN(ret, node, pop);
11277 }
11278 break;
11279 }
11280 case NODE_EVSTR:
11281 CHECK(compile_evstr(iseq, ret, RNODE_EVSTR(node)->nd_body, popped));
11282 break;
11283 case NODE_REGX:{
11284 if (!popped) {
11285 VALUE lit = rb_node_regx_string_val(node);
11286 RB_OBJ_SET_SHAREABLE(lit);
11287 ADD_INSN1(ret, node, putobject, lit);
11288 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11289 }
11290 break;
11291 }
11292 case NODE_DREGX:
11293 compile_dregx(iseq, ret, node, popped);
11294 break;
11295 case NODE_ONCE:{
11296 int ic_index = body->ise_size++;
11297 const rb_iseq_t *block_iseq;
11298 block_iseq = NEW_CHILD_ISEQ(RNODE_ONCE(node)->nd_body, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, line);
11299
11300 ADD_INSN2(ret, node, once, block_iseq, INT2FIX(ic_index));
11301 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block_iseq);
11302
11303 if (popped) {
11304 ADD_INSN(ret, node, pop);
11305 }
11306 break;
11307 }
11308 case NODE_ARGSCAT:{
11309 if (popped) {
11310 CHECK(COMPILE(ret, "argscat head", RNODE_ARGSCAT(node)->nd_head));
11311 ADD_INSN1(ret, node, splatarray, Qfalse);
11312 ADD_INSN(ret, node, pop);
11313 CHECK(COMPILE(ret, "argscat body", RNODE_ARGSCAT(node)->nd_body));
11314 ADD_INSN1(ret, node, splatarray, Qfalse);
11315 ADD_INSN(ret, node, pop);
11316 }
11317 else {
11318 CHECK(COMPILE(ret, "argscat head", RNODE_ARGSCAT(node)->nd_head));
11319 const NODE *body_node = RNODE_ARGSCAT(node)->nd_body;
11320 if (nd_type_p(body_node, NODE_LIST)) {
11321 CHECK(compile_array(iseq, ret, body_node, popped, FALSE) >= 0);
11322 }
11323 else {
11324 CHECK(COMPILE(ret, "argscat body", body_node));
11325 ADD_INSN(ret, node, concattoarray);
11326 }
11327 }
11328 break;
11329 }
11330 case NODE_ARGSPUSH:{
11331 if (popped) {
11332 CHECK(COMPILE(ret, "argspush head", RNODE_ARGSPUSH(node)->nd_head));
11333 ADD_INSN1(ret, node, splatarray, Qfalse);
11334 ADD_INSN(ret, node, pop);
11335 CHECK(COMPILE_(ret, "argspush body", RNODE_ARGSPUSH(node)->nd_body, popped));
11336 }
11337 else {
11338 CHECK(COMPILE(ret, "argspush head", RNODE_ARGSPUSH(node)->nd_head));
11339 const NODE *body_node = RNODE_ARGSPUSH(node)->nd_body;
11340 if (keyword_node_p(body_node)) {
11341 CHECK(COMPILE_(ret, "array element", body_node, FALSE));
11342 ADD_INSN(ret, node, pushtoarraykwsplat);
11343 }
11344 else if (static_literal_node_p(body_node, iseq, false)) {
11345 ADD_INSN1(ret, body_node, putobject, static_literal_value(body_node, iseq));
11346 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
11347 }
11348 else {
11349 CHECK(COMPILE_(ret, "array element", body_node, FALSE));
11350 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
11351 }
11352 }
11353 break;
11354 }
11355 case NODE_SPLAT:{
11356 CHECK(COMPILE(ret, "splat", RNODE_SPLAT(node)->nd_head));
11357 ADD_INSN1(ret, node, splatarray, Qtrue);
11358
11359 if (popped) {
11360 ADD_INSN(ret, node, pop);
11361 }
11362 break;
11363 }
11364 case NODE_DEFN:{
11365 ID mid = RNODE_DEFN(node)->nd_mid;
11366 const rb_iseq_t *method_iseq = NEW_ISEQ(RNODE_DEFN(node)->nd_defn,
11367 rb_id2str(mid),
11368 ISEQ_TYPE_METHOD, line);
11369
11370 debugp_param("defn/iseq", rb_iseqw_new(method_iseq));
11371 ADD_INSN2(ret, node, definemethod, ID2SYM(mid), method_iseq);
11372 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)method_iseq);
11373
11374 if (!popped) {
11375 ADD_INSN1(ret, node, putobject, ID2SYM(mid));
11376 }
11377
11378 break;
11379 }
11380 case NODE_DEFS:{
11381 ID mid = RNODE_DEFS(node)->nd_mid;
11382 const rb_iseq_t * singleton_method_iseq = NEW_ISEQ(RNODE_DEFS(node)->nd_defn,
11383 rb_id2str(mid),
11384 ISEQ_TYPE_METHOD, line);
11385
11386 debugp_param("defs/iseq", rb_iseqw_new(singleton_method_iseq));
11387 CHECK(COMPILE(ret, "defs: recv", RNODE_DEFS(node)->nd_recv));
11388 ADD_INSN2(ret, node, definesmethod, ID2SYM(mid), singleton_method_iseq);
11389 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_method_iseq);
11390
11391 if (!popped) {
11392 ADD_INSN1(ret, node, putobject, ID2SYM(mid));
11393 }
11394 break;
11395 }
11396 case NODE_ALIAS:{
11397 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
11398 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
11399 CHECK(COMPILE(ret, "alias arg1", RNODE_ALIAS(node)->nd_1st));
11400 CHECK(COMPILE(ret, "alias arg2", RNODE_ALIAS(node)->nd_2nd));
11401 ADD_SEND(ret, node, id_core_set_method_alias, INT2FIX(3));
11402
11403 if (popped) {
11404 ADD_INSN(ret, node, pop);
11405 }
11406 break;
11407 }
11408 case NODE_VALIAS:{
11409 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
11410 ADD_INSN1(ret, node, putobject, ID2SYM(RNODE_VALIAS(node)->nd_alias));
11411 ADD_INSN1(ret, node, putobject, ID2SYM(RNODE_VALIAS(node)->nd_orig));
11412 ADD_SEND(ret, node, id_core_set_variable_alias, INT2FIX(2));
11413
11414 if (popped) {
11415 ADD_INSN(ret, node, pop);
11416 }
11417 break;
11418 }
11419 case NODE_UNDEF:{
11420 const rb_parser_ary_t *ary = RNODE_UNDEF(node)->nd_undefs;
11421
11422 for (long i = 0; i < ary->len; i++) {
11423 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
11424 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
11425 CHECK(COMPILE(ret, "undef arg", ary->data[i]));
11426 ADD_SEND(ret, node, id_core_undef_method, INT2FIX(2));
11427
11428 if (i < ary->len - 1) {
11429 ADD_INSN(ret, node, pop);
11430 }
11431 }
11432
11433 if (popped) {
11434 ADD_INSN(ret, node, pop);
11435 }
11436 break;
11437 }
11438 case NODE_CLASS:{
11439 const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(RNODE_CLASS(node)->nd_body,
11440 rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(get_node_colon_nd_mid(RNODE_CLASS(node)->nd_cpath)))),
11441 ISEQ_TYPE_CLASS, line);
11442 const int flags = VM_DEFINECLASS_TYPE_CLASS |
11443 (RNODE_CLASS(node)->nd_super ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
11444 compile_cpath(ret, iseq, RNODE_CLASS(node)->nd_cpath);
11445
11446 CHECK(COMPILE(ret, "super", RNODE_CLASS(node)->nd_super));
11447 ADD_INSN3(ret, node, defineclass, ID2SYM(get_node_colon_nd_mid(RNODE_CLASS(node)->nd_cpath)), class_iseq, INT2FIX(flags));
11448 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq);
11449
11450 if (popped) {
11451 ADD_INSN(ret, node, pop);
11452 }
11453 break;
11454 }
11455 case NODE_MODULE:{
11456 const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(RNODE_MODULE(node)->nd_body,
11457 rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(get_node_colon_nd_mid(RNODE_MODULE(node)->nd_cpath)))),
11458 ISEQ_TYPE_CLASS, line);
11459 const int flags = VM_DEFINECLASS_TYPE_MODULE |
11460 compile_cpath(ret, iseq, RNODE_MODULE(node)->nd_cpath);
11461
11462 ADD_INSN (ret, node, putnil); /* dummy */
11463 ADD_INSN3(ret, node, defineclass, ID2SYM(get_node_colon_nd_mid(RNODE_MODULE(node)->nd_cpath)), module_iseq, INT2FIX(flags));
11464 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)module_iseq);
11465
11466 if (popped) {
11467 ADD_INSN(ret, node, pop);
11468 }
11469 break;
11470 }
11471 case NODE_SCLASS:{
11472 ID singletonclass;
11473 const rb_iseq_t *singleton_class = NEW_ISEQ(RNODE_SCLASS(node)->nd_body, rb_fstring_lit("singleton class"),
11474 ISEQ_TYPE_CLASS, line);
11475
11476 CHECK(COMPILE(ret, "sclass#recv", RNODE_SCLASS(node)->nd_recv));
11477 ADD_INSN (ret, node, putnil);
11478 CONST_ID(singletonclass, "singletonclass");
11479 ADD_INSN3(ret, node, defineclass,
11480 ID2SYM(singletonclass), singleton_class,
11481 INT2FIX(VM_DEFINECLASS_TYPE_SINGLETON_CLASS));
11482 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_class);
11483
11484 if (popped) {
11485 ADD_INSN(ret, node, pop);
11486 }
11487 break;
11488 }
11489 case NODE_COLON2:
11490 CHECK(compile_colon2(iseq, ret, node, popped));
11491 break;
11492 case NODE_COLON3:
11493 CHECK(compile_colon3(iseq, ret, node, popped));
11494 break;
11495 case NODE_DOT2:
11496 CHECK(compile_dots(iseq, ret, node, popped, FALSE));
11497 break;
11498 case NODE_DOT3:
11499 CHECK(compile_dots(iseq, ret, node, popped, TRUE));
11500 break;
11501 case NODE_FLIP2:
11502 case NODE_FLIP3:{
11503 LABEL *lend = NEW_LABEL(line);
11504 LABEL *ltrue = NEW_LABEL(line);
11505 LABEL *lfalse = NEW_LABEL(line);
11506 CHECK(compile_flip_flop(iseq, ret, node, type == NODE_FLIP2,
11507 ltrue, lfalse));
11508 ADD_LABEL(ret, ltrue);
11509 ADD_INSN1(ret, node, putobject, Qtrue);
11510 ADD_INSNL(ret, node, jump, lend);
11511 ADD_LABEL(ret, lfalse);
11512 ADD_INSN1(ret, node, putobject, Qfalse);
11513 ADD_LABEL(ret, lend);
11514 break;
11515 }
11516 case NODE_SELF:{
11517 if (!popped) {
11518 ADD_INSN(ret, node, putself);
11519 }
11520 break;
11521 }
11522 case NODE_NIL:{
11523 if (!popped) {
11524 ADD_INSN(ret, node, putnil);
11525 }
11526 break;
11527 }
11528 case NODE_TRUE:{
11529 if (!popped) {
11530 ADD_INSN1(ret, node, putobject, Qtrue);
11531 }
11532 break;
11533 }
11534 case NODE_FALSE:{
11535 if (!popped) {
11536 ADD_INSN1(ret, node, putobject, Qfalse);
11537 }
11538 break;
11539 }
11540 case NODE_ERRINFO:
11541 CHECK(compile_errinfo(iseq, ret, node, popped));
11542 break;
11543 case NODE_DEFINED:
11544 if (!popped) {
11545 CHECK(compile_defined_expr(iseq, ret, node, Qtrue, false));
11546 }
11547 break;
11548 case NODE_POSTEXE:{
11549 /* compiled to:
11550 * ONCE{ rb_mRubyVMFrozenCore::core#set_postexe{ ... } }
11551 */
11552 int is_index = body->ise_size++;
11554 rb_iseq_new_with_callback_new_callback(build_postexe_iseq, RNODE_POSTEXE(node)->nd_body);
11555 const rb_iseq_t *once_iseq =
11556 NEW_CHILD_ISEQ_WITH_CALLBACK(ifunc, rb_fstring(make_name_for_block(iseq)), ISEQ_TYPE_BLOCK, line);
11557
11558 ADD_INSN2(ret, node, once, once_iseq, INT2FIX(is_index));
11559 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)once_iseq);
11560
11561 if (popped) {
11562 ADD_INSN(ret, node, pop);
11563 }
11564 break;
11565 }
11566 case NODE_KW_ARG:
11567 CHECK(compile_kw_arg(iseq, ret, node, popped));
11568 break;
11569 case NODE_DSYM:{
11570 compile_dstr(iseq, ret, node);
11571 if (!popped) {
11572 ADD_INSN(ret, node, intern);
11573 }
11574 else {
11575 ADD_INSN(ret, node, pop);
11576 }
11577 break;
11578 }
11579 case NODE_ATTRASGN:
11580 CHECK(compile_attrasgn(iseq, ret, node, popped));
11581 break;
11582 case NODE_LAMBDA:{
11583 /* compile same as lambda{...} */
11584 const rb_iseq_t *block = NEW_CHILD_ISEQ(RNODE_LAMBDA(node)->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line);
11585 VALUE argc = INT2FIX(0);
11586
11587 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
11588 ADD_CALL_WITH_BLOCK(ret, node, idLambda, argc, block);
11589 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block);
11590
11591 if (popped) {
11592 ADD_INSN(ret, node, pop);
11593 }
11594 break;
11595 }
11596 default:
11597 UNKNOWN_NODE("iseq_compile_each", node, COMPILE_NG);
11598 ng:
11599 debug_node_end();
11600 return COMPILE_NG;
11601 }
11602
11603 debug_node_end();
11604 return COMPILE_OK;
11605}
11606
11607/***************************/
11608/* instruction information */
11609/***************************/
11610
11611static int
11612insn_data_length(INSN *iobj)
11613{
11614 return insn_len(iobj->insn_id);
11615}
11616
11617static int
11618calc_sp_depth(int depth, INSN *insn)
11619{
11620 return comptime_insn_stack_increase(depth, insn->insn_id, insn->operands);
11621}
11622
11623static VALUE
11624opobj_inspect(VALUE obj)
11625{
11626 if (!SPECIAL_CONST_P(obj) && !RBASIC_CLASS(obj)) {
11627 switch (BUILTIN_TYPE(obj)) {
11628 case T_STRING:
11629 obj = rb_str_new_cstr(RSTRING_PTR(obj));
11630 break;
11631 case T_ARRAY:
11632 obj = rb_ary_dup(obj);
11633 break;
11634 default:
11635 break;
11636 }
11637 }
11638 return rb_inspect(obj);
11639}
11640
11641
11642
11643static VALUE
11644insn_data_to_s_detail(INSN *iobj)
11645{
11646 VALUE str = rb_sprintf("%-20s ", insn_name(iobj->insn_id));
11647
11648 if (iobj->operands) {
11649 const char *types = insn_op_types(iobj->insn_id);
11650 int j;
11651
11652 for (j = 0; types[j]; j++) {
11653 char type = types[j];
11654
11655 switch (type) {
11656 case TS_OFFSET: /* label(destination position) */
11657 {
11658 LABEL *lobj = (LABEL *)OPERAND_AT(iobj, j);
11659 rb_str_catf(str, LABEL_FORMAT, lobj->label_no);
11660 break;
11661 }
11662 break;
11663 case TS_ISEQ: /* iseq */
11664 {
11665 rb_iseq_t *iseq = (rb_iseq_t *)OPERAND_AT(iobj, j);
11666 VALUE val = Qnil;
11667 if (0 && iseq) { /* TODO: invalidate now */
11668 val = (VALUE)iseq;
11669 }
11670 rb_str_concat(str, opobj_inspect(val));
11671 }
11672 break;
11673 case TS_LINDEX:
11674 case TS_NUM: /* ulong */
11675 case TS_VALUE: /* VALUE */
11676 {
11677 VALUE v = OPERAND_AT(iobj, j);
11678 if (!CLASS_OF(v))
11679 rb_str_cat2(str, "<hidden>");
11680 else {
11681 rb_str_concat(str, opobj_inspect(v));
11682 }
11683 break;
11684 }
11685 case TS_ID: /* ID */
11686 rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j)));
11687 break;
11688 case TS_IC: /* inline cache */
11689 rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j)));
11690 break;
11691 case TS_IVC: /* inline ivar cache */
11692 rb_str_catf(str, "<ivc:%d>", FIX2INT(OPERAND_AT(iobj, j)));
11693 break;
11694 case TS_ICVARC: /* inline cvar cache */
11695 rb_str_catf(str, "<icvarc:%d>", FIX2INT(OPERAND_AT(iobj, j)));
11696 break;
11697 case TS_ISE: /* inline storage entry */
11698 rb_str_catf(str, "<ise:%d>", FIX2INT(OPERAND_AT(iobj, j)));
11699 break;
11700 case TS_CALLDATA: /* we store these as call infos at compile time */
11701 {
11702 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, j);
11703 rb_str_cat2(str, "<calldata:");
11704 if (vm_ci_mid(ci)) rb_str_catf(str, "%"PRIsVALUE, rb_id2str(vm_ci_mid(ci)));
11705 rb_str_catf(str, ", %d>", vm_ci_argc(ci));
11706 break;
11707 }
11708 case TS_CDHASH: /* case/when condition cache */
11709 rb_str_cat2(str, "<ch>");
11710 break;
11711 case TS_FUNCPTR:
11712 {
11713 void *func = (void *)OPERAND_AT(iobj, j);
11714#ifdef HAVE_DLADDR
11715 Dl_info info;
11716 if (dladdr(func, &info) && info.dli_sname) {
11717 rb_str_cat2(str, info.dli_sname);
11718 break;
11719 }
11720#endif
11721 rb_str_catf(str, "<%p>", func);
11722 }
11723 break;
11724 case TS_BUILTIN:
11725 rb_str_cat2(str, "<TS_BUILTIN>");
11726 break;
11727 default:{
11728 rb_raise(rb_eSyntaxError, "unknown operand type: %c", type);
11729 }
11730 }
11731 if (types[j + 1]) {
11732 rb_str_cat2(str, ", ");
11733 }
11734 }
11735 }
11736 return str;
11737}
11738
11739static void
11740dump_disasm_list(const LINK_ELEMENT *link)
11741{
11742 dump_disasm_list_with_cursor(link, NULL, NULL);
11743}
11744
11745static void
11746dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, const LABEL *dest)
11747{
11748 int pos = 0;
11749 INSN *iobj;
11750 LABEL *lobj;
11751 VALUE str;
11752
11753 printf("-- raw disasm--------\n");
11754
11755 while (link) {
11756 if (curr) printf(curr == link ? "*" : " ");
11757 switch (link->type) {
11758 case ISEQ_ELEMENT_INSN:
11759 {
11760 iobj = (INSN *)link;
11761 str = insn_data_to_s_detail(iobj);
11762 printf(" %04d %-65s(%4u)\n", pos, StringValueCStr(str), iobj->insn_info.line_no);
11763 pos += insn_data_length(iobj);
11764 break;
11765 }
11766 case ISEQ_ELEMENT_LABEL:
11767 {
11768 lobj = (LABEL *)link;
11769 printf(LABEL_FORMAT" [sp: %d, unremovable: %d, refcnt: %d]%s\n", lobj->label_no, lobj->sp, lobj->unremovable, lobj->refcnt,
11770 dest == lobj ? " <---" : "");
11771 break;
11772 }
11773 case ISEQ_ELEMENT_TRACE:
11774 {
11775 TRACE *trace = (TRACE *)link;
11776 printf(" trace: %0x\n", trace->event);
11777 break;
11778 }
11779 case ISEQ_ELEMENT_ADJUST:
11780 {
11781 ADJUST *adjust = (ADJUST *)link;
11782 printf(" adjust: [label: %d]\n", adjust->label ? adjust->label->label_no : -1);
11783 break;
11784 }
11785 default:
11786 /* ignore */
11787 rb_raise(rb_eSyntaxError, "dump_disasm_list error: %d\n", (int)link->type);
11788 }
11789 link = link->next;
11790 }
11791 printf("---------------------\n");
11792 fflush(stdout);
11793}
11794
11795int
11796rb_insn_len(VALUE insn)
11797{
11798 return insn_len(insn);
11799}
11800
11801const char *
11802rb_insns_name(int i)
11803{
11804 return insn_name(i);
11805}
11806
11807VALUE
11808rb_insns_name_array(void)
11809{
11810 VALUE ary = rb_ary_new_capa(VM_INSTRUCTION_SIZE);
11811 int i;
11812 for (i = 0; i < VM_INSTRUCTION_SIZE; i++) {
11813 rb_ary_push(ary, rb_fstring_cstr(insn_name(i)));
11814 }
11815 return rb_ary_freeze(ary);
11816}
11817
11818static LABEL *
11819register_label(rb_iseq_t *iseq, struct st_table *labels_table, VALUE obj)
11820{
11821 LABEL *label = 0;
11822 st_data_t tmp;
11823 obj = rb_to_symbol_type(obj);
11824
11825 if (st_lookup(labels_table, obj, &tmp) == 0) {
11826 label = NEW_LABEL(0);
11827 st_insert(labels_table, obj, (st_data_t)label);
11828 }
11829 else {
11830 label = (LABEL *)tmp;
11831 }
11832 LABEL_REF(label);
11833 return label;
11834}
11835
11836static VALUE
11837get_exception_sym2type(VALUE sym)
11838{
11839 static VALUE symRescue, symEnsure, symRetry;
11840 static VALUE symBreak, symRedo, symNext;
11841
11842 if (symRescue == 0) {
11843 symRescue = ID2SYM(rb_intern_const("rescue"));
11844 symEnsure = ID2SYM(rb_intern_const("ensure"));
11845 symRetry = ID2SYM(rb_intern_const("retry"));
11846 symBreak = ID2SYM(rb_intern_const("break"));
11847 symRedo = ID2SYM(rb_intern_const("redo"));
11848 symNext = ID2SYM(rb_intern_const("next"));
11849 }
11850
11851 if (sym == symRescue) return CATCH_TYPE_RESCUE;
11852 if (sym == symEnsure) return CATCH_TYPE_ENSURE;
11853 if (sym == symRetry) return CATCH_TYPE_RETRY;
11854 if (sym == symBreak) return CATCH_TYPE_BREAK;
11855 if (sym == symRedo) return CATCH_TYPE_REDO;
11856 if (sym == symNext) return CATCH_TYPE_NEXT;
11857 rb_raise(rb_eSyntaxError, "invalid exception symbol: %+"PRIsVALUE, sym);
11858 return 0;
11859}
11860
11861static int
11862iseq_build_from_ary_exception(rb_iseq_t *iseq, struct st_table *labels_table,
11863 VALUE exception)
11864{
11865 int i;
11866
11867 for (i=0; i<RARRAY_LEN(exception); i++) {
11868 const rb_iseq_t *eiseq;
11869 VALUE v, type;
11870 LABEL *lstart, *lend, *lcont;
11871 unsigned int sp;
11872
11873 v = rb_to_array_type(RARRAY_AREF(exception, i));
11874 if (RARRAY_LEN(v) != 6) {
11875 rb_raise(rb_eSyntaxError, "wrong exception entry");
11876 }
11877 type = get_exception_sym2type(RARRAY_AREF(v, 0));
11878 if (NIL_P(RARRAY_AREF(v, 1))) {
11879 eiseq = NULL;
11880 }
11881 else {
11882 eiseq = rb_iseqw_to_iseq(rb_iseq_load(RARRAY_AREF(v, 1), (VALUE)iseq, Qnil));
11883 }
11884
11885 lstart = register_label(iseq, labels_table, RARRAY_AREF(v, 2));
11886 lend = register_label(iseq, labels_table, RARRAY_AREF(v, 3));
11887 lcont = register_label(iseq, labels_table, RARRAY_AREF(v, 4));
11888 sp = NUM2UINT(RARRAY_AREF(v, 5));
11889
11890 /* TODO: Dirty Hack! Fix me */
11891 if (type == CATCH_TYPE_RESCUE ||
11892 type == CATCH_TYPE_BREAK ||
11893 type == CATCH_TYPE_NEXT) {
11894 ++sp;
11895 }
11896
11897 lcont->sp = sp;
11898
11899 ADD_CATCH_ENTRY(type, lstart, lend, eiseq, lcont);
11900
11901 RB_GC_GUARD(v);
11902 }
11903 return COMPILE_OK;
11904}
11905
11906static struct st_table *
11907insn_make_insn_table(void)
11908{
11909 struct st_table *table;
11910 int i;
11911 table = st_init_numtable_with_size(VM_INSTRUCTION_SIZE);
11912
11913 for (i=0; i<VM_INSTRUCTION_SIZE; i++) {
11914 st_insert(table, ID2SYM(rb_intern_const(insn_name(i))), i);
11915 }
11916
11917 return table;
11918}
11919
11920static const rb_iseq_t *
11921iseq_build_load_iseq(const rb_iseq_t *iseq, VALUE op)
11922{
11923 VALUE iseqw;
11924 const rb_iseq_t *loaded_iseq;
11925
11926 if (RB_TYPE_P(op, T_ARRAY)) {
11927 iseqw = rb_iseq_load(op, (VALUE)iseq, Qnil);
11928 }
11929 else if (CLASS_OF(op) == rb_cISeq) {
11930 iseqw = op;
11931 }
11932 else {
11933 rb_raise(rb_eSyntaxError, "ISEQ is required");
11934 }
11935
11936 loaded_iseq = rb_iseqw_to_iseq(iseqw);
11937 return loaded_iseq;
11938}
11939
11940static VALUE
11941iseq_build_callinfo_from_hash(rb_iseq_t *iseq, VALUE op)
11942{
11943 ID mid = 0;
11944 int orig_argc = 0;
11945 unsigned int flag = 0;
11946 struct rb_callinfo_kwarg *kw_arg = 0;
11947
11948 if (!NIL_P(op)) {
11949 VALUE vmid = rb_hash_aref(op, ID2SYM(rb_intern_const("mid")));
11950 VALUE vflag = rb_hash_aref(op, ID2SYM(rb_intern_const("flag")));
11951 VALUE vorig_argc = rb_hash_aref(op, ID2SYM(rb_intern_const("orig_argc")));
11952 VALUE vkw_arg = rb_hash_aref(op, ID2SYM(rb_intern_const("kw_arg")));
11953
11954 if (!NIL_P(vmid)) mid = SYM2ID(vmid);
11955 if (!NIL_P(vflag)) flag = NUM2UINT(vflag);
11956 if (!NIL_P(vorig_argc)) orig_argc = FIX2INT(vorig_argc);
11957
11958 if (!NIL_P(vkw_arg)) {
11959 int i;
11960 int len = RARRAY_LENINT(vkw_arg);
11961 size_t n = rb_callinfo_kwarg_bytes(len);
11962
11963 kw_arg = xmalloc(n);
11964 kw_arg->references = 0;
11965 kw_arg->keyword_len = len;
11966 for (i = 0; i < len; i++) {
11967 VALUE kw = RARRAY_AREF(vkw_arg, i);
11968 SYM2ID(kw); /* make immortal */
11969 kw_arg->keywords[i] = kw;
11970 }
11971 }
11972 }
11973
11974 const struct rb_callinfo *ci = new_callinfo(iseq, mid, orig_argc, flag, kw_arg, (flag & VM_CALL_ARGS_SIMPLE) == 0);
11975 RB_OBJ_WRITTEN(iseq, Qundef, ci);
11976 return (VALUE)ci;
11977}
11978
11979static rb_event_flag_t
11980event_name_to_flag(VALUE sym)
11981{
11982#define CHECK_EVENT(ev) if (sym == ID2SYM(rb_intern_const(#ev))) return ev;
11983 CHECK_EVENT(RUBY_EVENT_LINE);
11984 CHECK_EVENT(RUBY_EVENT_CLASS);
11985 CHECK_EVENT(RUBY_EVENT_END);
11986 CHECK_EVENT(RUBY_EVENT_CALL);
11987 CHECK_EVENT(RUBY_EVENT_RETURN);
11988 CHECK_EVENT(RUBY_EVENT_B_CALL);
11989 CHECK_EVENT(RUBY_EVENT_B_RETURN);
11990 CHECK_EVENT(RUBY_EVENT_RESCUE);
11991#undef CHECK_EVENT
11992 return RUBY_EVENT_NONE;
11993}
11994
11995static int
11996iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
11997 VALUE body, VALUE node_ids, VALUE labels_wrapper)
11998{
11999 /* TODO: body should be frozen */
12000 long i, len = RARRAY_LEN(body);
12001 struct st_table *labels_table = RTYPEDDATA_DATA(labels_wrapper);
12002 int j;
12003 int line_no = 0, node_id = -1, insn_idx = 0;
12004 int ret = COMPILE_OK;
12005
12006 /*
12007 * index -> LABEL *label
12008 */
12009 static struct st_table *insn_table;
12010
12011 if (insn_table == 0) {
12012 insn_table = insn_make_insn_table();
12013 }
12014
12015 for (i=0; i<len; i++) {
12016 VALUE obj = RARRAY_AREF(body, i);
12017
12018 if (SYMBOL_P(obj)) {
12019 rb_event_flag_t event;
12020 if ((event = event_name_to_flag(obj)) != RUBY_EVENT_NONE) {
12021 ADD_TRACE(anchor, event);
12022 }
12023 else {
12024 LABEL *label = register_label(iseq, labels_table, obj);
12025 ADD_LABEL(anchor, label);
12026 }
12027 }
12028 else if (FIXNUM_P(obj)) {
12029 line_no = NUM2INT(obj);
12030 }
12031 else if (RB_TYPE_P(obj, T_ARRAY)) {
12032 VALUE *argv = 0;
12033 int argc = RARRAY_LENINT(obj) - 1;
12034 st_data_t insn_id;
12035 VALUE insn;
12036
12037 if (node_ids) {
12038 node_id = NUM2INT(rb_ary_entry(node_ids, insn_idx++));
12039 }
12040
12041 insn = (argc < 0) ? Qnil : RARRAY_AREF(obj, 0);
12042 if (st_lookup(insn_table, (st_data_t)insn, &insn_id) == 0) {
12043 /* TODO: exception */
12044 COMPILE_ERROR(iseq, line_no,
12045 "unknown instruction: %+"PRIsVALUE, insn);
12046 ret = COMPILE_NG;
12047 break;
12048 }
12049
12050 if (argc != insn_len((VALUE)insn_id)-1) {
12051 COMPILE_ERROR(iseq, line_no,
12052 "operand size mismatch");
12053 ret = COMPILE_NG;
12054 break;
12055 }
12056
12057 if (argc > 0) {
12058 argv = compile_data_calloc2_type(iseq, VALUE, argc);
12059
12060 // add element before operand setup to make GC root
12061 ADD_ELEM(anchor,
12062 (LINK_ELEMENT*)new_insn_core(iseq, line_no, node_id,
12063 (enum ruby_vminsn_type)insn_id, argc, argv));
12064
12065 for (j=0; j<argc; j++) {
12066 VALUE op = rb_ary_entry(obj, j+1);
12067 switch (insn_op_type((VALUE)insn_id, j)) {
12068 case TS_OFFSET: {
12069 LABEL *label = register_label(iseq, labels_table, op);
12070 argv[j] = (VALUE)label;
12071 break;
12072 }
12073 case TS_LINDEX:
12074 case TS_NUM:
12075 (void)NUM2INT(op);
12076 argv[j] = op;
12077 break;
12078 case TS_VALUE:
12079 argv[j] = op;
12080 RB_OBJ_WRITTEN(iseq, Qundef, op);
12081 break;
12082 case TS_ISEQ:
12083 {
12084 if (op != Qnil) {
12085 VALUE v = (VALUE)iseq_build_load_iseq(iseq, op);
12086 argv[j] = v;
12087 RB_OBJ_WRITTEN(iseq, Qundef, v);
12088 }
12089 else {
12090 argv[j] = 0;
12091 }
12092 }
12093 break;
12094 case TS_ISE:
12095 argv[j] = op;
12096 if (NUM2UINT(op) >= ISEQ_BODY(iseq)->ise_size) {
12097 ISEQ_BODY(iseq)->ise_size = NUM2INT(op) + 1;
12098 }
12099 break;
12100 case TS_IC:
12101 {
12102 VALUE segments = rb_ary_new();
12103 op = rb_to_array_type(op);
12104
12105 for (int i = 0; i < RARRAY_LEN(op); i++) {
12106 VALUE sym = RARRAY_AREF(op, i);
12107 sym = rb_to_symbol_type(sym);
12108 rb_ary_push(segments, sym);
12109 }
12110
12111 RB_GC_GUARD(op);
12112 argv[j] = segments;
12113 RB_OBJ_WRITTEN(iseq, Qundef, segments);
12114 ISEQ_BODY(iseq)->ic_size++;
12115 }
12116 break;
12117 case TS_IVC: /* inline ivar cache */
12118 argv[j] = op;
12119 if (NUM2UINT(op) >= ISEQ_BODY(iseq)->ivc_size) {
12120 ISEQ_BODY(iseq)->ivc_size = NUM2INT(op) + 1;
12121 }
12122 break;
12123 case TS_ICVARC: /* inline cvar cache */
12124 argv[j] = op;
12125 if (NUM2UINT(op) >= ISEQ_BODY(iseq)->icvarc_size) {
12126 ISEQ_BODY(iseq)->icvarc_size = NUM2INT(op) + 1;
12127 }
12128 break;
12129 case TS_CALLDATA:
12130 argv[j] = iseq_build_callinfo_from_hash(iseq, op);
12131 break;
12132 case TS_ID:
12133 argv[j] = rb_to_symbol_type(op);
12134 break;
12135 case TS_CDHASH:
12136 {
12137 int i;
12138 VALUE map = rb_hash_new_with_size(RARRAY_LEN(op)/2);
12139
12140 RHASH_TBL_RAW(map)->type = &cdhash_type;
12141 op = rb_to_array_type(op);
12142 for (i=0; i<RARRAY_LEN(op); i+=2) {
12143 VALUE key = RARRAY_AREF(op, i);
12144 VALUE sym = RARRAY_AREF(op, i+1);
12145 LABEL *label =
12146 register_label(iseq, labels_table, sym);
12147 rb_hash_aset(map, key, (VALUE)label | 1);
12148 }
12149 RB_GC_GUARD(op);
12150 RB_OBJ_SET_SHAREABLE(rb_obj_hide(map)); // allow mutation while compiling
12151 argv[j] = map;
12152 RB_OBJ_WRITTEN(iseq, Qundef, map);
12153 }
12154 break;
12155 case TS_FUNCPTR:
12156 {
12157#if SIZEOF_VALUE <= SIZEOF_LONG
12158 long funcptr = NUM2LONG(op);
12159#else
12160 LONG_LONG funcptr = NUM2LL(op);
12161#endif
12162 argv[j] = (VALUE)funcptr;
12163 }
12164 break;
12165 default:
12166 rb_raise(rb_eSyntaxError, "unknown operand: %c", insn_op_type((VALUE)insn_id, j));
12167 }
12168 }
12169 }
12170 else {
12171 ADD_ELEM(anchor,
12172 (LINK_ELEMENT*)new_insn_core(iseq, line_no, node_id,
12173 (enum ruby_vminsn_type)insn_id, argc, NULL));
12174 }
12175 }
12176 else {
12177 rb_raise(rb_eTypeError, "unexpected object for instruction");
12178 }
12179 }
12180 RTYPEDDATA_DATA(labels_wrapper) = 0;
12181 RB_GC_GUARD(labels_wrapper);
12182 validate_labels(iseq, labels_table);
12183 if (!ret) return ret;
12184 return iseq_setup(iseq, anchor);
12185}
12186
12187#define CHECK_ARRAY(v) rb_to_array_type(v)
12188#define CHECK_SYMBOL(v) rb_to_symbol_type(v)
12189
12190static int
12191int_param(int *dst, VALUE param, VALUE sym)
12192{
12193 VALUE val = rb_hash_aref(param, sym);
12194 if (FIXNUM_P(val)) {
12195 *dst = FIX2INT(val);
12196 return TRUE;
12197 }
12198 else if (!NIL_P(val)) {
12199 rb_raise(rb_eTypeError, "invalid %+"PRIsVALUE" Fixnum: %+"PRIsVALUE,
12200 sym, val);
12201 }
12202 return FALSE;
12203}
12204
12205static const struct rb_iseq_param_keyword *
12206iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords)
12207{
12208 int i, j;
12209 int len = RARRAY_LENINT(keywords);
12210 int default_len;
12211 VALUE key, sym, default_val;
12212 VALUE *dvs;
12213 ID *ids;
12214 struct rb_iseq_param_keyword *keyword = ZALLOC(struct rb_iseq_param_keyword);
12215
12216 ISEQ_BODY(iseq)->param.flags.has_kw = TRUE;
12217
12218 keyword->num = len;
12219#define SYM(s) ID2SYM(rb_intern_const(#s))
12220 (void)int_param(&keyword->bits_start, params, SYM(kwbits));
12221 i = keyword->bits_start - keyword->num;
12222 ids = (ID *)&ISEQ_BODY(iseq)->local_table[i];
12223#undef SYM
12224
12225 /* required args */
12226 for (i = 0; i < len; i++) {
12227 VALUE val = RARRAY_AREF(keywords, i);
12228
12229 if (!SYMBOL_P(val)) {
12230 goto default_values;
12231 }
12232 ids[i] = SYM2ID(val);
12233 keyword->required_num++;
12234 }
12235
12236 default_values: /* note: we intentionally preserve `i' from previous loop */
12237 default_len = len - i;
12238 if (default_len == 0) {
12239 keyword->table = ids;
12240 return keyword;
12241 }
12242 else if (default_len < 0) {
12244 }
12245
12246 dvs = ALLOC_N(VALUE, (unsigned int)default_len);
12247
12248 for (j = 0; i < len; i++, j++) {
12249 key = RARRAY_AREF(keywords, i);
12250 CHECK_ARRAY(key);
12251
12252 switch (RARRAY_LEN(key)) {
12253 case 1:
12254 sym = RARRAY_AREF(key, 0);
12255 default_val = Qundef;
12256 break;
12257 case 2:
12258 sym = RARRAY_AREF(key, 0);
12259 default_val = RARRAY_AREF(key, 1);
12260 break;
12261 default:
12262 rb_raise(rb_eTypeError, "keyword default has unsupported len %+"PRIsVALUE, key);
12263 }
12264 ids[i] = SYM2ID(sym);
12265 RB_OBJ_WRITE(iseq, &dvs[j], default_val);
12266 }
12267
12268 keyword->table = ids;
12269 keyword->default_values = dvs;
12270
12271 return keyword;
12272}
12273
12274static void
12275iseq_insn_each_object_mark_and_move(VALUE * obj, VALUE _)
12276{
12277 rb_gc_mark_and_move(obj);
12278}
12279
12280void
12281rb_iseq_mark_and_move_insn_storage(struct iseq_compile_data_storage *storage)
12282{
12283 INSN *iobj = 0;
12284 size_t size = sizeof(INSN);
12285 size_t align = ALIGNMENT_SIZE_OF(INSN);
12286 unsigned int pos = 0;
12287
12288 while (storage) {
12289 size_t padding = calc_padding((void *)&storage->buff[pos], align);
12290 size_t offset = pos + size + padding;
12291 if (offset > storage->size || offset > storage->pos) {
12292 pos = 0;
12293 storage = storage->next;
12294 }
12295 else {
12296 pos += (int)padding;
12297
12298 iobj = (INSN *)&storage->buff[pos];
12299
12300 if (iobj->operands) {
12301 iseq_insn_each_markable_object(iobj, iseq_insn_each_object_mark_and_move, (VALUE)0);
12302 }
12303 pos += (int)size;
12304 }
12305 }
12306}
12307
12308static const rb_data_type_t labels_wrapper_type = {
12309 .wrap_struct_name = "compiler/labels_wrapper",
12310 .function = {
12311 .dmark = (RUBY_DATA_FUNC)rb_mark_set,
12312 .dfree = (RUBY_DATA_FUNC)st_free_table,
12313 },
12314 .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
12315};
12316
12317void
12318rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
12319 VALUE exception, VALUE body)
12320{
12321#define SYM(s) ID2SYM(rb_intern_const(#s))
12322 int i, len;
12323 unsigned int arg_size, local_size, stack_max;
12324 ID *tbl;
12325 struct st_table *labels_table = st_init_numtable();
12326 VALUE labels_wrapper = TypedData_Wrap_Struct(0, &labels_wrapper_type, labels_table);
12327 VALUE arg_opt_labels = rb_hash_aref(params, SYM(opt));
12328 VALUE keywords = rb_hash_aref(params, SYM(keyword));
12329 VALUE sym_arg_rest = ID2SYM(rb_intern_const("#arg_rest"));
12330 DECL_ANCHOR(anchor);
12331 INIT_ANCHOR(anchor);
12332
12333 len = RARRAY_LENINT(locals);
12334 ISEQ_BODY(iseq)->local_table_size = len;
12335 ISEQ_BODY(iseq)->local_table = tbl = len > 0 ? (ID *)ALLOC_N(ID, ISEQ_BODY(iseq)->local_table_size) : NULL;
12336
12337 for (i = 0; i < len; i++) {
12338 VALUE lv = RARRAY_AREF(locals, i);
12339
12340 if (sym_arg_rest == lv) {
12341 tbl[i] = 0;
12342 }
12343 else {
12344 tbl[i] = FIXNUM_P(lv) ? (ID)FIX2LONG(lv) : SYM2ID(CHECK_SYMBOL(lv));
12345 }
12346 }
12347
12348#define INT_PARAM(F) int_param(&ISEQ_BODY(iseq)->param.F, params, SYM(F))
12349 if (INT_PARAM(lead_num)) {
12350 ISEQ_BODY(iseq)->param.flags.has_lead = TRUE;
12351 }
12352 if (INT_PARAM(post_num)) ISEQ_BODY(iseq)->param.flags.has_post = TRUE;
12353 if (INT_PARAM(post_start)) ISEQ_BODY(iseq)->param.flags.has_post = TRUE;
12354 if (INT_PARAM(rest_start)) ISEQ_BODY(iseq)->param.flags.has_rest = TRUE;
12355 if (INT_PARAM(block_start)) ISEQ_BODY(iseq)->param.flags.has_block = TRUE;
12356#undef INT_PARAM
12357 {
12358#define INT_PARAM(F) F = (int_param(&x, misc, SYM(F)) ? (unsigned int)x : 0)
12359 int x;
12360 INT_PARAM(arg_size);
12361 INT_PARAM(local_size);
12362 INT_PARAM(stack_max);
12363#undef INT_PARAM
12364 }
12365
12366 VALUE node_ids = Qfalse;
12367#ifdef USE_ISEQ_NODE_ID
12368 node_ids = rb_hash_aref(misc, ID2SYM(rb_intern("node_ids")));
12369 if (!RB_TYPE_P(node_ids, T_ARRAY)) {
12370 rb_raise(rb_eTypeError, "node_ids is not an array");
12371 }
12372#endif
12373
12374 if (RB_TYPE_P(arg_opt_labels, T_ARRAY)) {
12375 len = RARRAY_LENINT(arg_opt_labels);
12376 ISEQ_BODY(iseq)->param.flags.has_opt = !!(len - 1 >= 0);
12377
12378 if (ISEQ_BODY(iseq)->param.flags.has_opt) {
12379 VALUE *opt_table = ALLOC_N(VALUE, len);
12380
12381 for (i = 0; i < len; i++) {
12382 VALUE ent = RARRAY_AREF(arg_opt_labels, i);
12383 LABEL *label = register_label(iseq, labels_table, ent);
12384 opt_table[i] = (VALUE)label;
12385 }
12386
12387 ISEQ_BODY(iseq)->param.opt_num = len - 1;
12388 ISEQ_BODY(iseq)->param.opt_table = opt_table;
12389 }
12390 }
12391 else if (!NIL_P(arg_opt_labels)) {
12392 rb_raise(rb_eTypeError, ":opt param is not an array: %+"PRIsVALUE,
12393 arg_opt_labels);
12394 }
12395
12396 if (RB_TYPE_P(keywords, T_ARRAY)) {
12397 ISEQ_BODY(iseq)->param.keyword = iseq_build_kw(iseq, params, keywords);
12398 }
12399 else if (!NIL_P(keywords)) {
12400 rb_raise(rb_eTypeError, ":keywords param is not an array: %+"PRIsVALUE,
12401 keywords);
12402 }
12403
12404 if (Qtrue == rb_hash_aref(params, SYM(ambiguous_param0))) {
12405 ISEQ_BODY(iseq)->param.flags.ambiguous_param0 = TRUE;
12406 }
12407
12408 if (Qtrue == rb_hash_aref(params, SYM(use_block))) {
12409 ISEQ_BODY(iseq)->param.flags.use_block = TRUE;
12410 }
12411
12412 if (int_param(&i, params, SYM(kwrest))) {
12413 struct rb_iseq_param_keyword *keyword = (struct rb_iseq_param_keyword *)ISEQ_BODY(iseq)->param.keyword;
12414 if (keyword == NULL) {
12415 ISEQ_BODY(iseq)->param.keyword = keyword = ZALLOC(struct rb_iseq_param_keyword);
12416 }
12417 keyword->rest_start = i;
12418 ISEQ_BODY(iseq)->param.flags.has_kwrest = TRUE;
12419 }
12420#undef SYM
12421 iseq_calc_param_size(iseq);
12422
12423 /* exception */
12424 iseq_build_from_ary_exception(iseq, labels_table, exception);
12425
12426 /* body */
12427 iseq_build_from_ary_body(iseq, anchor, body, node_ids, labels_wrapper);
12428
12429 ISEQ_BODY(iseq)->param.size = arg_size;
12430 ISEQ_BODY(iseq)->local_table_size = local_size;
12431 ISEQ_BODY(iseq)->stack_max = stack_max;
12432}
12433
12434/* for parser */
12435
12436int
12437rb_dvar_defined(ID id, const rb_iseq_t *iseq)
12438{
12439 if (iseq) {
12440 const struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
12441 while (body->type == ISEQ_TYPE_BLOCK ||
12442 body->type == ISEQ_TYPE_RESCUE ||
12443 body->type == ISEQ_TYPE_ENSURE ||
12444 body->type == ISEQ_TYPE_EVAL ||
12445 body->type == ISEQ_TYPE_MAIN
12446 ) {
12447 unsigned int i;
12448
12449 for (i = 0; i < body->local_table_size; i++) {
12450 if (body->local_table[i] == id) {
12451 return 1;
12452 }
12453 }
12454 iseq = body->parent_iseq;
12455 body = ISEQ_BODY(iseq);
12456 }
12457 }
12458 return 0;
12459}
12460
12461int
12462rb_local_defined(ID id, const rb_iseq_t *iseq)
12463{
12464 if (iseq) {
12465 unsigned int i;
12466 const struct rb_iseq_constant_body *const body = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq);
12467
12468 for (i=0; i<body->local_table_size; i++) {
12469 if (body->local_table[i] == id) {
12470 return 1;
12471 }
12472 }
12473 }
12474 return 0;
12475}
12476
12477/* ISeq binary format */
12478
12479#ifndef IBF_ISEQ_DEBUG
12480#define IBF_ISEQ_DEBUG 0
12481#endif
12482
12483#ifndef IBF_ISEQ_ENABLE_LOCAL_BUFFER
12484#define IBF_ISEQ_ENABLE_LOCAL_BUFFER 0
12485#endif
12486
12487typedef uint32_t ibf_offset_t;
12488#define IBF_OFFSET(ptr) ((ibf_offset_t)(VALUE)(ptr))
12489
12490#define IBF_MAJOR_VERSION ISEQ_MAJOR_VERSION
12491#ifdef RUBY_DEVEL
12492#define IBF_DEVEL_VERSION 5
12493#define IBF_MINOR_VERSION (ISEQ_MINOR_VERSION * 10000 + IBF_DEVEL_VERSION)
12494#else
12495#define IBF_MINOR_VERSION ISEQ_MINOR_VERSION
12496#endif
12497
12498static const char IBF_ENDIAN_MARK =
12499#ifdef WORDS_BIGENDIAN
12500 'b'
12501#else
12502 'l'
12503#endif
12504 ;
12505
12507 char magic[4]; /* YARB */
12508 uint32_t major_version;
12509 uint32_t minor_version;
12510 uint32_t size;
12511 uint32_t extra_size;
12512
12513 uint32_t iseq_list_size;
12514 uint32_t global_object_list_size;
12515 ibf_offset_t iseq_list_offset;
12516 ibf_offset_t global_object_list_offset;
12517 uint8_t endian;
12518 uint8_t wordsize; /* assume no 2048-bit CPU */
12519};
12520
12522 VALUE str;
12523 st_table *obj_table; /* obj -> obj number */
12524};
12525
12526struct ibf_dump {
12527 st_table *iseq_table; /* iseq -> iseq number */
12528 struct ibf_dump_buffer global_buffer;
12529 struct ibf_dump_buffer *current_buffer;
12530};
12531
12533 const char *buff;
12534 ibf_offset_t size;
12535
12536 VALUE obj_list; /* [obj0, ...] */
12537 unsigned int obj_list_size;
12538 ibf_offset_t obj_list_offset;
12539};
12540
12541struct ibf_load {
12542 const struct ibf_header *header;
12543 VALUE iseq_list; /* [iseq0, ...] */
12544 struct ibf_load_buffer global_buffer;
12545 VALUE loader_obj;
12546 rb_iseq_t *iseq;
12547 VALUE str;
12548 struct ibf_load_buffer *current_buffer;
12549};
12550
12552 long size;
12553 VALUE buffer[1];
12554};
12555
12556static void
12557pinned_list_mark(void *ptr)
12558{
12559 long i;
12560 struct pinned_list *list = (struct pinned_list *)ptr;
12561 for (i = 0; i < list->size; i++) {
12562 if (list->buffer[i]) {
12563 rb_gc_mark(list->buffer[i]);
12564 }
12565 }
12566}
12567
12568static const rb_data_type_t pinned_list_type = {
12569 "pinned_list",
12570 {
12571 pinned_list_mark,
12573 NULL, // No external memory to report,
12574 },
12575 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE
12576};
12577
12578static VALUE
12579pinned_list_fetch(VALUE list, long offset)
12580{
12581 struct pinned_list * ptr;
12582
12583 TypedData_Get_Struct(list, struct pinned_list, &pinned_list_type, ptr);
12584
12585 if (offset >= ptr->size) {
12586 rb_raise(rb_eIndexError, "object index out of range: %ld", offset);
12587 }
12588
12589 return ptr->buffer[offset];
12590}
12591
12592static void
12593pinned_list_store(VALUE list, long offset, VALUE object)
12594{
12595 struct pinned_list * ptr;
12596
12597 TypedData_Get_Struct(list, struct pinned_list, &pinned_list_type, ptr);
12598
12599 if (offset >= ptr->size) {
12600 rb_raise(rb_eIndexError, "object index out of range: %ld", offset);
12601 }
12602
12603 RB_OBJ_WRITE(list, &ptr->buffer[offset], object);
12604}
12605
12606static VALUE
12607pinned_list_new(long size)
12608{
12609 size_t memsize = offsetof(struct pinned_list, buffer) + size * sizeof(VALUE);
12610 VALUE obj_list = rb_data_typed_object_zalloc(0, memsize, &pinned_list_type);
12611 struct pinned_list * ptr = RTYPEDDATA_GET_DATA(obj_list);
12612 ptr->size = size;
12613 return obj_list;
12614}
12615
12616static ibf_offset_t
12617ibf_dump_pos(struct ibf_dump *dump)
12618{
12619 long pos = RSTRING_LEN(dump->current_buffer->str);
12620#if SIZEOF_LONG > SIZEOF_INT
12621 if (pos >= UINT_MAX) {
12622 rb_raise(rb_eRuntimeError, "dump size exceeds");
12623 }
12624#endif
12625 return (unsigned int)pos;
12626}
12627
12628static void
12629ibf_dump_align(struct ibf_dump *dump, size_t align)
12630{
12631 ibf_offset_t pos = ibf_dump_pos(dump);
12632 if (pos % align) {
12633 static const char padding[sizeof(VALUE)];
12634 size_t size = align - ((size_t)pos % align);
12635#if SIZEOF_LONG > SIZEOF_INT
12636 if (pos + size >= UINT_MAX) {
12637 rb_raise(rb_eRuntimeError, "dump size exceeds");
12638 }
12639#endif
12640 for (; size > sizeof(padding); size -= sizeof(padding)) {
12641 rb_str_cat(dump->current_buffer->str, padding, sizeof(padding));
12642 }
12643 rb_str_cat(dump->current_buffer->str, padding, size);
12644 }
12645}
12646
12647static ibf_offset_t
12648ibf_dump_write(struct ibf_dump *dump, const void *buff, unsigned long size)
12649{
12650 ibf_offset_t pos = ibf_dump_pos(dump);
12651#if SIZEOF_LONG > SIZEOF_INT
12652 /* ensure the resulting dump does not exceed UINT_MAX */
12653 if (size >= UINT_MAX || pos + size >= UINT_MAX) {
12654 rb_raise(rb_eRuntimeError, "dump size exceeds");
12655 }
12656#endif
12657 rb_str_cat(dump->current_buffer->str, (const char *)buff, size);
12658 return pos;
12659}
12660
12661static ibf_offset_t
12662ibf_dump_write_byte(struct ibf_dump *dump, unsigned char byte)
12663{
12664 return ibf_dump_write(dump, &byte, sizeof(unsigned char));
12665}
12666
12667static void
12668ibf_dump_overwrite(struct ibf_dump *dump, void *buff, unsigned int size, long offset)
12669{
12670 VALUE str = dump->current_buffer->str;
12671 char *ptr = RSTRING_PTR(str);
12672 if ((unsigned long)(size + offset) > (unsigned long)RSTRING_LEN(str))
12673 rb_bug("ibf_dump_overwrite: overflow");
12674 memcpy(ptr + offset, buff, size);
12675}
12676
12677static const void *
12678ibf_load_ptr(const struct ibf_load *load, ibf_offset_t *offset, int size)
12679{
12680 ibf_offset_t beg = *offset;
12681 *offset += size;
12682 return load->current_buffer->buff + beg;
12683}
12684
12685static void *
12686ibf_load_alloc(const struct ibf_load *load, ibf_offset_t offset, size_t x, size_t y)
12687{
12688 void *buff = ruby_xmalloc2(x, y);
12689 size_t size = x * y;
12690 memcpy(buff, load->current_buffer->buff + offset, size);
12691 return buff;
12692}
12693
12694#define IBF_W_ALIGN(type) (RUBY_ALIGNOF(type) > 1 ? ibf_dump_align(dump, RUBY_ALIGNOF(type)) : (void)0)
12695
12696#define IBF_W(b, type, n) (IBF_W_ALIGN(type), (type *)(VALUE)IBF_WP(b, type, n))
12697#define IBF_WV(variable) ibf_dump_write(dump, &(variable), sizeof(variable))
12698#define IBF_WP(b, type, n) ibf_dump_write(dump, (b), sizeof(type) * (n))
12699#define IBF_R(val, type, n) (type *)ibf_load_alloc(load, IBF_OFFSET(val), sizeof(type), (n))
12700#define IBF_ZERO(variable) memset(&(variable), 0, sizeof(variable))
12701
12702static int
12703ibf_table_lookup(struct st_table *table, st_data_t key)
12704{
12705 st_data_t val;
12706
12707 if (st_lookup(table, key, &val)) {
12708 return (int)val;
12709 }
12710 else {
12711 return -1;
12712 }
12713}
12714
12715static int
12716ibf_table_find_or_insert(struct st_table *table, st_data_t key)
12717{
12718 int index = ibf_table_lookup(table, key);
12719
12720 if (index < 0) { /* not found */
12721 index = (int)table->num_entries;
12722 st_insert(table, key, (st_data_t)index);
12723 }
12724
12725 return index;
12726}
12727
12728/* dump/load generic */
12729
12730static void ibf_dump_object_list(struct ibf_dump *dump, ibf_offset_t *obj_list_offset, unsigned int *obj_list_size);
12731
12732static VALUE ibf_load_object(const struct ibf_load *load, VALUE object_index);
12733static rb_iseq_t *ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq);
12734
12735static st_table *
12736ibf_dump_object_table_new(void)
12737{
12738 st_table *obj_table = st_init_numtable(); /* need free */
12739 st_insert(obj_table, (st_data_t)Qnil, (st_data_t)0); /* 0th is nil */
12740
12741 return obj_table;
12742}
12743
12744static VALUE
12745ibf_dump_object(struct ibf_dump *dump, VALUE obj)
12746{
12747 return ibf_table_find_or_insert(dump->current_buffer->obj_table, (st_data_t)obj);
12748}
12749
12750static VALUE
12751ibf_dump_id(struct ibf_dump *dump, ID id)
12752{
12753 if (id == 0 || rb_id2name(id) == NULL) {
12754 return 0;
12755 }
12756 return ibf_dump_object(dump, rb_id2sym(id));
12757}
12758
12759static ID
12760ibf_load_id(const struct ibf_load *load, const ID id_index)
12761{
12762 if (id_index == 0) {
12763 return 0;
12764 }
12765 VALUE sym = ibf_load_object(load, id_index);
12766 if (rb_integer_type_p(sym)) {
12767 /* Load hidden local variables as indexes */
12768 return NUM2ULONG(sym);
12769 }
12770 return rb_sym2id(sym);
12771}
12772
12773/* dump/load: code */
12774
12775static ibf_offset_t ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq);
12776
12777static int
12778ibf_dump_iseq(struct ibf_dump *dump, const rb_iseq_t *iseq)
12779{
12780 if (iseq == NULL) {
12781 return -1;
12782 }
12783 else {
12784 return ibf_table_find_or_insert(dump->iseq_table, (st_data_t)iseq);
12785 }
12786}
12787
12788static unsigned char
12789ibf_load_byte(const struct ibf_load *load, ibf_offset_t *offset)
12790{
12791 if (*offset >= load->current_buffer->size) { rb_raise(rb_eRuntimeError, "invalid bytecode"); }
12792 return (unsigned char)load->current_buffer->buff[(*offset)++];
12793}
12794
12795/*
12796 * Small uint serialization
12797 * 0x00000000_00000000 - 0x00000000_0000007f: 1byte | XXXX XXX1 |
12798 * 0x00000000_00000080 - 0x00000000_00003fff: 2byte | XXXX XX10 | XXXX XXXX |
12799 * 0x00000000_00004000 - 0x00000000_001fffff: 3byte | XXXX X100 | XXXX XXXX | XXXX XXXX |
12800 * 0x00000000_00020000 - 0x00000000_0fffffff: 4byte | XXXX 1000 | XXXX XXXX | XXXX XXXX | XXXX XXXX |
12801 * ...
12802 * 0x00010000_00000000 - 0x00ffffff_ffffffff: 8byte | 1000 0000 | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX |
12803 * 0x01000000_00000000 - 0xffffffff_ffffffff: 9byte | 0000 0000 | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX |
12804 */
12805static void
12806ibf_dump_write_small_value(struct ibf_dump *dump, VALUE x)
12807{
12808 if (sizeof(VALUE) > 8 || CHAR_BIT != 8) {
12809 ibf_dump_write(dump, &x, sizeof(VALUE));
12810 return;
12811 }
12812
12813 enum { max_byte_length = sizeof(VALUE) + 1 };
12814
12815 unsigned char bytes[max_byte_length];
12816 ibf_offset_t n;
12817
12818 for (n = 0; n < sizeof(VALUE) && (x >> (7 - n)); n++, x >>= 8) {
12819 bytes[max_byte_length - 1 - n] = (unsigned char)x;
12820 }
12821
12822 x <<= 1;
12823 x |= 1;
12824 x <<= n;
12825 bytes[max_byte_length - 1 - n] = (unsigned char)x;
12826 n++;
12827
12828 ibf_dump_write(dump, bytes + max_byte_length - n, n);
12829}
12830
12831static VALUE
12832ibf_load_small_value(const struct ibf_load *load, ibf_offset_t *offset)
12833{
12834 if (sizeof(VALUE) > 8 || CHAR_BIT != 8) {
12835 union { char s[sizeof(VALUE)]; VALUE v; } x;
12836
12837 memcpy(x.s, load->current_buffer->buff + *offset, sizeof(VALUE));
12838 *offset += sizeof(VALUE);
12839
12840 return x.v;
12841 }
12842
12843 enum { max_byte_length = sizeof(VALUE) + 1 };
12844
12845 const unsigned char *buffer = (const unsigned char *)load->current_buffer->buff;
12846 const unsigned char c = buffer[*offset];
12847
12848 ibf_offset_t n =
12849 c & 1 ? 1 :
12850 c == 0 ? 9 : ntz_int32(c) + 1;
12851 VALUE x = (VALUE)c >> n;
12852
12853 if (*offset + n > load->current_buffer->size) {
12854 rb_raise(rb_eRuntimeError, "invalid byte sequence");
12855 }
12856
12857 ibf_offset_t i;
12858 for (i = 1; i < n; i++) {
12859 x <<= 8;
12860 x |= (VALUE)buffer[*offset + i];
12861 }
12862
12863 *offset += n;
12864 return x;
12865}
12866
12867static void
12868ibf_dump_builtin(struct ibf_dump *dump, const struct rb_builtin_function *bf)
12869{
12870 // short: index
12871 // short: name.length
12872 // bytes: name
12873 // // omit argc (only verify with name)
12874 ibf_dump_write_small_value(dump, (VALUE)bf->index);
12875
12876 size_t len = strlen(bf->name);
12877 ibf_dump_write_small_value(dump, (VALUE)len);
12878 ibf_dump_write(dump, bf->name, len);
12879}
12880
12881static const struct rb_builtin_function *
12882ibf_load_builtin(const struct ibf_load *load, ibf_offset_t *offset)
12883{
12884 int i = (int)ibf_load_small_value(load, offset);
12885 int len = (int)ibf_load_small_value(load, offset);
12886 const char *name = (char *)ibf_load_ptr(load, offset, len);
12887
12888 if (0) {
12889 fprintf(stderr, "%.*s!!\n", len, name);
12890 }
12891
12892 const struct rb_builtin_function *table = GET_VM()->builtin_function_table;
12893 if (table == NULL) rb_raise(rb_eArgError, "builtin function table is not provided");
12894 if (strncmp(table[i].name, name, len) != 0) {
12895 rb_raise(rb_eArgError, "builtin function index (%d) mismatch (expect %s but %s)", i, name, table[i].name);
12896 }
12897 // fprintf(stderr, "load-builtin: name:%s(%d)\n", table[i].name, table[i].argc);
12898
12899 return &table[i];
12900}
12901
12902static ibf_offset_t
12903ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
12904{
12905 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
12906 const int iseq_size = body->iseq_size;
12907 int code_index;
12908 const VALUE *orig_code = rb_iseq_original_iseq(iseq);
12909
12910 ibf_offset_t offset = ibf_dump_pos(dump);
12911
12912 for (code_index=0; code_index<iseq_size;) {
12913 const VALUE insn = orig_code[code_index++];
12914 const char *types = insn_op_types(insn);
12915 int op_index;
12916
12917 /* opcode */
12918 if (insn >= 0x100) { rb_raise(rb_eRuntimeError, "invalid instruction"); }
12919 ibf_dump_write_small_value(dump, insn);
12920
12921 /* operands */
12922 for (op_index=0; types[op_index]; op_index++, code_index++) {
12923 VALUE op = orig_code[code_index];
12924 VALUE wv;
12925
12926 switch (types[op_index]) {
12927 case TS_CDHASH:
12928 case TS_VALUE:
12929 wv = ibf_dump_object(dump, op);
12930 break;
12931 case TS_ISEQ:
12932 wv = (VALUE)ibf_dump_iseq(dump, (const rb_iseq_t *)op);
12933 break;
12934 case TS_IC:
12935 {
12936 IC ic = (IC)op;
12937 VALUE arr = idlist_to_array(ic->segments);
12938 wv = ibf_dump_object(dump, arr);
12939 }
12940 break;
12941 case TS_ISE:
12942 case TS_IVC:
12943 case TS_ICVARC:
12944 {
12946 wv = is - ISEQ_IS_ENTRY_START(body, types[op_index]);
12947 }
12948 break;
12949 case TS_CALLDATA:
12950 {
12951 goto skip_wv;
12952 }
12953 case TS_ID:
12954 wv = ibf_dump_id(dump, (ID)op);
12955 break;
12956 case TS_FUNCPTR:
12957 rb_raise(rb_eRuntimeError, "TS_FUNCPTR is not supported");
12958 goto skip_wv;
12959 case TS_BUILTIN:
12960 ibf_dump_builtin(dump, (const struct rb_builtin_function *)op);
12961 goto skip_wv;
12962 default:
12963 wv = op;
12964 break;
12965 }
12966 ibf_dump_write_small_value(dump, wv);
12967 skip_wv:;
12968 }
12969 RUBY_ASSERT(insn_len(insn) == op_index+1);
12970 }
12971
12972 return offset;
12973}
12974
12975static VALUE *
12976ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecode_offset, ibf_offset_t bytecode_size, unsigned int iseq_size)
12977{
12978 VALUE iseqv = (VALUE)iseq;
12979 unsigned int code_index;
12980 ibf_offset_t reading_pos = bytecode_offset;
12981 VALUE *code = ALLOC_N(VALUE, iseq_size);
12982
12983 struct rb_iseq_constant_body *load_body = ISEQ_BODY(iseq);
12984 struct rb_call_data *cd_entries = load_body->call_data;
12985 int ic_index = 0;
12986
12987 load_body->iseq_encoded = code;
12988 load_body->iseq_size = 0;
12989
12990 iseq_bits_t * mark_offset_bits;
12991
12992 iseq_bits_t tmp[1] = {0};
12993
12994 if (ISEQ_MBITS_BUFLEN(iseq_size) == 1) {
12995 mark_offset_bits = tmp;
12996 }
12997 else {
12998 mark_offset_bits = ZALLOC_N(iseq_bits_t, ISEQ_MBITS_BUFLEN(iseq_size));
12999 }
13000 bool needs_bitmap = false;
13001
13002 for (code_index=0; code_index<iseq_size;) {
13003 /* opcode */
13004 const VALUE insn = code[code_index] = ibf_load_small_value(load, &reading_pos);
13005 const char *types = insn_op_types(insn);
13006 int op_index;
13007
13008 code_index++;
13009
13010 /* operands */
13011 for (op_index=0; types[op_index]; op_index++, code_index++) {
13012 const char operand_type = types[op_index];
13013 switch (operand_type) {
13014 case TS_VALUE:
13015 {
13016 VALUE op = ibf_load_small_value(load, &reading_pos);
13017 VALUE v = ibf_load_object(load, op);
13018 code[code_index] = v;
13019 if (!SPECIAL_CONST_P(v)) {
13020 RB_OBJ_WRITTEN(iseqv, Qundef, v);
13021 ISEQ_MBITS_SET(mark_offset_bits, code_index);
13022 needs_bitmap = true;
13023 }
13024 break;
13025 }
13026 case TS_CDHASH:
13027 {
13028 VALUE op = ibf_load_small_value(load, &reading_pos);
13029 VALUE v = ibf_load_object(load, op);
13030 v = rb_hash_dup(v); // hash dumped as frozen
13031 RHASH_TBL_RAW(v)->type = &cdhash_type;
13032 rb_hash_rehash(v); // hash function changed
13033 RB_OBJ_SET_SHAREABLE(freeze_hide_obj(v));
13034
13035 // Overwrite the existing hash in the object list. This
13036 // is to keep the object alive during load time.
13037 // [Bug #17984] [ruby-core:104259]
13038 pinned_list_store(load->current_buffer->obj_list, (long)op, v);
13039
13040 code[code_index] = v;
13041 ISEQ_MBITS_SET(mark_offset_bits, code_index);
13042 RB_OBJ_WRITTEN(iseqv, Qundef, v);
13043 needs_bitmap = true;
13044 break;
13045 }
13046 case TS_ISEQ:
13047 {
13048 VALUE op = (VALUE)ibf_load_small_value(load, &reading_pos);
13049 VALUE v = (VALUE)ibf_load_iseq(load, (const rb_iseq_t *)op);
13050 code[code_index] = v;
13051 if (!SPECIAL_CONST_P(v)) {
13052 RB_OBJ_WRITTEN(iseqv, Qundef, v);
13053 ISEQ_MBITS_SET(mark_offset_bits, code_index);
13054 needs_bitmap = true;
13055 }
13056 break;
13057 }
13058 case TS_IC:
13059 {
13060 VALUE op = ibf_load_small_value(load, &reading_pos);
13061 VALUE arr = ibf_load_object(load, op);
13062
13063 IC ic = &ISEQ_IS_IC_ENTRY(load_body, ic_index++);
13064 ic->segments = array_to_idlist(arr);
13065
13066 code[code_index] = (VALUE)ic;
13067 }
13068 break;
13069 case TS_ISE:
13070 case TS_ICVARC:
13071 case TS_IVC:
13072 {
13073 unsigned int op = (unsigned int)ibf_load_small_value(load, &reading_pos);
13074
13075 ISE ic = ISEQ_IS_ENTRY_START(load_body, operand_type) + op;
13076 code[code_index] = (VALUE)ic;
13077
13078 if (operand_type == TS_IVC) {
13079 IVC cache = (IVC)ic;
13080
13081 if (insn == BIN(setinstancevariable)) {
13082 ID iv_name = (ID)code[code_index - 1];
13083 cache->iv_set_name = iv_name;
13084 }
13085 else {
13086 cache->iv_set_name = 0;
13087 }
13088
13089 vm_ic_attr_index_initialize(cache, INVALID_SHAPE_ID);
13090 }
13091
13092 }
13093 break;
13094 case TS_CALLDATA:
13095 {
13096 code[code_index] = (VALUE)cd_entries++;
13097 }
13098 break;
13099 case TS_ID:
13100 {
13101 VALUE op = ibf_load_small_value(load, &reading_pos);
13102 code[code_index] = ibf_load_id(load, (ID)(VALUE)op);
13103 }
13104 break;
13105 case TS_FUNCPTR:
13106 rb_raise(rb_eRuntimeError, "TS_FUNCPTR is not supported");
13107 break;
13108 case TS_BUILTIN:
13109 code[code_index] = (VALUE)ibf_load_builtin(load, &reading_pos);
13110 break;
13111 default:
13112 code[code_index] = ibf_load_small_value(load, &reading_pos);
13113 continue;
13114 }
13115 }
13116 if (insn_len(insn) != op_index+1) {
13117 rb_raise(rb_eRuntimeError, "operand size mismatch");
13118 }
13119 }
13120
13121 load_body->iseq_size = code_index;
13122
13123 if (ISEQ_MBITS_BUFLEN(load_body->iseq_size) == 1) {
13124 load_body->mark_bits.single = mark_offset_bits[0];
13125 }
13126 else {
13127 if (needs_bitmap) {
13128 load_body->mark_bits.list = mark_offset_bits;
13129 }
13130 else {
13131 load_body->mark_bits.list = 0;
13132 SIZED_FREE_N(mark_offset_bits, ISEQ_MBITS_BUFLEN(iseq_size));
13133 }
13134 }
13135
13136 RUBY_ASSERT(code_index == iseq_size);
13137 RUBY_ASSERT(reading_pos == bytecode_offset + bytecode_size);
13138 return code;
13139}
13140
13141static ibf_offset_t
13142ibf_dump_param_opt_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
13143{
13144 int opt_num = ISEQ_BODY(iseq)->param.opt_num;
13145
13146 if (opt_num > 0) {
13147 IBF_W_ALIGN(VALUE);
13148 return ibf_dump_write(dump, ISEQ_BODY(iseq)->param.opt_table, sizeof(VALUE) * (opt_num + 1));
13149 }
13150 else {
13151 return ibf_dump_pos(dump);
13152 }
13153}
13154
13155static VALUE *
13156ibf_load_param_opt_table(const struct ibf_load *load, ibf_offset_t opt_table_offset, int opt_num)
13157{
13158 if (opt_num > 0) {
13159 VALUE *table = ALLOC_N(VALUE, opt_num+1);
13160 MEMCPY(table, load->current_buffer->buff + opt_table_offset, VALUE, opt_num+1);
13161 return table;
13162 }
13163 else {
13164 return NULL;
13165 }
13166}
13167
13168static ibf_offset_t
13169ibf_dump_param_keyword(struct ibf_dump *dump, const rb_iseq_t *iseq)
13170{
13171 const struct rb_iseq_param_keyword *kw = ISEQ_BODY(iseq)->param.keyword;
13172
13173 if (kw) {
13174 struct rb_iseq_param_keyword dump_kw = *kw;
13175 int dv_num = kw->num - kw->required_num;
13176 ID *ids = kw->num > 0 ? ALLOCA_N(ID, kw->num) : NULL;
13177 VALUE *dvs = dv_num > 0 ? ALLOCA_N(VALUE, dv_num) : NULL;
13178 int i;
13179
13180 for (i=0; i<kw->num; i++) ids[i] = (ID)ibf_dump_id(dump, kw->table[i]);
13181 for (i=0; i<dv_num; i++) dvs[i] = (VALUE)ibf_dump_object(dump, kw->default_values[i]);
13182
13183 dump_kw.table = IBF_W(ids, ID, kw->num);
13184 dump_kw.default_values = IBF_W(dvs, VALUE, dv_num);
13185 IBF_W_ALIGN(struct rb_iseq_param_keyword);
13186 return ibf_dump_write(dump, &dump_kw, sizeof(struct rb_iseq_param_keyword) * 1);
13187 }
13188 else {
13189 return 0;
13190 }
13191}
13192
13193static const struct rb_iseq_param_keyword *
13194ibf_load_param_keyword(const struct ibf_load *load, ibf_offset_t param_keyword_offset)
13195{
13196 if (param_keyword_offset) {
13197 struct rb_iseq_param_keyword *kw = IBF_R(param_keyword_offset, struct rb_iseq_param_keyword, 1);
13198 int dv_num = kw->num - kw->required_num;
13199 VALUE *dvs = dv_num ? IBF_R(kw->default_values, VALUE, dv_num) : NULL;
13200
13201 int i;
13202 for (i=0; i<dv_num; i++) {
13203 dvs[i] = ibf_load_object(load, dvs[i]);
13204 }
13205
13206 // Will be set once the local table is loaded.
13207 kw->table = NULL;
13208
13209 kw->default_values = dvs;
13210 return kw;
13211 }
13212 else {
13213 return NULL;
13214 }
13215}
13216
13217static ibf_offset_t
13218ibf_dump_insns_info_body(struct ibf_dump *dump, const rb_iseq_t *iseq)
13219{
13220 ibf_offset_t offset = ibf_dump_pos(dump);
13221 const struct iseq_insn_info_entry *entries = ISEQ_BODY(iseq)->insns_info.body;
13222
13223 unsigned int i;
13224 for (i = 0; i < ISEQ_BODY(iseq)->insns_info.size; i++) {
13225 ibf_dump_write_small_value(dump, entries[i].line_no);
13226#ifdef USE_ISEQ_NODE_ID
13227 ibf_dump_write_small_value(dump, entries[i].node_id);
13228#endif
13229 ibf_dump_write_small_value(dump, entries[i].events);
13230 }
13231
13232 return offset;
13233}
13234
13235static struct iseq_insn_info_entry *
13236ibf_load_insns_info_body(const struct ibf_load *load, ibf_offset_t body_offset, unsigned int size)
13237{
13238 ibf_offset_t reading_pos = body_offset;
13239 struct iseq_insn_info_entry *entries = ALLOC_N(struct iseq_insn_info_entry, size);
13240
13241 unsigned int i;
13242 for (i = 0; i < size; i++) {
13243 entries[i].line_no = (int)ibf_load_small_value(load, &reading_pos);
13244#ifdef USE_ISEQ_NODE_ID
13245 entries[i].node_id = (int)ibf_load_small_value(load, &reading_pos);
13246#endif
13247 entries[i].events = (rb_event_flag_t)ibf_load_small_value(load, &reading_pos);
13248 }
13249
13250 return entries;
13251}
13252
13253static ibf_offset_t
13254ibf_dump_insns_info_positions(struct ibf_dump *dump, const unsigned int *positions, unsigned int size)
13255{
13256 ibf_offset_t offset = ibf_dump_pos(dump);
13257
13258 unsigned int last = 0;
13259 unsigned int i;
13260 for (i = 0; i < size; i++) {
13261 ibf_dump_write_small_value(dump, positions[i] - last);
13262 last = positions[i];
13263 }
13264
13265 return offset;
13266}
13267
13268static unsigned int *
13269ibf_load_insns_info_positions(const struct ibf_load *load, ibf_offset_t positions_offset, unsigned int size)
13270{
13271 ibf_offset_t reading_pos = positions_offset;
13272 unsigned int *positions = ALLOC_N(unsigned int, size);
13273
13274 unsigned int last = 0;
13275 unsigned int i;
13276 for (i = 0; i < size; i++) {
13277 positions[i] = last + (unsigned int)ibf_load_small_value(load, &reading_pos);
13278 last = positions[i];
13279 }
13280
13281 return positions;
13282}
13283
13284static ibf_offset_t
13285ibf_dump_local_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
13286{
13287 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
13288 const int size = body->local_table_size;
13289 ID *table = ALLOCA_N(ID, size);
13290 int i;
13291
13292 for (i=0; i<size; i++) {
13293 VALUE v = ibf_dump_id(dump, body->local_table[i]);
13294 if (v == 0) {
13295 /* Dump hidden local variables as indexes, so load_from_binary will work with them */
13296 v = ibf_dump_object(dump, ULONG2NUM(body->local_table[i]));
13297 }
13298 table[i] = v;
13299 }
13300
13301 IBF_W_ALIGN(ID);
13302 return ibf_dump_write(dump, table, sizeof(ID) * size);
13303}
13304
13305static const ID *
13306ibf_load_local_table(const struct ibf_load *load, ibf_offset_t local_table_offset, int size)
13307{
13308 if (size > 0) {
13309 ID *table = IBF_R(local_table_offset, ID, size);
13310 int i;
13311
13312 for (i=0; i<size; i++) {
13313 table[i] = ibf_load_id(load, table[i]);
13314 }
13315
13316 if (size == 1 && table[0] == idERROR_INFO) {
13317 ruby_sized_xfree(table, sizeof(ID) * size);
13318 return rb_iseq_shared_exc_local_tbl;
13319 }
13320 else {
13321 return table;
13322 }
13323 }
13324 else {
13325 return NULL;
13326 }
13327}
13328
13329static ibf_offset_t
13330ibf_dump_lvar_states(struct ibf_dump *dump, const rb_iseq_t *iseq)
13331{
13332 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
13333 const int size = body->local_table_size;
13334 IBF_W_ALIGN(enum lvar_state);
13335 return ibf_dump_write(dump, body->lvar_states, sizeof(enum lvar_state) * (body->lvar_states ? size : 0));
13336}
13337
13338static enum lvar_state *
13339ibf_load_lvar_states(const struct ibf_load *load, ibf_offset_t lvar_states_offset, int size, const ID *local_table)
13340{
13341 if (local_table == rb_iseq_shared_exc_local_tbl ||
13342 size <= 0) {
13343 return NULL;
13344 }
13345 else {
13346 enum lvar_state *states = IBF_R(lvar_states_offset, enum lvar_state, size);
13347 return states;
13348 }
13349}
13350
13351static ibf_offset_t
13352ibf_dump_catch_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
13353{
13354 const struct iseq_catch_table *table = ISEQ_BODY(iseq)->catch_table;
13355
13356 if (table) {
13357 int *iseq_indices = ALLOCA_N(int, table->size);
13358 unsigned int i;
13359
13360 for (i=0; i<table->size; i++) {
13361 iseq_indices[i] = ibf_dump_iseq(dump, table->entries[i].iseq);
13362 }
13363
13364 const ibf_offset_t offset = ibf_dump_pos(dump);
13365
13366 for (i=0; i<table->size; i++) {
13367 ibf_dump_write_small_value(dump, iseq_indices[i]);
13368 ibf_dump_write_small_value(dump, table->entries[i].type);
13369 ibf_dump_write_small_value(dump, table->entries[i].start);
13370 ibf_dump_write_small_value(dump, table->entries[i].end);
13371 ibf_dump_write_small_value(dump, table->entries[i].cont);
13372 ibf_dump_write_small_value(dump, table->entries[i].sp);
13373 }
13374 return offset;
13375 }
13376 else {
13377 return ibf_dump_pos(dump);
13378 }
13379}
13380
13381static void
13382ibf_load_catch_table(const struct ibf_load *load, ibf_offset_t catch_table_offset, unsigned int size, const rb_iseq_t *parent_iseq)
13383{
13384 if (size) {
13385 struct iseq_catch_table *table = ruby_xcalloc(1, iseq_catch_table_bytes(size));
13386 table->size = size;
13387 ISEQ_BODY(parent_iseq)->catch_table = table;
13388
13389 ibf_offset_t reading_pos = catch_table_offset;
13390
13391 unsigned int i;
13392 for (i=0; i<table->size; i++) {
13393 int iseq_index = (int)ibf_load_small_value(load, &reading_pos);
13394 table->entries[i].type = (enum rb_catch_type)ibf_load_small_value(load, &reading_pos);
13395 table->entries[i].start = (unsigned int)ibf_load_small_value(load, &reading_pos);
13396 table->entries[i].end = (unsigned int)ibf_load_small_value(load, &reading_pos);
13397 table->entries[i].cont = (unsigned int)ibf_load_small_value(load, &reading_pos);
13398 table->entries[i].sp = (unsigned int)ibf_load_small_value(load, &reading_pos);
13399
13400 rb_iseq_t *catch_iseq = (rb_iseq_t *)ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)iseq_index);
13401 RB_OBJ_WRITE(parent_iseq, UNALIGNED_MEMBER_PTR(&table->entries[i], iseq), catch_iseq);
13402 }
13403 }
13404 else {
13405 ISEQ_BODY(parent_iseq)->catch_table = NULL;
13406 }
13407}
13408
13409static ibf_offset_t
13410ibf_dump_ci_entries(struct ibf_dump *dump, const rb_iseq_t *iseq)
13411{
13412 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
13413 const unsigned int ci_size = body->ci_size;
13414 const struct rb_call_data *cds = body->call_data;
13415
13416 ibf_offset_t offset = ibf_dump_pos(dump);
13417
13418 unsigned int i;
13419
13420 for (i = 0; i < ci_size; i++) {
13421 const struct rb_callinfo *ci = cds[i].ci;
13422 if (ci != NULL) {
13423 ibf_dump_write_small_value(dump, ibf_dump_id(dump, vm_ci_mid(ci)));
13424 ibf_dump_write_small_value(dump, vm_ci_flag(ci));
13425 ibf_dump_write_small_value(dump, vm_ci_argc(ci));
13426
13427 const struct rb_callinfo_kwarg *kwarg = vm_ci_kwarg(ci);
13428 if (kwarg) {
13429 int len = kwarg->keyword_len;
13430 ibf_dump_write_small_value(dump, len);
13431 for (int j=0; j<len; j++) {
13432 VALUE keyword = ibf_dump_object(dump, kwarg->keywords[j]);
13433 ibf_dump_write_small_value(dump, keyword);
13434 }
13435 }
13436 else {
13437 ibf_dump_write_small_value(dump, 0);
13438 }
13439 }
13440 else {
13441 // TODO: truncate NULL ci from call_data.
13442 ibf_dump_write_small_value(dump, (VALUE)-1);
13443 }
13444 }
13445
13446 return offset;
13447}
13448
13450 ID id;
13451 VALUE name;
13452 VALUE val;
13453};
13454
13456 size_t num;
13457 struct outer_variable_pair pairs[1];
13458};
13459
13460static enum rb_id_table_iterator_result
13461store_outer_variable(ID id, VALUE val, void *dump)
13462{
13463 struct outer_variable_list *ovlist = dump;
13464 struct outer_variable_pair *pair = &ovlist->pairs[ovlist->num++];
13465 pair->id = id;
13466 pair->name = rb_id2str(id);
13467 pair->val = val;
13468 return ID_TABLE_CONTINUE;
13469}
13470
13471static int
13472outer_variable_cmp(const void *a, const void *b, void *arg)
13473{
13474 const struct outer_variable_pair *ap = (const struct outer_variable_pair *)a;
13475 const struct outer_variable_pair *bp = (const struct outer_variable_pair *)b;
13476
13477 if (!ap->name) {
13478 return -1;
13479 }
13480 else if (!bp->name) {
13481 return 1;
13482 }
13483
13484 return rb_str_cmp(ap->name, bp->name);
13485}
13486
13487static ibf_offset_t
13488ibf_dump_outer_variables(struct ibf_dump *dump, const rb_iseq_t *iseq)
13489{
13490 struct rb_id_table * ovs = ISEQ_BODY(iseq)->outer_variables;
13491
13492 ibf_offset_t offset = ibf_dump_pos(dump);
13493
13494 size_t size = ovs ? rb_id_table_size(ovs) : 0;
13495 ibf_dump_write_small_value(dump, (VALUE)size);
13496 if (size > 0) {
13497 VALUE buff;
13498 size_t buffsize =
13499 rb_size_mul_add_or_raise(sizeof(struct outer_variable_pair), size,
13500 offsetof(struct outer_variable_list, pairs),
13501 rb_eArgError);
13502 struct outer_variable_list *ovlist = RB_ALLOCV(buff, buffsize);
13503 ovlist->num = 0;
13504 rb_id_table_foreach(ovs, store_outer_variable, ovlist);
13505 ruby_qsort(ovlist->pairs, size, sizeof(struct outer_variable_pair), outer_variable_cmp, NULL);
13506 for (size_t i = 0; i < size; ++i) {
13507 ID id = ovlist->pairs[i].id;
13508 ID val = ovlist->pairs[i].val;
13509 ibf_dump_write_small_value(dump, ibf_dump_id(dump, id));
13510 ibf_dump_write_small_value(dump, val);
13511 }
13512 }
13513
13514 return offset;
13515}
13516
13517/* note that we dump out rb_call_info but load back rb_call_data */
13518static void
13519ibf_load_ci_entries(const struct ibf_load *load,
13520 ibf_offset_t ci_entries_offset,
13521 unsigned int ci_size,
13522 struct rb_call_data **cd_ptr)
13523{
13524 if (!ci_size) {
13525 *cd_ptr = NULL;
13526 return;
13527 }
13528
13529 ibf_offset_t reading_pos = ci_entries_offset;
13530
13531 unsigned int i;
13532
13533 struct rb_call_data *cds = ZALLOC_N(struct rb_call_data, ci_size);
13534 *cd_ptr = cds;
13535
13536 for (i = 0; i < ci_size; i++) {
13537 VALUE mid_index = ibf_load_small_value(load, &reading_pos);
13538 if (mid_index != (VALUE)-1) {
13539 ID mid = ibf_load_id(load, mid_index);
13540 unsigned int flag = (unsigned int)ibf_load_small_value(load, &reading_pos);
13541 unsigned int argc = (unsigned int)ibf_load_small_value(load, &reading_pos);
13542
13543 struct rb_callinfo_kwarg *kwarg = NULL;
13544 int kwlen = (int)ibf_load_small_value(load, &reading_pos);
13545 if (kwlen > 0) {
13546 kwarg = rb_xmalloc_mul_add(kwlen, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
13547 kwarg->references = 0;
13548 kwarg->keyword_len = kwlen;
13549 for (int j=0; j<kwlen; j++) {
13550 VALUE keyword = ibf_load_small_value(load, &reading_pos);
13551 kwarg->keywords[j] = ibf_load_object(load, keyword);
13552 }
13553 }
13554
13555 cds[i].ci = vm_ci_new(mid, flag, argc, kwarg);
13556 RB_OBJ_WRITTEN(load->iseq, Qundef, cds[i].ci);
13557 cds[i].cc = vm_cc_empty();
13558 }
13559 else {
13560 // NULL ci
13561 cds[i].ci = NULL;
13562 cds[i].cc = NULL;
13563 }
13564 }
13565}
13566
13567static struct rb_id_table *
13568ibf_load_outer_variables(const struct ibf_load * load, ibf_offset_t outer_variables_offset)
13569{
13570 ibf_offset_t reading_pos = outer_variables_offset;
13571
13572 struct rb_id_table *tbl = NULL;
13573
13574 size_t table_size = (size_t)ibf_load_small_value(load, &reading_pos);
13575
13576 if (table_size > 0) {
13577 tbl = rb_id_table_create(table_size);
13578 }
13579
13580 for (size_t i = 0; i < table_size; i++) {
13581 ID key = ibf_load_id(load, (ID)ibf_load_small_value(load, &reading_pos));
13582 VALUE value = ibf_load_small_value(load, &reading_pos);
13583 if (!key) key = rb_make_temporary_id(i);
13584 rb_id_table_insert(tbl, key, value);
13585 }
13586
13587 return tbl;
13588}
13589
13590static ibf_offset_t
13591ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
13592{
13593 RUBY_ASSERT(dump->current_buffer == &dump->global_buffer);
13594
13595 unsigned int *positions;
13596
13597 const struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
13598
13599 const VALUE location_pathobj_index = ibf_dump_object(dump, body->location.pathobj); /* TODO: freeze */
13600 const VALUE location_base_label_index = ibf_dump_object(dump, body->location.base_label);
13601 const VALUE location_label_index = ibf_dump_object(dump, body->location.label);
13602
13603#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13604 ibf_offset_t iseq_start = ibf_dump_pos(dump);
13605
13606 struct ibf_dump_buffer *saved_buffer = dump->current_buffer;
13607 struct ibf_dump_buffer buffer;
13608 buffer.str = rb_str_new(0, 0);
13609 buffer.obj_table = ibf_dump_object_table_new();
13610 dump->current_buffer = &buffer;
13611#endif
13612
13613 const ibf_offset_t bytecode_offset = ibf_dump_code(dump, iseq);
13614 const ibf_offset_t bytecode_size = ibf_dump_pos(dump) - bytecode_offset;
13615 const ibf_offset_t param_opt_table_offset = ibf_dump_param_opt_table(dump, iseq);
13616 const ibf_offset_t param_keyword_offset = ibf_dump_param_keyword(dump, iseq);
13617 const ibf_offset_t insns_info_body_offset = ibf_dump_insns_info_body(dump, iseq);
13618
13619 positions = rb_iseq_insns_info_decode_positions(ISEQ_BODY(iseq));
13620 const ibf_offset_t insns_info_positions_offset = ibf_dump_insns_info_positions(dump, positions, body->insns_info.size);
13621 SIZED_FREE_N(positions, ISEQ_BODY(iseq)->insns_info.size);
13622
13623 const ibf_offset_t local_table_offset = ibf_dump_local_table(dump, iseq);
13624 const ibf_offset_t lvar_states_offset = ibf_dump_lvar_states(dump, iseq);
13625 const unsigned int catch_table_size = body->catch_table ? body->catch_table->size : 0;
13626 const ibf_offset_t catch_table_offset = ibf_dump_catch_table(dump, iseq);
13627 const int parent_iseq_index = ibf_dump_iseq(dump, ISEQ_BODY(iseq)->parent_iseq);
13628 const int local_iseq_index = ibf_dump_iseq(dump, ISEQ_BODY(iseq)->local_iseq);
13629 const int mandatory_only_iseq_index = ibf_dump_iseq(dump, ISEQ_BODY(iseq)->mandatory_only_iseq);
13630 const ibf_offset_t ci_entries_offset = ibf_dump_ci_entries(dump, iseq);
13631 const ibf_offset_t outer_variables_offset = ibf_dump_outer_variables(dump, iseq);
13632
13633#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13634 ibf_offset_t local_obj_list_offset;
13635 unsigned int local_obj_list_size;
13636
13637 ibf_dump_object_list(dump, &local_obj_list_offset, &local_obj_list_size);
13638#endif
13639
13640 ibf_offset_t body_offset = ibf_dump_pos(dump);
13641
13642 /* dump the constant body */
13643 unsigned int param_flags =
13644 (body->param.flags.has_lead << 0) |
13645 (body->param.flags.has_opt << 1) |
13646 (body->param.flags.has_rest << 2) |
13647 (body->param.flags.has_post << 3) |
13648 (body->param.flags.has_kw << 4) |
13649 (body->param.flags.has_kwrest << 5) |
13650 (body->param.flags.has_block << 6) |
13651 (body->param.flags.ambiguous_param0 << 7) |
13652 (body->param.flags.accepts_no_kwarg << 8) |
13653 (body->param.flags.ruby2_keywords << 9) |
13654 (body->param.flags.anon_rest << 10) |
13655 (body->param.flags.anon_kwrest << 11) |
13656 (body->param.flags.use_block << 12) |
13657 (body->param.flags.forwardable << 13) |
13658 (body->param.flags.accepts_no_block << 14);
13659
13660#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13661# define IBF_BODY_OFFSET(x) (x)
13662#else
13663# define IBF_BODY_OFFSET(x) (body_offset - (x))
13664#endif
13665
13666 ibf_dump_write_small_value(dump, body->type);
13667 ibf_dump_write_small_value(dump, body->iseq_size);
13668 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(bytecode_offset));
13669 ibf_dump_write_small_value(dump, bytecode_size);
13670 ibf_dump_write_small_value(dump, param_flags);
13671 ibf_dump_write_small_value(dump, body->param.size);
13672 ibf_dump_write_small_value(dump, body->param.lead_num);
13673 ibf_dump_write_small_value(dump, body->param.opt_num);
13674 ibf_dump_write_small_value(dump, body->param.rest_start);
13675 ibf_dump_write_small_value(dump, body->param.post_start);
13676 ibf_dump_write_small_value(dump, body->param.post_num);
13677 ibf_dump_write_small_value(dump, body->param.block_start);
13678 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(param_opt_table_offset));
13679 ibf_dump_write_small_value(dump, param_keyword_offset);
13680 ibf_dump_write_small_value(dump, location_pathobj_index);
13681 ibf_dump_write_small_value(dump, location_base_label_index);
13682 ibf_dump_write_small_value(dump, location_label_index);
13683 ibf_dump_write_small_value(dump, body->location.first_lineno);
13684 ibf_dump_write_small_value(dump, body->location.node_id);
13685 ibf_dump_write_small_value(dump, body->location.code_location.beg_pos.lineno);
13686 ibf_dump_write_small_value(dump, body->location.code_location.beg_pos.column);
13687 ibf_dump_write_small_value(dump, body->location.code_location.end_pos.lineno);
13688 ibf_dump_write_small_value(dump, body->location.code_location.end_pos.column);
13689 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(insns_info_body_offset));
13690 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(insns_info_positions_offset));
13691 ibf_dump_write_small_value(dump, body->insns_info.size);
13692 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(local_table_offset));
13693 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(lvar_states_offset));
13694 ibf_dump_write_small_value(dump, catch_table_size);
13695 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(catch_table_offset));
13696 ibf_dump_write_small_value(dump, parent_iseq_index);
13697 ibf_dump_write_small_value(dump, local_iseq_index);
13698 ibf_dump_write_small_value(dump, mandatory_only_iseq_index);
13699 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(ci_entries_offset));
13700 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(outer_variables_offset));
13701 ibf_dump_write_small_value(dump, body->variable.flip_count);
13702 ibf_dump_write_small_value(dump, body->local_table_size);
13703 ibf_dump_write_small_value(dump, body->ivc_size);
13704 ibf_dump_write_small_value(dump, body->icvarc_size);
13705 ibf_dump_write_small_value(dump, body->ise_size);
13706 ibf_dump_write_small_value(dump, body->ic_size);
13707 ibf_dump_write_small_value(dump, body->ci_size);
13708 ibf_dump_write_small_value(dump, body->stack_max);
13709 ibf_dump_write_small_value(dump, body->builtin_attrs);
13710 ibf_dump_write_small_value(dump, body->prism ? 1 : 0);
13711
13712#undef IBF_BODY_OFFSET
13713
13714#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13715 ibf_offset_t iseq_length_bytes = ibf_dump_pos(dump);
13716
13717 dump->current_buffer = saved_buffer;
13718 ibf_dump_write(dump, RSTRING_PTR(buffer.str), iseq_length_bytes);
13719
13720 ibf_offset_t offset = ibf_dump_pos(dump);
13721 ibf_dump_write_small_value(dump, iseq_start);
13722 ibf_dump_write_small_value(dump, iseq_length_bytes);
13723 ibf_dump_write_small_value(dump, body_offset);
13724
13725 ibf_dump_write_small_value(dump, local_obj_list_offset);
13726 ibf_dump_write_small_value(dump, local_obj_list_size);
13727
13728 st_free_table(buffer.obj_table); // TODO: this leaks in case of exception
13729
13730 return offset;
13731#else
13732 return body_offset;
13733#endif
13734}
13735
13736static VALUE
13737ibf_load_location_str(const struct ibf_load *load, VALUE str_index)
13738{
13739 VALUE str = ibf_load_object(load, str_index);
13740 if (str != Qnil) {
13741 str = rb_fstring(str);
13742 }
13743 return str;
13744}
13745
13746static void
13747ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
13748{
13749 struct rb_iseq_constant_body *load_body = ISEQ_BODY(iseq) = rb_iseq_constant_body_alloc();
13750
13751 ibf_offset_t reading_pos = offset;
13752
13753#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13754 struct ibf_load_buffer *saved_buffer = load->current_buffer;
13755 load->current_buffer = &load->global_buffer;
13756
13757 const ibf_offset_t iseq_start = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13758 const ibf_offset_t iseq_length_bytes = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13759 const ibf_offset_t body_offset = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13760
13761 struct ibf_load_buffer buffer;
13762 buffer.buff = load->global_buffer.buff + iseq_start;
13763 buffer.size = iseq_length_bytes;
13764 buffer.obj_list_offset = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13765 buffer.obj_list_size = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13766 buffer.obj_list = pinned_list_new(buffer.obj_list_size);
13767
13768 load->current_buffer = &buffer;
13769 reading_pos = body_offset;
13770#endif
13771
13772#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13773# define IBF_BODY_OFFSET(x) (x)
13774#else
13775# define IBF_BODY_OFFSET(x) (offset - (x))
13776#endif
13777
13778 const unsigned int type = (unsigned int)ibf_load_small_value(load, &reading_pos);
13779 const unsigned int iseq_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13780 const ibf_offset_t bytecode_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13781 const ibf_offset_t bytecode_size = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13782 const unsigned int param_flags = (unsigned int)ibf_load_small_value(load, &reading_pos);
13783 const unsigned int param_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13784 const int param_lead_num = (int)ibf_load_small_value(load, &reading_pos);
13785 const int param_opt_num = (int)ibf_load_small_value(load, &reading_pos);
13786 const int param_rest_start = (int)ibf_load_small_value(load, &reading_pos);
13787 const int param_post_start = (int)ibf_load_small_value(load, &reading_pos);
13788 const int param_post_num = (int)ibf_load_small_value(load, &reading_pos);
13789 const int param_block_start = (int)ibf_load_small_value(load, &reading_pos);
13790 const ibf_offset_t param_opt_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13791 const ibf_offset_t param_keyword_offset = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13792 const VALUE location_pathobj_index = ibf_load_small_value(load, &reading_pos);
13793 const VALUE location_base_label_index = ibf_load_small_value(load, &reading_pos);
13794 const VALUE location_label_index = ibf_load_small_value(load, &reading_pos);
13795 const int location_first_lineno = (int)ibf_load_small_value(load, &reading_pos);
13796 const int location_node_id = (int)ibf_load_small_value(load, &reading_pos);
13797 const int location_code_location_beg_pos_lineno = (int)ibf_load_small_value(load, &reading_pos);
13798 const int location_code_location_beg_pos_column = (int)ibf_load_small_value(load, &reading_pos);
13799 const int location_code_location_end_pos_lineno = (int)ibf_load_small_value(load, &reading_pos);
13800 const int location_code_location_end_pos_column = (int)ibf_load_small_value(load, &reading_pos);
13801 const ibf_offset_t insns_info_body_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13802 const ibf_offset_t insns_info_positions_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13803 const unsigned int insns_info_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13804 const ibf_offset_t local_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13805 const ibf_offset_t lvar_states_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13806 const unsigned int catch_table_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13807 const ibf_offset_t catch_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13808 const int parent_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
13809 const int local_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
13810 const int mandatory_only_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
13811 const ibf_offset_t ci_entries_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13812 const ibf_offset_t outer_variables_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13813 const rb_snum_t variable_flip_count = (rb_snum_t)ibf_load_small_value(load, &reading_pos);
13814 const unsigned int local_table_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13815
13816 const unsigned int ivc_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13817 const unsigned int icvarc_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13818 const unsigned int ise_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13819 const unsigned int ic_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13820
13821 const unsigned int ci_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13822 const unsigned int stack_max = (unsigned int)ibf_load_small_value(load, &reading_pos);
13823 const unsigned int builtin_attrs = (unsigned int)ibf_load_small_value(load, &reading_pos);
13824 const bool prism = (bool)ibf_load_small_value(load, &reading_pos);
13825
13826 // setup fname and dummy frame
13827 VALUE path = ibf_load_object(load, location_pathobj_index);
13828 {
13829 VALUE realpath = Qnil;
13830
13831 if (RB_TYPE_P(path, T_STRING)) {
13832 realpath = path = rb_fstring(path);
13833 }
13834 else if (RB_TYPE_P(path, T_ARRAY)) {
13835 VALUE pathobj = path;
13836 if (RARRAY_LEN(pathobj) != 2) {
13837 rb_raise(rb_eRuntimeError, "path object size mismatch");
13838 }
13839 path = rb_fstring(RARRAY_AREF(pathobj, 0));
13840 realpath = RARRAY_AREF(pathobj, 1);
13841 if (!NIL_P(realpath)) {
13842 if (!RB_TYPE_P(realpath, T_STRING)) {
13843 rb_raise(rb_eArgError, "unexpected realpath %"PRIxVALUE
13844 "(%x), path=%+"PRIsVALUE,
13845 realpath, TYPE(realpath), path);
13846 }
13847 realpath = rb_fstring(realpath);
13848 }
13849 }
13850 else {
13851 rb_raise(rb_eRuntimeError, "unexpected path object");
13852 }
13853 rb_iseq_pathobj_set(iseq, path, realpath);
13854 }
13855
13856 // push dummy frame
13857 rb_execution_context_t *ec = GET_EC();
13858 VALUE dummy_frame = rb_vm_push_frame_fname(ec, path);
13859
13860#undef IBF_BODY_OFFSET
13861
13862 load_body->type = type;
13863 load_body->stack_max = stack_max;
13864 load_body->param.flags.has_lead = (param_flags >> 0) & 1;
13865 load_body->param.flags.has_opt = (param_flags >> 1) & 1;
13866 load_body->param.flags.has_rest = (param_flags >> 2) & 1;
13867 load_body->param.flags.has_post = (param_flags >> 3) & 1;
13868 load_body->param.flags.has_kw = FALSE;
13869 load_body->param.flags.has_kwrest = (param_flags >> 5) & 1;
13870 load_body->param.flags.has_block = (param_flags >> 6) & 1;
13871 load_body->param.flags.ambiguous_param0 = (param_flags >> 7) & 1;
13872 load_body->param.flags.accepts_no_kwarg = (param_flags >> 8) & 1;
13873 load_body->param.flags.ruby2_keywords = (param_flags >> 9) & 1;
13874 load_body->param.flags.anon_rest = (param_flags >> 10) & 1;
13875 load_body->param.flags.anon_kwrest = (param_flags >> 11) & 1;
13876 load_body->param.flags.use_block = (param_flags >> 12) & 1;
13877 load_body->param.flags.forwardable = (param_flags >> 13) & 1;
13878 load_body->param.flags.accepts_no_block = (param_flags >> 14) & 1;
13879 load_body->param.size = param_size;
13880 load_body->param.lead_num = param_lead_num;
13881 load_body->param.opt_num = param_opt_num;
13882 load_body->param.rest_start = param_rest_start;
13883 load_body->param.post_start = param_post_start;
13884 load_body->param.post_num = param_post_num;
13885 load_body->param.block_start = param_block_start;
13886 load_body->local_table_size = local_table_size;
13887 load_body->ci_size = ci_size;
13888 load_body->insns_info.size = insns_info_size;
13889
13890 ISEQ_COVERAGE_SET(iseq, Qnil);
13891 ISEQ_ORIGINAL_ISEQ_CLEAR(iseq);
13892 load_body->variable.flip_count = variable_flip_count;
13893 load_body->variable.script_lines = Qnil;
13894
13895 load_body->location.first_lineno = location_first_lineno;
13896 load_body->location.node_id = location_node_id;
13897 load_body->location.code_location.beg_pos.lineno = location_code_location_beg_pos_lineno;
13898 load_body->location.code_location.beg_pos.column = location_code_location_beg_pos_column;
13899 load_body->location.code_location.end_pos.lineno = location_code_location_end_pos_lineno;
13900 load_body->location.code_location.end_pos.column = location_code_location_end_pos_column;
13901 load_body->builtin_attrs = builtin_attrs;
13902 load_body->prism = prism;
13903
13904 load_body->ivc_size = ivc_size;
13905 load_body->icvarc_size = icvarc_size;
13906 load_body->ise_size = ise_size;
13907 load_body->ic_size = ic_size;
13908
13909 if (ISEQ_IS_SIZE(load_body)) {
13910 load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(load_body));
13911 }
13912 else {
13913 load_body->is_entries = NULL;
13914 }
13915 ibf_load_ci_entries(load, ci_entries_offset, ci_size, &load_body->call_data);
13916 load_body->outer_variables = ibf_load_outer_variables(load, outer_variables_offset);
13917 load_body->param.opt_table = ibf_load_param_opt_table(load, param_opt_table_offset, param_opt_num);
13918 load_body->param.keyword = ibf_load_param_keyword(load, param_keyword_offset);
13919 load_body->param.flags.has_kw = (param_flags >> 4) & 1;
13920 load_body->insns_info.body = ibf_load_insns_info_body(load, insns_info_body_offset, insns_info_size);
13921 load_body->insns_info.positions = ibf_load_insns_info_positions(load, insns_info_positions_offset, insns_info_size);
13922 load_body->local_table = ibf_load_local_table(load, local_table_offset, local_table_size);
13923 load_body->lvar_states = ibf_load_lvar_states(load, lvar_states_offset, local_table_size, load_body->local_table);
13924 ibf_load_catch_table(load, catch_table_offset, catch_table_size, iseq);
13925
13926 const rb_iseq_t *parent_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)parent_iseq_index);
13927 const rb_iseq_t *local_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)local_iseq_index);
13928 const rb_iseq_t *mandatory_only_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)mandatory_only_iseq_index);
13929
13930 RB_OBJ_WRITE(iseq, &load_body->parent_iseq, parent_iseq);
13931 RB_OBJ_WRITE(iseq, &load_body->local_iseq, local_iseq);
13932 RB_OBJ_WRITE(iseq, &load_body->mandatory_only_iseq, mandatory_only_iseq);
13933
13934 // This must be done after the local table is loaded.
13935 if (load_body->param.keyword != NULL) {
13936 RUBY_ASSERT(load_body->local_table);
13937 struct rb_iseq_param_keyword *keyword = (struct rb_iseq_param_keyword *) load_body->param.keyword;
13938 keyword->table = &load_body->local_table[keyword->bits_start - keyword->num];
13939 }
13940
13941 ibf_load_code(load, iseq, bytecode_offset, bytecode_size, iseq_size);
13942#if VM_INSN_INFO_TABLE_IMPL == 2
13943 rb_iseq_insns_info_encode_positions(iseq);
13944#endif
13945
13946 rb_iseq_translate_threaded_code(iseq);
13947
13948#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13949 load->current_buffer = &load->global_buffer;
13950#endif
13951
13952 RB_OBJ_WRITE(iseq, &load_body->location.base_label, ibf_load_location_str(load, location_base_label_index));
13953 RB_OBJ_WRITE(iseq, &load_body->location.label, ibf_load_location_str(load, location_label_index));
13954
13955#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13956 load->current_buffer = saved_buffer;
13957#endif
13958 verify_call_cache(iseq);
13959
13960 RB_GC_GUARD(dummy_frame);
13961 rb_vm_pop_frame_no_int(ec);
13962}
13963
13965{
13966 struct ibf_dump *dump;
13967 VALUE offset_list;
13968};
13969
13970static int
13971ibf_dump_iseq_list_i(st_data_t key, st_data_t val, st_data_t ptr)
13972{
13973 const rb_iseq_t *iseq = (const rb_iseq_t *)key;
13974 struct ibf_dump_iseq_list_arg *args = (struct ibf_dump_iseq_list_arg *)ptr;
13975
13976 ibf_offset_t offset = ibf_dump_iseq_each(args->dump, iseq);
13977 rb_ary_push(args->offset_list, UINT2NUM(offset));
13978
13979 return ST_CONTINUE;
13980}
13981
13982static void
13983ibf_dump_iseq_list(struct ibf_dump *dump, struct ibf_header *header)
13984{
13985 VALUE offset_list = rb_ary_hidden_new(dump->iseq_table->num_entries);
13986
13987 struct ibf_dump_iseq_list_arg args;
13988 args.dump = dump;
13989 args.offset_list = offset_list;
13990
13991 st_foreach(dump->iseq_table, ibf_dump_iseq_list_i, (st_data_t)&args);
13992
13993 st_index_t i;
13994 st_index_t size = dump->iseq_table->num_entries;
13995 ibf_offset_t *offsets = ALLOCA_N(ibf_offset_t, size);
13996
13997 for (i = 0; i < size; i++) {
13998 offsets[i] = NUM2UINT(RARRAY_AREF(offset_list, i));
13999 }
14000
14001 ibf_dump_align(dump, sizeof(ibf_offset_t));
14002 header->iseq_list_offset = ibf_dump_write(dump, offsets, sizeof(ibf_offset_t) * size);
14003 header->iseq_list_size = (unsigned int)size;
14004}
14005
14006/*
14007 * Binary format
14008 * - ibf_object_header
14009 * - ibf_object_xxx (xxx is type)
14010 */
14011
14013 unsigned int type: 5;
14014 unsigned int special_const: 1;
14015 unsigned int frozen: 1;
14016 unsigned int internal: 1;
14017};
14018
14019enum ibf_object_class_index {
14020 IBF_OBJECT_CLASS_OBJECT,
14021 IBF_OBJECT_CLASS_ARRAY,
14022 IBF_OBJECT_CLASS_STANDARD_ERROR,
14023 IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_ERROR,
14024 IBF_OBJECT_CLASS_TYPE_ERROR,
14025 IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_KEY_ERROR,
14026};
14027
14029 long srcstr;
14030 char option;
14031};
14032
14034 long len;
14035 long keyval[FLEX_ARY_LEN];
14036};
14037
14039 long class_index;
14040 long len;
14041 long beg;
14042 long end;
14043 int excl;
14044};
14045
14047 ssize_t slen;
14048 BDIGIT digits[FLEX_ARY_LEN];
14049};
14050
14051enum ibf_object_data_type {
14052 IBF_OBJECT_DATA_ENCODING,
14053};
14054
14056 long a, b;
14057};
14058
14060 long str;
14061};
14062
14063#define IBF_ALIGNED_OFFSET(align, offset) /* offset > 0 */ \
14064 ((((offset) - 1) / (align) + 1) * (align))
14065/* No cast, since it's UB to create an unaligned pointer.
14066 * Leave as void* for use with memcpy in those cases.
14067 * We align the offset, but the buffer pointer is only VALUE aligned,
14068 * so the returned pointer may be unaligned for `type` .*/
14069#define IBF_OBJBODY(type, offset) \
14070 ibf_load_check_offset(load, IBF_ALIGNED_OFFSET(RUBY_ALIGNOF(type), offset))
14071
14072static const void *
14073ibf_load_check_offset(const struct ibf_load *load, size_t offset)
14074{
14075 if (offset >= load->current_buffer->size) {
14076 rb_raise(rb_eIndexError, "object offset out of range: %"PRIdSIZE, offset);
14077 }
14078 return load->current_buffer->buff + offset;
14079}
14080
14081NORETURN(static void ibf_dump_object_unsupported(struct ibf_dump *dump, VALUE obj));
14082
14083static void
14084ibf_dump_object_unsupported(struct ibf_dump *dump, VALUE obj)
14085{
14086 char buff[0x100];
14087 rb_raw_obj_info(buff, sizeof(buff), obj);
14088 rb_raise(rb_eNotImpError, "ibf_dump_object_unsupported: %s", buff);
14089}
14090
14091NORETURN(static VALUE ibf_load_object_unsupported(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset));
14092
14093static VALUE
14094ibf_load_object_unsupported(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14095{
14096 rb_raise(rb_eArgError, "unsupported");
14098}
14099
14100static void
14101ibf_dump_object_class(struct ibf_dump *dump, VALUE obj)
14102{
14103 enum ibf_object_class_index cindex;
14104 if (obj == rb_cObject) {
14105 cindex = IBF_OBJECT_CLASS_OBJECT;
14106 }
14107 else if (obj == rb_cArray) {
14108 cindex = IBF_OBJECT_CLASS_ARRAY;
14109 }
14110 else if (obj == rb_eStandardError) {
14111 cindex = IBF_OBJECT_CLASS_STANDARD_ERROR;
14112 }
14113 else if (obj == rb_eNoMatchingPatternError) {
14114 cindex = IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_ERROR;
14115 }
14116 else if (obj == rb_eTypeError) {
14117 cindex = IBF_OBJECT_CLASS_TYPE_ERROR;
14118 }
14119 else if (obj == rb_eNoMatchingPatternKeyError) {
14120 cindex = IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_KEY_ERROR;
14121 }
14122 else {
14123 rb_obj_info_dump(obj);
14124 rb_p(obj);
14125 rb_bug("unsupported class");
14126 }
14127 ibf_dump_write_small_value(dump, (VALUE)cindex);
14128}
14129
14130static VALUE
14131ibf_load_object_class(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14132{
14133 enum ibf_object_class_index cindex = (enum ibf_object_class_index)ibf_load_small_value(load, &offset);
14134
14135 switch (cindex) {
14136 case IBF_OBJECT_CLASS_OBJECT:
14137 return rb_cObject;
14138 case IBF_OBJECT_CLASS_ARRAY:
14139 return rb_cArray;
14140 case IBF_OBJECT_CLASS_STANDARD_ERROR:
14141 return rb_eStandardError;
14142 case IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_ERROR:
14144 case IBF_OBJECT_CLASS_TYPE_ERROR:
14145 return rb_eTypeError;
14146 case IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_KEY_ERROR:
14148 }
14149
14150 rb_raise(rb_eArgError, "ibf_load_object_class: unknown class (%d)", (int)cindex);
14151}
14152
14153
14154static void
14155ibf_dump_object_float(struct ibf_dump *dump, VALUE obj)
14156{
14157 double dbl = RFLOAT_VALUE(obj);
14158 (void)IBF_W(&dbl, double, 1);
14159}
14160
14161static VALUE
14162ibf_load_object_float(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14163{
14164 double d;
14165 /* Avoid unaligned VFP load on ARMv7; IBF payload may be unaligned (C99 6.3.2.3 p7). */
14166 memcpy(&d, IBF_OBJBODY(double, offset), sizeof(d));
14167 VALUE f = DBL2NUM(d);
14168 if (!FLONUM_P(f)) RB_OBJ_SET_SHAREABLE(f);
14169 return f;
14170}
14171
14172static void
14173ibf_dump_object_string(struct ibf_dump *dump, VALUE obj)
14174{
14175 long encindex = (long)rb_enc_get_index(obj);
14176 long len = RSTRING_LEN(obj);
14177 const char *ptr = RSTRING_PTR(obj);
14178
14179 if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
14180 rb_encoding *enc = rb_enc_from_index((int)encindex);
14181 const char *enc_name = rb_enc_name(enc);
14182 encindex = RUBY_ENCINDEX_BUILTIN_MAX + ibf_dump_object(dump, rb_str_new2(enc_name));
14183 }
14184
14185 ibf_dump_write_small_value(dump, encindex);
14186 ibf_dump_write_small_value(dump, len);
14187 IBF_WP(ptr, char, len);
14188}
14189
14190static VALUE
14191ibf_load_object_string(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14192{
14193 ibf_offset_t reading_pos = offset;
14194
14195 int encindex = (int)ibf_load_small_value(load, &reading_pos);
14196 const long len = (long)ibf_load_small_value(load, &reading_pos);
14197 const char *ptr = load->current_buffer->buff + reading_pos;
14198
14199 if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
14200 VALUE enc_name_str = ibf_load_object(load, encindex - RUBY_ENCINDEX_BUILTIN_MAX);
14201 encindex = rb_enc_find_index(RSTRING_PTR(enc_name_str));
14202 }
14203
14204 VALUE str;
14205 if (header->frozen && !header->internal) {
14206 str = rb_enc_literal_str(ptr, len, rb_enc_from_index(encindex));
14207 }
14208 else {
14209 str = rb_enc_str_new(ptr, len, rb_enc_from_index(encindex));
14210
14211 if (header->internal) rb_obj_hide(str);
14212 if (header->frozen) str = rb_fstring(str);
14213 }
14214 return str;
14215}
14216
14217static void
14218ibf_dump_object_regexp(struct ibf_dump *dump, VALUE obj)
14219{
14220 VALUE srcstr = RREGEXP_SRC(obj);
14221 struct ibf_object_regexp regexp;
14222 regexp.option = (char)rb_reg_options(obj);
14223 regexp.srcstr = (long)ibf_dump_object(dump, srcstr);
14224
14225 ibf_dump_write_byte(dump, (unsigned char)regexp.option);
14226 ibf_dump_write_small_value(dump, regexp.srcstr);
14227}
14228
14229static VALUE
14230ibf_load_object_regexp(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14231{
14232 struct ibf_object_regexp regexp;
14233 regexp.option = ibf_load_byte(load, &offset);
14234 regexp.srcstr = ibf_load_small_value(load, &offset);
14235
14236 VALUE srcstr = ibf_load_object(load, regexp.srcstr);
14237 VALUE reg = rb_reg_compile(srcstr, (int)regexp.option, NULL, 0);
14238
14239 if (header->internal) rb_obj_hide(reg);
14240 if (header->frozen) RB_OBJ_SET_SHAREABLE(rb_obj_freeze(reg));
14241
14242 return reg;
14243}
14244
14245static void
14246ibf_dump_object_array(struct ibf_dump *dump, VALUE obj)
14247{
14248 long i, len = RARRAY_LEN(obj);
14249 ibf_dump_write_small_value(dump, len);
14250 for (i=0; i<len; i++) {
14251 long index = (long)ibf_dump_object(dump, RARRAY_AREF(obj, i));
14252 ibf_dump_write_small_value(dump, index);
14253 }
14254}
14255
14256static VALUE
14257ibf_load_object_array(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14258{
14259 ibf_offset_t reading_pos = offset;
14260
14261 const long len = (long)ibf_load_small_value(load, &reading_pos);
14262
14263 VALUE ary = header->internal ? rb_ary_hidden_new(len) : rb_ary_new_capa(len);
14264 int i;
14265
14266 for (i=0; i<len; i++) {
14267 const VALUE index = ibf_load_small_value(load, &reading_pos);
14268 rb_ary_push(ary, ibf_load_object(load, index));
14269 }
14270
14271 if (header->frozen) {
14272 rb_ary_freeze(ary);
14273 rb_ractor_make_shareable(ary); // TODO: check elements
14274 }
14275
14276 return ary;
14277}
14278
14279static int
14280ibf_dump_object_hash_i(st_data_t key, st_data_t val, st_data_t ptr)
14281{
14282 struct ibf_dump *dump = (struct ibf_dump *)ptr;
14283
14284 VALUE key_index = ibf_dump_object(dump, (VALUE)key);
14285 VALUE val_index = ibf_dump_object(dump, (VALUE)val);
14286
14287 ibf_dump_write_small_value(dump, key_index);
14288 ibf_dump_write_small_value(dump, val_index);
14289 return ST_CONTINUE;
14290}
14291
14292static void
14293ibf_dump_object_hash(struct ibf_dump *dump, VALUE obj)
14294{
14295 long len = RHASH_SIZE(obj);
14296 ibf_dump_write_small_value(dump, (VALUE)len);
14297
14298 if (len > 0) rb_hash_foreach(obj, ibf_dump_object_hash_i, (VALUE)dump);
14299}
14300
14301static VALUE
14302ibf_load_object_hash(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14303{
14304 long len = (long)ibf_load_small_value(load, &offset);
14305 VALUE obj = rb_hash_new_with_size(len);
14306 int i;
14307
14308 for (i = 0; i < len; i++) {
14309 VALUE key_index = ibf_load_small_value(load, &offset);
14310 VALUE val_index = ibf_load_small_value(load, &offset);
14311
14312 VALUE key = ibf_load_object(load, key_index);
14313 VALUE val = ibf_load_object(load, val_index);
14314 rb_hash_aset(obj, key, val);
14315 }
14316 rb_hash_rehash(obj);
14317
14318 if (header->internal) rb_obj_hide(obj);
14319 if (header->frozen) {
14320 RB_OBJ_SET_FROZEN_SHAREABLE(obj);
14321 }
14322
14323 return obj;
14324}
14325
14326static void
14327ibf_dump_object_struct(struct ibf_dump *dump, VALUE obj)
14328{
14329 if (rb_obj_is_kind_of(obj, rb_cRange)) {
14330 struct ibf_object_struct_range range;
14331 VALUE beg, end;
14332 IBF_ZERO(range);
14333 range.len = 3;
14334 range.class_index = 0;
14335
14336 rb_range_values(obj, &beg, &end, &range.excl);
14337 range.beg = (long)ibf_dump_object(dump, beg);
14338 range.end = (long)ibf_dump_object(dump, end);
14339
14340 IBF_W_ALIGN(struct ibf_object_struct_range);
14341 IBF_WV(range);
14342 }
14343 else {
14344 rb_raise(rb_eNotImpError, "ibf_dump_object_struct: unsupported class %"PRIsVALUE,
14345 rb_class_name(CLASS_OF(obj)));
14346 }
14347}
14348
14349static VALUE
14350ibf_load_object_struct(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14351{
14352 const struct ibf_object_struct_range *range = IBF_OBJBODY(struct ibf_object_struct_range, offset);
14353 VALUE beg = ibf_load_object(load, range->beg);
14354 VALUE end = ibf_load_object(load, range->end);
14355 VALUE obj = rb_range_new(beg, end, range->excl);
14356 if (header->internal) rb_obj_hide(obj);
14357 if (header->frozen) RB_OBJ_SET_FROZEN_SHAREABLE(obj);
14358 return obj;
14359}
14360
14361static void
14362ibf_dump_object_bignum(struct ibf_dump *dump, VALUE obj)
14363{
14364 ssize_t len = BIGNUM_LEN(obj);
14365 ssize_t slen = BIGNUM_SIGN(obj) > 0 ? len : len * -1;
14366 BDIGIT *d = BIGNUM_DIGITS(obj);
14367
14368 (void)IBF_W(&slen, ssize_t, 1);
14369 IBF_WP(d, BDIGIT, len);
14370}
14371
14372static VALUE
14373ibf_load_object_bignum(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14374{
14375 const struct ibf_object_bignum *bignum = IBF_OBJBODY(struct ibf_object_bignum, offset);
14376 int sign = bignum->slen > 0;
14377 ssize_t len = sign > 0 ? bignum->slen : -1 * bignum->slen;
14378 const int big_unpack_flags = /* c.f. rb_big_unpack() */
14381 VALUE obj = rb_integer_unpack(bignum->digits, len, sizeof(BDIGIT), 0,
14382 big_unpack_flags |
14383 (sign == 0 ? INTEGER_PACK_NEGATIVE : 0));
14384 if (header->internal) rb_obj_hide(obj);
14385 if (header->frozen) RB_OBJ_SET_FROZEN_SHAREABLE(obj);
14386 return obj;
14387}
14388
14389static void
14390ibf_dump_object_data(struct ibf_dump *dump, VALUE obj)
14391{
14392 if (rb_data_is_encoding(obj)) {
14393 rb_encoding *enc = rb_to_encoding(obj);
14394 const char *name = rb_enc_name(enc);
14395 long len = strlen(name) + 1;
14396 long data[2];
14397 data[0] = IBF_OBJECT_DATA_ENCODING;
14398 data[1] = len;
14399 (void)IBF_W(data, long, 2);
14400 IBF_WP(name, char, len);
14401 }
14402 else {
14403 ibf_dump_object_unsupported(dump, obj);
14404 }
14405}
14406
14407static VALUE
14408ibf_load_object_data(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14409{
14410 const long *body = IBF_OBJBODY(long, offset);
14411 const enum ibf_object_data_type type = (enum ibf_object_data_type)body[0];
14412 /* const long len = body[1]; */
14413 const char *data = (const char *)&body[2];
14414
14415 switch (type) {
14416 case IBF_OBJECT_DATA_ENCODING:
14417 {
14418 VALUE encobj = rb_enc_from_encoding(rb_enc_find(data));
14419 return encobj;
14420 }
14421 }
14422
14423 return ibf_load_object_unsupported(load, header, offset);
14424}
14425
14426static void
14427ibf_dump_object_complex_rational(struct ibf_dump *dump, VALUE obj)
14428{
14429 long data[2];
14430 data[0] = (long)ibf_dump_object(dump, RCOMPLEX(obj)->real);
14431 data[1] = (long)ibf_dump_object(dump, RCOMPLEX(obj)->imag);
14432
14433 (void)IBF_W(data, long, 2);
14434}
14435
14436static VALUE
14437ibf_load_object_complex_rational(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14438{
14439 const struct ibf_object_complex_rational *nums = IBF_OBJBODY(struct ibf_object_complex_rational, offset);
14440 VALUE a = ibf_load_object(load, nums->a);
14441 VALUE b = ibf_load_object(load, nums->b);
14442 VALUE obj = header->type == T_COMPLEX ?
14443 rb_complex_new(a, b) : rb_rational_new(a, b);
14444
14445 if (header->internal) rb_obj_hide(obj);
14446 if (header->frozen) rb_ractor_make_shareable(rb_obj_freeze(obj));
14447 return obj;
14448}
14449
14450static void
14451ibf_dump_object_symbol(struct ibf_dump *dump, VALUE obj)
14452{
14453 ibf_dump_object_string(dump, rb_sym2str(obj));
14454}
14455
14456static VALUE
14457ibf_load_object_symbol(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14458{
14459 ibf_offset_t reading_pos = offset;
14460
14461 int encindex = (int)ibf_load_small_value(load, &reading_pos);
14462 const long len = (long)ibf_load_small_value(load, &reading_pos);
14463 const char *ptr = load->current_buffer->buff + reading_pos;
14464
14465 if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
14466 VALUE enc_name_str = ibf_load_object(load, encindex - RUBY_ENCINDEX_BUILTIN_MAX);
14467 encindex = rb_enc_find_index(RSTRING_PTR(enc_name_str));
14468 }
14469
14470 ID id = rb_intern3(ptr, len, rb_enc_from_index(encindex));
14471 return ID2SYM(id);
14472}
14473
14474typedef void (*ibf_dump_object_function)(struct ibf_dump *dump, VALUE obj);
14475static const ibf_dump_object_function dump_object_functions[RUBY_T_MASK+1] = {
14476 ibf_dump_object_unsupported, /* T_NONE */
14477 ibf_dump_object_unsupported, /* T_OBJECT */
14478 ibf_dump_object_class, /* T_CLASS */
14479 ibf_dump_object_unsupported, /* T_MODULE */
14480 ibf_dump_object_float, /* T_FLOAT */
14481 ibf_dump_object_string, /* T_STRING */
14482 ibf_dump_object_regexp, /* T_REGEXP */
14483 ibf_dump_object_array, /* T_ARRAY */
14484 ibf_dump_object_hash, /* T_HASH */
14485 ibf_dump_object_struct, /* T_STRUCT */
14486 ibf_dump_object_bignum, /* T_BIGNUM */
14487 ibf_dump_object_unsupported, /* T_FILE */
14488 ibf_dump_object_data, /* T_DATA */
14489 ibf_dump_object_unsupported, /* T_MATCH */
14490 ibf_dump_object_complex_rational, /* T_COMPLEX */
14491 ibf_dump_object_complex_rational, /* T_RATIONAL */
14492 ibf_dump_object_unsupported, /* 0x10 */
14493 ibf_dump_object_unsupported, /* 0x11 T_NIL */
14494 ibf_dump_object_unsupported, /* 0x12 T_TRUE */
14495 ibf_dump_object_unsupported, /* 0x13 T_FALSE */
14496 ibf_dump_object_symbol, /* 0x14 T_SYMBOL */
14497 ibf_dump_object_unsupported, /* T_FIXNUM */
14498 ibf_dump_object_unsupported, /* T_UNDEF */
14499 ibf_dump_object_unsupported, /* 0x17 */
14500 ibf_dump_object_unsupported, /* 0x18 */
14501 ibf_dump_object_unsupported, /* 0x19 */
14502 ibf_dump_object_unsupported, /* T_IMEMO 0x1a */
14503 ibf_dump_object_unsupported, /* T_NODE 0x1b */
14504 ibf_dump_object_unsupported, /* T_ICLASS 0x1c */
14505 ibf_dump_object_unsupported, /* T_ZOMBIE 0x1d */
14506 ibf_dump_object_unsupported, /* 0x1e */
14507 ibf_dump_object_unsupported, /* 0x1f */
14508};
14509
14510static void
14511ibf_dump_object_object_header(struct ibf_dump *dump, const struct ibf_object_header header)
14512{
14513 unsigned char byte =
14514 (header.type << 0) |
14515 (header.special_const << 5) |
14516 (header.frozen << 6) |
14517 (header.internal << 7);
14518
14519 IBF_WV(byte);
14520}
14521
14522static struct ibf_object_header
14523ibf_load_object_object_header(const struct ibf_load *load, ibf_offset_t *offset)
14524{
14525 unsigned char byte = ibf_load_byte(load, offset);
14526
14527 struct ibf_object_header header;
14528 header.type = (byte >> 0) & 0x1f;
14529 header.special_const = (byte >> 5) & 0x01;
14530 header.frozen = (byte >> 6) & 0x01;
14531 header.internal = (byte >> 7) & 0x01;
14532
14533 return header;
14534}
14535
14536static ibf_offset_t
14537ibf_dump_object_object(struct ibf_dump *dump, VALUE obj)
14538{
14539 struct ibf_object_header obj_header;
14540 ibf_offset_t current_offset;
14541 IBF_ZERO(obj_header);
14542 obj_header.type = TYPE(obj);
14543
14544 IBF_W_ALIGN(ibf_offset_t);
14545 current_offset = ibf_dump_pos(dump);
14546
14547 if (SPECIAL_CONST_P(obj) &&
14548 ! (SYMBOL_P(obj) ||
14549 RB_FLOAT_TYPE_P(obj))) {
14550 obj_header.special_const = TRUE;
14551 obj_header.frozen = TRUE;
14552 obj_header.internal = TRUE;
14553 ibf_dump_object_object_header(dump, obj_header);
14554 ibf_dump_write_small_value(dump, obj);
14555 }
14556 else {
14557 obj_header.internal = SPECIAL_CONST_P(obj) ? FALSE : (RBASIC_CLASS(obj) == 0) ? TRUE : FALSE;
14558 obj_header.special_const = FALSE;
14559 obj_header.frozen = OBJ_FROZEN(obj) ? TRUE : FALSE;
14560 ibf_dump_object_object_header(dump, obj_header);
14561 (*dump_object_functions[obj_header.type])(dump, obj);
14562 }
14563
14564 return current_offset;
14565}
14566
14567typedef VALUE (*ibf_load_object_function)(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset);
14568static const ibf_load_object_function load_object_functions[RUBY_T_MASK+1] = {
14569 ibf_load_object_unsupported, /* T_NONE */
14570 ibf_load_object_unsupported, /* T_OBJECT */
14571 ibf_load_object_class, /* T_CLASS */
14572 ibf_load_object_unsupported, /* T_MODULE */
14573 ibf_load_object_float, /* T_FLOAT */
14574 ibf_load_object_string, /* T_STRING */
14575 ibf_load_object_regexp, /* T_REGEXP */
14576 ibf_load_object_array, /* T_ARRAY */
14577 ibf_load_object_hash, /* T_HASH */
14578 ibf_load_object_struct, /* T_STRUCT */
14579 ibf_load_object_bignum, /* T_BIGNUM */
14580 ibf_load_object_unsupported, /* T_FILE */
14581 ibf_load_object_data, /* T_DATA */
14582 ibf_load_object_unsupported, /* T_MATCH */
14583 ibf_load_object_complex_rational, /* T_COMPLEX */
14584 ibf_load_object_complex_rational, /* T_RATIONAL */
14585 ibf_load_object_unsupported, /* 0x10 */
14586 ibf_load_object_unsupported, /* T_NIL */
14587 ibf_load_object_unsupported, /* T_TRUE */
14588 ibf_load_object_unsupported, /* T_FALSE */
14589 ibf_load_object_symbol,
14590 ibf_load_object_unsupported, /* T_FIXNUM */
14591 ibf_load_object_unsupported, /* T_UNDEF */
14592 ibf_load_object_unsupported, /* 0x17 */
14593 ibf_load_object_unsupported, /* 0x18 */
14594 ibf_load_object_unsupported, /* 0x19 */
14595 ibf_load_object_unsupported, /* T_IMEMO 0x1a */
14596 ibf_load_object_unsupported, /* T_NODE 0x1b */
14597 ibf_load_object_unsupported, /* T_ICLASS 0x1c */
14598 ibf_load_object_unsupported, /* T_ZOMBIE 0x1d */
14599 ibf_load_object_unsupported, /* 0x1e */
14600 ibf_load_object_unsupported, /* 0x1f */
14601};
14602
14603static VALUE
14604ibf_load_object(const struct ibf_load *load, VALUE object_index)
14605{
14606 if (object_index == 0) {
14607 return Qnil;
14608 }
14609 else {
14610 VALUE obj = pinned_list_fetch(load->current_buffer->obj_list, (long)object_index);
14611 if (!obj) {
14612 ibf_offset_t *offsets = (ibf_offset_t *)(load->current_buffer->obj_list_offset + load->current_buffer->buff);
14613 ibf_offset_t offset = offsets[object_index];
14614 const struct ibf_object_header header = ibf_load_object_object_header(load, &offset);
14615
14616#if IBF_ISEQ_DEBUG
14617 fprintf(stderr, "ibf_load_object: list=%#x offsets=%p offset=%#x\n",
14618 load->current_buffer->obj_list_offset, (void *)offsets, offset);
14619 fprintf(stderr, "ibf_load_object: type=%#x special=%d frozen=%d internal=%d\n",
14620 header.type, header.special_const, header.frozen, header.internal);
14621#endif
14622 if (offset >= load->current_buffer->size) {
14623 rb_raise(rb_eIndexError, "object offset out of range: %u", offset);
14624 }
14625
14626 if (header.special_const) {
14627 ibf_offset_t reading_pos = offset;
14628
14629 obj = ibf_load_small_value(load, &reading_pos);
14630 }
14631 else {
14632 obj = (*load_object_functions[header.type])(load, &header, offset);
14633 }
14634
14635 pinned_list_store(load->current_buffer->obj_list, (long)object_index, obj);
14636 }
14637#if IBF_ISEQ_DEBUG
14638 fprintf(stderr, "ibf_load_object: index=%#"PRIxVALUE" obj=%#"PRIxVALUE"\n",
14639 object_index, obj);
14640#endif
14641 return obj;
14642 }
14643}
14644
14646{
14647 struct ibf_dump *dump;
14648 VALUE offset_list;
14649};
14650
14651static int
14652ibf_dump_object_list_i(st_data_t key, st_data_t val, st_data_t ptr)
14653{
14654 VALUE obj = (VALUE)key;
14655 struct ibf_dump_object_list_arg *args = (struct ibf_dump_object_list_arg *)ptr;
14656
14657 ibf_offset_t offset = ibf_dump_object_object(args->dump, obj);
14658 rb_ary_push(args->offset_list, UINT2NUM(offset));
14659
14660 return ST_CONTINUE;
14661}
14662
14663static void
14664ibf_dump_object_list(struct ibf_dump *dump, ibf_offset_t *obj_list_offset, unsigned int *obj_list_size)
14665{
14666 st_table *obj_table = dump->current_buffer->obj_table;
14667 VALUE offset_list = rb_ary_hidden_new(obj_table->num_entries);
14668
14669 struct ibf_dump_object_list_arg args;
14670 args.dump = dump;
14671 args.offset_list = offset_list;
14672
14673 st_foreach(obj_table, ibf_dump_object_list_i, (st_data_t)&args);
14674
14675 IBF_W_ALIGN(ibf_offset_t);
14676 *obj_list_offset = ibf_dump_pos(dump);
14677
14678 st_index_t size = obj_table->num_entries;
14679 st_index_t i;
14680
14681 for (i=0; i<size; i++) {
14682 ibf_offset_t offset = NUM2UINT(RARRAY_AREF(offset_list, i));
14683 IBF_WV(offset);
14684 }
14685
14686 *obj_list_size = (unsigned int)size;
14687}
14688
14689static void
14690ibf_dump_mark(void *ptr)
14691{
14692 struct ibf_dump *dump = (struct ibf_dump *)ptr;
14693 rb_gc_mark(dump->global_buffer.str);
14694
14695 rb_mark_set(dump->global_buffer.obj_table);
14696 rb_mark_set(dump->iseq_table);
14697}
14698
14699static void
14700ibf_dump_free(void *ptr)
14701{
14702 struct ibf_dump *dump = (struct ibf_dump *)ptr;
14703 if (dump->global_buffer.obj_table) {
14704 st_free_table(dump->global_buffer.obj_table);
14705 dump->global_buffer.obj_table = 0;
14706 }
14707 if (dump->iseq_table) {
14708 st_free_table(dump->iseq_table);
14709 dump->iseq_table = 0;
14710 }
14711}
14712
14713static size_t
14714ibf_dump_memsize(const void *ptr)
14715{
14716 struct ibf_dump *dump = (struct ibf_dump *)ptr;
14717 size_t size = 0;
14718 if (dump->iseq_table) size += st_memsize(dump->iseq_table);
14719 if (dump->global_buffer.obj_table) size += st_memsize(dump->global_buffer.obj_table);
14720 return size;
14721}
14722
14723static const rb_data_type_t ibf_dump_type = {
14724 "ibf_dump",
14725 {ibf_dump_mark, ibf_dump_free, ibf_dump_memsize,},
14726 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE
14727};
14728
14729static void
14730ibf_dump_setup(struct ibf_dump *dump, VALUE dumper_obj)
14731{
14732 dump->global_buffer.obj_table = NULL; // GC may run before a value is assigned
14733 dump->iseq_table = NULL;
14734
14735 RB_OBJ_WRITE(dumper_obj, &dump->global_buffer.str, rb_str_new(0, 0));
14736 dump->global_buffer.obj_table = ibf_dump_object_table_new();
14737 dump->iseq_table = st_init_numtable(); /* need free */
14738
14739 dump->current_buffer = &dump->global_buffer;
14740}
14741
14742VALUE
14743rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt)
14744{
14745 struct ibf_dump *dump;
14746 struct ibf_header header = {{0}};
14747 VALUE dump_obj;
14748 VALUE str;
14749
14750 if (ISEQ_BODY(iseq)->parent_iseq != NULL ||
14751 ISEQ_BODY(iseq)->local_iseq != iseq) {
14752 rb_raise(rb_eRuntimeError, "should be top of iseq");
14753 }
14754 if (RTEST(ISEQ_COVERAGE(iseq))) {
14755 rb_raise(rb_eRuntimeError, "should not compile with coverage");
14756 }
14757
14758 dump_obj = TypedData_Make_Struct(0, struct ibf_dump, &ibf_dump_type, dump);
14759 ibf_dump_setup(dump, dump_obj);
14760
14761 ibf_dump_write(dump, &header, sizeof(header));
14762 ibf_dump_iseq(dump, iseq);
14763
14764 header.magic[0] = 'Y'; /* YARB */
14765 header.magic[1] = 'A';
14766 header.magic[2] = 'R';
14767 header.magic[3] = 'B';
14768 header.major_version = IBF_MAJOR_VERSION;
14769 header.minor_version = IBF_MINOR_VERSION;
14770 header.endian = IBF_ENDIAN_MARK;
14771 header.wordsize = (uint8_t)SIZEOF_VALUE;
14772 ibf_dump_iseq_list(dump, &header);
14773 ibf_dump_object_list(dump, &header.global_object_list_offset, &header.global_object_list_size);
14774 header.size = ibf_dump_pos(dump);
14775
14776 if (RTEST(opt)) {
14777 VALUE opt_str = opt;
14778 const char *ptr = StringValuePtr(opt_str);
14779 header.extra_size = RSTRING_LENINT(opt_str);
14780 ibf_dump_write(dump, ptr, header.extra_size);
14781 }
14782 else {
14783 header.extra_size = 0;
14784 }
14785
14786 ibf_dump_overwrite(dump, &header, sizeof(header), 0);
14787
14788 str = dump->global_buffer.str;
14789 RB_GC_GUARD(dump_obj);
14790 return str;
14791}
14792
14793static const ibf_offset_t *
14794ibf_iseq_list(const struct ibf_load *load)
14795{
14796 return (const ibf_offset_t *)(load->global_buffer.buff + load->header->iseq_list_offset);
14797}
14798
14799void
14800rb_ibf_load_iseq_complete(rb_iseq_t *iseq)
14801{
14802 struct ibf_load *load = RTYPEDDATA_DATA(iseq->aux.loader.obj);
14803 rb_iseq_t *prev_src_iseq = load->iseq;
14804 ibf_offset_t offset = ibf_iseq_list(load)[iseq->aux.loader.index];
14805 load->iseq = iseq;
14806#if IBF_ISEQ_DEBUG
14807 fprintf(stderr, "rb_ibf_load_iseq_complete: index=%#x offset=%#x size=%#x\n",
14808 iseq->aux.loader.index, offset,
14809 load->header->size);
14810#endif
14811 ibf_load_iseq_each(load, iseq, offset);
14812 ISEQ_COMPILE_DATA_CLEAR(iseq);
14813 FL_UNSET((VALUE)iseq, ISEQ_NOT_LOADED_YET);
14814 rb_iseq_init_trace(iseq);
14815 load->iseq = prev_src_iseq;
14816}
14817
14818#if USE_LAZY_LOAD
14819const rb_iseq_t *
14820rb_iseq_complete(const rb_iseq_t *iseq)
14821{
14822 rb_ibf_load_iseq_complete((rb_iseq_t *)iseq);
14823 return iseq;
14824}
14825#endif
14826
14827static rb_iseq_t *
14828ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq)
14829{
14830 int iseq_index = (int)(VALUE)index_iseq;
14831
14832#if IBF_ISEQ_DEBUG
14833 fprintf(stderr, "ibf_load_iseq: index_iseq=%p iseq_list=%p\n",
14834 (void *)index_iseq, (void *)load->iseq_list);
14835#endif
14836 if (iseq_index == -1) {
14837 return NULL;
14838 }
14839 else {
14840 VALUE iseqv = pinned_list_fetch(load->iseq_list, iseq_index);
14841
14842#if IBF_ISEQ_DEBUG
14843 fprintf(stderr, "ibf_load_iseq: iseqv=%p\n", (void *)iseqv);
14844#endif
14845 if (iseqv) {
14846 return (rb_iseq_t *)iseqv;
14847 }
14848 else {
14849 rb_iseq_t *iseq = iseq_imemo_alloc();
14850#if IBF_ISEQ_DEBUG
14851 fprintf(stderr, "ibf_load_iseq: new iseq=%p\n", (void *)iseq);
14852#endif
14853 FL_SET((VALUE)iseq, ISEQ_NOT_LOADED_YET);
14854 iseq->aux.loader.obj = load->loader_obj;
14855 iseq->aux.loader.index = iseq_index;
14856#if IBF_ISEQ_DEBUG
14857 fprintf(stderr, "ibf_load_iseq: iseq=%p loader_obj=%p index=%d\n",
14858 (void *)iseq, (void *)load->loader_obj, iseq_index);
14859#endif
14860 pinned_list_store(load->iseq_list, iseq_index, (VALUE)iseq);
14861
14862 if (!USE_LAZY_LOAD || GET_VM()->builtin_function_table) {
14863#if IBF_ISEQ_DEBUG
14864 fprintf(stderr, "ibf_load_iseq: loading iseq=%p\n", (void *)iseq);
14865#endif
14866 rb_ibf_load_iseq_complete(iseq);
14867 }
14868
14869#if IBF_ISEQ_DEBUG
14870 fprintf(stderr, "ibf_load_iseq: iseq=%p loaded %p\n",
14871 (void *)iseq, (void *)load->iseq);
14872#endif
14873 return iseq;
14874 }
14875 }
14876}
14877
14878static void
14879ibf_load_setup_bytes(struct ibf_load *load, VALUE loader_obj, const char *bytes, size_t size)
14880{
14881 struct ibf_header *header = (struct ibf_header *)bytes;
14882 load->loader_obj = loader_obj;
14883 load->global_buffer.buff = bytes;
14884 load->header = header;
14885 load->global_buffer.size = header->size;
14886 load->global_buffer.obj_list_offset = header->global_object_list_offset;
14887 load->global_buffer.obj_list_size = header->global_object_list_size;
14888 RB_OBJ_WRITE(loader_obj, &load->iseq_list, pinned_list_new(header->iseq_list_size));
14889 RB_OBJ_WRITE(loader_obj, &load->global_buffer.obj_list, pinned_list_new(load->global_buffer.obj_list_size));
14890 load->iseq = NULL;
14891
14892 load->current_buffer = &load->global_buffer;
14893
14894 if (size < header->size) {
14895 rb_raise(rb_eRuntimeError, "broken binary format");
14896 }
14897 if (strncmp(header->magic, "YARB", 4) != 0) {
14898 rb_raise(rb_eRuntimeError, "unknown binary format");
14899 }
14900 if (header->major_version != IBF_MAJOR_VERSION ||
14901 header->minor_version != IBF_MINOR_VERSION) {
14902 rb_raise(rb_eRuntimeError, "unmatched version file (%u.%u for %u.%u)",
14903 header->major_version, header->minor_version, IBF_MAJOR_VERSION, IBF_MINOR_VERSION);
14904 }
14905 if (header->endian != IBF_ENDIAN_MARK) {
14906 rb_raise(rb_eRuntimeError, "unmatched endian: %c", header->endian);
14907 }
14908 if (header->wordsize != SIZEOF_VALUE) {
14909 rb_raise(rb_eRuntimeError, "unmatched word size: %d", header->wordsize);
14910 }
14911 if (header->iseq_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
14912 rb_raise(rb_eArgError, "unaligned iseq list offset: %u",
14913 header->iseq_list_offset);
14914 }
14915 if (load->global_buffer.obj_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
14916 rb_raise(rb_eArgError, "unaligned object list offset: %u",
14917 load->global_buffer.obj_list_offset);
14918 }
14919}
14920
14921static void
14922ibf_load_setup(struct ibf_load *load, VALUE loader_obj, VALUE str)
14923{
14924 StringValue(str);
14925
14926 if (RSTRING_LENINT(str) < (int)sizeof(struct ibf_header)) {
14927 rb_raise(rb_eRuntimeError, "broken binary format");
14928 }
14929
14930 if (USE_LAZY_LOAD) {
14931 str = rb_str_new(RSTRING_PTR(str), RSTRING_LEN(str));
14932 }
14933
14934 ibf_load_setup_bytes(load, loader_obj, RSTRING_PTR(str), RSTRING_LEN(str));
14935 RB_OBJ_WRITE(loader_obj, &load->str, str);
14936}
14937
14938static void
14939ibf_loader_mark(void *ptr)
14940{
14941 struct ibf_load *load = (struct ibf_load *)ptr;
14942 rb_gc_mark(load->str);
14943 rb_gc_mark(load->iseq_list);
14944 rb_gc_mark(load->global_buffer.obj_list);
14945}
14946
14947static void
14948ibf_loader_free(void *ptr)
14949{
14950 struct ibf_load *load = (struct ibf_load *)ptr;
14951 SIZED_FREE(load);
14952}
14953
14954static size_t
14955ibf_loader_memsize(const void *ptr)
14956{
14957 return sizeof(struct ibf_load);
14958}
14959
14960static const rb_data_type_t ibf_load_type = {
14961 "ibf_loader",
14962 {ibf_loader_mark, ibf_loader_free, ibf_loader_memsize,},
14963 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
14964};
14965
14966const rb_iseq_t *
14967rb_iseq_ibf_load(VALUE str)
14968{
14969 struct ibf_load *load;
14970 rb_iseq_t *iseq;
14971 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
14972
14973 ibf_load_setup(load, loader_obj, str);
14974 iseq = ibf_load_iseq(load, 0);
14975
14976 RB_GC_GUARD(loader_obj);
14977 return iseq;
14978}
14979
14980const rb_iseq_t *
14981rb_iseq_ibf_load_bytes(const char *bytes, size_t size)
14982{
14983 struct ibf_load *load;
14984 rb_iseq_t *iseq;
14985 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
14986
14987 ibf_load_setup_bytes(load, loader_obj, bytes, size);
14988 iseq = ibf_load_iseq(load, 0);
14989
14990 RB_GC_GUARD(loader_obj);
14991 return iseq;
14992}
14993
14994VALUE
14995rb_iseq_ibf_load_extra_data(VALUE str)
14996{
14997 struct ibf_load *load;
14998 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
14999 VALUE extra_str;
15000
15001 ibf_load_setup(load, loader_obj, str);
15002 extra_str = rb_str_new(load->global_buffer.buff + load->header->size, load->header->extra_size);
15003 RB_GC_GUARD(loader_obj);
15004 return extra_str;
15005}
15006
15007#include "prism_compile.c"
#define RBIMPL_ASSERT_OR_ASSUME(...)
This is either RUBY_ASSERT or RBIMPL_ASSUME, depending on RUBY_DEBUG.
Definition assert.h:311
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
#define LONG_LONG
Definition long_long.h:38
#define RUBY_ALIGNOF
Wraps (or simulates) alignof.
Definition stdalign.h:28
#define RUBY_EVENT_END
Encountered an end of a class clause.
Definition event.h:40
#define RUBY_EVENT_C_CALL
A method, written in C, is called.
Definition event.h:43
#define RUBY_EVENT_B_RETURN
Encountered a next statement.
Definition event.h:56
#define RUBY_EVENT_CLASS
Encountered a new class.
Definition event.h:39
#define RUBY_EVENT_NONE
No events.
Definition event.h:37
#define RUBY_EVENT_LINE
Encountered a new line.
Definition event.h:38
#define RUBY_EVENT_RETURN
Encountered a return statement.
Definition event.h:42
#define RUBY_EVENT_C_RETURN
Return from a method, written in C.
Definition event.h:44
#define RUBY_EVENT_B_CALL
Encountered an yield statement.
Definition event.h:55
uint32_t rb_event_flag_t
Represents event(s).
Definition event.h:108
#define RUBY_EVENT_CALL
A method, written in Ruby, is called.
Definition event.h:41
#define RUBY_EVENT_RESCUE
Encountered a rescue statement.
Definition event.h:61
#define RBIMPL_ATTR_FORMAT(x, y, z)
Wraps (or simulates) __attribute__((format))
Definition format.h:29
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1676
#define T_COMPLEX
Old name of RUBY_T_COMPLEX.
Definition value_type.h:59
#define TYPE(_)
Old name of rb_type.
Definition value_type.h:108
#define NUM2ULONG
Old name of RB_NUM2ULONG.
Definition long.h:52
#define NUM2LL
Old name of RB_NUM2LL.
Definition long_long.h:34
#define ALLOCV
Old name of RB_ALLOCV.
Definition memory.h:404
#define RFLOAT_VALUE
Old name of rb_float_value.
Definition double.h:28
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define OBJ_FROZEN
Old name of RB_OBJ_FROZEN.
Definition fl_type.h:133
#define rb_str_cat2
Old name of rb_str_cat_cstr.
Definition string.h:1684
#define T_NIL
Old name of RUBY_T_NIL.
Definition value_type.h:72
#define UNREACHABLE
Old name of RBIMPL_UNREACHABLE.
Definition assume.h:28
#define T_FLOAT
Old name of RUBY_T_FLOAT.
Definition value_type.h:64
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define T_BIGNUM
Old name of RUBY_T_BIGNUM.
Definition value_type.h:57
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
Definition fl_type.h:131
#define ULONG2NUM
Old name of RB_ULONG2NUM.
Definition long.h:60
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
Definition assume.h:29
#define SYM2ID
Old name of RB_SYM2ID.
Definition symbol.h:45
#define FIX2UINT
Old name of RB_FIX2UINT.
Definition int.h:42
#define ZALLOC
Old name of RB_ZALLOC.
Definition memory.h:402
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:205
#define FIXABLE
Old name of RB_FIXABLE.
Definition fixnum.h:25
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define FIX2INT
Old name of RB_FIX2INT.
Definition int.h:41
#define NUM2UINT
Old name of RB_NUM2UINT.
Definition int.h:45
#define ZALLOC_N
Old name of RB_ZALLOC_N.
Definition memory.h:401
#define ASSUME
Old name of RBIMPL_ASSUME.
Definition assume.h:27
#define T_RATIONAL
Old name of RUBY_T_RATIONAL.
Definition value_type.h:76
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
#define ALLOC_N
Old name of RB_ALLOC_N.
Definition memory.h:399
#define FL_SET
Old name of RB_FL_SET.
Definition fl_type.h:125
#define FLONUM_P
Old name of RB_FLONUM_P.
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define FIX2LONG
Old name of RB_FIX2LONG.
Definition long.h:46
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define NIL_P
Old name of RB_NIL_P.
#define T_SYMBOL
Old name of RUBY_T_SYMBOL.
Definition value_type.h:80
#define DBL2NUM
Old name of rb_float_new.
Definition double.h:29
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
Definition value_type.h:85
#define NUM2LONG
Old name of RB_NUM2LONG.
Definition long.h:51
#define FL_UNSET
Old name of RB_FL_UNSET.
Definition fl_type.h:129
#define UINT2NUM
Old name of RB_UINT2NUM.
Definition int.h:46
#define FIXNUM_P
Old name of RB_FIXNUM_P.
#define CONST_ID
Old name of RUBY_CONST_ID.
Definition symbol.h:47
#define ALLOCV_END
Old name of RB_ALLOCV_END.
Definition memory.h:406
#define SYMBOL_P
Old name of RB_SYMBOL_P.
Definition value_type.h:88
#define T_REGEXP
Old name of RUBY_T_REGEXP.
Definition value_type.h:77
#define ruby_debug
This variable controls whether the interpreter is in debug mode.
Definition error.h:487
VALUE rb_eNotImpError
NotImplementedError exception.
Definition error.c:1428
VALUE rb_eStandardError
StandardError exception.
Definition error.c:1415
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1418
VALUE rb_eNoMatchingPatternError
NoMatchingPatternError exception.
Definition error.c:1431
void rb_exc_fatal(VALUE mesg)
Raises a fatal error in the current thread.
Definition eval.c:673
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1416
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:466
VALUE rb_eNoMatchingPatternKeyError
NoMatchingPatternKeyError exception.
Definition error.c:1432
VALUE rb_eIndexError
IndexError exception.
Definition error.c:1420
VALUE rb_eSyntaxError
SyntaxError exception.
Definition error.c:1435
@ RB_WARN_CATEGORY_STRICT_UNUSED_BLOCK
Warning is for checking unused block strictly.
Definition error.h:57
VALUE rb_obj_reveal(VALUE obj, VALUE klass)
Make a hidden object visible again.
Definition object.c:109
VALUE rb_cArray
Array class.
VALUE rb_cObject
Object class.
Definition object.c:61
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:100
VALUE rb_cHash
Hash class.
Definition hash.c:109
VALUE rb_inspect(VALUE obj)
Generates a human-readable textual representation of the given object.
Definition object.c:686
VALUE rb_cRange
Range class.
Definition range.c:31
VALUE rb_obj_is_kind_of(VALUE obj, VALUE klass)
Queries if the given object is an instance (of possibly descendants) of the given class.
Definition object.c:923
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1342
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:615
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:603
#define RB_POSFIXABLE(_)
Checks if the passed value is in range of fixnum, assuming it is a positive number.
Definition fixnum.h:43
Defines RBIMPL_HAS_BUILTIN.
VALUE rb_ary_reverse(VALUE ary)
Destructively reverses the passed array in-place.
VALUE rb_ary_dup(VALUE ary)
Duplicates an array.
VALUE rb_ary_cat(VALUE ary, const VALUE *train, long len)
Destructively appends multiple elements at the end of the array.
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_new_capa(long capa)
Identical to rb_ary_new(), except it additionally specifies how many rooms of objects it should alloc...
VALUE rb_ary_hidden_new(long capa)
Allocates a hidden (no class) empty array.
VALUE rb_ary_clear(VALUE ary)
Destructively removes everything form an array.
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
VALUE rb_ary_freeze(VALUE obj)
Freeze an array, preventing further modifications.
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
VALUE rb_ary_join(VALUE ary, VALUE sep)
Recursively stringises the elements of the passed array, flattens that result, then joins the sequenc...
void rb_ary_store(VALUE ary, long key, VALUE val)
Destructively stores the passed value to the passed array's passed index.
#define INTEGER_PACK_NATIVE_BYTE_ORDER
Means either INTEGER_PACK_MSBYTE_FIRST or INTEGER_PACK_LSBYTE_FIRST, depending on the host processor'...
Definition bignum.h:546
#define INTEGER_PACK_NEGATIVE
Interprets the input as a signed negative number (unpack only).
Definition bignum.h:564
#define INTEGER_PACK_LSWORD_FIRST
Stores/interprets the least significant word as the first word.
Definition bignum.h:528
int rb_is_const_id(ID id)
Classifies the given ID, then sees if it is a constant.
Definition symbol.c:1117
int rb_is_attrset_id(ID id)
Classifies the given ID, then sees if it is an attribute writer.
Definition symbol.c:1141
int rb_range_values(VALUE range, VALUE *begp, VALUE *endp, int *exclp)
Deconstructs a range into its components.
Definition range.c:1861
VALUE rb_range_new(VALUE beg, VALUE end, int excl)
Creates a new Range.
Definition range.c:69
VALUE rb_rational_new(VALUE num, VALUE den)
Constructs a Rational, with reduction.
Definition rational.c:1985
int rb_reg_options(VALUE re)
Queries the options of the passed regular expression.
Definition re.c:4227
VALUE rb_str_append(VALUE dst, VALUE src)
Identical to rb_str_buf_append(), except it converts the right hand side before concatenating.
Definition string.c:3818
VALUE rb_str_tmp_new(long len)
Allocates a "temporary" string.
Definition string.c:1729
int rb_str_hash_cmp(VALUE str1, VALUE str2)
Compares two strings.
Definition string.c:4181
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1499
st_index_t rb_str_hash(VALUE str)
Calculates a hash value of a string.
Definition string.c:4167
VALUE rb_str_cat(VALUE dst, const char *src, long srclen)
Destructively appends the passed contents to the string.
Definition string.c:3586
VALUE rb_str_buf_append(VALUE dst, VALUE src)
Identical to rb_str_cat_cstr(), except it takes Ruby's string instead of C's.
Definition string.c:3784
int rb_str_cmp(VALUE lhs, VALUE rhs)
Compares two strings, as in strcmp(3).
Definition string.c:4235
VALUE rb_str_concat(VALUE dst, VALUE src)
Identical to rb_str_append(), except it also accepts an integer as a codepoint.
Definition string.c:4055
VALUE rb_str_freeze(VALUE str)
This is the implementation of String#freeze.
Definition string.c:3296
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1515
VALUE rb_class_name(VALUE obj)
Queries the name of the given object's class.
Definition variable.c:500
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:285
VALUE rb_id2sym(ID id)
Allocates an instance of rb_cSymbol that has the given id.
Definition symbol.c:1012
VALUE rb_sym2str(VALUE symbol)
Obtain a frozen string representation of a symbol (not including the leading colon).
Definition symbol.c:1031
ID rb_sym2id(VALUE obj)
Converts an instance of rb_cSymbol into an ID.
Definition symbol.c:981
int len
Length of the buffer.
Definition io.h:8
#define RB_OBJ_SHAREABLE_P(obj)
Queries if the passed object has previously classified as shareable or not.
Definition ractor.h:235
VALUE rb_ractor_make_shareable(VALUE obj)
Destructively transforms the passed object so that multiple Ractors can share it.
Definition ractor.c:1552
#define DECIMAL_SIZE_OF(expr)
An approximation of decimal representation size.
Definition util.h:48
void ruby_qsort(void *, const size_t, const size_t, int(*)(const void *, const void *, void *), void *)
Reentrant implementation of quick sort.
#define rb_long2int
Just another name of rb_long2int_inline.
Definition long.h:62
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:372
#define ALLOCA_N(type, n)
Definition memory.h:292
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition memory.h:360
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
#define RB_ALLOCV(v, n)
Identical to RB_ALLOCV_N(), except that it allocates a number of bytes and returns a void* .
Definition memory.h:304
VALUE type(ANYARGS)
ANYARGS-ed function type.
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
#define RBIMPL_ATTR_NORETURN()
Wraps (or simulates) [[noreturn]]
Definition noreturn.h:38
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition rarray.h:281
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition rarray.h:386
#define RARRAY_AREF(a, i)
Definition rarray.h:403
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
Definition rarray.h:52
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
Definition rbasic.h:166
#define RUBY_DEFAULT_FREE
This is a value you can set to RData::dfree.
Definition rdata.h:78
void(* RUBY_DATA_FUNC)(void *)
This is the type of callbacks registered to RData.
Definition rdata.h:104
#define RHASH_SIZE(h)
Queries the size of the hash.
Definition rhash.h:69
static VALUE RREGEXP_SRC(VALUE rexp)
Convenient getter function.
Definition rregexp.h:103
#define StringValue(v)
Ensures that the parameter object is a String.
Definition rstring.h:66
#define StringValuePtr(v)
Identical to StringValue, except it returns a char*.
Definition rstring.h:76
static int RSTRING_LENINT(VALUE str)
Identical to RSTRING_LEN(), except it differs for the return type.
Definition rstring.h:438
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
#define RTYPEDDATA_DATA(v)
Convenient getter macro.
Definition rtypeddata.h:103
#define RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
Definition rtypeddata.h:119
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:736
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:514
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:561
void rb_p(VALUE obj)
Inspects an object.
Definition io.c:9063
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
#define RTEST
This is an old name of RB_TEST.
#define _(args)
This was a transition path from K&R to ANSI.
Definition stdarg.h:35
Definition proc.c:30
Internal header for Complex.
Definition complex.h:13
Internal header for Rational.
Definition rational.h:16
Definition iseq.h:289
const ID * segments
A null-terminated list of ids, used to represent a constant's path idNULL is used to represent the ::...
Definition vm_core.h:285
Definition vm_core.h:288
Definition iseq.h:260
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:211
const char * wrap_struct_name
Name of structs of this kind.
Definition rtypeddata.h:218
Definition st.h:79
Definition vm_core.h:297
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
#define SIZEOF_VALUE
Identical to sizeof(VALUE), except it is a macro that can also be used inside of preprocessor directi...
Definition value.h:69
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_FLOAT_TYPE_P(VALUE obj)
Queries if the object is an instance of rb_cFloat.
Definition value_type.h:264
static bool rb_integer_type_p(VALUE obj)
Queries if the object is an instance of rb_cInteger.
Definition value_type.h:204
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376
@ RUBY_T_MASK
Bitmask of ruby_value_type.
Definition value_type.h:145