Ruby 4.1.0dev (2026-04-04 revision 3b6245536cf55da9e8bfcdb03c845fe9ef931d7f)
compile.c (3b6245536cf55da9e8bfcdb03c845fe9ef931d7f)
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_xfree_sized(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 RB_GC_GUARD(ary);
5379 hash = RB_OBJ_SET_FROZEN_SHAREABLE(rb_obj_hide(hash));
5380
5381 /* Emit optimized code */
5382 FLUSH_CHUNK();
5383 if (first_chunk) {
5384 ADD_INSN1(ret, line_node, duphash, hash);
5385 first_chunk = 0;
5386 }
5387 else {
5388 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5389 ADD_INSN(ret, line_node, swap);
5390
5391 ADD_INSN1(ret, line_node, putobject, hash);
5392
5393 ADD_SEND(ret, line_node, id_core_hash_merge_kwd, INT2FIX(2));
5394 }
5395 RB_OBJ_WRITTEN(iseq, Qundef, hash);
5396 }
5397 }
5398
5399 /* Base case: Compile "count" elements */
5400 for (; count; count--, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
5401
5402 if (CPDEBUG > 0) {
5403 EXPECT_NODE("compile_hash", node, NODE_LIST, -1);
5404 }
5405
5406 if (RNODE_LIST(node)->nd_head) {
5407 /* Normal key-value pair */
5408 NO_CHECK(COMPILE_(anchor, "hash key element", RNODE_LIST(node)->nd_head, 0));
5409 NO_CHECK(COMPILE_(anchor, "hash value element", RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, 0));
5410 stack_len += 2;
5411
5412 /* If there are many pushed elements, flush them to avoid stack overflow */
5413 if (stack_len >= max_stack_len) FLUSH_CHUNK();
5414 }
5415 else {
5416 /* kwsplat case: foo(..., **kw, ...) */
5417 FLUSH_CHUNK();
5418
5419 const NODE *kw = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head;
5420 int empty_kw = nd_type_p(kw, NODE_HASH) && (!RNODE_HASH(kw)->nd_head); /* foo( ..., **{}, ...) */
5421 int first_kw = first_chunk && stack_len == 0; /* foo(1,2,3, **kw, ...) */
5422 int last_kw = !RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next; /* foo( ..., **kw) */
5423 int only_kw = last_kw && first_kw; /* foo(1,2,3, **kw) */
5424
5425 empty_kw = empty_kw || nd_type_p(kw, NODE_NIL); /* foo( ..., **nil, ...) */
5426 if (empty_kw) {
5427 if (only_kw && method_call_keywords) {
5428 /* **{} appears at the only keyword argument in method call,
5429 * so it won't be modified.
5430 * kw is a special NODE_LIT that contains a special empty hash,
5431 * so this emits: putobject {}.
5432 * This is only done for method calls and not for literal hashes,
5433 * because literal hashes should always result in a new hash.
5434 */
5435 NO_CHECK(COMPILE(ret, "keyword splat", kw));
5436 }
5437 else if (first_kw) {
5438 /* **{} appears as the first keyword argument, so it may be modified.
5439 * We need to create a fresh hash object.
5440 */
5441 ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
5442 }
5443 /* Any empty keyword splats that are not the first can be ignored.
5444 * since merging an empty hash into the existing hash is the same
5445 * as not merging it. */
5446 }
5447 else {
5448 if (only_kw && method_call_keywords) {
5449 /* **kw is only keyword argument in method call.
5450 * Use directly. This will be not be flagged as mutable.
5451 * This is only done for method calls and not for literal hashes,
5452 * because literal hashes should always result in a new hash.
5453 */
5454 NO_CHECK(COMPILE(ret, "keyword splat", kw));
5455 }
5456 else {
5457 /* There is more than one keyword argument, or this is not a method
5458 * call. In that case, we need to add an empty hash (if first keyword),
5459 * or merge the hash to the accumulated hash (if not the first keyword).
5460 */
5461 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5462 if (first_kw) ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
5463 else ADD_INSN(ret, line_node, swap);
5464
5465 NO_CHECK(COMPILE(ret, "keyword splat", kw));
5466
5467 ADD_SEND(ret, line_node, id_core_hash_merge_kwd, INT2FIX(2));
5468 }
5469 }
5470
5471 first_chunk = 0;
5472 }
5473 }
5474 }
5475
5476 FLUSH_CHUNK();
5477#undef FLUSH_CHUNK
5478 return 1;
5479}
5480
5481VALUE
5482rb_node_case_when_optimizable_literal(const NODE *const node)
5483{
5484 switch (nd_type(node)) {
5485 case NODE_INTEGER:
5486 return rb_node_integer_literal_val(node);
5487 case NODE_FLOAT: {
5488 VALUE v = rb_node_float_literal_val(node);
5489 double ival;
5490
5491 if (modf(RFLOAT_VALUE(v), &ival) == 0.0) {
5492 return FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival);
5493 }
5494 return v;
5495 }
5496 case NODE_RATIONAL:
5497 case NODE_IMAGINARY:
5498 return Qundef;
5499 case NODE_NIL:
5500 return Qnil;
5501 case NODE_TRUE:
5502 return Qtrue;
5503 case NODE_FALSE:
5504 return Qfalse;
5505 case NODE_SYM:
5506 return rb_node_sym_string_val(node);
5507 case NODE_LINE:
5508 return rb_node_line_lineno_val(node);
5509 case NODE_STR:
5510 return rb_node_str_string_val(node);
5511 case NODE_FILE:
5512 return rb_node_file_path_val(node);
5513 }
5514 return Qundef;
5515}
5516
5517static int
5518when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
5519 LABEL *l1, int only_special_literals, VALUE literals)
5520{
5521 while (vals) {
5522 const NODE *val = RNODE_LIST(vals)->nd_head;
5523 VALUE lit = rb_node_case_when_optimizable_literal(val);
5524
5525 if (UNDEF_P(lit)) {
5526 only_special_literals = 0;
5527 }
5528 else if (NIL_P(rb_hash_lookup(literals, lit))) {
5529 rb_hash_aset(literals, lit, (VALUE)(l1) | 1);
5530 }
5531
5532 if (nd_type_p(val, NODE_STR) || nd_type_p(val, NODE_FILE)) {
5533 debugp_param("nd_lit", get_string_value(val));
5534 lit = get_string_value(val);
5535 ADD_INSN1(cond_seq, val, putobject, lit);
5536 RB_OBJ_WRITTEN(iseq, Qundef, lit);
5537 }
5538 else {
5539 if (!COMPILE(cond_seq, "when cond", val)) return -1;
5540 }
5541
5542 // Emit pattern === target
5543 ADD_INSN1(cond_seq, vals, topn, INT2FIX(1));
5544 ADD_CALL(cond_seq, vals, idEqq, INT2FIX(1));
5545 ADD_INSNL(cond_seq, val, branchif, l1);
5546 vals = RNODE_LIST(vals)->nd_next;
5547 }
5548 return only_special_literals;
5549}
5550
5551static int
5552when_splat_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
5553 LABEL *l1, int only_special_literals, VALUE literals)
5554{
5555 const NODE *line_node = vals;
5556
5557 switch (nd_type(vals)) {
5558 case NODE_LIST:
5559 if (when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals) < 0)
5560 return COMPILE_NG;
5561 break;
5562 case NODE_SPLAT:
5563 ADD_INSN (cond_seq, line_node, dup);
5564 CHECK(COMPILE(cond_seq, "when splat", RNODE_SPLAT(vals)->nd_head));
5565 ADD_INSN1(cond_seq, line_node, splatarray, Qfalse);
5566 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
5567 ADD_INSNL(cond_seq, line_node, branchif, l1);
5568 break;
5569 case NODE_ARGSCAT:
5570 CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSCAT(vals)->nd_head, l1, only_special_literals, literals));
5571 CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSCAT(vals)->nd_body, l1, only_special_literals, literals));
5572 break;
5573 case NODE_ARGSPUSH:
5574 CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSPUSH(vals)->nd_head, l1, only_special_literals, literals));
5575 ADD_INSN (cond_seq, line_node, dup);
5576 CHECK(COMPILE(cond_seq, "when argspush body", RNODE_ARGSPUSH(vals)->nd_body));
5577 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
5578 ADD_INSNL(cond_seq, line_node, branchif, l1);
5579 break;
5580 default:
5581 ADD_INSN (cond_seq, line_node, dup);
5582 CHECK(COMPILE(cond_seq, "when val", vals));
5583 ADD_INSN1(cond_seq, line_node, splatarray, Qfalse);
5584 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
5585 ADD_INSNL(cond_seq, line_node, branchif, l1);
5586 break;
5587 }
5588 return COMPILE_OK;
5589}
5590
5591/* Multiple Assignment Handling
5592 *
5593 * In order to handle evaluation of multiple assignment such that the left hand side
5594 * is evaluated before the right hand side, we need to process the left hand side
5595 * and see if there are any attributes that need to be assigned, or constants set
5596 * on explicit objects. If so, we add instructions to evaluate the receiver of
5597 * any assigned attributes or constants before we process the right hand side.
5598 *
5599 * For a multiple assignment such as:
5600 *
5601 * l1.m1, l2[0] = r3, r4
5602 *
5603 * We start off evaluating l1 and l2, then we evaluate r3 and r4, then we
5604 * assign the result of r3 to l1.m1, and then the result of r4 to l2.m2.
5605 * On the VM stack, this looks like:
5606 *
5607 * self # putself
5608 * l1 # send
5609 * l1, self # putself
5610 * l1, l2 # send
5611 * l1, l2, 0 # putobject 0
5612 * l1, l2, 0, [r3, r4] # after evaluation of RHS
5613 * l1, l2, 0, [r3, r4], r4, r3 # expandarray
5614 * l1, l2, 0, [r3, r4], r4, r3, l1 # topn 5
5615 * l1, l2, 0, [r3, r4], r4, l1, r3 # swap
5616 * l1, l2, 0, [r3, r4], r4, m1= # send
5617 * l1, l2, 0, [r3, r4], r4 # pop
5618 * l1, l2, 0, [r3, r4], r4, l2 # topn 3
5619 * l1, l2, 0, [r3, r4], r4, l2, 0 # topn 3
5620 * l1, l2, 0, [r3, r4], r4, l2, 0, r4 # topn 2
5621 * l1, l2, 0, [r3, r4], r4, []= # send
5622 * l1, l2, 0, [r3, r4], r4 # pop
5623 * l1, l2, 0, [r3, r4] # pop
5624 * [r3, r4], l2, 0, [r3, r4] # setn 3
5625 * [r3, r4], l2, 0 # pop
5626 * [r3, r4], l2 # pop
5627 * [r3, r4] # pop
5628 *
5629 * This is made more complex when you have to handle splats, post args,
5630 * and arbitrary levels of nesting. You need to keep track of the total
5631 * number of attributes to set, and for each attribute, how many entries
5632 * are on the stack before the final attribute, in order to correctly
5633 * calculate the topn value to use to get the receiver of the attribute
5634 * setter method.
5635 *
5636 * A brief description of the VM stack for simple multiple assignment
5637 * with no splat (rhs_array will not be present if the return value of
5638 * the multiple assignment is not needed):
5639 *
5640 * lhs_attr1, lhs_attr2, ..., rhs_array, ..., rhs_arg2, rhs_arg1
5641 *
5642 * For multiple assignment with splats, while processing the part before
5643 * the splat (splat+post here is an array of the splat and the post arguments):
5644 *
5645 * lhs_attr1, lhs_attr2, ..., rhs_array, splat+post, ..., rhs_arg2, rhs_arg1
5646 *
5647 * When processing the splat and post arguments:
5648 *
5649 * lhs_attr1, lhs_attr2, ..., rhs_array, ..., post_arg2, post_arg1, splat
5650 *
5651 * When processing nested multiple assignment, existing values on the stack
5652 * are kept. So for:
5653 *
5654 * (l1.m1, l2.m2), l3.m3, l4* = [r1, r2], r3, r4
5655 *
5656 * The stack layout would be the following before processing the nested
5657 * multiple assignment:
5658 *
5659 * l1, l2, [[r1, r2], r3, r4], [r4], r3, [r1, r2]
5660 *
5661 * In order to handle this correctly, we need to keep track of the nesting
5662 * level for each attribute assignment, as well as the attribute number
5663 * (left hand side attributes are processed left to right) and number of
5664 * arguments to pass to the setter method. struct masgn_lhs_node tracks
5665 * this information.
5666 *
5667 * We also need to track information for the entire multiple assignment, such
5668 * as the total number of arguments, and the current nesting level, to
5669 * handle both nested multiple assignment as well as cases where the
5670 * rhs is not needed. We also need to keep track of all attribute
5671 * assignments in this, which we do using a linked listed. struct masgn_state
5672 * tracks this information.
5673 */
5674
5676 INSN *before_insn;
5677 struct masgn_lhs_node *next;
5678 const NODE *line_node;
5679 int argn;
5680 int num_args;
5681 int lhs_pos;
5682};
5683
5685 struct masgn_lhs_node *first_memo;
5686 struct masgn_lhs_node *last_memo;
5687 int lhs_level;
5688 int num_args;
5689 bool nested;
5690};
5691
5692static int
5693add_masgn_lhs_node(struct masgn_state *state, int lhs_pos, const NODE *line_node, int argc, INSN *before_insn)
5694{
5695 if (!state) {
5696 rb_bug("no masgn_state");
5697 }
5698
5699 struct masgn_lhs_node *memo;
5700 memo = malloc(sizeof(struct masgn_lhs_node));
5701 if (!memo) {
5702 return COMPILE_NG;
5703 }
5704
5705 memo->before_insn = before_insn;
5706 memo->line_node = line_node;
5707 memo->argn = state->num_args + 1;
5708 memo->num_args = argc;
5709 state->num_args += argc;
5710 memo->lhs_pos = lhs_pos;
5711 memo->next = NULL;
5712 if (!state->first_memo) {
5713 state->first_memo = memo;
5714 }
5715 else {
5716 state->last_memo->next = memo;
5717 }
5718 state->last_memo = memo;
5719
5720 return COMPILE_OK;
5721}
5722
5723static 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);
5724
5725static int
5726compile_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)
5727{
5728 switch (nd_type(node)) {
5729 case NODE_ATTRASGN: {
5730 INSN *iobj;
5731 const NODE *line_node = node;
5732
5733 CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_ATTRASGN)", node));
5734
5735 bool safenav_call = false;
5736 LINK_ELEMENT *insn_element = LAST_ELEMENT(pre);
5737 iobj = (INSN *)get_prev_insn((INSN *)insn_element); /* send insn */
5738 ASSUME(iobj);
5739 ELEM_REMOVE(insn_element);
5740 if (!IS_INSN_ID(iobj, send)) {
5741 safenav_call = true;
5742 iobj = (INSN *)get_prev_insn(iobj);
5743 ELEM_INSERT_NEXT(&iobj->link, insn_element);
5744 }
5745 (pre->last = iobj->link.prev)->next = 0;
5746
5747 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
5748 int argc = vm_ci_argc(ci) + 1;
5749 ci = ci_argc_set(iseq, ci, argc);
5750 OPERAND_AT(iobj, 0) = (VALUE)ci;
5751 RB_OBJ_WRITTEN(iseq, Qundef, ci);
5752
5753 if (argc == 1) {
5754 ADD_INSN(lhs, line_node, swap);
5755 }
5756 else {
5757 ADD_INSN1(lhs, line_node, topn, INT2FIX(argc));
5758 }
5759
5760 if (!add_masgn_lhs_node(state, lhs_pos, line_node, argc, (INSN *)LAST_ELEMENT(lhs))) {
5761 return COMPILE_NG;
5762 }
5763
5764 iobj->link.prev = lhs->last;
5765 lhs->last->next = &iobj->link;
5766 for (lhs->last = &iobj->link; lhs->last->next; lhs->last = lhs->last->next);
5767 if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) {
5768 int argc = vm_ci_argc(ci);
5769 bool dupsplat = false;
5770 ci = ci_argc_set(iseq, ci, argc - 1);
5771 if (!(vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT_MUT)) {
5772 /* Given h[*a], _ = ary
5773 * setup_args sets VM_CALL_ARGS_SPLAT and not VM_CALL_ARGS_SPLAT_MUT
5774 * `a` must be dupped, because it will be appended with ary[0]
5775 * Since you are dupping `a`, you can set VM_CALL_ARGS_SPLAT_MUT
5776 */
5777 dupsplat = true;
5778 ci = ci_flag_set(iseq, ci, VM_CALL_ARGS_SPLAT_MUT);
5779 }
5780 OPERAND_AT(iobj, 0) = (VALUE)ci;
5781 RB_OBJ_WRITTEN(iseq, Qundef, iobj);
5782
5783 /* Given: h[*a], h[*b, 1] = ary
5784 * h[*a] uses splatarray false and does not set VM_CALL_ARGS_SPLAT_MUT,
5785 * so this uses splatarray true on a to dup it before using pushtoarray
5786 * h[*b, 1] uses splatarray true and sets VM_CALL_ARGS_SPLAT_MUT,
5787 * so you can use pushtoarray directly
5788 */
5789 int line_no = nd_line(line_node);
5790 int node_id = nd_node_id(line_node);
5791
5792 if (dupsplat) {
5793 INSERT_BEFORE_INSN(iobj, line_no, node_id, swap);
5794 INSERT_BEFORE_INSN1(iobj, line_no, node_id, splatarray, Qtrue);
5795 INSERT_BEFORE_INSN(iobj, line_no, node_id, swap);
5796 }
5797 INSERT_BEFORE_INSN1(iobj, line_no, node_id, pushtoarray, INT2FIX(1));
5798 }
5799 if (!safenav_call) {
5800 ADD_INSN(lhs, line_node, pop);
5801 if (argc != 1) {
5802 ADD_INSN(lhs, line_node, pop);
5803 }
5804 }
5805 for (int i=0; i < argc; i++) {
5806 ADD_INSN(post, line_node, pop);
5807 }
5808 break;
5809 }
5810 case NODE_MASGN: {
5811 DECL_ANCHOR(nest_rhs);
5812 INIT_ANCHOR(nest_rhs);
5813 DECL_ANCHOR(nest_lhs);
5814 INIT_ANCHOR(nest_lhs);
5815
5816 int prev_level = state->lhs_level;
5817 bool prev_nested = state->nested;
5818 state->nested = 1;
5819 state->lhs_level = lhs_pos - 1;
5820 CHECK(compile_massign0(iseq, pre, nest_rhs, nest_lhs, post, node, state, 1));
5821 state->lhs_level = prev_level;
5822 state->nested = prev_nested;
5823
5824 ADD_SEQ(lhs, nest_rhs);
5825 ADD_SEQ(lhs, nest_lhs);
5826 break;
5827 }
5828 case NODE_CDECL:
5829 if (!RNODE_CDECL(node)->nd_vid) {
5830 /* Special handling only needed for expr::C, not for C */
5831 INSN *iobj;
5832
5833 CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_CDECL)", node));
5834
5835 LINK_ELEMENT *insn_element = LAST_ELEMENT(pre);
5836 iobj = (INSN *)insn_element; /* setconstant insn */
5837 ELEM_REMOVE((LINK_ELEMENT *)get_prev_insn((INSN *)get_prev_insn(iobj)));
5838 ELEM_REMOVE((LINK_ELEMENT *)get_prev_insn(iobj));
5839 ELEM_REMOVE(insn_element);
5840 pre->last = iobj->link.prev;
5841 ADD_ELEM(lhs, (LINK_ELEMENT *)iobj);
5842
5843 if (!add_masgn_lhs_node(state, lhs_pos, node, 1, (INSN *)LAST_ELEMENT(lhs))) {
5844 return COMPILE_NG;
5845 }
5846
5847 ADD_INSN(post, node, pop);
5848 break;
5849 }
5850 /* Fallthrough */
5851 default: {
5852 DECL_ANCHOR(anchor);
5853 INIT_ANCHOR(anchor);
5854 CHECK(COMPILE_POPPED(anchor, "masgn lhs", node));
5855 ELEM_REMOVE(FIRST_ELEMENT(anchor));
5856 ADD_SEQ(lhs, anchor);
5857 }
5858 }
5859
5860 return COMPILE_OK;
5861}
5862
5863static int
5864compile_massign_opt_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *lhsn)
5865{
5866 if (lhsn) {
5867 CHECK(compile_massign_opt_lhs(iseq, ret, RNODE_LIST(lhsn)->nd_next));
5868 CHECK(compile_massign_lhs(iseq, ret, ret, ret, ret, RNODE_LIST(lhsn)->nd_head, NULL, 0));
5869 }
5870 return COMPILE_OK;
5871}
5872
5873static int
5874compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
5875 const NODE *rhsn, const NODE *orig_lhsn)
5876{
5877 VALUE mem[64];
5878 const int memsize = numberof(mem);
5879 int memindex = 0;
5880 int llen = 0, rlen = 0;
5881 int i;
5882 const NODE *lhsn = orig_lhsn;
5883
5884#define MEMORY(v) { \
5885 int i; \
5886 if (memindex == memsize) return 0; \
5887 for (i=0; i<memindex; i++) { \
5888 if (mem[i] == (v)) return 0; \
5889 } \
5890 mem[memindex++] = (v); \
5891}
5892
5893 if (rhsn == 0 || !nd_type_p(rhsn, NODE_LIST)) {
5894 return 0;
5895 }
5896
5897 while (lhsn) {
5898 const NODE *ln = RNODE_LIST(lhsn)->nd_head;
5899 switch (nd_type(ln)) {
5900 case NODE_LASGN:
5901 case NODE_DASGN:
5902 case NODE_IASGN:
5903 case NODE_CVASGN:
5904 MEMORY(get_nd_vid(ln));
5905 break;
5906 default:
5907 return 0;
5908 }
5909 lhsn = RNODE_LIST(lhsn)->nd_next;
5910 llen++;
5911 }
5912
5913 while (rhsn) {
5914 if (llen <= rlen) {
5915 NO_CHECK(COMPILE_POPPED(ret, "masgn val (popped)", RNODE_LIST(rhsn)->nd_head));
5916 }
5917 else {
5918 NO_CHECK(COMPILE(ret, "masgn val", RNODE_LIST(rhsn)->nd_head));
5919 }
5920 rhsn = RNODE_LIST(rhsn)->nd_next;
5921 rlen++;
5922 }
5923
5924 if (llen > rlen) {
5925 for (i=0; i<llen-rlen; i++) {
5926 ADD_INSN(ret, orig_lhsn, putnil);
5927 }
5928 }
5929
5930 compile_massign_opt_lhs(iseq, ret, orig_lhsn);
5931 return 1;
5932}
5933
5934static int
5935compile_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)
5936{
5937 const NODE *rhsn = RNODE_MASGN(node)->nd_value;
5938 const NODE *splatn = RNODE_MASGN(node)->nd_args;
5939 const NODE *lhsn = RNODE_MASGN(node)->nd_head;
5940 const NODE *lhsn_count = lhsn;
5941 int lhs_splat = (splatn && NODE_NAMED_REST_P(splatn)) ? 1 : 0;
5942
5943 int llen = 0;
5944 int lpos = 0;
5945
5946 while (lhsn_count) {
5947 llen++;
5948 lhsn_count = RNODE_LIST(lhsn_count)->nd_next;
5949 }
5950 while (lhsn) {
5951 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, RNODE_LIST(lhsn)->nd_head, state, (llen - lpos) + lhs_splat + state->lhs_level));
5952 lpos++;
5953 lhsn = RNODE_LIST(lhsn)->nd_next;
5954 }
5955
5956 if (lhs_splat) {
5957 if (nd_type_p(splatn, NODE_POSTARG)) {
5958 /*a, b, *r, p1, p2 */
5959 const NODE *postn = RNODE_POSTARG(splatn)->nd_2nd;
5960 const NODE *restn = RNODE_POSTARG(splatn)->nd_1st;
5961 int plen = (int)RNODE_LIST(postn)->as.nd_alen;
5962 int ppos = 0;
5963 int flag = 0x02 | (NODE_NAMED_REST_P(restn) ? 0x01 : 0x00);
5964
5965 ADD_INSN2(lhs, splatn, expandarray, INT2FIX(plen), INT2FIX(flag));
5966
5967 if (NODE_NAMED_REST_P(restn)) {
5968 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, restn, state, 1 + plen + state->lhs_level));
5969 }
5970 while (postn) {
5971 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, RNODE_LIST(postn)->nd_head, state, (plen - ppos) + state->lhs_level));
5972 ppos++;
5973 postn = RNODE_LIST(postn)->nd_next;
5974 }
5975 }
5976 else {
5977 /* a, b, *r */
5978 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, splatn, state, 1 + state->lhs_level));
5979 }
5980 }
5981
5982 if (!state->nested) {
5983 NO_CHECK(COMPILE(rhs, "normal masgn rhs", rhsn));
5984 }
5985
5986 if (!popped) {
5987 ADD_INSN(rhs, node, dup);
5988 }
5989 ADD_INSN2(rhs, node, expandarray, INT2FIX(llen), INT2FIX(lhs_splat));
5990 return COMPILE_OK;
5991}
5992
5993static int
5994compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
5995{
5996 if (!popped || RNODE_MASGN(node)->nd_args || !compile_massign_opt(iseq, ret, RNODE_MASGN(node)->nd_value, RNODE_MASGN(node)->nd_head)) {
5997 struct masgn_state state;
5998 state.lhs_level = popped ? 0 : 1;
5999 state.nested = 0;
6000 state.num_args = 0;
6001 state.first_memo = NULL;
6002 state.last_memo = NULL;
6003
6004 DECL_ANCHOR(pre);
6005 INIT_ANCHOR(pre);
6006 DECL_ANCHOR(rhs);
6007 INIT_ANCHOR(rhs);
6008 DECL_ANCHOR(lhs);
6009 INIT_ANCHOR(lhs);
6010 DECL_ANCHOR(post);
6011 INIT_ANCHOR(post);
6012 int ok = compile_massign0(iseq, pre, rhs, lhs, post, node, &state, popped);
6013
6014 struct masgn_lhs_node *memo = state.first_memo, *tmp_memo;
6015 while (memo) {
6016 VALUE topn_arg = INT2FIX((state.num_args - memo->argn) + memo->lhs_pos);
6017 for (int i = 0; i < memo->num_args; i++) {
6018 INSERT_BEFORE_INSN1(memo->before_insn, nd_line(memo->line_node), nd_node_id(memo->line_node), topn, topn_arg);
6019 }
6020 tmp_memo = memo->next;
6021 free(memo);
6022 memo = tmp_memo;
6023 }
6024 CHECK(ok);
6025
6026 ADD_SEQ(ret, pre);
6027 ADD_SEQ(ret, rhs);
6028 ADD_SEQ(ret, lhs);
6029 if (!popped && state.num_args >= 1) {
6030 /* make sure rhs array is returned before popping */
6031 ADD_INSN1(ret, node, setn, INT2FIX(state.num_args));
6032 }
6033 ADD_SEQ(ret, post);
6034 }
6035 return COMPILE_OK;
6036}
6037
6038static VALUE
6039collect_const_segments(rb_iseq_t *iseq, const NODE *node)
6040{
6041 VALUE arr = rb_ary_new();
6042 for (;;) {
6043 switch (nd_type(node)) {
6044 case NODE_CONST:
6045 rb_ary_unshift(arr, ID2SYM(RNODE_CONST(node)->nd_vid));
6046 RB_OBJ_SET_SHAREABLE(arr);
6047 return arr;
6048 case NODE_COLON3:
6049 rb_ary_unshift(arr, ID2SYM(RNODE_COLON3(node)->nd_mid));
6050 rb_ary_unshift(arr, ID2SYM(idNULL));
6051 RB_OBJ_SET_SHAREABLE(arr);
6052 return arr;
6053 case NODE_COLON2:
6054 rb_ary_unshift(arr, ID2SYM(RNODE_COLON2(node)->nd_mid));
6055 node = RNODE_COLON2(node)->nd_head;
6056 break;
6057 default:
6058 return Qfalse;
6059 }
6060 }
6061}
6062
6063static int
6064compile_const_prefix(rb_iseq_t *iseq, const NODE *const node,
6065 LINK_ANCHOR *const pref, LINK_ANCHOR *const body)
6066{
6067 switch (nd_type(node)) {
6068 case NODE_CONST:
6069 debugi("compile_const_prefix - colon", RNODE_CONST(node)->nd_vid);
6070 ADD_INSN1(body, node, putobject, Qtrue);
6071 ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_CONST(node)->nd_vid));
6072 break;
6073 case NODE_COLON3:
6074 debugi("compile_const_prefix - colon3", RNODE_COLON3(node)->nd_mid);
6075 ADD_INSN(body, node, pop);
6076 ADD_INSN1(body, node, putobject, rb_cObject);
6077 ADD_INSN1(body, node, putobject, Qtrue);
6078 ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_COLON3(node)->nd_mid));
6079 break;
6080 case NODE_COLON2:
6081 CHECK(compile_const_prefix(iseq, RNODE_COLON2(node)->nd_head, pref, body));
6082 debugi("compile_const_prefix - colon2", RNODE_COLON2(node)->nd_mid);
6083 ADD_INSN1(body, node, putobject, Qfalse);
6084 ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_COLON2(node)->nd_mid));
6085 break;
6086 default:
6087 CHECK(COMPILE(pref, "const colon2 prefix", node));
6088 break;
6089 }
6090 return COMPILE_OK;
6091}
6092
6093static int
6094cpath_const_p(const NODE *node)
6095{
6096 switch (nd_type(node)) {
6097 case NODE_CONST:
6098 case NODE_COLON3:
6099 return TRUE;
6100 case NODE_COLON2:
6101 if (RNODE_COLON2(node)->nd_head) {
6102 return cpath_const_p(RNODE_COLON2(node)->nd_head);
6103 }
6104 return TRUE;
6105 default:
6106 return FALSE;
6107 }
6108}
6109
6110static int
6111compile_cpath(LINK_ANCHOR *const ret, rb_iseq_t *iseq, const NODE *cpath)
6112{
6113 if (nd_type_p(cpath, NODE_COLON3)) {
6114 /* toplevel class ::Foo */
6115 ADD_INSN1(ret, cpath, putobject, rb_cObject);
6116 return VM_DEFINECLASS_FLAG_SCOPED;
6117 }
6118 else if (nd_type_p(cpath, NODE_COLON2) && RNODE_COLON2(cpath)->nd_head) {
6119 /* Bar::Foo or expr::Foo */
6120 NO_CHECK(COMPILE(ret, "nd_else->nd_head", RNODE_COLON2(cpath)->nd_head));
6121 int flags = VM_DEFINECLASS_FLAG_SCOPED;
6122 if (!cpath_const_p(RNODE_COLON2(cpath)->nd_head)) {
6123 flags |= VM_DEFINECLASS_FLAG_DYNAMIC_CREF;
6124 }
6125 return flags;
6126 }
6127 else {
6128 /* class at cbase Foo */
6129 ADD_INSN1(ret, cpath, putspecialobject,
6130 INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
6131 return 0;
6132 }
6133}
6134
6135static inline int
6136private_recv_p(const NODE *node)
6137{
6138 NODE *recv = get_nd_recv(node);
6139 if (recv && nd_type_p(recv, NODE_SELF)) {
6140 return RNODE_SELF(recv)->nd_state != 0;
6141 }
6142 return 0;
6143}
6144
6145static void
6146defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
6147 const NODE *const node, LABEL **lfinish, VALUE needstr, bool ignore);
6148
6149static int
6150compile_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);
6151
6152static void
6153defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
6154 const NODE *const node, LABEL **lfinish, VALUE needstr,
6155 bool keep_result)
6156{
6157 enum defined_type expr_type = DEFINED_NOT_DEFINED;
6158 enum node_type type;
6159 const int line = nd_line(node);
6160 const NODE *line_node = node;
6161
6162 switch (type = nd_type(node)) {
6163
6164 /* easy literals */
6165 case NODE_NIL:
6166 expr_type = DEFINED_NIL;
6167 break;
6168 case NODE_SELF:
6169 expr_type = DEFINED_SELF;
6170 break;
6171 case NODE_TRUE:
6172 expr_type = DEFINED_TRUE;
6173 break;
6174 case NODE_FALSE:
6175 expr_type = DEFINED_FALSE;
6176 break;
6177
6178 case NODE_HASH:
6179 case NODE_LIST:{
6180 const NODE *vals = (nd_type(node) == NODE_HASH) ? RNODE_HASH(node)->nd_head : node;
6181
6182 if (vals) {
6183 do {
6184 if (RNODE_LIST(vals)->nd_head) {
6185 defined_expr0(iseq, ret, RNODE_LIST(vals)->nd_head, lfinish, Qfalse, false);
6186
6187 if (!lfinish[1]) {
6188 lfinish[1] = NEW_LABEL(line);
6189 }
6190 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6191 }
6192 } while ((vals = RNODE_LIST(vals)->nd_next) != NULL);
6193 }
6194 }
6195 /* fall through */
6196 case NODE_STR:
6197 case NODE_SYM:
6198 case NODE_REGX:
6199 case NODE_LINE:
6200 case NODE_FILE:
6201 case NODE_ENCODING:
6202 case NODE_INTEGER:
6203 case NODE_FLOAT:
6204 case NODE_RATIONAL:
6205 case NODE_IMAGINARY:
6206 case NODE_ZLIST:
6207 case NODE_AND:
6208 case NODE_OR:
6209 default:
6210 expr_type = DEFINED_EXPR;
6211 break;
6212
6213 case NODE_SPLAT:
6214 defined_expr0(iseq, ret, RNODE_LIST(node)->nd_head, lfinish, Qfalse, false);
6215 if (!lfinish[1]) {
6216 lfinish[1] = NEW_LABEL(line);
6217 }
6218 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6219 expr_type = DEFINED_EXPR;
6220 break;
6221
6222 /* variables */
6223 case NODE_LVAR:
6224 case NODE_DVAR:
6225 expr_type = DEFINED_LVAR;
6226 break;
6227
6228#define PUSH_VAL(type) (needstr == Qfalse ? Qtrue : rb_iseq_defined_string(type))
6229 case NODE_IVAR:
6230 ADD_INSN3(ret, line_node, definedivar,
6231 ID2SYM(RNODE_IVAR(node)->nd_vid), get_ivar_ic_value(iseq,RNODE_IVAR(node)->nd_vid), PUSH_VAL(DEFINED_IVAR));
6232 return;
6233
6234 case NODE_GVAR:
6235 ADD_INSN(ret, line_node, putnil);
6236 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_GVAR),
6237 ID2SYM(RNODE_GVAR(node)->nd_vid), PUSH_VAL(DEFINED_GVAR));
6238 return;
6239
6240 case NODE_CVAR:
6241 ADD_INSN(ret, line_node, putnil);
6242 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CVAR),
6243 ID2SYM(RNODE_CVAR(node)->nd_vid), PUSH_VAL(DEFINED_CVAR));
6244 return;
6245
6246 case NODE_CONST:
6247 ADD_INSN(ret, line_node, putnil);
6248 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST),
6249 ID2SYM(RNODE_CONST(node)->nd_vid), PUSH_VAL(DEFINED_CONST));
6250 return;
6251 case NODE_COLON2:
6252 if (!lfinish[1]) {
6253 lfinish[1] = NEW_LABEL(line);
6254 }
6255 defined_expr0(iseq, ret, RNODE_COLON2(node)->nd_head, lfinish, Qfalse, false);
6256 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6257 NO_CHECK(COMPILE(ret, "defined/colon2#nd_head", RNODE_COLON2(node)->nd_head));
6258
6259 if (rb_is_const_id(RNODE_COLON2(node)->nd_mid)) {
6260 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST_FROM),
6261 ID2SYM(RNODE_COLON2(node)->nd_mid), PUSH_VAL(DEFINED_CONST));
6262 }
6263 else {
6264 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD),
6265 ID2SYM(RNODE_COLON2(node)->nd_mid), PUSH_VAL(DEFINED_METHOD));
6266 }
6267 return;
6268 case NODE_COLON3:
6269 ADD_INSN1(ret, line_node, putobject, rb_cObject);
6270 ADD_INSN3(ret, line_node, defined,
6271 INT2FIX(DEFINED_CONST_FROM), ID2SYM(RNODE_COLON3(node)->nd_mid), PUSH_VAL(DEFINED_CONST));
6272 return;
6273
6274 /* method dispatch */
6275 case NODE_CALL:
6276 case NODE_OPCALL:
6277 case NODE_VCALL:
6278 case NODE_FCALL:
6279 case NODE_ATTRASGN:{
6280 const int explicit_receiver =
6281 (type == NODE_CALL || type == NODE_OPCALL ||
6282 (type == NODE_ATTRASGN && !private_recv_p(node)));
6283
6284 if (get_nd_args(node) || explicit_receiver) {
6285 if (!lfinish[1]) {
6286 lfinish[1] = NEW_LABEL(line);
6287 }
6288 if (!lfinish[2]) {
6289 lfinish[2] = NEW_LABEL(line);
6290 }
6291 }
6292 if (get_nd_args(node)) {
6293 defined_expr0(iseq, ret, get_nd_args(node), lfinish, Qfalse, false);
6294 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6295 }
6296 if (explicit_receiver) {
6297 defined_expr0(iseq, ret, get_nd_recv(node), lfinish, Qfalse, true);
6298 switch (nd_type(get_nd_recv(node))) {
6299 case NODE_CALL:
6300 case NODE_OPCALL:
6301 case NODE_VCALL:
6302 case NODE_FCALL:
6303 case NODE_ATTRASGN:
6304 ADD_INSNL(ret, line_node, branchunless, lfinish[2]);
6305 compile_call(iseq, ret, get_nd_recv(node), nd_type(get_nd_recv(node)), line_node, 0, true);
6306 break;
6307 default:
6308 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6309 NO_CHECK(COMPILE(ret, "defined/recv", get_nd_recv(node)));
6310 break;
6311 }
6312 if (keep_result) {
6313 ADD_INSN(ret, line_node, dup);
6314 }
6315 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD),
6316 ID2SYM(get_node_call_nd_mid(node)), PUSH_VAL(DEFINED_METHOD));
6317 }
6318 else {
6319 ADD_INSN(ret, line_node, putself);
6320 if (keep_result) {
6321 ADD_INSN(ret, line_node, dup);
6322 }
6323 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_FUNC),
6324 ID2SYM(get_node_call_nd_mid(node)), PUSH_VAL(DEFINED_METHOD));
6325 }
6326 return;
6327 }
6328
6329 case NODE_YIELD:
6330 ADD_INSN(ret, line_node, putnil);
6331 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_YIELD), 0,
6332 PUSH_VAL(DEFINED_YIELD));
6333 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
6334 return;
6335
6336 case NODE_BACK_REF:
6337 case NODE_NTH_REF:
6338 ADD_INSN(ret, line_node, putnil);
6339 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_REF),
6340 INT2FIX((RNODE_BACK_REF(node)->nd_nth << 1) | (type == NODE_BACK_REF)),
6341 PUSH_VAL(DEFINED_GVAR));
6342 return;
6343
6344 case NODE_SUPER:
6345 case NODE_ZSUPER:
6346 ADD_INSN(ret, line_node, putnil);
6347 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_ZSUPER), 0,
6348 PUSH_VAL(DEFINED_ZSUPER));
6349 return;
6350
6351#undef PUSH_VAL
6352 case NODE_OP_ASGN1:
6353 case NODE_OP_ASGN2:
6354 case NODE_OP_ASGN_OR:
6355 case NODE_OP_ASGN_AND:
6356 case NODE_MASGN:
6357 case NODE_LASGN:
6358 case NODE_DASGN:
6359 case NODE_GASGN:
6360 case NODE_IASGN:
6361 case NODE_CDECL:
6362 case NODE_CVASGN:
6363 case NODE_OP_CDECL:
6364 expr_type = DEFINED_ASGN;
6365 break;
6366 }
6367
6368 RUBY_ASSERT(expr_type != DEFINED_NOT_DEFINED);
6369
6370 if (needstr != Qfalse) {
6371 VALUE str = rb_iseq_defined_string(expr_type);
6372 ADD_INSN1(ret, line_node, putobject, str);
6373 }
6374 else {
6375 ADD_INSN1(ret, line_node, putobject, Qtrue);
6376 }
6377}
6378
6379static void
6380build_defined_rescue_iseq(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const void *unused)
6381{
6382 ADD_SYNTHETIC_INSN(ret, 0, -1, putnil);
6383 iseq_set_exception_local_table(iseq);
6384}
6385
6386static void
6387defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
6388 const NODE *const node, LABEL **lfinish, VALUE needstr, bool ignore)
6389{
6390 LINK_ELEMENT *lcur = ret->last;
6391 defined_expr0(iseq, ret, node, lfinish, needstr, false);
6392 if (lfinish[1]) {
6393 int line = nd_line(node);
6394 LABEL *lstart = NEW_LABEL(line);
6395 LABEL *lend = NEW_LABEL(line);
6396 const rb_iseq_t *rescue;
6398 rb_iseq_new_with_callback_new_callback(build_defined_rescue_iseq, NULL);
6399 rescue = NEW_CHILD_ISEQ_WITH_CALLBACK(ifunc,
6400 rb_str_concat(rb_str_new2("defined guard in "),
6401 ISEQ_BODY(iseq)->location.label),
6402 ISEQ_TYPE_RESCUE, 0);
6403 lstart->rescued = LABEL_RESCUE_BEG;
6404 lend->rescued = LABEL_RESCUE_END;
6405 APPEND_LABEL(ret, lcur, lstart);
6406 ADD_LABEL(ret, lend);
6407 if (!ignore) {
6408 ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
6409 }
6410 }
6411}
6412
6413static int
6414compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr, bool ignore)
6415{
6416 const int line = nd_line(node);
6417 const NODE *line_node = node;
6418 if (!RNODE_DEFINED(node)->nd_head) {
6419 VALUE str = rb_iseq_defined_string(DEFINED_NIL);
6420 ADD_INSN1(ret, line_node, putobject, str);
6421 }
6422 else {
6423 LABEL *lfinish[3];
6424 LINK_ELEMENT *last = ret->last;
6425 lfinish[0] = NEW_LABEL(line);
6426 lfinish[1] = 0;
6427 lfinish[2] = 0;
6428 defined_expr(iseq, ret, RNODE_DEFINED(node)->nd_head, lfinish, needstr, ignore);
6429 if (lfinish[1]) {
6430 ELEM_INSERT_NEXT(last, &new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(putnil), 0)->link);
6431 ADD_INSN(ret, line_node, swap);
6432 if (lfinish[2]) {
6433 ADD_LABEL(ret, lfinish[2]);
6434 }
6435 ADD_INSN(ret, line_node, pop);
6436 ADD_LABEL(ret, lfinish[1]);
6437 }
6438 ADD_LABEL(ret, lfinish[0]);
6439 }
6440 return COMPILE_OK;
6441}
6442
6443static VALUE
6444make_name_for_block(const rb_iseq_t *orig_iseq)
6445{
6446 int level = 1;
6447 const rb_iseq_t *iseq = orig_iseq;
6448
6449 if (ISEQ_BODY(orig_iseq)->parent_iseq != 0) {
6450 while (ISEQ_BODY(orig_iseq)->local_iseq != iseq) {
6451 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_BLOCK) {
6452 level++;
6453 }
6454 iseq = ISEQ_BODY(iseq)->parent_iseq;
6455 }
6456 }
6457
6458 if (level == 1) {
6459 return rb_sprintf("block in %"PRIsVALUE, ISEQ_BODY(iseq)->location.label);
6460 }
6461 else {
6462 return rb_sprintf("block (%d levels) in %"PRIsVALUE, level, ISEQ_BODY(iseq)->location.label);
6463 }
6464}
6465
6466static void
6467push_ensure_entry(rb_iseq_t *iseq,
6469 struct ensure_range *er, const void *const node)
6470{
6471 enl->ensure_node = node;
6472 enl->prev = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack; /* prev */
6473 enl->erange = er;
6474 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl;
6475}
6476
6477static void
6478add_ensure_range(rb_iseq_t *iseq, struct ensure_range *erange,
6479 LABEL *lstart, LABEL *lend)
6480{
6481 struct ensure_range *ne =
6482 compile_data_alloc_type(iseq, struct ensure_range);
6483
6484 while (erange->next != 0) {
6485 erange = erange->next;
6486 }
6487 ne->next = 0;
6488 ne->begin = lend;
6489 ne->end = erange->end;
6490 erange->end = lstart;
6491
6492 erange->next = ne;
6493}
6494
6495static bool
6496can_add_ensure_iseq(const rb_iseq_t *iseq)
6497{
6499 if (ISEQ_COMPILE_DATA(iseq)->in_rescue && (e = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack) != NULL) {
6500 while (e) {
6501 if (e->ensure_node) return false;
6502 e = e->prev;
6503 }
6504 }
6505 return true;
6506}
6507
6508static void
6509add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return)
6510{
6511 RUBY_ASSERT(can_add_ensure_iseq(iseq));
6512
6514 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
6515 struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
6516 DECL_ANCHOR(ensure);
6517
6518 INIT_ANCHOR(ensure);
6519 while (enlp) {
6520 if (enlp->erange != NULL) {
6521 DECL_ANCHOR(ensure_part);
6522 LABEL *lstart = NEW_LABEL(0);
6523 LABEL *lend = NEW_LABEL(0);
6524 INIT_ANCHOR(ensure_part);
6525
6526 add_ensure_range(iseq, enlp->erange, lstart, lend);
6527
6528 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
6529 ADD_LABEL(ensure_part, lstart);
6530 NO_CHECK(COMPILE_POPPED(ensure_part, "ensure part", enlp->ensure_node));
6531 ADD_LABEL(ensure_part, lend);
6532 ADD_SEQ(ensure, ensure_part);
6533 }
6534 else {
6535 if (!is_return) {
6536 break;
6537 }
6538 }
6539 enlp = enlp->prev;
6540 }
6541 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = prev_enlp;
6542 ADD_SEQ(ret, ensure);
6543}
6544
6545#if RUBY_DEBUG
6546static int
6547check_keyword(const NODE *node)
6548{
6549 /* This check is essentially a code clone of compile_keyword_arg. */
6550
6551 if (nd_type_p(node, NODE_LIST)) {
6552 while (RNODE_LIST(node)->nd_next) {
6553 node = RNODE_LIST(node)->nd_next;
6554 }
6555 node = RNODE_LIST(node)->nd_head;
6556 }
6557
6558 return keyword_node_p(node);
6559}
6560#endif
6561
6562static bool
6563keyword_node_single_splat_p(NODE *kwnode)
6564{
6565 RUBY_ASSERT(keyword_node_p(kwnode));
6566
6567 NODE *node = RNODE_HASH(kwnode)->nd_head;
6568 return RNODE_LIST(node)->nd_head == NULL &&
6569 RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next == NULL;
6570}
6571
6572static void
6573compile_single_keyword_splat_mutable(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
6574 NODE *kwnode, unsigned int *flag_ptr)
6575{
6576 *flag_ptr |= VM_CALL_KW_SPLAT_MUT;
6577 ADD_INSN1(args, argn, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6578 ADD_INSN1(args, argn, newhash, INT2FIX(0));
6579 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6580 ADD_SEND(args, argn, id_core_hash_merge_kwd, INT2FIX(2));
6581}
6582
6583#define SPLATARRAY_FALSE 0
6584#define SPLATARRAY_TRUE 1
6585#define DUP_SINGLE_KW_SPLAT 2
6586
6587static int
6588setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
6589 unsigned int *dup_rest, unsigned int *flag_ptr, struct rb_callinfo_kwarg **kwarg_ptr)
6590{
6591 if (!argn) return 0;
6592
6593 NODE *kwnode = NULL;
6594
6595 switch (nd_type(argn)) {
6596 case NODE_LIST: {
6597 // f(x, y, z)
6598 int len = compile_args(iseq, args, argn, &kwnode);
6599 RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_ARGS_SPLAT) == 0);
6600
6601 if (kwnode) {
6602 if (compile_keyword_arg(iseq, args, kwnode, kwarg_ptr, flag_ptr)) {
6603 len -= 1;
6604 }
6605 else {
6606 if (keyword_node_single_splat_p(kwnode) && (*dup_rest & DUP_SINGLE_KW_SPLAT)) {
6607 compile_single_keyword_splat_mutable(iseq, args, argn, kwnode, flag_ptr);
6608 }
6609 else {
6610 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6611 }
6612 }
6613 }
6614
6615 return len;
6616 }
6617 case NODE_SPLAT: {
6618 // f(*a)
6619 NO_CHECK(COMPILE(args, "args (splat)", RNODE_SPLAT(argn)->nd_head));
6620 ADD_INSN1(args, argn, splatarray, RBOOL(*dup_rest & SPLATARRAY_TRUE));
6621 if (*dup_rest & SPLATARRAY_TRUE) *dup_rest &= ~SPLATARRAY_TRUE;
6622 if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT;
6623 RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_KW_SPLAT) == 0);
6624 return 1;
6625 }
6626 case NODE_ARGSCAT: {
6627 if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT;
6628 int argc = setup_args_core(iseq, args, RNODE_ARGSCAT(argn)->nd_head, dup_rest, NULL, NULL);
6629 bool args_pushed = false;
6630
6631 if (nd_type_p(RNODE_ARGSCAT(argn)->nd_body, NODE_LIST)) {
6632 int rest_len = compile_args(iseq, args, RNODE_ARGSCAT(argn)->nd_body, &kwnode);
6633 if (kwnode) rest_len--;
6634 ADD_INSN1(args, argn, pushtoarray, INT2FIX(rest_len));
6635 args_pushed = true;
6636 }
6637 else {
6638 RUBY_ASSERT(!check_keyword(RNODE_ARGSCAT(argn)->nd_body));
6639 NO_CHECK(COMPILE(args, "args (cat: splat)", RNODE_ARGSCAT(argn)->nd_body));
6640 }
6641
6642 if (nd_type_p(RNODE_ARGSCAT(argn)->nd_head, NODE_LIST)) {
6643 ADD_INSN1(args, argn, splatarray, RBOOL(*dup_rest & SPLATARRAY_TRUE));
6644 if (*dup_rest & SPLATARRAY_TRUE) *dup_rest &= ~SPLATARRAY_TRUE;
6645 argc += 1;
6646 }
6647 else if (!args_pushed) {
6648 ADD_INSN(args, argn, concattoarray);
6649 }
6650
6651 // f(..., *a, ..., k1:1, ...) #=> f(..., *[*a, ...], **{k1:1, ...})
6652 if (kwnode) {
6653 // kwsplat
6654 *flag_ptr |= VM_CALL_KW_SPLAT;
6655 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6656 argc += 1;
6657 }
6658
6659 return argc;
6660 }
6661 case NODE_ARGSPUSH: {
6662 if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT;
6663 int argc = setup_args_core(iseq, args, RNODE_ARGSPUSH(argn)->nd_head, dup_rest, NULL, NULL);
6664
6665 if (nd_type_p(RNODE_ARGSPUSH(argn)->nd_body, NODE_LIST)) {
6666 int rest_len = compile_args(iseq, args, RNODE_ARGSPUSH(argn)->nd_body, &kwnode);
6667 if (kwnode) rest_len--;
6668 ADD_INSN1(args, argn, newarray, INT2FIX(rest_len));
6669 ADD_INSN1(args, argn, pushtoarray, INT2FIX(1));
6670 }
6671 else {
6672 if (keyword_node_p(RNODE_ARGSPUSH(argn)->nd_body)) {
6673 kwnode = RNODE_ARGSPUSH(argn)->nd_body;
6674 }
6675 else {
6676 NO_CHECK(COMPILE(args, "args (cat: splat)", RNODE_ARGSPUSH(argn)->nd_body));
6677 ADD_INSN1(args, argn, pushtoarray, INT2FIX(1));
6678 }
6679 }
6680
6681 if (kwnode) {
6682 // f(*a, k:1)
6683 *flag_ptr |= VM_CALL_KW_SPLAT;
6684 if (!keyword_node_single_splat_p(kwnode)) {
6685 *flag_ptr |= VM_CALL_KW_SPLAT_MUT;
6686 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6687 }
6688 else if (*dup_rest & DUP_SINGLE_KW_SPLAT) {
6689 compile_single_keyword_splat_mutable(iseq, args, argn, kwnode, flag_ptr);
6690 }
6691 else {
6692 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6693 }
6694 argc += 1;
6695 }
6696
6697 return argc;
6698 }
6699 default: {
6700 UNKNOWN_NODE("setup_arg", argn, Qnil);
6701 }
6702 }
6703}
6704
6705static void
6706setup_args_splat_mut(unsigned int *flag, int dup_rest, int initial_dup_rest)
6707{
6708 if ((*flag & VM_CALL_ARGS_SPLAT) && dup_rest != initial_dup_rest) {
6709 *flag |= VM_CALL_ARGS_SPLAT_MUT;
6710 }
6711}
6712
6713static bool
6714setup_args_dup_rest_p(const NODE *argn)
6715{
6716 switch(nd_type(argn)) {
6717 case NODE_LVAR:
6718 case NODE_DVAR:
6719 case NODE_GVAR:
6720 case NODE_IVAR:
6721 case NODE_CVAR:
6722 case NODE_CONST:
6723 case NODE_COLON3:
6724 case NODE_INTEGER:
6725 case NODE_FLOAT:
6726 case NODE_RATIONAL:
6727 case NODE_IMAGINARY:
6728 case NODE_STR:
6729 case NODE_SYM:
6730 case NODE_REGX:
6731 case NODE_SELF:
6732 case NODE_NIL:
6733 case NODE_TRUE:
6734 case NODE_FALSE:
6735 case NODE_LAMBDA:
6736 case NODE_NTH_REF:
6737 case NODE_BACK_REF:
6738 return false;
6739 case NODE_COLON2:
6740 return setup_args_dup_rest_p(RNODE_COLON2(argn)->nd_head);
6741 case NODE_LIST:
6742 while (argn) {
6743 if (setup_args_dup_rest_p(RNODE_LIST(argn)->nd_head)) {
6744 return true;
6745 }
6746 argn = RNODE_LIST(argn)->nd_next;
6747 }
6748 return false;
6749 default:
6750 return true;
6751 }
6752}
6753
6754static VALUE
6755setup_args(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
6756 unsigned int *flag, struct rb_callinfo_kwarg **keywords)
6757{
6758 VALUE ret;
6759 unsigned int dup_rest = SPLATARRAY_TRUE, initial_dup_rest;
6760
6761 if (argn) {
6762 const NODE *check_arg = nd_type_p(argn, NODE_BLOCK_PASS) ?
6763 RNODE_BLOCK_PASS(argn)->nd_head : argn;
6764
6765 if (check_arg) {
6766 switch(nd_type(check_arg)) {
6767 case(NODE_SPLAT):
6768 // avoid caller side array allocation for f(*arg)
6769 dup_rest = SPLATARRAY_FALSE;
6770 break;
6771 case(NODE_ARGSCAT):
6772 // avoid caller side array allocation for f(1, *arg)
6773 dup_rest = !nd_type_p(RNODE_ARGSCAT(check_arg)->nd_head, NODE_LIST);
6774 break;
6775 case(NODE_ARGSPUSH):
6776 // avoid caller side array allocation for f(*arg, **hash) and f(1, *arg, **hash)
6777 dup_rest = !((nd_type_p(RNODE_ARGSPUSH(check_arg)->nd_head, NODE_SPLAT) ||
6778 (nd_type_p(RNODE_ARGSPUSH(check_arg)->nd_head, NODE_ARGSCAT) &&
6779 nd_type_p(RNODE_ARGSCAT(RNODE_ARGSPUSH(check_arg)->nd_head)->nd_head, NODE_LIST))) &&
6780 nd_type_p(RNODE_ARGSPUSH(check_arg)->nd_body, NODE_HASH) &&
6781 !RNODE_HASH(RNODE_ARGSPUSH(check_arg)->nd_body)->nd_brace);
6782
6783 if (dup_rest == SPLATARRAY_FALSE) {
6784 // require allocation for keyword key/value/splat that may modify splatted argument
6785 NODE *node = RNODE_HASH(RNODE_ARGSPUSH(check_arg)->nd_body)->nd_head;
6786 while (node) {
6787 NODE *key_node = RNODE_LIST(node)->nd_head;
6788 if (key_node && setup_args_dup_rest_p(key_node)) {
6789 dup_rest = SPLATARRAY_TRUE;
6790 break;
6791 }
6792
6793 node = RNODE_LIST(node)->nd_next;
6794 NODE *value_node = RNODE_LIST(node)->nd_head;
6795 if (setup_args_dup_rest_p(value_node)) {
6796 dup_rest = SPLATARRAY_TRUE;
6797 break;
6798 }
6799
6800 node = RNODE_LIST(node)->nd_next;
6801 }
6802 }
6803 break;
6804 default:
6805 break;
6806 }
6807 }
6808
6809 if (check_arg != argn && setup_args_dup_rest_p(RNODE_BLOCK_PASS(argn)->nd_body)) {
6810 // for block pass that may modify splatted argument, dup rest and kwrest if given
6811 dup_rest = SPLATARRAY_TRUE | DUP_SINGLE_KW_SPLAT;
6812 }
6813 }
6814 initial_dup_rest = dup_rest;
6815
6816 if (argn && nd_type_p(argn, NODE_BLOCK_PASS)) {
6817 DECL_ANCHOR(arg_block);
6818 INIT_ANCHOR(arg_block);
6819
6820 if (RNODE_BLOCK_PASS(argn)->forwarding && ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
6821 int idx = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size;// - get_local_var_idx(iseq, idDot3);
6822
6823 RUBY_ASSERT(nd_type_p(RNODE_BLOCK_PASS(argn)->nd_head, NODE_ARGSPUSH));
6824 const NODE * arg_node =
6825 RNODE_ARGSPUSH(RNODE_BLOCK_PASS(argn)->nd_head)->nd_head;
6826
6827 int argc = 0;
6828
6829 // Only compile leading args:
6830 // foo(x, y, ...)
6831 // ^^^^
6832 if (nd_type_p(arg_node, NODE_ARGSCAT)) {
6833 argc += setup_args_core(iseq, args, RNODE_ARGSCAT(arg_node)->nd_head, &dup_rest, flag, keywords);
6834 }
6835
6836 *flag |= VM_CALL_FORWARDING;
6837
6838 ADD_GETLOCAL(args, argn, idx, get_lvar_level(iseq));
6839 setup_args_splat_mut(flag, dup_rest, initial_dup_rest);
6840 return INT2FIX(argc);
6841 }
6842 else {
6843 *flag |= VM_CALL_ARGS_BLOCKARG;
6844
6845 NO_CHECK(COMPILE(arg_block, "block", RNODE_BLOCK_PASS(argn)->nd_body));
6846 }
6847
6848 if (LIST_INSN_SIZE_ONE(arg_block)) {
6849 LINK_ELEMENT *elem = FIRST_ELEMENT(arg_block);
6850 if (IS_INSN(elem)) {
6851 INSN *iobj = (INSN *)elem;
6852 if (iobj->insn_id == BIN(getblockparam)) {
6853 iobj->insn_id = BIN(getblockparamproxy);
6854 }
6855 }
6856 }
6857 ret = INT2FIX(setup_args_core(iseq, args, RNODE_BLOCK_PASS(argn)->nd_head, &dup_rest, flag, keywords));
6858 ADD_SEQ(args, arg_block);
6859 }
6860 else {
6861 ret = INT2FIX(setup_args_core(iseq, args, argn, &dup_rest, flag, keywords));
6862 }
6863 setup_args_splat_mut(flag, dup_rest, initial_dup_rest);
6864 return ret;
6865}
6866
6867static void
6868build_postexe_iseq(rb_iseq_t *iseq, LINK_ANCHOR *ret, const void *ptr)
6869{
6870 const NODE *body = ptr;
6871 int line = nd_line(body);
6872 VALUE argc = INT2FIX(0);
6873 const rb_iseq_t *block = NEW_CHILD_ISEQ(body, make_name_for_block(ISEQ_BODY(iseq)->parent_iseq), ISEQ_TYPE_BLOCK, line);
6874
6875 ADD_INSN1(ret, body, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6876 ADD_CALL_WITH_BLOCK(ret, body, id_core_set_postexe, argc, block);
6877 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block);
6878 iseq_set_local_table(iseq, 0, 0);
6879}
6880
6881static void
6882compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
6883{
6884 const NODE *vars;
6885 LINK_ELEMENT *last;
6886 int line = nd_line(node);
6887 const NODE *line_node = node;
6888 LABEL *fail_label = NEW_LABEL(line), *end_label = NEW_LABEL(line);
6889
6890#if !(defined(NAMED_CAPTURE_BY_SVAR) && NAMED_CAPTURE_BY_SVAR-0)
6891 ADD_INSN1(ret, line_node, getglobal, ID2SYM(idBACKREF));
6892#else
6893 ADD_INSN2(ret, line_node, getspecial, INT2FIX(1) /* '~' */, INT2FIX(0));
6894#endif
6895 ADD_INSN(ret, line_node, dup);
6896 ADD_INSNL(ret, line_node, branchunless, fail_label);
6897
6898 for (vars = node; vars; vars = RNODE_BLOCK(vars)->nd_next) {
6899 INSN *cap;
6900 if (RNODE_BLOCK(vars)->nd_next) {
6901 ADD_INSN(ret, line_node, dup);
6902 }
6903 last = ret->last;
6904 NO_CHECK(COMPILE_POPPED(ret, "capture", RNODE_BLOCK(vars)->nd_head));
6905 last = last->next; /* putobject :var */
6906 cap = new_insn_send(iseq, nd_line(line_node), nd_node_id(line_node), idAREF, INT2FIX(1),
6907 NULL, INT2FIX(0), NULL);
6908 ELEM_INSERT_PREV(last->next, (LINK_ELEMENT *)cap);
6909#if !defined(NAMED_CAPTURE_SINGLE_OPT) || NAMED_CAPTURE_SINGLE_OPT-0
6910 if (!RNODE_BLOCK(vars)->nd_next && vars == node) {
6911 /* only one name */
6912 DECL_ANCHOR(nom);
6913
6914 INIT_ANCHOR(nom);
6915 ADD_INSNL(nom, line_node, jump, end_label);
6916 ADD_LABEL(nom, fail_label);
6917# if 0 /* $~ must be MatchData or nil */
6918 ADD_INSN(nom, line_node, pop);
6919 ADD_INSN(nom, line_node, putnil);
6920# endif
6921 ADD_LABEL(nom, end_label);
6922 (nom->last->next = cap->link.next)->prev = nom->last;
6923 (cap->link.next = nom->anchor.next)->prev = &cap->link;
6924 return;
6925 }
6926#endif
6927 }
6928 ADD_INSNL(ret, line_node, jump, end_label);
6929 ADD_LABEL(ret, fail_label);
6930 ADD_INSN(ret, line_node, pop);
6931 for (vars = node; vars; vars = RNODE_BLOCK(vars)->nd_next) {
6932 last = ret->last;
6933 NO_CHECK(COMPILE_POPPED(ret, "capture", RNODE_BLOCK(vars)->nd_head));
6934 last = last->next; /* putobject :var */
6935 ((INSN*)last)->insn_id = BIN(putnil);
6936 ((INSN*)last)->operand_size = 0;
6937 }
6938 ADD_LABEL(ret, end_label);
6939}
6940
6941static int
6942optimizable_range_item_p(const NODE *n)
6943{
6944 if (!n) return FALSE;
6945 switch (nd_type(n)) {
6946 case NODE_LINE:
6947 return TRUE;
6948 case NODE_INTEGER:
6949 return TRUE;
6950 case NODE_NIL:
6951 return TRUE;
6952 default:
6953 return FALSE;
6954 }
6955}
6956
6957static VALUE
6958optimized_range_item(const NODE *n)
6959{
6960 switch (nd_type(n)) {
6961 case NODE_LINE:
6962 return rb_node_line_lineno_val(n);
6963 case NODE_INTEGER:
6964 return rb_node_integer_literal_val(n);
6965 case NODE_FLOAT:
6966 return rb_node_float_literal_val(n);
6967 case NODE_RATIONAL:
6968 return rb_node_rational_literal_val(n);
6969 case NODE_IMAGINARY:
6970 return rb_node_imaginary_literal_val(n);
6971 case NODE_NIL:
6972 return Qnil;
6973 default:
6974 rb_bug("unexpected node: %s", ruby_node_name(nd_type(n)));
6975 }
6976}
6977
6978static int
6979compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
6980{
6981 const NODE *const node_body = type == NODE_IF ? RNODE_IF(node)->nd_body : RNODE_UNLESS(node)->nd_else;
6982 const NODE *const node_else = type == NODE_IF ? RNODE_IF(node)->nd_else : RNODE_UNLESS(node)->nd_body;
6983
6984 const int line = nd_line(node);
6985 const NODE *line_node = node;
6986 DECL_ANCHOR(cond_seq);
6987 LABEL *then_label, *else_label, *end_label;
6988 VALUE branches = Qfalse;
6989
6990 INIT_ANCHOR(cond_seq);
6991 then_label = NEW_LABEL(line);
6992 else_label = NEW_LABEL(line);
6993 end_label = 0;
6994
6995 NODE *cond = RNODE_IF(node)->nd_cond;
6996 if (nd_type(cond) == NODE_BLOCK) {
6997 cond = RNODE_BLOCK(cond)->nd_head;
6998 }
6999
7000 CHECK(compile_branch_condition(iseq, cond_seq, cond, then_label, else_label));
7001 ADD_SEQ(ret, cond_seq);
7002
7003 if (then_label->refcnt && else_label->refcnt) {
7004 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_IF ? "if" : "unless");
7005 }
7006
7007 if (then_label->refcnt) {
7008 ADD_LABEL(ret, then_label);
7009
7010 DECL_ANCHOR(then_seq);
7011 INIT_ANCHOR(then_seq);
7012 CHECK(COMPILE_(then_seq, "then", node_body, popped));
7013
7014 if (else_label->refcnt) {
7015 const NODE *const coverage_node = node_body ? node_body : node;
7016 add_trace_branch_coverage(
7017 iseq,
7018 ret,
7019 nd_code_loc(coverage_node),
7020 nd_node_id(coverage_node),
7021 0,
7022 type == NODE_IF ? "then" : "else",
7023 branches);
7024 end_label = NEW_LABEL(line);
7025 ADD_INSNL(then_seq, line_node, jump, end_label);
7026 if (!popped) {
7027 ADD_INSN(then_seq, line_node, pop);
7028 }
7029 }
7030 ADD_SEQ(ret, then_seq);
7031 }
7032
7033 if (else_label->refcnt) {
7034 ADD_LABEL(ret, else_label);
7035
7036 DECL_ANCHOR(else_seq);
7037 INIT_ANCHOR(else_seq);
7038 CHECK(COMPILE_(else_seq, "else", node_else, popped));
7039
7040 if (then_label->refcnt) {
7041 const NODE *const coverage_node = node_else ? node_else : node;
7042 add_trace_branch_coverage(
7043 iseq,
7044 ret,
7045 nd_code_loc(coverage_node),
7046 nd_node_id(coverage_node),
7047 1,
7048 type == NODE_IF ? "else" : "then",
7049 branches);
7050 }
7051 ADD_SEQ(ret, else_seq);
7052 }
7053
7054 if (end_label) {
7055 ADD_LABEL(ret, end_label);
7056 }
7057
7058 return COMPILE_OK;
7059}
7060
7061static int
7062compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
7063{
7064 const NODE *vals;
7065 const NODE *node = orig_node;
7066 LABEL *endlabel, *elselabel;
7067 DECL_ANCHOR(head);
7068 DECL_ANCHOR(body_seq);
7069 DECL_ANCHOR(cond_seq);
7070 int only_special_literals = 1;
7071 VALUE literals = rb_hash_new();
7072 int line;
7073 enum node_type type;
7074 const NODE *line_node;
7075 VALUE branches = Qfalse;
7076 int branch_id = 0;
7077
7078 INIT_ANCHOR(head);
7079 INIT_ANCHOR(body_seq);
7080 INIT_ANCHOR(cond_seq);
7081
7082 RHASH_TBL_RAW(literals)->type = &cdhash_type;
7083
7084 CHECK(COMPILE(head, "case base", RNODE_CASE(node)->nd_head));
7085
7086 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case");
7087
7088 node = RNODE_CASE(node)->nd_body;
7089 EXPECT_NODE("NODE_CASE", node, NODE_WHEN, COMPILE_NG);
7090 type = nd_type(node);
7091 line = nd_line(node);
7092 line_node = node;
7093
7094 endlabel = NEW_LABEL(line);
7095 elselabel = NEW_LABEL(line);
7096
7097 ADD_SEQ(ret, head); /* case VAL */
7098
7099 while (type == NODE_WHEN) {
7100 LABEL *l1;
7101
7102 l1 = NEW_LABEL(line);
7103 ADD_LABEL(body_seq, l1);
7104 ADD_INSN(body_seq, line_node, pop);
7105
7106 const NODE *const coverage_node = RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node;
7107 add_trace_branch_coverage(
7108 iseq,
7109 body_seq,
7110 nd_code_loc(coverage_node),
7111 nd_node_id(coverage_node),
7112 branch_id++,
7113 "when",
7114 branches);
7115
7116 CHECK(COMPILE_(body_seq, "when body", RNODE_WHEN(node)->nd_body, popped));
7117 ADD_INSNL(body_seq, line_node, jump, endlabel);
7118
7119 vals = RNODE_WHEN(node)->nd_head;
7120 if (vals) {
7121 switch (nd_type(vals)) {
7122 case NODE_LIST:
7123 only_special_literals = when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals);
7124 if (only_special_literals < 0) return COMPILE_NG;
7125 break;
7126 case NODE_SPLAT:
7127 case NODE_ARGSCAT:
7128 case NODE_ARGSPUSH:
7129 only_special_literals = 0;
7130 CHECK(when_splat_vals(iseq, cond_seq, vals, l1, only_special_literals, literals));
7131 break;
7132 default:
7133 UNKNOWN_NODE("NODE_CASE", vals, COMPILE_NG);
7134 }
7135 }
7136 else {
7137 EXPECT_NODE_NONULL("NODE_CASE", node, NODE_LIST, COMPILE_NG);
7138 }
7139
7140 node = RNODE_WHEN(node)->nd_next;
7141 if (!node) {
7142 break;
7143 }
7144 type = nd_type(node);
7145 line = nd_line(node);
7146 line_node = node;
7147 }
7148 /* else */
7149 if (node) {
7150 ADD_LABEL(cond_seq, elselabel);
7151 ADD_INSN(cond_seq, line_node, pop);
7152 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(node), nd_node_id(node), branch_id, "else", branches);
7153 CHECK(COMPILE_(cond_seq, "else", node, popped));
7154 ADD_INSNL(cond_seq, line_node, jump, endlabel);
7155 }
7156 else {
7157 debugs("== else (implicit)\n");
7158 ADD_LABEL(cond_seq, elselabel);
7159 ADD_INSN(cond_seq, orig_node, pop);
7160 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(orig_node), nd_node_id(orig_node), branch_id, "else", branches);
7161 if (!popped) {
7162 ADD_INSN(cond_seq, orig_node, putnil);
7163 }
7164 ADD_INSNL(cond_seq, orig_node, jump, endlabel);
7165 }
7166
7167 if (only_special_literals && ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
7168 ADD_INSN(ret, orig_node, dup);
7169 rb_obj_hide(literals);
7170 ADD_INSN2(ret, orig_node, opt_case_dispatch, literals, elselabel);
7171 RB_OBJ_WRITTEN(iseq, Qundef, literals);
7172 LABEL_REF(elselabel);
7173 }
7174
7175 ADD_SEQ(ret, cond_seq);
7176 ADD_SEQ(ret, body_seq);
7177 ADD_LABEL(ret, endlabel);
7178 return COMPILE_OK;
7179}
7180
7181static int
7182compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
7183{
7184 const NODE *vals;
7185 const NODE *val;
7186 const NODE *node = RNODE_CASE2(orig_node)->nd_body;
7187 LABEL *endlabel;
7188 DECL_ANCHOR(body_seq);
7189 VALUE branches = Qfalse;
7190 int branch_id = 0;
7191
7192 branches = decl_branch_base(iseq, PTR2NUM(orig_node), nd_code_loc(orig_node), "case");
7193
7194 INIT_ANCHOR(body_seq);
7195 endlabel = NEW_LABEL(nd_line(node));
7196
7197 while (node && nd_type_p(node, NODE_WHEN)) {
7198 const int line = nd_line(node);
7199 LABEL *l1 = NEW_LABEL(line);
7200 ADD_LABEL(body_seq, l1);
7201
7202 const NODE *const coverage_node = RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node;
7203 add_trace_branch_coverage(
7204 iseq,
7205 body_seq,
7206 nd_code_loc(coverage_node),
7207 nd_node_id(coverage_node),
7208 branch_id++,
7209 "when",
7210 branches);
7211
7212 CHECK(COMPILE_(body_seq, "when", RNODE_WHEN(node)->nd_body, popped));
7213 ADD_INSNL(body_seq, node, jump, endlabel);
7214
7215 vals = RNODE_WHEN(node)->nd_head;
7216 if (!vals) {
7217 EXPECT_NODE_NONULL("NODE_WHEN", node, NODE_LIST, COMPILE_NG);
7218 }
7219 switch (nd_type(vals)) {
7220 case NODE_LIST:
7221 while (vals) {
7222 LABEL *lnext;
7223 val = RNODE_LIST(vals)->nd_head;
7224 lnext = NEW_LABEL(nd_line(val));
7225 debug_compile("== when2\n", (void)0);
7226 CHECK(compile_branch_condition(iseq, ret, val, l1, lnext));
7227 ADD_LABEL(ret, lnext);
7228 vals = RNODE_LIST(vals)->nd_next;
7229 }
7230 break;
7231 case NODE_SPLAT:
7232 case NODE_ARGSCAT:
7233 case NODE_ARGSPUSH:
7234 ADD_INSN(ret, vals, putnil);
7235 CHECK(COMPILE(ret, "when2/cond splat", vals));
7236 ADD_INSN1(ret, vals, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
7237 ADD_INSNL(ret, vals, branchif, l1);
7238 break;
7239 default:
7240 UNKNOWN_NODE("NODE_WHEN", vals, COMPILE_NG);
7241 }
7242 node = RNODE_WHEN(node)->nd_next;
7243 }
7244 /* else */
7245 const NODE *const coverage_node = node ? node : orig_node;
7246 add_trace_branch_coverage(
7247 iseq,
7248 ret,
7249 nd_code_loc(coverage_node),
7250 nd_node_id(coverage_node),
7251 branch_id,
7252 "else",
7253 branches);
7254 CHECK(COMPILE_(ret, "else", node, popped));
7255 ADD_INSNL(ret, orig_node, jump, endlabel);
7256
7257 ADD_SEQ(ret, body_seq);
7258 ADD_LABEL(ret, endlabel);
7259 return COMPILE_OK;
7260}
7261
7262static 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);
7263
7264static 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);
7265static 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);
7266static int iseq_compile_pattern_set_general_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE errmsg, int base_index);
7267static 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);
7268static int iseq_compile_pattern_set_eqq_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int base_index);
7269
7270#define CASE3_BI_OFFSET_DECONSTRUCTED_CACHE 0
7271#define CASE3_BI_OFFSET_ERROR_STRING 1
7272#define CASE3_BI_OFFSET_KEY_ERROR_P 2
7273#define CASE3_BI_OFFSET_KEY_ERROR_MATCHEE 3
7274#define CASE3_BI_OFFSET_KEY_ERROR_KEY 4
7275
7276static int
7277iseq_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)
7278{
7279 const int line = nd_line(node);
7280 const NODE *line_node = node;
7281
7282 switch (nd_type(node)) {
7283 case NODE_ARYPTN: {
7284 /*
7285 * if pattern.use_rest_num?
7286 * rest_num = 0
7287 * end
7288 * if pattern.has_constant_node?
7289 * unless pattern.constant === obj
7290 * goto match_failed
7291 * end
7292 * end
7293 * unless obj.respond_to?(:deconstruct)
7294 * goto match_failed
7295 * end
7296 * d = obj.deconstruct
7297 * unless Array === d
7298 * goto type_error
7299 * end
7300 * min_argc = pattern.pre_args_num + pattern.post_args_num
7301 * if pattern.has_rest_arg?
7302 * unless d.length >= min_argc
7303 * goto match_failed
7304 * end
7305 * else
7306 * unless d.length == min_argc
7307 * goto match_failed
7308 * end
7309 * end
7310 * pattern.pre_args_num.each do |i|
7311 * unless pattern.pre_args[i].match?(d[i])
7312 * goto match_failed
7313 * end
7314 * end
7315 * if pattern.use_rest_num?
7316 * rest_num = d.length - min_argc
7317 * if pattern.has_rest_arg? && pattern.has_rest_arg_id # not `*`, but `*rest`
7318 * unless pattern.rest_arg.match?(d[pattern.pre_args_num, rest_num])
7319 * goto match_failed
7320 * end
7321 * end
7322 * end
7323 * pattern.post_args_num.each do |i|
7324 * j = pattern.pre_args_num + i
7325 * j += rest_num
7326 * unless pattern.post_args[i].match?(d[j])
7327 * goto match_failed
7328 * end
7329 * end
7330 * goto matched
7331 * type_error:
7332 * FrozenCore.raise TypeError
7333 * match_failed:
7334 * goto unmatched
7335 */
7336 const NODE *args = RNODE_ARYPTN(node)->pre_args;
7337 const int pre_args_num = RNODE_ARYPTN(node)->pre_args ? rb_long2int(RNODE_LIST(RNODE_ARYPTN(node)->pre_args)->as.nd_alen) : 0;
7338 const int post_args_num = RNODE_ARYPTN(node)->post_args ? rb_long2int(RNODE_LIST(RNODE_ARYPTN(node)->post_args)->as.nd_alen) : 0;
7339
7340 const int min_argc = pre_args_num + post_args_num;
7341 const int use_rest_num = RNODE_ARYPTN(node)->rest_arg && (NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg) ||
7342 (!NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg) && post_args_num > 0));
7343
7344 LABEL *match_failed, *type_error, *deconstruct, *deconstructed;
7345 int i;
7346 match_failed = NEW_LABEL(line);
7347 type_error = NEW_LABEL(line);
7348 deconstruct = NEW_LABEL(line);
7349 deconstructed = NEW_LABEL(line);
7350
7351 if (use_rest_num) {
7352 ADD_INSN1(ret, line_node, putobject, INT2FIX(0)); /* allocate stack for rest_num */
7353 ADD_INSN(ret, line_node, swap);
7354 if (base_index) {
7355 base_index++;
7356 }
7357 }
7358
7359 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
7360
7361 CHECK(iseq_compile_array_deconstruct(iseq, ret, node, deconstruct, deconstructed, match_failed, type_error, in_single_pattern, base_index, use_deconstructed_cache));
7362
7363 ADD_INSN(ret, line_node, dup);
7364 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
7365 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
7366 ADD_SEND(ret, line_node, RNODE_ARYPTN(node)->rest_arg ? idGE : idEq, INT2FIX(1)); // (1)
7367 if (in_single_pattern) {
7368 CHECK(iseq_compile_pattern_set_length_errmsg(iseq, ret, node,
7369 RNODE_ARYPTN(node)->rest_arg ? rb_fstring_lit("%p length mismatch (given %p, expected %p+)") :
7370 rb_fstring_lit("%p length mismatch (given %p, expected %p)"),
7371 INT2FIX(min_argc), base_index + 1 /* (1) */));
7372 }
7373 ADD_INSNL(ret, line_node, branchunless, match_failed);
7374
7375 for (i = 0; i < pre_args_num; i++) {
7376 ADD_INSN(ret, line_node, dup);
7377 ADD_INSN1(ret, line_node, putobject, INT2FIX(i));
7378 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (2)
7379 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));
7380 args = RNODE_LIST(args)->nd_next;
7381 }
7382
7383 if (RNODE_ARYPTN(node)->rest_arg) {
7384 if (NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg)) {
7385 ADD_INSN(ret, line_node, dup);
7386 ADD_INSN1(ret, line_node, putobject, INT2FIX(pre_args_num));
7387 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
7388 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
7389 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
7390 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1));
7391 ADD_INSN1(ret, line_node, setn, INT2FIX(4));
7392 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (3)
7393
7394 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));
7395 }
7396 else {
7397 if (post_args_num > 0) {
7398 ADD_INSN(ret, line_node, dup);
7399 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
7400 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
7401 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1));
7402 ADD_INSN1(ret, line_node, setn, INT2FIX(2));
7403 ADD_INSN(ret, line_node, pop);
7404 }
7405 }
7406 }
7407
7408 args = RNODE_ARYPTN(node)->post_args;
7409 for (i = 0; i < post_args_num; i++) {
7410 ADD_INSN(ret, line_node, dup);
7411
7412 ADD_INSN1(ret, line_node, putobject, INT2FIX(pre_args_num + i));
7413 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7414 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
7415
7416 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (4)
7417 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));
7418 args = RNODE_LIST(args)->nd_next;
7419 }
7420
7421 ADD_INSN(ret, line_node, pop);
7422 if (use_rest_num) {
7423 ADD_INSN(ret, line_node, pop);
7424 }
7425 ADD_INSNL(ret, line_node, jump, matched);
7426 ADD_INSN(ret, line_node, putnil);
7427 if (use_rest_num) {
7428 ADD_INSN(ret, line_node, putnil);
7429 }
7430
7431 ADD_LABEL(ret, type_error);
7432 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7433 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
7434 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct must return Array"));
7435 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
7436 ADD_INSN(ret, line_node, pop);
7437
7438 ADD_LABEL(ret, match_failed);
7439 ADD_INSN(ret, line_node, pop);
7440 if (use_rest_num) {
7441 ADD_INSN(ret, line_node, pop);
7442 }
7443 ADD_INSNL(ret, line_node, jump, unmatched);
7444
7445 break;
7446 }
7447 case NODE_FNDPTN: {
7448 /*
7449 * if pattern.has_constant_node?
7450 * unless pattern.constant === obj
7451 * goto match_failed
7452 * end
7453 * end
7454 * unless obj.respond_to?(:deconstruct)
7455 * goto match_failed
7456 * end
7457 * d = obj.deconstruct
7458 * unless Array === d
7459 * goto type_error
7460 * end
7461 * unless d.length >= pattern.args_num
7462 * goto match_failed
7463 * end
7464 *
7465 * begin
7466 * len = d.length
7467 * limit = d.length - pattern.args_num
7468 * i = 0
7469 * while i <= limit
7470 * if pattern.args_num.times.all? {|j| pattern.args[j].match?(d[i+j]) }
7471 * if pattern.has_pre_rest_arg_id
7472 * unless pattern.pre_rest_arg.match?(d[0, i])
7473 * goto find_failed
7474 * end
7475 * end
7476 * if pattern.has_post_rest_arg_id
7477 * unless pattern.post_rest_arg.match?(d[i+pattern.args_num, len])
7478 * goto find_failed
7479 * end
7480 * end
7481 * goto find_succeeded
7482 * end
7483 * i+=1
7484 * end
7485 * find_failed:
7486 * goto match_failed
7487 * find_succeeded:
7488 * end
7489 *
7490 * goto matched
7491 * type_error:
7492 * FrozenCore.raise TypeError
7493 * match_failed:
7494 * goto unmatched
7495 */
7496 const NODE *args = RNODE_FNDPTN(node)->args;
7497 const int args_num = RNODE_FNDPTN(node)->args ? rb_long2int(RNODE_LIST(RNODE_FNDPTN(node)->args)->as.nd_alen) : 0;
7498
7499 LABEL *match_failed, *type_error, *deconstruct, *deconstructed;
7500 match_failed = NEW_LABEL(line);
7501 type_error = NEW_LABEL(line);
7502 deconstruct = NEW_LABEL(line);
7503 deconstructed = NEW_LABEL(line);
7504
7505 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
7506
7507 CHECK(iseq_compile_array_deconstruct(iseq, ret, node, deconstruct, deconstructed, match_failed, type_error, in_single_pattern, base_index, use_deconstructed_cache));
7508
7509 ADD_INSN(ret, line_node, dup);
7510 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
7511 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
7512 ADD_SEND(ret, line_node, idGE, INT2FIX(1)); // (1)
7513 if (in_single_pattern) {
7514 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) */));
7515 }
7516 ADD_INSNL(ret, line_node, branchunless, match_failed);
7517
7518 {
7519 LABEL *while_begin = NEW_LABEL(nd_line(node));
7520 LABEL *next_loop = NEW_LABEL(nd_line(node));
7521 LABEL *find_succeeded = NEW_LABEL(line);
7522 LABEL *find_failed = NEW_LABEL(nd_line(node));
7523 int j;
7524
7525 ADD_INSN(ret, line_node, dup); /* allocate stack for len */
7526 ADD_SEND(ret, line_node, idLength, INT2FIX(0)); // (2)
7527
7528 ADD_INSN(ret, line_node, dup); /* allocate stack for limit */
7529 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
7530 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1)); // (3)
7531
7532 ADD_INSN1(ret, line_node, putobject, INT2FIX(0)); /* allocate stack for i */ // (4)
7533
7534 ADD_LABEL(ret, while_begin);
7535
7536 ADD_INSN(ret, line_node, dup);
7537 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
7538 ADD_SEND(ret, line_node, idLE, INT2FIX(1));
7539 ADD_INSNL(ret, line_node, branchunless, find_failed);
7540
7541 for (j = 0; j < args_num; j++) {
7542 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7543 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
7544 if (j != 0) {
7545 ADD_INSN1(ret, line_node, putobject, INT2FIX(j));
7546 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
7547 }
7548 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (5)
7549
7550 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));
7551 args = RNODE_LIST(args)->nd_next;
7552 }
7553
7554 if (NODE_NAMED_REST_P(RNODE_FNDPTN(node)->pre_rest_arg)) {
7555 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7556 ADD_INSN1(ret, line_node, putobject, INT2FIX(0));
7557 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
7558 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (6)
7559 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));
7560 }
7561 if (NODE_NAMED_REST_P(RNODE_FNDPTN(node)->post_rest_arg)) {
7562 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7563 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
7564 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
7565 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
7566 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7567 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (7)
7568 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));
7569 }
7570 ADD_INSNL(ret, line_node, jump, find_succeeded);
7571
7572 ADD_LABEL(ret, next_loop);
7573 ADD_INSN1(ret, line_node, putobject, INT2FIX(1));
7574 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
7575 ADD_INSNL(ret, line_node, jump, while_begin);
7576
7577 ADD_LABEL(ret, find_failed);
7578 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(3));
7579 if (in_single_pattern) {
7580 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7581 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("%p does not match to find pattern"));
7582 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
7583 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(2)); // (8)
7584 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (8) */)); // (9)
7585
7586 ADD_INSN1(ret, line_node, putobject, Qfalse);
7587 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (8), (9) */));
7588
7589 ADD_INSN(ret, line_node, pop);
7590 ADD_INSN(ret, line_node, pop);
7591 }
7592 ADD_INSNL(ret, line_node, jump, match_failed);
7593 ADD_INSN1(ret, line_node, dupn, INT2FIX(3));
7594
7595 ADD_LABEL(ret, find_succeeded);
7596 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(3));
7597 }
7598
7599 ADD_INSN(ret, line_node, pop);
7600 ADD_INSNL(ret, line_node, jump, matched);
7601 ADD_INSN(ret, line_node, putnil);
7602
7603 ADD_LABEL(ret, type_error);
7604 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7605 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
7606 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct must return Array"));
7607 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
7608 ADD_INSN(ret, line_node, pop);
7609
7610 ADD_LABEL(ret, match_failed);
7611 ADD_INSN(ret, line_node, pop);
7612 ADD_INSNL(ret, line_node, jump, unmatched);
7613
7614 break;
7615 }
7616 case NODE_HSHPTN: {
7617 /*
7618 * keys = nil
7619 * if pattern.has_kw_args_node? && !pattern.has_kw_rest_arg_node?
7620 * keys = pattern.kw_args_node.keys
7621 * end
7622 * if pattern.has_constant_node?
7623 * unless pattern.constant === obj
7624 * goto match_failed
7625 * end
7626 * end
7627 * unless obj.respond_to?(:deconstruct_keys)
7628 * goto match_failed
7629 * end
7630 * d = obj.deconstruct_keys(keys)
7631 * unless Hash === d
7632 * goto type_error
7633 * end
7634 * if pattern.has_kw_rest_arg_node?
7635 * d = d.dup
7636 * end
7637 * if pattern.has_kw_args_node?
7638 * pattern.kw_args_node.each |k,|
7639 * unless d.key?(k)
7640 * goto match_failed
7641 * end
7642 * end
7643 * pattern.kw_args_node.each |k, pat|
7644 * if pattern.has_kw_rest_arg_node?
7645 * unless pat.match?(d.delete(k))
7646 * goto match_failed
7647 * end
7648 * else
7649 * unless pat.match?(d[k])
7650 * goto match_failed
7651 * end
7652 * end
7653 * end
7654 * else
7655 * unless d.empty?
7656 * goto match_failed
7657 * end
7658 * end
7659 * if pattern.has_kw_rest_arg_node?
7660 * if pattern.no_rest_keyword?
7661 * unless d.empty?
7662 * goto match_failed
7663 * end
7664 * else
7665 * unless pattern.kw_rest_arg_node.match?(d)
7666 * goto match_failed
7667 * end
7668 * end
7669 * end
7670 * goto matched
7671 * type_error:
7672 * FrozenCore.raise TypeError
7673 * match_failed:
7674 * goto unmatched
7675 */
7676 LABEL *match_failed, *type_error;
7677 VALUE keys = Qnil;
7678
7679 match_failed = NEW_LABEL(line);
7680 type_error = NEW_LABEL(line);
7681
7682 if (RNODE_HSHPTN(node)->nd_pkwargs && !RNODE_HSHPTN(node)->nd_pkwrestarg) {
7683 const NODE *kw_args = RNODE_HASH(RNODE_HSHPTN(node)->nd_pkwargs)->nd_head;
7684 keys = rb_ary_new_capa(kw_args ? RNODE_LIST(kw_args)->as.nd_alen/2 : 0);
7685 while (kw_args) {
7686 rb_ary_push(keys, get_symbol_value(iseq, RNODE_LIST(kw_args)->nd_head));
7687 kw_args = RNODE_LIST(RNODE_LIST(kw_args)->nd_next)->nd_next;
7688 }
7689 }
7690
7691 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
7692
7693 ADD_INSN(ret, line_node, dup);
7694 ADD_INSN1(ret, line_node, putobject, ID2SYM(rb_intern("deconstruct_keys")));
7695 ADD_SEND(ret, line_node, idRespond_to, INT2FIX(1)); // (1)
7696 if (in_single_pattern) {
7697 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p does not respond to #deconstruct_keys"), base_index + 1 /* (1) */));
7698 }
7699 ADD_INSNL(ret, line_node, branchunless, match_failed);
7700
7701 if (NIL_P(keys)) {
7702 ADD_INSN(ret, line_node, putnil);
7703 }
7704 else {
7705 RB_OBJ_SET_FROZEN_SHAREABLE(keys);
7706 ADD_INSN1(ret, line_node, duparray, keys);
7707 RB_OBJ_WRITTEN(iseq, Qundef, rb_obj_hide(keys));
7708 }
7709 ADD_SEND(ret, line_node, rb_intern("deconstruct_keys"), INT2FIX(1)); // (2)
7710
7711 ADD_INSN(ret, line_node, dup);
7712 ADD_INSN1(ret, line_node, checktype, INT2FIX(T_HASH));
7713 ADD_INSNL(ret, line_node, branchunless, type_error);
7714
7715 if (RNODE_HSHPTN(node)->nd_pkwrestarg) {
7716 ADD_SEND(ret, line_node, rb_intern("dup"), INT2FIX(0));
7717 }
7718
7719 if (RNODE_HSHPTN(node)->nd_pkwargs) {
7720 int i;
7721 int keys_num;
7722 const NODE *args;
7723 args = RNODE_HASH(RNODE_HSHPTN(node)->nd_pkwargs)->nd_head;
7724 if (args) {
7725 DECL_ANCHOR(match_values);
7726 INIT_ANCHOR(match_values);
7727 keys_num = rb_long2int(RNODE_LIST(args)->as.nd_alen) / 2;
7728 for (i = 0; i < keys_num; i++) {
7729 NODE *key_node = RNODE_LIST(args)->nd_head;
7730 NODE *value_node = RNODE_LIST(RNODE_LIST(args)->nd_next)->nd_head;
7731 VALUE key = get_symbol_value(iseq, key_node);
7732
7733 ADD_INSN(ret, line_node, dup);
7734 ADD_INSN1(ret, line_node, putobject, key);
7735 ADD_SEND(ret, line_node, rb_intern("key?"), INT2FIX(1)); // (3)
7736 if (in_single_pattern) {
7737 LABEL *match_succeeded;
7738 match_succeeded = NEW_LABEL(line);
7739
7740 ADD_INSN(ret, line_node, dup);
7741 ADD_INSNL(ret, line_node, branchif, match_succeeded);
7742
7743 VALUE str = rb_str_freeze(rb_sprintf("key not found: %+"PRIsVALUE, key));
7744 ADD_INSN1(ret, line_node, putobject, RB_OBJ_SET_SHAREABLE(str)); // (4)
7745 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 2 /* (3), (4) */));
7746 ADD_INSN1(ret, line_node, putobject, Qtrue); // (5)
7747 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 3 /* (3), (4), (5) */));
7748 ADD_INSN1(ret, line_node, topn, INT2FIX(3)); // (6)
7749 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_MATCHEE + 4 /* (3), (4), (5), (6) */));
7750 ADD_INSN1(ret, line_node, putobject, key); // (7)
7751 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_KEY + 5 /* (3), (4), (5), (6), (7) */));
7752
7753 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(4));
7754
7755 ADD_LABEL(ret, match_succeeded);
7756 }
7757 ADD_INSNL(ret, line_node, branchunless, match_failed);
7758
7759 ADD_INSN(match_values, line_node, dup);
7760 ADD_INSN1(match_values, line_node, putobject, key);
7761 ADD_SEND(match_values, line_node, RNODE_HSHPTN(node)->nd_pkwrestarg ? rb_intern("delete") : idAREF, INT2FIX(1)); // (8)
7762 CHECK(iseq_compile_pattern_match(iseq, match_values, value_node, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (8) */, false));
7763 args = RNODE_LIST(RNODE_LIST(args)->nd_next)->nd_next;
7764 }
7765 ADD_SEQ(ret, match_values);
7766 }
7767 }
7768 else {
7769 ADD_INSN(ret, line_node, dup);
7770 ADD_SEND(ret, line_node, idEmptyP, INT2FIX(0)); // (9)
7771 if (in_single_pattern) {
7772 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p is not empty"), base_index + 1 /* (9) */));
7773 }
7774 ADD_INSNL(ret, line_node, branchunless, match_failed);
7775 }
7776
7777 if (RNODE_HSHPTN(node)->nd_pkwrestarg) {
7778 if (RNODE_HSHPTN(node)->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD) {
7779 ADD_INSN(ret, line_node, dup);
7780 ADD_SEND(ret, line_node, idEmptyP, INT2FIX(0)); // (10)
7781 if (in_single_pattern) {
7782 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("rest of %p is not empty"), base_index + 1 /* (10) */));
7783 }
7784 ADD_INSNL(ret, line_node, branchunless, match_failed);
7785 }
7786 else {
7787 ADD_INSN(ret, line_node, dup); // (11)
7788 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));
7789 }
7790 }
7791
7792 ADD_INSN(ret, line_node, pop);
7793 ADD_INSNL(ret, line_node, jump, matched);
7794 ADD_INSN(ret, line_node, putnil);
7795
7796 ADD_LABEL(ret, type_error);
7797 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7798 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
7799 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct_keys must return Hash"));
7800 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
7801 ADD_INSN(ret, line_node, pop);
7802
7803 ADD_LABEL(ret, match_failed);
7804 ADD_INSN(ret, line_node, pop);
7805 ADD_INSNL(ret, line_node, jump, unmatched);
7806 break;
7807 }
7808 case NODE_SYM:
7809 case NODE_REGX:
7810 case NODE_LINE:
7811 case NODE_INTEGER:
7812 case NODE_FLOAT:
7813 case NODE_RATIONAL:
7814 case NODE_IMAGINARY:
7815 case NODE_FILE:
7816 case NODE_ENCODING:
7817 case NODE_STR:
7818 case NODE_XSTR:
7819 case NODE_DSTR:
7820 case NODE_DSYM:
7821 case NODE_DREGX:
7822 case NODE_LIST:
7823 case NODE_ZLIST:
7824 case NODE_LAMBDA:
7825 case NODE_DOT2:
7826 case NODE_DOT3:
7827 case NODE_CONST:
7828 case NODE_LVAR:
7829 case NODE_DVAR:
7830 case NODE_IVAR:
7831 case NODE_CVAR:
7832 case NODE_GVAR:
7833 case NODE_TRUE:
7834 case NODE_FALSE:
7835 case NODE_SELF:
7836 case NODE_NIL:
7837 case NODE_COLON2:
7838 case NODE_COLON3:
7839 case NODE_BEGIN:
7840 case NODE_BLOCK:
7841 case NODE_ONCE:
7842 CHECK(COMPILE(ret, "case in literal", node)); // (1)
7843 if (in_single_pattern) {
7844 ADD_INSN1(ret, line_node, dupn, INT2FIX(2));
7845 }
7846 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE)); // (2)
7847 if (in_single_pattern) {
7848 CHECK(iseq_compile_pattern_set_eqq_errmsg(iseq, ret, node, base_index + 2 /* (1), (2) */));
7849 }
7850 ADD_INSNL(ret, line_node, branchif, matched);
7851 ADD_INSNL(ret, line_node, jump, unmatched);
7852 break;
7853 case NODE_LASGN: {
7854 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
7855 ID id = RNODE_LASGN(node)->nd_vid;
7856 int idx = ISEQ_BODY(body->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
7857
7858 if (in_alt_pattern) {
7859 const char *name = rb_id2name(id);
7860 if (name && strlen(name) > 0 && name[0] != '_') {
7861 COMPILE_ERROR(ERROR_ARGS "illegal variable in alternative pattern (%"PRIsVALUE")",
7862 rb_id2str(id));
7863 return COMPILE_NG;
7864 }
7865 }
7866
7867 ADD_SETLOCAL(ret, line_node, idx, get_lvar_level(iseq));
7868 ADD_INSNL(ret, line_node, jump, matched);
7869 break;
7870 }
7871 case NODE_DASGN: {
7872 int idx, lv, ls;
7873 ID id = RNODE_DASGN(node)->nd_vid;
7874
7875 idx = get_dyna_var_idx(iseq, id, &lv, &ls);
7876
7877 if (in_alt_pattern) {
7878 const char *name = rb_id2name(id);
7879 if (name && strlen(name) > 0 && name[0] != '_') {
7880 COMPILE_ERROR(ERROR_ARGS "illegal variable in alternative pattern (%"PRIsVALUE")",
7881 rb_id2str(id));
7882 return COMPILE_NG;
7883 }
7884 }
7885
7886 if (idx < 0) {
7887 COMPILE_ERROR(ERROR_ARGS "NODE_DASGN: unknown id (%"PRIsVALUE")",
7888 rb_id2str(id));
7889 return COMPILE_NG;
7890 }
7891 ADD_SETLOCAL(ret, line_node, ls - idx, lv);
7892 ADD_INSNL(ret, line_node, jump, matched);
7893 break;
7894 }
7895 case NODE_IF:
7896 case NODE_UNLESS: {
7897 LABEL *match_failed;
7898 match_failed = unmatched;
7899 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_IF(node)->nd_body, unmatched, in_single_pattern, in_alt_pattern, base_index, use_deconstructed_cache));
7900 CHECK(COMPILE(ret, "case in if", RNODE_IF(node)->nd_cond));
7901 if (in_single_pattern) {
7902 LABEL *match_succeeded;
7903 match_succeeded = NEW_LABEL(line);
7904
7905 ADD_INSN(ret, line_node, dup);
7906 if (nd_type_p(node, NODE_IF)) {
7907 ADD_INSNL(ret, line_node, branchif, match_succeeded);
7908 }
7909 else {
7910 ADD_INSNL(ret, line_node, branchunless, match_succeeded);
7911 }
7912
7913 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("guard clause does not return true")); // (1)
7914 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
7915 ADD_INSN1(ret, line_node, putobject, Qfalse);
7916 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
7917
7918 ADD_INSN(ret, line_node, pop);
7919 ADD_INSN(ret, line_node, pop);
7920
7921 ADD_LABEL(ret, match_succeeded);
7922 }
7923 if (nd_type_p(node, NODE_IF)) {
7924 ADD_INSNL(ret, line_node, branchunless, match_failed);
7925 }
7926 else {
7927 ADD_INSNL(ret, line_node, branchif, match_failed);
7928 }
7929 ADD_INSNL(ret, line_node, jump, matched);
7930 break;
7931 }
7932 case NODE_HASH: {
7933 NODE *n;
7934 LABEL *match_failed;
7935 match_failed = NEW_LABEL(line);
7936
7937 n = RNODE_HASH(node)->nd_head;
7938 if (! (nd_type_p(n, NODE_LIST) && RNODE_LIST(n)->as.nd_alen == 2)) {
7939 COMPILE_ERROR(ERROR_ARGS "unexpected node");
7940 return COMPILE_NG;
7941 }
7942
7943 ADD_INSN(ret, line_node, dup); // (1)
7944 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));
7945 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));
7946 ADD_INSN(ret, line_node, putnil);
7947
7948 ADD_LABEL(ret, match_failed);
7949 ADD_INSN(ret, line_node, pop);
7950 ADD_INSNL(ret, line_node, jump, unmatched);
7951 break;
7952 }
7953 case NODE_OR: {
7954 LABEL *match_succeeded, *fin;
7955 match_succeeded = NEW_LABEL(line);
7956 fin = NEW_LABEL(line);
7957
7958 ADD_INSN(ret, line_node, dup); // (1)
7959 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));
7960 ADD_LABEL(ret, match_succeeded);
7961 ADD_INSN(ret, line_node, pop);
7962 ADD_INSNL(ret, line_node, jump, matched);
7963 ADD_INSN(ret, line_node, putnil);
7964 ADD_LABEL(ret, fin);
7965 CHECK(iseq_compile_pattern_each(iseq, ret, RNODE_OR(node)->nd_2nd, matched, unmatched, in_single_pattern, true, base_index, use_deconstructed_cache));
7966 break;
7967 }
7968 default:
7969 UNKNOWN_NODE("NODE_IN", node, COMPILE_NG);
7970 }
7971 return COMPILE_OK;
7972}
7973
7974static int
7975iseq_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)
7976{
7977 LABEL *fin = NEW_LABEL(nd_line(node));
7978 CHECK(iseq_compile_pattern_each(iseq, ret, node, fin, unmatched, in_single_pattern, in_alt_pattern, base_index, use_deconstructed_cache));
7979 ADD_LABEL(ret, fin);
7980 return COMPILE_OK;
7981}
7982
7983static int
7984iseq_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)
7985{
7986 const NODE *line_node = node;
7987
7988 if (RNODE_ARYPTN(node)->nd_pconst) {
7989 ADD_INSN(ret, line_node, dup); // (1)
7990 CHECK(COMPILE(ret, "constant", RNODE_ARYPTN(node)->nd_pconst)); // (2)
7991 if (in_single_pattern) {
7992 ADD_INSN1(ret, line_node, dupn, INT2FIX(2));
7993 }
7994 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE)); // (3)
7995 if (in_single_pattern) {
7996 CHECK(iseq_compile_pattern_set_eqq_errmsg(iseq, ret, node, base_index + 3 /* (1), (2), (3) */));
7997 }
7998 ADD_INSNL(ret, line_node, branchunless, match_failed);
7999 }
8000 return COMPILE_OK;
8001}
8002
8003
8004static int
8005iseq_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)
8006{
8007 const NODE *line_node = node;
8008
8009 // NOTE: this optimization allows us to re-use the #deconstruct value
8010 // (or its absence).
8011 if (use_deconstructed_cache) {
8012 // If value is nil then we haven't tried to deconstruct
8013 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
8014 ADD_INSNL(ret, line_node, branchnil, deconstruct);
8015
8016 // If false then the value is not deconstructable
8017 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
8018 ADD_INSNL(ret, line_node, branchunless, match_failed);
8019
8020 // Drop value, add deconstructed to the stack and jump
8021 ADD_INSN(ret, line_node, pop); // (1)
8022 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE - 1 /* (1) */));
8023 ADD_INSNL(ret, line_node, jump, deconstructed);
8024 }
8025 else {
8026 ADD_INSNL(ret, line_node, jump, deconstruct);
8027 }
8028
8029 ADD_LABEL(ret, deconstruct);
8030 ADD_INSN(ret, line_node, dup);
8031 ADD_INSN1(ret, line_node, putobject, ID2SYM(rb_intern("deconstruct")));
8032 ADD_SEND(ret, line_node, idRespond_to, INT2FIX(1)); // (2)
8033
8034 // Cache the result of respond_to? (in case it's false is stays there, if true - it's overwritten after #deconstruct)
8035 if (use_deconstructed_cache) {
8036 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE + 1 /* (2) */));
8037 }
8038
8039 if (in_single_pattern) {
8040 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p does not respond to #deconstruct"), base_index + 1 /* (2) */));
8041 }
8042
8043 ADD_INSNL(ret, line_node, branchunless, match_failed);
8044
8045 ADD_SEND(ret, line_node, rb_intern("deconstruct"), INT2FIX(0));
8046
8047 // Cache the result (if it's cacheable - currently, only top-level array patterns)
8048 if (use_deconstructed_cache) {
8049 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
8050 }
8051
8052 ADD_INSN(ret, line_node, dup);
8053 ADD_INSN1(ret, line_node, checktype, INT2FIX(T_ARRAY));
8054 ADD_INSNL(ret, line_node, branchunless, type_error);
8055
8056 ADD_LABEL(ret, deconstructed);
8057
8058 return COMPILE_OK;
8059}
8060
8061static int
8062iseq_compile_pattern_set_general_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE errmsg, int base_index)
8063{
8064 /*
8065 * if match_succeeded?
8066 * goto match_succeeded
8067 * end
8068 * error_string = FrozenCore.sprintf(errmsg, matchee)
8069 * key_error_p = false
8070 * match_succeeded:
8071 */
8072 const int line = nd_line(node);
8073 const NODE *line_node = node;
8074 LABEL *match_succeeded = NEW_LABEL(line);
8075
8076 ADD_INSN(ret, line_node, dup);
8077 ADD_INSNL(ret, line_node, branchif, match_succeeded);
8078
8079 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8080 ADD_INSN1(ret, line_node, putobject, errmsg);
8081 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
8082 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(2)); // (1)
8083 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
8084
8085 ADD_INSN1(ret, line_node, putobject, Qfalse);
8086 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
8087
8088 ADD_INSN(ret, line_node, pop);
8089 ADD_INSN(ret, line_node, pop);
8090 ADD_LABEL(ret, match_succeeded);
8091
8092 return COMPILE_OK;
8093}
8094
8095static int
8096iseq_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)
8097{
8098 /*
8099 * if match_succeeded?
8100 * goto match_succeeded
8101 * end
8102 * error_string = FrozenCore.sprintf(errmsg, matchee, matchee.length, pat.length)
8103 * key_error_p = false
8104 * match_succeeded:
8105 */
8106 const int line = nd_line(node);
8107 const NODE *line_node = node;
8108 LABEL *match_succeeded = NEW_LABEL(line);
8109
8110 ADD_INSN(ret, line_node, dup);
8111 ADD_INSNL(ret, line_node, branchif, match_succeeded);
8112
8113 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8114 ADD_INSN1(ret, line_node, putobject, errmsg);
8115 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
8116 ADD_INSN(ret, line_node, dup);
8117 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
8118 ADD_INSN1(ret, line_node, putobject, pattern_length);
8119 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(4)); // (1)
8120 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
8121
8122 ADD_INSN1(ret, line_node, putobject, Qfalse);
8123 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2/* (1), (2) */));
8124
8125 ADD_INSN(ret, line_node, pop);
8126 ADD_INSN(ret, line_node, pop);
8127 ADD_LABEL(ret, match_succeeded);
8128
8129 return COMPILE_OK;
8130}
8131
8132static int
8133iseq_compile_pattern_set_eqq_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int base_index)
8134{
8135 /*
8136 * if match_succeeded?
8137 * goto match_succeeded
8138 * end
8139 * error_string = FrozenCore.sprintf("%p === %p does not return true", pat, matchee)
8140 * key_error_p = false
8141 * match_succeeded:
8142 */
8143 const int line = nd_line(node);
8144 const NODE *line_node = node;
8145 LABEL *match_succeeded = NEW_LABEL(line);
8146
8147 ADD_INSN(ret, line_node, dup);
8148 ADD_INSNL(ret, line_node, branchif, match_succeeded);
8149
8150 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8151 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("%p === %p does not return true"));
8152 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
8153 ADD_INSN1(ret, line_node, topn, INT2FIX(5));
8154 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(3)); // (1)
8155 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
8156
8157 ADD_INSN1(ret, line_node, putobject, Qfalse);
8158 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
8159
8160 ADD_INSN(ret, line_node, pop);
8161 ADD_INSN(ret, line_node, pop);
8162
8163 ADD_LABEL(ret, match_succeeded);
8164 ADD_INSN1(ret, line_node, setn, INT2FIX(2));
8165 ADD_INSN(ret, line_node, pop);
8166 ADD_INSN(ret, line_node, pop);
8167
8168 return COMPILE_OK;
8169}
8170
8171static int
8172compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
8173{
8174 const NODE *pattern;
8175 const NODE *node = orig_node;
8176 LABEL *endlabel, *elselabel;
8177 DECL_ANCHOR(head);
8178 DECL_ANCHOR(body_seq);
8179 DECL_ANCHOR(cond_seq);
8180 int line;
8181 enum node_type type;
8182 const NODE *line_node;
8183 VALUE branches = 0;
8184 int branch_id = 0;
8185 bool single_pattern;
8186
8187 INIT_ANCHOR(head);
8188 INIT_ANCHOR(body_seq);
8189 INIT_ANCHOR(cond_seq);
8190
8191 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case");
8192
8193 node = RNODE_CASE3(node)->nd_body;
8194 EXPECT_NODE("NODE_CASE3", node, NODE_IN, COMPILE_NG);
8195 type = nd_type(node);
8196 line = nd_line(node);
8197 line_node = node;
8198 single_pattern = !RNODE_IN(node)->nd_next;
8199
8200 endlabel = NEW_LABEL(line);
8201 elselabel = NEW_LABEL(line);
8202
8203 if (single_pattern) {
8204 /* allocate stack for ... */
8205 ADD_INSN(head, line_node, putnil); /* key_error_key */
8206 ADD_INSN(head, line_node, putnil); /* key_error_matchee */
8207 ADD_INSN1(head, line_node, putobject, Qfalse); /* key_error_p */
8208 ADD_INSN(head, line_node, putnil); /* error_string */
8209 }
8210 ADD_INSN(head, line_node, putnil); /* allocate stack for cached #deconstruct value */
8211
8212 CHECK(COMPILE(head, "case base", RNODE_CASE3(orig_node)->nd_head));
8213
8214 ADD_SEQ(ret, head); /* case VAL */
8215
8216 while (type == NODE_IN) {
8217 LABEL *l1;
8218
8219 if (branch_id) {
8220 ADD_INSN(body_seq, line_node, putnil);
8221 }
8222 l1 = NEW_LABEL(line);
8223 ADD_LABEL(body_seq, l1);
8224 ADD_INSN1(body_seq, line_node, adjuststack, INT2FIX(single_pattern ? 6 : 2));
8225
8226 const NODE *const coverage_node = RNODE_IN(node)->nd_body ? RNODE_IN(node)->nd_body : node;
8227 add_trace_branch_coverage(
8228 iseq,
8229 body_seq,
8230 nd_code_loc(coverage_node),
8231 nd_node_id(coverage_node),
8232 branch_id++,
8233 "in",
8234 branches);
8235
8236 CHECK(COMPILE_(body_seq, "in body", RNODE_IN(node)->nd_body, popped));
8237 ADD_INSNL(body_seq, line_node, jump, endlabel);
8238
8239 pattern = RNODE_IN(node)->nd_head;
8240 if (pattern) {
8241 int pat_line = nd_line(pattern);
8242 LABEL *next_pat = NEW_LABEL(pat_line);
8243 ADD_INSN (cond_seq, pattern, dup); /* dup case VAL */
8244 // NOTE: set base_index (it's "under" the matchee value, so it's position is 2)
8245 CHECK(iseq_compile_pattern_each(iseq, cond_seq, pattern, l1, next_pat, single_pattern, false, 2, true));
8246 ADD_LABEL(cond_seq, next_pat);
8247 LABEL_UNREMOVABLE(next_pat);
8248 }
8249 else {
8250 COMPILE_ERROR(ERROR_ARGS "unexpected node");
8251 return COMPILE_NG;
8252 }
8253
8254 node = RNODE_IN(node)->nd_next;
8255 if (!node) {
8256 break;
8257 }
8258 type = nd_type(node);
8259 line = nd_line(node);
8260 line_node = node;
8261 }
8262 /* else */
8263 if (node) {
8264 ADD_LABEL(cond_seq, elselabel);
8265 ADD_INSN(cond_seq, line_node, pop);
8266 ADD_INSN(cond_seq, line_node, pop); /* discard cached #deconstruct value */
8267 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(node), nd_node_id(node), branch_id, "else", branches);
8268 CHECK(COMPILE_(cond_seq, "else", node, popped));
8269 ADD_INSNL(cond_seq, line_node, jump, endlabel);
8270 ADD_INSN(cond_seq, line_node, putnil);
8271 if (popped) {
8272 ADD_INSN(cond_seq, line_node, putnil);
8273 }
8274 }
8275 else {
8276 debugs("== else (implicit)\n");
8277 ADD_LABEL(cond_seq, elselabel);
8278 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(orig_node), nd_node_id(orig_node), branch_id, "else", branches);
8279 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8280
8281 if (single_pattern) {
8282 /*
8283 * if key_error_p
8284 * FrozenCore.raise NoMatchingPatternKeyError.new(FrozenCore.sprintf("%p: %s", case_val, error_string), matchee: key_error_matchee, key: key_error_key)
8285 * else
8286 * FrozenCore.raise NoMatchingPatternError, FrozenCore.sprintf("%p: %s", case_val, error_string)
8287 * end
8288 */
8289 LABEL *key_error, *fin;
8290 struct rb_callinfo_kwarg *kw_arg;
8291
8292 key_error = NEW_LABEL(line);
8293 fin = NEW_LABEL(line);
8294
8295 kw_arg = rb_xmalloc_mul_add(2, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
8296 kw_arg->references = 0;
8297 kw_arg->keyword_len = 2;
8298 kw_arg->keywords[0] = ID2SYM(rb_intern("matchee"));
8299 kw_arg->keywords[1] = ID2SYM(rb_intern("key"));
8300
8301 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_P + 2));
8302 ADD_INSNL(cond_seq, orig_node, branchif, key_error);
8303 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternError);
8304 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8305 ADD_INSN1(cond_seq, orig_node, putobject, rb_fstring_lit("%p: %s"));
8306 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(4)); /* case VAL */
8307 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_ERROR_STRING + 6));
8308 ADD_SEND(cond_seq, orig_node, id_core_sprintf, INT2FIX(3));
8309 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(2));
8310 ADD_INSNL(cond_seq, orig_node, jump, fin);
8311
8312 ADD_LABEL(cond_seq, key_error);
8313 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternKeyError);
8314 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8315 ADD_INSN1(cond_seq, orig_node, putobject, rb_fstring_lit("%p: %s"));
8316 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(4)); /* case VAL */
8317 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_ERROR_STRING + 6));
8318 ADD_SEND(cond_seq, orig_node, id_core_sprintf, INT2FIX(3));
8319 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_MATCHEE + 4));
8320 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_KEY + 5));
8321 ADD_SEND_R(cond_seq, orig_node, rb_intern("new"), INT2FIX(1), NULL, INT2FIX(VM_CALL_KWARG), kw_arg);
8322 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(1));
8323
8324 ADD_LABEL(cond_seq, fin);
8325 }
8326 else {
8327 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternError);
8328 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(2));
8329 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(2));
8330 }
8331 ADD_INSN1(cond_seq, orig_node, adjuststack, INT2FIX(single_pattern ? 7 : 3));
8332 if (!popped) {
8333 ADD_INSN(cond_seq, orig_node, putnil);
8334 }
8335 ADD_INSNL(cond_seq, orig_node, jump, endlabel);
8336 ADD_INSN1(cond_seq, orig_node, dupn, INT2FIX(single_pattern ? 5 : 1));
8337 if (popped) {
8338 ADD_INSN(cond_seq, line_node, putnil);
8339 }
8340 }
8341
8342 ADD_SEQ(ret, cond_seq);
8343 ADD_SEQ(ret, body_seq);
8344 ADD_LABEL(ret, endlabel);
8345 return COMPILE_OK;
8346}
8347
8348#undef CASE3_BI_OFFSET_DECONSTRUCTED_CACHE
8349#undef CASE3_BI_OFFSET_ERROR_STRING
8350#undef CASE3_BI_OFFSET_KEY_ERROR_P
8351#undef CASE3_BI_OFFSET_KEY_ERROR_MATCHEE
8352#undef CASE3_BI_OFFSET_KEY_ERROR_KEY
8353
8354static int
8355compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
8356{
8357 const int line = (int)nd_line(node);
8358 const NODE *line_node = node;
8359
8360 LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
8361 LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
8362 LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
8363 int prev_loopval_popped = ISEQ_COMPILE_DATA(iseq)->loopval_popped;
8364 VALUE branches = Qfalse;
8365
8367
8368 LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(line); /* next */
8369 LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(line); /* redo */
8370 LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(line); /* break */
8371 LABEL *end_label = NEW_LABEL(line);
8372 LABEL *adjust_label = NEW_LABEL(line);
8373
8374 LABEL *next_catch_label = NEW_LABEL(line);
8375 LABEL *tmp_label = NULL;
8376
8377 ISEQ_COMPILE_DATA(iseq)->loopval_popped = 0;
8378 push_ensure_entry(iseq, &enl, NULL, NULL);
8379
8380 if (RNODE_WHILE(node)->nd_state == 1) {
8381 ADD_INSNL(ret, line_node, jump, next_label);
8382 }
8383 else {
8384 tmp_label = NEW_LABEL(line);
8385 ADD_INSNL(ret, line_node, jump, tmp_label);
8386 }
8387 ADD_LABEL(ret, adjust_label);
8388 ADD_INSN(ret, line_node, putnil);
8389 ADD_LABEL(ret, next_catch_label);
8390 ADD_INSN(ret, line_node, pop);
8391 ADD_INSNL(ret, line_node, jump, next_label);
8392 if (tmp_label) ADD_LABEL(ret, tmp_label);
8393
8394 ADD_LABEL(ret, redo_label);
8395 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_WHILE ? "while" : "until");
8396
8397 const NODE *const coverage_node = RNODE_WHILE(node)->nd_body ? RNODE_WHILE(node)->nd_body : node;
8398 add_trace_branch_coverage(
8399 iseq,
8400 ret,
8401 nd_code_loc(coverage_node),
8402 nd_node_id(coverage_node),
8403 0,
8404 "body",
8405 branches);
8406
8407 CHECK(COMPILE_POPPED(ret, "while body", RNODE_WHILE(node)->nd_body));
8408 ADD_LABEL(ret, next_label); /* next */
8409
8410 if (type == NODE_WHILE) {
8411 CHECK(compile_branch_condition(iseq, ret, RNODE_WHILE(node)->nd_cond,
8412 redo_label, end_label));
8413 }
8414 else {
8415 /* until */
8416 CHECK(compile_branch_condition(iseq, ret, RNODE_WHILE(node)->nd_cond,
8417 end_label, redo_label));
8418 }
8419
8420 ADD_LABEL(ret, end_label);
8421 ADD_ADJUST_RESTORE(ret, adjust_label);
8422
8423 if (UNDEF_P(RNODE_WHILE(node)->nd_state)) {
8424 /* ADD_INSN(ret, line_node, putundef); */
8425 COMPILE_ERROR(ERROR_ARGS "unsupported: putundef");
8426 return COMPILE_NG;
8427 }
8428 else {
8429 ADD_INSN(ret, line_node, putnil);
8430 }
8431
8432 ADD_LABEL(ret, break_label); /* break */
8433
8434 if (popped) {
8435 ADD_INSN(ret, line_node, pop);
8436 }
8437
8438 ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL,
8439 break_label);
8440 ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL,
8441 next_catch_label);
8442 ADD_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL,
8443 ISEQ_COMPILE_DATA(iseq)->redo_label);
8444
8445 ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
8446 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
8447 ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
8448 ISEQ_COMPILE_DATA(iseq)->loopval_popped = prev_loopval_popped;
8449 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->prev;
8450 return COMPILE_OK;
8451}
8452
8453static int
8454compile_iter(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8455{
8456 const int line = nd_line(node);
8457 const NODE *line_node = node;
8458 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
8459 LABEL *retry_label = NEW_LABEL(line);
8460 LABEL *retry_end_l = NEW_LABEL(line);
8461 const rb_iseq_t *child_iseq;
8462
8463 ADD_LABEL(ret, retry_label);
8464 if (nd_type_p(node, NODE_FOR)) {
8465 CHECK(COMPILE(ret, "iter caller (for)", RNODE_FOR(node)->nd_iter));
8466
8467 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
8468 NEW_CHILD_ISEQ(RNODE_FOR(node)->nd_body, make_name_for_block(iseq),
8469 ISEQ_TYPE_BLOCK, line);
8470 ADD_SEND_WITH_BLOCK(ret, line_node, idEach, INT2FIX(0), child_iseq);
8471 }
8472 else {
8473 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
8474 NEW_CHILD_ISEQ(RNODE_ITER(node)->nd_body, make_name_for_block(iseq),
8475 ISEQ_TYPE_BLOCK, line);
8476 CHECK(COMPILE(ret, "iter caller", RNODE_ITER(node)->nd_iter));
8477 }
8478
8479 {
8480 // We need to put the label "retry_end_l" immediately after the last "send" instruction.
8481 // This because vm_throw checks if the break cont is equal to the index of next insn of the "send".
8482 // (Otherwise, it is considered "break from proc-closure". See "TAG_BREAK" handling in "vm_throw_start".)
8483 //
8484 // Normally, "send" instruction is at the last.
8485 // However, qcall under branch coverage measurement adds some instructions after the "send".
8486 //
8487 // Note that "invokesuper", "invokesuperforward" appears instead of "send".
8488 INSN *iobj;
8489 LINK_ELEMENT *last_elem = LAST_ELEMENT(ret);
8490 iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem);
8491 while (!IS_INSN_ID(iobj, send) && !IS_INSN_ID(iobj, invokesuper) && !IS_INSN_ID(iobj, sendforward) && !IS_INSN_ID(iobj, invokesuperforward)) {
8492 iobj = (INSN*) get_prev_insn(iobj);
8493 }
8494 ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l);
8495
8496 // LINK_ANCHOR has a pointer to the last element, but ELEM_INSERT_NEXT does not update it
8497 // even if we add an insn to the last of LINK_ANCHOR. So this updates it manually.
8498 if (&iobj->link == LAST_ELEMENT(ret)) {
8499 ret->last = (LINK_ELEMENT*) retry_end_l;
8500 }
8501 }
8502
8503 if (popped) {
8504 ADD_INSN(ret, line_node, pop);
8505 }
8506
8507 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
8508
8509 ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
8510 return COMPILE_OK;
8511}
8512
8513static int
8514compile_for_masgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8515{
8516 /* massign to var in "for"
8517 * (args.length == 1 && Array.try_convert(args[0])) || args
8518 */
8519 const NODE *line_node = node;
8520 const NODE *var = RNODE_FOR_MASGN(node)->nd_var;
8521 LABEL *not_single = NEW_LABEL(nd_line(var));
8522 LABEL *not_ary = NEW_LABEL(nd_line(var));
8523 CHECK(COMPILE(ret, "for var", var));
8524 ADD_INSN(ret, line_node, dup);
8525 ADD_CALL(ret, line_node, idLength, INT2FIX(0));
8526 ADD_INSN1(ret, line_node, putobject, INT2FIX(1));
8527 ADD_CALL(ret, line_node, idEq, INT2FIX(1));
8528 ADD_INSNL(ret, line_node, branchunless, not_single);
8529 ADD_INSN(ret, line_node, dup);
8530 ADD_INSN1(ret, line_node, putobject, INT2FIX(0));
8531 ADD_CALL(ret, line_node, idAREF, INT2FIX(1));
8532 ADD_INSN1(ret, line_node, putobject, rb_cArray);
8533 ADD_INSN(ret, line_node, swap);
8534 ADD_CALL(ret, line_node, rb_intern("try_convert"), INT2FIX(1));
8535 ADD_INSN(ret, line_node, dup);
8536 ADD_INSNL(ret, line_node, branchunless, not_ary);
8537 ADD_INSN(ret, line_node, swap);
8538 ADD_LABEL(ret, not_ary);
8539 ADD_INSN(ret, line_node, pop);
8540 ADD_LABEL(ret, not_single);
8541 return COMPILE_OK;
8542}
8543
8544static int
8545compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8546{
8547 const NODE *line_node = node;
8548 unsigned long throw_flag = 0;
8549
8550 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
8551 /* while/until */
8552 LABEL *splabel = NEW_LABEL(0);
8553 ADD_LABEL(ret, splabel);
8554 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
8555 CHECK(COMPILE_(ret, "break val (while/until)", RNODE_BREAK(node)->nd_stts,
8556 ISEQ_COMPILE_DATA(iseq)->loopval_popped));
8557 add_ensure_iseq(ret, iseq, 0);
8558 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
8559 ADD_ADJUST_RESTORE(ret, splabel);
8560
8561 if (!popped) {
8562 ADD_INSN(ret, line_node, putnil);
8563 }
8564 }
8565 else {
8566 const rb_iseq_t *ip = iseq;
8567
8568 while (ip) {
8569 if (!ISEQ_COMPILE_DATA(ip)) {
8570 ip = 0;
8571 break;
8572 }
8573
8574 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8575 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
8576 }
8577 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8578 throw_flag = 0;
8579 }
8580 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8581 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with break");
8582 return COMPILE_NG;
8583 }
8584 else {
8585 ip = ISEQ_BODY(ip)->parent_iseq;
8586 continue;
8587 }
8588
8589 /* escape from block */
8590 CHECK(COMPILE(ret, "break val (block)", RNODE_BREAK(node)->nd_stts));
8591 ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_BREAK));
8592 if (popped) {
8593 ADD_INSN(ret, line_node, pop);
8594 }
8595 return COMPILE_OK;
8596 }
8597 COMPILE_ERROR(ERROR_ARGS "Invalid break");
8598 return COMPILE_NG;
8599 }
8600 return COMPILE_OK;
8601}
8602
8603static int
8604compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8605{
8606 const NODE *line_node = node;
8607 unsigned long throw_flag = 0;
8608
8609 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
8610 LABEL *splabel = NEW_LABEL(0);
8611 debugs("next in while loop\n");
8612 ADD_LABEL(ret, splabel);
8613 CHECK(COMPILE(ret, "next val/valid syntax?", RNODE_NEXT(node)->nd_stts));
8614 add_ensure_iseq(ret, iseq, 0);
8615 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
8616 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8617 ADD_ADJUST_RESTORE(ret, splabel);
8618 if (!popped) {
8619 ADD_INSN(ret, line_node, putnil);
8620 }
8621 }
8622 else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
8623 LABEL *splabel = NEW_LABEL(0);
8624 debugs("next in block\n");
8625 ADD_LABEL(ret, splabel);
8626 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
8627 CHECK(COMPILE(ret, "next val", RNODE_NEXT(node)->nd_stts));
8628 add_ensure_iseq(ret, iseq, 0);
8629 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
8630 ADD_ADJUST_RESTORE(ret, splabel);
8631
8632 if (!popped) {
8633 ADD_INSN(ret, line_node, putnil);
8634 }
8635 }
8636 else {
8637 const rb_iseq_t *ip = iseq;
8638
8639 while (ip) {
8640 if (!ISEQ_COMPILE_DATA(ip)) {
8641 ip = 0;
8642 break;
8643 }
8644
8645 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
8646 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8647 /* while loop */
8648 break;
8649 }
8650 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8651 break;
8652 }
8653 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8654 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with next");
8655 return COMPILE_NG;
8656 }
8657
8658 ip = ISEQ_BODY(ip)->parent_iseq;
8659 }
8660 if (ip != 0) {
8661 CHECK(COMPILE(ret, "next val", RNODE_NEXT(node)->nd_stts));
8662 ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_NEXT));
8663
8664 if (popped) {
8665 ADD_INSN(ret, line_node, pop);
8666 }
8667 }
8668 else {
8669 COMPILE_ERROR(ERROR_ARGS "Invalid next");
8670 return COMPILE_NG;
8671 }
8672 }
8673 return COMPILE_OK;
8674}
8675
8676static int
8677compile_redo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8678{
8679 const NODE *line_node = node;
8680
8681 if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
8682 LABEL *splabel = NEW_LABEL(0);
8683 debugs("redo in while");
8684 ADD_LABEL(ret, splabel);
8685 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
8686 add_ensure_iseq(ret, iseq, 0);
8687 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
8688 ADD_ADJUST_RESTORE(ret, splabel);
8689 if (!popped) {
8690 ADD_INSN(ret, line_node, putnil);
8691 }
8692 }
8693 else if (ISEQ_BODY(iseq)->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
8694 LABEL *splabel = NEW_LABEL(0);
8695
8696 debugs("redo in block");
8697 ADD_LABEL(ret, splabel);
8698 add_ensure_iseq(ret, iseq, 0);
8699 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
8700 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8701 ADD_ADJUST_RESTORE(ret, splabel);
8702
8703 if (!popped) {
8704 ADD_INSN(ret, line_node, putnil);
8705 }
8706 }
8707 else {
8708 const rb_iseq_t *ip = iseq;
8709
8710 while (ip) {
8711 if (!ISEQ_COMPILE_DATA(ip)) {
8712 ip = 0;
8713 break;
8714 }
8715
8716 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8717 break;
8718 }
8719 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8720 break;
8721 }
8722 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8723 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with redo");
8724 return COMPILE_NG;
8725 }
8726
8727 ip = ISEQ_BODY(ip)->parent_iseq;
8728 }
8729 if (ip != 0) {
8730 ADD_INSN(ret, line_node, putnil);
8731 ADD_INSN1(ret, line_node, throw, INT2FIX(VM_THROW_NO_ESCAPE_FLAG | TAG_REDO));
8732
8733 if (popped) {
8734 ADD_INSN(ret, line_node, pop);
8735 }
8736 }
8737 else {
8738 COMPILE_ERROR(ERROR_ARGS "Invalid redo");
8739 return COMPILE_NG;
8740 }
8741 }
8742 return COMPILE_OK;
8743}
8744
8745static int
8746compile_retry(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8747{
8748 const NODE *line_node = node;
8749
8750 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
8751 ADD_INSN(ret, line_node, putnil);
8752 ADD_INSN1(ret, line_node, throw, INT2FIX(TAG_RETRY));
8753
8754 if (popped) {
8755 ADD_INSN(ret, line_node, pop);
8756 }
8757 }
8758 else {
8759 COMPILE_ERROR(ERROR_ARGS "Invalid retry");
8760 return COMPILE_NG;
8761 }
8762 return COMPILE_OK;
8763}
8764
8765static int
8766compile_rescue(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8767{
8768 const int line = nd_line(node);
8769 const NODE *line_node = node;
8770 LABEL *lstart = NEW_LABEL(line);
8771 LABEL *lend = NEW_LABEL(line);
8772 LABEL *lcont = NEW_LABEL(line);
8773 const rb_iseq_t *rescue = NEW_CHILD_ISEQ(RNODE_RESCUE(node)->nd_resq,
8774 rb_str_concat(rb_str_new2("rescue in "),
8775 ISEQ_BODY(iseq)->location.label),
8776 ISEQ_TYPE_RESCUE, line);
8777
8778 lstart->rescued = LABEL_RESCUE_BEG;
8779 lend->rescued = LABEL_RESCUE_END;
8780 ADD_LABEL(ret, lstart);
8781
8782 bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
8783 ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
8784 {
8785 CHECK(COMPILE(ret, "rescue head", RNODE_RESCUE(node)->nd_head));
8786 }
8787 ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
8788
8789 ADD_LABEL(ret, lend);
8790 if (RNODE_RESCUE(node)->nd_else) {
8791 ADD_INSN(ret, line_node, pop);
8792 CHECK(COMPILE(ret, "rescue else", RNODE_RESCUE(node)->nd_else));
8793 }
8794 ADD_INSN(ret, line_node, nop);
8795 ADD_LABEL(ret, lcont);
8796
8797 if (popped) {
8798 ADD_INSN(ret, line_node, pop);
8799 }
8800
8801 /* register catch entry */
8802 ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lcont);
8803 ADD_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
8804 return COMPILE_OK;
8805}
8806
8807static int
8808compile_resbody(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8809{
8810 const int line = nd_line(node);
8811 const NODE *line_node = node;
8812 const NODE *resq = node;
8813 const NODE *narg;
8814 LABEL *label_miss, *label_hit;
8815
8816 while (resq) {
8817 label_miss = NEW_LABEL(line);
8818 label_hit = NEW_LABEL(line);
8819
8820 narg = RNODE_RESBODY(resq)->nd_args;
8821 if (narg) {
8822 switch (nd_type(narg)) {
8823 case NODE_LIST:
8824 while (narg) {
8825 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
8826 CHECK(COMPILE(ret, "rescue arg", RNODE_LIST(narg)->nd_head));
8827 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
8828 ADD_INSNL(ret, line_node, branchif, label_hit);
8829 narg = RNODE_LIST(narg)->nd_next;
8830 }
8831 break;
8832 case NODE_SPLAT:
8833 case NODE_ARGSCAT:
8834 case NODE_ARGSPUSH:
8835 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
8836 CHECK(COMPILE(ret, "rescue/cond splat", narg));
8837 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE | VM_CHECKMATCH_ARRAY));
8838 ADD_INSNL(ret, line_node, branchif, label_hit);
8839 break;
8840 default:
8841 UNKNOWN_NODE("NODE_RESBODY", narg, COMPILE_NG);
8842 }
8843 }
8844 else {
8845 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
8846 ADD_INSN1(ret, line_node, putobject, rb_eStandardError);
8847 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
8848 ADD_INSNL(ret, line_node, branchif, label_hit);
8849 }
8850 ADD_INSNL(ret, line_node, jump, label_miss);
8851 ADD_LABEL(ret, label_hit);
8852 ADD_TRACE(ret, RUBY_EVENT_RESCUE);
8853
8854 if (RNODE_RESBODY(resq)->nd_exc_var) {
8855 CHECK(COMPILE_POPPED(ret, "resbody exc_var", RNODE_RESBODY(resq)->nd_exc_var));
8856 }
8857
8858 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) {
8859 // empty body
8860 ADD_SYNTHETIC_INSN(ret, nd_line(RNODE_RESBODY(resq)->nd_body), -1, putnil);
8861 }
8862 else {
8863 CHECK(COMPILE(ret, "resbody body", RNODE_RESBODY(resq)->nd_body));
8864 }
8865
8866 if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) {
8867 ADD_INSN(ret, line_node, nop);
8868 }
8869 ADD_INSN(ret, line_node, leave);
8870 ADD_LABEL(ret, label_miss);
8871 resq = RNODE_RESBODY(resq)->nd_next;
8872 }
8873 return COMPILE_OK;
8874}
8875
8876static int
8877compile_ensure(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8878{
8879 const int line = nd_line(RNODE_ENSURE(node)->nd_ensr);
8880 const NODE *line_node = node;
8881 DECL_ANCHOR(ensr);
8882 const rb_iseq_t *ensure = NEW_CHILD_ISEQ(RNODE_ENSURE(node)->nd_ensr,
8883 rb_str_concat(rb_str_new2 ("ensure in "), ISEQ_BODY(iseq)->location.label),
8884 ISEQ_TYPE_ENSURE, line);
8885 LABEL *lstart = NEW_LABEL(line);
8886 LABEL *lend = NEW_LABEL(line);
8887 LABEL *lcont = NEW_LABEL(line);
8888 LINK_ELEMENT *last;
8889 int last_leave = 0;
8890 struct ensure_range er;
8892 struct ensure_range *erange;
8893
8894 INIT_ANCHOR(ensr);
8895 CHECK(COMPILE_POPPED(ensr, "ensure ensr", RNODE_ENSURE(node)->nd_ensr));
8896 last = ensr->last;
8897 last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave);
8898
8899 er.begin = lstart;
8900 er.end = lend;
8901 er.next = 0;
8902 push_ensure_entry(iseq, &enl, &er, RNODE_ENSURE(node)->nd_ensr);
8903
8904 ADD_LABEL(ret, lstart);
8905 CHECK(COMPILE_(ret, "ensure head", RNODE_ENSURE(node)->nd_head, (popped | last_leave)));
8906 ADD_LABEL(ret, lend);
8907 ADD_SEQ(ret, ensr);
8908 if (!popped && last_leave) ADD_INSN(ret, line_node, putnil);
8909 ADD_LABEL(ret, lcont);
8910 if (last_leave) ADD_INSN(ret, line_node, pop);
8911
8912 erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
8913 if (lstart->link.next != &lend->link) {
8914 while (erange) {
8915 ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end,
8916 ensure, lcont);
8917 erange = erange->next;
8918 }
8919 }
8920
8921 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
8922 return COMPILE_OK;
8923}
8924
8925static int
8926compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8927{
8928 const NODE *line_node = node;
8929
8930 if (iseq) {
8931 enum rb_iseq_type type = ISEQ_BODY(iseq)->type;
8932 const rb_iseq_t *is = iseq;
8933 enum rb_iseq_type t = type;
8934 const NODE *retval = RNODE_RETURN(node)->nd_stts;
8935 LABEL *splabel = 0;
8936
8937 while (t == ISEQ_TYPE_RESCUE || t == ISEQ_TYPE_ENSURE) {
8938 if (!(is = ISEQ_BODY(is)->parent_iseq)) break;
8939 t = ISEQ_BODY(is)->type;
8940 }
8941 switch (t) {
8942 case ISEQ_TYPE_TOP:
8943 case ISEQ_TYPE_MAIN:
8944 if (retval) {
8945 rb_warn("argument of top-level return is ignored");
8946 }
8947 if (is == iseq) {
8948 /* plain top-level, leave directly */
8949 type = ISEQ_TYPE_METHOD;
8950 }
8951 break;
8952 default:
8953 break;
8954 }
8955
8956 if (type == ISEQ_TYPE_METHOD) {
8957 splabel = NEW_LABEL(0);
8958 ADD_LABEL(ret, splabel);
8959 ADD_ADJUST(ret, line_node, 0);
8960 }
8961
8962 CHECK(COMPILE(ret, "return nd_stts (return val)", retval));
8963
8964 if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
8965 add_ensure_iseq(ret, iseq, 1);
8966 ADD_TRACE(ret, RUBY_EVENT_RETURN);
8967 ADD_INSN(ret, line_node, leave);
8968 ADD_ADJUST_RESTORE(ret, splabel);
8969
8970 if (!popped) {
8971 ADD_INSN(ret, line_node, putnil);
8972 }
8973 }
8974 else {
8975 ADD_INSN1(ret, line_node, throw, INT2FIX(TAG_RETURN));
8976 if (popped) {
8977 ADD_INSN(ret, line_node, pop);
8978 }
8979 }
8980 }
8981 return COMPILE_OK;
8982}
8983
8984static bool
8985drop_unreachable_return(LINK_ANCHOR *ret)
8986{
8987 LINK_ELEMENT *i = ret->last, *last;
8988 if (!i) return false;
8989 if (IS_TRACE(i)) i = i->prev;
8990 if (!IS_INSN(i) || !IS_INSN_ID(i, putnil)) return false;
8991 last = i = i->prev;
8992 if (IS_ADJUST(i)) i = i->prev;
8993 if (!IS_INSN(i)) return false;
8994 switch (INSN_OF(i)) {
8995 case BIN(leave):
8996 case BIN(jump):
8997 break;
8998 default:
8999 return false;
9000 }
9001 (ret->last = last->prev)->next = NULL;
9002 return true;
9003}
9004
9005static int
9006compile_evstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9007{
9008 CHECK(COMPILE_(ret, "nd_body", node, popped));
9009
9010 if (!popped && !all_string_result_p(node)) {
9011 const NODE *line_node = node;
9012 const unsigned int flag = VM_CALL_FCALL;
9013
9014 // Note, this dup could be removed if we are willing to change anytostring. It pops
9015 // two VALUEs off the stack when it could work by replacing the top most VALUE.
9016 ADD_INSN(ret, line_node, dup);
9017 ADD_INSN1(ret, line_node, objtostring, new_callinfo(iseq, idTo_s, 0, flag, NULL, FALSE));
9018 ADD_INSN(ret, line_node, anytostring);
9019 }
9020 return COMPILE_OK;
9021}
9022
9023static void
9024compile_lvar(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *line_node, ID id)
9025{
9026 int idx = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
9027
9028 debugs("id: %s idx: %d\n", rb_id2name(id), idx);
9029 ADD_GETLOCAL(ret, line_node, idx, get_lvar_level(iseq));
9030}
9031
9032static LABEL *
9033qcall_branch_start(rb_iseq_t *iseq, LINK_ANCHOR *const recv, VALUE *branches, const NODE *node, const NODE *line_node)
9034{
9035 LABEL *else_label = NEW_LABEL(nd_line(line_node));
9036 VALUE br = 0;
9037
9038 br = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "&.");
9039 *branches = br;
9040 ADD_INSN(recv, line_node, dup);
9041 ADD_INSNL(recv, line_node, branchnil, else_label);
9042 add_trace_branch_coverage(iseq, recv, nd_code_loc(node), nd_node_id(node), 0, "then", br);
9043 return else_label;
9044}
9045
9046static void
9047qcall_branch_end(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *else_label, VALUE branches, const NODE *node, const NODE *line_node)
9048{
9049 LABEL *end_label;
9050 if (!else_label) return;
9051 end_label = NEW_LABEL(nd_line(line_node));
9052 ADD_INSNL(ret, line_node, jump, end_label);
9053 ADD_LABEL(ret, else_label);
9054 add_trace_branch_coverage(iseq, ret, nd_code_loc(node), nd_node_id(node), 1, "else", branches);
9055 ADD_LABEL(ret, end_label);
9056}
9057
9058static int
9059compile_call_precheck_freeze(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const NODE *line_node, int popped)
9060{
9061 /* optimization shortcut
9062 * "literal".freeze -> opt_str_freeze("literal")
9063 */
9064 if (get_nd_recv(node) &&
9065 (nd_type_p(get_nd_recv(node), NODE_STR) || nd_type_p(get_nd_recv(node), NODE_FILE)) &&
9066 (get_node_call_nd_mid(node) == idFreeze || get_node_call_nd_mid(node) == idUMinus) &&
9067 get_nd_args(node) == NULL &&
9068 ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
9069 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
9070 VALUE str = get_string_value(get_nd_recv(node));
9071 if (get_node_call_nd_mid(node) == idUMinus) {
9072 ADD_INSN2(ret, line_node, opt_str_uminus, str,
9073 new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE));
9074 }
9075 else {
9076 ADD_INSN2(ret, line_node, opt_str_freeze, str,
9077 new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE));
9078 }
9079 RB_OBJ_WRITTEN(iseq, Qundef, str);
9080 if (popped) {
9081 ADD_INSN(ret, line_node, pop);
9082 }
9083 return TRUE;
9084 }
9085 return FALSE;
9086}
9087
9088static int
9089iseq_has_builtin_function_table(const rb_iseq_t *iseq)
9090{
9091 return ISEQ_COMPILE_DATA(iseq)->builtin_function_table != NULL;
9092}
9093
9094static const struct rb_builtin_function *
9095iseq_builtin_function_lookup(const rb_iseq_t *iseq, const char *name)
9096{
9097 int i;
9098 const struct rb_builtin_function *table = ISEQ_COMPILE_DATA(iseq)->builtin_function_table;
9099 for (i=0; table[i].index != -1; i++) {
9100 if (strcmp(table[i].name, name) == 0) {
9101 return &table[i];
9102 }
9103 }
9104 return NULL;
9105}
9106
9107static const char *
9108iseq_builtin_function_name(const enum node_type type, const NODE *recv, ID mid)
9109{
9110 const char *name = rb_id2name(mid);
9111 static const char prefix[] = "__builtin_";
9112 const size_t prefix_len = sizeof(prefix) - 1;
9113
9114 switch (type) {
9115 case NODE_CALL:
9116 if (recv) {
9117 switch (nd_type(recv)) {
9118 case NODE_VCALL:
9119 if (RNODE_VCALL(recv)->nd_mid == rb_intern("__builtin")) {
9120 return name;
9121 }
9122 break;
9123 case NODE_CONST:
9124 if (RNODE_CONST(recv)->nd_vid == rb_intern("Primitive")) {
9125 return name;
9126 }
9127 break;
9128 default: break;
9129 }
9130 }
9131 break;
9132 case NODE_VCALL:
9133 case NODE_FCALL:
9134 if (UNLIKELY(strncmp(prefix, name, prefix_len) == 0)) {
9135 return &name[prefix_len];
9136 }
9137 break;
9138 default: break;
9139 }
9140 return NULL;
9141}
9142
9143static int
9144delegate_call_p(const rb_iseq_t *iseq, unsigned int argc, const LINK_ANCHOR *args, unsigned int *pstart_index)
9145{
9146
9147 if (argc == 0) {
9148 *pstart_index = 0;
9149 return TRUE;
9150 }
9151 else if (argc <= ISEQ_BODY(iseq)->local_table_size) {
9152 unsigned int start=0;
9153
9154 // local_table: [p1, p2, p3, l1, l2, l3]
9155 // arguments: [p3, l1, l2] -> 2
9156 for (start = 0;
9157 argc + start <= ISEQ_BODY(iseq)->local_table_size;
9158 start++) {
9159 const LINK_ELEMENT *elem = FIRST_ELEMENT(args);
9160
9161 for (unsigned int i=start; i-start<argc; i++) {
9162 if (IS_INSN(elem) &&
9163 INSN_OF(elem) == BIN(getlocal)) {
9164 int local_index = FIX2INT(OPERAND_AT(elem, 0));
9165 int local_level = FIX2INT(OPERAND_AT(elem, 1));
9166
9167 if (local_level == 0) {
9168 unsigned int index = ISEQ_BODY(iseq)->local_table_size - (local_index - VM_ENV_DATA_SIZE + 1);
9169 if (0) { // for debug
9170 fprintf(stderr, "lvar:%s (%d), id:%s (%d) local_index:%d, local_size:%d\n",
9171 rb_id2name(ISEQ_BODY(iseq)->local_table[i]), i,
9172 rb_id2name(ISEQ_BODY(iseq)->local_table[index]), index,
9173 local_index, (int)ISEQ_BODY(iseq)->local_table_size);
9174 }
9175 if (i == index) {
9176 elem = elem->next;
9177 continue; /* for */
9178 }
9179 else {
9180 goto next;
9181 }
9182 }
9183 else {
9184 goto fail; // level != 0 is unsupported
9185 }
9186 }
9187 else {
9188 goto fail; // insn is not a getlocal
9189 }
9190 }
9191 goto success;
9192 next:;
9193 }
9194 fail:
9195 return FALSE;
9196 success:
9197 *pstart_index = start;
9198 return TRUE;
9199 }
9200 else {
9201 return FALSE;
9202 }
9203}
9204
9205// Compile Primitive.attr! :leaf, ...
9206static int
9207compile_builtin_attr(rb_iseq_t *iseq, const NODE *node)
9208{
9209 VALUE symbol;
9210 VALUE string;
9211 if (!node) goto no_arg;
9212 while (node) {
9213 if (!nd_type_p(node, NODE_LIST)) goto bad_arg;
9214 const NODE *next = RNODE_LIST(node)->nd_next;
9215
9216 node = RNODE_LIST(node)->nd_head;
9217 if (!node) goto no_arg;
9218 switch (nd_type(node)) {
9219 case NODE_SYM:
9220 symbol = rb_node_sym_string_val(node);
9221 break;
9222 default:
9223 goto bad_arg;
9224 }
9225
9226 if (!SYMBOL_P(symbol)) goto non_symbol_arg;
9227
9228 string = rb_sym2str(symbol);
9229 if (strcmp(RSTRING_PTR(string), "leaf") == 0) {
9230 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_LEAF;
9231 }
9232 else if (strcmp(RSTRING_PTR(string), "inline_block") == 0) {
9233 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_INLINE_BLOCK;
9234 }
9235 else if (strcmp(RSTRING_PTR(string), "use_block") == 0) {
9236 iseq_set_use_block(iseq);
9237 }
9238 else if (strcmp(RSTRING_PTR(string), "c_trace") == 0) {
9239 // Let the iseq act like a C method in backtraces
9240 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_C_TRACE;
9241 }
9242 else if (strcmp(RSTRING_PTR(string), "without_interrupts") == 0) {
9243 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_WITHOUT_INTERRUPTS;
9244 }
9245 else {
9246 goto unknown_arg;
9247 }
9248 node = next;
9249 }
9250 return COMPILE_OK;
9251 no_arg:
9252 COMPILE_ERROR(ERROR_ARGS "attr!: no argument");
9253 return COMPILE_NG;
9254 non_symbol_arg:
9255 COMPILE_ERROR(ERROR_ARGS "non symbol argument to attr!: %s", rb_builtin_class_name(symbol));
9256 return COMPILE_NG;
9257 unknown_arg:
9258 COMPILE_ERROR(ERROR_ARGS "unknown argument to attr!: %s", RSTRING_PTR(string));
9259 return COMPILE_NG;
9260 bad_arg:
9261 UNKNOWN_NODE("attr!", node, COMPILE_NG);
9262}
9263
9264static int
9265compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, const NODE *line_node, int popped)
9266{
9267 VALUE name;
9268
9269 if (!node) goto no_arg;
9270 if (!nd_type_p(node, NODE_LIST)) goto bad_arg;
9271 if (RNODE_LIST(node)->nd_next) goto too_many_arg;
9272 node = RNODE_LIST(node)->nd_head;
9273 if (!node) goto no_arg;
9274 switch (nd_type(node)) {
9275 case NODE_SYM:
9276 name = rb_node_sym_string_val(node);
9277 break;
9278 default:
9279 goto bad_arg;
9280 }
9281 if (!SYMBOL_P(name)) goto non_symbol_arg;
9282 if (!popped) {
9283 compile_lvar(iseq, ret, line_node, SYM2ID(name));
9284 }
9285 return COMPILE_OK;
9286 no_arg:
9287 COMPILE_ERROR(ERROR_ARGS "arg!: no argument");
9288 return COMPILE_NG;
9289 too_many_arg:
9290 COMPILE_ERROR(ERROR_ARGS "arg!: too many argument");
9291 return COMPILE_NG;
9292 non_symbol_arg:
9293 COMPILE_ERROR(ERROR_ARGS "non symbol argument to arg!: %s",
9294 rb_builtin_class_name(name));
9295 return COMPILE_NG;
9296 bad_arg:
9297 UNKNOWN_NODE("arg!", node, COMPILE_NG);
9298}
9299
9300static NODE *
9301mandatory_node(const rb_iseq_t *iseq, const NODE *cond_node)
9302{
9303 const NODE *node = ISEQ_COMPILE_DATA(iseq)->root_node;
9304 if (nd_type(node) == NODE_IF && RNODE_IF(node)->nd_cond == cond_node) {
9305 return RNODE_IF(node)->nd_body;
9306 }
9307 else {
9308 rb_bug("mandatory_node: can't find mandatory node");
9309 }
9310}
9311
9312static int
9313compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const NODE *line_node)
9314{
9315 // arguments
9316 struct rb_args_info args = {
9317 .pre_args_num = ISEQ_BODY(iseq)->param.lead_num,
9318 };
9319 rb_node_args_t args_node;
9320 rb_node_init(RNODE(&args_node), NODE_ARGS);
9321 args_node.nd_ainfo = args;
9322
9323 // local table without non-mandatory parameters
9324 const int skip_local_size = ISEQ_BODY(iseq)->param.size - ISEQ_BODY(iseq)->param.lead_num;
9325 const int table_size = ISEQ_BODY(iseq)->local_table_size - skip_local_size;
9326
9327 VALUE idtmp = 0;
9328 rb_ast_id_table_t *tbl = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
9329 tbl->size = table_size;
9330
9331 int i;
9332
9333 // lead parameters
9334 for (i=0; i<ISEQ_BODY(iseq)->param.lead_num; i++) {
9335 tbl->ids[i] = ISEQ_BODY(iseq)->local_table[i];
9336 }
9337 // local variables
9338 for (; i<table_size; i++) {
9339 tbl->ids[i] = ISEQ_BODY(iseq)->local_table[i + skip_local_size];
9340 }
9341
9342 rb_node_scope_t scope_node;
9343 rb_node_init(RNODE(&scope_node), NODE_SCOPE);
9344 scope_node.nd_tbl = tbl;
9345 scope_node.nd_body = mandatory_node(iseq, node);
9346 scope_node.nd_parent = NULL;
9347 scope_node.nd_args = &args_node;
9348
9349 VALUE ast_value = rb_ruby_ast_new(RNODE(&scope_node));
9350
9351 const rb_iseq_t *mandatory_only_iseq =
9352 rb_iseq_new_with_opt(ast_value, rb_iseq_base_label(iseq),
9353 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
9354 nd_line(line_node), NULL, 0,
9355 ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option,
9356 ISEQ_BODY(iseq)->variable.script_lines);
9357 RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->mandatory_only_iseq, (VALUE)mandatory_only_iseq);
9358
9359 ALLOCV_END(idtmp);
9360 return COMPILE_OK;
9361}
9362
9363static int
9364compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const NODE *line_node, int popped,
9365 const rb_iseq_t *parent_block, LINK_ANCHOR *args, const char *builtin_func)
9366{
9367 NODE *args_node = get_nd_args(node);
9368
9369 if (parent_block != NULL) {
9370 COMPILE_ERROR(ERROR_ARGS_AT(line_node) "should not call builtins here.");
9371 return COMPILE_NG;
9372 }
9373 else {
9374# define BUILTIN_INLINE_PREFIX "_bi"
9375 char inline_func[sizeof(BUILTIN_INLINE_PREFIX) + DECIMAL_SIZE_OF(int)];
9376 bool cconst = false;
9377 retry:;
9378 const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
9379
9380 if (bf == NULL) {
9381 if (strcmp("cstmt!", builtin_func) == 0 ||
9382 strcmp("cexpr!", builtin_func) == 0) {
9383 // ok
9384 }
9385 else if (strcmp("cconst!", builtin_func) == 0) {
9386 cconst = true;
9387 }
9388 else if (strcmp("cinit!", builtin_func) == 0) {
9389 // ignore
9390 return COMPILE_OK;
9391 }
9392 else if (strcmp("attr!", builtin_func) == 0) {
9393 return compile_builtin_attr(iseq, args_node);
9394 }
9395 else if (strcmp("arg!", builtin_func) == 0) {
9396 return compile_builtin_arg(iseq, ret, args_node, line_node, popped);
9397 }
9398 else if (strcmp("mandatory_only?", builtin_func) == 0) {
9399 if (popped) {
9400 rb_bug("mandatory_only? should be in if condition");
9401 }
9402 else if (!LIST_INSN_SIZE_ZERO(ret)) {
9403 rb_bug("mandatory_only? should be put on top");
9404 }
9405
9406 ADD_INSN1(ret, line_node, putobject, Qfalse);
9407 return compile_builtin_mandatory_only_method(iseq, node, line_node);
9408 }
9409 else if (1) {
9410 rb_bug("can't find builtin function:%s", builtin_func);
9411 }
9412 else {
9413 COMPILE_ERROR(ERROR_ARGS "can't find builtin function:%s", builtin_func);
9414 return COMPILE_NG;
9415 }
9416
9417 int inline_index = nd_line(node);
9418 snprintf(inline_func, sizeof(inline_func), BUILTIN_INLINE_PREFIX "%d", inline_index);
9419 builtin_func = inline_func;
9420 args_node = NULL;
9421 goto retry;
9422 }
9423
9424 if (cconst) {
9425 typedef VALUE(*builtin_func0)(void *, VALUE);
9426 VALUE const_val = (*(builtin_func0)(uintptr_t)bf->func_ptr)(NULL, Qnil);
9427 ADD_INSN1(ret, line_node, putobject, const_val);
9428 return COMPILE_OK;
9429 }
9430
9431 // fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
9432
9433 unsigned int flag = 0;
9434 struct rb_callinfo_kwarg *keywords = NULL;
9435 VALUE argc = setup_args(iseq, args, args_node, &flag, &keywords);
9436
9437 if (FIX2INT(argc) != bf->argc) {
9438 COMPILE_ERROR(ERROR_ARGS "argc is not match for builtin function:%s (expect %d but %d)",
9439 builtin_func, bf->argc, FIX2INT(argc));
9440 return COMPILE_NG;
9441 }
9442
9443 unsigned int start_index;
9444 if (delegate_call_p(iseq, FIX2INT(argc), args, &start_index)) {
9445 ADD_INSN2(ret, line_node, opt_invokebuiltin_delegate, bf, INT2FIX(start_index));
9446 }
9447 else {
9448 ADD_SEQ(ret, args);
9449 ADD_INSN1(ret, line_node, invokebuiltin, bf);
9450 }
9451
9452 if (popped) ADD_INSN(ret, line_node, pop);
9453 return COMPILE_OK;
9454 }
9455}
9456
9457static int
9458compile_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)
9459{
9460 /* call: obj.method(...)
9461 * fcall: func(...)
9462 * vcall: func
9463 */
9464 DECL_ANCHOR(recv);
9465 DECL_ANCHOR(args);
9466 ID mid = get_node_call_nd_mid(node);
9467 VALUE argc;
9468 unsigned int flag = 0;
9469 struct rb_callinfo_kwarg *keywords = NULL;
9470 const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
9471 LABEL *else_label = NULL;
9472 VALUE branches = Qfalse;
9473
9474 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
9475
9476 INIT_ANCHOR(recv);
9477 INIT_ANCHOR(args);
9478
9479#if OPT_SUPPORT_JOKE
9480 if (nd_type_p(node, NODE_VCALL)) {
9481 ID id_bitblt;
9482 ID id_answer;
9483
9484 CONST_ID(id_bitblt, "bitblt");
9485 CONST_ID(id_answer, "the_answer_to_life_the_universe_and_everything");
9486
9487 if (mid == id_bitblt) {
9488 ADD_INSN(ret, line_node, bitblt);
9489 return COMPILE_OK;
9490 }
9491 else if (mid == id_answer) {
9492 ADD_INSN(ret, line_node, answer);
9493 return COMPILE_OK;
9494 }
9495 }
9496 /* only joke */
9497 {
9498 ID goto_id;
9499 ID label_id;
9500
9501 CONST_ID(goto_id, "__goto__");
9502 CONST_ID(label_id, "__label__");
9503
9504 if (nd_type_p(node, NODE_FCALL) &&
9505 (mid == goto_id || mid == label_id)) {
9506 LABEL *label;
9507 st_data_t data;
9508 st_table *labels_table = ISEQ_COMPILE_DATA(iseq)->labels_table;
9509 VALUE label_name;
9510
9511 if (!labels_table) {
9512 labels_table = st_init_numtable();
9513 ISEQ_COMPILE_DATA(iseq)->labels_table = labels_table;
9514 }
9515 {
9516 COMPILE_ERROR(ERROR_ARGS "invalid goto/label format");
9517 return COMPILE_NG;
9518 }
9519
9520 if (mid == goto_id) {
9521 ADD_INSNL(ret, line_node, jump, label);
9522 }
9523 else {
9524 ADD_LABEL(ret, label);
9525 }
9526 return COMPILE_OK;
9527 }
9528 }
9529#endif
9530
9531 const char *builtin_func;
9532 if (UNLIKELY(iseq_has_builtin_function_table(iseq)) &&
9533 (builtin_func = iseq_builtin_function_name(type, get_nd_recv(node), mid)) != NULL) {
9534 return compile_builtin_function_call(iseq, ret, node, line_node, popped, parent_block, args, builtin_func);
9535 }
9536
9537 /* receiver */
9538 if (!assume_receiver) {
9539 if (type == NODE_CALL || type == NODE_OPCALL || type == NODE_QCALL) {
9540 int idx, level;
9541
9542 if (mid == idCall &&
9543 nd_type_p(get_nd_recv(node), NODE_LVAR) &&
9544 iseq_block_param_id_p(iseq, RNODE_LVAR(get_nd_recv(node))->nd_vid, &idx, &level)) {
9545 ADD_INSN2(recv, get_nd_recv(node), getblockparamproxy, INT2FIX(idx + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
9546 }
9547 else if (private_recv_p(node)) {
9548 ADD_INSN(recv, node, putself);
9549 flag |= VM_CALL_FCALL;
9550 }
9551 else {
9552 CHECK(COMPILE(recv, "recv", get_nd_recv(node)));
9553 }
9554
9555 if (type == NODE_QCALL) {
9556 else_label = qcall_branch_start(iseq, recv, &branches, node, line_node);
9557 }
9558 }
9559 else if (type == NODE_FCALL || type == NODE_VCALL) {
9560 ADD_CALL_RECEIVER(recv, line_node);
9561 }
9562 }
9563
9564 /* args */
9565 if (type != NODE_VCALL) {
9566 argc = setup_args(iseq, args, get_nd_args(node), &flag, &keywords);
9567 CHECK(!NIL_P(argc));
9568 }
9569 else {
9570 argc = INT2FIX(0);
9571 }
9572
9573 ADD_SEQ(ret, recv);
9574
9575 bool inline_new = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction &&
9576 mid == rb_intern("new") &&
9577 parent_block == NULL &&
9578 !(flag & VM_CALL_ARGS_BLOCKARG);
9579
9580 if (inline_new) {
9581 ADD_INSN(ret, node, putnil);
9582 ADD_INSN(ret, node, swap);
9583 }
9584
9585 ADD_SEQ(ret, args);
9586
9587 debugp_param("call args argc", argc);
9588 debugp_param("call method", ID2SYM(mid));
9589
9590 switch ((int)type) {
9591 case NODE_VCALL:
9592 flag |= VM_CALL_VCALL;
9593 /* VCALL is funcall, so fall through */
9594 case NODE_FCALL:
9595 flag |= VM_CALL_FCALL;
9596 }
9597
9598 if ((flag & VM_CALL_ARGS_BLOCKARG) && (flag & VM_CALL_KW_SPLAT) && !(flag & VM_CALL_KW_SPLAT_MUT)) {
9599 ADD_INSN(ret, line_node, splatkw);
9600 }
9601
9602 LABEL *not_basic_new = NEW_LABEL(nd_line(node));
9603 LABEL *not_basic_new_finish = NEW_LABEL(nd_line(node));
9604
9605 if (inline_new) {
9606 // Jump unless the receiver uses the "basic" implementation of "new"
9607 VALUE ci;
9608 if (flag & VM_CALL_FORWARDING) {
9609 ci = (VALUE)new_callinfo(iseq, mid, NUM2INT(argc) + 1, flag, keywords, 0);
9610 }
9611 else {
9612 ci = (VALUE)new_callinfo(iseq, mid, NUM2INT(argc), flag, keywords, 0);
9613 }
9614 ADD_INSN2(ret, node, opt_new, ci, not_basic_new);
9615 LABEL_REF(not_basic_new);
9616
9617 // optimized path
9618 ADD_SEND_R(ret, line_node, rb_intern("initialize"), argc, parent_block, INT2FIX(flag | VM_CALL_FCALL), keywords);
9619 ADD_INSNL(ret, line_node, jump, not_basic_new_finish);
9620
9621 ADD_LABEL(ret, not_basic_new);
9622 // Fall back to normal send
9623 ADD_SEND_R(ret, line_node, mid, argc, parent_block, INT2FIX(flag), keywords);
9624 ADD_INSN(ret, line_node, swap);
9625
9626 ADD_LABEL(ret, not_basic_new_finish);
9627 ADD_INSN(ret, line_node, pop);
9628 }
9629 else {
9630 ADD_SEND_R(ret, line_node, mid, argc, parent_block, INT2FIX(flag), keywords);
9631 }
9632
9633 qcall_branch_end(iseq, ret, else_label, branches, node, line_node);
9634 if (popped) {
9635 ADD_INSN(ret, line_node, pop);
9636 }
9637 return COMPILE_OK;
9638}
9639
9640static int
9641compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9642{
9643 const int line = nd_line(node);
9644 VALUE argc;
9645 unsigned int flag = 0;
9646 int asgnflag = 0;
9647 ID id = RNODE_OP_ASGN1(node)->nd_mid;
9648
9649 /*
9650 * a[x] (op)= y
9651 *
9652 * nil # nil
9653 * eval a # nil a
9654 * eval x # nil a x
9655 * dupn 2 # nil a x a x
9656 * send :[] # nil a x a[x]
9657 * eval y # nil a x a[x] y
9658 * send op # nil a x ret
9659 * setn 3 # ret a x ret
9660 * send []= # ret ?
9661 * pop # ret
9662 */
9663
9664 /*
9665 * nd_recv[nd_args->nd_body] (nd_mid)= nd_args->nd_head;
9666 * NODE_OP_ASGN nd_recv
9667 * nd_args->nd_head
9668 * nd_args->nd_body
9669 * nd_mid
9670 */
9671
9672 if (!popped) {
9673 ADD_INSN(ret, node, putnil);
9674 }
9675 asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN1 recv", node, RNODE_OP_ASGN1(node)->nd_recv);
9676 CHECK(asgnflag != -1);
9677 switch (nd_type(RNODE_OP_ASGN1(node)->nd_index)) {
9678 case NODE_ZLIST:
9679 argc = INT2FIX(0);
9680 break;
9681 default:
9682 argc = setup_args(iseq, ret, RNODE_OP_ASGN1(node)->nd_index, &flag, NULL);
9683 CHECK(!NIL_P(argc));
9684 }
9685 int dup_argn = FIX2INT(argc) + 1;
9686 ADD_INSN1(ret, node, dupn, INT2FIX(dup_argn));
9687 flag |= asgnflag;
9688 ADD_SEND_R(ret, node, idAREF, argc, NULL, INT2FIX(flag & ~VM_CALL_ARGS_SPLAT_MUT), NULL);
9689
9690 if (id == idOROP || id == idANDOP) {
9691 /* a[x] ||= y or a[x] &&= y
9692
9693 unless/if a[x]
9694 a[x]= y
9695 else
9696 nil
9697 end
9698 */
9699 LABEL *label = NEW_LABEL(line);
9700 LABEL *lfin = NEW_LABEL(line);
9701
9702 ADD_INSN(ret, node, dup);
9703 if (id == idOROP) {
9704 ADD_INSNL(ret, node, branchif, label);
9705 }
9706 else { /* idANDOP */
9707 ADD_INSNL(ret, node, branchunless, label);
9708 }
9709 ADD_INSN(ret, node, pop);
9710
9711 CHECK(COMPILE(ret, "NODE_OP_ASGN1 nd_rvalue: ", RNODE_OP_ASGN1(node)->nd_rvalue));
9712 if (!popped) {
9713 ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
9714 }
9715 if (flag & VM_CALL_ARGS_SPLAT) {
9716 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
9717 ADD_INSN(ret, node, swap);
9718 ADD_INSN1(ret, node, splatarray, Qtrue);
9719 ADD_INSN(ret, node, swap);
9720 flag |= VM_CALL_ARGS_SPLAT_MUT;
9721 }
9722 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
9723 ADD_SEND_R(ret, node, idASET, argc, NULL, INT2FIX(flag), NULL);
9724 }
9725 else {
9726 ADD_SEND_R(ret, node, idASET, FIXNUM_INC(argc, 1), NULL, INT2FIX(flag), NULL);
9727 }
9728 ADD_INSN(ret, node, pop);
9729 ADD_INSNL(ret, node, jump, lfin);
9730 ADD_LABEL(ret, label);
9731 if (!popped) {
9732 ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
9733 }
9734 ADD_INSN1(ret, node, adjuststack, INT2FIX(dup_argn+1));
9735 ADD_LABEL(ret, lfin);
9736 }
9737 else {
9738 CHECK(COMPILE(ret, "NODE_OP_ASGN1 nd_rvalue: ", RNODE_OP_ASGN1(node)->nd_rvalue));
9739 ADD_SEND(ret, node, id, INT2FIX(1));
9740 if (!popped) {
9741 ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
9742 }
9743 if (flag & VM_CALL_ARGS_SPLAT) {
9744 if (flag & VM_CALL_KW_SPLAT) {
9745 ADD_INSN1(ret, node, topn, INT2FIX(2));
9746 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
9747 ADD_INSN1(ret, node, splatarray, Qtrue);
9748 flag |= VM_CALL_ARGS_SPLAT_MUT;
9749 }
9750 ADD_INSN(ret, node, swap);
9751 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
9752 ADD_INSN1(ret, node, setn, INT2FIX(2));
9753 ADD_INSN(ret, node, pop);
9754 }
9755 else {
9756 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
9757 ADD_INSN(ret, node, swap);
9758 ADD_INSN1(ret, node, splatarray, Qtrue);
9759 ADD_INSN(ret, node, swap);
9760 flag |= VM_CALL_ARGS_SPLAT_MUT;
9761 }
9762 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
9763 }
9764 ADD_SEND_R(ret, node, idASET, argc, NULL, INT2FIX(flag), NULL);
9765 }
9766 else {
9767 ADD_SEND_R(ret, node, idASET, FIXNUM_INC(argc, 1), NULL, INT2FIX(flag), NULL);
9768 }
9769 ADD_INSN(ret, node, pop);
9770 }
9771 return COMPILE_OK;
9772}
9773
9774static int
9775compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9776{
9777 const int line = nd_line(node);
9778 ID atype = RNODE_OP_ASGN2(node)->nd_mid;
9779 ID vid = RNODE_OP_ASGN2(node)->nd_vid, aid = rb_id_attrset(vid);
9780 int asgnflag;
9781 LABEL *lfin = NEW_LABEL(line);
9782 LABEL *lcfin = NEW_LABEL(line);
9783 LABEL *lskip = 0;
9784 /*
9785 class C; attr_accessor :c; end
9786 r = C.new
9787 r.a &&= v # asgn2
9788
9789 eval r # r
9790 dup # r r
9791 eval r.a # r o
9792
9793 # or
9794 dup # r o o
9795 if lcfin # r o
9796 pop # r
9797 eval v # r v
9798 swap # v r
9799 topn 1 # v r v
9800 send a= # v ?
9801 jump lfin # v ?
9802
9803 lcfin: # r o
9804 swap # o r
9805
9806 lfin: # o ?
9807 pop # o
9808
9809 # or (popped)
9810 if lcfin # r
9811 eval v # r v
9812 send a= # ?
9813 jump lfin # ?
9814
9815 lcfin: # r
9816
9817 lfin: # ?
9818 pop #
9819
9820 # and
9821 dup # r o o
9822 unless lcfin
9823 pop # r
9824 eval v # r v
9825 swap # v r
9826 topn 1 # v r v
9827 send a= # v ?
9828 jump lfin # v ?
9829
9830 # others
9831 eval v # r o v
9832 send ?? # r w
9833 send a= # w
9834
9835 */
9836
9837 asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN2#recv", node, RNODE_OP_ASGN2(node)->nd_recv);
9838 CHECK(asgnflag != -1);
9839 if (RNODE_OP_ASGN2(node)->nd_aid) {
9840 lskip = NEW_LABEL(line);
9841 ADD_INSN(ret, node, dup);
9842 ADD_INSNL(ret, node, branchnil, lskip);
9843 }
9844 ADD_INSN(ret, node, dup);
9845 ADD_SEND_WITH_FLAG(ret, node, vid, INT2FIX(0), INT2FIX(asgnflag));
9846
9847 if (atype == idOROP || atype == idANDOP) {
9848 if (!popped) {
9849 ADD_INSN(ret, node, dup);
9850 }
9851 if (atype == idOROP) {
9852 ADD_INSNL(ret, node, branchif, lcfin);
9853 }
9854 else { /* idANDOP */
9855 ADD_INSNL(ret, node, branchunless, lcfin);
9856 }
9857 if (!popped) {
9858 ADD_INSN(ret, node, pop);
9859 }
9860 CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", RNODE_OP_ASGN2(node)->nd_value));
9861 if (!popped) {
9862 ADD_INSN(ret, node, swap);
9863 ADD_INSN1(ret, node, topn, INT2FIX(1));
9864 }
9865 ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
9866 ADD_INSNL(ret, node, jump, lfin);
9867
9868 ADD_LABEL(ret, lcfin);
9869 if (!popped) {
9870 ADD_INSN(ret, node, swap);
9871 }
9872
9873 ADD_LABEL(ret, lfin);
9874 }
9875 else {
9876 CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", RNODE_OP_ASGN2(node)->nd_value));
9877 ADD_SEND(ret, node, atype, INT2FIX(1));
9878 if (!popped) {
9879 ADD_INSN(ret, node, swap);
9880 ADD_INSN1(ret, node, topn, INT2FIX(1));
9881 }
9882 ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
9883 }
9884 if (lskip && popped) {
9885 ADD_LABEL(ret, lskip);
9886 }
9887 ADD_INSN(ret, node, pop);
9888 if (lskip && !popped) {
9889 ADD_LABEL(ret, lskip);
9890 }
9891 return COMPILE_OK;
9892}
9893
9894static int compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value);
9895
9896static int
9897compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9898{
9899 const int line = nd_line(node);
9900 LABEL *lfin = 0;
9901 LABEL *lassign = 0;
9902 ID mid;
9903
9904 switch (nd_type(RNODE_OP_CDECL(node)->nd_head)) {
9905 case NODE_COLON3:
9906 ADD_INSN1(ret, node, putobject, rb_cObject);
9907 break;
9908 case NODE_COLON2:
9909 CHECK(COMPILE(ret, "NODE_OP_CDECL/colon2#nd_head", RNODE_COLON2(RNODE_OP_CDECL(node)->nd_head)->nd_head));
9910 break;
9911 default:
9912 COMPILE_ERROR(ERROR_ARGS "%s: invalid node in NODE_OP_CDECL",
9913 ruby_node_name(nd_type(RNODE_OP_CDECL(node)->nd_head)));
9914 return COMPILE_NG;
9915 }
9916 mid = get_node_colon_nd_mid(RNODE_OP_CDECL(node)->nd_head);
9917 /* cref */
9918 if (RNODE_OP_CDECL(node)->nd_aid == idOROP) {
9919 lassign = NEW_LABEL(line);
9920 ADD_INSN(ret, node, dup); /* cref cref */
9921 ADD_INSN3(ret, node, defined, INT2FIX(DEFINED_CONST_FROM),
9922 ID2SYM(mid), Qtrue); /* cref bool */
9923 ADD_INSNL(ret, node, branchunless, lassign); /* cref */
9924 }
9925 ADD_INSN(ret, node, dup); /* cref cref */
9926 ADD_INSN1(ret, node, putobject, Qtrue);
9927 ADD_INSN1(ret, node, getconstant, ID2SYM(mid)); /* cref obj */
9928
9929 if (RNODE_OP_CDECL(node)->nd_aid == idOROP || RNODE_OP_CDECL(node)->nd_aid == idANDOP) {
9930 lfin = NEW_LABEL(line);
9931 if (!popped) ADD_INSN(ret, node, dup); /* cref [obj] obj */
9932 if (RNODE_OP_CDECL(node)->nd_aid == idOROP)
9933 ADD_INSNL(ret, node, branchif, lfin);
9934 else /* idANDOP */
9935 ADD_INSNL(ret, node, branchunless, lfin);
9936 /* cref [obj] */
9937 if (!popped) ADD_INSN(ret, node, pop); /* cref */
9938 if (lassign) ADD_LABEL(ret, lassign);
9939 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value));
9940 /* cref value */
9941 if (popped)
9942 ADD_INSN1(ret, node, topn, INT2FIX(1)); /* cref value cref */
9943 else {
9944 ADD_INSN1(ret, node, dupn, INT2FIX(2)); /* cref value cref value */
9945 ADD_INSN(ret, node, swap); /* cref value value cref */
9946 }
9947 ADD_INSN1(ret, node, setconstant, ID2SYM(mid)); /* cref [value] */
9948 ADD_LABEL(ret, lfin); /* cref [value] */
9949 if (!popped) ADD_INSN(ret, node, swap); /* [value] cref */
9950 ADD_INSN(ret, node, pop); /* [value] */
9951 }
9952 else {
9953 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value));
9954 /* cref obj value */
9955 ADD_CALL(ret, node, RNODE_OP_CDECL(node)->nd_aid, INT2FIX(1));
9956 /* cref value */
9957 ADD_INSN(ret, node, swap); /* value cref */
9958 if (!popped) {
9959 ADD_INSN1(ret, node, topn, INT2FIX(1)); /* value cref value */
9960 ADD_INSN(ret, node, swap); /* value value cref */
9961 }
9962 ADD_INSN1(ret, node, setconstant, ID2SYM(mid));
9963 }
9964 return COMPILE_OK;
9965}
9966
9967static int
9968compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
9969{
9970 const int line = nd_line(node);
9971 LABEL *lfin = NEW_LABEL(line);
9972 LABEL *lassign;
9973
9974 if (type == NODE_OP_ASGN_OR && !nd_type_p(RNODE_OP_ASGN_OR(node)->nd_head, NODE_IVAR)) {
9975 LABEL *lfinish[2];
9976 lfinish[0] = lfin;
9977 lfinish[1] = 0;
9978 defined_expr(iseq, ret, RNODE_OP_ASGN_OR(node)->nd_head, lfinish, Qfalse, false);
9979 lassign = lfinish[1];
9980 if (!lassign) {
9981 lassign = NEW_LABEL(line);
9982 }
9983 ADD_INSNL(ret, node, branchunless, lassign);
9984 }
9985 else {
9986 lassign = NEW_LABEL(line);
9987 }
9988
9989 CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", RNODE_OP_ASGN_OR(node)->nd_head));
9990
9991 if (!popped) {
9992 ADD_INSN(ret, node, dup);
9993 }
9994
9995 if (type == NODE_OP_ASGN_AND) {
9996 ADD_INSNL(ret, node, branchunless, lfin);
9997 }
9998 else {
9999 ADD_INSNL(ret, node, branchif, lfin);
10000 }
10001
10002 if (!popped) {
10003 ADD_INSN(ret, node, pop);
10004 }
10005
10006 ADD_LABEL(ret, lassign);
10007 CHECK(COMPILE_(ret, "NODE_OP_ASGN_AND/OR#nd_value", RNODE_OP_ASGN_OR(node)->nd_value, popped));
10008 ADD_LABEL(ret, lfin);
10009 return COMPILE_OK;
10010}
10011
10012static int
10013compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
10014{
10015 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
10016 DECL_ANCHOR(args);
10017 int argc;
10018 unsigned int flag = 0;
10019 struct rb_callinfo_kwarg *keywords = NULL;
10020 const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
10021 int use_block = 1;
10022
10023 INIT_ANCHOR(args);
10024 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
10025
10026 if (type == NODE_SUPER) {
10027 VALUE vargc = setup_args(iseq, args, RNODE_SUPER(node)->nd_args, &flag, &keywords);
10028 CHECK(!NIL_P(vargc));
10029 argc = FIX2INT(vargc);
10030 if ((flag & VM_CALL_ARGS_BLOCKARG) && (flag & VM_CALL_KW_SPLAT) && !(flag & VM_CALL_KW_SPLAT_MUT)) {
10031 ADD_INSN(args, node, splatkw);
10032 }
10033
10034 if (flag & VM_CALL_ARGS_BLOCKARG) {
10035 use_block = 0;
10036 }
10037 }
10038 else {
10039 /* NODE_ZSUPER */
10040 int i;
10041 const rb_iseq_t *liseq = body->local_iseq;
10042 const struct rb_iseq_constant_body *const local_body = ISEQ_BODY(liseq);
10043 const struct rb_iseq_param_keyword *const local_kwd = local_body->param.keyword;
10044 int lvar_level = get_lvar_level(iseq);
10045
10046 argc = local_body->param.lead_num;
10047
10048 /* normal arguments */
10049 for (i = 0; i < local_body->param.lead_num; i++) {
10050 int idx = local_body->local_table_size - i;
10051 ADD_GETLOCAL(args, node, idx, lvar_level);
10052 }
10053
10054 /* forward ... */
10055 if (local_body->param.flags.forwardable) {
10056 flag |= VM_CALL_FORWARDING;
10057 int idx = local_body->local_table_size - get_local_var_idx(liseq, idDot3);
10058 ADD_GETLOCAL(args, node, idx, lvar_level);
10059 }
10060
10061 if (local_body->param.flags.has_opt) {
10062 /* optional arguments */
10063 int j;
10064 for (j = 0; j < local_body->param.opt_num; j++) {
10065 int idx = local_body->local_table_size - (i + j);
10066 ADD_GETLOCAL(args, node, idx, lvar_level);
10067 }
10068 i += j;
10069 argc = i;
10070 }
10071 if (local_body->param.flags.has_rest) {
10072 /* rest argument */
10073 int idx = local_body->local_table_size - local_body->param.rest_start;
10074 ADD_GETLOCAL(args, node, idx, lvar_level);
10075 ADD_INSN1(args, node, splatarray, RBOOL(local_body->param.flags.has_post));
10076
10077 argc = local_body->param.rest_start + 1;
10078 flag |= VM_CALL_ARGS_SPLAT;
10079 }
10080 if (local_body->param.flags.has_post) {
10081 /* post arguments */
10082 int post_len = local_body->param.post_num;
10083 int post_start = local_body->param.post_start;
10084
10085 if (local_body->param.flags.has_rest) {
10086 int j;
10087 for (j=0; j<post_len; j++) {
10088 int idx = local_body->local_table_size - (post_start + j);
10089 ADD_GETLOCAL(args, node, idx, lvar_level);
10090 }
10091 ADD_INSN1(args, node, pushtoarray, INT2FIX(j));
10092 flag |= VM_CALL_ARGS_SPLAT_MUT;
10093 /* argc is settled at above */
10094 }
10095 else {
10096 int j;
10097 for (j=0; j<post_len; j++) {
10098 int idx = local_body->local_table_size - (post_start + j);
10099 ADD_GETLOCAL(args, node, idx, lvar_level);
10100 }
10101 argc = post_len + post_start;
10102 }
10103 }
10104
10105 if (local_body->param.flags.has_kw) { /* TODO: support keywords */
10106 int local_size = local_body->local_table_size;
10107 argc++;
10108
10109 ADD_INSN1(args, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10110
10111 if (local_body->param.flags.has_kwrest) {
10112 int idx = local_body->local_table_size - local_kwd->rest_start;
10113 ADD_GETLOCAL(args, node, idx, lvar_level);
10114 RUBY_ASSERT(local_kwd->num > 0);
10115 ADD_SEND (args, node, rb_intern("dup"), INT2FIX(0));
10116 }
10117 else {
10118 ADD_INSN1(args, node, newhash, INT2FIX(0));
10119 }
10120 for (i = 0; i < local_kwd->num; ++i) {
10121 ID id = local_kwd->table[i];
10122 int idx = local_size - get_local_var_idx(liseq, id);
10123 ADD_INSN1(args, node, putobject, ID2SYM(id));
10124 ADD_GETLOCAL(args, node, idx, lvar_level);
10125 }
10126 ADD_SEND(args, node, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
10127 flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT;
10128 }
10129 else if (local_body->param.flags.has_kwrest) {
10130 int idx = local_body->local_table_size - local_kwd->rest_start;
10131 ADD_GETLOCAL(args, node, idx, lvar_level);
10132 argc++;
10133 flag |= VM_CALL_KW_SPLAT;
10134 }
10135 }
10136
10137 if (use_block && parent_block == NULL) {
10138 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
10139 }
10140
10141 flag |= VM_CALL_SUPER | VM_CALL_FCALL;
10142 if (type == NODE_ZSUPER) flag |= VM_CALL_ZSUPER;
10143 ADD_INSN(ret, node, putself);
10144 ADD_SEQ(ret, args);
10145
10146 const struct rb_callinfo * ci = new_callinfo(iseq, 0, argc, flag, keywords, parent_block != NULL);
10147
10148 if (vm_ci_flag(ci) & VM_CALL_FORWARDING) {
10149 ADD_INSN2(ret, node, invokesuperforward, ci, parent_block);
10150 }
10151 else {
10152 ADD_INSN2(ret, node, invokesuper, ci, parent_block);
10153 }
10154
10155 if (popped) {
10156 ADD_INSN(ret, node, pop);
10157 }
10158 return COMPILE_OK;
10159}
10160
10161static int
10162compile_yield(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10163{
10164 DECL_ANCHOR(args);
10165 VALUE argc;
10166 unsigned int flag = 0;
10167 struct rb_callinfo_kwarg *keywords = NULL;
10168
10169 INIT_ANCHOR(args);
10170
10171 switch (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->type) {
10172 case ISEQ_TYPE_TOP:
10173 case ISEQ_TYPE_MAIN:
10174 case ISEQ_TYPE_CLASS:
10175 COMPILE_ERROR(ERROR_ARGS "Invalid yield");
10176 return COMPILE_NG;
10177 default: /* valid */;
10178 }
10179
10180 if (RNODE_YIELD(node)->nd_head) {
10181 argc = setup_args(iseq, args, RNODE_YIELD(node)->nd_head, &flag, &keywords);
10182 CHECK(!NIL_P(argc));
10183 }
10184 else {
10185 argc = INT2FIX(0);
10186 }
10187
10188 ADD_SEQ(ret, args);
10189 ADD_INSN1(ret, node, invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), flag, keywords, FALSE));
10190 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
10191
10192 if (popped) {
10193 ADD_INSN(ret, node, pop);
10194 }
10195
10196 int level = 0;
10197 const rb_iseq_t *tmp_iseq = iseq;
10198 for (; tmp_iseq != ISEQ_BODY(iseq)->local_iseq; level++ ) {
10199 tmp_iseq = ISEQ_BODY(tmp_iseq)->parent_iseq;
10200 }
10201 if (level > 0) access_outer_variables(iseq, level, rb_intern("yield"), true);
10202
10203 return COMPILE_OK;
10204}
10205
10206static int
10207compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
10208{
10209 DECL_ANCHOR(recv);
10210 DECL_ANCHOR(val);
10211
10212 INIT_ANCHOR(recv);
10213 INIT_ANCHOR(val);
10214 switch ((int)type) {
10215 case NODE_MATCH:
10216 {
10217 VALUE re = rb_node_regx_string_val(node);
10218 RB_OBJ_SET_FROZEN_SHAREABLE(re);
10219 ADD_INSN1(recv, node, putobject, re);
10220 ADD_INSN2(val, node, getspecial, INT2FIX(0),
10221 INT2FIX(0));
10222 }
10223 break;
10224 case NODE_MATCH2:
10225 CHECK(COMPILE(recv, "receiver", RNODE_MATCH2(node)->nd_recv));
10226 CHECK(COMPILE(val, "value", RNODE_MATCH2(node)->nd_value));
10227 break;
10228 case NODE_MATCH3:
10229 CHECK(COMPILE(recv, "receiver", RNODE_MATCH3(node)->nd_value));
10230 CHECK(COMPILE(val, "value", RNODE_MATCH3(node)->nd_recv));
10231 break;
10232 }
10233
10234 ADD_SEQ(ret, recv);
10235 ADD_SEQ(ret, val);
10236 ADD_SEND(ret, node, idEqTilde, INT2FIX(1));
10237
10238 if (nd_type_p(node, NODE_MATCH2) && RNODE_MATCH2(node)->nd_args) {
10239 compile_named_capture_assign(iseq, ret, RNODE_MATCH2(node)->nd_args);
10240 }
10241
10242 if (popped) {
10243 ADD_INSN(ret, node, pop);
10244 }
10245 return COMPILE_OK;
10246}
10247
10248static int
10249compile_colon2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10250{
10251 if (rb_is_const_id(RNODE_COLON2(node)->nd_mid)) {
10252 /* constant */
10253 VALUE segments;
10254 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache &&
10255 (segments = collect_const_segments(iseq, node))) {
10256 ISEQ_BODY(iseq)->ic_size++;
10257 ADD_INSN1(ret, node, opt_getconstant_path, segments);
10258 RB_OBJ_WRITTEN(iseq, Qundef, segments);
10259 }
10260 else {
10261 /* constant */
10262 DECL_ANCHOR(pref);
10263 DECL_ANCHOR(body);
10264
10265 INIT_ANCHOR(pref);
10266 INIT_ANCHOR(body);
10267 CHECK(compile_const_prefix(iseq, node, pref, body));
10268 if (LIST_INSN_SIZE_ZERO(pref)) {
10269 ADD_INSN(ret, node, putnil);
10270 ADD_SEQ(ret, body);
10271 }
10272 else {
10273 ADD_SEQ(ret, pref);
10274 ADD_SEQ(ret, body);
10275 }
10276 }
10277 }
10278 else {
10279 /* function call */
10280 ADD_CALL_RECEIVER(ret, node);
10281 CHECK(COMPILE(ret, "colon2#nd_head", RNODE_COLON2(node)->nd_head));
10282 ADD_CALL(ret, node, RNODE_COLON2(node)->nd_mid, INT2FIX(1));
10283 }
10284 if (popped) {
10285 ADD_INSN(ret, node, pop);
10286 }
10287 return COMPILE_OK;
10288}
10289
10290static int
10291compile_colon3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10292{
10293 debugi("colon3#nd_mid", RNODE_COLON3(node)->nd_mid);
10294
10295 /* add cache insn */
10296 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
10297 ISEQ_BODY(iseq)->ic_size++;
10298 VALUE segments = rb_ary_new_from_args(2, ID2SYM(idNULL), ID2SYM(RNODE_COLON3(node)->nd_mid));
10299 RB_OBJ_SET_FROZEN_SHAREABLE(segments);
10300 ADD_INSN1(ret, node, opt_getconstant_path, segments);
10301 RB_OBJ_WRITTEN(iseq, Qundef, segments);
10302 }
10303 else {
10304 ADD_INSN1(ret, node, putobject, rb_cObject);
10305 ADD_INSN1(ret, node, putobject, Qtrue);
10306 ADD_INSN1(ret, node, getconstant, ID2SYM(RNODE_COLON3(node)->nd_mid));
10307 }
10308
10309 if (popped) {
10310 ADD_INSN(ret, node, pop);
10311 }
10312 return COMPILE_OK;
10313}
10314
10315static int
10316compile_dots(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const int excl)
10317{
10318 VALUE flag = INT2FIX(excl);
10319 const NODE *b = RNODE_DOT2(node)->nd_beg;
10320 const NODE *e = RNODE_DOT2(node)->nd_end;
10321
10322 if (optimizable_range_item_p(b) && optimizable_range_item_p(e)) {
10323 if (!popped) {
10324 VALUE bv = optimized_range_item(b);
10325 VALUE ev = optimized_range_item(e);
10326 VALUE val = rb_range_new(bv, ev, excl);
10328 ADD_INSN1(ret, node, putobject, val);
10329 RB_OBJ_WRITTEN(iseq, Qundef, val);
10330 }
10331 }
10332 else {
10333 CHECK(COMPILE_(ret, "min", b, popped));
10334 CHECK(COMPILE_(ret, "max", e, popped));
10335 if (!popped) {
10336 ADD_INSN1(ret, node, newrange, flag);
10337 }
10338 }
10339 return COMPILE_OK;
10340}
10341
10342static int
10343compile_errinfo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10344{
10345 if (!popped) {
10346 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
10347 ADD_GETLOCAL(ret, node, LVAR_ERRINFO, 0);
10348 }
10349 else {
10350 const rb_iseq_t *ip = iseq;
10351 int level = 0;
10352 while (ip) {
10353 if (ISEQ_BODY(ip)->type == ISEQ_TYPE_RESCUE) {
10354 break;
10355 }
10356 ip = ISEQ_BODY(ip)->parent_iseq;
10357 level++;
10358 }
10359 if (ip) {
10360 ADD_GETLOCAL(ret, node, LVAR_ERRINFO, level);
10361 }
10362 else {
10363 ADD_INSN(ret, node, putnil);
10364 }
10365 }
10366 }
10367 return COMPILE_OK;
10368}
10369
10370static int
10371compile_kw_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10372{
10373 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
10374 LABEL *end_label = NEW_LABEL(nd_line(node));
10375 const NODE *default_value = get_nd_value(RNODE_KW_ARG(node)->nd_body);
10376
10377 if (default_value == NODE_SPECIAL_REQUIRED_KEYWORD) {
10378 /* required argument. do nothing */
10379 COMPILE_ERROR(ERROR_ARGS "unreachable");
10380 return COMPILE_NG;
10381 }
10382 else if (nd_type_p(default_value, NODE_SYM) ||
10383 nd_type_p(default_value, NODE_REGX) ||
10384 nd_type_p(default_value, NODE_LINE) ||
10385 nd_type_p(default_value, NODE_INTEGER) ||
10386 nd_type_p(default_value, NODE_FLOAT) ||
10387 nd_type_p(default_value, NODE_RATIONAL) ||
10388 nd_type_p(default_value, NODE_IMAGINARY) ||
10389 nd_type_p(default_value, NODE_NIL) ||
10390 nd_type_p(default_value, NODE_TRUE) ||
10391 nd_type_p(default_value, NODE_FALSE)) {
10392 COMPILE_ERROR(ERROR_ARGS "unreachable");
10393 return COMPILE_NG;
10394 }
10395 else {
10396 /* if keywordcheck(_kw_bits, nth_keyword)
10397 * kw = default_value
10398 * end
10399 */
10400 int kw_bits_idx = body->local_table_size - body->param.keyword->bits_start;
10401 int keyword_idx = body->param.keyword->num;
10402
10403 ADD_INSN2(ret, node, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(keyword_idx));
10404 ADD_INSNL(ret, node, branchif, end_label);
10405 CHECK(COMPILE_POPPED(ret, "keyword default argument", RNODE_KW_ARG(node)->nd_body));
10406 ADD_LABEL(ret, end_label);
10407 }
10408 return COMPILE_OK;
10409}
10410
10411static int
10412compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10413{
10414 DECL_ANCHOR(recv);
10415 DECL_ANCHOR(args);
10416 unsigned int flag = 0;
10417 ID mid = RNODE_ATTRASGN(node)->nd_mid;
10418 VALUE argc;
10419 LABEL *else_label = NULL;
10420 VALUE branches = Qfalse;
10421
10422 INIT_ANCHOR(recv);
10423 INIT_ANCHOR(args);
10424 argc = setup_args(iseq, args, RNODE_ATTRASGN(node)->nd_args, &flag, NULL);
10425 CHECK(!NIL_P(argc));
10426
10427 int asgnflag = COMPILE_RECV(recv, "recv", node, RNODE_ATTRASGN(node)->nd_recv);
10428 CHECK(asgnflag != -1);
10429 flag |= (unsigned int)asgnflag;
10430
10431 debugp_param("argc", argc);
10432 debugp_param("nd_mid", ID2SYM(mid));
10433
10434 if (!rb_is_attrset_id(mid)) {
10435 /* safe nav attr */
10436 mid = rb_id_attrset(mid);
10437 else_label = qcall_branch_start(iseq, recv, &branches, node, node);
10438 }
10439 if (!popped) {
10440 ADD_INSN(ret, node, putnil);
10441 ADD_SEQ(ret, recv);
10442 ADD_SEQ(ret, args);
10443
10444 if (flag & VM_CALL_ARGS_SPLAT) {
10445 ADD_INSN(ret, node, dup);
10446 ADD_INSN1(ret, node, putobject, INT2FIX(-1));
10447 ADD_SEND_WITH_FLAG(ret, node, idAREF, INT2FIX(1), INT2FIX(asgnflag));
10448 ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2));
10449 ADD_INSN (ret, node, pop);
10450 }
10451 else {
10452 ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 1));
10453 }
10454 }
10455 else {
10456 ADD_SEQ(ret, recv);
10457 ADD_SEQ(ret, args);
10458 }
10459 ADD_SEND_WITH_FLAG(ret, node, mid, argc, INT2FIX(flag));
10460 qcall_branch_end(iseq, ret, else_label, branches, node, node);
10461 ADD_INSN(ret, node, pop);
10462 return COMPILE_OK;
10463}
10464
10465static int
10466compile_make_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, LINK_ANCHOR *sub, const NODE *value, bool copy)
10467{
10468 ADD_INSN1(ret, value, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10469 ADD_SEQ(ret, sub);
10470
10471 if (copy) {
10472 /*
10473 * NEW_CALL(fcore, rb_intern("make_shareable_copy"),
10474 * NEW_LIST(value, loc), loc);
10475 */
10476 ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
10477 }
10478 else {
10479 /*
10480 * NEW_CALL(fcore, rb_intern("make_shareable"),
10481 * NEW_LIST(value, loc), loc);
10482 */
10483 ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
10484 }
10485
10486 return COMPILE_OK;
10487}
10488
10489static VALUE
10490node_const_decl_val(const NODE *node)
10491{
10492 VALUE path;
10493 switch (nd_type(node)) {
10494 case NODE_CDECL:
10495 if (RNODE_CDECL(node)->nd_vid) {
10496 path = rb_id2str(RNODE_CDECL(node)->nd_vid);
10497 goto end;
10498 }
10499 else {
10500 node = RNODE_CDECL(node)->nd_else;
10501 }
10502 break;
10503 case NODE_COLON2:
10504 break;
10505 case NODE_COLON3:
10506 // ::Const
10507 path = rb_str_new_cstr("::");
10508 rb_str_append(path, rb_id2str(RNODE_COLON3(node)->nd_mid));
10509 goto end;
10510 default:
10511 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
10513 }
10514
10515 path = rb_ary_new();
10516 if (node) {
10517 for (; node && nd_type_p(node, NODE_COLON2); node = RNODE_COLON2(node)->nd_head) {
10518 rb_ary_push(path, rb_id2str(RNODE_COLON2(node)->nd_mid));
10519 }
10520 if (node && nd_type_p(node, NODE_CONST)) {
10521 // Const::Name
10522 rb_ary_push(path, rb_id2str(RNODE_CONST(node)->nd_vid));
10523 }
10524 else if (node && nd_type_p(node, NODE_COLON3)) {
10525 // ::Const::Name
10526 rb_ary_push(path, rb_id2str(RNODE_COLON3(node)->nd_mid));
10527 rb_ary_push(path, rb_str_new(0, 0));
10528 }
10529 else {
10530 // expression::Name
10531 rb_ary_push(path, rb_str_new_cstr("..."));
10532 }
10533 path = rb_ary_join(rb_ary_reverse(path), rb_str_new_cstr("::"));
10534 }
10535 end:
10536 path = rb_fstring(path);
10537 return path;
10538}
10539
10540static VALUE
10541const_decl_path(NODE *dest)
10542{
10543 VALUE path = Qnil;
10544 if (!nd_type_p(dest, NODE_CALL)) {
10545 path = node_const_decl_val(dest);
10546 }
10547 return path;
10548}
10549
10550static int
10551compile_ensure_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *dest, const NODE *value)
10552{
10553 /*
10554 *. RubyVM::FrozenCore.ensure_shareable(value, const_decl_path(dest))
10555 */
10556 VALUE path = const_decl_path(dest);
10557 ADD_INSN1(ret, value, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10558 CHECK(COMPILE(ret, "compile_ensure_shareable_node", value));
10559 ADD_INSN1(ret, value, putobject, path);
10560 RB_OBJ_WRITTEN(iseq, Qundef, path);
10561 ADD_SEND_WITH_FLAG(ret, value, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE));
10562
10563 return COMPILE_OK;
10564}
10565
10566#ifndef SHAREABLE_BARE_EXPRESSION
10567#define SHAREABLE_BARE_EXPRESSION 1
10568#endif
10569
10570static int
10571compile_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)
10572{
10573# define compile_shareable_literal_constant_next(node, anchor, value_p, shareable_literal_p) \
10574 compile_shareable_literal_constant(iseq, anchor, shareable, dest, node, level+1, value_p, shareable_literal_p)
10575 VALUE lit = Qnil;
10576 DECL_ANCHOR(anchor);
10577
10578 enum node_type type = node ? nd_type(node) : NODE_NIL;
10579 switch (type) {
10580 case NODE_TRUE:
10581 *value_p = Qtrue;
10582 goto compile;
10583 case NODE_FALSE:
10584 *value_p = Qfalse;
10585 goto compile;
10586 case NODE_NIL:
10587 *value_p = Qnil;
10588 goto compile;
10589 case NODE_SYM:
10590 *value_p = rb_node_sym_string_val(node);
10591 goto compile;
10592 case NODE_REGX:
10593 *value_p = rb_node_regx_string_val(node);
10594 goto compile;
10595 case NODE_LINE:
10596 *value_p = rb_node_line_lineno_val(node);
10597 goto compile;
10598 case NODE_INTEGER:
10599 *value_p = rb_node_integer_literal_val(node);
10600 goto compile;
10601 case NODE_FLOAT:
10602 *value_p = rb_node_float_literal_val(node);
10603 goto compile;
10604 case NODE_RATIONAL:
10605 *value_p = rb_node_rational_literal_val(node);
10606 goto compile;
10607 case NODE_IMAGINARY:
10608 *value_p = rb_node_imaginary_literal_val(node);
10609 goto compile;
10610 case NODE_ENCODING:
10611 *value_p = rb_node_encoding_val(node);
10612
10613 compile:
10614 CHECK(COMPILE(ret, "shareable_literal_constant", node));
10615 *shareable_literal_p = 1;
10616 return COMPILE_OK;
10617
10618 case NODE_DSTR:
10619 CHECK(COMPILE(ret, "shareable_literal_constant", node));
10620 if (shareable == rb_parser_shareable_literal) {
10621 /*
10622 * NEW_CALL(node, idUMinus, 0, loc);
10623 *
10624 * -"#{var}"
10625 */
10626 ADD_SEND_WITH_FLAG(ret, node, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE));
10627 }
10628 *value_p = Qundef;
10629 *shareable_literal_p = 1;
10630 return COMPILE_OK;
10631
10632 case NODE_STR:{
10633 VALUE lit = rb_node_str_string_val(node);
10634 ADD_INSN1(ret, node, putobject, lit);
10635 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10636 *value_p = lit;
10637 *shareable_literal_p = 1;
10638
10639 return COMPILE_OK;
10640 }
10641
10642 case NODE_FILE:{
10643 VALUE lit = rb_node_file_path_val(node);
10644 ADD_INSN1(ret, node, putobject, lit);
10645 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10646 *value_p = lit;
10647 *shareable_literal_p = 1;
10648
10649 return COMPILE_OK;
10650 }
10651
10652 case NODE_ZLIST:{
10653 VALUE lit = rb_ary_new();
10654 OBJ_FREEZE(lit);
10655 ADD_INSN1(ret, node, putobject, lit);
10656 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10657 *value_p = lit;
10658 *shareable_literal_p = 1;
10659
10660 return COMPILE_OK;
10661 }
10662
10663 case NODE_LIST:{
10664 INIT_ANCHOR(anchor);
10665 lit = rb_ary_new();
10666 for (NODE *n = (NODE *)node; n; n = RNODE_LIST(n)->nd_next) {
10667 VALUE val;
10668 int shareable_literal_p2;
10669 NODE *elt = RNODE_LIST(n)->nd_head;
10670 if (elt) {
10671 CHECK(compile_shareable_literal_constant_next(elt, anchor, &val, &shareable_literal_p2));
10672 if (shareable_literal_p2) {
10673 /* noop */
10674 }
10675 else if (RTEST(lit)) {
10676 rb_ary_clear(lit);
10677 lit = Qfalse;
10678 }
10679 }
10680 if (RTEST(lit)) {
10681 if (!UNDEF_P(val)) {
10682 rb_ary_push(lit, val);
10683 }
10684 else {
10685 rb_ary_clear(lit);
10686 lit = Qnil; /* make shareable at runtime */
10687 }
10688 }
10689 }
10690 break;
10691 }
10692 case NODE_HASH:{
10693 if (!RNODE_HASH(node)->nd_brace) {
10694 *value_p = Qundef;
10695 *shareable_literal_p = 0;
10696 return COMPILE_OK;
10697 }
10698 for (NODE *n = RNODE_HASH(node)->nd_head; n; n = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_next) {
10699 if (!RNODE_LIST(n)->nd_head) {
10700 // If the hash node have a keyword splat, fall back to the default case.
10701 goto compile_shareable;
10702 }
10703 }
10704
10705 INIT_ANCHOR(anchor);
10706 lit = rb_hash_new();
10707 for (NODE *n = RNODE_HASH(node)->nd_head; n; n = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_next) {
10708 VALUE key_val = 0;
10709 VALUE value_val = 0;
10710 int shareable_literal_p2;
10711 NODE *key = RNODE_LIST(n)->nd_head;
10712 NODE *val = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head;
10713 CHECK(compile_shareable_literal_constant_next(key, anchor, &key_val, &shareable_literal_p2));
10714 if (shareable_literal_p2) {
10715 /* noop */
10716 }
10717 else if (RTEST(lit)) {
10718 rb_hash_clear(lit);
10719 lit = Qfalse;
10720 }
10721 CHECK(compile_shareable_literal_constant_next(val, anchor, &value_val, &shareable_literal_p2));
10722 if (shareable_literal_p2) {
10723 /* noop */
10724 }
10725 else if (RTEST(lit)) {
10726 rb_hash_clear(lit);
10727 lit = Qfalse;
10728 }
10729 if (RTEST(lit)) {
10730 if (!UNDEF_P(key_val) && !UNDEF_P(value_val)) {
10731 rb_hash_aset(lit, key_val, value_val);
10732 }
10733 else {
10734 rb_hash_clear(lit);
10735 lit = Qnil; /* make shareable at runtime */
10736 }
10737 }
10738 }
10739 break;
10740 }
10741
10742 default:
10743
10744 compile_shareable:
10745 if (shareable == rb_parser_shareable_literal &&
10746 (SHAREABLE_BARE_EXPRESSION || level > 0)) {
10747 CHECK(compile_ensure_shareable_node(iseq, ret, dest, node));
10748 *value_p = Qundef;
10749 *shareable_literal_p = 1;
10750 return COMPILE_OK;
10751 }
10752 CHECK(COMPILE(ret, "shareable_literal_constant", node));
10753 *value_p = Qundef;
10754 *shareable_literal_p = 0;
10755 return COMPILE_OK;
10756 }
10757
10758 /* Array or Hash that does not have keyword splat */
10759 if (!lit) {
10760 if (nd_type(node) == NODE_LIST) {
10761 ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen));
10762 }
10763 else if (nd_type(node) == NODE_HASH) {
10764 long len = RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen;
10767 ADD_INSN1(anchor, node, newhash, LONG2FIX(len));
10768 }
10769 *value_p = Qundef;
10770 *shareable_literal_p = 0;
10771 ADD_SEQ(ret, anchor);
10772 return COMPILE_OK;
10773 }
10774 if (NIL_P(lit)) {
10775 // if shareable_literal, all elements should have been ensured
10776 // as shareable
10777 if (nd_type(node) == NODE_LIST) {
10778 ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen));
10779 }
10780 else if (nd_type(node) == NODE_HASH) {
10781 long len = RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen;
10784 ADD_INSN1(anchor, node, newhash, LONG2FIX(len));
10785 }
10786 CHECK(compile_make_shareable_node(iseq, ret, anchor, node, false));
10787 *value_p = Qundef;
10788 *shareable_literal_p = 1;
10789 }
10790 else {
10792 ADD_INSN1(ret, node, putobject, val);
10793 RB_OBJ_WRITTEN(iseq, Qundef, val);
10794 *value_p = val;
10795 *shareable_literal_p = 1;
10796 }
10797
10798 return COMPILE_OK;
10799}
10800
10801static int
10802compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value)
10803{
10804 int literal_p = 0;
10805 VALUE val;
10806 DECL_ANCHOR(anchor);
10807 INIT_ANCHOR(anchor);
10808
10809 switch (shareable) {
10810 case rb_parser_shareable_none:
10811 CHECK(COMPILE(ret, "compile_shareable_constant_value", value));
10812 return COMPILE_OK;
10813
10814 case rb_parser_shareable_literal:
10815 CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p));
10816 ADD_SEQ(ret, anchor);
10817 return COMPILE_OK;
10818
10819 case rb_parser_shareable_copy:
10820 case rb_parser_shareable_everything:
10821 CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p));
10822 if (!literal_p) {
10823 CHECK(compile_make_shareable_node(iseq, ret, anchor, value, shareable == rb_parser_shareable_copy));
10824 }
10825 else {
10826 ADD_SEQ(ret, anchor);
10827 }
10828 return COMPILE_OK;
10829 default:
10830 rb_bug("unexpected rb_parser_shareability: %d", shareable);
10831 }
10832}
10833
10834static int iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped);
10842static int
10843iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *node, int popped)
10844{
10845 if (node == 0) {
10846 if (!popped) {
10847 int lineno = ISEQ_COMPILE_DATA(iseq)->last_line;
10848 if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq));
10849 debugs("node: NODE_NIL(implicit)\n");
10850 ADD_SYNTHETIC_INSN(ret, lineno, -1, putnil);
10851 }
10852 return COMPILE_OK;
10853 }
10854 return iseq_compile_each0(iseq, ret, node, popped);
10855}
10856
10857static int
10858iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10859{
10860 const int line = (int)nd_line(node);
10861 const enum node_type type = nd_type(node);
10862 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
10863
10864 if (ISEQ_COMPILE_DATA(iseq)->last_line == line) {
10865 /* ignore */
10866 }
10867 else {
10868 if (nd_fl_newline(node)) {
10869 int event = RUBY_EVENT_LINE;
10870 ISEQ_COMPILE_DATA(iseq)->last_line = line;
10871 if (line > 0 && ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
10872 event |= RUBY_EVENT_COVERAGE_LINE;
10873 }
10874 ADD_TRACE(ret, event);
10875 }
10876 }
10877
10878 debug_node_start(node);
10879#undef BEFORE_RETURN
10880#define BEFORE_RETURN debug_node_end()
10881
10882 switch (type) {
10883 case NODE_BLOCK:
10884 CHECK(compile_block(iseq, ret, node, popped));
10885 break;
10886 case NODE_IF:
10887 case NODE_UNLESS:
10888 CHECK(compile_if(iseq, ret, node, popped, type));
10889 break;
10890 case NODE_CASE:
10891 CHECK(compile_case(iseq, ret, node, popped));
10892 break;
10893 case NODE_CASE2:
10894 CHECK(compile_case2(iseq, ret, node, popped));
10895 break;
10896 case NODE_CASE3:
10897 CHECK(compile_case3(iseq, ret, node, popped));
10898 break;
10899 case NODE_WHILE:
10900 case NODE_UNTIL:
10901 CHECK(compile_loop(iseq, ret, node, popped, type));
10902 break;
10903 case NODE_FOR:
10904 case NODE_ITER:
10905 CHECK(compile_iter(iseq, ret, node, popped));
10906 break;
10907 case NODE_FOR_MASGN:
10908 CHECK(compile_for_masgn(iseq, ret, node, popped));
10909 break;
10910 case NODE_BREAK:
10911 CHECK(compile_break(iseq, ret, node, popped));
10912 break;
10913 case NODE_NEXT:
10914 CHECK(compile_next(iseq, ret, node, popped));
10915 break;
10916 case NODE_REDO:
10917 CHECK(compile_redo(iseq, ret, node, popped));
10918 break;
10919 case NODE_RETRY:
10920 CHECK(compile_retry(iseq, ret, node, popped));
10921 break;
10922 case NODE_BEGIN:{
10923 CHECK(COMPILE_(ret, "NODE_BEGIN", RNODE_BEGIN(node)->nd_body, popped));
10924 break;
10925 }
10926 case NODE_RESCUE:
10927 CHECK(compile_rescue(iseq, ret, node, popped));
10928 break;
10929 case NODE_RESBODY:
10930 CHECK(compile_resbody(iseq, ret, node, popped));
10931 break;
10932 case NODE_ENSURE:
10933 CHECK(compile_ensure(iseq, ret, node, popped));
10934 break;
10935
10936 case NODE_AND:
10937 case NODE_OR:{
10938 LABEL *end_label = NEW_LABEL(line);
10939 CHECK(COMPILE(ret, "nd_1st", RNODE_OR(node)->nd_1st));
10940 if (!popped) {
10941 ADD_INSN(ret, node, dup);
10942 }
10943 if (type == NODE_AND) {
10944 ADD_INSNL(ret, node, branchunless, end_label);
10945 }
10946 else {
10947 ADD_INSNL(ret, node, branchif, end_label);
10948 }
10949 if (!popped) {
10950 ADD_INSN(ret, node, pop);
10951 }
10952 CHECK(COMPILE_(ret, "nd_2nd", RNODE_OR(node)->nd_2nd, popped));
10953 ADD_LABEL(ret, end_label);
10954 break;
10955 }
10956
10957 case NODE_MASGN:{
10958 compile_massign(iseq, ret, node, popped);
10959 break;
10960 }
10961
10962 case NODE_LASGN:{
10963 ID id = RNODE_LASGN(node)->nd_vid;
10964 int idx = ISEQ_BODY(body->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
10965
10966 debugs("lvar: %s idx: %d\n", rb_id2name(id), idx);
10967 CHECK(COMPILE(ret, "rvalue", RNODE_LASGN(node)->nd_value));
10968
10969 if (!popped) {
10970 ADD_INSN(ret, node, dup);
10971 }
10972 ADD_SETLOCAL(ret, node, idx, get_lvar_level(iseq));
10973 break;
10974 }
10975 case NODE_DASGN: {
10976 int idx, lv, ls;
10977 ID id = RNODE_DASGN(node)->nd_vid;
10978 CHECK(COMPILE(ret, "dvalue", RNODE_DASGN(node)->nd_value));
10979 debugi("dassn id", rb_id2str(id) ? id : '*');
10980
10981 if (!popped) {
10982 ADD_INSN(ret, node, dup);
10983 }
10984
10985 idx = get_dyna_var_idx(iseq, id, &lv, &ls);
10986
10987 if (idx < 0) {
10988 COMPILE_ERROR(ERROR_ARGS "NODE_DASGN: unknown id (%"PRIsVALUE")",
10989 rb_id2str(id));
10990 goto ng;
10991 }
10992 ADD_SETLOCAL(ret, node, ls - idx, lv);
10993 break;
10994 }
10995 case NODE_GASGN:{
10996 CHECK(COMPILE(ret, "lvalue", RNODE_GASGN(node)->nd_value));
10997
10998 if (!popped) {
10999 ADD_INSN(ret, node, dup);
11000 }
11001 ADD_INSN1(ret, node, setglobal, ID2SYM(RNODE_GASGN(node)->nd_vid));
11002 break;
11003 }
11004 case NODE_IASGN:{
11005 CHECK(COMPILE(ret, "lvalue", RNODE_IASGN(node)->nd_value));
11006 if (!popped) {
11007 ADD_INSN(ret, node, dup);
11008 }
11009 ADD_INSN2(ret, node, setinstancevariable,
11010 ID2SYM(RNODE_IASGN(node)->nd_vid),
11011 get_ivar_ic_value(iseq,RNODE_IASGN(node)->nd_vid));
11012 break;
11013 }
11014 case NODE_CDECL:{
11015 if (RNODE_CDECL(node)->nd_vid) {
11016 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value));
11017
11018 if (!popped) {
11019 ADD_INSN(ret, node, dup);
11020 }
11021
11022 ADD_INSN1(ret, node, putspecialobject,
11023 INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
11024 ADD_INSN1(ret, node, setconstant, ID2SYM(RNODE_CDECL(node)->nd_vid));
11025 }
11026 else {
11027 compile_cpath(ret, iseq, RNODE_CDECL(node)->nd_else);
11028 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value));
11029 ADD_INSN(ret, node, swap);
11030
11031 if (!popped) {
11032 ADD_INSN1(ret, node, topn, INT2FIX(1));
11033 ADD_INSN(ret, node, swap);
11034 }
11035
11036 ADD_INSN1(ret, node, setconstant, ID2SYM(get_node_colon_nd_mid(RNODE_CDECL(node)->nd_else)));
11037 }
11038 break;
11039 }
11040 case NODE_CVASGN:{
11041 CHECK(COMPILE(ret, "cvasgn val", RNODE_CVASGN(node)->nd_value));
11042 if (!popped) {
11043 ADD_INSN(ret, node, dup);
11044 }
11045 ADD_INSN2(ret, node, setclassvariable,
11046 ID2SYM(RNODE_CVASGN(node)->nd_vid),
11047 get_cvar_ic_value(iseq, RNODE_CVASGN(node)->nd_vid));
11048 break;
11049 }
11050 case NODE_OP_ASGN1:
11051 CHECK(compile_op_asgn1(iseq, ret, node, popped));
11052 break;
11053 case NODE_OP_ASGN2:
11054 CHECK(compile_op_asgn2(iseq, ret, node, popped));
11055 break;
11056 case NODE_OP_CDECL:
11057 CHECK(compile_op_cdecl(iseq, ret, node, popped));
11058 break;
11059 case NODE_OP_ASGN_AND:
11060 case NODE_OP_ASGN_OR:
11061 CHECK(compile_op_log(iseq, ret, node, popped, type));
11062 break;
11063 case NODE_CALL: /* obj.foo */
11064 case NODE_OPCALL: /* foo[] */
11065 if (compile_call_precheck_freeze(iseq, ret, node, node, popped) == TRUE) {
11066 break;
11067 }
11068 case NODE_QCALL: /* obj&.foo */
11069 case NODE_FCALL: /* foo() */
11070 case NODE_VCALL: /* foo (variable or call) */
11071 if (compile_call(iseq, ret, node, type, node, popped, false) == COMPILE_NG) {
11072 goto ng;
11073 }
11074 break;
11075 case NODE_SUPER:
11076 case NODE_ZSUPER:
11077 CHECK(compile_super(iseq, ret, node, popped, type));
11078 break;
11079 case NODE_LIST:{
11080 CHECK(compile_array(iseq, ret, node, popped, TRUE) >= 0);
11081 break;
11082 }
11083 case NODE_ZLIST:{
11084 if (!popped) {
11085 ADD_INSN1(ret, node, newarray, INT2FIX(0));
11086 }
11087 break;
11088 }
11089 case NODE_HASH:
11090 CHECK(compile_hash(iseq, ret, node, FALSE, popped) >= 0);
11091 break;
11092 case NODE_RETURN:
11093 CHECK(compile_return(iseq, ret, node, popped));
11094 break;
11095 case NODE_YIELD:
11096 CHECK(compile_yield(iseq, ret, node, popped));
11097 break;
11098 case NODE_LVAR:{
11099 if (!popped) {
11100 compile_lvar(iseq, ret, node, RNODE_LVAR(node)->nd_vid);
11101 }
11102 break;
11103 }
11104 case NODE_DVAR:{
11105 int lv, idx, ls;
11106 debugi("nd_vid", RNODE_DVAR(node)->nd_vid);
11107 if (!popped) {
11108 idx = get_dyna_var_idx(iseq, RNODE_DVAR(node)->nd_vid, &lv, &ls);
11109 if (idx < 0) {
11110 COMPILE_ERROR(ERROR_ARGS "unknown dvar (%"PRIsVALUE")",
11111 rb_id2str(RNODE_DVAR(node)->nd_vid));
11112 goto ng;
11113 }
11114 ADD_GETLOCAL(ret, node, ls - idx, lv);
11115 }
11116 break;
11117 }
11118 case NODE_GVAR:{
11119 ADD_INSN1(ret, node, getglobal, ID2SYM(RNODE_GVAR(node)->nd_vid));
11120 if (popped) {
11121 ADD_INSN(ret, node, pop);
11122 }
11123 break;
11124 }
11125 case NODE_IVAR:{
11126 debugi("nd_vid", RNODE_IVAR(node)->nd_vid);
11127 if (!popped) {
11128 ADD_INSN2(ret, node, getinstancevariable,
11129 ID2SYM(RNODE_IVAR(node)->nd_vid),
11130 get_ivar_ic_value(iseq, RNODE_IVAR(node)->nd_vid));
11131 }
11132 break;
11133 }
11134 case NODE_CONST:{
11135 debugi("nd_vid", RNODE_CONST(node)->nd_vid);
11136
11137 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
11138 body->ic_size++;
11139 VALUE segments = rb_ary_new_from_args(1, ID2SYM(RNODE_CONST(node)->nd_vid));
11140 RB_OBJ_SET_FROZEN_SHAREABLE(segments);
11141 ADD_INSN1(ret, node, opt_getconstant_path, segments);
11142 RB_OBJ_WRITTEN(iseq, Qundef, segments);
11143 }
11144 else {
11145 ADD_INSN(ret, node, putnil);
11146 ADD_INSN1(ret, node, putobject, Qtrue);
11147 ADD_INSN1(ret, node, getconstant, ID2SYM(RNODE_CONST(node)->nd_vid));
11148 }
11149
11150 if (popped) {
11151 ADD_INSN(ret, node, pop);
11152 }
11153 break;
11154 }
11155 case NODE_CVAR:{
11156 if (!popped) {
11157 ADD_INSN2(ret, node, getclassvariable,
11158 ID2SYM(RNODE_CVAR(node)->nd_vid),
11159 get_cvar_ic_value(iseq, RNODE_CVAR(node)->nd_vid));
11160 }
11161 break;
11162 }
11163 case NODE_NTH_REF:{
11164 if (!popped) {
11165 if (!RNODE_NTH_REF(node)->nd_nth) {
11166 ADD_INSN(ret, node, putnil);
11167 break;
11168 }
11169 ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
11170 INT2FIX(RNODE_NTH_REF(node)->nd_nth << 1));
11171 }
11172 break;
11173 }
11174 case NODE_BACK_REF:{
11175 if (!popped) {
11176 ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
11177 INT2FIX(0x01 | (RNODE_BACK_REF(node)->nd_nth << 1)));
11178 }
11179 break;
11180 }
11181 case NODE_MATCH:
11182 case NODE_MATCH2:
11183 case NODE_MATCH3:
11184 CHECK(compile_match(iseq, ret, node, popped, type));
11185 break;
11186 case NODE_SYM:{
11187 if (!popped) {
11188 ADD_INSN1(ret, node, putobject, rb_node_sym_string_val(node));
11189 }
11190 break;
11191 }
11192 case NODE_LINE:{
11193 if (!popped) {
11194 ADD_INSN1(ret, node, putobject, rb_node_line_lineno_val(node));
11195 }
11196 break;
11197 }
11198 case NODE_ENCODING:{
11199 if (!popped) {
11200 ADD_INSN1(ret, node, putobject, rb_node_encoding_val(node));
11201 }
11202 break;
11203 }
11204 case NODE_INTEGER:{
11205 VALUE lit = rb_node_integer_literal_val(node);
11206 if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit);
11207 debugp_param("integer", lit);
11208 if (!popped) {
11209 ADD_INSN1(ret, node, putobject, lit);
11210 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11211 }
11212 break;
11213 }
11214 case NODE_FLOAT:{
11215 VALUE lit = rb_node_float_literal_val(node);
11216 if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit);
11217 debugp_param("float", lit);
11218 if (!popped) {
11219 ADD_INSN1(ret, node, putobject, lit);
11220 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11221 }
11222 break;
11223 }
11224 case NODE_RATIONAL:{
11225 VALUE lit = rb_node_rational_literal_val(node);
11227 debugp_param("rational", lit);
11228 if (!popped) {
11229 ADD_INSN1(ret, node, putobject, lit);
11230 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11231 }
11232 break;
11233 }
11234 case NODE_IMAGINARY:{
11235 VALUE lit = rb_node_imaginary_literal_val(node);
11237 debugp_param("imaginary", lit);
11238 if (!popped) {
11239 ADD_INSN1(ret, node, putobject, lit);
11240 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11241 }
11242 break;
11243 }
11244 case NODE_FILE:
11245 case NODE_STR:{
11246 debugp_param("nd_lit", get_string_value(node));
11247 if (!popped) {
11248 VALUE lit = get_string_value(node);
11249 const rb_compile_option_t *option = ISEQ_COMPILE_DATA(iseq)->option;
11250 if ((option->debug_frozen_string_literal || RTEST(ruby_debug)) &&
11251 option->frozen_string_literal != ISEQ_FROZEN_STRING_LITERAL_DISABLED) {
11252 lit = rb_str_with_debug_created_info(lit, rb_iseq_path(iseq), line);
11253 RB_OBJ_SET_SHAREABLE(lit);
11254 }
11255 switch (option->frozen_string_literal) {
11256 case ISEQ_FROZEN_STRING_LITERAL_UNSET:
11257 ADD_INSN1(ret, node, putchilledstring, lit);
11258 break;
11259 case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
11260 ADD_INSN1(ret, node, putstring, lit);
11261 break;
11262 case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
11263 ADD_INSN1(ret, node, putobject, lit);
11264 break;
11265 default:
11266 rb_bug("invalid frozen_string_literal");
11267 }
11268 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11269 }
11270 break;
11271 }
11272 case NODE_DSTR:{
11273 compile_dstr(iseq, ret, node);
11274
11275 if (popped) {
11276 ADD_INSN(ret, node, pop);
11277 }
11278 break;
11279 }
11280 case NODE_XSTR:{
11281 ADD_CALL_RECEIVER(ret, node);
11282 VALUE str = rb_node_str_string_val(node);
11283 ADD_INSN1(ret, node, putobject, str);
11284 RB_OBJ_WRITTEN(iseq, Qundef, str);
11285 ADD_CALL(ret, node, idBackquote, INT2FIX(1));
11286
11287 if (popped) {
11288 ADD_INSN(ret, node, pop);
11289 }
11290 break;
11291 }
11292 case NODE_DXSTR:{
11293 ADD_CALL_RECEIVER(ret, node);
11294 compile_dstr(iseq, ret, node);
11295 ADD_CALL(ret, node, idBackquote, INT2FIX(1));
11296
11297 if (popped) {
11298 ADD_INSN(ret, node, pop);
11299 }
11300 break;
11301 }
11302 case NODE_EVSTR:
11303 CHECK(compile_evstr(iseq, ret, RNODE_EVSTR(node)->nd_body, popped));
11304 break;
11305 case NODE_REGX:{
11306 if (!popped) {
11307 VALUE lit = rb_node_regx_string_val(node);
11308 RB_OBJ_SET_SHAREABLE(lit);
11309 ADD_INSN1(ret, node, putobject, lit);
11310 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11311 }
11312 break;
11313 }
11314 case NODE_DREGX:
11315 compile_dregx(iseq, ret, node, popped);
11316 break;
11317 case NODE_ONCE:{
11318 int ic_index = body->ise_size++;
11319 const rb_iseq_t *block_iseq;
11320 block_iseq = NEW_CHILD_ISEQ(RNODE_ONCE(node)->nd_body, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, line);
11321
11322 ADD_INSN2(ret, node, once, block_iseq, INT2FIX(ic_index));
11323 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block_iseq);
11324
11325 if (popped) {
11326 ADD_INSN(ret, node, pop);
11327 }
11328 break;
11329 }
11330 case NODE_ARGSCAT:{
11331 if (popped) {
11332 CHECK(COMPILE(ret, "argscat head", RNODE_ARGSCAT(node)->nd_head));
11333 ADD_INSN1(ret, node, splatarray, Qfalse);
11334 ADD_INSN(ret, node, pop);
11335 CHECK(COMPILE(ret, "argscat body", RNODE_ARGSCAT(node)->nd_body));
11336 ADD_INSN1(ret, node, splatarray, Qfalse);
11337 ADD_INSN(ret, node, pop);
11338 }
11339 else {
11340 CHECK(COMPILE(ret, "argscat head", RNODE_ARGSCAT(node)->nd_head));
11341 const NODE *body_node = RNODE_ARGSCAT(node)->nd_body;
11342 if (nd_type_p(body_node, NODE_LIST)) {
11343 CHECK(compile_array(iseq, ret, body_node, popped, FALSE) >= 0);
11344 }
11345 else {
11346 CHECK(COMPILE(ret, "argscat body", body_node));
11347 ADD_INSN(ret, node, concattoarray);
11348 }
11349 }
11350 break;
11351 }
11352 case NODE_ARGSPUSH:{
11353 if (popped) {
11354 CHECK(COMPILE(ret, "argspush head", RNODE_ARGSPUSH(node)->nd_head));
11355 ADD_INSN1(ret, node, splatarray, Qfalse);
11356 ADD_INSN(ret, node, pop);
11357 CHECK(COMPILE_(ret, "argspush body", RNODE_ARGSPUSH(node)->nd_body, popped));
11358 }
11359 else {
11360 CHECK(COMPILE(ret, "argspush head", RNODE_ARGSPUSH(node)->nd_head));
11361 const NODE *body_node = RNODE_ARGSPUSH(node)->nd_body;
11362 if (keyword_node_p(body_node)) {
11363 CHECK(COMPILE_(ret, "array element", body_node, FALSE));
11364 ADD_INSN(ret, node, pushtoarraykwsplat);
11365 }
11366 else if (static_literal_node_p(body_node, iseq, false)) {
11367 ADD_INSN1(ret, body_node, putobject, static_literal_value(body_node, iseq));
11368 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
11369 }
11370 else {
11371 CHECK(COMPILE_(ret, "array element", body_node, FALSE));
11372 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
11373 }
11374 }
11375 break;
11376 }
11377 case NODE_SPLAT:{
11378 CHECK(COMPILE(ret, "splat", RNODE_SPLAT(node)->nd_head));
11379 ADD_INSN1(ret, node, splatarray, Qtrue);
11380
11381 if (popped) {
11382 ADD_INSN(ret, node, pop);
11383 }
11384 break;
11385 }
11386 case NODE_DEFN:{
11387 ID mid = RNODE_DEFN(node)->nd_mid;
11388 const rb_iseq_t *method_iseq = NEW_ISEQ(RNODE_DEFN(node)->nd_defn,
11389 rb_id2str(mid),
11390 ISEQ_TYPE_METHOD, line);
11391
11392 debugp_param("defn/iseq", rb_iseqw_new(method_iseq));
11393 ADD_INSN2(ret, node, definemethod, ID2SYM(mid), method_iseq);
11394 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)method_iseq);
11395
11396 if (!popped) {
11397 ADD_INSN1(ret, node, putobject, ID2SYM(mid));
11398 }
11399
11400 break;
11401 }
11402 case NODE_DEFS:{
11403 ID mid = RNODE_DEFS(node)->nd_mid;
11404 const rb_iseq_t * singleton_method_iseq = NEW_ISEQ(RNODE_DEFS(node)->nd_defn,
11405 rb_id2str(mid),
11406 ISEQ_TYPE_METHOD, line);
11407
11408 debugp_param("defs/iseq", rb_iseqw_new(singleton_method_iseq));
11409 CHECK(COMPILE(ret, "defs: recv", RNODE_DEFS(node)->nd_recv));
11410 ADD_INSN2(ret, node, definesmethod, ID2SYM(mid), singleton_method_iseq);
11411 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_method_iseq);
11412
11413 if (!popped) {
11414 ADD_INSN1(ret, node, putobject, ID2SYM(mid));
11415 }
11416 break;
11417 }
11418 case NODE_ALIAS:{
11419 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
11420 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
11421 CHECK(COMPILE(ret, "alias arg1", RNODE_ALIAS(node)->nd_1st));
11422 CHECK(COMPILE(ret, "alias arg2", RNODE_ALIAS(node)->nd_2nd));
11423 ADD_SEND(ret, node, id_core_set_method_alias, INT2FIX(3));
11424
11425 if (popped) {
11426 ADD_INSN(ret, node, pop);
11427 }
11428 break;
11429 }
11430 case NODE_VALIAS:{
11431 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
11432 ADD_INSN1(ret, node, putobject, ID2SYM(RNODE_VALIAS(node)->nd_alias));
11433 ADD_INSN1(ret, node, putobject, ID2SYM(RNODE_VALIAS(node)->nd_orig));
11434 ADD_SEND(ret, node, id_core_set_variable_alias, INT2FIX(2));
11435
11436 if (popped) {
11437 ADD_INSN(ret, node, pop);
11438 }
11439 break;
11440 }
11441 case NODE_UNDEF:{
11442 const rb_parser_ary_t *ary = RNODE_UNDEF(node)->nd_undefs;
11443
11444 for (long i = 0; i < ary->len; i++) {
11445 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
11446 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
11447 CHECK(COMPILE(ret, "undef arg", ary->data[i]));
11448 ADD_SEND(ret, node, id_core_undef_method, INT2FIX(2));
11449
11450 if (i < ary->len - 1) {
11451 ADD_INSN(ret, node, pop);
11452 }
11453 }
11454
11455 if (popped) {
11456 ADD_INSN(ret, node, pop);
11457 }
11458 break;
11459 }
11460 case NODE_CLASS:{
11461 const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(RNODE_CLASS(node)->nd_body,
11462 rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(get_node_colon_nd_mid(RNODE_CLASS(node)->nd_cpath)))),
11463 ISEQ_TYPE_CLASS, line);
11464 const int flags = VM_DEFINECLASS_TYPE_CLASS |
11465 (RNODE_CLASS(node)->nd_super ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
11466 compile_cpath(ret, iseq, RNODE_CLASS(node)->nd_cpath);
11467
11468 CHECK(COMPILE(ret, "super", RNODE_CLASS(node)->nd_super));
11469 ADD_INSN3(ret, node, defineclass, ID2SYM(get_node_colon_nd_mid(RNODE_CLASS(node)->nd_cpath)), class_iseq, INT2FIX(flags));
11470 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq);
11471
11472 if (popped) {
11473 ADD_INSN(ret, node, pop);
11474 }
11475 break;
11476 }
11477 case NODE_MODULE:{
11478 const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(RNODE_MODULE(node)->nd_body,
11479 rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(get_node_colon_nd_mid(RNODE_MODULE(node)->nd_cpath)))),
11480 ISEQ_TYPE_CLASS, line);
11481 const int flags = VM_DEFINECLASS_TYPE_MODULE |
11482 compile_cpath(ret, iseq, RNODE_MODULE(node)->nd_cpath);
11483
11484 ADD_INSN (ret, node, putnil); /* dummy */
11485 ADD_INSN3(ret, node, defineclass, ID2SYM(get_node_colon_nd_mid(RNODE_MODULE(node)->nd_cpath)), module_iseq, INT2FIX(flags));
11486 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)module_iseq);
11487
11488 if (popped) {
11489 ADD_INSN(ret, node, pop);
11490 }
11491 break;
11492 }
11493 case NODE_SCLASS:{
11494 ID singletonclass;
11495 const rb_iseq_t *singleton_class = NEW_ISEQ(RNODE_SCLASS(node)->nd_body, rb_fstring_lit("singleton class"),
11496 ISEQ_TYPE_CLASS, line);
11497
11498 CHECK(COMPILE(ret, "sclass#recv", RNODE_SCLASS(node)->nd_recv));
11499 ADD_INSN (ret, node, putnil);
11500 CONST_ID(singletonclass, "singletonclass");
11501
11502 /* `class << self` in a class body and `class << Foo` (constant
11503 receiver) are stable. All other forms are potentially dynamic. */
11504 int sclass_flags = VM_DEFINECLASS_TYPE_SINGLETON_CLASS;
11505 const NODE *recv = RNODE_SCLASS(node)->nd_recv;
11506 if (!(nd_type_p(recv, NODE_SELF) &&
11507 ISEQ_BODY(iseq)->type == ISEQ_TYPE_CLASS) &&
11508 !cpath_const_p(recv)) {
11509 sclass_flags |= VM_DEFINECLASS_FLAG_DYNAMIC_CREF;
11510 }
11511
11512 ADD_INSN3(ret, node, defineclass,
11513 ID2SYM(singletonclass), singleton_class,
11514 INT2FIX(sclass_flags));
11515 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_class);
11516
11517 if (popped) {
11518 ADD_INSN(ret, node, pop);
11519 }
11520 break;
11521 }
11522 case NODE_COLON2:
11523 CHECK(compile_colon2(iseq, ret, node, popped));
11524 break;
11525 case NODE_COLON3:
11526 CHECK(compile_colon3(iseq, ret, node, popped));
11527 break;
11528 case NODE_DOT2:
11529 CHECK(compile_dots(iseq, ret, node, popped, FALSE));
11530 break;
11531 case NODE_DOT3:
11532 CHECK(compile_dots(iseq, ret, node, popped, TRUE));
11533 break;
11534 case NODE_FLIP2:
11535 case NODE_FLIP3:{
11536 LABEL *lend = NEW_LABEL(line);
11537 LABEL *ltrue = NEW_LABEL(line);
11538 LABEL *lfalse = NEW_LABEL(line);
11539 CHECK(compile_flip_flop(iseq, ret, node, type == NODE_FLIP2,
11540 ltrue, lfalse));
11541 ADD_LABEL(ret, ltrue);
11542 ADD_INSN1(ret, node, putobject, Qtrue);
11543 ADD_INSNL(ret, node, jump, lend);
11544 ADD_LABEL(ret, lfalse);
11545 ADD_INSN1(ret, node, putobject, Qfalse);
11546 ADD_LABEL(ret, lend);
11547 break;
11548 }
11549 case NODE_SELF:{
11550 if (!popped) {
11551 ADD_INSN(ret, node, putself);
11552 }
11553 break;
11554 }
11555 case NODE_NIL:{
11556 if (!popped) {
11557 ADD_INSN(ret, node, putnil);
11558 }
11559 break;
11560 }
11561 case NODE_TRUE:{
11562 if (!popped) {
11563 ADD_INSN1(ret, node, putobject, Qtrue);
11564 }
11565 break;
11566 }
11567 case NODE_FALSE:{
11568 if (!popped) {
11569 ADD_INSN1(ret, node, putobject, Qfalse);
11570 }
11571 break;
11572 }
11573 case NODE_ERRINFO:
11574 CHECK(compile_errinfo(iseq, ret, node, popped));
11575 break;
11576 case NODE_DEFINED:
11577 if (!popped) {
11578 CHECK(compile_defined_expr(iseq, ret, node, Qtrue, false));
11579 }
11580 break;
11581 case NODE_POSTEXE:{
11582 /* compiled to:
11583 * ONCE{ rb_mRubyVMFrozenCore::core#set_postexe{ ... } }
11584 */
11585 int is_index = body->ise_size++;
11587 rb_iseq_new_with_callback_new_callback(build_postexe_iseq, RNODE_POSTEXE(node)->nd_body);
11588 const rb_iseq_t *once_iseq =
11589 NEW_CHILD_ISEQ_WITH_CALLBACK(ifunc, rb_fstring(make_name_for_block(iseq)), ISEQ_TYPE_BLOCK, line);
11590
11591 ADD_INSN2(ret, node, once, once_iseq, INT2FIX(is_index));
11592 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)once_iseq);
11593
11594 if (popped) {
11595 ADD_INSN(ret, node, pop);
11596 }
11597 break;
11598 }
11599 case NODE_KW_ARG:
11600 CHECK(compile_kw_arg(iseq, ret, node, popped));
11601 break;
11602 case NODE_DSYM:{
11603 compile_dstr(iseq, ret, node);
11604 if (!popped) {
11605 ADD_INSN(ret, node, intern);
11606 }
11607 else {
11608 ADD_INSN(ret, node, pop);
11609 }
11610 break;
11611 }
11612 case NODE_ATTRASGN:
11613 CHECK(compile_attrasgn(iseq, ret, node, popped));
11614 break;
11615 case NODE_LAMBDA:{
11616 /* compile same as lambda{...} */
11617 const rb_iseq_t *block = NEW_CHILD_ISEQ(RNODE_LAMBDA(node)->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line);
11618 VALUE argc = INT2FIX(0);
11619
11620 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
11621 ADD_CALL_WITH_BLOCK(ret, node, idLambda, argc, block);
11622 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block);
11623
11624 if (popped) {
11625 ADD_INSN(ret, node, pop);
11626 }
11627 break;
11628 }
11629 default:
11630 UNKNOWN_NODE("iseq_compile_each", node, COMPILE_NG);
11631 ng:
11632 debug_node_end();
11633 return COMPILE_NG;
11634 }
11635
11636 debug_node_end();
11637 return COMPILE_OK;
11638}
11639
11640/***************************/
11641/* instruction information */
11642/***************************/
11643
11644static int
11645insn_data_length(INSN *iobj)
11646{
11647 return insn_len(iobj->insn_id);
11648}
11649
11650static int
11651calc_sp_depth(int depth, INSN *insn)
11652{
11653 return comptime_insn_stack_increase(depth, insn->insn_id, insn->operands);
11654}
11655
11656static VALUE
11657opobj_inspect(VALUE obj)
11658{
11659 if (!SPECIAL_CONST_P(obj) && !RBASIC_CLASS(obj)) {
11660 switch (BUILTIN_TYPE(obj)) {
11661 case T_STRING:
11662 obj = rb_str_new_cstr(RSTRING_PTR(obj));
11663 break;
11664 case T_ARRAY:
11665 obj = rb_ary_dup(obj);
11666 break;
11667 default:
11668 break;
11669 }
11670 }
11671 return rb_inspect(obj);
11672}
11673
11674
11675
11676static VALUE
11677insn_data_to_s_detail(INSN *iobj)
11678{
11679 VALUE str = rb_sprintf("%-20s ", insn_name(iobj->insn_id));
11680
11681 if (iobj->operands) {
11682 const char *types = insn_op_types(iobj->insn_id);
11683 int j;
11684
11685 for (j = 0; types[j]; j++) {
11686 char type = types[j];
11687
11688 switch (type) {
11689 case TS_OFFSET: /* label(destination position) */
11690 {
11691 LABEL *lobj = (LABEL *)OPERAND_AT(iobj, j);
11692 rb_str_catf(str, LABEL_FORMAT, lobj->label_no);
11693 break;
11694 }
11695 break;
11696 case TS_ISEQ: /* iseq */
11697 {
11698 rb_iseq_t *iseq = (rb_iseq_t *)OPERAND_AT(iobj, j);
11699 VALUE val = Qnil;
11700 if (0 && iseq) { /* TODO: invalidate now */
11701 val = (VALUE)iseq;
11702 }
11703 rb_str_concat(str, opobj_inspect(val));
11704 }
11705 break;
11706 case TS_LINDEX:
11707 case TS_NUM: /* ulong */
11708 case TS_VALUE: /* VALUE */
11709 {
11710 VALUE v = OPERAND_AT(iobj, j);
11711 if (!CLASS_OF(v))
11712 rb_str_cat2(str, "<hidden>");
11713 else {
11714 rb_str_concat(str, opobj_inspect(v));
11715 }
11716 break;
11717 }
11718 case TS_ID: /* ID */
11719 rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j)));
11720 break;
11721 case TS_IC: /* inline cache */
11722 rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j)));
11723 break;
11724 case TS_IVC: /* inline ivar cache */
11725 rb_str_catf(str, "<ivc:%d>", FIX2INT(OPERAND_AT(iobj, j)));
11726 break;
11727 case TS_ICVARC: /* inline cvar cache */
11728 rb_str_catf(str, "<icvarc:%d>", FIX2INT(OPERAND_AT(iobj, j)));
11729 break;
11730 case TS_ISE: /* inline storage entry */
11731 rb_str_catf(str, "<ise:%d>", FIX2INT(OPERAND_AT(iobj, j)));
11732 break;
11733 case TS_CALLDATA: /* we store these as call infos at compile time */
11734 {
11735 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, j);
11736 rb_str_cat2(str, "<calldata:");
11737 if (vm_ci_mid(ci)) rb_str_catf(str, "%"PRIsVALUE, rb_id2str(vm_ci_mid(ci)));
11738 rb_str_catf(str, ", %d>", vm_ci_argc(ci));
11739 break;
11740 }
11741 case TS_CDHASH: /* case/when condition cache */
11742 rb_str_cat2(str, "<ch>");
11743 break;
11744 case TS_FUNCPTR:
11745 {
11746 void *func = (void *)OPERAND_AT(iobj, j);
11747#ifdef HAVE_DLADDR
11748 Dl_info info;
11749 if (dladdr(func, &info) && info.dli_sname) {
11750 rb_str_cat2(str, info.dli_sname);
11751 break;
11752 }
11753#endif
11754 rb_str_catf(str, "<%p>", func);
11755 }
11756 break;
11757 case TS_BUILTIN:
11758 rb_str_cat2(str, "<TS_BUILTIN>");
11759 break;
11760 default:{
11761 rb_raise(rb_eSyntaxError, "unknown operand type: %c", type);
11762 }
11763 }
11764 if (types[j + 1]) {
11765 rb_str_cat2(str, ", ");
11766 }
11767 }
11768 }
11769 return str;
11770}
11771
11772static void
11773dump_disasm_list(const LINK_ELEMENT *link)
11774{
11775 dump_disasm_list_with_cursor(link, NULL, NULL);
11776}
11777
11778static void
11779dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, const LABEL *dest)
11780{
11781 int pos = 0;
11782 INSN *iobj;
11783 LABEL *lobj;
11784 VALUE str;
11785
11786 printf("-- raw disasm--------\n");
11787
11788 while (link) {
11789 if (curr) printf(curr == link ? "*" : " ");
11790 switch (link->type) {
11791 case ISEQ_ELEMENT_INSN:
11792 {
11793 iobj = (INSN *)link;
11794 str = insn_data_to_s_detail(iobj);
11795 printf(" %04d %-65s(%4u)\n", pos, StringValueCStr(str), iobj->insn_info.line_no);
11796 pos += insn_data_length(iobj);
11797 break;
11798 }
11799 case ISEQ_ELEMENT_LABEL:
11800 {
11801 lobj = (LABEL *)link;
11802 printf(LABEL_FORMAT" [sp: %d, unremovable: %d, refcnt: %d]%s\n", lobj->label_no, lobj->sp, lobj->unremovable, lobj->refcnt,
11803 dest == lobj ? " <---" : "");
11804 break;
11805 }
11806 case ISEQ_ELEMENT_TRACE:
11807 {
11808 TRACE *trace = (TRACE *)link;
11809 printf(" trace: %0x\n", trace->event);
11810 break;
11811 }
11812 case ISEQ_ELEMENT_ADJUST:
11813 {
11814 ADJUST *adjust = (ADJUST *)link;
11815 printf(" adjust: [label: %d]\n", adjust->label ? adjust->label->label_no : -1);
11816 break;
11817 }
11818 default:
11819 /* ignore */
11820 rb_raise(rb_eSyntaxError, "dump_disasm_list error: %d\n", (int)link->type);
11821 }
11822 link = link->next;
11823 }
11824 printf("---------------------\n");
11825 fflush(stdout);
11826}
11827
11828int
11829rb_insn_len(VALUE insn)
11830{
11831 return insn_len(insn);
11832}
11833
11834const char *
11835rb_insns_name(int i)
11836{
11837 return insn_name(i);
11838}
11839
11840VALUE
11841rb_insns_name_array(void)
11842{
11843 VALUE ary = rb_ary_new_capa(VM_INSTRUCTION_SIZE);
11844 int i;
11845 for (i = 0; i < VM_INSTRUCTION_SIZE; i++) {
11846 rb_ary_push(ary, rb_fstring_cstr(insn_name(i)));
11847 }
11848 return rb_ary_freeze(ary);
11849}
11850
11851static LABEL *
11852register_label(rb_iseq_t *iseq, struct st_table *labels_table, VALUE obj)
11853{
11854 LABEL *label = 0;
11855 st_data_t tmp;
11856 obj = rb_to_symbol_type(obj);
11857
11858 if (st_lookup(labels_table, obj, &tmp) == 0) {
11859 label = NEW_LABEL(0);
11860 st_insert(labels_table, obj, (st_data_t)label);
11861 }
11862 else {
11863 label = (LABEL *)tmp;
11864 }
11865 LABEL_REF(label);
11866 return label;
11867}
11868
11869static VALUE
11870get_exception_sym2type(VALUE sym)
11871{
11872 static VALUE symRescue, symEnsure, symRetry;
11873 static VALUE symBreak, symRedo, symNext;
11874
11875 if (symRescue == 0) {
11876 symRescue = ID2SYM(rb_intern_const("rescue"));
11877 symEnsure = ID2SYM(rb_intern_const("ensure"));
11878 symRetry = ID2SYM(rb_intern_const("retry"));
11879 symBreak = ID2SYM(rb_intern_const("break"));
11880 symRedo = ID2SYM(rb_intern_const("redo"));
11881 symNext = ID2SYM(rb_intern_const("next"));
11882 }
11883
11884 if (sym == symRescue) return CATCH_TYPE_RESCUE;
11885 if (sym == symEnsure) return CATCH_TYPE_ENSURE;
11886 if (sym == symRetry) return CATCH_TYPE_RETRY;
11887 if (sym == symBreak) return CATCH_TYPE_BREAK;
11888 if (sym == symRedo) return CATCH_TYPE_REDO;
11889 if (sym == symNext) return CATCH_TYPE_NEXT;
11890 rb_raise(rb_eSyntaxError, "invalid exception symbol: %+"PRIsVALUE, sym);
11891 return 0;
11892}
11893
11894static int
11895iseq_build_from_ary_exception(rb_iseq_t *iseq, struct st_table *labels_table,
11896 VALUE exception)
11897{
11898 int i;
11899
11900 for (i=0; i<RARRAY_LEN(exception); i++) {
11901 const rb_iseq_t *eiseq;
11902 VALUE v, type;
11903 LABEL *lstart, *lend, *lcont;
11904 unsigned int sp;
11905
11906 v = rb_to_array_type(RARRAY_AREF(exception, i));
11907 if (RARRAY_LEN(v) != 6) {
11908 rb_raise(rb_eSyntaxError, "wrong exception entry");
11909 }
11910 type = get_exception_sym2type(RARRAY_AREF(v, 0));
11911 if (NIL_P(RARRAY_AREF(v, 1))) {
11912 eiseq = NULL;
11913 }
11914 else {
11915 eiseq = rb_iseqw_to_iseq(rb_iseq_load(RARRAY_AREF(v, 1), (VALUE)iseq, Qnil));
11916 }
11917
11918 lstart = register_label(iseq, labels_table, RARRAY_AREF(v, 2));
11919 lend = register_label(iseq, labels_table, RARRAY_AREF(v, 3));
11920 lcont = register_label(iseq, labels_table, RARRAY_AREF(v, 4));
11921 sp = NUM2UINT(RARRAY_AREF(v, 5));
11922
11923 /* TODO: Dirty Hack! Fix me */
11924 if (type == CATCH_TYPE_RESCUE ||
11925 type == CATCH_TYPE_BREAK ||
11926 type == CATCH_TYPE_NEXT) {
11927 ++sp;
11928 }
11929
11930 lcont->sp = sp;
11931
11932 ADD_CATCH_ENTRY(type, lstart, lend, eiseq, lcont);
11933
11934 RB_GC_GUARD(v);
11935 }
11936 return COMPILE_OK;
11937}
11938
11939static struct st_table *
11940insn_make_insn_table(void)
11941{
11942 struct st_table *table;
11943 int i;
11944 table = st_init_numtable_with_size(VM_INSTRUCTION_SIZE);
11945
11946 for (i=0; i<VM_INSTRUCTION_SIZE; i++) {
11947 st_insert(table, ID2SYM(rb_intern_const(insn_name(i))), i);
11948 }
11949
11950 return table;
11951}
11952
11953static const rb_iseq_t *
11954iseq_build_load_iseq(const rb_iseq_t *iseq, VALUE op)
11955{
11956 VALUE iseqw;
11957 const rb_iseq_t *loaded_iseq;
11958
11959 if (RB_TYPE_P(op, T_ARRAY)) {
11960 iseqw = rb_iseq_load(op, (VALUE)iseq, Qnil);
11961 }
11962 else if (CLASS_OF(op) == rb_cISeq) {
11963 iseqw = op;
11964 }
11965 else {
11966 rb_raise(rb_eSyntaxError, "ISEQ is required");
11967 }
11968
11969 loaded_iseq = rb_iseqw_to_iseq(iseqw);
11970 return loaded_iseq;
11971}
11972
11973static VALUE
11974iseq_build_callinfo_from_hash(rb_iseq_t *iseq, VALUE op)
11975{
11976 ID mid = 0;
11977 int orig_argc = 0;
11978 unsigned int flag = 0;
11979 struct rb_callinfo_kwarg *kw_arg = 0;
11980
11981 if (!NIL_P(op)) {
11982 VALUE vmid = rb_hash_aref(op, ID2SYM(rb_intern_const("mid")));
11983 VALUE vflag = rb_hash_aref(op, ID2SYM(rb_intern_const("flag")));
11984 VALUE vorig_argc = rb_hash_aref(op, ID2SYM(rb_intern_const("orig_argc")));
11985 VALUE vkw_arg = rb_hash_aref(op, ID2SYM(rb_intern_const("kw_arg")));
11986
11987 if (!NIL_P(vmid)) mid = SYM2ID(vmid);
11988 if (!NIL_P(vflag)) flag = NUM2UINT(vflag);
11989 if (!NIL_P(vorig_argc)) orig_argc = FIX2INT(vorig_argc);
11990
11991 if (!NIL_P(vkw_arg)) {
11992 int i;
11993 int len = RARRAY_LENINT(vkw_arg);
11994 size_t n = rb_callinfo_kwarg_bytes(len);
11995
11996 kw_arg = xmalloc(n);
11997 kw_arg->references = 0;
11998 kw_arg->keyword_len = len;
11999 for (i = 0; i < len; i++) {
12000 VALUE kw = RARRAY_AREF(vkw_arg, i);
12001 SYM2ID(kw); /* make immortal */
12002 kw_arg->keywords[i] = kw;
12003 }
12004 }
12005 }
12006
12007 const struct rb_callinfo *ci = new_callinfo(iseq, mid, orig_argc, flag, kw_arg, (flag & VM_CALL_ARGS_SIMPLE) == 0);
12008 RB_OBJ_WRITTEN(iseq, Qundef, ci);
12009 return (VALUE)ci;
12010}
12011
12012static rb_event_flag_t
12013event_name_to_flag(VALUE sym)
12014{
12015#define CHECK_EVENT(ev) if (sym == ID2SYM(rb_intern_const(#ev))) return ev;
12016 CHECK_EVENT(RUBY_EVENT_LINE);
12017 CHECK_EVENT(RUBY_EVENT_CLASS);
12018 CHECK_EVENT(RUBY_EVENT_END);
12019 CHECK_EVENT(RUBY_EVENT_CALL);
12020 CHECK_EVENT(RUBY_EVENT_RETURN);
12021 CHECK_EVENT(RUBY_EVENT_B_CALL);
12022 CHECK_EVENT(RUBY_EVENT_B_RETURN);
12023 CHECK_EVENT(RUBY_EVENT_RESCUE);
12024#undef CHECK_EVENT
12025 return RUBY_EVENT_NONE;
12026}
12027
12028static int
12029iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
12030 VALUE body, VALUE node_ids, VALUE labels_wrapper)
12031{
12032 /* TODO: body should be frozen */
12033 long i, len = RARRAY_LEN(body);
12034 struct st_table *labels_table = RTYPEDDATA_DATA(labels_wrapper);
12035 int j;
12036 int line_no = 0, node_id = -1, insn_idx = 0;
12037 int ret = COMPILE_OK;
12038
12039 /*
12040 * index -> LABEL *label
12041 */
12042 static struct st_table *insn_table;
12043
12044 if (insn_table == 0) {
12045 insn_table = insn_make_insn_table();
12046 }
12047
12048 for (i=0; i<len; i++) {
12049 VALUE obj = RARRAY_AREF(body, i);
12050
12051 if (SYMBOL_P(obj)) {
12052 rb_event_flag_t event;
12053 if ((event = event_name_to_flag(obj)) != RUBY_EVENT_NONE) {
12054 ADD_TRACE(anchor, event);
12055 }
12056 else {
12057 LABEL *label = register_label(iseq, labels_table, obj);
12058 ADD_LABEL(anchor, label);
12059 }
12060 }
12061 else if (FIXNUM_P(obj)) {
12062 line_no = NUM2INT(obj);
12063 }
12064 else if (RB_TYPE_P(obj, T_ARRAY)) {
12065 VALUE *argv = 0;
12066 int argc = RARRAY_LENINT(obj) - 1;
12067 st_data_t insn_id;
12068 VALUE insn;
12069
12070 if (node_ids) {
12071 node_id = NUM2INT(rb_ary_entry(node_ids, insn_idx++));
12072 }
12073
12074 insn = (argc < 0) ? Qnil : RARRAY_AREF(obj, 0);
12075 if (st_lookup(insn_table, (st_data_t)insn, &insn_id) == 0) {
12076 /* TODO: exception */
12077 COMPILE_ERROR(iseq, line_no,
12078 "unknown instruction: %+"PRIsVALUE, insn);
12079 ret = COMPILE_NG;
12080 break;
12081 }
12082
12083 if (argc != insn_len((VALUE)insn_id)-1) {
12084 COMPILE_ERROR(iseq, line_no,
12085 "operand size mismatch");
12086 ret = COMPILE_NG;
12087 break;
12088 }
12089
12090 if (argc > 0) {
12091 argv = compile_data_calloc2_type(iseq, VALUE, argc);
12092
12093 // add element before operand setup to make GC root
12094 ADD_ELEM(anchor,
12095 (LINK_ELEMENT*)new_insn_core(iseq, line_no, node_id,
12096 (enum ruby_vminsn_type)insn_id, argc, argv));
12097
12098 for (j=0; j<argc; j++) {
12099 VALUE op = rb_ary_entry(obj, j+1);
12100 switch (insn_op_type((VALUE)insn_id, j)) {
12101 case TS_OFFSET: {
12102 LABEL *label = register_label(iseq, labels_table, op);
12103 argv[j] = (VALUE)label;
12104 break;
12105 }
12106 case TS_LINDEX:
12107 case TS_NUM:
12108 (void)NUM2INT(op);
12109 argv[j] = op;
12110 break;
12111 case TS_VALUE:
12112 argv[j] = op;
12113 RB_OBJ_WRITTEN(iseq, Qundef, op);
12114 break;
12115 case TS_ISEQ:
12116 {
12117 if (op != Qnil) {
12118 VALUE v = (VALUE)iseq_build_load_iseq(iseq, op);
12119 argv[j] = v;
12120 RB_OBJ_WRITTEN(iseq, Qundef, v);
12121 }
12122 else {
12123 argv[j] = 0;
12124 }
12125 }
12126 break;
12127 case TS_ISE:
12128 argv[j] = op;
12129 if (NUM2UINT(op) >= ISEQ_BODY(iseq)->ise_size) {
12130 ISEQ_BODY(iseq)->ise_size = NUM2INT(op) + 1;
12131 }
12132 break;
12133 case TS_IC:
12134 {
12135 VALUE segments = rb_ary_new();
12136 op = rb_to_array_type(op);
12137
12138 for (int i = 0; i < RARRAY_LEN(op); i++) {
12139 VALUE sym = RARRAY_AREF(op, i);
12140 sym = rb_to_symbol_type(sym);
12141 rb_ary_push(segments, sym);
12142 }
12143
12144 RB_GC_GUARD(op);
12145 argv[j] = segments;
12146 RB_OBJ_WRITTEN(iseq, Qundef, segments);
12147 ISEQ_BODY(iseq)->ic_size++;
12148 }
12149 break;
12150 case TS_IVC: /* inline ivar cache */
12151 argv[j] = op;
12152 if (NUM2UINT(op) >= ISEQ_BODY(iseq)->ivc_size) {
12153 ISEQ_BODY(iseq)->ivc_size = NUM2INT(op) + 1;
12154 }
12155 break;
12156 case TS_ICVARC: /* inline cvar cache */
12157 argv[j] = op;
12158 if (NUM2UINT(op) >= ISEQ_BODY(iseq)->icvarc_size) {
12159 ISEQ_BODY(iseq)->icvarc_size = NUM2INT(op) + 1;
12160 }
12161 break;
12162 case TS_CALLDATA:
12163 argv[j] = iseq_build_callinfo_from_hash(iseq, op);
12164 break;
12165 case TS_ID:
12166 argv[j] = rb_to_symbol_type(op);
12167 break;
12168 case TS_CDHASH:
12169 {
12170 int i;
12171 VALUE map = rb_hash_new_with_size(RARRAY_LEN(op)/2);
12172
12173 RHASH_TBL_RAW(map)->type = &cdhash_type;
12174 op = rb_to_array_type(op);
12175 for (i=0; i<RARRAY_LEN(op); i+=2) {
12176 VALUE key = RARRAY_AREF(op, i);
12177 VALUE sym = RARRAY_AREF(op, i+1);
12178 LABEL *label =
12179 register_label(iseq, labels_table, sym);
12180 rb_hash_aset(map, key, (VALUE)label | 1);
12181 }
12182 RB_GC_GUARD(op);
12183 RB_OBJ_SET_SHAREABLE(rb_obj_hide(map)); // allow mutation while compiling
12184 argv[j] = map;
12185 RB_OBJ_WRITTEN(iseq, Qundef, map);
12186 }
12187 break;
12188 case TS_FUNCPTR:
12189 {
12190#if SIZEOF_VALUE <= SIZEOF_LONG
12191 long funcptr = NUM2LONG(op);
12192#else
12193 LONG_LONG funcptr = NUM2LL(op);
12194#endif
12195 argv[j] = (VALUE)funcptr;
12196 }
12197 break;
12198 default:
12199 rb_raise(rb_eSyntaxError, "unknown operand: %c", insn_op_type((VALUE)insn_id, j));
12200 }
12201 }
12202 }
12203 else {
12204 ADD_ELEM(anchor,
12205 (LINK_ELEMENT*)new_insn_core(iseq, line_no, node_id,
12206 (enum ruby_vminsn_type)insn_id, argc, NULL));
12207 }
12208 }
12209 else {
12210 rb_raise(rb_eTypeError, "unexpected object for instruction");
12211 }
12212 }
12213 RTYPEDDATA_DATA(labels_wrapper) = 0;
12214 RB_GC_GUARD(labels_wrapper);
12215 validate_labels(iseq, labels_table);
12216 if (!ret) return ret;
12217 return iseq_setup(iseq, anchor);
12218}
12219
12220#define CHECK_ARRAY(v) rb_to_array_type(v)
12221#define CHECK_SYMBOL(v) rb_to_symbol_type(v)
12222
12223static int
12224int_param(int *dst, VALUE param, VALUE sym)
12225{
12226 VALUE val = rb_hash_aref(param, sym);
12227 if (FIXNUM_P(val)) {
12228 *dst = FIX2INT(val);
12229 return TRUE;
12230 }
12231 else if (!NIL_P(val)) {
12232 rb_raise(rb_eTypeError, "invalid %+"PRIsVALUE" Fixnum: %+"PRIsVALUE,
12233 sym, val);
12234 }
12235 return FALSE;
12236}
12237
12238static const struct rb_iseq_param_keyword *
12239iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords)
12240{
12241 int i, j;
12242 int len = RARRAY_LENINT(keywords);
12243 int default_len;
12244 VALUE key, sym, default_val;
12245 VALUE *dvs;
12246 ID *ids;
12247 struct rb_iseq_param_keyword *keyword = ZALLOC(struct rb_iseq_param_keyword);
12248
12249 ISEQ_BODY(iseq)->param.flags.has_kw = TRUE;
12250
12251 keyword->num = len;
12252#define SYM(s) ID2SYM(rb_intern_const(#s))
12253 (void)int_param(&keyword->bits_start, params, SYM(kwbits));
12254 i = keyword->bits_start - keyword->num;
12255 ids = (ID *)&ISEQ_BODY(iseq)->local_table[i];
12256#undef SYM
12257
12258 /* required args */
12259 for (i = 0; i < len; i++) {
12260 VALUE val = RARRAY_AREF(keywords, i);
12261
12262 if (!SYMBOL_P(val)) {
12263 goto default_values;
12264 }
12265 ids[i] = SYM2ID(val);
12266 keyword->required_num++;
12267 }
12268
12269 default_values: /* note: we intentionally preserve `i' from previous loop */
12270 default_len = len - i;
12271 if (default_len == 0) {
12272 keyword->table = ids;
12273 return keyword;
12274 }
12275 else if (default_len < 0) {
12277 }
12278
12279 dvs = ALLOC_N(VALUE, (unsigned int)default_len);
12280
12281 for (j = 0; i < len; i++, j++) {
12282 key = RARRAY_AREF(keywords, i);
12283 CHECK_ARRAY(key);
12284
12285 switch (RARRAY_LEN(key)) {
12286 case 1:
12287 sym = RARRAY_AREF(key, 0);
12288 default_val = Qundef;
12289 break;
12290 case 2:
12291 sym = RARRAY_AREF(key, 0);
12292 default_val = RARRAY_AREF(key, 1);
12293 break;
12294 default:
12295 rb_raise(rb_eTypeError, "keyword default has unsupported len %+"PRIsVALUE, key);
12296 }
12297 ids[i] = SYM2ID(sym);
12298 RB_OBJ_WRITE(iseq, &dvs[j], default_val);
12299 }
12300
12301 keyword->table = ids;
12302 keyword->default_values = dvs;
12303
12304 return keyword;
12305}
12306
12307static void
12308iseq_insn_each_object_mark_and_move(VALUE * obj, VALUE _)
12309{
12310 rb_gc_mark_and_move(obj);
12311}
12312
12313void
12314rb_iseq_mark_and_move_insn_storage(struct iseq_compile_data_storage *storage)
12315{
12316 INSN *iobj = 0;
12317 size_t size = sizeof(INSN);
12318 size_t align = ALIGNMENT_SIZE_OF(INSN);
12319 unsigned int pos = 0;
12320
12321 while (storage) {
12322 size_t padding = calc_padding((void *)&storage->buff[pos], align);
12323 size_t offset = pos + size + padding;
12324 if (offset > storage->size || offset > storage->pos) {
12325 pos = 0;
12326 storage = storage->next;
12327 }
12328 else {
12329 pos += (int)padding;
12330
12331 iobj = (INSN *)&storage->buff[pos];
12332
12333 if (iobj->operands) {
12334 iseq_insn_each_markable_object(iobj, iseq_insn_each_object_mark_and_move, (VALUE)0);
12335 }
12336 pos += (int)size;
12337 }
12338 }
12339}
12340
12341static const rb_data_type_t labels_wrapper_type = {
12342 .wrap_struct_name = "compiler/labels_wrapper",
12343 .function = {
12344 .dmark = (RUBY_DATA_FUNC)rb_mark_set,
12345 .dfree = (RUBY_DATA_FUNC)st_free_table,
12346 },
12347 .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
12348};
12349
12350void
12351rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
12352 VALUE exception, VALUE body)
12353{
12354#define SYM(s) ID2SYM(rb_intern_const(#s))
12355 int i, len;
12356 unsigned int arg_size, local_size, stack_max;
12357 ID *tbl;
12358 struct st_table *labels_table = st_init_numtable();
12359 VALUE labels_wrapper = TypedData_Wrap_Struct(0, &labels_wrapper_type, labels_table);
12360 VALUE arg_opt_labels = rb_hash_aref(params, SYM(opt));
12361 VALUE keywords = rb_hash_aref(params, SYM(keyword));
12362 VALUE sym_arg_rest = ID2SYM(rb_intern_const("#arg_rest"));
12363 DECL_ANCHOR(anchor);
12364 INIT_ANCHOR(anchor);
12365
12366 len = RARRAY_LENINT(locals);
12367 ISEQ_BODY(iseq)->local_table_size = len;
12368 ISEQ_BODY(iseq)->local_table = tbl = len > 0 ? (ID *)ALLOC_N(ID, ISEQ_BODY(iseq)->local_table_size) : NULL;
12369
12370 for (i = 0; i < len; i++) {
12371 VALUE lv = RARRAY_AREF(locals, i);
12372
12373 if (sym_arg_rest == lv) {
12374 tbl[i] = 0;
12375 }
12376 else {
12377 tbl[i] = FIXNUM_P(lv) ? (ID)FIX2LONG(lv) : SYM2ID(CHECK_SYMBOL(lv));
12378 }
12379 }
12380
12381#define INT_PARAM(F) int_param(&ISEQ_BODY(iseq)->param.F, params, SYM(F))
12382 if (INT_PARAM(lead_num)) {
12383 ISEQ_BODY(iseq)->param.flags.has_lead = TRUE;
12384 }
12385 if (INT_PARAM(post_num)) ISEQ_BODY(iseq)->param.flags.has_post = TRUE;
12386 if (INT_PARAM(post_start)) ISEQ_BODY(iseq)->param.flags.has_post = TRUE;
12387 if (INT_PARAM(rest_start)) ISEQ_BODY(iseq)->param.flags.has_rest = TRUE;
12388 if (INT_PARAM(block_start)) ISEQ_BODY(iseq)->param.flags.has_block = TRUE;
12389#undef INT_PARAM
12390 {
12391#define INT_PARAM(F) F = (int_param(&x, misc, SYM(F)) ? (unsigned int)x : 0)
12392 int x;
12393 INT_PARAM(arg_size);
12394 INT_PARAM(local_size);
12395 INT_PARAM(stack_max);
12396#undef INT_PARAM
12397 }
12398
12399 VALUE node_ids = Qfalse;
12400#ifdef USE_ISEQ_NODE_ID
12401 node_ids = rb_hash_aref(misc, ID2SYM(rb_intern("node_ids")));
12402 if (!RB_TYPE_P(node_ids, T_ARRAY)) {
12403 rb_raise(rb_eTypeError, "node_ids is not an array");
12404 }
12405#endif
12406
12407 if (RB_TYPE_P(arg_opt_labels, T_ARRAY)) {
12408 len = RARRAY_LENINT(arg_opt_labels);
12409 ISEQ_BODY(iseq)->param.flags.has_opt = !!(len - 1 >= 0);
12410
12411 if (ISEQ_BODY(iseq)->param.flags.has_opt) {
12412 VALUE *opt_table = ALLOC_N(VALUE, len);
12413
12414 for (i = 0; i < len; i++) {
12415 VALUE ent = RARRAY_AREF(arg_opt_labels, i);
12416 LABEL *label = register_label(iseq, labels_table, ent);
12417 opt_table[i] = (VALUE)label;
12418 }
12419
12420 ISEQ_BODY(iseq)->param.opt_num = len - 1;
12421 ISEQ_BODY(iseq)->param.opt_table = opt_table;
12422 }
12423 }
12424 else if (!NIL_P(arg_opt_labels)) {
12425 rb_raise(rb_eTypeError, ":opt param is not an array: %+"PRIsVALUE,
12426 arg_opt_labels);
12427 }
12428
12429 if (RB_TYPE_P(keywords, T_ARRAY)) {
12430 ISEQ_BODY(iseq)->param.keyword = iseq_build_kw(iseq, params, keywords);
12431 }
12432 else if (!NIL_P(keywords)) {
12433 rb_raise(rb_eTypeError, ":keywords param is not an array: %+"PRIsVALUE,
12434 keywords);
12435 }
12436
12437 if (Qtrue == rb_hash_aref(params, SYM(ambiguous_param0))) {
12438 ISEQ_BODY(iseq)->param.flags.ambiguous_param0 = TRUE;
12439 }
12440
12441 if (Qtrue == rb_hash_aref(params, SYM(use_block))) {
12442 ISEQ_BODY(iseq)->param.flags.use_block = TRUE;
12443 }
12444
12445 if (int_param(&i, params, SYM(kwrest))) {
12446 struct rb_iseq_param_keyword *keyword = (struct rb_iseq_param_keyword *)ISEQ_BODY(iseq)->param.keyword;
12447 if (keyword == NULL) {
12448 ISEQ_BODY(iseq)->param.keyword = keyword = ZALLOC(struct rb_iseq_param_keyword);
12449 }
12450 keyword->rest_start = i;
12451 ISEQ_BODY(iseq)->param.flags.has_kwrest = TRUE;
12452 }
12453#undef SYM
12454 iseq_calc_param_size(iseq);
12455
12456 /* exception */
12457 iseq_build_from_ary_exception(iseq, labels_table, exception);
12458
12459 /* body */
12460 iseq_build_from_ary_body(iseq, anchor, body, node_ids, labels_wrapper);
12461
12462 ISEQ_BODY(iseq)->param.size = arg_size;
12463 ISEQ_BODY(iseq)->local_table_size = local_size;
12464 ISEQ_BODY(iseq)->stack_max = stack_max;
12465}
12466
12467/* for parser */
12468
12469int
12470rb_dvar_defined(ID id, const rb_iseq_t *iseq)
12471{
12472 if (iseq) {
12473 const struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
12474 while (body->type == ISEQ_TYPE_BLOCK ||
12475 body->type == ISEQ_TYPE_RESCUE ||
12476 body->type == ISEQ_TYPE_ENSURE ||
12477 body->type == ISEQ_TYPE_EVAL ||
12478 body->type == ISEQ_TYPE_MAIN
12479 ) {
12480 unsigned int i;
12481
12482 for (i = 0; i < body->local_table_size; i++) {
12483 if (body->local_table[i] == id) {
12484 return 1;
12485 }
12486 }
12487 iseq = body->parent_iseq;
12488 body = ISEQ_BODY(iseq);
12489 }
12490 }
12491 return 0;
12492}
12493
12494int
12495rb_local_defined(ID id, const rb_iseq_t *iseq)
12496{
12497 if (iseq) {
12498 unsigned int i;
12499 const struct rb_iseq_constant_body *const body = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq);
12500
12501 for (i=0; i<body->local_table_size; i++) {
12502 if (body->local_table[i] == id) {
12503 return 1;
12504 }
12505 }
12506 }
12507 return 0;
12508}
12509
12510/* ISeq binary format */
12511
12512#ifndef IBF_ISEQ_DEBUG
12513#define IBF_ISEQ_DEBUG 0
12514#endif
12515
12516#ifndef IBF_ISEQ_ENABLE_LOCAL_BUFFER
12517#define IBF_ISEQ_ENABLE_LOCAL_BUFFER 0
12518#endif
12519
12520typedef uint32_t ibf_offset_t;
12521#define IBF_OFFSET(ptr) ((ibf_offset_t)(VALUE)(ptr))
12522
12523#define IBF_MAJOR_VERSION ISEQ_MAJOR_VERSION
12524#ifdef RUBY_DEVEL
12525#define IBF_DEVEL_VERSION 5
12526#define IBF_MINOR_VERSION (ISEQ_MINOR_VERSION * 10000 + IBF_DEVEL_VERSION)
12527#else
12528#define IBF_MINOR_VERSION ISEQ_MINOR_VERSION
12529#endif
12530
12531static const char IBF_ENDIAN_MARK =
12532#ifdef WORDS_BIGENDIAN
12533 'b'
12534#else
12535 'l'
12536#endif
12537 ;
12538
12540 char magic[4]; /* YARB */
12541 uint32_t major_version;
12542 uint32_t minor_version;
12543 uint32_t size;
12544 uint32_t extra_size;
12545
12546 uint32_t iseq_list_size;
12547 uint32_t global_object_list_size;
12548 ibf_offset_t iseq_list_offset;
12549 ibf_offset_t global_object_list_offset;
12550 uint8_t endian;
12551 uint8_t wordsize; /* assume no 2048-bit CPU */
12552};
12553
12555 VALUE str;
12556 st_table *obj_table; /* obj -> obj number */
12557};
12558
12559struct ibf_dump {
12560 st_table *iseq_table; /* iseq -> iseq number */
12561 struct ibf_dump_buffer global_buffer;
12562 struct ibf_dump_buffer *current_buffer;
12563};
12564
12566 const char *buff;
12567 ibf_offset_t size;
12568
12569 VALUE obj_list; /* [obj0, ...] */
12570 unsigned int obj_list_size;
12571 ibf_offset_t obj_list_offset;
12572};
12573
12574struct ibf_load {
12575 const struct ibf_header *header;
12576 VALUE iseq_list; /* [iseq0, ...] */
12577 struct ibf_load_buffer global_buffer;
12578 VALUE loader_obj;
12579 rb_iseq_t *iseq;
12580 VALUE str;
12581 struct ibf_load_buffer *current_buffer;
12582};
12583
12585 long size;
12586 VALUE buffer[1];
12587};
12588
12589static void
12590pinned_list_mark(void *ptr)
12591{
12592 long i;
12593 struct pinned_list *list = (struct pinned_list *)ptr;
12594 for (i = 0; i < list->size; i++) {
12595 if (list->buffer[i]) {
12596 rb_gc_mark(list->buffer[i]);
12597 }
12598 }
12599}
12600
12601static const rb_data_type_t pinned_list_type = {
12602 "pinned_list",
12603 {
12604 pinned_list_mark,
12606 NULL, // No external memory to report,
12607 },
12608 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE
12609};
12610
12611static VALUE
12612pinned_list_fetch(VALUE list, long offset)
12613{
12614 struct pinned_list * ptr;
12615
12616 TypedData_Get_Struct(list, struct pinned_list, &pinned_list_type, ptr);
12617
12618 if (offset >= ptr->size) {
12619 rb_raise(rb_eIndexError, "object index out of range: %ld", offset);
12620 }
12621
12622 return ptr->buffer[offset];
12623}
12624
12625static void
12626pinned_list_store(VALUE list, long offset, VALUE object)
12627{
12628 struct pinned_list * ptr;
12629
12630 TypedData_Get_Struct(list, struct pinned_list, &pinned_list_type, ptr);
12631
12632 if (offset >= ptr->size) {
12633 rb_raise(rb_eIndexError, "object index out of range: %ld", offset);
12634 }
12635
12636 RB_OBJ_WRITE(list, &ptr->buffer[offset], object);
12637}
12638
12639static VALUE
12640pinned_list_new(long size)
12641{
12642 size_t memsize = offsetof(struct pinned_list, buffer) + size * sizeof(VALUE);
12643 VALUE obj_list = rb_data_typed_object_zalloc(0, memsize, &pinned_list_type);
12644 struct pinned_list * ptr = RTYPEDDATA_GET_DATA(obj_list);
12645 ptr->size = size;
12646 return obj_list;
12647}
12648
12649static ibf_offset_t
12650ibf_dump_pos(struct ibf_dump *dump)
12651{
12652 long pos = RSTRING_LEN(dump->current_buffer->str);
12653#if SIZEOF_LONG > SIZEOF_INT
12654 if (pos >= UINT_MAX) {
12655 rb_raise(rb_eRuntimeError, "dump size exceeds");
12656 }
12657#endif
12658 return (unsigned int)pos;
12659}
12660
12661static void
12662ibf_dump_align(struct ibf_dump *dump, size_t align)
12663{
12664 ibf_offset_t pos = ibf_dump_pos(dump);
12665 if (pos % align) {
12666 static const char padding[sizeof(VALUE)];
12667 size_t size = align - ((size_t)pos % align);
12668#if SIZEOF_LONG > SIZEOF_INT
12669 if (pos + size >= UINT_MAX) {
12670 rb_raise(rb_eRuntimeError, "dump size exceeds");
12671 }
12672#endif
12673 for (; size > sizeof(padding); size -= sizeof(padding)) {
12674 rb_str_cat(dump->current_buffer->str, padding, sizeof(padding));
12675 }
12676 rb_str_cat(dump->current_buffer->str, padding, size);
12677 }
12678}
12679
12680static ibf_offset_t
12681ibf_dump_write(struct ibf_dump *dump, const void *buff, unsigned long size)
12682{
12683 ibf_offset_t pos = ibf_dump_pos(dump);
12684#if SIZEOF_LONG > SIZEOF_INT
12685 /* ensure the resulting dump does not exceed UINT_MAX */
12686 if (size >= UINT_MAX || pos + size >= UINT_MAX) {
12687 rb_raise(rb_eRuntimeError, "dump size exceeds");
12688 }
12689#endif
12690 rb_str_cat(dump->current_buffer->str, (const char *)buff, size);
12691 return pos;
12692}
12693
12694static ibf_offset_t
12695ibf_dump_write_byte(struct ibf_dump *dump, unsigned char byte)
12696{
12697 return ibf_dump_write(dump, &byte, sizeof(unsigned char));
12698}
12699
12700static void
12701ibf_dump_overwrite(struct ibf_dump *dump, void *buff, unsigned int size, long offset)
12702{
12703 VALUE str = dump->current_buffer->str;
12704 char *ptr = RSTRING_PTR(str);
12705 if ((unsigned long)(size + offset) > (unsigned long)RSTRING_LEN(str))
12706 rb_bug("ibf_dump_overwrite: overflow");
12707 memcpy(ptr + offset, buff, size);
12708}
12709
12710static const void *
12711ibf_load_ptr(const struct ibf_load *load, ibf_offset_t *offset, int size)
12712{
12713 ibf_offset_t beg = *offset;
12714 *offset += size;
12715 return load->current_buffer->buff + beg;
12716}
12717
12718static void *
12719ibf_load_alloc(const struct ibf_load *load, ibf_offset_t offset, size_t x, size_t y)
12720{
12721 void *buff = ruby_xmalloc2(x, y);
12722 size_t size = x * y;
12723 memcpy(buff, load->current_buffer->buff + offset, size);
12724 return buff;
12725}
12726
12727#define IBF_W_ALIGN(type) (RUBY_ALIGNOF(type) > 1 ? ibf_dump_align(dump, RUBY_ALIGNOF(type)) : (void)0)
12728
12729#define IBF_W(b, type, n) (IBF_W_ALIGN(type), (type *)(VALUE)IBF_WP(b, type, n))
12730#define IBF_WV(variable) ibf_dump_write(dump, &(variable), sizeof(variable))
12731#define IBF_WP(b, type, n) ibf_dump_write(dump, (b), sizeof(type) * (n))
12732#define IBF_R(val, type, n) (type *)ibf_load_alloc(load, IBF_OFFSET(val), sizeof(type), (n))
12733#define IBF_ZERO(variable) memset(&(variable), 0, sizeof(variable))
12734
12735static int
12736ibf_table_lookup(struct st_table *table, st_data_t key)
12737{
12738 st_data_t val;
12739
12740 if (st_lookup(table, key, &val)) {
12741 return (int)val;
12742 }
12743 else {
12744 return -1;
12745 }
12746}
12747
12748static int
12749ibf_table_find_or_insert(struct st_table *table, st_data_t key)
12750{
12751 int index = ibf_table_lookup(table, key);
12752
12753 if (index < 0) { /* not found */
12754 index = (int)table->num_entries;
12755 st_insert(table, key, (st_data_t)index);
12756 }
12757
12758 return index;
12759}
12760
12761/* dump/load generic */
12762
12763static void ibf_dump_object_list(struct ibf_dump *dump, ibf_offset_t *obj_list_offset, unsigned int *obj_list_size);
12764
12765static VALUE ibf_load_object(const struct ibf_load *load, VALUE object_index);
12766static rb_iseq_t *ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq);
12767
12768static st_table *
12769ibf_dump_object_table_new(void)
12770{
12771 st_table *obj_table = st_init_numtable(); /* need free */
12772 st_insert(obj_table, (st_data_t)Qnil, (st_data_t)0); /* 0th is nil */
12773
12774 return obj_table;
12775}
12776
12777static VALUE
12778ibf_dump_object(struct ibf_dump *dump, VALUE obj)
12779{
12780 return ibf_table_find_or_insert(dump->current_buffer->obj_table, (st_data_t)obj);
12781}
12782
12783static VALUE
12784ibf_dump_id(struct ibf_dump *dump, ID id)
12785{
12786 if (id == 0 || rb_id2name(id) == NULL) {
12787 return 0;
12788 }
12789 return ibf_dump_object(dump, rb_id2sym(id));
12790}
12791
12792static ID
12793ibf_load_id(const struct ibf_load *load, const ID id_index)
12794{
12795 if (id_index == 0) {
12796 return 0;
12797 }
12798 VALUE sym = ibf_load_object(load, id_index);
12799 if (rb_integer_type_p(sym)) {
12800 /* Load hidden local variables as indexes */
12801 return NUM2ULONG(sym);
12802 }
12803 return rb_sym2id(sym);
12804}
12805
12806/* dump/load: code */
12807
12808static ibf_offset_t ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq);
12809
12810static int
12811ibf_dump_iseq(struct ibf_dump *dump, const rb_iseq_t *iseq)
12812{
12813 if (iseq == NULL) {
12814 return -1;
12815 }
12816 else {
12817 return ibf_table_find_or_insert(dump->iseq_table, (st_data_t)iseq);
12818 }
12819}
12820
12821static unsigned char
12822ibf_load_byte(const struct ibf_load *load, ibf_offset_t *offset)
12823{
12824 if (*offset >= load->current_buffer->size) { rb_raise(rb_eRuntimeError, "invalid bytecode"); }
12825 return (unsigned char)load->current_buffer->buff[(*offset)++];
12826}
12827
12828/*
12829 * Small uint serialization
12830 * 0x00000000_00000000 - 0x00000000_0000007f: 1byte | XXXX XXX1 |
12831 * 0x00000000_00000080 - 0x00000000_00003fff: 2byte | XXXX XX10 | XXXX XXXX |
12832 * 0x00000000_00004000 - 0x00000000_001fffff: 3byte | XXXX X100 | XXXX XXXX | XXXX XXXX |
12833 * 0x00000000_00020000 - 0x00000000_0fffffff: 4byte | XXXX 1000 | XXXX XXXX | XXXX XXXX | XXXX XXXX |
12834 * ...
12835 * 0x00010000_00000000 - 0x00ffffff_ffffffff: 8byte | 1000 0000 | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX |
12836 * 0x01000000_00000000 - 0xffffffff_ffffffff: 9byte | 0000 0000 | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX |
12837 */
12838static void
12839ibf_dump_write_small_value(struct ibf_dump *dump, VALUE x)
12840{
12841 if (sizeof(VALUE) > 8 || CHAR_BIT != 8) {
12842 ibf_dump_write(dump, &x, sizeof(VALUE));
12843 return;
12844 }
12845
12846 enum { max_byte_length = sizeof(VALUE) + 1 };
12847
12848 unsigned char bytes[max_byte_length];
12849 ibf_offset_t n;
12850
12851 for (n = 0; n < sizeof(VALUE) && (x >> (7 - n)); n++, x >>= 8) {
12852 bytes[max_byte_length - 1 - n] = (unsigned char)x;
12853 }
12854
12855 x <<= 1;
12856 x |= 1;
12857 x <<= n;
12858 bytes[max_byte_length - 1 - n] = (unsigned char)x;
12859 n++;
12860
12861 ibf_dump_write(dump, bytes + max_byte_length - n, n);
12862}
12863
12864static VALUE
12865ibf_load_small_value(const struct ibf_load *load, ibf_offset_t *offset)
12866{
12867 if (sizeof(VALUE) > 8 || CHAR_BIT != 8) {
12868 union { char s[sizeof(VALUE)]; VALUE v; } x;
12869
12870 memcpy(x.s, load->current_buffer->buff + *offset, sizeof(VALUE));
12871 *offset += sizeof(VALUE);
12872
12873 return x.v;
12874 }
12875
12876 enum { max_byte_length = sizeof(VALUE) + 1 };
12877
12878 const unsigned char *buffer = (const unsigned char *)load->current_buffer->buff;
12879 const unsigned char c = buffer[*offset];
12880
12881 ibf_offset_t n =
12882 c & 1 ? 1 :
12883 c == 0 ? 9 : ntz_int32(c) + 1;
12884 VALUE x = (VALUE)c >> n;
12885
12886 if (*offset + n > load->current_buffer->size) {
12887 rb_raise(rb_eRuntimeError, "invalid byte sequence");
12888 }
12889
12890 ibf_offset_t i;
12891 for (i = 1; i < n; i++) {
12892 x <<= 8;
12893 x |= (VALUE)buffer[*offset + i];
12894 }
12895
12896 *offset += n;
12897 return x;
12898}
12899
12900static void
12901ibf_dump_builtin(struct ibf_dump *dump, const struct rb_builtin_function *bf)
12902{
12903 // short: index
12904 // short: name.length
12905 // bytes: name
12906 // // omit argc (only verify with name)
12907 ibf_dump_write_small_value(dump, (VALUE)bf->index);
12908
12909 size_t len = strlen(bf->name);
12910 ibf_dump_write_small_value(dump, (VALUE)len);
12911 ibf_dump_write(dump, bf->name, len);
12912}
12913
12914static const struct rb_builtin_function *
12915ibf_load_builtin(const struct ibf_load *load, ibf_offset_t *offset)
12916{
12917 int i = (int)ibf_load_small_value(load, offset);
12918 int len = (int)ibf_load_small_value(load, offset);
12919 const char *name = (char *)ibf_load_ptr(load, offset, len);
12920
12921 if (0) {
12922 fprintf(stderr, "%.*s!!\n", len, name);
12923 }
12924
12925 const struct rb_builtin_function *table = GET_VM()->builtin_function_table;
12926 if (table == NULL) rb_raise(rb_eArgError, "builtin function table is not provided");
12927 if (strncmp(table[i].name, name, len) != 0) {
12928 rb_raise(rb_eArgError, "builtin function index (%d) mismatch (expect %s but %s)", i, name, table[i].name);
12929 }
12930 // fprintf(stderr, "load-builtin: name:%s(%d)\n", table[i].name, table[i].argc);
12931
12932 return &table[i];
12933}
12934
12935static ibf_offset_t
12936ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
12937{
12938 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
12939 const int iseq_size = body->iseq_size;
12940 int code_index;
12941 const VALUE *orig_code = rb_iseq_original_iseq(iseq);
12942
12943 ibf_offset_t offset = ibf_dump_pos(dump);
12944
12945 for (code_index=0; code_index<iseq_size;) {
12946 const VALUE insn = orig_code[code_index++];
12947 const char *types = insn_op_types(insn);
12948 int op_index;
12949
12950 /* opcode */
12951 if (insn >= 0x100) { rb_raise(rb_eRuntimeError, "invalid instruction"); }
12952 ibf_dump_write_small_value(dump, insn);
12953
12954 /* operands */
12955 for (op_index=0; types[op_index]; op_index++, code_index++) {
12956 VALUE op = orig_code[code_index];
12957 VALUE wv;
12958
12959 switch (types[op_index]) {
12960 case TS_CDHASH:
12961 case TS_VALUE:
12962 wv = ibf_dump_object(dump, op);
12963 break;
12964 case TS_ISEQ:
12965 wv = (VALUE)ibf_dump_iseq(dump, (const rb_iseq_t *)op);
12966 break;
12967 case TS_IC:
12968 {
12969 IC ic = (IC)op;
12970 VALUE arr = idlist_to_array(ic->segments);
12971 wv = ibf_dump_object(dump, arr);
12972 }
12973 break;
12974 case TS_ISE:
12975 case TS_IVC:
12976 case TS_ICVARC:
12977 {
12979 wv = is - ISEQ_IS_ENTRY_START(body, types[op_index]);
12980 }
12981 break;
12982 case TS_CALLDATA:
12983 {
12984 goto skip_wv;
12985 }
12986 case TS_ID:
12987 wv = ibf_dump_id(dump, (ID)op);
12988 break;
12989 case TS_FUNCPTR:
12990 rb_raise(rb_eRuntimeError, "TS_FUNCPTR is not supported");
12991 goto skip_wv;
12992 case TS_BUILTIN:
12993 ibf_dump_builtin(dump, (const struct rb_builtin_function *)op);
12994 goto skip_wv;
12995 default:
12996 wv = op;
12997 break;
12998 }
12999 ibf_dump_write_small_value(dump, wv);
13000 skip_wv:;
13001 }
13002 RUBY_ASSERT(insn_len(insn) == op_index+1);
13003 }
13004
13005 return offset;
13006}
13007
13008static VALUE *
13009ibf_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)
13010{
13011 VALUE iseqv = (VALUE)iseq;
13012 unsigned int code_index;
13013 ibf_offset_t reading_pos = bytecode_offset;
13014 VALUE *code = ALLOC_N(VALUE, iseq_size);
13015
13016 struct rb_iseq_constant_body *load_body = ISEQ_BODY(iseq);
13017 struct rb_call_data *cd_entries = load_body->call_data;
13018 int ic_index = 0;
13019
13020 load_body->iseq_encoded = code;
13021 load_body->iseq_size = 0;
13022
13023 iseq_bits_t * mark_offset_bits;
13024
13025 iseq_bits_t tmp[1] = {0};
13026
13027 if (ISEQ_MBITS_BUFLEN(iseq_size) == 1) {
13028 mark_offset_bits = tmp;
13029 }
13030 else {
13031 mark_offset_bits = ZALLOC_N(iseq_bits_t, ISEQ_MBITS_BUFLEN(iseq_size));
13032 }
13033 bool needs_bitmap = false;
13034
13035 for (code_index=0; code_index<iseq_size;) {
13036 /* opcode */
13037 const VALUE insn = code[code_index] = ibf_load_small_value(load, &reading_pos);
13038 const char *types = insn_op_types(insn);
13039 int op_index;
13040
13041 code_index++;
13042
13043 /* operands */
13044 for (op_index=0; types[op_index]; op_index++, code_index++) {
13045 const char operand_type = types[op_index];
13046 switch (operand_type) {
13047 case TS_VALUE:
13048 {
13049 VALUE op = ibf_load_small_value(load, &reading_pos);
13050 VALUE v = ibf_load_object(load, op);
13051 code[code_index] = v;
13052 if (!SPECIAL_CONST_P(v)) {
13053 RB_OBJ_WRITTEN(iseqv, Qundef, v);
13054 ISEQ_MBITS_SET(mark_offset_bits, code_index);
13055 needs_bitmap = true;
13056 }
13057 break;
13058 }
13059 case TS_CDHASH:
13060 {
13061 VALUE op = ibf_load_small_value(load, &reading_pos);
13062 VALUE v = ibf_load_object(load, op);
13063 v = rb_hash_dup(v); // hash dumped as frozen
13064 RHASH_TBL_RAW(v)->type = &cdhash_type;
13065 rb_hash_rehash(v); // hash function changed
13066 RB_OBJ_SET_SHAREABLE(freeze_hide_obj(v));
13067
13068 // Overwrite the existing hash in the object list. This
13069 // is to keep the object alive during load time.
13070 // [Bug #17984] [ruby-core:104259]
13071 pinned_list_store(load->current_buffer->obj_list, (long)op, v);
13072
13073 code[code_index] = v;
13074 ISEQ_MBITS_SET(mark_offset_bits, code_index);
13075 RB_OBJ_WRITTEN(iseqv, Qundef, v);
13076 needs_bitmap = true;
13077 break;
13078 }
13079 case TS_ISEQ:
13080 {
13081 VALUE op = (VALUE)ibf_load_small_value(load, &reading_pos);
13082 VALUE v = (VALUE)ibf_load_iseq(load, (const rb_iseq_t *)op);
13083 code[code_index] = v;
13084 if (!SPECIAL_CONST_P(v)) {
13085 RB_OBJ_WRITTEN(iseqv, Qundef, v);
13086 ISEQ_MBITS_SET(mark_offset_bits, code_index);
13087 needs_bitmap = true;
13088 }
13089 break;
13090 }
13091 case TS_IC:
13092 {
13093 VALUE op = ibf_load_small_value(load, &reading_pos);
13094 VALUE arr = ibf_load_object(load, op);
13095
13096 IC ic = &ISEQ_IS_IC_ENTRY(load_body, ic_index++);
13097 ic->segments = array_to_idlist(arr);
13098
13099 code[code_index] = (VALUE)ic;
13100 }
13101 break;
13102 case TS_ISE:
13103 case TS_ICVARC:
13104 case TS_IVC:
13105 {
13106 unsigned int op = (unsigned int)ibf_load_small_value(load, &reading_pos);
13107
13108 ISE ic = ISEQ_IS_ENTRY_START(load_body, operand_type) + op;
13109 code[code_index] = (VALUE)ic;
13110
13111 if (operand_type == TS_IVC) {
13112 IVC cache = (IVC)ic;
13113
13114 if (insn == BIN(setinstancevariable)) {
13115 ID iv_name = (ID)code[code_index - 1];
13116 cache->iv_set_name = iv_name;
13117 }
13118 else {
13119 cache->iv_set_name = 0;
13120 }
13121
13122 vm_ic_attr_index_initialize(cache, INVALID_SHAPE_ID);
13123 }
13124
13125 }
13126 break;
13127 case TS_CALLDATA:
13128 {
13129 code[code_index] = (VALUE)cd_entries++;
13130 }
13131 break;
13132 case TS_ID:
13133 {
13134 VALUE op = ibf_load_small_value(load, &reading_pos);
13135 code[code_index] = ibf_load_id(load, (ID)(VALUE)op);
13136 }
13137 break;
13138 case TS_FUNCPTR:
13139 rb_raise(rb_eRuntimeError, "TS_FUNCPTR is not supported");
13140 break;
13141 case TS_BUILTIN:
13142 code[code_index] = (VALUE)ibf_load_builtin(load, &reading_pos);
13143 break;
13144 default:
13145 code[code_index] = ibf_load_small_value(load, &reading_pos);
13146 continue;
13147 }
13148 }
13149 if (insn_len(insn) != op_index+1) {
13150 rb_raise(rb_eRuntimeError, "operand size mismatch");
13151 }
13152 }
13153
13154 load_body->iseq_size = code_index;
13155
13156 if (ISEQ_MBITS_BUFLEN(load_body->iseq_size) == 1) {
13157 load_body->mark_bits.single = mark_offset_bits[0];
13158 }
13159 else {
13160 if (needs_bitmap) {
13161 load_body->mark_bits.list = mark_offset_bits;
13162 }
13163 else {
13164 load_body->mark_bits.list = 0;
13165 SIZED_FREE_N(mark_offset_bits, ISEQ_MBITS_BUFLEN(iseq_size));
13166 }
13167 }
13168
13169 RUBY_ASSERT(code_index == iseq_size);
13170 RUBY_ASSERT(reading_pos == bytecode_offset + bytecode_size);
13171 return code;
13172}
13173
13174static ibf_offset_t
13175ibf_dump_param_opt_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
13176{
13177 int opt_num = ISEQ_BODY(iseq)->param.opt_num;
13178
13179 if (opt_num > 0) {
13180 IBF_W_ALIGN(VALUE);
13181 return ibf_dump_write(dump, ISEQ_BODY(iseq)->param.opt_table, sizeof(VALUE) * (opt_num + 1));
13182 }
13183 else {
13184 return ibf_dump_pos(dump);
13185 }
13186}
13187
13188static VALUE *
13189ibf_load_param_opt_table(const struct ibf_load *load, ibf_offset_t opt_table_offset, int opt_num)
13190{
13191 if (opt_num > 0) {
13192 VALUE *table = ALLOC_N(VALUE, opt_num+1);
13193 MEMCPY(table, load->current_buffer->buff + opt_table_offset, VALUE, opt_num+1);
13194 return table;
13195 }
13196 else {
13197 return NULL;
13198 }
13199}
13200
13201static ibf_offset_t
13202ibf_dump_param_keyword(struct ibf_dump *dump, const rb_iseq_t *iseq)
13203{
13204 const struct rb_iseq_param_keyword *kw = ISEQ_BODY(iseq)->param.keyword;
13205
13206 if (kw) {
13207 struct rb_iseq_param_keyword dump_kw = *kw;
13208 int dv_num = kw->num - kw->required_num;
13209 ID *ids = kw->num > 0 ? ALLOCA_N(ID, kw->num) : NULL;
13210 VALUE *dvs = dv_num > 0 ? ALLOCA_N(VALUE, dv_num) : NULL;
13211 int i;
13212
13213 for (i=0; i<kw->num; i++) ids[i] = (ID)ibf_dump_id(dump, kw->table[i]);
13214 for (i=0; i<dv_num; i++) dvs[i] = (VALUE)ibf_dump_object(dump, kw->default_values[i]);
13215
13216 dump_kw.table = IBF_W(ids, ID, kw->num);
13217 dump_kw.default_values = IBF_W(dvs, VALUE, dv_num);
13218 IBF_W_ALIGN(struct rb_iseq_param_keyword);
13219 return ibf_dump_write(dump, &dump_kw, sizeof(struct rb_iseq_param_keyword) * 1);
13220 }
13221 else {
13222 return 0;
13223 }
13224}
13225
13226static const struct rb_iseq_param_keyword *
13227ibf_load_param_keyword(const struct ibf_load *load, ibf_offset_t param_keyword_offset)
13228{
13229 if (param_keyword_offset) {
13230 struct rb_iseq_param_keyword *kw = IBF_R(param_keyword_offset, struct rb_iseq_param_keyword, 1);
13231 int dv_num = kw->num - kw->required_num;
13232 VALUE *dvs = dv_num ? IBF_R(kw->default_values, VALUE, dv_num) : NULL;
13233
13234 int i;
13235 for (i=0; i<dv_num; i++) {
13236 dvs[i] = ibf_load_object(load, dvs[i]);
13237 }
13238
13239 // Will be set once the local table is loaded.
13240 kw->table = NULL;
13241
13242 kw->default_values = dvs;
13243 return kw;
13244 }
13245 else {
13246 return NULL;
13247 }
13248}
13249
13250static ibf_offset_t
13251ibf_dump_insns_info_body(struct ibf_dump *dump, const rb_iseq_t *iseq)
13252{
13253 ibf_offset_t offset = ibf_dump_pos(dump);
13254 const struct iseq_insn_info_entry *entries = ISEQ_BODY(iseq)->insns_info.body;
13255
13256 unsigned int i;
13257 for (i = 0; i < ISEQ_BODY(iseq)->insns_info.size; i++) {
13258 ibf_dump_write_small_value(dump, entries[i].line_no);
13259#ifdef USE_ISEQ_NODE_ID
13260 ibf_dump_write_small_value(dump, entries[i].node_id);
13261#endif
13262 ibf_dump_write_small_value(dump, entries[i].events);
13263 }
13264
13265 return offset;
13266}
13267
13268static struct iseq_insn_info_entry *
13269ibf_load_insns_info_body(const struct ibf_load *load, ibf_offset_t body_offset, unsigned int size)
13270{
13271 ibf_offset_t reading_pos = body_offset;
13272 struct iseq_insn_info_entry *entries = ALLOC_N(struct iseq_insn_info_entry, size);
13273
13274 unsigned int i;
13275 for (i = 0; i < size; i++) {
13276 entries[i].line_no = (int)ibf_load_small_value(load, &reading_pos);
13277#ifdef USE_ISEQ_NODE_ID
13278 entries[i].node_id = (int)ibf_load_small_value(load, &reading_pos);
13279#endif
13280 entries[i].events = (rb_event_flag_t)ibf_load_small_value(load, &reading_pos);
13281 }
13282
13283 return entries;
13284}
13285
13286static ibf_offset_t
13287ibf_dump_insns_info_positions(struct ibf_dump *dump, const unsigned int *positions, unsigned int size)
13288{
13289 ibf_offset_t offset = ibf_dump_pos(dump);
13290
13291 unsigned int last = 0;
13292 unsigned int i;
13293 for (i = 0; i < size; i++) {
13294 ibf_dump_write_small_value(dump, positions[i] - last);
13295 last = positions[i];
13296 }
13297
13298 return offset;
13299}
13300
13301static unsigned int *
13302ibf_load_insns_info_positions(const struct ibf_load *load, ibf_offset_t positions_offset, unsigned int size)
13303{
13304 ibf_offset_t reading_pos = positions_offset;
13305 unsigned int *positions = ALLOC_N(unsigned int, size);
13306
13307 unsigned int last = 0;
13308 unsigned int i;
13309 for (i = 0; i < size; i++) {
13310 positions[i] = last + (unsigned int)ibf_load_small_value(load, &reading_pos);
13311 last = positions[i];
13312 }
13313
13314 return positions;
13315}
13316
13317static ibf_offset_t
13318ibf_dump_local_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
13319{
13320 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
13321 const int size = body->local_table_size;
13322 ID *table = ALLOCA_N(ID, size);
13323 int i;
13324
13325 for (i=0; i<size; i++) {
13326 VALUE v = ibf_dump_id(dump, body->local_table[i]);
13327 if (v == 0) {
13328 /* Dump hidden local variables as indexes, so load_from_binary will work with them */
13329 v = ibf_dump_object(dump, ULONG2NUM(body->local_table[i]));
13330 }
13331 table[i] = v;
13332 }
13333
13334 IBF_W_ALIGN(ID);
13335 return ibf_dump_write(dump, table, sizeof(ID) * size);
13336}
13337
13338static const ID *
13339ibf_load_local_table(const struct ibf_load *load, ibf_offset_t local_table_offset, int size)
13340{
13341 if (size > 0) {
13342 ID *table = IBF_R(local_table_offset, ID, size);
13343 int i;
13344
13345 for (i=0; i<size; i++) {
13346 table[i] = ibf_load_id(load, table[i]);
13347 }
13348
13349 if (size == 1 && table[0] == idERROR_INFO) {
13350 ruby_xfree_sized(table, sizeof(ID) * size);
13351 return rb_iseq_shared_exc_local_tbl;
13352 }
13353 else {
13354 return table;
13355 }
13356 }
13357 else {
13358 return NULL;
13359 }
13360}
13361
13362static ibf_offset_t
13363ibf_dump_lvar_states(struct ibf_dump *dump, const rb_iseq_t *iseq)
13364{
13365 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
13366 const int size = body->local_table_size;
13367 IBF_W_ALIGN(enum lvar_state);
13368 return ibf_dump_write(dump, body->lvar_states, sizeof(enum lvar_state) * (body->lvar_states ? size : 0));
13369}
13370
13371static enum lvar_state *
13372ibf_load_lvar_states(const struct ibf_load *load, ibf_offset_t lvar_states_offset, int size, const ID *local_table)
13373{
13374 if (local_table == rb_iseq_shared_exc_local_tbl ||
13375 size <= 0) {
13376 return NULL;
13377 }
13378 else {
13379 enum lvar_state *states = IBF_R(lvar_states_offset, enum lvar_state, size);
13380 return states;
13381 }
13382}
13383
13384static ibf_offset_t
13385ibf_dump_catch_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
13386{
13387 const struct iseq_catch_table *table = ISEQ_BODY(iseq)->catch_table;
13388
13389 if (table) {
13390 int *iseq_indices = ALLOCA_N(int, table->size);
13391 unsigned int i;
13392
13393 for (i=0; i<table->size; i++) {
13394 iseq_indices[i] = ibf_dump_iseq(dump, table->entries[i].iseq);
13395 }
13396
13397 const ibf_offset_t offset = ibf_dump_pos(dump);
13398
13399 for (i=0; i<table->size; i++) {
13400 ibf_dump_write_small_value(dump, iseq_indices[i]);
13401 ibf_dump_write_small_value(dump, table->entries[i].type);
13402 ibf_dump_write_small_value(dump, table->entries[i].start);
13403 ibf_dump_write_small_value(dump, table->entries[i].end);
13404 ibf_dump_write_small_value(dump, table->entries[i].cont);
13405 ibf_dump_write_small_value(dump, table->entries[i].sp);
13406 }
13407 return offset;
13408 }
13409 else {
13410 return ibf_dump_pos(dump);
13411 }
13412}
13413
13414static void
13415ibf_load_catch_table(const struct ibf_load *load, ibf_offset_t catch_table_offset, unsigned int size, const rb_iseq_t *parent_iseq)
13416{
13417 if (size) {
13418 struct iseq_catch_table *table = ruby_xcalloc(1, iseq_catch_table_bytes(size));
13419 table->size = size;
13420 ISEQ_BODY(parent_iseq)->catch_table = table;
13421
13422 ibf_offset_t reading_pos = catch_table_offset;
13423
13424 unsigned int i;
13425 for (i=0; i<table->size; i++) {
13426 int iseq_index = (int)ibf_load_small_value(load, &reading_pos);
13427 table->entries[i].type = (enum rb_catch_type)ibf_load_small_value(load, &reading_pos);
13428 table->entries[i].start = (unsigned int)ibf_load_small_value(load, &reading_pos);
13429 table->entries[i].end = (unsigned int)ibf_load_small_value(load, &reading_pos);
13430 table->entries[i].cont = (unsigned int)ibf_load_small_value(load, &reading_pos);
13431 table->entries[i].sp = (unsigned int)ibf_load_small_value(load, &reading_pos);
13432
13433 rb_iseq_t *catch_iseq = (rb_iseq_t *)ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)iseq_index);
13434 RB_OBJ_WRITE(parent_iseq, UNALIGNED_MEMBER_PTR(&table->entries[i], iseq), catch_iseq);
13435 }
13436 }
13437 else {
13438 ISEQ_BODY(parent_iseq)->catch_table = NULL;
13439 }
13440}
13441
13442static ibf_offset_t
13443ibf_dump_ci_entries(struct ibf_dump *dump, const rb_iseq_t *iseq)
13444{
13445 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
13446 const unsigned int ci_size = body->ci_size;
13447 const struct rb_call_data *cds = body->call_data;
13448
13449 ibf_offset_t offset = ibf_dump_pos(dump);
13450
13451 unsigned int i;
13452
13453 for (i = 0; i < ci_size; i++) {
13454 const struct rb_callinfo *ci = cds[i].ci;
13455 if (ci != NULL) {
13456 ibf_dump_write_small_value(dump, ibf_dump_id(dump, vm_ci_mid(ci)));
13457 ibf_dump_write_small_value(dump, vm_ci_flag(ci));
13458 ibf_dump_write_small_value(dump, vm_ci_argc(ci));
13459
13460 const struct rb_callinfo_kwarg *kwarg = vm_ci_kwarg(ci);
13461 if (kwarg) {
13462 int len = kwarg->keyword_len;
13463 ibf_dump_write_small_value(dump, len);
13464 for (int j=0; j<len; j++) {
13465 VALUE keyword = ibf_dump_object(dump, kwarg->keywords[j]);
13466 ibf_dump_write_small_value(dump, keyword);
13467 }
13468 }
13469 else {
13470 ibf_dump_write_small_value(dump, 0);
13471 }
13472 }
13473 else {
13474 // TODO: truncate NULL ci from call_data.
13475 ibf_dump_write_small_value(dump, (VALUE)-1);
13476 }
13477 }
13478
13479 return offset;
13480}
13481
13483 ID id;
13484 VALUE name;
13485 VALUE val;
13486};
13487
13489 size_t num;
13490 struct outer_variable_pair pairs[1];
13491};
13492
13493static enum rb_id_table_iterator_result
13494store_outer_variable(ID id, VALUE val, void *dump)
13495{
13496 struct outer_variable_list *ovlist = dump;
13497 struct outer_variable_pair *pair = &ovlist->pairs[ovlist->num++];
13498 pair->id = id;
13499 pair->name = rb_id2str(id);
13500 pair->val = val;
13501 return ID_TABLE_CONTINUE;
13502}
13503
13504static int
13505outer_variable_cmp(const void *a, const void *b, void *arg)
13506{
13507 const struct outer_variable_pair *ap = (const struct outer_variable_pair *)a;
13508 const struct outer_variable_pair *bp = (const struct outer_variable_pair *)b;
13509
13510 if (!ap->name) {
13511 return -1;
13512 }
13513 else if (!bp->name) {
13514 return 1;
13515 }
13516
13517 return rb_str_cmp(ap->name, bp->name);
13518}
13519
13520static ibf_offset_t
13521ibf_dump_outer_variables(struct ibf_dump *dump, const rb_iseq_t *iseq)
13522{
13523 struct rb_id_table * ovs = ISEQ_BODY(iseq)->outer_variables;
13524
13525 ibf_offset_t offset = ibf_dump_pos(dump);
13526
13527 size_t size = ovs ? rb_id_table_size(ovs) : 0;
13528 ibf_dump_write_small_value(dump, (VALUE)size);
13529 if (size > 0) {
13530 VALUE buff;
13531 size_t buffsize =
13532 rb_size_mul_add_or_raise(sizeof(struct outer_variable_pair), size,
13533 offsetof(struct outer_variable_list, pairs),
13534 rb_eArgError);
13535 struct outer_variable_list *ovlist = RB_ALLOCV(buff, buffsize);
13536 ovlist->num = 0;
13537 rb_id_table_foreach(ovs, store_outer_variable, ovlist);
13538 ruby_qsort(ovlist->pairs, size, sizeof(struct outer_variable_pair), outer_variable_cmp, NULL);
13539 for (size_t i = 0; i < size; ++i) {
13540 ID id = ovlist->pairs[i].id;
13541 ID val = ovlist->pairs[i].val;
13542 ibf_dump_write_small_value(dump, ibf_dump_id(dump, id));
13543 ibf_dump_write_small_value(dump, val);
13544 }
13545 }
13546
13547 return offset;
13548}
13549
13550/* note that we dump out rb_call_info but load back rb_call_data */
13551static void
13552ibf_load_ci_entries(const struct ibf_load *load,
13553 ibf_offset_t ci_entries_offset,
13554 unsigned int ci_size,
13555 struct rb_call_data **cd_ptr)
13556{
13557 if (!ci_size) {
13558 *cd_ptr = NULL;
13559 return;
13560 }
13561
13562 ibf_offset_t reading_pos = ci_entries_offset;
13563
13564 unsigned int i;
13565
13566 struct rb_call_data *cds = ZALLOC_N(struct rb_call_data, ci_size);
13567 *cd_ptr = cds;
13568
13569 for (i = 0; i < ci_size; i++) {
13570 VALUE mid_index = ibf_load_small_value(load, &reading_pos);
13571 if (mid_index != (VALUE)-1) {
13572 ID mid = ibf_load_id(load, mid_index);
13573 unsigned int flag = (unsigned int)ibf_load_small_value(load, &reading_pos);
13574 unsigned int argc = (unsigned int)ibf_load_small_value(load, &reading_pos);
13575
13576 struct rb_callinfo_kwarg *kwarg = NULL;
13577 int kwlen = (int)ibf_load_small_value(load, &reading_pos);
13578 if (kwlen > 0) {
13579 kwarg = rb_xmalloc_mul_add(kwlen, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
13580 kwarg->references = 0;
13581 kwarg->keyword_len = kwlen;
13582 for (int j=0; j<kwlen; j++) {
13583 VALUE keyword = ibf_load_small_value(load, &reading_pos);
13584 kwarg->keywords[j] = ibf_load_object(load, keyword);
13585 }
13586 }
13587
13588 cds[i].ci = vm_ci_new(mid, flag, argc, kwarg);
13589 RB_OBJ_WRITTEN(load->iseq, Qundef, cds[i].ci);
13590 cds[i].cc = vm_cc_empty();
13591 }
13592 else {
13593 // NULL ci
13594 cds[i].ci = NULL;
13595 cds[i].cc = NULL;
13596 }
13597 }
13598}
13599
13600static struct rb_id_table *
13601ibf_load_outer_variables(const struct ibf_load * load, ibf_offset_t outer_variables_offset)
13602{
13603 ibf_offset_t reading_pos = outer_variables_offset;
13604
13605 struct rb_id_table *tbl = NULL;
13606
13607 size_t table_size = (size_t)ibf_load_small_value(load, &reading_pos);
13608
13609 if (table_size > 0) {
13610 tbl = rb_id_table_create(table_size);
13611 }
13612
13613 for (size_t i = 0; i < table_size; i++) {
13614 ID key = ibf_load_id(load, (ID)ibf_load_small_value(load, &reading_pos));
13615 VALUE value = ibf_load_small_value(load, &reading_pos);
13616 if (!key) key = rb_make_temporary_id(i);
13617 rb_id_table_insert(tbl, key, value);
13618 }
13619
13620 return tbl;
13621}
13622
13623static ibf_offset_t
13624ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
13625{
13626 RUBY_ASSERT(dump->current_buffer == &dump->global_buffer);
13627
13628 unsigned int *positions;
13629
13630 const struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
13631
13632 const VALUE location_pathobj_index = ibf_dump_object(dump, body->location.pathobj); /* TODO: freeze */
13633 const VALUE location_base_label_index = ibf_dump_object(dump, body->location.base_label);
13634 const VALUE location_label_index = ibf_dump_object(dump, body->location.label);
13635
13636#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13637 ibf_offset_t iseq_start = ibf_dump_pos(dump);
13638
13639 struct ibf_dump_buffer *saved_buffer = dump->current_buffer;
13640 struct ibf_dump_buffer buffer;
13641 buffer.str = rb_str_new(0, 0);
13642 buffer.obj_table = ibf_dump_object_table_new();
13643 dump->current_buffer = &buffer;
13644#endif
13645
13646 const ibf_offset_t bytecode_offset = ibf_dump_code(dump, iseq);
13647 const ibf_offset_t bytecode_size = ibf_dump_pos(dump) - bytecode_offset;
13648 const ibf_offset_t param_opt_table_offset = ibf_dump_param_opt_table(dump, iseq);
13649 const ibf_offset_t param_keyword_offset = ibf_dump_param_keyword(dump, iseq);
13650 const ibf_offset_t insns_info_body_offset = ibf_dump_insns_info_body(dump, iseq);
13651
13652 positions = rb_iseq_insns_info_decode_positions(ISEQ_BODY(iseq));
13653 const ibf_offset_t insns_info_positions_offset = ibf_dump_insns_info_positions(dump, positions, body->insns_info.size);
13654 SIZED_FREE_N(positions, ISEQ_BODY(iseq)->insns_info.size);
13655
13656 const ibf_offset_t local_table_offset = ibf_dump_local_table(dump, iseq);
13657 const ibf_offset_t lvar_states_offset = ibf_dump_lvar_states(dump, iseq);
13658 const unsigned int catch_table_size = body->catch_table ? body->catch_table->size : 0;
13659 const ibf_offset_t catch_table_offset = ibf_dump_catch_table(dump, iseq);
13660 const int parent_iseq_index = ibf_dump_iseq(dump, ISEQ_BODY(iseq)->parent_iseq);
13661 const int local_iseq_index = ibf_dump_iseq(dump, ISEQ_BODY(iseq)->local_iseq);
13662 const int mandatory_only_iseq_index = ibf_dump_iseq(dump, ISEQ_BODY(iseq)->mandatory_only_iseq);
13663 const ibf_offset_t ci_entries_offset = ibf_dump_ci_entries(dump, iseq);
13664 const ibf_offset_t outer_variables_offset = ibf_dump_outer_variables(dump, iseq);
13665
13666#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13667 ibf_offset_t local_obj_list_offset;
13668 unsigned int local_obj_list_size;
13669
13670 ibf_dump_object_list(dump, &local_obj_list_offset, &local_obj_list_size);
13671#endif
13672
13673 ibf_offset_t body_offset = ibf_dump_pos(dump);
13674
13675 /* dump the constant body */
13676 unsigned int param_flags =
13677 (body->param.flags.has_lead << 0) |
13678 (body->param.flags.has_opt << 1) |
13679 (body->param.flags.has_rest << 2) |
13680 (body->param.flags.has_post << 3) |
13681 (body->param.flags.has_kw << 4) |
13682 (body->param.flags.has_kwrest << 5) |
13683 (body->param.flags.has_block << 6) |
13684 (body->param.flags.ambiguous_param0 << 7) |
13685 (body->param.flags.accepts_no_kwarg << 8) |
13686 (body->param.flags.ruby2_keywords << 9) |
13687 (body->param.flags.anon_rest << 10) |
13688 (body->param.flags.anon_kwrest << 11) |
13689 (body->param.flags.use_block << 12) |
13690 (body->param.flags.forwardable << 13) |
13691 (body->param.flags.accepts_no_block << 14);
13692
13693#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13694# define IBF_BODY_OFFSET(x) (x)
13695#else
13696# define IBF_BODY_OFFSET(x) (body_offset - (x))
13697#endif
13698
13699 ibf_dump_write_small_value(dump, body->type);
13700 ibf_dump_write_small_value(dump, body->iseq_size);
13701 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(bytecode_offset));
13702 ibf_dump_write_small_value(dump, bytecode_size);
13703 ibf_dump_write_small_value(dump, param_flags);
13704 ibf_dump_write_small_value(dump, body->param.size);
13705 ibf_dump_write_small_value(dump, body->param.lead_num);
13706 ibf_dump_write_small_value(dump, body->param.opt_num);
13707 ibf_dump_write_small_value(dump, body->param.rest_start);
13708 ibf_dump_write_small_value(dump, body->param.post_start);
13709 ibf_dump_write_small_value(dump, body->param.post_num);
13710 ibf_dump_write_small_value(dump, body->param.block_start);
13711 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(param_opt_table_offset));
13712 ibf_dump_write_small_value(dump, param_keyword_offset);
13713 ibf_dump_write_small_value(dump, location_pathobj_index);
13714 ibf_dump_write_small_value(dump, location_base_label_index);
13715 ibf_dump_write_small_value(dump, location_label_index);
13716 ibf_dump_write_small_value(dump, body->location.first_lineno);
13717 ibf_dump_write_small_value(dump, body->location.node_id);
13718 ibf_dump_write_small_value(dump, body->location.code_location.beg_pos.lineno);
13719 ibf_dump_write_small_value(dump, body->location.code_location.beg_pos.column);
13720 ibf_dump_write_small_value(dump, body->location.code_location.end_pos.lineno);
13721 ibf_dump_write_small_value(dump, body->location.code_location.end_pos.column);
13722 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(insns_info_body_offset));
13723 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(insns_info_positions_offset));
13724 ibf_dump_write_small_value(dump, body->insns_info.size);
13725 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(local_table_offset));
13726 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(lvar_states_offset));
13727 ibf_dump_write_small_value(dump, catch_table_size);
13728 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(catch_table_offset));
13729 ibf_dump_write_small_value(dump, parent_iseq_index);
13730 ibf_dump_write_small_value(dump, local_iseq_index);
13731 ibf_dump_write_small_value(dump, mandatory_only_iseq_index);
13732 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(ci_entries_offset));
13733 ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(outer_variables_offset));
13734 ibf_dump_write_small_value(dump, body->variable.flip_count);
13735 ibf_dump_write_small_value(dump, body->local_table_size);
13736 ibf_dump_write_small_value(dump, body->ivc_size);
13737 ibf_dump_write_small_value(dump, body->icvarc_size);
13738 ibf_dump_write_small_value(dump, body->ise_size);
13739 ibf_dump_write_small_value(dump, body->ic_size);
13740 ibf_dump_write_small_value(dump, body->ci_size);
13741 ibf_dump_write_small_value(dump, body->stack_max);
13742 ibf_dump_write_small_value(dump, body->builtin_attrs);
13743 ibf_dump_write_small_value(dump, body->prism ? 1 : 0);
13744
13745#undef IBF_BODY_OFFSET
13746
13747#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13748 ibf_offset_t iseq_length_bytes = ibf_dump_pos(dump);
13749
13750 dump->current_buffer = saved_buffer;
13751 ibf_dump_write(dump, RSTRING_PTR(buffer.str), iseq_length_bytes);
13752
13753 ibf_offset_t offset = ibf_dump_pos(dump);
13754 ibf_dump_write_small_value(dump, iseq_start);
13755 ibf_dump_write_small_value(dump, iseq_length_bytes);
13756 ibf_dump_write_small_value(dump, body_offset);
13757
13758 ibf_dump_write_small_value(dump, local_obj_list_offset);
13759 ibf_dump_write_small_value(dump, local_obj_list_size);
13760
13761 st_free_table(buffer.obj_table); // TODO: this leaks in case of exception
13762
13763 return offset;
13764#else
13765 return body_offset;
13766#endif
13767}
13768
13769static VALUE
13770ibf_load_location_str(const struct ibf_load *load, VALUE str_index)
13771{
13772 VALUE str = ibf_load_object(load, str_index);
13773 if (str != Qnil) {
13774 str = rb_fstring(str);
13775 }
13776 return str;
13777}
13778
13779static void
13780ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
13781{
13782 struct rb_iseq_constant_body *load_body = ISEQ_BODY(iseq) = rb_iseq_constant_body_alloc();
13783
13784 ibf_offset_t reading_pos = offset;
13785
13786#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13787 struct ibf_load_buffer *saved_buffer = load->current_buffer;
13788 load->current_buffer = &load->global_buffer;
13789
13790 const ibf_offset_t iseq_start = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13791 const ibf_offset_t iseq_length_bytes = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13792 const ibf_offset_t body_offset = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13793
13794 struct ibf_load_buffer buffer;
13795 buffer.buff = load->global_buffer.buff + iseq_start;
13796 buffer.size = iseq_length_bytes;
13797 buffer.obj_list_offset = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13798 buffer.obj_list_size = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13799 buffer.obj_list = pinned_list_new(buffer.obj_list_size);
13800
13801 load->current_buffer = &buffer;
13802 reading_pos = body_offset;
13803#endif
13804
13805#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13806# define IBF_BODY_OFFSET(x) (x)
13807#else
13808# define IBF_BODY_OFFSET(x) (offset - (x))
13809#endif
13810
13811 const unsigned int type = (unsigned int)ibf_load_small_value(load, &reading_pos);
13812 const unsigned int iseq_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13813 const ibf_offset_t bytecode_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13814 const ibf_offset_t bytecode_size = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13815 const unsigned int param_flags = (unsigned int)ibf_load_small_value(load, &reading_pos);
13816 const unsigned int param_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13817 const int param_lead_num = (int)ibf_load_small_value(load, &reading_pos);
13818 const int param_opt_num = (int)ibf_load_small_value(load, &reading_pos);
13819 const int param_rest_start = (int)ibf_load_small_value(load, &reading_pos);
13820 const int param_post_start = (int)ibf_load_small_value(load, &reading_pos);
13821 const int param_post_num = (int)ibf_load_small_value(load, &reading_pos);
13822 const int param_block_start = (int)ibf_load_small_value(load, &reading_pos);
13823 const ibf_offset_t param_opt_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13824 const ibf_offset_t param_keyword_offset = (ibf_offset_t)ibf_load_small_value(load, &reading_pos);
13825 const VALUE location_pathobj_index = ibf_load_small_value(load, &reading_pos);
13826 const VALUE location_base_label_index = ibf_load_small_value(load, &reading_pos);
13827 const VALUE location_label_index = ibf_load_small_value(load, &reading_pos);
13828 const int location_first_lineno = (int)ibf_load_small_value(load, &reading_pos);
13829 const int location_node_id = (int)ibf_load_small_value(load, &reading_pos);
13830 const int location_code_location_beg_pos_lineno = (int)ibf_load_small_value(load, &reading_pos);
13831 const int location_code_location_beg_pos_column = (int)ibf_load_small_value(load, &reading_pos);
13832 const int location_code_location_end_pos_lineno = (int)ibf_load_small_value(load, &reading_pos);
13833 const int location_code_location_end_pos_column = (int)ibf_load_small_value(load, &reading_pos);
13834 const ibf_offset_t insns_info_body_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13835 const ibf_offset_t insns_info_positions_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13836 const unsigned int insns_info_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13837 const ibf_offset_t local_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13838 const ibf_offset_t lvar_states_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13839 const unsigned int catch_table_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13840 const ibf_offset_t catch_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13841 const int parent_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
13842 const int local_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
13843 const int mandatory_only_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
13844 const ibf_offset_t ci_entries_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13845 const ibf_offset_t outer_variables_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
13846 const rb_snum_t variable_flip_count = (rb_snum_t)ibf_load_small_value(load, &reading_pos);
13847 const unsigned int local_table_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13848
13849 const unsigned int ivc_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13850 const unsigned int icvarc_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13851 const unsigned int ise_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13852 const unsigned int ic_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13853
13854 const unsigned int ci_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
13855 const unsigned int stack_max = (unsigned int)ibf_load_small_value(load, &reading_pos);
13856 const unsigned int builtin_attrs = (unsigned int)ibf_load_small_value(load, &reading_pos);
13857 const bool prism = (bool)ibf_load_small_value(load, &reading_pos);
13858
13859 // setup fname and dummy frame
13860 VALUE path = ibf_load_object(load, location_pathobj_index);
13861 {
13862 VALUE realpath = Qnil;
13863
13864 if (RB_TYPE_P(path, T_STRING)) {
13865 realpath = path = rb_fstring(path);
13866 }
13867 else if (RB_TYPE_P(path, T_ARRAY)) {
13868 VALUE pathobj = path;
13869 if (RARRAY_LEN(pathobj) != 2) {
13870 rb_raise(rb_eRuntimeError, "path object size mismatch");
13871 }
13872 path = rb_fstring(RARRAY_AREF(pathobj, 0));
13873 realpath = RARRAY_AREF(pathobj, 1);
13874 if (!NIL_P(realpath)) {
13875 if (!RB_TYPE_P(realpath, T_STRING)) {
13876 rb_raise(rb_eArgError, "unexpected realpath %"PRIxVALUE
13877 "(%x), path=%+"PRIsVALUE,
13878 realpath, TYPE(realpath), path);
13879 }
13880 realpath = rb_fstring(realpath);
13881 }
13882 }
13883 else {
13884 rb_raise(rb_eRuntimeError, "unexpected path object");
13885 }
13886 rb_iseq_pathobj_set(iseq, path, realpath);
13887 }
13888
13889 // push dummy frame
13890 rb_execution_context_t *ec = GET_EC();
13891 VALUE dummy_frame = rb_vm_push_frame_fname(ec, path);
13892
13893#undef IBF_BODY_OFFSET
13894
13895 load_body->type = type;
13896 load_body->stack_max = stack_max;
13897 load_body->param.flags.has_lead = (param_flags >> 0) & 1;
13898 load_body->param.flags.has_opt = (param_flags >> 1) & 1;
13899 load_body->param.flags.has_rest = (param_flags >> 2) & 1;
13900 load_body->param.flags.has_post = (param_flags >> 3) & 1;
13901 load_body->param.flags.has_kw = FALSE;
13902 load_body->param.flags.has_kwrest = (param_flags >> 5) & 1;
13903 load_body->param.flags.has_block = (param_flags >> 6) & 1;
13904 load_body->param.flags.ambiguous_param0 = (param_flags >> 7) & 1;
13905 load_body->param.flags.accepts_no_kwarg = (param_flags >> 8) & 1;
13906 load_body->param.flags.ruby2_keywords = (param_flags >> 9) & 1;
13907 load_body->param.flags.anon_rest = (param_flags >> 10) & 1;
13908 load_body->param.flags.anon_kwrest = (param_flags >> 11) & 1;
13909 load_body->param.flags.use_block = (param_flags >> 12) & 1;
13910 load_body->param.flags.forwardable = (param_flags >> 13) & 1;
13911 load_body->param.flags.accepts_no_block = (param_flags >> 14) & 1;
13912 load_body->param.size = param_size;
13913 load_body->param.lead_num = param_lead_num;
13914 load_body->param.opt_num = param_opt_num;
13915 load_body->param.rest_start = param_rest_start;
13916 load_body->param.post_start = param_post_start;
13917 load_body->param.post_num = param_post_num;
13918 load_body->param.block_start = param_block_start;
13919 load_body->local_table_size = local_table_size;
13920 load_body->ci_size = ci_size;
13921 load_body->insns_info.size = insns_info_size;
13922
13923 ISEQ_COVERAGE_SET(iseq, Qnil);
13924 ISEQ_ORIGINAL_ISEQ_CLEAR(iseq);
13925 load_body->variable.flip_count = variable_flip_count;
13926 load_body->variable.script_lines = Qnil;
13927
13928 load_body->location.first_lineno = location_first_lineno;
13929 load_body->location.node_id = location_node_id;
13930 load_body->location.code_location.beg_pos.lineno = location_code_location_beg_pos_lineno;
13931 load_body->location.code_location.beg_pos.column = location_code_location_beg_pos_column;
13932 load_body->location.code_location.end_pos.lineno = location_code_location_end_pos_lineno;
13933 load_body->location.code_location.end_pos.column = location_code_location_end_pos_column;
13934 load_body->builtin_attrs = builtin_attrs;
13935 load_body->prism = prism;
13936
13937 load_body->ivc_size = ivc_size;
13938 load_body->icvarc_size = icvarc_size;
13939 load_body->ise_size = ise_size;
13940 load_body->ic_size = ic_size;
13941
13942 if (ISEQ_IS_SIZE(load_body)) {
13943 load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(load_body));
13944 }
13945 else {
13946 load_body->is_entries = NULL;
13947 }
13948 ibf_load_ci_entries(load, ci_entries_offset, ci_size, &load_body->call_data);
13949 load_body->outer_variables = ibf_load_outer_variables(load, outer_variables_offset);
13950 load_body->param.opt_table = ibf_load_param_opt_table(load, param_opt_table_offset, param_opt_num);
13951 load_body->param.keyword = ibf_load_param_keyword(load, param_keyword_offset);
13952 load_body->param.flags.has_kw = (param_flags >> 4) & 1;
13953 load_body->insns_info.body = ibf_load_insns_info_body(load, insns_info_body_offset, insns_info_size);
13954 load_body->insns_info.positions = ibf_load_insns_info_positions(load, insns_info_positions_offset, insns_info_size);
13955 load_body->local_table = ibf_load_local_table(load, local_table_offset, local_table_size);
13956 load_body->lvar_states = ibf_load_lvar_states(load, lvar_states_offset, local_table_size, load_body->local_table);
13957 ibf_load_catch_table(load, catch_table_offset, catch_table_size, iseq);
13958
13959 const rb_iseq_t *parent_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)parent_iseq_index);
13960 const rb_iseq_t *local_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)local_iseq_index);
13961 const rb_iseq_t *mandatory_only_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)mandatory_only_iseq_index);
13962
13963 RB_OBJ_WRITE(iseq, &load_body->parent_iseq, parent_iseq);
13964 RB_OBJ_WRITE(iseq, &load_body->local_iseq, local_iseq);
13965 RB_OBJ_WRITE(iseq, &load_body->mandatory_only_iseq, mandatory_only_iseq);
13966
13967 // This must be done after the local table is loaded.
13968 if (load_body->param.keyword != NULL) {
13969 RUBY_ASSERT(load_body->local_table);
13970 struct rb_iseq_param_keyword *keyword = (struct rb_iseq_param_keyword *) load_body->param.keyword;
13971 keyword->table = &load_body->local_table[keyword->bits_start - keyword->num];
13972 }
13973
13974 ibf_load_code(load, iseq, bytecode_offset, bytecode_size, iseq_size);
13975#if VM_INSN_INFO_TABLE_IMPL == 2
13976 rb_iseq_insns_info_encode_positions(iseq);
13977#endif
13978
13979 rb_iseq_translate_threaded_code(iseq);
13980
13981#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13982 load->current_buffer = &load->global_buffer;
13983#endif
13984
13985 RB_OBJ_WRITE(iseq, &load_body->location.base_label, ibf_load_location_str(load, location_base_label_index));
13986 RB_OBJ_WRITE(iseq, &load_body->location.label, ibf_load_location_str(load, location_label_index));
13987
13988#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
13989 load->current_buffer = saved_buffer;
13990#endif
13991 verify_call_cache(iseq);
13992
13993 RB_GC_GUARD(dummy_frame);
13994 rb_vm_pop_frame_no_int(ec);
13995}
13996
13998{
13999 struct ibf_dump *dump;
14000 VALUE offset_list;
14001};
14002
14003static int
14004ibf_dump_iseq_list_i(st_data_t key, st_data_t val, st_data_t ptr)
14005{
14006 const rb_iseq_t *iseq = (const rb_iseq_t *)key;
14007 struct ibf_dump_iseq_list_arg *args = (struct ibf_dump_iseq_list_arg *)ptr;
14008
14009 ibf_offset_t offset = ibf_dump_iseq_each(args->dump, iseq);
14010 rb_ary_push(args->offset_list, UINT2NUM(offset));
14011
14012 return ST_CONTINUE;
14013}
14014
14015static void
14016ibf_dump_iseq_list(struct ibf_dump *dump, struct ibf_header *header)
14017{
14018 VALUE offset_list = rb_ary_hidden_new(dump->iseq_table->num_entries);
14019
14020 struct ibf_dump_iseq_list_arg args;
14021 args.dump = dump;
14022 args.offset_list = offset_list;
14023
14024 st_foreach(dump->iseq_table, ibf_dump_iseq_list_i, (st_data_t)&args);
14025
14026 st_index_t i;
14027 st_index_t size = dump->iseq_table->num_entries;
14028 ibf_offset_t *offsets = ALLOCA_N(ibf_offset_t, size);
14029
14030 for (i = 0; i < size; i++) {
14031 offsets[i] = NUM2UINT(RARRAY_AREF(offset_list, i));
14032 }
14033
14034 ibf_dump_align(dump, sizeof(ibf_offset_t));
14035 header->iseq_list_offset = ibf_dump_write(dump, offsets, sizeof(ibf_offset_t) * size);
14036 header->iseq_list_size = (unsigned int)size;
14037}
14038
14039/*
14040 * Binary format
14041 * - ibf_object_header
14042 * - ibf_object_xxx (xxx is type)
14043 */
14044
14046 unsigned int type: 5;
14047 unsigned int special_const: 1;
14048 unsigned int frozen: 1;
14049 unsigned int internal: 1;
14050};
14051
14052enum ibf_object_class_index {
14053 IBF_OBJECT_CLASS_OBJECT,
14054 IBF_OBJECT_CLASS_ARRAY,
14055 IBF_OBJECT_CLASS_STANDARD_ERROR,
14056 IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_ERROR,
14057 IBF_OBJECT_CLASS_TYPE_ERROR,
14058 IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_KEY_ERROR,
14059};
14060
14062 long srcstr;
14063 char option;
14064};
14065
14067 long len;
14068 long keyval[FLEX_ARY_LEN];
14069};
14070
14072 long class_index;
14073 long len;
14074 long beg;
14075 long end;
14076 int excl;
14077};
14078
14080 ssize_t slen;
14081 BDIGIT digits[FLEX_ARY_LEN];
14082};
14083
14084enum ibf_object_data_type {
14085 IBF_OBJECT_DATA_ENCODING,
14086};
14087
14089 long a, b;
14090};
14091
14093 long str;
14094};
14095
14096#define IBF_ALIGNED_OFFSET(align, offset) /* offset > 0 */ \
14097 ((((offset) - 1) / (align) + 1) * (align))
14098/* No cast, since it's UB to create an unaligned pointer.
14099 * Leave as void* for use with memcpy in those cases.
14100 * We align the offset, but the buffer pointer is only VALUE aligned,
14101 * so the returned pointer may be unaligned for `type` .*/
14102#define IBF_OBJBODY(type, offset) \
14103 ibf_load_check_offset(load, IBF_ALIGNED_OFFSET(RUBY_ALIGNOF(type), offset))
14104
14105static const void *
14106ibf_load_check_offset(const struct ibf_load *load, size_t offset)
14107{
14108 if (offset >= load->current_buffer->size) {
14109 rb_raise(rb_eIndexError, "object offset out of range: %"PRIdSIZE, offset);
14110 }
14111 return load->current_buffer->buff + offset;
14112}
14113
14114NORETURN(static void ibf_dump_object_unsupported(struct ibf_dump *dump, VALUE obj));
14115
14116static void
14117ibf_dump_object_unsupported(struct ibf_dump *dump, VALUE obj)
14118{
14119 char buff[0x100];
14120 rb_raw_obj_info(buff, sizeof(buff), obj);
14121 rb_raise(rb_eNotImpError, "ibf_dump_object_unsupported: %s", buff);
14122}
14123
14124NORETURN(static VALUE ibf_load_object_unsupported(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset));
14125
14126static VALUE
14127ibf_load_object_unsupported(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14128{
14129 rb_raise(rb_eArgError, "unsupported");
14131}
14132
14133static void
14134ibf_dump_object_class(struct ibf_dump *dump, VALUE obj)
14135{
14136 enum ibf_object_class_index cindex;
14137 if (obj == rb_cObject) {
14138 cindex = IBF_OBJECT_CLASS_OBJECT;
14139 }
14140 else if (obj == rb_cArray) {
14141 cindex = IBF_OBJECT_CLASS_ARRAY;
14142 }
14143 else if (obj == rb_eStandardError) {
14144 cindex = IBF_OBJECT_CLASS_STANDARD_ERROR;
14145 }
14146 else if (obj == rb_eNoMatchingPatternError) {
14147 cindex = IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_ERROR;
14148 }
14149 else if (obj == rb_eTypeError) {
14150 cindex = IBF_OBJECT_CLASS_TYPE_ERROR;
14151 }
14152 else if (obj == rb_eNoMatchingPatternKeyError) {
14153 cindex = IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_KEY_ERROR;
14154 }
14155 else {
14156 rb_obj_info_dump(obj);
14157 rb_p(obj);
14158 rb_bug("unsupported class");
14159 }
14160 ibf_dump_write_small_value(dump, (VALUE)cindex);
14161}
14162
14163static VALUE
14164ibf_load_object_class(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14165{
14166 enum ibf_object_class_index cindex = (enum ibf_object_class_index)ibf_load_small_value(load, &offset);
14167
14168 switch (cindex) {
14169 case IBF_OBJECT_CLASS_OBJECT:
14170 return rb_cObject;
14171 case IBF_OBJECT_CLASS_ARRAY:
14172 return rb_cArray;
14173 case IBF_OBJECT_CLASS_STANDARD_ERROR:
14174 return rb_eStandardError;
14175 case IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_ERROR:
14177 case IBF_OBJECT_CLASS_TYPE_ERROR:
14178 return rb_eTypeError;
14179 case IBF_OBJECT_CLASS_NO_MATCHING_PATTERN_KEY_ERROR:
14181 }
14182
14183 rb_raise(rb_eArgError, "ibf_load_object_class: unknown class (%d)", (int)cindex);
14184}
14185
14186
14187static void
14188ibf_dump_object_float(struct ibf_dump *dump, VALUE obj)
14189{
14190 double dbl = RFLOAT_VALUE(obj);
14191 (void)IBF_W(&dbl, double, 1);
14192}
14193
14194static VALUE
14195ibf_load_object_float(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14196{
14197 double d;
14198 /* Avoid unaligned VFP load on ARMv7; IBF payload may be unaligned (C99 6.3.2.3 p7). */
14199 memcpy(&d, IBF_OBJBODY(double, offset), sizeof(d));
14200 VALUE f = DBL2NUM(d);
14201 if (!FLONUM_P(f)) RB_OBJ_SET_SHAREABLE(f);
14202 return f;
14203}
14204
14205static void
14206ibf_dump_object_string(struct ibf_dump *dump, VALUE obj)
14207{
14208 long encindex = (long)rb_enc_get_index(obj);
14209 long len = RSTRING_LEN(obj);
14210 const char *ptr = RSTRING_PTR(obj);
14211
14212 if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
14213 rb_encoding *enc = rb_enc_from_index((int)encindex);
14214 const char *enc_name = rb_enc_name(enc);
14215 encindex = RUBY_ENCINDEX_BUILTIN_MAX + ibf_dump_object(dump, rb_str_new2(enc_name));
14216 }
14217
14218 ibf_dump_write_small_value(dump, encindex);
14219 ibf_dump_write_small_value(dump, len);
14220 IBF_WP(ptr, char, len);
14221}
14222
14223static VALUE
14224ibf_load_object_string(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14225{
14226 ibf_offset_t reading_pos = offset;
14227
14228 int encindex = (int)ibf_load_small_value(load, &reading_pos);
14229 const long len = (long)ibf_load_small_value(load, &reading_pos);
14230 const char *ptr = load->current_buffer->buff + reading_pos;
14231
14232 if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
14233 VALUE enc_name_str = ibf_load_object(load, encindex - RUBY_ENCINDEX_BUILTIN_MAX);
14234 encindex = rb_enc_find_index(RSTRING_PTR(enc_name_str));
14235 }
14236
14237 VALUE str;
14238 if (header->frozen && !header->internal) {
14239 str = rb_enc_literal_str(ptr, len, rb_enc_from_index(encindex));
14240 }
14241 else {
14242 str = rb_enc_str_new(ptr, len, rb_enc_from_index(encindex));
14243
14244 if (header->internal) rb_obj_hide(str);
14245 if (header->frozen) str = rb_fstring(str);
14246 }
14247 return str;
14248}
14249
14250static void
14251ibf_dump_object_regexp(struct ibf_dump *dump, VALUE obj)
14252{
14253 VALUE srcstr = RREGEXP_SRC(obj);
14254 struct ibf_object_regexp regexp;
14255 regexp.option = (char)rb_reg_options(obj);
14256 regexp.srcstr = (long)ibf_dump_object(dump, srcstr);
14257
14258 ibf_dump_write_byte(dump, (unsigned char)regexp.option);
14259 ibf_dump_write_small_value(dump, regexp.srcstr);
14260}
14261
14262static VALUE
14263ibf_load_object_regexp(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14264{
14265 struct ibf_object_regexp regexp;
14266 regexp.option = ibf_load_byte(load, &offset);
14267 regexp.srcstr = ibf_load_small_value(load, &offset);
14268
14269 VALUE srcstr = ibf_load_object(load, regexp.srcstr);
14270 VALUE reg = rb_reg_compile(srcstr, (int)regexp.option, NULL, 0);
14271
14272 if (header->internal) rb_obj_hide(reg);
14273 if (header->frozen) RB_OBJ_SET_SHAREABLE(rb_obj_freeze(reg));
14274
14275 return reg;
14276}
14277
14278static void
14279ibf_dump_object_array(struct ibf_dump *dump, VALUE obj)
14280{
14281 long i, len = RARRAY_LEN(obj);
14282 ibf_dump_write_small_value(dump, len);
14283 for (i=0; i<len; i++) {
14284 long index = (long)ibf_dump_object(dump, RARRAY_AREF(obj, i));
14285 ibf_dump_write_small_value(dump, index);
14286 }
14287}
14288
14289static VALUE
14290ibf_load_object_array(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14291{
14292 ibf_offset_t reading_pos = offset;
14293
14294 const long len = (long)ibf_load_small_value(load, &reading_pos);
14295
14296 VALUE ary = header->internal ? rb_ary_hidden_new(len) : rb_ary_new_capa(len);
14297 int i;
14298
14299 for (i=0; i<len; i++) {
14300 const VALUE index = ibf_load_small_value(load, &reading_pos);
14301 rb_ary_push(ary, ibf_load_object(load, index));
14302 }
14303
14304 if (header->frozen) {
14305 rb_ary_freeze(ary);
14306 rb_ractor_make_shareable(ary); // TODO: check elements
14307 }
14308
14309 return ary;
14310}
14311
14312static int
14313ibf_dump_object_hash_i(st_data_t key, st_data_t val, st_data_t ptr)
14314{
14315 struct ibf_dump *dump = (struct ibf_dump *)ptr;
14316
14317 VALUE key_index = ibf_dump_object(dump, (VALUE)key);
14318 VALUE val_index = ibf_dump_object(dump, (VALUE)val);
14319
14320 ibf_dump_write_small_value(dump, key_index);
14321 ibf_dump_write_small_value(dump, val_index);
14322 return ST_CONTINUE;
14323}
14324
14325static void
14326ibf_dump_object_hash(struct ibf_dump *dump, VALUE obj)
14327{
14328 long len = RHASH_SIZE(obj);
14329 ibf_dump_write_small_value(dump, (VALUE)len);
14330
14331 if (len > 0) rb_hash_foreach(obj, ibf_dump_object_hash_i, (VALUE)dump);
14332}
14333
14334static VALUE
14335ibf_load_object_hash(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14336{
14337 long len = (long)ibf_load_small_value(load, &offset);
14338 VALUE obj = rb_hash_new_with_size(len);
14339 int i;
14340
14341 for (i = 0; i < len; i++) {
14342 VALUE key_index = ibf_load_small_value(load, &offset);
14343 VALUE val_index = ibf_load_small_value(load, &offset);
14344
14345 VALUE key = ibf_load_object(load, key_index);
14346 VALUE val = ibf_load_object(load, val_index);
14347 rb_hash_aset(obj, key, val);
14348 }
14349 rb_hash_rehash(obj);
14350
14351 if (header->internal) rb_obj_hide(obj);
14352 if (header->frozen) {
14353 RB_OBJ_SET_FROZEN_SHAREABLE(obj);
14354 }
14355
14356 return obj;
14357}
14358
14359static void
14360ibf_dump_object_struct(struct ibf_dump *dump, VALUE obj)
14361{
14362 if (rb_obj_is_kind_of(obj, rb_cRange)) {
14363 struct ibf_object_struct_range range;
14364 VALUE beg, end;
14365 IBF_ZERO(range);
14366 range.len = 3;
14367 range.class_index = 0;
14368
14369 rb_range_values(obj, &beg, &end, &range.excl);
14370 range.beg = (long)ibf_dump_object(dump, beg);
14371 range.end = (long)ibf_dump_object(dump, end);
14372
14373 IBF_W_ALIGN(struct ibf_object_struct_range);
14374 IBF_WV(range);
14375 }
14376 else {
14377 rb_raise(rb_eNotImpError, "ibf_dump_object_struct: unsupported class %"PRIsVALUE,
14378 rb_class_name(CLASS_OF(obj)));
14379 }
14380}
14381
14382static VALUE
14383ibf_load_object_struct(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14384{
14385 const struct ibf_object_struct_range *range = IBF_OBJBODY(struct ibf_object_struct_range, offset);
14386 VALUE beg = ibf_load_object(load, range->beg);
14387 VALUE end = ibf_load_object(load, range->end);
14388 VALUE obj = rb_range_new(beg, end, range->excl);
14389 if (header->internal) rb_obj_hide(obj);
14390 if (header->frozen) RB_OBJ_SET_FROZEN_SHAREABLE(obj);
14391 return obj;
14392}
14393
14394static void
14395ibf_dump_object_bignum(struct ibf_dump *dump, VALUE obj)
14396{
14397 ssize_t len = BIGNUM_LEN(obj);
14398 ssize_t slen = BIGNUM_SIGN(obj) > 0 ? len : len * -1;
14399 BDIGIT *d = BIGNUM_DIGITS(obj);
14400
14401 (void)IBF_W(&slen, ssize_t, 1);
14402 IBF_WP(d, BDIGIT, len);
14403}
14404
14405static VALUE
14406ibf_load_object_bignum(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14407{
14408 const struct ibf_object_bignum *bignum = IBF_OBJBODY(struct ibf_object_bignum, offset);
14409 int sign = bignum->slen > 0;
14410 ssize_t len = sign > 0 ? bignum->slen : -1 * bignum->slen;
14411 const int big_unpack_flags = /* c.f. rb_big_unpack() */
14414 VALUE obj = rb_integer_unpack(bignum->digits, len, sizeof(BDIGIT), 0,
14415 big_unpack_flags |
14416 (sign == 0 ? INTEGER_PACK_NEGATIVE : 0));
14417 if (header->internal) rb_obj_hide(obj);
14418 if (header->frozen) RB_OBJ_SET_FROZEN_SHAREABLE(obj);
14419 return obj;
14420}
14421
14422static void
14423ibf_dump_object_data(struct ibf_dump *dump, VALUE obj)
14424{
14425 if (rb_data_is_encoding(obj)) {
14426 rb_encoding *enc = rb_to_encoding(obj);
14427 const char *name = rb_enc_name(enc);
14428 long len = strlen(name) + 1;
14429 long data[2];
14430 data[0] = IBF_OBJECT_DATA_ENCODING;
14431 data[1] = len;
14432 (void)IBF_W(data, long, 2);
14433 IBF_WP(name, char, len);
14434 }
14435 else {
14436 ibf_dump_object_unsupported(dump, obj);
14437 }
14438}
14439
14440static VALUE
14441ibf_load_object_data(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14442{
14443 const long *body = IBF_OBJBODY(long, offset);
14444 const enum ibf_object_data_type type = (enum ibf_object_data_type)body[0];
14445 /* const long len = body[1]; */
14446 const char *data = (const char *)&body[2];
14447
14448 switch (type) {
14449 case IBF_OBJECT_DATA_ENCODING:
14450 {
14451 VALUE encobj = rb_enc_from_encoding(rb_enc_find(data));
14452 return encobj;
14453 }
14454 }
14455
14456 return ibf_load_object_unsupported(load, header, offset);
14457}
14458
14459static void
14460ibf_dump_object_complex_rational(struct ibf_dump *dump, VALUE obj)
14461{
14462 long data[2];
14463 data[0] = (long)ibf_dump_object(dump, RCOMPLEX(obj)->real);
14464 data[1] = (long)ibf_dump_object(dump, RCOMPLEX(obj)->imag);
14465
14466 (void)IBF_W(data, long, 2);
14467}
14468
14469static VALUE
14470ibf_load_object_complex_rational(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14471{
14472 const struct ibf_object_complex_rational *nums = IBF_OBJBODY(struct ibf_object_complex_rational, offset);
14473 VALUE a = ibf_load_object(load, nums->a);
14474 VALUE b = ibf_load_object(load, nums->b);
14475 VALUE obj = header->type == T_COMPLEX ?
14476 rb_complex_new(a, b) : rb_rational_new(a, b);
14477
14478 if (header->internal) rb_obj_hide(obj);
14479 if (header->frozen) rb_ractor_make_shareable(rb_obj_freeze(obj));
14480 return obj;
14481}
14482
14483static void
14484ibf_dump_object_symbol(struct ibf_dump *dump, VALUE obj)
14485{
14486 ibf_dump_object_string(dump, rb_sym2str(obj));
14487}
14488
14489static VALUE
14490ibf_load_object_symbol(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14491{
14492 ibf_offset_t reading_pos = offset;
14493
14494 int encindex = (int)ibf_load_small_value(load, &reading_pos);
14495 const long len = (long)ibf_load_small_value(load, &reading_pos);
14496 const char *ptr = load->current_buffer->buff + reading_pos;
14497
14498 if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
14499 VALUE enc_name_str = ibf_load_object(load, encindex - RUBY_ENCINDEX_BUILTIN_MAX);
14500 encindex = rb_enc_find_index(RSTRING_PTR(enc_name_str));
14501 }
14502
14503 ID id = rb_intern3(ptr, len, rb_enc_from_index(encindex));
14504 return ID2SYM(id);
14505}
14506
14507typedef void (*ibf_dump_object_function)(struct ibf_dump *dump, VALUE obj);
14508static const ibf_dump_object_function dump_object_functions[RUBY_T_MASK+1] = {
14509 ibf_dump_object_unsupported, /* T_NONE */
14510 ibf_dump_object_unsupported, /* T_OBJECT */
14511 ibf_dump_object_class, /* T_CLASS */
14512 ibf_dump_object_unsupported, /* T_MODULE */
14513 ibf_dump_object_float, /* T_FLOAT */
14514 ibf_dump_object_string, /* T_STRING */
14515 ibf_dump_object_regexp, /* T_REGEXP */
14516 ibf_dump_object_array, /* T_ARRAY */
14517 ibf_dump_object_hash, /* T_HASH */
14518 ibf_dump_object_struct, /* T_STRUCT */
14519 ibf_dump_object_bignum, /* T_BIGNUM */
14520 ibf_dump_object_unsupported, /* T_FILE */
14521 ibf_dump_object_data, /* T_DATA */
14522 ibf_dump_object_unsupported, /* T_MATCH */
14523 ibf_dump_object_complex_rational, /* T_COMPLEX */
14524 ibf_dump_object_complex_rational, /* T_RATIONAL */
14525 ibf_dump_object_unsupported, /* 0x10 */
14526 ibf_dump_object_unsupported, /* 0x11 T_NIL */
14527 ibf_dump_object_unsupported, /* 0x12 T_TRUE */
14528 ibf_dump_object_unsupported, /* 0x13 T_FALSE */
14529 ibf_dump_object_symbol, /* 0x14 T_SYMBOL */
14530 ibf_dump_object_unsupported, /* T_FIXNUM */
14531 ibf_dump_object_unsupported, /* T_UNDEF */
14532 ibf_dump_object_unsupported, /* 0x17 */
14533 ibf_dump_object_unsupported, /* 0x18 */
14534 ibf_dump_object_unsupported, /* 0x19 */
14535 ibf_dump_object_unsupported, /* T_IMEMO 0x1a */
14536 ibf_dump_object_unsupported, /* T_NODE 0x1b */
14537 ibf_dump_object_unsupported, /* T_ICLASS 0x1c */
14538 ibf_dump_object_unsupported, /* T_ZOMBIE 0x1d */
14539 ibf_dump_object_unsupported, /* 0x1e */
14540 ibf_dump_object_unsupported, /* 0x1f */
14541};
14542
14543static void
14544ibf_dump_object_object_header(struct ibf_dump *dump, const struct ibf_object_header header)
14545{
14546 unsigned char byte =
14547 (header.type << 0) |
14548 (header.special_const << 5) |
14549 (header.frozen << 6) |
14550 (header.internal << 7);
14551
14552 IBF_WV(byte);
14553}
14554
14555static struct ibf_object_header
14556ibf_load_object_object_header(const struct ibf_load *load, ibf_offset_t *offset)
14557{
14558 unsigned char byte = ibf_load_byte(load, offset);
14559
14560 struct ibf_object_header header;
14561 header.type = (byte >> 0) & 0x1f;
14562 header.special_const = (byte >> 5) & 0x01;
14563 header.frozen = (byte >> 6) & 0x01;
14564 header.internal = (byte >> 7) & 0x01;
14565
14566 return header;
14567}
14568
14569static ibf_offset_t
14570ibf_dump_object_object(struct ibf_dump *dump, VALUE obj)
14571{
14572 struct ibf_object_header obj_header;
14573 ibf_offset_t current_offset;
14574 IBF_ZERO(obj_header);
14575 obj_header.type = TYPE(obj);
14576
14577 IBF_W_ALIGN(ibf_offset_t);
14578 current_offset = ibf_dump_pos(dump);
14579
14580 if (SPECIAL_CONST_P(obj) &&
14581 ! (SYMBOL_P(obj) ||
14582 RB_FLOAT_TYPE_P(obj))) {
14583 obj_header.special_const = TRUE;
14584 obj_header.frozen = TRUE;
14585 obj_header.internal = TRUE;
14586 ibf_dump_object_object_header(dump, obj_header);
14587 ibf_dump_write_small_value(dump, obj);
14588 }
14589 else {
14590 obj_header.internal = SPECIAL_CONST_P(obj) ? FALSE : (RBASIC_CLASS(obj) == 0) ? TRUE : FALSE;
14591 obj_header.special_const = FALSE;
14592 obj_header.frozen = OBJ_FROZEN(obj) ? TRUE : FALSE;
14593 ibf_dump_object_object_header(dump, obj_header);
14594 (*dump_object_functions[obj_header.type])(dump, obj);
14595 }
14596
14597 return current_offset;
14598}
14599
14600typedef VALUE (*ibf_load_object_function)(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset);
14601static const ibf_load_object_function load_object_functions[RUBY_T_MASK+1] = {
14602 ibf_load_object_unsupported, /* T_NONE */
14603 ibf_load_object_unsupported, /* T_OBJECT */
14604 ibf_load_object_class, /* T_CLASS */
14605 ibf_load_object_unsupported, /* T_MODULE */
14606 ibf_load_object_float, /* T_FLOAT */
14607 ibf_load_object_string, /* T_STRING */
14608 ibf_load_object_regexp, /* T_REGEXP */
14609 ibf_load_object_array, /* T_ARRAY */
14610 ibf_load_object_hash, /* T_HASH */
14611 ibf_load_object_struct, /* T_STRUCT */
14612 ibf_load_object_bignum, /* T_BIGNUM */
14613 ibf_load_object_unsupported, /* T_FILE */
14614 ibf_load_object_data, /* T_DATA */
14615 ibf_load_object_unsupported, /* T_MATCH */
14616 ibf_load_object_complex_rational, /* T_COMPLEX */
14617 ibf_load_object_complex_rational, /* T_RATIONAL */
14618 ibf_load_object_unsupported, /* 0x10 */
14619 ibf_load_object_unsupported, /* T_NIL */
14620 ibf_load_object_unsupported, /* T_TRUE */
14621 ibf_load_object_unsupported, /* T_FALSE */
14622 ibf_load_object_symbol,
14623 ibf_load_object_unsupported, /* T_FIXNUM */
14624 ibf_load_object_unsupported, /* T_UNDEF */
14625 ibf_load_object_unsupported, /* 0x17 */
14626 ibf_load_object_unsupported, /* 0x18 */
14627 ibf_load_object_unsupported, /* 0x19 */
14628 ibf_load_object_unsupported, /* T_IMEMO 0x1a */
14629 ibf_load_object_unsupported, /* T_NODE 0x1b */
14630 ibf_load_object_unsupported, /* T_ICLASS 0x1c */
14631 ibf_load_object_unsupported, /* T_ZOMBIE 0x1d */
14632 ibf_load_object_unsupported, /* 0x1e */
14633 ibf_load_object_unsupported, /* 0x1f */
14634};
14635
14636static VALUE
14637ibf_load_object(const struct ibf_load *load, VALUE object_index)
14638{
14639 if (object_index == 0) {
14640 return Qnil;
14641 }
14642 else {
14643 VALUE obj = pinned_list_fetch(load->current_buffer->obj_list, (long)object_index);
14644 if (!obj) {
14645 ibf_offset_t *offsets = (ibf_offset_t *)(load->current_buffer->obj_list_offset + load->current_buffer->buff);
14646 ibf_offset_t offset = offsets[object_index];
14647 const struct ibf_object_header header = ibf_load_object_object_header(load, &offset);
14648
14649#if IBF_ISEQ_DEBUG
14650 fprintf(stderr, "ibf_load_object: list=%#x offsets=%p offset=%#x\n",
14651 load->current_buffer->obj_list_offset, (void *)offsets, offset);
14652 fprintf(stderr, "ibf_load_object: type=%#x special=%d frozen=%d internal=%d\n",
14653 header.type, header.special_const, header.frozen, header.internal);
14654#endif
14655 if (offset >= load->current_buffer->size) {
14656 rb_raise(rb_eIndexError, "object offset out of range: %u", offset);
14657 }
14658
14659 if (header.special_const) {
14660 ibf_offset_t reading_pos = offset;
14661
14662 obj = ibf_load_small_value(load, &reading_pos);
14663 }
14664 else {
14665 obj = (*load_object_functions[header.type])(load, &header, offset);
14666 }
14667
14668 pinned_list_store(load->current_buffer->obj_list, (long)object_index, obj);
14669 }
14670#if IBF_ISEQ_DEBUG
14671 fprintf(stderr, "ibf_load_object: index=%#"PRIxVALUE" obj=%#"PRIxVALUE"\n",
14672 object_index, obj);
14673#endif
14674 return obj;
14675 }
14676}
14677
14679{
14680 struct ibf_dump *dump;
14681 VALUE offset_list;
14682};
14683
14684static int
14685ibf_dump_object_list_i(st_data_t key, st_data_t val, st_data_t ptr)
14686{
14687 VALUE obj = (VALUE)key;
14688 struct ibf_dump_object_list_arg *args = (struct ibf_dump_object_list_arg *)ptr;
14689
14690 ibf_offset_t offset = ibf_dump_object_object(args->dump, obj);
14691 rb_ary_push(args->offset_list, UINT2NUM(offset));
14692
14693 return ST_CONTINUE;
14694}
14695
14696static void
14697ibf_dump_object_list(struct ibf_dump *dump, ibf_offset_t *obj_list_offset, unsigned int *obj_list_size)
14698{
14699 st_table *obj_table = dump->current_buffer->obj_table;
14700 VALUE offset_list = rb_ary_hidden_new(obj_table->num_entries);
14701
14702 struct ibf_dump_object_list_arg args;
14703 args.dump = dump;
14704 args.offset_list = offset_list;
14705
14706 st_foreach(obj_table, ibf_dump_object_list_i, (st_data_t)&args);
14707
14708 IBF_W_ALIGN(ibf_offset_t);
14709 *obj_list_offset = ibf_dump_pos(dump);
14710
14711 st_index_t size = obj_table->num_entries;
14712 st_index_t i;
14713
14714 for (i=0; i<size; i++) {
14715 ibf_offset_t offset = NUM2UINT(RARRAY_AREF(offset_list, i));
14716 IBF_WV(offset);
14717 }
14718
14719 *obj_list_size = (unsigned int)size;
14720}
14721
14722static void
14723ibf_dump_mark(void *ptr)
14724{
14725 struct ibf_dump *dump = (struct ibf_dump *)ptr;
14726 rb_gc_mark(dump->global_buffer.str);
14727
14728 rb_mark_set(dump->global_buffer.obj_table);
14729 rb_mark_set(dump->iseq_table);
14730}
14731
14732static void
14733ibf_dump_free(void *ptr)
14734{
14735 struct ibf_dump *dump = (struct ibf_dump *)ptr;
14736 if (dump->global_buffer.obj_table) {
14737 st_free_table(dump->global_buffer.obj_table);
14738 dump->global_buffer.obj_table = 0;
14739 }
14740 if (dump->iseq_table) {
14741 st_free_table(dump->iseq_table);
14742 dump->iseq_table = 0;
14743 }
14744}
14745
14746static size_t
14747ibf_dump_memsize(const void *ptr)
14748{
14749 struct ibf_dump *dump = (struct ibf_dump *)ptr;
14750 size_t size = 0;
14751 if (dump->iseq_table) size += st_memsize(dump->iseq_table);
14752 if (dump->global_buffer.obj_table) size += st_memsize(dump->global_buffer.obj_table);
14753 return size;
14754}
14755
14756static const rb_data_type_t ibf_dump_type = {
14757 "ibf_dump",
14758 {ibf_dump_mark, ibf_dump_free, ibf_dump_memsize,},
14759 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE
14760};
14761
14762static void
14763ibf_dump_setup(struct ibf_dump *dump, VALUE dumper_obj)
14764{
14765 dump->global_buffer.obj_table = NULL; // GC may run before a value is assigned
14766 dump->iseq_table = NULL;
14767
14768 RB_OBJ_WRITE(dumper_obj, &dump->global_buffer.str, rb_str_new(0, 0));
14769 dump->global_buffer.obj_table = ibf_dump_object_table_new();
14770 dump->iseq_table = st_init_numtable(); /* need free */
14771
14772 dump->current_buffer = &dump->global_buffer;
14773}
14774
14775VALUE
14776rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt)
14777{
14778 struct ibf_dump *dump;
14779 struct ibf_header header = {{0}};
14780 VALUE dump_obj;
14781 VALUE str;
14782
14783 if (ISEQ_BODY(iseq)->parent_iseq != NULL ||
14784 ISEQ_BODY(iseq)->local_iseq != iseq) {
14785 rb_raise(rb_eRuntimeError, "should be top of iseq");
14786 }
14787 if (RTEST(ISEQ_COVERAGE(iseq))) {
14788 rb_raise(rb_eRuntimeError, "should not compile with coverage");
14789 }
14790
14791 dump_obj = TypedData_Make_Struct(0, struct ibf_dump, &ibf_dump_type, dump);
14792 ibf_dump_setup(dump, dump_obj);
14793
14794 ibf_dump_write(dump, &header, sizeof(header));
14795 ibf_dump_iseq(dump, iseq);
14796
14797 header.magic[0] = 'Y'; /* YARB */
14798 header.magic[1] = 'A';
14799 header.magic[2] = 'R';
14800 header.magic[3] = 'B';
14801 header.major_version = IBF_MAJOR_VERSION;
14802 header.minor_version = IBF_MINOR_VERSION;
14803 header.endian = IBF_ENDIAN_MARK;
14804 header.wordsize = (uint8_t)SIZEOF_VALUE;
14805 ibf_dump_iseq_list(dump, &header);
14806 ibf_dump_object_list(dump, &header.global_object_list_offset, &header.global_object_list_size);
14807 header.size = ibf_dump_pos(dump);
14808
14809 if (RTEST(opt)) {
14810 VALUE opt_str = opt;
14811 const char *ptr = StringValuePtr(opt_str);
14812 header.extra_size = RSTRING_LENINT(opt_str);
14813 ibf_dump_write(dump, ptr, header.extra_size);
14814 }
14815 else {
14816 header.extra_size = 0;
14817 }
14818
14819 ibf_dump_overwrite(dump, &header, sizeof(header), 0);
14820
14821 str = dump->global_buffer.str;
14822 RB_GC_GUARD(dump_obj);
14823 return str;
14824}
14825
14826static const ibf_offset_t *
14827ibf_iseq_list(const struct ibf_load *load)
14828{
14829 return (const ibf_offset_t *)(load->global_buffer.buff + load->header->iseq_list_offset);
14830}
14831
14832void
14833rb_ibf_load_iseq_complete(rb_iseq_t *iseq)
14834{
14835 struct ibf_load *load = RTYPEDDATA_DATA(iseq->aux.loader.obj);
14836 rb_iseq_t *prev_src_iseq = load->iseq;
14837 ibf_offset_t offset = ibf_iseq_list(load)[iseq->aux.loader.index];
14838 load->iseq = iseq;
14839#if IBF_ISEQ_DEBUG
14840 fprintf(stderr, "rb_ibf_load_iseq_complete: index=%#x offset=%#x size=%#x\n",
14841 iseq->aux.loader.index, offset,
14842 load->header->size);
14843#endif
14844 ibf_load_iseq_each(load, iseq, offset);
14845 ISEQ_COMPILE_DATA_CLEAR(iseq);
14846 FL_UNSET((VALUE)iseq, ISEQ_NOT_LOADED_YET);
14847 rb_iseq_init_trace(iseq);
14848 load->iseq = prev_src_iseq;
14849}
14850
14851#if USE_LAZY_LOAD
14852const rb_iseq_t *
14853rb_iseq_complete(const rb_iseq_t *iseq)
14854{
14855 rb_ibf_load_iseq_complete((rb_iseq_t *)iseq);
14856 return iseq;
14857}
14858#endif
14859
14860static rb_iseq_t *
14861ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq)
14862{
14863 int iseq_index = (int)(VALUE)index_iseq;
14864
14865#if IBF_ISEQ_DEBUG
14866 fprintf(stderr, "ibf_load_iseq: index_iseq=%p iseq_list=%p\n",
14867 (void *)index_iseq, (void *)load->iseq_list);
14868#endif
14869 if (iseq_index == -1) {
14870 return NULL;
14871 }
14872 else {
14873 VALUE iseqv = pinned_list_fetch(load->iseq_list, iseq_index);
14874
14875#if IBF_ISEQ_DEBUG
14876 fprintf(stderr, "ibf_load_iseq: iseqv=%p\n", (void *)iseqv);
14877#endif
14878 if (iseqv) {
14879 return (rb_iseq_t *)iseqv;
14880 }
14881 else {
14882 rb_iseq_t *iseq = iseq_imemo_alloc();
14883#if IBF_ISEQ_DEBUG
14884 fprintf(stderr, "ibf_load_iseq: new iseq=%p\n", (void *)iseq);
14885#endif
14886 FL_SET((VALUE)iseq, ISEQ_NOT_LOADED_YET);
14887 iseq->aux.loader.obj = load->loader_obj;
14888 iseq->aux.loader.index = iseq_index;
14889#if IBF_ISEQ_DEBUG
14890 fprintf(stderr, "ibf_load_iseq: iseq=%p loader_obj=%p index=%d\n",
14891 (void *)iseq, (void *)load->loader_obj, iseq_index);
14892#endif
14893 pinned_list_store(load->iseq_list, iseq_index, (VALUE)iseq);
14894
14895 if (!USE_LAZY_LOAD || GET_VM()->builtin_function_table) {
14896#if IBF_ISEQ_DEBUG
14897 fprintf(stderr, "ibf_load_iseq: loading iseq=%p\n", (void *)iseq);
14898#endif
14899 rb_ibf_load_iseq_complete(iseq);
14900 }
14901
14902#if IBF_ISEQ_DEBUG
14903 fprintf(stderr, "ibf_load_iseq: iseq=%p loaded %p\n",
14904 (void *)iseq, (void *)load->iseq);
14905#endif
14906 return iseq;
14907 }
14908 }
14909}
14910
14911static void
14912ibf_load_setup_bytes(struct ibf_load *load, VALUE loader_obj, const char *bytes, size_t size)
14913{
14914 struct ibf_header *header = (struct ibf_header *)bytes;
14915 load->loader_obj = loader_obj;
14916 load->global_buffer.buff = bytes;
14917 load->header = header;
14918 load->global_buffer.size = header->size;
14919 load->global_buffer.obj_list_offset = header->global_object_list_offset;
14920 load->global_buffer.obj_list_size = header->global_object_list_size;
14921 RB_OBJ_WRITE(loader_obj, &load->iseq_list, pinned_list_new(header->iseq_list_size));
14922 RB_OBJ_WRITE(loader_obj, &load->global_buffer.obj_list, pinned_list_new(load->global_buffer.obj_list_size));
14923 load->iseq = NULL;
14924
14925 load->current_buffer = &load->global_buffer;
14926
14927 if (size < header->size) {
14928 rb_raise(rb_eRuntimeError, "broken binary format");
14929 }
14930 if (strncmp(header->magic, "YARB", 4) != 0) {
14931 rb_raise(rb_eRuntimeError, "unknown binary format");
14932 }
14933 if (header->major_version != IBF_MAJOR_VERSION ||
14934 header->minor_version != IBF_MINOR_VERSION) {
14935 rb_raise(rb_eRuntimeError, "unmatched version file (%u.%u for %u.%u)",
14936 header->major_version, header->minor_version, IBF_MAJOR_VERSION, IBF_MINOR_VERSION);
14937 }
14938 if (header->endian != IBF_ENDIAN_MARK) {
14939 rb_raise(rb_eRuntimeError, "unmatched endian: %c", header->endian);
14940 }
14941 if (header->wordsize != SIZEOF_VALUE) {
14942 rb_raise(rb_eRuntimeError, "unmatched word size: %d", header->wordsize);
14943 }
14944 if (header->iseq_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
14945 rb_raise(rb_eArgError, "unaligned iseq list offset: %u",
14946 header->iseq_list_offset);
14947 }
14948 if (load->global_buffer.obj_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
14949 rb_raise(rb_eArgError, "unaligned object list offset: %u",
14950 load->global_buffer.obj_list_offset);
14951 }
14952}
14953
14954static void
14955ibf_load_setup(struct ibf_load *load, VALUE loader_obj, VALUE str)
14956{
14957 StringValue(str);
14958
14959 if (RSTRING_LENINT(str) < (int)sizeof(struct ibf_header)) {
14960 rb_raise(rb_eRuntimeError, "broken binary format");
14961 }
14962
14963 if (USE_LAZY_LOAD) {
14964 str = rb_str_new(RSTRING_PTR(str), RSTRING_LEN(str));
14965 }
14966
14967 ibf_load_setup_bytes(load, loader_obj, RSTRING_PTR(str), RSTRING_LEN(str));
14968 RB_OBJ_WRITE(loader_obj, &load->str, str);
14969}
14970
14971static void
14972ibf_loader_mark(void *ptr)
14973{
14974 struct ibf_load *load = (struct ibf_load *)ptr;
14975 rb_gc_mark(load->str);
14976 rb_gc_mark(load->iseq_list);
14977 rb_gc_mark(load->global_buffer.obj_list);
14978}
14979
14980static void
14981ibf_loader_free(void *ptr)
14982{
14983 struct ibf_load *load = (struct ibf_load *)ptr;
14984 SIZED_FREE(load);
14985}
14986
14987static size_t
14988ibf_loader_memsize(const void *ptr)
14989{
14990 return sizeof(struct ibf_load);
14991}
14992
14993static const rb_data_type_t ibf_load_type = {
14994 "ibf_loader",
14995 {ibf_loader_mark, ibf_loader_free, ibf_loader_memsize,},
14996 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
14997};
14998
14999const rb_iseq_t *
15000rb_iseq_ibf_load(VALUE str)
15001{
15002 struct ibf_load *load;
15003 rb_iseq_t *iseq;
15004 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
15005
15006 ibf_load_setup(load, loader_obj, str);
15007 iseq = ibf_load_iseq(load, 0);
15008
15009 RB_GC_GUARD(loader_obj);
15010 return iseq;
15011}
15012
15013const rb_iseq_t *
15014rb_iseq_ibf_load_bytes(const char *bytes, size_t size)
15015{
15016 struct ibf_load *load;
15017 rb_iseq_t *iseq;
15018 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
15019
15020 ibf_load_setup_bytes(load, loader_obj, bytes, size);
15021 iseq = ibf_load_iseq(load, 0);
15022
15023 RB_GC_GUARD(loader_obj);
15024 return iseq;
15025}
15026
15027VALUE
15028rb_iseq_ibf_load_extra_data(VALUE str)
15029{
15030 struct ibf_load *load;
15031 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
15032 VALUE extra_str;
15033
15034 ibf_load_setup(load, loader_obj, str);
15035 extra_str = rb_str_new(load->global_buffer.buff + load->header->size, load->header->extra_size);
15036 RB_GC_GUARD(loader_obj);
15037 return extra_str;
15038}
15039
15040#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 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:1437
VALUE rb_eStandardError
StandardError exception.
Definition error.c:1424
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1427
VALUE rb_eNoMatchingPatternError
NoMatchingPatternError exception.
Definition error.c:1440
void rb_exc_fatal(VALUE mesg)
Raises a fatal error in the current thread.
Definition eval.c:674
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1425
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:467
VALUE rb_eNoMatchingPatternKeyError
NoMatchingPatternKeyError exception.
Definition error.c:1441
VALUE rb_eIndexError
IndexError exception.
Definition error.c:1429
VALUE rb_eSyntaxError
SyntaxError exception.
Definition error.c:1444
@ 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:104
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:95
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:651
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:888
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1307
#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
#define RBIMPL_ATTR_FORMAT(x, y, z)
Wraps (or simulates) __attribute__((format))
Definition format.h:29
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:1110
int rb_is_attrset_id(ID id)
Classifies the given ID, then sees if it is an attribute writer.
Definition symbol.c:1134
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:4304
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:1005
VALUE rb_sym2str(VALUE symbol)
Obtain a frozen string representation of a symbol (not including the leading colon).
Definition symbol.c:1024
ID rb_sym2id(VALUE obj)
Converts an instance of rb_cSymbol into an ID.
Definition symbol.c:974
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:1551
#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:106
#define RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
Definition rtypeddata.h:122
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:769
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:531
#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:578
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:229
const char * wrap_struct_name
Name of structs of this kind.
Definition rtypeddata.h:236
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