Ruby 4.1.0dev (2026-05-15 revision a8bcae043f931d9b79f1cb1fe2c021985d07b984)
compile.c (a8bcae043f931d9b79f1cb1fe2c021985d07b984)
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 freeze_hide_obj(map);
2736 generated_iseq[code_index + 1 + j] = map;
2737 ISEQ_MBITS_SET(mark_offset_bits, code_index + 1 + j);
2738 RB_OBJ_WRITTEN(iseq, Qundef, map);
2739 needs_bitmap = true;
2740 break;
2741 }
2742 case TS_LINDEX:
2743 case TS_NUM: /* ulong */
2744 generated_iseq[code_index + 1 + j] = FIX2INT(operands[j]);
2745 break;
2746 case TS_ISEQ: /* iseq */
2747 case TS_VALUE: /* VALUE */
2748 {
2749 VALUE v = operands[j];
2750 generated_iseq[code_index + 1 + j] = v;
2751 /* to mark ruby object */
2752 if (!SPECIAL_CONST_P(v)) {
2753 RB_OBJ_WRITTEN(iseq, Qundef, v);
2754 ISEQ_MBITS_SET(mark_offset_bits, code_index + 1 + j);
2755 needs_bitmap = true;
2756 }
2757 break;
2758 }
2759 /* [ TS_IVC | TS_ICVARC | TS_ISE | TS_IC ] */
2760 case TS_IC: /* inline cache: constants */
2761 {
2762 unsigned int ic_index = ISEQ_COMPILE_DATA(iseq)->ic_index++;
2763 IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache;
2764 if (UNLIKELY(ic_index >= body->ic_size)) {
2765 BADINSN_DUMP(anchor, &iobj->link, 0);
2766 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2767 "iseq_set_sequence: ic_index overflow: index: %d, size: %d",
2768 ic_index, ISEQ_IS_SIZE(body));
2769 }
2770
2771 ic->segments = array_to_idlist(operands[j]);
2772
2773 generated_iseq[code_index + 1 + j] = (VALUE)ic;
2774 }
2775 break;
2776 case TS_IVC: /* inline ivar cache */
2777 {
2778 unsigned int ic_index = FIX2UINT(operands[j]);
2779
2780 IVC cache = ((IVC)&body->is_entries[ic_index]);
2781
2782 if (insn == BIN(setinstancevariable)) {
2783 cache->iv_set_name = SYM2ID(operands[j - 1]);
2784 cache->value = IVAR_CACHE_INIT;
2785 }
2786 else {
2787 cache->iv_set_name = 0;
2788 cache->value = rb_getivar_cache_pack(ROOT_SHAPE_ID, ATTR_INDEX_NOT_SET);
2789 }
2790 }
2791 case TS_ISE: /* inline storage entry: `once` insn */
2792 case TS_ICVARC: /* inline cvar cache */
2793 {
2794 unsigned int ic_index = FIX2UINT(operands[j]);
2795 IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache;
2796 if (UNLIKELY(ic_index >= ISEQ_IS_SIZE(body))) {
2797 BADINSN_DUMP(anchor, &iobj->link, 0);
2798 COMPILE_ERROR(iseq, iobj->insn_info.line_no,
2799 "iseq_set_sequence: ic_index overflow: index: %d, size: %d",
2800 ic_index, ISEQ_IS_SIZE(body));
2801 }
2802 generated_iseq[code_index + 1 + j] = (VALUE)ic;
2803
2804 break;
2805 }
2806 case TS_CALLDATA:
2807 {
2808 const struct rb_callinfo *source_ci = (const struct rb_callinfo *)operands[j];
2809 RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq)->ci_index <= body->ci_size);
2810 struct rb_call_data *cd = &body->call_data[ISEQ_COMPILE_DATA(iseq)->ci_index++];
2811 cd->ci = source_ci;
2812 cd->cc = vm_cc_empty();
2813 generated_iseq[code_index + 1 + j] = (VALUE)cd;
2814 break;
2815 }
2816 case TS_ID: /* ID */
2817 generated_iseq[code_index + 1 + j] = SYM2ID(operands[j]);
2818 break;
2819 case TS_FUNCPTR:
2820 generated_iseq[code_index + 1 + j] = operands[j];
2821 break;
2822 case TS_BUILTIN:
2823 generated_iseq[code_index + 1 + j] = operands[j];
2824 break;
2825 default:
2826 BADINSN_ERROR(iseq, iobj->insn_info.line_no,
2827 "unknown operand type: %c", type);
2828 return COMPILE_NG;
2829 }
2830 }
2831 if (add_insn_info(insns_info, positions, insns_info_index, code_index, iobj)) insns_info_index++;
2832 code_index += len;
2833 break;
2834 }
2835 case ISEQ_ELEMENT_LABEL:
2836 {
2837 LABEL *lobj = (LABEL *)list;
2838 if (lobj->sp != sp) {
2839 debugs("%s: sp inconsistency found but ignored (" LABEL_FORMAT " sp: %d, calculated sp: %d)\n",
2840 RSTRING_PTR(rb_iseq_path(iseq)),
2841 lobj->label_no, lobj->sp, sp);
2842 }
2843 sp = lobj->sp;
2844 break;
2845 }
2846 case ISEQ_ELEMENT_ADJUST:
2847 {
2848 ADJUST *adjust = (ADJUST *)list;
2849 int orig_sp = sp;
2850
2851 if (adjust->label) {
2852 sp = adjust->label->sp;
2853 }
2854 else {
2855 sp = 0;
2856 }
2857
2858 if (adjust->line_no != -1) {
2859 const int diff = orig_sp - sp;
2860 if (diff > 0) {
2861 if (insns_info_index == 0) {
2862 COMPILE_ERROR(iseq, adjust->line_no,
2863 "iseq_set_sequence: adjust bug (ISEQ_ELEMENT_ADJUST must not be the first in iseq)");
2864 }
2865 if (add_adjust_info(insns_info, positions, insns_info_index, code_index, adjust)) insns_info_index++;
2866 }
2867 if (diff > 1) {
2868 generated_iseq[code_index++] = BIN(adjuststack);
2869 generated_iseq[code_index++] = orig_sp - sp;
2870 }
2871 else if (diff == 1) {
2872 generated_iseq[code_index++] = BIN(pop);
2873 }
2874 else if (diff < 0) {
2875 int label_no = adjust->label ? adjust->label->label_no : -1;
2876 SIZED_FREE_N(generated_iseq, generated_iseq_size);
2877 SIZED_FREE_N(insns_info, insns_info_size);
2878 SIZED_FREE_N(positions, positions_size);
2879 if (ISEQ_MBITS_BUFLEN(code_size) > 1) {
2880 SIZED_FREE_N(mark_offset_bits, ISEQ_MBITS_BUFLEN(code_index));
2881 }
2882 debug_list(anchor, list);
2883 COMPILE_ERROR(iseq, adjust->line_no,
2884 "iseq_set_sequence: adjust bug to %d %d < %d",
2885 label_no, orig_sp, sp);
2886 return COMPILE_NG;
2887 }
2888 }
2889 break;
2890 }
2891 default:
2892 /* ignore */
2893 break;
2894 }
2895 list = list->next;
2896 }
2897
2898 body->iseq_encoded = (void *)generated_iseq;
2899 body->iseq_size = code_index;
2900 body->stack_max = stack_max;
2901
2902 if (ISEQ_COMPILE_DATA(iseq)->is_single_mark_bit) {
2903 body->mark_bits.single = ISEQ_COMPILE_DATA(iseq)->mark_bits.single;
2904 }
2905 else {
2906 if (needs_bitmap) {
2907 body->mark_bits.list = mark_offset_bits;
2908 }
2909 else {
2910 body->mark_bits.list = NULL;
2911 ISEQ_COMPILE_DATA(iseq)->mark_bits.list = NULL;
2912 SIZED_FREE_N(mark_offset_bits, mark_offset_bits_size);
2913 }
2914 }
2915
2916 /* get rid of memory leak when REALLOC failed */
2917 body->insns_info.body = insns_info;
2918 body->insns_info.positions = positions;
2919
2920 SIZED_REALLOC_N(insns_info, struct iseq_insn_info_entry, insns_info_index, insns_info_size);
2921 body->insns_info.body = insns_info;
2922 SIZED_REALLOC_N(positions, unsigned int, insns_info_index, positions_size);
2923 body->insns_info.positions = positions;
2924 body->insns_info.size = insns_info_index;
2925
2926 return COMPILE_OK;
2927}
2928
2929static int
2930label_get_position(LABEL *lobj)
2931{
2932 return lobj->position;
2933}
2934
2935static int
2936label_get_sp(LABEL *lobj)
2937{
2938 return lobj->sp;
2939}
2940
2941static int
2942iseq_set_exception_table(rb_iseq_t *iseq)
2943{
2944 const VALUE *tptr, *ptr;
2945 unsigned int tlen, i;
2946 struct iseq_catch_table_entry *entry;
2947
2948 ISEQ_BODY(iseq)->catch_table = NULL;
2949
2950 VALUE catch_table_ary = ISEQ_COMPILE_DATA(iseq)->catch_table_ary;
2951 if (NIL_P(catch_table_ary)) return COMPILE_OK;
2952 tlen = (int)RARRAY_LEN(catch_table_ary);
2953 tptr = RARRAY_CONST_PTR(catch_table_ary);
2954
2955 if (tlen > 0) {
2956 struct iseq_catch_table *table = xmalloc(iseq_catch_table_bytes(tlen));
2957 table->size = tlen;
2958
2959 for (i = 0; i < table->size; i++) {
2960 int pos;
2961 ptr = RARRAY_CONST_PTR(tptr[i]);
2962 entry = UNALIGNED_MEMBER_PTR(table, entries[i]);
2963 entry->type = (enum rb_catch_type)(ptr[0] & 0xffff);
2964 pos = label_get_position((LABEL *)(ptr[1] & ~1));
2965 RUBY_ASSERT(pos >= 0);
2966 entry->start = (unsigned int)pos;
2967 pos = label_get_position((LABEL *)(ptr[2] & ~1));
2968 RUBY_ASSERT(pos >= 0);
2969 entry->end = (unsigned int)pos;
2970 entry->iseq = (rb_iseq_t *)ptr[3];
2971 RB_OBJ_WRITTEN(iseq, Qundef, entry->iseq);
2972
2973 /* stack depth */
2974 if (ptr[4]) {
2975 LABEL *lobj = (LABEL *)(ptr[4] & ~1);
2976 entry->cont = label_get_position(lobj);
2977 entry->sp = label_get_sp(lobj);
2978
2979 /* TODO: Dirty Hack! Fix me */
2980 if (entry->type == CATCH_TYPE_RESCUE ||
2981 entry->type == CATCH_TYPE_BREAK ||
2982 entry->type == CATCH_TYPE_NEXT) {
2983 RUBY_ASSERT(entry->sp > 0);
2984 entry->sp--;
2985 }
2986 }
2987 else {
2988 entry->cont = 0;
2989 }
2990 }
2991 ISEQ_BODY(iseq)->catch_table = table;
2992 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, 0); /* free */
2993 }
2994
2995 RB_GC_GUARD(catch_table_ary);
2996
2997 return COMPILE_OK;
2998}
2999
3000/*
3001 * set optional argument table
3002 * def foo(a, b=expr1, c=expr2)
3003 * =>
3004 * b:
3005 * expr1
3006 * c:
3007 * expr2
3008 */
3009static int
3010iseq_set_optargs_table(rb_iseq_t *iseq)
3011{
3012 int i;
3013 VALUE *opt_table = (VALUE *)ISEQ_BODY(iseq)->param.opt_table;
3014
3015 if (ISEQ_BODY(iseq)->param.flags.has_opt) {
3016 for (i = 0; i < ISEQ_BODY(iseq)->param.opt_num + 1; i++) {
3017 opt_table[i] = label_get_position((LABEL *)opt_table[i]);
3018 }
3019 }
3020 return COMPILE_OK;
3021}
3022
3023static LINK_ELEMENT *
3024get_destination_insn(INSN *iobj)
3025{
3026 LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
3027 LINK_ELEMENT *list;
3028 rb_event_flag_t events = 0;
3029
3030 list = lobj->link.next;
3031 while (list) {
3032 switch (list->type) {
3033 case ISEQ_ELEMENT_INSN:
3034 case ISEQ_ELEMENT_ADJUST:
3035 goto found;
3036 case ISEQ_ELEMENT_LABEL:
3037 /* ignore */
3038 break;
3039 case ISEQ_ELEMENT_TRACE:
3040 {
3041 TRACE *trace = (TRACE *)list;
3042 events |= trace->event;
3043 }
3044 break;
3045 default: break;
3046 }
3047 list = list->next;
3048 }
3049 found:
3050 if (list && IS_INSN(list)) {
3051 INSN *iobj = (INSN *)list;
3052 iobj->insn_info.events |= events;
3053 }
3054 return list;
3055}
3056
3057static LINK_ELEMENT *
3058get_next_insn(INSN *iobj)
3059{
3060 LINK_ELEMENT *list = iobj->link.next;
3061
3062 while (list) {
3063 if (IS_INSN(list) || IS_ADJUST(list)) {
3064 return list;
3065 }
3066 list = list->next;
3067 }
3068 return 0;
3069}
3070
3071static LINK_ELEMENT *
3072get_prev_insn(INSN *iobj)
3073{
3074 LINK_ELEMENT *list = iobj->link.prev;
3075
3076 while (list) {
3077 if (IS_INSN(list) || IS_ADJUST(list)) {
3078 return list;
3079 }
3080 list = list->prev;
3081 }
3082 return 0;
3083}
3084
3085static void
3086unref_destination(INSN *iobj, int pos)
3087{
3088 LABEL *lobj = (LABEL *)OPERAND_AT(iobj, pos);
3089 --lobj->refcnt;
3090 if (!lobj->refcnt) ELEM_REMOVE(&lobj->link);
3091}
3092
3093static bool
3094replace_destination(INSN *dobj, INSN *nobj)
3095{
3096 VALUE n = OPERAND_AT(nobj, 0);
3097 LABEL *dl = (LABEL *)OPERAND_AT(dobj, 0);
3098 LABEL *nl = (LABEL *)n;
3099 if (dl == nl) return false;
3100 --dl->refcnt;
3101 ++nl->refcnt;
3102 OPERAND_AT(dobj, 0) = n;
3103 if (!dl->refcnt) ELEM_REMOVE(&dl->link);
3104 return true;
3105}
3106
3107static LABEL*
3108find_destination(INSN *i)
3109{
3110 int pos, len = insn_len(i->insn_id);
3111 for (pos = 0; pos < len; ++pos) {
3112 if (insn_op_types(i->insn_id)[pos] == TS_OFFSET) {
3113 return (LABEL *)OPERAND_AT(i, pos);
3114 }
3115 }
3116 return 0;
3117}
3118
3119static int
3120remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i)
3121{
3122 LINK_ELEMENT *first = i, *end;
3123 int *unref_counts = 0, nlabels = ISEQ_COMPILE_DATA(iseq)->label_no;
3124
3125 if (!i) return 0;
3126 unref_counts = ALLOCA_N(int, nlabels);
3127 MEMZERO(unref_counts, int, nlabels);
3128 end = i;
3129 do {
3130 LABEL *lab;
3131 if (IS_INSN(i)) {
3132 if (IS_INSN_ID(i, leave)) {
3133 end = i;
3134 break;
3135 }
3136 else if ((lab = find_destination((INSN *)i)) != 0) {
3137 unref_counts[lab->label_no]++;
3138 }
3139 }
3140 else if (IS_LABEL(i)) {
3141 lab = (LABEL *)i;
3142 if (lab->unremovable) return 0;
3143 if (lab->refcnt > unref_counts[lab->label_no]) {
3144 if (i == first) return 0;
3145 break;
3146 }
3147 continue;
3148 }
3149 else if (IS_TRACE(i)) {
3150 /* do nothing */
3151 }
3152 else if (IS_ADJUST(i)) {
3153 return 0;
3154 }
3155 end = i;
3156 } while ((i = i->next) != 0);
3157 i = first;
3158 do {
3159 if (IS_INSN(i)) {
3160 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
3161 VALUE insn = INSN_OF(i);
3162 int pos, len = insn_len(insn);
3163 for (pos = 0; pos < len; ++pos) {
3164 switch (insn_op_types(insn)[pos]) {
3165 case TS_OFFSET:
3166 unref_destination((INSN *)i, pos);
3167 break;
3168 case TS_CALLDATA:
3169 --(body->ci_size);
3170 break;
3171 }
3172 }
3173 }
3174 ELEM_REMOVE(i);
3175 } while ((i != end) && (i = i->next) != 0);
3176 return 1;
3177}
3178
3179static int
3180iseq_pop_newarray(rb_iseq_t *iseq, INSN *iobj)
3181{
3182 switch (OPERAND_AT(iobj, 0)) {
3183 case INT2FIX(0): /* empty array */
3184 ELEM_REMOVE(&iobj->link);
3185 return TRUE;
3186 case INT2FIX(1): /* single element array */
3187 ELEM_REMOVE(&iobj->link);
3188 return FALSE;
3189 default:
3190 iobj->insn_id = BIN(adjuststack);
3191 return TRUE;
3192 }
3193}
3194
3195static int
3196is_frozen_putstring(INSN *insn, VALUE *op)
3197{
3198 if (IS_INSN_ID(insn, dupstring) || IS_INSN_ID(insn, dupchilledstring)) {
3199 *op = OPERAND_AT(insn, 0);
3200 return 1;
3201 }
3202 else if (IS_INSN_ID(insn, putobject)) { /* frozen_string_literal */
3203 *op = OPERAND_AT(insn, 0);
3204 return RB_TYPE_P(*op, T_STRING);
3205 }
3206 return 0;
3207}
3208
3209static int
3210insn_has_label_before(LINK_ELEMENT *elem)
3211{
3212 LINK_ELEMENT *prev = elem->prev;
3213 while (prev) {
3214 if (prev->type == ISEQ_ELEMENT_LABEL) {
3215 LABEL *label = (LABEL *)prev;
3216 if (label->refcnt > 0) {
3217 return 1;
3218 }
3219 }
3220 else if (prev->type == ISEQ_ELEMENT_INSN) {
3221 break;
3222 }
3223 prev = prev->prev;
3224 }
3225 return 0;
3226}
3227
3228static int
3229optimize_checktype(rb_iseq_t *iseq, INSN *iobj)
3230{
3231 /*
3232 * putobject obj
3233 * dup
3234 * checktype T_XXX
3235 * branchif l1
3236 * l2:
3237 * ...
3238 * l1:
3239 *
3240 * => obj is a T_XXX
3241 *
3242 * putobject obj (T_XXX)
3243 * jump L1
3244 * L1:
3245 *
3246 * => obj is not a T_XXX
3247 *
3248 * putobject obj (T_XXX)
3249 * jump L2
3250 * L2:
3251 */
3252 int line, node_id;
3253 INSN *niobj, *ciobj, *dup = 0;
3254 LABEL *dest = 0;
3255 VALUE type;
3256
3257 switch (INSN_OF(iobj)) {
3258 case BIN(dupstring):
3259 case BIN(dupchilledstring):
3261 break;
3262 case BIN(putnil):
3263 type = INT2FIX(T_NIL);
3264 break;
3265 case BIN(putobject):
3266 type = INT2FIX(TYPE(OPERAND_AT(iobj, 0)));
3267 break;
3268 default: return FALSE;
3269 }
3270
3271 ciobj = (INSN *)get_next_insn(iobj);
3272 if (IS_INSN_ID(ciobj, jump)) {
3273 ciobj = (INSN *)get_next_insn((INSN*)OPERAND_AT(ciobj, 0));
3274 }
3275 if (IS_INSN_ID(ciobj, dup)) {
3276 ciobj = (INSN *)get_next_insn(dup = ciobj);
3277 }
3278 if (!ciobj || !IS_INSN_ID(ciobj, checktype)) return FALSE;
3279 niobj = (INSN *)get_next_insn(ciobj);
3280 if (!niobj) {
3281 /* TODO: putobject true/false */
3282 return FALSE;
3283 }
3284 switch (INSN_OF(niobj)) {
3285 case BIN(branchif):
3286 if (OPERAND_AT(ciobj, 0) == type) {
3287 dest = (LABEL *)OPERAND_AT(niobj, 0);
3288 }
3289 break;
3290 case BIN(branchunless):
3291 if (OPERAND_AT(ciobj, 0) != type) {
3292 dest = (LABEL *)OPERAND_AT(niobj, 0);
3293 }
3294 break;
3295 default:
3296 return FALSE;
3297 }
3298 line = ciobj->insn_info.line_no;
3299 node_id = ciobj->insn_info.node_id;
3300 if (!dest) {
3301 if (niobj->link.next && IS_LABEL(niobj->link.next)) {
3302 dest = (LABEL *)niobj->link.next; /* reuse label */
3303 }
3304 else {
3305 dest = NEW_LABEL(line);
3306 ELEM_INSERT_NEXT(&niobj->link, &dest->link);
3307 }
3308 }
3309 INSERT_AFTER_INSN1(iobj, line, node_id, jump, dest);
3310 LABEL_REF(dest);
3311 if (!dup) INSERT_AFTER_INSN(iobj, line, node_id, pop);
3312 return TRUE;
3313}
3314
3315static const struct rb_callinfo *
3316ci_flag_set(const rb_iseq_t *iseq, const struct rb_callinfo *ci, unsigned int add)
3317{
3318 const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci),
3319 vm_ci_flag(ci) | add,
3320 vm_ci_argc(ci),
3321 vm_ci_kwarg(ci));
3322 RB_OBJ_WRITTEN(iseq, ci, nci);
3323 return nci;
3324}
3325
3326static const struct rb_callinfo *
3327ci_argc_set(const rb_iseq_t *iseq, const struct rb_callinfo *ci, int argc)
3328{
3329 const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci),
3330 vm_ci_flag(ci),
3331 argc,
3332 vm_ci_kwarg(ci));
3333 RB_OBJ_WRITTEN(iseq, ci, nci);
3334 return nci;
3335}
3336
3337#define vm_ci_simple(ci) (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE)
3338
3339static int
3340iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt)
3341{
3342 INSN *const iobj = (INSN *)list;
3343
3344 again:
3345 optimize_checktype(iseq, iobj);
3346
3347 if (IS_INSN_ID(iobj, jump)) {
3348 INSN *niobj, *diobj, *piobj;
3349 diobj = (INSN *)get_destination_insn(iobj);
3350 niobj = (INSN *)get_next_insn(iobj);
3351
3352 if (diobj == niobj) {
3353 /*
3354 * jump LABEL
3355 * LABEL:
3356 * =>
3357 * LABEL:
3358 */
3359 unref_destination(iobj, 0);
3360 ELEM_REMOVE(&iobj->link);
3361 return COMPILE_OK;
3362 }
3363 else if (iobj != diobj && IS_INSN(&diobj->link) &&
3364 IS_INSN_ID(diobj, jump) &&
3365 OPERAND_AT(iobj, 0) != OPERAND_AT(diobj, 0) &&
3366 diobj->insn_info.events == 0) {
3367 /*
3368 * useless jump elimination:
3369 * jump LABEL1
3370 * ...
3371 * LABEL1:
3372 * jump LABEL2
3373 *
3374 * => in this case, first jump instruction should jump to
3375 * LABEL2 directly
3376 */
3377 if (replace_destination(iobj, diobj)) {
3378 remove_unreachable_chunk(iseq, iobj->link.next);
3379 goto again;
3380 }
3381 }
3382 else if (IS_INSN_ID(diobj, leave)) {
3383 /*
3384 * jump LABEL
3385 * ...
3386 * LABEL:
3387 * leave
3388 * =>
3389 * leave
3390 * ...
3391 * LABEL:
3392 * leave
3393 */
3394 /* replace */
3395 unref_destination(iobj, 0);
3396 iobj->insn_id = BIN(leave);
3397 iobj->operand_size = 0;
3398 iobj->insn_info = diobj->insn_info;
3399 goto again;
3400 }
3401 else if (IS_INSN(iobj->link.prev) &&
3402 (piobj = (INSN *)iobj->link.prev) &&
3403 (IS_INSN_ID(piobj, branchif) ||
3404 IS_INSN_ID(piobj, branchunless))) {
3405 INSN *pdiobj = (INSN *)get_destination_insn(piobj);
3406 if (niobj == pdiobj) {
3407 int refcnt = IS_LABEL(piobj->link.next) ?
3408 ((LABEL *)piobj->link.next)->refcnt : 0;
3409 /*
3410 * useless jump elimination (if/unless destination):
3411 * if L1
3412 * jump L2
3413 * L1:
3414 * ...
3415 * L2:
3416 *
3417 * ==>
3418 * unless L2
3419 * L1:
3420 * ...
3421 * L2:
3422 */
3423 piobj->insn_id = (IS_INSN_ID(piobj, branchif))
3424 ? BIN(branchunless) : BIN(branchif);
3425 if (replace_destination(piobj, iobj) && refcnt <= 1) {
3426 ELEM_REMOVE(&iobj->link);
3427 }
3428 else {
3429 /* TODO: replace other branch destinations too */
3430 }
3431 return COMPILE_OK;
3432 }
3433 else if (diobj == pdiobj) {
3434 /*
3435 * useless jump elimination (if/unless before jump):
3436 * L1:
3437 * ...
3438 * if L1
3439 * jump L1
3440 *
3441 * ==>
3442 * L1:
3443 * ...
3444 * pop
3445 * jump L1
3446 */
3447 INSN *popiobj = new_insn_core(iseq, iobj->insn_info.line_no, iobj->insn_info.node_id, BIN(pop), 0, 0);
3448 ELEM_REPLACE(&piobj->link, &popiobj->link);
3449 }
3450 }
3451 if (remove_unreachable_chunk(iseq, iobj->link.next)) {
3452 goto again;
3453 }
3454 }
3455
3456 /*
3457 * dupstring "beg"
3458 * dupstring "end"
3459 * newrange excl
3460 *
3461 * ==>
3462 *
3463 * putobject "beg".."end"
3464 */
3465 if (IS_INSN_ID(iobj, newrange)) {
3466 INSN *const range = iobj;
3467 INSN *beg, *end;
3468 VALUE str_beg, str_end;
3469
3470 if ((end = (INSN *)get_prev_insn(range)) != 0 &&
3471 is_frozen_putstring(end, &str_end) &&
3472 (beg = (INSN *)get_prev_insn(end)) != 0 &&
3473 is_frozen_putstring(beg, &str_beg) &&
3474 !(insn_has_label_before(&beg->link) || insn_has_label_before(&end->link))) {
3475 int excl = FIX2INT(OPERAND_AT(range, 0));
3476 VALUE lit_range = RB_OBJ_SET_SHAREABLE(rb_range_new(str_beg, str_end, excl));
3477
3478 ELEM_REMOVE(&beg->link);
3479 ELEM_REMOVE(&end->link);
3480 range->insn_id = BIN(putobject);
3481 OPERAND_AT(range, 0) = lit_range;
3482 RB_OBJ_WRITTEN(iseq, Qundef, lit_range);
3483 }
3484 }
3485
3486 if (IS_INSN_ID(iobj, leave)) {
3487 remove_unreachable_chunk(iseq, iobj->link.next);
3488 }
3489
3490 /*
3491 * ...
3492 * duparray [...]
3493 * concatarray | concattoarray
3494 * =>
3495 * ...
3496 * putobject [...]
3497 * concatarray | concattoarray
3498 */
3499 if (IS_INSN_ID(iobj, duparray)) {
3500 LINK_ELEMENT *next = iobj->link.next;
3501 if (IS_INSN(next) && (IS_INSN_ID(next, concatarray) || IS_INSN_ID(next, concattoarray))) {
3502 iobj->insn_id = BIN(putobject);
3503 }
3504 }
3505
3506 /*
3507 * duparray [...]
3508 * send <calldata!mid:freeze, argc:0, ARGS_SIMPLE>, nil
3509 * =>
3510 * opt_ary_freeze [...], <calldata!mid:freeze, argc:0, ARGS_SIMPLE>
3511 */
3512 if (IS_INSN_ID(iobj, duparray)) {
3513 LINK_ELEMENT *next = iobj->link.next;
3514 if (IS_INSN(next) && (IS_INSN_ID(next, send))) {
3515 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(next, 0);
3516 const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1);
3517
3518 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) {
3519 VALUE ary = iobj->operands[0];
3521
3522 insn_replace_with_operands(iseq, iobj, BIN(opt_ary_freeze), 2, ary, (VALUE)ci);
3523 ELEM_REMOVE(next);
3524 }
3525 }
3526 }
3527
3528 /*
3529 * duphash {...}
3530 * send <calldata!mid:freeze, argc:0, ARGS_SIMPLE>, nil
3531 * =>
3532 * opt_hash_freeze {...}, <calldata!mid:freeze, argc:0, ARGS_SIMPLE>
3533 */
3534 if (IS_INSN_ID(iobj, duphash)) {
3535 LINK_ELEMENT *next = iobj->link.next;
3536 if (IS_INSN(next) && (IS_INSN_ID(next, send))) {
3537 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(next, 0);
3538 const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1);
3539
3540 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) {
3541 VALUE hash = iobj->operands[0];
3542 rb_obj_reveal(hash, rb_cHash);
3543 RB_OBJ_SET_SHAREABLE(hash);
3544
3545 insn_replace_with_operands(iseq, iobj, BIN(opt_hash_freeze), 2, hash, (VALUE)ci);
3546 ELEM_REMOVE(next);
3547 }
3548 }
3549 }
3550
3551 /*
3552 * newarray 0
3553 * send <calldata!mid:freeze, argc:0, ARGS_SIMPLE>, nil
3554 * =>
3555 * opt_ary_freeze [], <calldata!mid:freeze, argc:0, ARGS_SIMPLE>
3556 */
3557 if (IS_INSN_ID(iobj, newarray) && iobj->operands[0] == INT2FIX(0)) {
3558 LINK_ELEMENT *next = iobj->link.next;
3559 if (IS_INSN(next) && (IS_INSN_ID(next, send))) {
3560 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(next, 0);
3561 const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1);
3562
3563 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) {
3564 insn_replace_with_operands(iseq, iobj, BIN(opt_ary_freeze), 2, rb_cArray_empty_frozen, (VALUE)ci);
3565 ELEM_REMOVE(next);
3566 }
3567 }
3568 }
3569
3570 /*
3571 * newhash 0
3572 * send <calldata!mid:freeze, argc:0, ARGS_SIMPLE>, nil
3573 * =>
3574 * opt_hash_freeze {}, <calldata!mid:freeze, argc:0, ARGS_SIMPLE>
3575 */
3576 if (IS_INSN_ID(iobj, newhash) && iobj->operands[0] == INT2FIX(0)) {
3577 LINK_ELEMENT *next = iobj->link.next;
3578 if (IS_INSN(next) && (IS_INSN_ID(next, send))) {
3579 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(next, 0);
3580 const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1);
3581
3582 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) {
3583 insn_replace_with_operands(iseq, iobj, BIN(opt_hash_freeze), 2, rb_cHash_empty_frozen, (VALUE)ci);
3584 ELEM_REMOVE(next);
3585 }
3586 }
3587 }
3588
3589 if (IS_INSN_ID(iobj, branchif) ||
3590 IS_INSN_ID(iobj, branchnil) ||
3591 IS_INSN_ID(iobj, branchunless)) {
3592 /*
3593 * if L1
3594 * ...
3595 * L1:
3596 * jump L2
3597 * =>
3598 * if L2
3599 */
3600 INSN *nobj = (INSN *)get_destination_insn(iobj);
3601
3602 /* This is super nasty hack!!!
3603 *
3604 * This jump-jump optimization may ignore event flags of the jump
3605 * instruction being skipped. Actually, Line 2 TracePoint event
3606 * is never fired in the following code:
3607 *
3608 * 1: raise if 1 == 2
3609 * 2: while true
3610 * 3: break
3611 * 4: end
3612 *
3613 * This is critical for coverage measurement. [Bug #15980]
3614 *
3615 * This is a stopgap measure: stop the jump-jump optimization if
3616 * coverage measurement is enabled and if the skipped instruction
3617 * has any event flag.
3618 *
3619 * Note that, still, TracePoint Line event does not occur on Line 2.
3620 * This should be fixed in future.
3621 */
3622 int stop_optimization =
3623 ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq) &&
3624 nobj->link.type == ISEQ_ELEMENT_INSN &&
3625 nobj->insn_info.events;
3626 if (!stop_optimization) {
3627 INSN *pobj = (INSN *)iobj->link.prev;
3628 int prev_dup = 0;
3629 if (pobj) {
3630 if (!IS_INSN(&pobj->link))
3631 pobj = 0;
3632 else if (IS_INSN_ID(pobj, dup))
3633 prev_dup = 1;
3634 }
3635
3636 for (;;) {
3637 if (IS_INSN(&nobj->link) && IS_INSN_ID(nobj, jump)) {
3638 if (!replace_destination(iobj, nobj)) break;
3639 }
3640 else if (prev_dup && IS_INSN_ID(nobj, dup) &&
3641 !!(nobj = (INSN *)nobj->link.next) &&
3642 /* basic blocks, with no labels in the middle */
3643 nobj->insn_id == iobj->insn_id) {
3644 /*
3645 * dup
3646 * if L1
3647 * ...
3648 * L1:
3649 * dup
3650 * if L2
3651 * =>
3652 * dup
3653 * if L2
3654 * ...
3655 * L1:
3656 * dup
3657 * if L2
3658 */
3659 if (!replace_destination(iobj, nobj)) break;
3660 }
3661 else if (pobj) {
3662 /*
3663 * putnil
3664 * if L1
3665 * =>
3666 * # nothing
3667 *
3668 * putobject true
3669 * if L1
3670 * =>
3671 * jump L1
3672 *
3673 * dupstring ".."
3674 * if L1
3675 * =>
3676 * jump L1
3677 *
3678 * dupstring ".."
3679 * dup
3680 * if L1
3681 * =>
3682 * dupstring ".."
3683 * jump L1
3684 *
3685 */
3686 int cond;
3687 if (prev_dup && IS_INSN(pobj->link.prev)) {
3688 pobj = (INSN *)pobj->link.prev;
3689 }
3690 if (IS_INSN_ID(pobj, putobject)) {
3691 cond = (IS_INSN_ID(iobj, branchif) ?
3692 OPERAND_AT(pobj, 0) != Qfalse :
3693 IS_INSN_ID(iobj, branchunless) ?
3694 OPERAND_AT(pobj, 0) == Qfalse :
3695 FALSE);
3696 }
3697 else if (IS_INSN_ID(pobj, dupstring) ||
3698 IS_INSN_ID(pobj, duparray) ||
3699 IS_INSN_ID(pobj, newarray)) {
3700 cond = IS_INSN_ID(iobj, branchif);
3701 }
3702 else if (IS_INSN_ID(pobj, putnil)) {
3703 cond = !IS_INSN_ID(iobj, branchif);
3704 }
3705 else break;
3706 if (prev_dup || !IS_INSN_ID(pobj, newarray)) {
3707 ELEM_REMOVE(iobj->link.prev);
3708 }
3709 else if (!iseq_pop_newarray(iseq, pobj)) {
3710 pobj = new_insn_core(iseq, pobj->insn_info.line_no, pobj->insn_info.node_id, BIN(pop), 0, NULL);
3711 ELEM_INSERT_PREV(&iobj->link, &pobj->link);
3712 }
3713 if (cond) {
3714 if (prev_dup) {
3715 pobj = new_insn_core(iseq, pobj->insn_info.line_no, pobj->insn_info.node_id, BIN(putnil), 0, NULL);
3716 ELEM_INSERT_NEXT(&iobj->link, &pobj->link);
3717 }
3718 iobj->insn_id = BIN(jump);
3719 goto again;
3720 }
3721 else {
3722 unref_destination(iobj, 0);
3723 ELEM_REMOVE(&iobj->link);
3724 }
3725 break;
3726 }
3727 else break;
3728 nobj = (INSN *)get_destination_insn(nobj);
3729 }
3730 }
3731 }
3732
3733 if (IS_INSN_ID(iobj, pop)) {
3734 /*
3735 * putself / putnil / putobject obj / dupstring "..."
3736 * pop
3737 * =>
3738 * # do nothing
3739 */
3740 LINK_ELEMENT *prev = iobj->link.prev;
3741 if (IS_INSN(prev)) {
3742 enum ruby_vminsn_type previ = ((INSN *)prev)->insn_id;
3743 if (previ == BIN(putobject) || previ == BIN(putnil) ||
3744 previ == BIN(putself) || previ == BIN(dupstring) ||
3745 previ == BIN(dupchilledstring) ||
3746 previ == BIN(dup) ||
3747 previ == BIN(getlocal) ||
3748 previ == BIN(getblockparam) ||
3749 previ == BIN(getblockparamproxy) ||
3750 previ == BIN(getinstancevariable) ||
3751 previ == BIN(duparray)) {
3752 /* just push operand or static value and pop soon, no
3753 * side effects */
3754 ELEM_REMOVE(prev);
3755 ELEM_REMOVE(&iobj->link);
3756 }
3757 else if (previ == BIN(newarray) && iseq_pop_newarray(iseq, (INSN*)prev)) {
3758 ELEM_REMOVE(&iobj->link);
3759 }
3760 else if (previ == BIN(concatarray)) {
3761 INSN *piobj = (INSN *)prev;
3762 INSERT_BEFORE_INSN1(piobj, piobj->insn_info.line_no, piobj->insn_info.node_id, splatarray, Qfalse);
3763 INSN_OF(piobj) = BIN(pop);
3764 }
3765 else if (previ == BIN(concatstrings)) {
3766 if (OPERAND_AT(prev, 0) == INT2FIX(1)) {
3767 ELEM_REMOVE(prev);
3768 }
3769 else {
3770 ELEM_REMOVE(&iobj->link);
3771 INSN_OF(prev) = BIN(adjuststack);
3772 }
3773 }
3774 }
3775 }
3776
3777 if (IS_INSN_ID(iobj, newarray) ||
3778 IS_INSN_ID(iobj, duparray) ||
3779 IS_INSN_ID(iobj, concatarray) ||
3780 IS_INSN_ID(iobj, splatarray) ||
3781 0) {
3782 /*
3783 * newarray N
3784 * splatarray
3785 * =>
3786 * newarray N
3787 * newarray always puts an array
3788 */
3789 LINK_ELEMENT *next = iobj->link.next;
3790 if (IS_INSN(next) && IS_INSN_ID(next, splatarray)) {
3791 /* remove splatarray following always-array insn */
3792 ELEM_REMOVE(next);
3793 }
3794 }
3795
3796 if (IS_INSN_ID(iobj, newarray)) {
3797 LINK_ELEMENT *next = iobj->link.next;
3798 if (IS_INSN(next) && IS_INSN_ID(next, expandarray) &&
3799 OPERAND_AT(next, 1) == INT2FIX(0)) {
3800 VALUE op1, op2;
3801 op1 = OPERAND_AT(iobj, 0);
3802 op2 = OPERAND_AT(next, 0);
3803 ELEM_REMOVE(next);
3804
3805 if (op1 == op2) {
3806 /*
3807 * newarray 2
3808 * expandarray 2, 0
3809 * =>
3810 * swap
3811 */
3812 if (op1 == INT2FIX(2)) {
3813 INSN_OF(iobj) = BIN(swap);
3814 iobj->operand_size = 0;
3815 }
3816 /*
3817 * newarray X
3818 * expandarray X, 0
3819 * =>
3820 * opt_reverse X
3821 */
3822 else {
3823 INSN_OF(iobj) = BIN(opt_reverse);
3824 }
3825 }
3826 else {
3827 long diff = FIX2LONG(op1) - FIX2LONG(op2);
3828 INSN_OF(iobj) = BIN(opt_reverse);
3829 OPERAND_AT(iobj, 0) = OPERAND_AT(next, 0);
3830
3831 if (op1 > op2) {
3832 /* X > Y
3833 * newarray X
3834 * expandarray Y, 0
3835 * =>
3836 * pop * (Y-X)
3837 * opt_reverse Y
3838 */
3839 for (; diff > 0; diff--) {
3840 INSERT_BEFORE_INSN(iobj, iobj->insn_info.line_no, iobj->insn_info.node_id, pop);
3841 }
3842 }
3843 else { /* (op1 < op2) */
3844 /* X < Y
3845 * newarray X
3846 * expandarray Y, 0
3847 * =>
3848 * putnil * (Y-X)
3849 * opt_reverse Y
3850 */
3851 for (; diff < 0; diff++) {
3852 INSERT_BEFORE_INSN(iobj, iobj->insn_info.line_no, iobj->insn_info.node_id, putnil);
3853 }
3854 }
3855 }
3856 }
3857 }
3858
3859 if (IS_INSN_ID(iobj, duparray)) {
3860 LINK_ELEMENT *next = iobj->link.next;
3861 /*
3862 * duparray obj
3863 * expandarray X, 0
3864 * =>
3865 * putobject obj
3866 * expandarray X, 0
3867 */
3868 if (IS_INSN(next) && IS_INSN_ID(next, expandarray)) {
3869 INSN_OF(iobj) = BIN(putobject);
3870 }
3871 }
3872
3873 if (IS_INSN_ID(iobj, anytostring)) {
3874 LINK_ELEMENT *next = iobj->link.next;
3875 /*
3876 * anytostring
3877 * concatstrings 1
3878 * =>
3879 * anytostring
3880 */
3881 if (IS_INSN(next) && IS_INSN_ID(next, concatstrings) &&
3882 OPERAND_AT(next, 0) == INT2FIX(1)) {
3883 ELEM_REMOVE(next);
3884 }
3885 }
3886
3887 if (IS_INSN_ID(iobj, dupstring) || IS_INSN_ID(iobj, dupchilledstring) ||
3888 (IS_INSN_ID(iobj, putobject) && RB_TYPE_P(OPERAND_AT(iobj, 0), T_STRING))) {
3889 /*
3890 * dupstring ""
3891 * concatstrings N
3892 * =>
3893 * concatstrings N-1
3894 */
3895 if (IS_NEXT_INSN_ID(&iobj->link, concatstrings) &&
3896 RSTRING_LEN(OPERAND_AT(iobj, 0)) == 0) {
3897 INSN *next = (INSN *)iobj->link.next;
3898 if ((OPERAND_AT(next, 0) = FIXNUM_INC(OPERAND_AT(next, 0), -1)) == INT2FIX(1)) {
3899 ELEM_REMOVE(&next->link);
3900 }
3901 ELEM_REMOVE(&iobj->link);
3902 }
3903 if (IS_NEXT_INSN_ID(&iobj->link, toregexp)) {
3904 INSN *next = (INSN *)iobj->link.next;
3905 if (OPERAND_AT(next, 1) == INT2FIX(1)) {
3906 VALUE src = OPERAND_AT(iobj, 0);
3907 int opt = (int)FIX2LONG(OPERAND_AT(next, 0));
3908 VALUE path = rb_iseq_path(iseq);
3909 int line = iobj->insn_info.line_no;
3910 VALUE errinfo = rb_errinfo();
3911 VALUE re = rb_reg_compile(src, opt, RSTRING_PTR(path), line);
3912 if (NIL_P(re)) {
3913 VALUE message = rb_attr_get(rb_errinfo(), idMesg);
3914 rb_set_errinfo(errinfo);
3915 COMPILE_ERROR(iseq, line, "%" PRIsVALUE, message);
3916 }
3917 else {
3918 RB_OBJ_SET_SHAREABLE(re);
3919 }
3920 RB_OBJ_WRITE(iseq, &OPERAND_AT(iobj, 0), re);
3921 ELEM_REMOVE(iobj->link.next);
3922 }
3923 }
3924 }
3925
3926 if (IS_INSN_ID(iobj, concatstrings)) {
3927 /*
3928 * concatstrings N
3929 * concatstrings M
3930 * =>
3931 * concatstrings N+M-1
3932 */
3933 LINK_ELEMENT *next = iobj->link.next;
3934 INSN *jump = 0;
3935 if (IS_INSN(next) && IS_INSN_ID(next, jump))
3936 next = get_destination_insn(jump = (INSN *)next);
3937 if (IS_INSN(next) && IS_INSN_ID(next, concatstrings)) {
3938 int n = FIX2INT(OPERAND_AT(iobj, 0)) + FIX2INT(OPERAND_AT(next, 0)) - 1;
3939 OPERAND_AT(iobj, 0) = INT2FIX(n);
3940 if (jump) {
3941 LABEL *label = ((LABEL *)OPERAND_AT(jump, 0));
3942 if (!--label->refcnt) {
3943 ELEM_REMOVE(&label->link);
3944 }
3945 else {
3946 label = NEW_LABEL(0);
3947 OPERAND_AT(jump, 0) = (VALUE)label;
3948 }
3949 label->refcnt++;
3950 ELEM_INSERT_NEXT(next, &label->link);
3951 CHECK(iseq_peephole_optimize(iseq, get_next_insn(jump), do_tailcallopt));
3952 }
3953 else {
3954 ELEM_REMOVE(next);
3955 }
3956 }
3957 }
3958
3959 if (do_tailcallopt &&
3960 (IS_INSN_ID(iobj, send) ||
3961 IS_INSN_ID(iobj, invokesuper))) {
3962 /*
3963 * send ...
3964 * leave
3965 * =>
3966 * send ..., ... | VM_CALL_TAILCALL, ...
3967 * leave # unreachable
3968 */
3969 INSN *piobj = NULL;
3970 if (iobj->link.next) {
3971 LINK_ELEMENT *next = iobj->link.next;
3972 do {
3973 if (!IS_INSN(next)) {
3974 next = next->next;
3975 continue;
3976 }
3977 switch (INSN_OF(next)) {
3978 case BIN(nop):
3979 next = next->next;
3980 break;
3981 case BIN(jump):
3982 /* if cond
3983 * return tailcall
3984 * end
3985 */
3986 next = get_destination_insn((INSN *)next);
3987 break;
3988 case BIN(leave):
3989 piobj = iobj;
3990 /* fall through */
3991 default:
3992 next = NULL;
3993 break;
3994 }
3995 } while (next);
3996 }
3997
3998 if (piobj) {
3999 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(piobj, 0);
4000 if (IS_INSN_ID(piobj, send) ||
4001 IS_INSN_ID(piobj, invokesuper)) {
4002 if (OPERAND_AT(piobj, 1) == 0) { /* no blockiseq */
4003 ci = ci_flag_set(iseq, ci, VM_CALL_TAILCALL);
4004 OPERAND_AT(piobj, 0) = (VALUE)ci;
4005 RB_OBJ_WRITTEN(iseq, Qundef, ci);
4006 }
4007 }
4008 else {
4009 ci = ci_flag_set(iseq, ci, VM_CALL_TAILCALL);
4010 OPERAND_AT(piobj, 0) = (VALUE)ci;
4011 RB_OBJ_WRITTEN(iseq, Qundef, ci);
4012 }
4013 }
4014 }
4015
4016 if (IS_INSN_ID(iobj, dup)) {
4017 if (IS_NEXT_INSN_ID(&iobj->link, setlocal)) {
4018 LINK_ELEMENT *set1 = iobj->link.next, *set2 = NULL;
4019
4020 /*
4021 * dup
4022 * setlocal x, y
4023 * setlocal x, y
4024 * =>
4025 * dup
4026 * setlocal x, y
4027 */
4028 if (IS_NEXT_INSN_ID(set1, setlocal)) {
4029 set2 = set1->next;
4030 if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
4031 OPERAND_AT(set1, 1) == OPERAND_AT(set2, 1)) {
4032 ELEM_REMOVE(set1);
4033 ELEM_REMOVE(&iobj->link);
4034 }
4035 }
4036
4037 /*
4038 * dup
4039 * setlocal x, y
4040 * dup
4041 * setlocal x, y
4042 * =>
4043 * dup
4044 * setlocal x, y
4045 */
4046 else if (IS_NEXT_INSN_ID(set1, dup) &&
4047 IS_NEXT_INSN_ID(set1->next, setlocal)) {
4048 set2 = set1->next->next;
4049 if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
4050 OPERAND_AT(set1, 1) == OPERAND_AT(set2, 1)) {
4051 ELEM_REMOVE(set1->next);
4052 ELEM_REMOVE(set2);
4053 }
4054 }
4055 }
4056 }
4057
4058 /*
4059 * getlocal x, y
4060 * dup
4061 * setlocal x, y
4062 * =>
4063 * dup
4064 */
4065 if (IS_INSN_ID(iobj, getlocal)) {
4066 LINK_ELEMENT *niobj = &iobj->link;
4067 if (IS_NEXT_INSN_ID(niobj, dup)) {
4068 niobj = niobj->next;
4069 }
4070 if (IS_NEXT_INSN_ID(niobj, setlocal)) {
4071 LINK_ELEMENT *set1 = niobj->next;
4072 if (OPERAND_AT(iobj, 0) == OPERAND_AT(set1, 0) &&
4073 OPERAND_AT(iobj, 1) == OPERAND_AT(set1, 1)) {
4074 ELEM_REMOVE(set1);
4075 ELEM_REMOVE(niobj);
4076 }
4077 }
4078 }
4079
4080 /*
4081 * opt_invokebuiltin_delegate
4082 * trace
4083 * leave
4084 * =>
4085 * opt_invokebuiltin_delegate_leave
4086 * trace
4087 * leave
4088 */
4089 if (IS_INSN_ID(iobj, opt_invokebuiltin_delegate)) {
4090 if (IS_TRACE(iobj->link.next)) {
4091 if (IS_NEXT_INSN_ID(iobj->link.next, leave)) {
4092 iobj->insn_id = BIN(opt_invokebuiltin_delegate_leave);
4093 const struct rb_builtin_function *bf = (const struct rb_builtin_function *)iobj->operands[0];
4094 if (iobj == (INSN *)list && bf->argc == 0 && (ISEQ_BODY(iseq)->builtin_attrs & BUILTIN_ATTR_LEAF)) {
4095 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_SINGLE_NOARG_LEAF;
4096 }
4097 }
4098 }
4099 }
4100
4101 /*
4102 * getblockparam
4103 * branchif / branchunless
4104 * =>
4105 * getblockparamproxy
4106 * branchif / branchunless
4107 */
4108 if (IS_INSN_ID(iobj, getblockparam)) {
4109 if (IS_NEXT_INSN_ID(&iobj->link, branchif) || IS_NEXT_INSN_ID(&iobj->link, branchunless)) {
4110 iobj->insn_id = BIN(getblockparamproxy);
4111 }
4112 }
4113
4114 if (IS_INSN_ID(iobj, splatarray) && OPERAND_AT(iobj, 0) == false) {
4115 LINK_ELEMENT *niobj = &iobj->link;
4116 if (IS_NEXT_INSN_ID(niobj, duphash)) {
4117 niobj = niobj->next;
4118 LINK_ELEMENT *siobj;
4119 unsigned int set_flags = 0, unset_flags = 0;
4120
4121 /*
4122 * Eliminate hash allocation for f(*a, kw: 1)
4123 *
4124 * splatarray false
4125 * duphash
4126 * send ARGS_SPLAT|KW_SPLAT|KW_SPLAT_MUT and not ARGS_BLOCKARG
4127 * =>
4128 * splatarray false
4129 * putobject
4130 * send ARGS_SPLAT|KW_SPLAT
4131 */
4132 if (IS_NEXT_INSN_ID(niobj, send)) {
4133 siobj = niobj->next;
4134 set_flags = VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT;
4135 unset_flags = VM_CALL_ARGS_BLOCKARG;
4136 }
4137 /*
4138 * Eliminate hash allocation for f(*a, kw: 1, &{arg,lvar,@iv})
4139 *
4140 * splatarray false
4141 * duphash
4142 * getlocal / getinstancevariable / getblockparamproxy
4143 * send ARGS_SPLAT|KW_SPLAT|KW_SPLAT_MUT|ARGS_BLOCKARG
4144 * =>
4145 * splatarray false
4146 * putobject
4147 * getlocal / getinstancevariable / getblockparamproxy
4148 * send ARGS_SPLAT|KW_SPLAT|ARGS_BLOCKARG
4149 */
4150 else if ((IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable) ||
4151 IS_NEXT_INSN_ID(niobj, getblockparamproxy)) && (IS_NEXT_INSN_ID(niobj->next, send))) {
4152 siobj = niobj->next->next;
4153 set_flags = VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT|VM_CALL_ARGS_BLOCKARG;
4154 }
4155
4156 if (set_flags) {
4157 const struct rb_callinfo *ci = (const struct rb_callinfo *)OPERAND_AT(siobj, 0);
4158 unsigned int flags = vm_ci_flag(ci);
4159 if ((flags & set_flags) == set_flags && !(flags & unset_flags)) {
4160 ((INSN*)niobj)->insn_id = BIN(putobject);
4161 RB_OBJ_WRITE(iseq, &OPERAND_AT(niobj, 0), RB_OBJ_SET_SHAREABLE(rb_hash_freeze(rb_hash_resurrect(OPERAND_AT(niobj, 0)))));
4162
4163 const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci),
4164 flags & ~VM_CALL_KW_SPLAT_MUT, vm_ci_argc(ci), vm_ci_kwarg(ci));
4165 RB_OBJ_WRITTEN(iseq, ci, nci);
4166 OPERAND_AT(siobj, 0) = (VALUE)nci;
4167 }
4168 }
4169 }
4170 }
4171
4172 return COMPILE_OK;
4173}
4174
4175static int
4176insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id)
4177{
4178 if (insn_id == BIN(opt_neq)) {
4179 VALUE original_ci = iobj->operands[0];
4180 VALUE new_ci = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE);
4181 insn_replace_with_operands(iseq, iobj, insn_id, 2, new_ci, original_ci);
4182 }
4183 else {
4184 iobj->insn_id = insn_id;
4185 iobj->operand_size = insn_len(insn_id) - 1;
4186 }
4187 iobj->insn_info.events |= RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN;
4188
4189 return COMPILE_OK;
4190}
4191
4192static int
4193iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
4194{
4195 if (IS_INSN_ID(iobj, newarray) && iobj->link.next &&
4196 IS_INSN(iobj->link.next)) {
4197 /*
4198 * [a, b, ...].max/min -> a, b, c, opt_newarray_send max/min
4199 */
4200 INSN *niobj = (INSN *)iobj->link.next;
4201 if (IS_INSN_ID(niobj, send)) {
4202 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(niobj, 0);
4203 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0) {
4204 VALUE method = INT2FIX(0);
4205 switch (vm_ci_mid(ci)) {
4206 case idMax:
4207 method = INT2FIX(VM_OPT_NEWARRAY_SEND_MAX);
4208 break;
4209 case idMin:
4210 method = INT2FIX(VM_OPT_NEWARRAY_SEND_MIN);
4211 break;
4212 case idHash:
4213 method = INT2FIX(VM_OPT_NEWARRAY_SEND_HASH);
4214 break;
4215 }
4216
4217 if (method != INT2FIX(0)) {
4218 VALUE num = iobj->operands[0];
4219 insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, num, method);
4220 ELEM_REMOVE(&niobj->link);
4221 return COMPILE_OK;
4222 }
4223 }
4224 }
4225 else if ((IS_INSN_ID(niobj, dupstring) || IS_INSN_ID(niobj, dupchilledstring) ||
4226 (IS_INSN_ID(niobj, putobject) && RB_TYPE_P(OPERAND_AT(niobj, 0), T_STRING))) &&
4227 IS_NEXT_INSN_ID(&niobj->link, send)) {
4228 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT((INSN *)niobj->link.next, 0);
4229 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idPack) {
4230 VALUE num = iobj->operands[0];
4231 insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 1), INT2FIX(VM_OPT_NEWARRAY_SEND_PACK));
4232 ELEM_REMOVE(&iobj->link);
4233 ELEM_REMOVE(niobj->link.next);
4234 ELEM_INSERT_NEXT(&niobj->link, &iobj->link);
4235 return COMPILE_OK;
4236 }
4237 }
4238 // newarray n, dupchilledstring "E", getlocal b, send :pack with {buffer: b}
4239 // -> dupchilledstring "E", getlocal b, opt_newarray_send n+2, :pack, :buffer
4240 else if ((IS_INSN_ID(niobj, dupstring) || IS_INSN_ID(niobj, dupchilledstring) ||
4241 (IS_INSN_ID(niobj, putobject) && RB_TYPE_P(OPERAND_AT(niobj, 0), T_STRING))) &&
4242 IS_NEXT_INSN_ID(&niobj->link, getlocal) &&
4243 (niobj->link.next && IS_NEXT_INSN_ID(niobj->link.next, send))) {
4244 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT((INSN *)(niobj->link.next)->next, 0);
4245 const struct rb_callinfo_kwarg *kwarg = vm_ci_kwarg(ci);
4246 if (vm_ci_mid(ci) == idPack && vm_ci_argc(ci) == 2 &&
4247 (kwarg && kwarg->keyword_len == 1 && kwarg->keywords[0] == rb_id2sym(idBuffer))) {
4248 VALUE num = iobj->operands[0];
4249 insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 2), INT2FIX(VM_OPT_NEWARRAY_SEND_PACK_BUFFER));
4250 // Remove the "send" insn.
4251 ELEM_REMOVE((niobj->link.next)->next);
4252 // Remove the modified insn from its original "newarray" position...
4253 ELEM_REMOVE(&iobj->link);
4254 // and insert it after the buffer insn.
4255 ELEM_INSERT_NEXT(niobj->link.next, &iobj->link);
4256 return COMPILE_OK;
4257 }
4258 }
4259
4260 // Break the "else if" chain since some prior checks abort after sub-ifs.
4261 // We already found "newarray". To match `[...].include?(arg)` we look for
4262 // the instruction(s) representing the argument followed by a "send".
4263 if ((IS_INSN_ID(niobj, dupstring) || IS_INSN_ID(niobj, dupchilledstring) ||
4264 IS_INSN_ID(niobj, putobject) ||
4265 IS_INSN_ID(niobj, putself) ||
4266 IS_INSN_ID(niobj, getlocal) ||
4267 IS_INSN_ID(niobj, getinstancevariable)) &&
4268 IS_NEXT_INSN_ID(&niobj->link, send)) {
4269
4270 LINK_ELEMENT *sendobj = &(niobj->link); // Below we call ->next;
4271 const struct rb_callinfo *ci;
4272 // Allow any number (0 or more) of simple method calls on the argument
4273 // (as in `[...].include?(arg.method1.method2)`.
4274 do {
4275 sendobj = sendobj->next;
4276 ci = (struct rb_callinfo *)OPERAND_AT(sendobj, 0);
4277 } while (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && IS_NEXT_INSN_ID(sendobj, send));
4278
4279 // If this send is for .include? with one arg we can do our opt.
4280 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idIncludeP) {
4281 VALUE num = iobj->operands[0];
4282 INSN *sendins = (INSN *)sendobj;
4283 insn_replace_with_operands(iseq, sendins, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 1), INT2FIX(VM_OPT_NEWARRAY_SEND_INCLUDE_P));
4284 // Remove the original "newarray" insn.
4285 ELEM_REMOVE(&iobj->link);
4286 return COMPILE_OK;
4287 }
4288 }
4289 }
4290
4291 /*
4292 * duparray [...]
4293 * some insn for the arg...
4294 * send <calldata!mid:include?, argc:1, ARGS_SIMPLE>, nil
4295 * =>
4296 * arg insn...
4297 * opt_duparray_send [...], :include?, 1
4298 */
4299 if (IS_INSN_ID(iobj, duparray) && iobj->link.next && IS_INSN(iobj->link.next)) {
4300 INSN *niobj = (INSN *)iobj->link.next;
4301 if ((IS_INSN_ID(niobj, getlocal) ||
4302 IS_INSN_ID(niobj, getinstancevariable) ||
4303 IS_INSN_ID(niobj, putself)) &&
4304 IS_NEXT_INSN_ID(&niobj->link, send)) {
4305
4306 LINK_ELEMENT *sendobj = &(niobj->link); // Below we call ->next;
4307 const struct rb_callinfo *ci;
4308 // Allow any number (0 or more) of simple method calls on the argument
4309 // (as in `[...].include?(arg.method1.method2)`.
4310 do {
4311 sendobj = sendobj->next;
4312 ci = (struct rb_callinfo *)OPERAND_AT(sendobj, 0);
4313 } while (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && IS_NEXT_INSN_ID(sendobj, send));
4314
4315 if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idIncludeP) {
4316 // Move the array arg from duparray to opt_duparray_send.
4317 VALUE ary = iobj->operands[0];
4319
4320 INSN *sendins = (INSN *)sendobj;
4321 insn_replace_with_operands(iseq, sendins, BIN(opt_duparray_send), 3, ary, rb_id2sym(idIncludeP), INT2FIX(1));
4322
4323 // Remove the duparray insn.
4324 ELEM_REMOVE(&iobj->link);
4325 return COMPILE_OK;
4326 }
4327 }
4328 }
4329
4330
4331 if (IS_INSN_ID(iobj, send)) {
4332 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
4333 const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(iobj, 1);
4334
4335#define SP_INSN(opt) insn_set_specialized_instruction(iseq, iobj, BIN(opt_##opt))
4336 if (vm_ci_simple(ci)) {
4337 switch (vm_ci_argc(ci)) {
4338 case 0:
4339 switch (vm_ci_mid(ci)) {
4340 case idLength: SP_INSN(length); return COMPILE_OK;
4341 case idSize: SP_INSN(size); return COMPILE_OK;
4342 case idEmptyP: SP_INSN(empty_p);return COMPILE_OK;
4343 case idNilP: SP_INSN(nil_p); return COMPILE_OK;
4344 case idSucc: SP_INSN(succ); return COMPILE_OK;
4345 case idNot: SP_INSN(not); return COMPILE_OK;
4346 }
4347 break;
4348 case 1:
4349 switch (vm_ci_mid(ci)) {
4350 case idPLUS: SP_INSN(plus); return COMPILE_OK;
4351 case idMINUS: SP_INSN(minus); return COMPILE_OK;
4352 case idMULT: SP_INSN(mult); return COMPILE_OK;
4353 case idDIV: SP_INSN(div); return COMPILE_OK;
4354 case idMOD: SP_INSN(mod); return COMPILE_OK;
4355 case idEq: SP_INSN(eq); return COMPILE_OK;
4356 case idNeq: SP_INSN(neq); return COMPILE_OK;
4357 case idEqTilde:SP_INSN(regexpmatch2);return COMPILE_OK;
4358 case idLT: SP_INSN(lt); return COMPILE_OK;
4359 case idLE: SP_INSN(le); return COMPILE_OK;
4360 case idGT: SP_INSN(gt); return COMPILE_OK;
4361 case idGE: SP_INSN(ge); return COMPILE_OK;
4362 case idLTLT: SP_INSN(ltlt); return COMPILE_OK;
4363 case idAREF: SP_INSN(aref); return COMPILE_OK;
4364 case idAnd: SP_INSN(and); return COMPILE_OK;
4365 case idOr: SP_INSN(or); return COMPILE_OK;
4366 }
4367 break;
4368 case 2:
4369 switch (vm_ci_mid(ci)) {
4370 case idASET: SP_INSN(aset); return COMPILE_OK;
4371 }
4372 break;
4373 }
4374 }
4375
4376 if ((vm_ci_flag(ci) & (VM_CALL_ARGS_BLOCKARG | VM_CALL_FORWARDING)) == 0 && blockiseq == NULL) {
4377 iobj->insn_id = BIN(opt_send_without_block);
4378 iobj->operand_size = insn_len(iobj->insn_id) - 1;
4379 }
4380 }
4381#undef SP_INSN
4382
4383 return COMPILE_OK;
4384}
4385
4386static inline int
4387tailcallable_p(rb_iseq_t *iseq)
4388{
4389 switch (ISEQ_BODY(iseq)->type) {
4390 case ISEQ_TYPE_TOP:
4391 case ISEQ_TYPE_EVAL:
4392 case ISEQ_TYPE_MAIN:
4393 /* not tail callable because cfp will be over popped */
4394 case ISEQ_TYPE_RESCUE:
4395 case ISEQ_TYPE_ENSURE:
4396 /* rescue block can't tail call because of errinfo */
4397 return FALSE;
4398 default:
4399 return TRUE;
4400 }
4401}
4402
4403static int
4404iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
4405{
4406 LINK_ELEMENT *list;
4407 const int do_peepholeopt = ISEQ_COMPILE_DATA(iseq)->option->peephole_optimization;
4408 const int do_tailcallopt = tailcallable_p(iseq) &&
4409 ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization;
4410 const int do_si = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction;
4411 const int do_ou = ISEQ_COMPILE_DATA(iseq)->option->operands_unification;
4412 const int do_without_ints = ISEQ_BODY(iseq)->builtin_attrs & BUILTIN_ATTR_WITHOUT_INTERRUPTS;
4413 int rescue_level = 0;
4414 int tailcallopt = do_tailcallopt;
4415
4416 list = FIRST_ELEMENT(anchor);
4417
4418 int do_block_optimization = 0;
4419 LABEL * block_loop_label = NULL;
4420
4421 // If we're optimizing a block
4422 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_BLOCK) {
4423 do_block_optimization = 1;
4424
4425 // If the block starts with a nop and a label,
4426 // record the label so we can detect if it's a jump target
4427 LINK_ELEMENT * le = FIRST_ELEMENT(anchor)->next;
4428 if (IS_INSN(le) && IS_INSN_ID((INSN *)le, nop) && IS_LABEL(le->next)) {
4429 block_loop_label = (LABEL *)le->next;
4430 }
4431 }
4432
4433 while (list) {
4434 if (IS_INSN(list)) {
4435 if (do_peepholeopt) {
4436 iseq_peephole_optimize(iseq, list, tailcallopt);
4437 }
4438 if (do_si) {
4439 iseq_specialized_instruction(iseq, (INSN *)list);
4440 }
4441 if (do_ou) {
4442 insn_operands_unification((INSN *)list);
4443 }
4444
4445 if (do_without_ints) {
4446 INSN *item = (INSN *)list;
4447 if (IS_INSN_ID(item, jump)) {
4448 item->insn_id = BIN(jump_without_ints);
4449 }
4450 else if (IS_INSN_ID(item, branchif)) {
4451 item->insn_id = BIN(branchif_without_ints);
4452 }
4453 else if (IS_INSN_ID(item, branchunless)) {
4454 item->insn_id = BIN(branchunless_without_ints);
4455 }
4456 else if (IS_INSN_ID(item, branchnil)) {
4457 item->insn_id = BIN(branchnil_without_ints);
4458 }
4459 }
4460
4461 if (do_block_optimization) {
4462 INSN * item = (INSN *)list;
4463 // Give up if there is a throw
4464 if (IS_INSN_ID(item, throw)) {
4465 do_block_optimization = 0;
4466 }
4467 else {
4468 // If the instruction has a jump target, check if the
4469 // jump target is the block loop label
4470 const char *types = insn_op_types(item->insn_id);
4471 for (int j = 0; types[j]; j++) {
4472 if (types[j] == TS_OFFSET) {
4473 // If the jump target is equal to the block loop
4474 // label, then we can't do the optimization because
4475 // the leading `nop` instruction fires the block
4476 // entry tracepoint
4477 LABEL * target = (LABEL *)OPERAND_AT(item, j);
4478 if (target == block_loop_label) {
4479 do_block_optimization = 0;
4480 }
4481 }
4482 }
4483 }
4484 }
4485 }
4486 if (IS_LABEL(list)) {
4487 switch (((LABEL *)list)->rescued) {
4488 case LABEL_RESCUE_BEG:
4489 rescue_level++;
4490 tailcallopt = FALSE;
4491 break;
4492 case LABEL_RESCUE_END:
4493 if (!--rescue_level) tailcallopt = do_tailcallopt;
4494 break;
4495 }
4496 }
4497 list = list->next;
4498 }
4499
4500 if (do_block_optimization) {
4501 LINK_ELEMENT * le = FIRST_ELEMENT(anchor)->next;
4502 if (IS_INSN(le) && IS_INSN_ID((INSN *)le, nop)) {
4503 ELEM_REMOVE(le);
4504 }
4505 }
4506 return COMPILE_OK;
4507}
4508
4509#if OPT_INSTRUCTIONS_UNIFICATION
4510static INSN *
4511new_unified_insn(rb_iseq_t *iseq,
4512 int insn_id, int size, LINK_ELEMENT *seq_list)
4513{
4514 INSN *iobj = 0;
4515 LINK_ELEMENT *list = seq_list;
4516 int i, argc = 0;
4517 VALUE *operands = 0, *ptr = 0;
4518
4519
4520 /* count argc */
4521 for (i = 0; i < size; i++) {
4522 iobj = (INSN *)list;
4523 argc += iobj->operand_size;
4524 list = list->next;
4525 }
4526
4527 if (argc > 0) {
4528 ptr = operands = compile_data_alloc2_type(iseq, VALUE, argc);
4529 }
4530
4531 /* copy operands */
4532 list = seq_list;
4533 for (i = 0; i < size; i++) {
4534 iobj = (INSN *)list;
4535 MEMCPY(ptr, iobj->operands, VALUE, iobj->operand_size);
4536 ptr += iobj->operand_size;
4537 list = list->next;
4538 }
4539
4540 return new_insn_core(iseq, iobj->insn_info.line_no, iobj->insn_info.node_id, insn_id, argc, operands);
4541}
4542#endif
4543
4544/*
4545 * This scheme can get more performance if do this optimize with
4546 * label address resolving.
4547 * It's future work (if compile time was bottle neck).
4548 */
4549static int
4550iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
4551{
4552#if OPT_INSTRUCTIONS_UNIFICATION
4553 LINK_ELEMENT *list;
4554 INSN *iobj, *niobj;
4555 int id, k;
4556 intptr_t j;
4557
4558 list = FIRST_ELEMENT(anchor);
4559 while (list) {
4560 if (IS_INSN(list)) {
4561 iobj = (INSN *)list;
4562 id = iobj->insn_id;
4563 if (unified_insns_data[id] != 0) {
4564 const int *const *entry = unified_insns_data[id];
4565 for (j = 1; j < (intptr_t)entry[0]; j++) {
4566 const int *unified = entry[j];
4567 LINK_ELEMENT *li = list->next;
4568 for (k = 2; k < unified[1]; k++) {
4569 if (!IS_INSN(li) ||
4570 ((INSN *)li)->insn_id != unified[k]) {
4571 goto miss;
4572 }
4573 li = li->next;
4574 }
4575 /* matched */
4576 niobj =
4577 new_unified_insn(iseq, unified[0], unified[1] - 1,
4578 list);
4579
4580 /* insert to list */
4581 niobj->link.prev = (LINK_ELEMENT *)iobj->link.prev;
4582 niobj->link.next = li;
4583 if (li) {
4584 li->prev = (LINK_ELEMENT *)niobj;
4585 }
4586
4587 list->prev->next = (LINK_ELEMENT *)niobj;
4588 list = (LINK_ELEMENT *)niobj;
4589 break;
4590 miss:;
4591 }
4592 }
4593 }
4594 list = list->next;
4595 }
4596#endif
4597 return COMPILE_OK;
4598}
4599
4600static int
4601all_string_result_p(const NODE *node)
4602{
4603 if (!node) return FALSE;
4604 switch (nd_type(node)) {
4605 case NODE_STR: case NODE_DSTR: case NODE_FILE:
4606 return TRUE;
4607 case NODE_IF: case NODE_UNLESS:
4608 if (!RNODE_IF(node)->nd_body || !RNODE_IF(node)->nd_else) return FALSE;
4609 if (all_string_result_p(RNODE_IF(node)->nd_body))
4610 return all_string_result_p(RNODE_IF(node)->nd_else);
4611 return FALSE;
4612 case NODE_AND: case NODE_OR:
4613 if (!RNODE_AND(node)->nd_2nd)
4614 return all_string_result_p(RNODE_AND(node)->nd_1st);
4615 if (!all_string_result_p(RNODE_AND(node)->nd_1st))
4616 return FALSE;
4617 return all_string_result_p(RNODE_AND(node)->nd_2nd);
4618 default:
4619 return FALSE;
4620 }
4621}
4622
4624 rb_iseq_t *const iseq;
4625 LINK_ANCHOR *const ret;
4626 VALUE lit;
4627 const NODE *lit_node;
4628 int cnt;
4629 int dregx;
4630};
4631
4632static int
4633append_dstr_fragment(struct dstr_ctxt *args, const NODE *const node, rb_parser_string_t *str)
4634{
4635 VALUE s = rb_str_new_mutable_parser_string(str);
4636 if (args->dregx) {
4637 VALUE error = rb_reg_check_preprocess(s);
4638 if (!NIL_P(error)) {
4639 COMPILE_ERROR(args->iseq, nd_line(node), "%" PRIsVALUE, error);
4640 return COMPILE_NG;
4641 }
4642 }
4643 if (NIL_P(args->lit)) {
4644 args->lit = s;
4645 args->lit_node = node;
4646 }
4647 else {
4648 rb_str_buf_append(args->lit, s);
4649 }
4650 return COMPILE_OK;
4651}
4652
4653static void
4654flush_dstr_fragment(struct dstr_ctxt *args)
4655{
4656 if (!NIL_P(args->lit)) {
4657 rb_iseq_t *iseq = args->iseq;
4658 VALUE lit = args->lit;
4659 args->lit = Qnil;
4660 lit = rb_fstring(lit);
4661 ADD_INSN1(args->ret, args->lit_node, putobject, lit);
4662 RB_OBJ_WRITTEN(args->iseq, Qundef, lit);
4663 args->cnt++;
4664 }
4665}
4666
4667static int
4668compile_dstr_fragments_0(struct dstr_ctxt *args, const NODE *const node)
4669{
4670 const struct RNode_LIST *list = RNODE_DSTR(node)->nd_next;
4671 rb_parser_string_t *str = RNODE_DSTR(node)->string;
4672
4673 if (str) {
4674 CHECK(append_dstr_fragment(args, node, str));
4675 }
4676
4677 while (list) {
4678 const NODE *const head = list->nd_head;
4679 if (nd_type_p(head, NODE_STR)) {
4680 CHECK(append_dstr_fragment(args, node, RNODE_STR(head)->string));
4681 }
4682 else if (nd_type_p(head, NODE_DSTR)) {
4683 CHECK(compile_dstr_fragments_0(args, head));
4684 }
4685 else {
4686 flush_dstr_fragment(args);
4687 rb_iseq_t *iseq = args->iseq;
4688 CHECK(COMPILE(args->ret, "each string", head));
4689 args->cnt++;
4690 }
4691 list = (struct RNode_LIST *)list->nd_next;
4692 }
4693 return COMPILE_OK;
4694}
4695
4696static int
4697compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int *cntp, int dregx)
4698{
4699 struct dstr_ctxt args = {
4700 .iseq = iseq, .ret = ret,
4701 .lit = Qnil, .lit_node = NULL,
4702 .cnt = 0, .dregx = dregx,
4703 };
4704 CHECK(compile_dstr_fragments_0(&args, node));
4705 flush_dstr_fragment(&args);
4706
4707 *cntp = args.cnt;
4708
4709 return COMPILE_OK;
4710}
4711
4712static int
4713compile_block(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped)
4714{
4715 while (node && nd_type_p(node, NODE_BLOCK)) {
4716 CHECK(COMPILE_(ret, "BLOCK body", RNODE_BLOCK(node)->nd_head,
4717 (RNODE_BLOCK(node)->nd_next ? 1 : popped)));
4718 node = RNODE_BLOCK(node)->nd_next;
4719 }
4720 if (node) {
4721 CHECK(COMPILE_(ret, "BLOCK next", RNODE_BLOCK(node)->nd_next, popped));
4722 }
4723 return COMPILE_OK;
4724}
4725
4726static int
4727compile_dstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
4728{
4729 int cnt;
4730 if (!RNODE_DSTR(node)->nd_next) {
4731 VALUE lit = rb_node_dstr_string_val(node);
4732 ADD_INSN1(ret, node, dupstring, lit);
4733 RB_OBJ_SET_SHAREABLE(lit);
4734 RB_OBJ_WRITTEN(iseq, Qundef, lit);
4735 }
4736 else {
4737 CHECK(compile_dstr_fragments(iseq, ret, node, &cnt, FALSE));
4738 ADD_INSN1(ret, node, concatstrings, INT2FIX(cnt));
4739 }
4740 return COMPILE_OK;
4741}
4742
4743static int
4744compile_dregx(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
4745{
4746 int cnt;
4747 int cflag = (int)RNODE_DREGX(node)->as.nd_cflag;
4748
4749 if (!RNODE_DREGX(node)->nd_next) {
4750 if (!popped) {
4751 VALUE src = rb_node_dregx_string_val(node);
4752 VALUE match = rb_reg_compile(src, cflag, NULL, 0);
4753 RB_OBJ_SET_SHAREABLE(match);
4754 ADD_INSN1(ret, node, putobject, match);
4755 RB_OBJ_WRITTEN(iseq, Qundef, match);
4756 }
4757 return COMPILE_OK;
4758 }
4759
4760 CHECK(compile_dstr_fragments(iseq, ret, node, &cnt, TRUE));
4761 ADD_INSN2(ret, node, toregexp, INT2FIX(cflag), INT2FIX(cnt));
4762
4763 if (popped) {
4764 ADD_INSN(ret, node, pop);
4765 }
4766
4767 return COMPILE_OK;
4768}
4769
4770static int
4771compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int again,
4772 LABEL *then_label, LABEL *else_label)
4773{
4774 const int line = nd_line(node);
4775 LABEL *lend = NEW_LABEL(line);
4776 rb_num_t cnt = ISEQ_FLIP_CNT_INCREMENT(ISEQ_BODY(iseq)->local_iseq)
4777 + VM_SVAR_FLIPFLOP_START;
4778 VALUE key = INT2FIX(cnt);
4779
4780 ADD_INSN2(ret, node, getspecial, key, INT2FIX(0));
4781 ADD_INSNL(ret, node, branchif, lend);
4782
4783 /* *flip == 0 */
4784 CHECK(COMPILE(ret, "flip2 beg", RNODE_FLIP2(node)->nd_beg));
4785 ADD_INSNL(ret, node, branchunless, else_label);
4786 ADD_INSN1(ret, node, putobject, Qtrue);
4787 ADD_INSN1(ret, node, setspecial, key);
4788 if (!again) {
4789 ADD_INSNL(ret, node, jump, then_label);
4790 }
4791
4792 /* *flip == 1 */
4793 ADD_LABEL(ret, lend);
4794 CHECK(COMPILE(ret, "flip2 end", RNODE_FLIP2(node)->nd_end));
4795 ADD_INSNL(ret, node, branchunless, then_label);
4796 ADD_INSN1(ret, node, putobject, Qfalse);
4797 ADD_INSN1(ret, node, setspecial, key);
4798 ADD_INSNL(ret, node, jump, then_label);
4799
4800 return COMPILE_OK;
4801}
4802
4803static int
4804compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *cond,
4805 LABEL *then_label, LABEL *else_label);
4806
4807#define COMPILE_SINGLE 2
4808static int
4809compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cond,
4810 LABEL *then_label, LABEL *else_label)
4811{
4812 DECL_ANCHOR(seq);
4813 INIT_ANCHOR(seq);
4814 LABEL *label = NEW_LABEL(nd_line(cond));
4815 if (!then_label) then_label = label;
4816 else if (!else_label) else_label = label;
4817
4818 CHECK(compile_branch_condition(iseq, seq, cond, then_label, else_label));
4819
4820 if (LIST_INSN_SIZE_ONE(seq)) {
4821 INSN *insn = (INSN *)ELEM_FIRST_INSN(FIRST_ELEMENT(seq));
4822 if (insn->insn_id == BIN(jump) && (LABEL *)(insn->operands[0]) == label)
4823 return COMPILE_OK;
4824 }
4825 if (!label->refcnt) {
4826 return COMPILE_SINGLE;
4827 }
4828 ADD_LABEL(seq, label);
4829 ADD_SEQ(ret, seq);
4830 return COMPILE_OK;
4831}
4832
4833static int
4834compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *cond,
4835 LABEL *then_label, LABEL *else_label)
4836{
4837 int ok;
4838 DECL_ANCHOR(ignore);
4839
4840 again:
4841 switch (nd_type(cond)) {
4842 case NODE_AND:
4843 CHECK(ok = compile_logical(iseq, ret, RNODE_AND(cond)->nd_1st, NULL, else_label));
4844 cond = RNODE_AND(cond)->nd_2nd;
4845 if (ok == COMPILE_SINGLE) {
4846 ADD_INSNL(ret, cond, jump, else_label);
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 ADD_INSNL(ret, cond, jump, then_label);
4857 INIT_ANCHOR(ignore);
4858 ret = ignore;
4859 else_label = NEW_LABEL(nd_line(cond));
4860 }
4861 goto again;
4862 case NODE_SYM:
4863 case NODE_LINE:
4864 case NODE_FILE:
4865 case NODE_ENCODING:
4866 case NODE_INTEGER: /* NODE_INTEGER is always true */
4867 case NODE_FLOAT: /* NODE_FLOAT is always true */
4868 case NODE_RATIONAL: /* NODE_RATIONAL is always true */
4869 case NODE_IMAGINARY: /* NODE_IMAGINARY is always true */
4870 case NODE_TRUE:
4871 case NODE_STR:
4872 case NODE_REGX:
4873 case NODE_ZLIST:
4874 case NODE_LAMBDA:
4875 /* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
4876 ADD_INSNL(ret, cond, jump, then_label);
4877 return COMPILE_OK;
4878 case NODE_FALSE:
4879 case NODE_NIL:
4880 /* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
4881 ADD_INSNL(ret, cond, jump, else_label);
4882 return COMPILE_OK;
4883 case NODE_LIST:
4884 case NODE_ARGSCAT:
4885 case NODE_DREGX:
4886 case NODE_DSTR:
4887 CHECK(COMPILE_POPPED(ret, "branch condition", cond));
4888 ADD_INSNL(ret, cond, jump, then_label);
4889 return COMPILE_OK;
4890 case NODE_FLIP2:
4891 CHECK(compile_flip_flop(iseq, ret, cond, TRUE, then_label, else_label));
4892 return COMPILE_OK;
4893 case NODE_FLIP3:
4894 CHECK(compile_flip_flop(iseq, ret, cond, FALSE, then_label, else_label));
4895 return COMPILE_OK;
4896 case NODE_DEFINED:
4897 CHECK(compile_defined_expr(iseq, ret, cond, Qfalse, ret == ignore));
4898 break;
4899 default:
4900 {
4901 DECL_ANCHOR(cond_seq);
4902 INIT_ANCHOR(cond_seq);
4903
4904 CHECK(COMPILE(cond_seq, "branch condition", cond));
4905
4906 if (LIST_INSN_SIZE_ONE(cond_seq)) {
4907 INSN *insn = (INSN *)ELEM_FIRST_INSN(FIRST_ELEMENT(cond_seq));
4908 if (insn->insn_id == BIN(putobject)) {
4909 if (RTEST(insn->operands[0])) {
4910 ADD_INSNL(ret, cond, jump, then_label);
4911 // maybe unreachable
4912 return COMPILE_OK;
4913 }
4914 else {
4915 ADD_INSNL(ret, cond, jump, else_label);
4916 return COMPILE_OK;
4917 }
4918 }
4919 }
4920 ADD_SEQ(ret, cond_seq);
4921 }
4922 break;
4923 }
4924
4925 ADD_INSNL(ret, cond, branchunless, else_label);
4926 ADD_INSNL(ret, cond, jump, then_label);
4927 return COMPILE_OK;
4928}
4929
4930#define HASH_BRACE 1
4931
4932static int
4933keyword_node_p(const NODE *const node)
4934{
4935 return nd_type_p(node, NODE_HASH) && (RNODE_HASH(node)->nd_brace & HASH_BRACE) != HASH_BRACE;
4936}
4937
4938static VALUE
4939get_symbol_value(rb_iseq_t *iseq, const NODE *node)
4940{
4941 switch (nd_type(node)) {
4942 case NODE_SYM:
4943 return rb_node_sym_string_val(node);
4944 default:
4945 UNKNOWN_NODE("get_symbol_value", node, Qnil);
4946 }
4947}
4948
4949static VALUE
4950node_hash_unique_key_index(rb_iseq_t *iseq, rb_node_hash_t *node_hash, int *count_ptr)
4951{
4952 NODE *node = node_hash->nd_head;
4953 VALUE hash = rb_hash_new();
4954 VALUE ary = rb_ary_new();
4955
4956 for (int i = 0; node != NULL; i++, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
4957 VALUE key = get_symbol_value(iseq, RNODE_LIST(node)->nd_head);
4958 VALUE idx = rb_hash_aref(hash, key);
4959 if (!NIL_P(idx)) {
4960 rb_ary_store(ary, FIX2INT(idx), Qfalse);
4961 (*count_ptr)--;
4962 }
4963 rb_hash_aset(hash, key, INT2FIX(i));
4964 rb_ary_store(ary, i, Qtrue);
4965 (*count_ptr)++;
4966 }
4967
4968 return ary;
4969}
4970
4971static int
4972compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
4973 const NODE *const root_node,
4974 struct rb_callinfo_kwarg **const kw_arg_ptr,
4975 unsigned int *flag)
4976{
4977 RUBY_ASSERT(nd_type_p(root_node, NODE_HASH));
4978 RUBY_ASSERT(kw_arg_ptr != NULL);
4979 RUBY_ASSERT(flag != NULL);
4980
4981 if (RNODE_HASH(root_node)->nd_head && nd_type_p(RNODE_HASH(root_node)->nd_head, NODE_LIST)) {
4982 const NODE *node = RNODE_HASH(root_node)->nd_head;
4983 int seen_nodes = 0;
4984
4985 while (node) {
4986 const NODE *key_node = RNODE_LIST(node)->nd_head;
4987 seen_nodes++;
4988
4989 RUBY_ASSERT(nd_type_p(node, NODE_LIST));
4990 if (key_node && nd_type_p(key_node, NODE_SYM)) {
4991 /* can be keywords */
4992 }
4993 else {
4994 if (flag) {
4995 *flag |= VM_CALL_KW_SPLAT;
4996 if (seen_nodes > 1 || RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
4997 /* A new hash will be created for the keyword arguments
4998 * in this case, so mark the method as passing mutable
4999 * keyword splat.
5000 */
5001 *flag |= VM_CALL_KW_SPLAT_MUT;
5002 }
5003 }
5004 return FALSE;
5005 }
5006 node = RNODE_LIST(node)->nd_next; /* skip value node */
5007 node = RNODE_LIST(node)->nd_next;
5008 }
5009
5010 /* may be keywords */
5011 node = RNODE_HASH(root_node)->nd_head;
5012 {
5013 int len = 0;
5014 VALUE key_index = node_hash_unique_key_index(iseq, RNODE_HASH(root_node), &len);
5015 struct rb_callinfo_kwarg *kw_arg =
5016 rb_xmalloc_mul_add(len, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
5017 VALUE *keywords = kw_arg->keywords;
5018 int i = 0;
5019 int j = 0;
5020 kw_arg->references = 0;
5021 kw_arg->keyword_len = len;
5022
5023 *kw_arg_ptr = kw_arg;
5024
5025 for (i=0; node != NULL; i++, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
5026 const NODE *key_node = RNODE_LIST(node)->nd_head;
5027 const NODE *val_node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head;
5028 int popped = TRUE;
5029 if (rb_ary_entry(key_index, i)) {
5030 keywords[j] = get_symbol_value(iseq, key_node);
5031 j++;
5032 popped = FALSE;
5033 }
5034 NO_CHECK(COMPILE_(ret, "keyword values", val_node, popped));
5035 }
5036 RUBY_ASSERT(j == len);
5037 return TRUE;
5038 }
5039 }
5040 return FALSE;
5041}
5042
5043static int
5044compile_args(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, NODE **kwnode_ptr)
5045{
5046 int len = 0;
5047
5048 for (; node; len++, node = RNODE_LIST(node)->nd_next) {
5049 if (CPDEBUG > 0) {
5050 EXPECT_NODE("compile_args", node, NODE_LIST, -1);
5051 }
5052
5053 if (RNODE_LIST(node)->nd_next == NULL && keyword_node_p(RNODE_LIST(node)->nd_head)) { /* last node is kwnode */
5054 *kwnode_ptr = RNODE_LIST(node)->nd_head;
5055 }
5056 else {
5057 RUBY_ASSERT(!keyword_node_p(RNODE_LIST(node)->nd_head));
5058 NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, FALSE));
5059 }
5060 }
5061
5062 return len;
5063}
5064
5065static inline bool
5066frozen_string_literal_p(const rb_iseq_t *iseq)
5067{
5068 return ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal > 0;
5069}
5070
5071static inline bool
5072static_literal_node_p(const NODE *node, const rb_iseq_t *iseq, bool hash_key)
5073{
5074 switch (nd_type(node)) {
5075 case NODE_SYM:
5076 case NODE_REGX:
5077 case NODE_LINE:
5078 case NODE_ENCODING:
5079 case NODE_INTEGER:
5080 case NODE_FLOAT:
5081 case NODE_RATIONAL:
5082 case NODE_IMAGINARY:
5083 case NODE_NIL:
5084 case NODE_TRUE:
5085 case NODE_FALSE:
5086 return TRUE;
5087 case NODE_STR:
5088 case NODE_FILE:
5089 return hash_key || frozen_string_literal_p(iseq);
5090 default:
5091 return FALSE;
5092 }
5093}
5094
5095static inline VALUE
5096static_literal_value(const NODE *node, rb_iseq_t *iseq)
5097{
5098 switch (nd_type(node)) {
5099 case NODE_INTEGER:
5100 {
5101 VALUE lit = rb_node_integer_literal_val(node);
5102 if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit);
5103 return lit;
5104 }
5105 case NODE_FLOAT:
5106 {
5107 VALUE lit = rb_node_float_literal_val(node);
5108 if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit);
5109 return lit;
5110 }
5111 case NODE_RATIONAL:
5112 return rb_ractor_make_shareable(rb_node_rational_literal_val(node));
5113 case NODE_IMAGINARY:
5114 return rb_ractor_make_shareable(rb_node_imaginary_literal_val(node));
5115 case NODE_NIL:
5116 return Qnil;
5117 case NODE_TRUE:
5118 return Qtrue;
5119 case NODE_FALSE:
5120 return Qfalse;
5121 case NODE_SYM:
5122 return rb_node_sym_string_val(node);
5123 case NODE_REGX:
5124 return RB_OBJ_SET_SHAREABLE(rb_node_regx_string_val(node));
5125 case NODE_LINE:
5126 return rb_node_line_lineno_val(node);
5127 case NODE_ENCODING:
5128 return rb_node_encoding_val(node);
5129 case NODE_FILE:
5130 case NODE_STR:
5131 if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
5132 VALUE lit = get_string_value(node);
5133 VALUE str = rb_str_with_debug_created_info(lit, rb_iseq_path(iseq), (int)nd_line(node));
5134 RB_OBJ_SET_SHAREABLE(str);
5135 return str;
5136 }
5137 else {
5138 return get_string_value(node);
5139 }
5140 default:
5141 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
5142 }
5143}
5144
5145static int
5146compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped, bool first_chunk)
5147{
5148 const NODE *line_node = node;
5149
5150 if (nd_type_p(node, NODE_ZLIST)) {
5151 if (!popped) {
5152 ADD_INSN1(ret, line_node, newarray, INT2FIX(0));
5153 }
5154 return 0;
5155 }
5156
5157 EXPECT_NODE("compile_array", node, NODE_LIST, -1);
5158
5159 if (popped) {
5160 for (; node; node = RNODE_LIST(node)->nd_next) {
5161 NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, popped));
5162 }
5163 return 1;
5164 }
5165
5166 /* Compilation of an array literal.
5167 * The following code is essentially the same as:
5168 *
5169 * for (int count = 0; node; count++; node->nd_next) {
5170 * compile(node->nd_head);
5171 * }
5172 * ADD_INSN(newarray, count);
5173 *
5174 * However, there are three points.
5175 *
5176 * - The code above causes stack overflow for a big string literal.
5177 * The following limits the stack length up to max_stack_len.
5178 *
5179 * [x1,x2,...,x10000] =>
5180 * push x1 ; push x2 ; ...; push x256; newarray 256;
5181 * push x257; push x258; ...; push x512; pushtoarray 256;
5182 * push x513; push x514; ...; push x768; pushtoarray 256;
5183 * ...
5184 *
5185 * - Long subarray can be optimized by pre-allocating a hidden array.
5186 *
5187 * [1,2,3,...,100] =>
5188 * duparray [1,2,3,...,100]
5189 *
5190 * [x, 1,2,3,...,100, z] =>
5191 * push x; newarray 1;
5192 * putobject [1,2,3,...,100] (<- hidden array); concattoarray;
5193 * push z; pushtoarray 1;
5194 *
5195 * - If the last element is a keyword, pushtoarraykwsplat should be emitted
5196 * to only push it onto the array if it is not empty
5197 * (Note: a keyword is NODE_HASH which is not static_literal_node_p.)
5198 *
5199 * [1,2,3,**kw] =>
5200 * putobject 1; putobject 2; putobject 3; newarray 3; ...; pushtoarraykwsplat kw
5201 */
5202
5203 const int max_stack_len = 0x100;
5204 const int min_tmp_ary_len = 0x40;
5205 int stack_len = 0;
5206
5207 /* Either create a new array, or push to the existing array */
5208#define FLUSH_CHUNK \
5209 if (stack_len) { \
5210 if (first_chunk) ADD_INSN1(ret, line_node, newarray, INT2FIX(stack_len)); \
5211 else ADD_INSN1(ret, line_node, pushtoarray, INT2FIX(stack_len)); \
5212 first_chunk = FALSE; \
5213 stack_len = 0; \
5214 }
5215
5216 while (node) {
5217 int count = 1;
5218
5219 /* pre-allocation check (this branch can be omittable) */
5220 if (static_literal_node_p(RNODE_LIST(node)->nd_head, iseq, false)) {
5221 /* count the elements that are optimizable */
5222 const NODE *node_tmp = RNODE_LIST(node)->nd_next;
5223 for (; node_tmp && static_literal_node_p(RNODE_LIST(node_tmp)->nd_head, iseq, false); node_tmp = RNODE_LIST(node_tmp)->nd_next)
5224 count++;
5225
5226 if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_ary_len) {
5227 /* The literal contains only optimizable elements, or the subarray is long enough */
5228 VALUE ary = rb_ary_hidden_new(count);
5229
5230 /* Create a hidden array */
5231 for (; count; count--, node = RNODE_LIST(node)->nd_next)
5232 rb_ary_push(ary, static_literal_value(RNODE_LIST(node)->nd_head, iseq));
5233 RB_OBJ_SET_FROZEN_SHAREABLE(ary);
5234
5235 /* Emit optimized code */
5236 FLUSH_CHUNK;
5237 if (first_chunk) {
5238 ADD_INSN1(ret, line_node, duparray, ary);
5239 first_chunk = FALSE;
5240 }
5241 else {
5242 ADD_INSN1(ret, line_node, putobject, ary);
5243 ADD_INSN(ret, line_node, concattoarray);
5244 }
5245 RB_OBJ_SET_SHAREABLE(ary);
5246 RB_OBJ_WRITTEN(iseq, Qundef, ary);
5247 }
5248 }
5249
5250 /* Base case: Compile "count" elements */
5251 for (; count; count--, node = RNODE_LIST(node)->nd_next) {
5252 if (CPDEBUG > 0) {
5253 EXPECT_NODE("compile_array", node, NODE_LIST, -1);
5254 }
5255
5256 if (!RNODE_LIST(node)->nd_next && keyword_node_p(RNODE_LIST(node)->nd_head)) {
5257 /* Create array or push existing non-keyword elements onto array */
5258 if (stack_len == 0 && first_chunk) {
5259 ADD_INSN1(ret, line_node, newarray, INT2FIX(0));
5260 }
5261 else {
5262 FLUSH_CHUNK;
5263 }
5264 NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, 0));
5265 ADD_INSN(ret, line_node, pushtoarraykwsplat);
5266 return 1;
5267 }
5268 else {
5269 NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, 0));
5270 stack_len++;
5271 }
5272
5273 /* If there are many pushed elements, flush them to avoid stack overflow */
5274 if (stack_len >= max_stack_len) FLUSH_CHUNK;
5275 }
5276 }
5277
5278 FLUSH_CHUNK;
5279#undef FLUSH_CHUNK
5280 return 1;
5281}
5282
5283static inline int
5284static_literal_node_pair_p(const NODE *node, const rb_iseq_t *iseq)
5285{
5286 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);
5287}
5288
5289static int
5290compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int method_call_keywords, int popped)
5291{
5292 const NODE *line_node = node;
5293
5294 node = RNODE_HASH(node)->nd_head;
5295
5296 if (!node || nd_type_p(node, NODE_ZLIST)) {
5297 if (!popped) {
5298 ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
5299 }
5300 return 0;
5301 }
5302
5303 EXPECT_NODE("compile_hash", node, NODE_LIST, -1);
5304
5305 if (popped) {
5306 for (; node; node = RNODE_LIST(node)->nd_next) {
5307 NO_CHECK(COMPILE_(ret, "hash element", RNODE_LIST(node)->nd_head, popped));
5308 }
5309 return 1;
5310 }
5311
5312 /* Compilation of a hash literal (or keyword arguments).
5313 * This is very similar to compile_array, but there are some differences:
5314 *
5315 * - It contains key-value pairs. So we need to take every two elements.
5316 * We can assume that the length is always even.
5317 *
5318 * - Merging is done by a method call (id_core_hash_merge_ptr).
5319 * Sometimes we need to insert the receiver, so "anchor" is needed.
5320 * In addition, a method call is much slower than concatarray.
5321 * So it pays only when the subsequence is really long.
5322 * (min_tmp_hash_len must be much larger than min_tmp_ary_len.)
5323 *
5324 * - We need to handle keyword splat: **kw.
5325 * For **kw, the key part (node->nd_head) is NULL, and the value part
5326 * (node->nd_next->nd_head) is "kw".
5327 * The code is a bit difficult to avoid hash allocation for **{}.
5328 */
5329
5330 const int max_stack_len = 0x100;
5331 const int min_tmp_hash_len = 0x800;
5332 int stack_len = 0;
5333 int first_chunk = 1;
5334 DECL_ANCHOR(anchor);
5335 INIT_ANCHOR(anchor);
5336
5337 /* Convert pushed elements to a hash, and merge if needed */
5338#define FLUSH_CHUNK() \
5339 if (stack_len) { \
5340 if (first_chunk) { \
5341 APPEND_LIST(ret, anchor); \
5342 ADD_INSN1(ret, line_node, newhash, INT2FIX(stack_len)); \
5343 } \
5344 else { \
5345 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); \
5346 ADD_INSN(ret, line_node, swap); \
5347 APPEND_LIST(ret, anchor); \
5348 ADD_SEND(ret, line_node, id_core_hash_merge_ptr, INT2FIX(stack_len + 1)); \
5349 } \
5350 INIT_ANCHOR(anchor); \
5351 first_chunk = stack_len = 0; \
5352 }
5353
5354 while (node) {
5355 int count = 1;
5356
5357 /* pre-allocation check (this branch can be omittable) */
5358 if (static_literal_node_pair_p(node, iseq)) {
5359 /* count the elements that are optimizable */
5360 const NODE *node_tmp = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next;
5361 for (; node_tmp && static_literal_node_pair_p(node_tmp, iseq); node_tmp = RNODE_LIST(RNODE_LIST(node_tmp)->nd_next)->nd_next)
5362 count++;
5363
5364 if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_hash_len) {
5365 /* The literal contains only optimizable elements, or the subsequence is long enough */
5366 VALUE ary = rb_ary_hidden_new(count);
5367
5368 /* Create a hidden hash */
5369 for (; count; count--, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
5370 VALUE elem[2];
5371 elem[0] = static_literal_value(RNODE_LIST(node)->nd_head, iseq);
5372 if (!RB_SPECIAL_CONST_P(elem[0])) RB_OBJ_SET_FROZEN_SHAREABLE(elem[0]);
5373 elem[1] = static_literal_value(RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, iseq);
5374 if (!RB_SPECIAL_CONST_P(elem[1])) RB_OBJ_SET_FROZEN_SHAREABLE(elem[1]);
5375 rb_ary_cat(ary, elem, 2);
5376 }
5377 VALUE hash = rb_hash_alloc_fixed_size(Qfalse, RARRAY_LEN(ary) / 2);
5378 rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary), hash);
5379 RB_GC_GUARD(ary);
5380 hash = RB_OBJ_SET_FROZEN_SHAREABLE(hash);
5381
5382 /* Emit optimized code */
5383 FLUSH_CHUNK();
5384 if (first_chunk) {
5385 ADD_INSN1(ret, line_node, duphash, hash);
5386 first_chunk = 0;
5387 }
5388 else {
5389 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5390 ADD_INSN(ret, line_node, swap);
5391
5392 ADD_INSN1(ret, line_node, putobject, hash);
5393
5394 ADD_SEND(ret, line_node, id_core_hash_merge_kwd, INT2FIX(2));
5395 }
5396 RB_OBJ_WRITTEN(iseq, Qundef, hash);
5397 }
5398 }
5399
5400 /* Base case: Compile "count" elements */
5401 for (; count; count--, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) {
5402
5403 if (CPDEBUG > 0) {
5404 EXPECT_NODE("compile_hash", node, NODE_LIST, -1);
5405 }
5406
5407 if (RNODE_LIST(node)->nd_head) {
5408 /* Normal key-value pair */
5409 NO_CHECK(COMPILE_(anchor, "hash key element", RNODE_LIST(node)->nd_head, 0));
5410 NO_CHECK(COMPILE_(anchor, "hash value element", RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, 0));
5411 stack_len += 2;
5412
5413 /* If there are many pushed elements, flush them to avoid stack overflow */
5414 if (stack_len >= max_stack_len) FLUSH_CHUNK();
5415 }
5416 else {
5417 /* kwsplat case: foo(..., **kw, ...) */
5418 FLUSH_CHUNK();
5419
5420 const NODE *kw = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head;
5421 int empty_kw = nd_type_p(kw, NODE_HASH) && (!RNODE_HASH(kw)->nd_head); /* foo( ..., **{}, ...) */
5422 int first_kw = first_chunk && stack_len == 0; /* foo(1,2,3, **kw, ...) */
5423 int last_kw = !RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next; /* foo( ..., **kw) */
5424 int only_kw = last_kw && first_kw; /* foo(1,2,3, **kw) */
5425
5426 empty_kw = empty_kw || nd_type_p(kw, NODE_NIL); /* foo( ..., **nil, ...) */
5427 if (empty_kw) {
5428 if (only_kw && method_call_keywords) {
5429 /* **{} appears at the only keyword argument in method call,
5430 * so it won't be modified.
5431 * kw is a special NODE_LIT that contains a special empty hash,
5432 * so this emits: putobject {}.
5433 * This is only done for method calls and not for literal hashes,
5434 * because literal hashes should always result in a new hash.
5435 */
5436 NO_CHECK(COMPILE(ret, "keyword splat", kw));
5437 }
5438 else if (first_kw) {
5439 /* **{} appears as the first keyword argument, so it may be modified.
5440 * We need to create a fresh hash object.
5441 */
5442 ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
5443 }
5444 /* Any empty keyword splats that are not the first can be ignored.
5445 * since merging an empty hash into the existing hash is the same
5446 * as not merging it. */
5447 }
5448 else {
5449 if (only_kw && method_call_keywords) {
5450 /* **kw is only keyword argument in method call.
5451 * Use directly. This will be not be flagged as mutable.
5452 * This is only done for method calls and not for literal hashes,
5453 * because literal hashes should always result in a new hash.
5454 */
5455 NO_CHECK(COMPILE(ret, "keyword splat", kw));
5456 }
5457 else {
5458 /* There is more than one keyword argument, or this is not a method
5459 * call. In that case, we need to add an empty hash (if first keyword),
5460 * or merge the hash to the accumulated hash (if not the first keyword).
5461 */
5462 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
5463 if (first_kw) ADD_INSN1(ret, line_node, newhash, INT2FIX(0));
5464 else ADD_INSN(ret, line_node, swap);
5465
5466 NO_CHECK(COMPILE(ret, "keyword splat", kw));
5467
5468 ADD_SEND(ret, line_node, id_core_hash_merge_kwd, INT2FIX(2));
5469 }
5470 }
5471
5472 first_chunk = 0;
5473 }
5474 }
5475 }
5476
5477 FLUSH_CHUNK();
5478#undef FLUSH_CHUNK
5479 return 1;
5480}
5481
5482VALUE
5483rb_node_case_when_optimizable_literal(const NODE *const node)
5484{
5485 switch (nd_type(node)) {
5486 case NODE_INTEGER:
5487 return rb_node_integer_literal_val(node);
5488 case NODE_FLOAT: {
5489 VALUE v = rb_node_float_literal_val(node);
5490 double ival;
5491
5492 if (modf(RFLOAT_VALUE(v), &ival) == 0.0) {
5493 return FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival);
5494 }
5495 return v;
5496 }
5497 case NODE_RATIONAL:
5498 case NODE_IMAGINARY:
5499 return Qundef;
5500 case NODE_NIL:
5501 return Qnil;
5502 case NODE_TRUE:
5503 return Qtrue;
5504 case NODE_FALSE:
5505 return Qfalse;
5506 case NODE_SYM:
5507 return rb_node_sym_string_val(node);
5508 case NODE_LINE:
5509 return rb_node_line_lineno_val(node);
5510 case NODE_STR:
5511 return rb_node_str_string_val(node);
5512 case NODE_FILE:
5513 return rb_node_file_path_val(node);
5514 }
5515 return Qundef;
5516}
5517
5518static int
5519when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
5520 LABEL *l1, int only_special_literals, VALUE literals)
5521{
5522 while (vals) {
5523 const NODE *val = RNODE_LIST(vals)->nd_head;
5524 VALUE lit = rb_node_case_when_optimizable_literal(val);
5525
5526 if (UNDEF_P(lit)) {
5527 only_special_literals = 0;
5528 }
5529 else if (NIL_P(rb_hash_lookup(literals, lit))) {
5530 rb_hash_aset(literals, lit, (VALUE)(l1) | 1);
5531 }
5532
5533 if (nd_type_p(val, NODE_STR) || nd_type_p(val, NODE_FILE)) {
5534 debugp_param("nd_lit", get_string_value(val));
5535 lit = get_string_value(val);
5536 ADD_INSN1(cond_seq, val, putobject, lit);
5537 RB_OBJ_WRITTEN(iseq, Qundef, lit);
5538 }
5539 else {
5540 if (!COMPILE(cond_seq, "when cond", val)) return -1;
5541 }
5542
5543 // Emit pattern === target
5544 ADD_INSN1(cond_seq, vals, topn, INT2FIX(1));
5545 ADD_CALL(cond_seq, vals, idEqq, INT2FIX(1));
5546 ADD_INSNL(cond_seq, val, branchif, l1);
5547 vals = RNODE_LIST(vals)->nd_next;
5548 }
5549 return only_special_literals;
5550}
5551
5552static int
5553when_splat_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
5554 LABEL *l1, int only_special_literals, VALUE literals)
5555{
5556 const NODE *line_node = vals;
5557
5558 switch (nd_type(vals)) {
5559 case NODE_LIST:
5560 if (when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals) < 0)
5561 return COMPILE_NG;
5562 break;
5563 case NODE_SPLAT:
5564 ADD_INSN (cond_seq, line_node, dup);
5565 CHECK(COMPILE(cond_seq, "when splat", RNODE_SPLAT(vals)->nd_head));
5566 ADD_INSN1(cond_seq, line_node, splatarray, Qfalse);
5567 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
5568 ADD_INSNL(cond_seq, line_node, branchif, l1);
5569 break;
5570 case NODE_ARGSCAT:
5571 CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSCAT(vals)->nd_head, l1, only_special_literals, literals));
5572 CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSCAT(vals)->nd_body, l1, only_special_literals, literals));
5573 break;
5574 case NODE_ARGSPUSH:
5575 CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSPUSH(vals)->nd_head, l1, only_special_literals, literals));
5576 ADD_INSN (cond_seq, line_node, dup);
5577 CHECK(COMPILE(cond_seq, "when argspush body", RNODE_ARGSPUSH(vals)->nd_body));
5578 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
5579 ADD_INSNL(cond_seq, line_node, branchif, l1);
5580 break;
5581 default:
5582 ADD_INSN (cond_seq, line_node, dup);
5583 CHECK(COMPILE(cond_seq, "when val", vals));
5584 ADD_INSN1(cond_seq, line_node, splatarray, Qfalse);
5585 ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
5586 ADD_INSNL(cond_seq, line_node, branchif, l1);
5587 break;
5588 }
5589 return COMPILE_OK;
5590}
5591
5592/* Multiple Assignment Handling
5593 *
5594 * In order to handle evaluation of multiple assignment such that the left hand side
5595 * is evaluated before the right hand side, we need to process the left hand side
5596 * and see if there are any attributes that need to be assigned, or constants set
5597 * on explicit objects. If so, we add instructions to evaluate the receiver of
5598 * any assigned attributes or constants before we process the right hand side.
5599 *
5600 * For a multiple assignment such as:
5601 *
5602 * l1.m1, l2[0] = r3, r4
5603 *
5604 * We start off evaluating l1 and l2, then we evaluate r3 and r4, then we
5605 * assign the result of r3 to l1.m1, and then the result of r4 to l2.m2.
5606 * On the VM stack, this looks like:
5607 *
5608 * self # putself
5609 * l1 # send
5610 * l1, self # putself
5611 * l1, l2 # send
5612 * l1, l2, 0 # putobject 0
5613 * l1, l2, 0, [r3, r4] # after evaluation of RHS
5614 * l1, l2, 0, [r3, r4], r4, r3 # expandarray
5615 * l1, l2, 0, [r3, r4], r4, r3, l1 # topn 5
5616 * l1, l2, 0, [r3, r4], r4, l1, r3 # swap
5617 * l1, l2, 0, [r3, r4], r4, m1= # send
5618 * l1, l2, 0, [r3, r4], r4 # pop
5619 * l1, l2, 0, [r3, r4], r4, l2 # topn 3
5620 * l1, l2, 0, [r3, r4], r4, l2, 0 # topn 3
5621 * l1, l2, 0, [r3, r4], r4, l2, 0, r4 # topn 2
5622 * l1, l2, 0, [r3, r4], r4, []= # send
5623 * l1, l2, 0, [r3, r4], r4 # pop
5624 * l1, l2, 0, [r3, r4] # pop
5625 * [r3, r4], l2, 0, [r3, r4] # setn 3
5626 * [r3, r4], l2, 0 # pop
5627 * [r3, r4], l2 # pop
5628 * [r3, r4] # pop
5629 *
5630 * This is made more complex when you have to handle splats, post args,
5631 * and arbitrary levels of nesting. You need to keep track of the total
5632 * number of attributes to set, and for each attribute, how many entries
5633 * are on the stack before the final attribute, in order to correctly
5634 * calculate the topn value to use to get the receiver of the attribute
5635 * setter method.
5636 *
5637 * A brief description of the VM stack for simple multiple assignment
5638 * with no splat (rhs_array will not be present if the return value of
5639 * the multiple assignment is not needed):
5640 *
5641 * lhs_attr1, lhs_attr2, ..., rhs_array, ..., rhs_arg2, rhs_arg1
5642 *
5643 * For multiple assignment with splats, while processing the part before
5644 * the splat (splat+post here is an array of the splat and the post arguments):
5645 *
5646 * lhs_attr1, lhs_attr2, ..., rhs_array, splat+post, ..., rhs_arg2, rhs_arg1
5647 *
5648 * When processing the splat and post arguments:
5649 *
5650 * lhs_attr1, lhs_attr2, ..., rhs_array, ..., post_arg2, post_arg1, splat
5651 *
5652 * When processing nested multiple assignment, existing values on the stack
5653 * are kept. So for:
5654 *
5655 * (l1.m1, l2.m2), l3.m3, l4* = [r1, r2], r3, r4
5656 *
5657 * The stack layout would be the following before processing the nested
5658 * multiple assignment:
5659 *
5660 * l1, l2, [[r1, r2], r3, r4], [r4], r3, [r1, r2]
5661 *
5662 * In order to handle this correctly, we need to keep track of the nesting
5663 * level for each attribute assignment, as well as the attribute number
5664 * (left hand side attributes are processed left to right) and number of
5665 * arguments to pass to the setter method. struct masgn_lhs_node tracks
5666 * this information.
5667 *
5668 * We also need to track information for the entire multiple assignment, such
5669 * as the total number of arguments, and the current nesting level, to
5670 * handle both nested multiple assignment as well as cases where the
5671 * rhs is not needed. We also need to keep track of all attribute
5672 * assignments in this, which we do using a linked listed. struct masgn_state
5673 * tracks this information.
5674 */
5675
5677 INSN *before_insn;
5678 struct masgn_lhs_node *next;
5679 const NODE *line_node;
5680 int argn;
5681 int num_args;
5682 int lhs_pos;
5683};
5684
5686 struct masgn_lhs_node *first_memo;
5687 struct masgn_lhs_node *last_memo;
5688 int lhs_level;
5689 int num_args;
5690 bool nested;
5691};
5692
5693static int
5694add_masgn_lhs_node(struct masgn_state *state, int lhs_pos, const NODE *line_node, int argc, INSN *before_insn)
5695{
5696 if (!state) {
5697 rb_bug("no masgn_state");
5698 }
5699
5700 struct masgn_lhs_node *memo;
5701 memo = malloc(sizeof(struct masgn_lhs_node));
5702 if (!memo) {
5703 return COMPILE_NG;
5704 }
5705
5706 memo->before_insn = before_insn;
5707 memo->line_node = line_node;
5708 memo->argn = state->num_args + 1;
5709 memo->num_args = argc;
5710 state->num_args += argc;
5711 memo->lhs_pos = lhs_pos;
5712 memo->next = NULL;
5713 if (!state->first_memo) {
5714 state->first_memo = memo;
5715 }
5716 else {
5717 state->last_memo->next = memo;
5718 }
5719 state->last_memo = memo;
5720
5721 return COMPILE_OK;
5722}
5723
5724static 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);
5725
5726static int
5727compile_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)
5728{
5729 switch (nd_type(node)) {
5730 case NODE_ATTRASGN: {
5731 INSN *iobj;
5732 const NODE *line_node = node;
5733
5734 CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_ATTRASGN)", node));
5735
5736 bool safenav_call = false;
5737 LINK_ELEMENT *insn_element = LAST_ELEMENT(pre);
5738 iobj = (INSN *)get_prev_insn((INSN *)insn_element); /* send insn */
5739 ASSUME(iobj);
5740 ELEM_REMOVE(insn_element);
5741 if (!IS_INSN_ID(iobj, send)) {
5742 safenav_call = true;
5743 iobj = (INSN *)get_prev_insn(iobj);
5744 ELEM_INSERT_NEXT(&iobj->link, insn_element);
5745 }
5746 (pre->last = iobj->link.prev)->next = 0;
5747
5748 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
5749 int argc = vm_ci_argc(ci) + 1;
5750 ci = ci_argc_set(iseq, ci, argc);
5751 OPERAND_AT(iobj, 0) = (VALUE)ci;
5752 RB_OBJ_WRITTEN(iseq, Qundef, ci);
5753
5754 if (argc == 1) {
5755 ADD_INSN(lhs, line_node, swap);
5756 }
5757 else {
5758 ADD_INSN1(lhs, line_node, topn, INT2FIX(argc));
5759 }
5760
5761 if (!add_masgn_lhs_node(state, lhs_pos, line_node, argc, (INSN *)LAST_ELEMENT(lhs))) {
5762 return COMPILE_NG;
5763 }
5764
5765 iobj->link.prev = lhs->last;
5766 lhs->last->next = &iobj->link;
5767 for (lhs->last = &iobj->link; lhs->last->next; lhs->last = lhs->last->next);
5768 if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) {
5769 int argc = vm_ci_argc(ci);
5770 bool dupsplat = false;
5771 ci = ci_argc_set(iseq, ci, argc - 1);
5772 if (!(vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT_MUT)) {
5773 /* Given h[*a], _ = ary
5774 * setup_args sets VM_CALL_ARGS_SPLAT and not VM_CALL_ARGS_SPLAT_MUT
5775 * `a` must be dupped, because it will be appended with ary[0]
5776 * Since you are dupping `a`, you can set VM_CALL_ARGS_SPLAT_MUT
5777 */
5778 dupsplat = true;
5779 ci = ci_flag_set(iseq, ci, VM_CALL_ARGS_SPLAT_MUT);
5780 }
5781 OPERAND_AT(iobj, 0) = (VALUE)ci;
5782 RB_OBJ_WRITTEN(iseq, Qundef, iobj);
5783
5784 /* Given: h[*a], h[*b, 1] = ary
5785 * h[*a] uses splatarray false and does not set VM_CALL_ARGS_SPLAT_MUT,
5786 * so this uses splatarray true on a to dup it before using pushtoarray
5787 * h[*b, 1] uses splatarray true and sets VM_CALL_ARGS_SPLAT_MUT,
5788 * so you can use pushtoarray directly
5789 */
5790 int line_no = nd_line(line_node);
5791 int node_id = nd_node_id(line_node);
5792
5793 if (dupsplat) {
5794 INSERT_BEFORE_INSN(iobj, line_no, node_id, swap);
5795 INSERT_BEFORE_INSN1(iobj, line_no, node_id, splatarray, Qtrue);
5796 INSERT_BEFORE_INSN(iobj, line_no, node_id, swap);
5797 }
5798 INSERT_BEFORE_INSN1(iobj, line_no, node_id, pushtoarray, INT2FIX(1));
5799 }
5800 if (!safenav_call) {
5801 ADD_INSN(lhs, line_node, pop);
5802 if (argc != 1) {
5803 ADD_INSN(lhs, line_node, pop);
5804 }
5805 }
5806 for (int i=0; i < argc; i++) {
5807 ADD_INSN(post, line_node, pop);
5808 }
5809 break;
5810 }
5811 case NODE_MASGN: {
5812 DECL_ANCHOR(nest_rhs);
5813 INIT_ANCHOR(nest_rhs);
5814 DECL_ANCHOR(nest_lhs);
5815 INIT_ANCHOR(nest_lhs);
5816
5817 int prev_level = state->lhs_level;
5818 bool prev_nested = state->nested;
5819 state->nested = 1;
5820 state->lhs_level = lhs_pos - 1;
5821 CHECK(compile_massign0(iseq, pre, nest_rhs, nest_lhs, post, node, state, 1));
5822 state->lhs_level = prev_level;
5823 state->nested = prev_nested;
5824
5825 ADD_SEQ(lhs, nest_rhs);
5826 ADD_SEQ(lhs, nest_lhs);
5827 break;
5828 }
5829 case NODE_CDECL:
5830 if (!RNODE_CDECL(node)->nd_vid) {
5831 /* Special handling only needed for expr::C, not for C */
5832 INSN *iobj;
5833
5834 CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_CDECL)", node));
5835
5836 LINK_ELEMENT *insn_element = LAST_ELEMENT(pre);
5837 iobj = (INSN *)insn_element; /* setconstant insn */
5838 ELEM_REMOVE((LINK_ELEMENT *)get_prev_insn((INSN *)get_prev_insn(iobj)));
5839 ELEM_REMOVE((LINK_ELEMENT *)get_prev_insn(iobj));
5840 ELEM_REMOVE(insn_element);
5841 pre->last = iobj->link.prev;
5842 ADD_ELEM(lhs, (LINK_ELEMENT *)iobj);
5843
5844 if (!add_masgn_lhs_node(state, lhs_pos, node, 1, (INSN *)LAST_ELEMENT(lhs))) {
5845 return COMPILE_NG;
5846 }
5847
5848 ADD_INSN(post, node, pop);
5849 break;
5850 }
5851 /* Fallthrough */
5852 default: {
5853 DECL_ANCHOR(anchor);
5854 INIT_ANCHOR(anchor);
5855 CHECK(COMPILE_POPPED(anchor, "masgn lhs", node));
5856 ELEM_REMOVE(FIRST_ELEMENT(anchor));
5857 ADD_SEQ(lhs, anchor);
5858 }
5859 }
5860
5861 return COMPILE_OK;
5862}
5863
5864static int
5865compile_massign_opt_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *lhsn)
5866{
5867 if (lhsn) {
5868 CHECK(compile_massign_opt_lhs(iseq, ret, RNODE_LIST(lhsn)->nd_next));
5869 CHECK(compile_massign_lhs(iseq, ret, ret, ret, ret, RNODE_LIST(lhsn)->nd_head, NULL, 0));
5870 }
5871 return COMPILE_OK;
5872}
5873
5874static int
5875compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
5876 const NODE *rhsn, const NODE *orig_lhsn)
5877{
5878 VALUE mem[64];
5879 const int memsize = numberof(mem);
5880 int memindex = 0;
5881 int llen = 0, rlen = 0;
5882 int i;
5883 const NODE *lhsn = orig_lhsn;
5884
5885#define MEMORY(v) { \
5886 int i; \
5887 if (memindex == memsize) return 0; \
5888 for (i=0; i<memindex; i++) { \
5889 if (mem[i] == (v)) return 0; \
5890 } \
5891 mem[memindex++] = (v); \
5892}
5893
5894 if (rhsn == 0 || !nd_type_p(rhsn, NODE_LIST)) {
5895 return 0;
5896 }
5897
5898 while (lhsn) {
5899 const NODE *ln = RNODE_LIST(lhsn)->nd_head;
5900 switch (nd_type(ln)) {
5901 case NODE_LASGN:
5902 case NODE_DASGN:
5903 case NODE_IASGN:
5904 case NODE_CVASGN:
5905 MEMORY(get_nd_vid(ln));
5906 break;
5907 default:
5908 return 0;
5909 }
5910 lhsn = RNODE_LIST(lhsn)->nd_next;
5911 llen++;
5912 }
5913
5914 while (rhsn) {
5915 if (llen <= rlen) {
5916 NO_CHECK(COMPILE_POPPED(ret, "masgn val (popped)", RNODE_LIST(rhsn)->nd_head));
5917 }
5918 else {
5919 NO_CHECK(COMPILE(ret, "masgn val", RNODE_LIST(rhsn)->nd_head));
5920 }
5921 rhsn = RNODE_LIST(rhsn)->nd_next;
5922 rlen++;
5923 }
5924
5925 if (llen > rlen) {
5926 for (i=0; i<llen-rlen; i++) {
5927 ADD_INSN(ret, orig_lhsn, putnil);
5928 }
5929 }
5930
5931 compile_massign_opt_lhs(iseq, ret, orig_lhsn);
5932 return 1;
5933}
5934
5935static int
5936compile_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)
5937{
5938 const NODE *rhsn = RNODE_MASGN(node)->nd_value;
5939 const NODE *splatn = RNODE_MASGN(node)->nd_args;
5940 const NODE *lhsn = RNODE_MASGN(node)->nd_head;
5941 const NODE *lhsn_count = lhsn;
5942 int lhs_splat = (splatn && NODE_NAMED_REST_P(splatn)) ? 1 : 0;
5943
5944 int llen = 0;
5945 int lpos = 0;
5946
5947 while (lhsn_count) {
5948 llen++;
5949 lhsn_count = RNODE_LIST(lhsn_count)->nd_next;
5950 }
5951 while (lhsn) {
5952 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, RNODE_LIST(lhsn)->nd_head, state, (llen - lpos) + lhs_splat + state->lhs_level));
5953 lpos++;
5954 lhsn = RNODE_LIST(lhsn)->nd_next;
5955 }
5956
5957 if (lhs_splat) {
5958 if (nd_type_p(splatn, NODE_POSTARG)) {
5959 /*a, b, *r, p1, p2 */
5960 const NODE *postn = RNODE_POSTARG(splatn)->nd_2nd;
5961 const NODE *restn = RNODE_POSTARG(splatn)->nd_1st;
5962 int plen = (int)RNODE_LIST(postn)->as.nd_alen;
5963 int ppos = 0;
5964 int flag = 0x02 | (NODE_NAMED_REST_P(restn) ? 0x01 : 0x00);
5965
5966 ADD_INSN2(lhs, splatn, expandarray, INT2FIX(plen), INT2FIX(flag));
5967
5968 if (NODE_NAMED_REST_P(restn)) {
5969 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, restn, state, 1 + plen + state->lhs_level));
5970 }
5971 while (postn) {
5972 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, RNODE_LIST(postn)->nd_head, state, (plen - ppos) + state->lhs_level));
5973 ppos++;
5974 postn = RNODE_LIST(postn)->nd_next;
5975 }
5976 }
5977 else {
5978 /* a, b, *r */
5979 CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, splatn, state, 1 + state->lhs_level));
5980 }
5981 }
5982
5983 if (!state->nested) {
5984 NO_CHECK(COMPILE(rhs, "normal masgn rhs", rhsn));
5985 }
5986
5987 if (!popped) {
5988 ADD_INSN(rhs, node, dup);
5989 }
5990 ADD_INSN2(rhs, node, expandarray, INT2FIX(llen), INT2FIX(lhs_splat));
5991 return COMPILE_OK;
5992}
5993
5994static int
5995compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
5996{
5997 if (!popped || RNODE_MASGN(node)->nd_args || !compile_massign_opt(iseq, ret, RNODE_MASGN(node)->nd_value, RNODE_MASGN(node)->nd_head)) {
5998 struct masgn_state state;
5999 state.lhs_level = popped ? 0 : 1;
6000 state.nested = 0;
6001 state.num_args = 0;
6002 state.first_memo = NULL;
6003 state.last_memo = NULL;
6004
6005 DECL_ANCHOR(pre);
6006 INIT_ANCHOR(pre);
6007 DECL_ANCHOR(rhs);
6008 INIT_ANCHOR(rhs);
6009 DECL_ANCHOR(lhs);
6010 INIT_ANCHOR(lhs);
6011 DECL_ANCHOR(post);
6012 INIT_ANCHOR(post);
6013 int ok = compile_massign0(iseq, pre, rhs, lhs, post, node, &state, popped);
6014
6015 struct masgn_lhs_node *memo = state.first_memo, *tmp_memo;
6016 while (memo) {
6017 VALUE topn_arg = INT2FIX((state.num_args - memo->argn) + memo->lhs_pos);
6018 for (int i = 0; i < memo->num_args; i++) {
6019 INSERT_BEFORE_INSN1(memo->before_insn, nd_line(memo->line_node), nd_node_id(memo->line_node), topn, topn_arg);
6020 }
6021 tmp_memo = memo->next;
6022 free(memo);
6023 memo = tmp_memo;
6024 }
6025 CHECK(ok);
6026
6027 ADD_SEQ(ret, pre);
6028 ADD_SEQ(ret, rhs);
6029 ADD_SEQ(ret, lhs);
6030 if (!popped && state.num_args >= 1) {
6031 /* make sure rhs array is returned before popping */
6032 ADD_INSN1(ret, node, setn, INT2FIX(state.num_args));
6033 }
6034 ADD_SEQ(ret, post);
6035 }
6036 return COMPILE_OK;
6037}
6038
6039static VALUE
6040collect_const_segments(rb_iseq_t *iseq, const NODE *node)
6041{
6042 VALUE arr = rb_ary_new();
6043 for (;;) {
6044 switch (nd_type(node)) {
6045 case NODE_CONST:
6046 rb_ary_unshift(arr, ID2SYM(RNODE_CONST(node)->nd_vid));
6047 RB_OBJ_SET_SHAREABLE(arr);
6048 return arr;
6049 case NODE_COLON3:
6050 rb_ary_unshift(arr, ID2SYM(RNODE_COLON3(node)->nd_mid));
6051 rb_ary_unshift(arr, ID2SYM(idNULL));
6052 RB_OBJ_SET_SHAREABLE(arr);
6053 return arr;
6054 case NODE_COLON2:
6055 rb_ary_unshift(arr, ID2SYM(RNODE_COLON2(node)->nd_mid));
6056 node = RNODE_COLON2(node)->nd_head;
6057 break;
6058 default:
6059 return Qfalse;
6060 }
6061 }
6062}
6063
6064static int
6065compile_const_prefix(rb_iseq_t *iseq, const NODE *const node,
6066 LINK_ANCHOR *const pref, LINK_ANCHOR *const body)
6067{
6068 switch (nd_type(node)) {
6069 case NODE_CONST:
6070 debugi("compile_const_prefix - colon", RNODE_CONST(node)->nd_vid);
6071 ADD_INSN1(body, node, putobject, Qtrue);
6072 ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_CONST(node)->nd_vid));
6073 break;
6074 case NODE_COLON3:
6075 debugi("compile_const_prefix - colon3", RNODE_COLON3(node)->nd_mid);
6076 ADD_INSN(body, node, pop);
6077 ADD_INSN1(body, node, putobject, rb_cObject);
6078 ADD_INSN1(body, node, putobject, Qtrue);
6079 ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_COLON3(node)->nd_mid));
6080 break;
6081 case NODE_COLON2:
6082 CHECK(compile_const_prefix(iseq, RNODE_COLON2(node)->nd_head, pref, body));
6083 debugi("compile_const_prefix - colon2", RNODE_COLON2(node)->nd_mid);
6084 ADD_INSN1(body, node, putobject, Qfalse);
6085 ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_COLON2(node)->nd_mid));
6086 break;
6087 default:
6088 CHECK(COMPILE(pref, "const colon2 prefix", node));
6089 break;
6090 }
6091 return COMPILE_OK;
6092}
6093
6094static int
6095cpath_const_p(const NODE *node)
6096{
6097 switch (nd_type(node)) {
6098 case NODE_CONST:
6099 case NODE_COLON3:
6100 return TRUE;
6101 case NODE_COLON2:
6102 if (RNODE_COLON2(node)->nd_head) {
6103 return cpath_const_p(RNODE_COLON2(node)->nd_head);
6104 }
6105 return TRUE;
6106 default:
6107 return FALSE;
6108 }
6109}
6110
6111static int
6112compile_cpath(LINK_ANCHOR *const ret, rb_iseq_t *iseq, const NODE *cpath)
6113{
6114 if (nd_type_p(cpath, NODE_COLON3)) {
6115 /* toplevel class ::Foo */
6116 ADD_INSN1(ret, cpath, putobject, rb_cObject);
6117 return VM_DEFINECLASS_FLAG_SCOPED;
6118 }
6119 else if (nd_type_p(cpath, NODE_COLON2) && RNODE_COLON2(cpath)->nd_head) {
6120 /* Bar::Foo or expr::Foo */
6121 NO_CHECK(COMPILE(ret, "nd_else->nd_head", RNODE_COLON2(cpath)->nd_head));
6122 int flags = VM_DEFINECLASS_FLAG_SCOPED;
6123 if (!cpath_const_p(RNODE_COLON2(cpath)->nd_head)) {
6124 flags |= VM_DEFINECLASS_FLAG_DYNAMIC_CREF;
6125 }
6126 return flags;
6127 }
6128 else {
6129 /* class at cbase Foo */
6130 ADD_INSN1(ret, cpath, putspecialobject,
6131 INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
6132 return 0;
6133 }
6134}
6135
6136static inline int
6137private_recv_p(const NODE *node)
6138{
6139 NODE *recv = get_nd_recv(node);
6140 if (recv && nd_type_p(recv, NODE_SELF)) {
6141 return RNODE_SELF(recv)->nd_state != 0;
6142 }
6143 return 0;
6144}
6145
6146static void
6147defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
6148 const NODE *const node, LABEL **lfinish, VALUE needstr, bool ignore);
6149
6150static int
6151compile_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);
6152
6153static void
6154defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
6155 const NODE *const node, LABEL **lfinish, VALUE needstr,
6156 bool keep_result)
6157{
6158 enum defined_type expr_type = DEFINED_NOT_DEFINED;
6159 enum node_type type;
6160 const int line = nd_line(node);
6161 const NODE *line_node = node;
6162
6163 switch (type = nd_type(node)) {
6164
6165 /* easy literals */
6166 case NODE_NIL:
6167 expr_type = DEFINED_NIL;
6168 break;
6169 case NODE_SELF:
6170 expr_type = DEFINED_SELF;
6171 break;
6172 case NODE_TRUE:
6173 expr_type = DEFINED_TRUE;
6174 break;
6175 case NODE_FALSE:
6176 expr_type = DEFINED_FALSE;
6177 break;
6178
6179 case NODE_HASH:
6180 case NODE_LIST:{
6181 const NODE *vals = (nd_type(node) == NODE_HASH) ? RNODE_HASH(node)->nd_head : node;
6182
6183 if (vals) {
6184 do {
6185 if (RNODE_LIST(vals)->nd_head) {
6186 defined_expr0(iseq, ret, RNODE_LIST(vals)->nd_head, lfinish, Qfalse, false);
6187
6188 if (!lfinish[1]) {
6189 lfinish[1] = NEW_LABEL(line);
6190 }
6191 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6192 }
6193 } while ((vals = RNODE_LIST(vals)->nd_next) != NULL);
6194 }
6195 }
6196 /* fall through */
6197 case NODE_STR:
6198 case NODE_SYM:
6199 case NODE_REGX:
6200 case NODE_LINE:
6201 case NODE_FILE:
6202 case NODE_ENCODING:
6203 case NODE_INTEGER:
6204 case NODE_FLOAT:
6205 case NODE_RATIONAL:
6206 case NODE_IMAGINARY:
6207 case NODE_ZLIST:
6208 case NODE_AND:
6209 case NODE_OR:
6210 default:
6211 expr_type = DEFINED_EXPR;
6212 break;
6213
6214 case NODE_SPLAT:
6215 defined_expr0(iseq, ret, RNODE_LIST(node)->nd_head, lfinish, Qfalse, false);
6216 if (!lfinish[1]) {
6217 lfinish[1] = NEW_LABEL(line);
6218 }
6219 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6220 expr_type = DEFINED_EXPR;
6221 break;
6222
6223 /* variables */
6224 case NODE_LVAR:
6225 case NODE_DVAR:
6226 expr_type = DEFINED_LVAR;
6227 break;
6228
6229#define PUSH_VAL(type) (needstr == Qfalse ? Qtrue : rb_iseq_defined_string(type))
6230 case NODE_IVAR:
6231 ADD_INSN3(ret, line_node, definedivar,
6232 ID2SYM(RNODE_IVAR(node)->nd_vid), get_ivar_ic_value(iseq,RNODE_IVAR(node)->nd_vid), PUSH_VAL(DEFINED_IVAR));
6233 return;
6234
6235 case NODE_GVAR:
6236 ADD_INSN(ret, line_node, putnil);
6237 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_GVAR),
6238 ID2SYM(RNODE_GVAR(node)->nd_vid), PUSH_VAL(DEFINED_GVAR));
6239 return;
6240
6241 case NODE_CVAR:
6242 ADD_INSN(ret, line_node, putnil);
6243 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CVAR),
6244 ID2SYM(RNODE_CVAR(node)->nd_vid), PUSH_VAL(DEFINED_CVAR));
6245 return;
6246
6247 case NODE_CONST:
6248 ADD_INSN(ret, line_node, putnil);
6249 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST),
6250 ID2SYM(RNODE_CONST(node)->nd_vid), PUSH_VAL(DEFINED_CONST));
6251 return;
6252 case NODE_COLON2:
6253 if (!lfinish[1]) {
6254 lfinish[1] = NEW_LABEL(line);
6255 }
6256 defined_expr0(iseq, ret, RNODE_COLON2(node)->nd_head, lfinish, Qfalse, false);
6257 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6258 NO_CHECK(COMPILE(ret, "defined/colon2#nd_head", RNODE_COLON2(node)->nd_head));
6259
6260 if (rb_is_const_id(RNODE_COLON2(node)->nd_mid)) {
6261 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST_FROM),
6262 ID2SYM(RNODE_COLON2(node)->nd_mid), PUSH_VAL(DEFINED_CONST));
6263 }
6264 else {
6265 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD),
6266 ID2SYM(RNODE_COLON2(node)->nd_mid), PUSH_VAL(DEFINED_METHOD));
6267 }
6268 return;
6269 case NODE_COLON3:
6270 ADD_INSN1(ret, line_node, putobject, rb_cObject);
6271 ADD_INSN3(ret, line_node, defined,
6272 INT2FIX(DEFINED_CONST_FROM), ID2SYM(RNODE_COLON3(node)->nd_mid), PUSH_VAL(DEFINED_CONST));
6273 return;
6274
6275 /* method dispatch */
6276 case NODE_CALL:
6277 case NODE_OPCALL:
6278 case NODE_VCALL:
6279 case NODE_FCALL:
6280 case NODE_ATTRASGN:{
6281 const int explicit_receiver =
6282 (type == NODE_CALL || type == NODE_OPCALL ||
6283 (type == NODE_ATTRASGN && !private_recv_p(node)));
6284
6285 if (get_nd_args(node) || explicit_receiver) {
6286 if (!lfinish[1]) {
6287 lfinish[1] = NEW_LABEL(line);
6288 }
6289 if (!lfinish[2]) {
6290 lfinish[2] = NEW_LABEL(line);
6291 }
6292 }
6293 if (get_nd_args(node)) {
6294 defined_expr0(iseq, ret, get_nd_args(node), lfinish, Qfalse, false);
6295 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6296 }
6297 if (explicit_receiver) {
6298 defined_expr0(iseq, ret, get_nd_recv(node), lfinish, Qfalse, true);
6299 switch (nd_type(get_nd_recv(node))) {
6300 case NODE_CALL:
6301 case NODE_OPCALL:
6302 case NODE_VCALL:
6303 case NODE_FCALL:
6304 case NODE_ATTRASGN:
6305 ADD_INSNL(ret, line_node, branchunless, lfinish[2]);
6306 compile_call(iseq, ret, get_nd_recv(node), nd_type(get_nd_recv(node)), line_node, 0, true);
6307 break;
6308 default:
6309 ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
6310 NO_CHECK(COMPILE(ret, "defined/recv", get_nd_recv(node)));
6311 break;
6312 }
6313 if (keep_result) {
6314 ADD_INSN(ret, line_node, dup);
6315 }
6316 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD),
6317 ID2SYM(get_node_call_nd_mid(node)), PUSH_VAL(DEFINED_METHOD));
6318 }
6319 else {
6320 ADD_INSN(ret, line_node, putself);
6321 if (keep_result) {
6322 ADD_INSN(ret, line_node, dup);
6323 }
6324 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_FUNC),
6325 ID2SYM(get_node_call_nd_mid(node)), PUSH_VAL(DEFINED_METHOD));
6326 }
6327 return;
6328 }
6329
6330 case NODE_YIELD:
6331 ADD_INSN(ret, line_node, putnil);
6332 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_YIELD), 0,
6333 PUSH_VAL(DEFINED_YIELD));
6334 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
6335 return;
6336
6337 case NODE_BACK_REF:
6338 case NODE_NTH_REF:
6339 ADD_INSN(ret, line_node, putnil);
6340 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_REF),
6341 INT2FIX((RNODE_BACK_REF(node)->nd_nth << 1) | (type == NODE_BACK_REF)),
6342 PUSH_VAL(DEFINED_GVAR));
6343 return;
6344
6345 case NODE_SUPER:
6346 case NODE_ZSUPER:
6347 ADD_INSN(ret, line_node, putnil);
6348 ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_ZSUPER), 0,
6349 PUSH_VAL(DEFINED_ZSUPER));
6350 return;
6351
6352#undef PUSH_VAL
6353 case NODE_OP_ASGN1:
6354 case NODE_OP_ASGN2:
6355 case NODE_OP_ASGN_OR:
6356 case NODE_OP_ASGN_AND:
6357 case NODE_MASGN:
6358 case NODE_LASGN:
6359 case NODE_DASGN:
6360 case NODE_GASGN:
6361 case NODE_IASGN:
6362 case NODE_CDECL:
6363 case NODE_CVASGN:
6364 case NODE_OP_CDECL:
6365 expr_type = DEFINED_ASGN;
6366 break;
6367 }
6368
6369 RUBY_ASSERT(expr_type != DEFINED_NOT_DEFINED);
6370
6371 if (needstr != Qfalse) {
6372 VALUE str = rb_iseq_defined_string(expr_type);
6373 ADD_INSN1(ret, line_node, putobject, str);
6374 }
6375 else {
6376 ADD_INSN1(ret, line_node, putobject, Qtrue);
6377 }
6378}
6379
6380static void
6381build_defined_rescue_iseq(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const void *unused)
6382{
6383 ADD_SYNTHETIC_INSN(ret, 0, -1, putnil);
6384 iseq_set_exception_local_table(iseq);
6385}
6386
6387static void
6388defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
6389 const NODE *const node, LABEL **lfinish, VALUE needstr, bool ignore)
6390{
6391 LINK_ELEMENT *lcur = ret->last;
6392 defined_expr0(iseq, ret, node, lfinish, needstr, false);
6393 if (lfinish[1]) {
6394 int line = nd_line(node);
6395 LABEL *lstart = NEW_LABEL(line);
6396 LABEL *lend = NEW_LABEL(line);
6397 const rb_iseq_t *rescue;
6399 rb_iseq_new_with_callback_new_callback(build_defined_rescue_iseq, NULL);
6400 rescue = NEW_CHILD_ISEQ_WITH_CALLBACK(ifunc,
6401 rb_str_concat(rb_str_new2("defined guard in "),
6402 ISEQ_BODY(iseq)->location.label),
6403 ISEQ_TYPE_RESCUE, 0);
6404 lstart->rescued = LABEL_RESCUE_BEG;
6405 lend->rescued = LABEL_RESCUE_END;
6406 APPEND_LABEL(ret, lcur, lstart);
6407 ADD_LABEL(ret, lend);
6408 if (!ignore) {
6409 ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
6410 }
6411 }
6412}
6413
6414static int
6415compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr, bool ignore)
6416{
6417 const int line = nd_line(node);
6418 const NODE *line_node = node;
6419 if (!RNODE_DEFINED(node)->nd_head) {
6420 VALUE str = rb_iseq_defined_string(DEFINED_NIL);
6421 ADD_INSN1(ret, line_node, putobject, str);
6422 }
6423 else {
6424 LABEL *lfinish[3];
6425 LINK_ELEMENT *last = ret->last;
6426 lfinish[0] = NEW_LABEL(line);
6427 lfinish[1] = 0;
6428 lfinish[2] = 0;
6429 defined_expr(iseq, ret, RNODE_DEFINED(node)->nd_head, lfinish, needstr, ignore);
6430 if (lfinish[1]) {
6431 ELEM_INSERT_NEXT(last, &new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(putnil), 0)->link);
6432 ADD_INSN(ret, line_node, swap);
6433 if (lfinish[2]) {
6434 ADD_LABEL(ret, lfinish[2]);
6435 }
6436 ADD_INSN(ret, line_node, pop);
6437 ADD_LABEL(ret, lfinish[1]);
6438 }
6439 ADD_LABEL(ret, lfinish[0]);
6440 }
6441 return COMPILE_OK;
6442}
6443
6444static VALUE
6445make_name_for_block(const rb_iseq_t *orig_iseq)
6446{
6447 int level = 1;
6448 const rb_iseq_t *iseq = orig_iseq;
6449
6450 if (ISEQ_BODY(orig_iseq)->parent_iseq != 0) {
6451 while (ISEQ_BODY(orig_iseq)->local_iseq != iseq) {
6452 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_BLOCK) {
6453 level++;
6454 }
6455 iseq = ISEQ_BODY(iseq)->parent_iseq;
6456 }
6457 }
6458
6459 if (level == 1) {
6460 return rb_sprintf("block in %"PRIsVALUE, ISEQ_BODY(iseq)->location.label);
6461 }
6462 else {
6463 return rb_sprintf("block (%d levels) in %"PRIsVALUE, level, ISEQ_BODY(iseq)->location.label);
6464 }
6465}
6466
6467static void
6468push_ensure_entry(rb_iseq_t *iseq,
6470 struct ensure_range *er, const void *const node)
6471{
6472 enl->ensure_node = node;
6473 enl->prev = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack; /* prev */
6474 enl->erange = er;
6475 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl;
6476}
6477
6478static void
6479add_ensure_range(rb_iseq_t *iseq, struct ensure_range *erange,
6480 LABEL *lstart, LABEL *lend)
6481{
6482 struct ensure_range *ne =
6483 compile_data_alloc_type(iseq, struct ensure_range);
6484
6485 while (erange->next != 0) {
6486 erange = erange->next;
6487 }
6488 ne->next = 0;
6489 ne->begin = lend;
6490 ne->end = erange->end;
6491 erange->end = lstart;
6492
6493 erange->next = ne;
6494}
6495
6496static bool
6497can_add_ensure_iseq(const rb_iseq_t *iseq)
6498{
6500 if (ISEQ_COMPILE_DATA(iseq)->in_rescue && (e = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack) != NULL) {
6501 while (e) {
6502 if (e->ensure_node) return false;
6503 e = e->prev;
6504 }
6505 }
6506 return true;
6507}
6508
6509static void
6510add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return)
6511{
6512 RUBY_ASSERT(can_add_ensure_iseq(iseq));
6513
6515 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
6516 struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
6517 DECL_ANCHOR(ensure);
6518
6519 INIT_ANCHOR(ensure);
6520 while (enlp) {
6521 if (enlp->erange != NULL) {
6522 DECL_ANCHOR(ensure_part);
6523 LABEL *lstart = NEW_LABEL(0);
6524 LABEL *lend = NEW_LABEL(0);
6525 INIT_ANCHOR(ensure_part);
6526
6527 add_ensure_range(iseq, enlp->erange, lstart, lend);
6528
6529 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enlp->prev;
6530 ADD_LABEL(ensure_part, lstart);
6531 NO_CHECK(COMPILE_POPPED(ensure_part, "ensure part", enlp->ensure_node));
6532 ADD_LABEL(ensure_part, lend);
6533 ADD_SEQ(ensure, ensure_part);
6534 }
6535 else {
6536 if (!is_return) {
6537 break;
6538 }
6539 }
6540 enlp = enlp->prev;
6541 }
6542 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = prev_enlp;
6543 ADD_SEQ(ret, ensure);
6544}
6545
6546#if RUBY_DEBUG
6547static int
6548check_keyword(const NODE *node)
6549{
6550 /* This check is essentially a code clone of compile_keyword_arg. */
6551
6552 if (nd_type_p(node, NODE_LIST)) {
6553 while (RNODE_LIST(node)->nd_next) {
6554 node = RNODE_LIST(node)->nd_next;
6555 }
6556 node = RNODE_LIST(node)->nd_head;
6557 }
6558
6559 return keyword_node_p(node);
6560}
6561#endif
6562
6563static bool
6564keyword_node_single_splat_p(NODE *kwnode)
6565{
6566 RUBY_ASSERT(keyword_node_p(kwnode));
6567
6568 NODE *node = RNODE_HASH(kwnode)->nd_head;
6569 return RNODE_LIST(node)->nd_head == NULL &&
6570 RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next == NULL;
6571}
6572
6573static void
6574compile_single_keyword_splat_mutable(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
6575 NODE *kwnode, unsigned int *flag_ptr)
6576{
6577 *flag_ptr |= VM_CALL_KW_SPLAT_MUT;
6578 ADD_INSN1(args, argn, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6579 ADD_INSN1(args, argn, newhash, INT2FIX(0));
6580 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6581 ADD_SEND(args, argn, id_core_hash_merge_kwd, INT2FIX(2));
6582}
6583
6584#define SPLATARRAY_FALSE 0
6585#define SPLATARRAY_TRUE 1
6586#define DUP_SINGLE_KW_SPLAT 2
6587
6588static int
6589setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
6590 unsigned int *dup_rest, unsigned int *flag_ptr, struct rb_callinfo_kwarg **kwarg_ptr)
6591{
6592 if (!argn) return 0;
6593
6594 NODE *kwnode = NULL;
6595
6596 switch (nd_type(argn)) {
6597 case NODE_LIST: {
6598 // f(x, y, z)
6599 int len = compile_args(iseq, args, argn, &kwnode);
6600 RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_ARGS_SPLAT) == 0);
6601
6602 if (kwnode) {
6603 if (compile_keyword_arg(iseq, args, kwnode, kwarg_ptr, flag_ptr)) {
6604 len -= 1;
6605 }
6606 else {
6607 if (keyword_node_single_splat_p(kwnode) && (*dup_rest & DUP_SINGLE_KW_SPLAT)) {
6608 compile_single_keyword_splat_mutable(iseq, args, argn, kwnode, flag_ptr);
6609 }
6610 else {
6611 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6612 }
6613 }
6614 }
6615
6616 return len;
6617 }
6618 case NODE_SPLAT: {
6619 // f(*a)
6620 NO_CHECK(COMPILE(args, "args (splat)", RNODE_SPLAT(argn)->nd_head));
6621 ADD_INSN1(args, argn, splatarray, RBOOL(*dup_rest & SPLATARRAY_TRUE));
6622 if (*dup_rest & SPLATARRAY_TRUE) *dup_rest &= ~SPLATARRAY_TRUE;
6623 if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT;
6624 RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_KW_SPLAT) == 0);
6625 return 1;
6626 }
6627 case NODE_ARGSCAT: {
6628 if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT;
6629 int argc = setup_args_core(iseq, args, RNODE_ARGSCAT(argn)->nd_head, dup_rest, NULL, NULL);
6630 bool args_pushed = false;
6631
6632 if (nd_type_p(RNODE_ARGSCAT(argn)->nd_body, NODE_LIST)) {
6633 int rest_len = compile_args(iseq, args, RNODE_ARGSCAT(argn)->nd_body, &kwnode);
6634 if (kwnode) rest_len--;
6635 ADD_INSN1(args, argn, pushtoarray, INT2FIX(rest_len));
6636 args_pushed = true;
6637 }
6638 else {
6639 RUBY_ASSERT(!check_keyword(RNODE_ARGSCAT(argn)->nd_body));
6640 NO_CHECK(COMPILE(args, "args (cat: splat)", RNODE_ARGSCAT(argn)->nd_body));
6641 }
6642
6643 if (nd_type_p(RNODE_ARGSCAT(argn)->nd_head, NODE_LIST)) {
6644 ADD_INSN1(args, argn, splatarray, RBOOL(*dup_rest & SPLATARRAY_TRUE));
6645 if (*dup_rest & SPLATARRAY_TRUE) *dup_rest &= ~SPLATARRAY_TRUE;
6646 argc += 1;
6647 }
6648 else if (!args_pushed) {
6649 ADD_INSN(args, argn, concattoarray);
6650 }
6651
6652 // f(..., *a, ..., k1:1, ...) #=> f(..., *[*a, ...], **{k1:1, ...})
6653 if (kwnode) {
6654 // kwsplat
6655 *flag_ptr |= VM_CALL_KW_SPLAT;
6656 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6657 argc += 1;
6658 }
6659
6660 return argc;
6661 }
6662 case NODE_ARGSPUSH: {
6663 if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT;
6664 int argc = setup_args_core(iseq, args, RNODE_ARGSPUSH(argn)->nd_head, dup_rest, NULL, NULL);
6665
6666 if (nd_type_p(RNODE_ARGSPUSH(argn)->nd_body, NODE_LIST)) {
6667 int rest_len = compile_args(iseq, args, RNODE_ARGSPUSH(argn)->nd_body, &kwnode);
6668 if (kwnode) rest_len--;
6669 ADD_INSN1(args, argn, newarray, INT2FIX(rest_len));
6670 ADD_INSN1(args, argn, pushtoarray, INT2FIX(1));
6671 }
6672 else {
6673 if (keyword_node_p(RNODE_ARGSPUSH(argn)->nd_body)) {
6674 kwnode = RNODE_ARGSPUSH(argn)->nd_body;
6675 }
6676 else {
6677 NO_CHECK(COMPILE(args, "args (cat: splat)", RNODE_ARGSPUSH(argn)->nd_body));
6678 ADD_INSN1(args, argn, pushtoarray, INT2FIX(1));
6679 }
6680 }
6681
6682 if (kwnode) {
6683 // f(*a, k:1)
6684 *flag_ptr |= VM_CALL_KW_SPLAT;
6685 if (!keyword_node_single_splat_p(kwnode)) {
6686 *flag_ptr |= VM_CALL_KW_SPLAT_MUT;
6687 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6688 }
6689 else if (*dup_rest & DUP_SINGLE_KW_SPLAT) {
6690 compile_single_keyword_splat_mutable(iseq, args, argn, kwnode, flag_ptr);
6691 }
6692 else {
6693 compile_hash(iseq, args, kwnode, TRUE, FALSE);
6694 }
6695 argc += 1;
6696 }
6697
6698 return argc;
6699 }
6700 default: {
6701 UNKNOWN_NODE("setup_arg", argn, Qnil);
6702 }
6703 }
6704}
6705
6706static void
6707setup_args_splat_mut(unsigned int *flag, int dup_rest, int initial_dup_rest)
6708{
6709 if ((*flag & VM_CALL_ARGS_SPLAT) && dup_rest != initial_dup_rest) {
6710 *flag |= VM_CALL_ARGS_SPLAT_MUT;
6711 }
6712}
6713
6714static bool
6715setup_args_dup_rest_p(const NODE *argn)
6716{
6717 switch(nd_type(argn)) {
6718 case NODE_LVAR:
6719 case NODE_DVAR:
6720 case NODE_GVAR:
6721 case NODE_IVAR:
6722 case NODE_CVAR:
6723 case NODE_CONST:
6724 case NODE_COLON3:
6725 case NODE_INTEGER:
6726 case NODE_FLOAT:
6727 case NODE_RATIONAL:
6728 case NODE_IMAGINARY:
6729 case NODE_STR:
6730 case NODE_SYM:
6731 case NODE_REGX:
6732 case NODE_SELF:
6733 case NODE_NIL:
6734 case NODE_TRUE:
6735 case NODE_FALSE:
6736 case NODE_LAMBDA:
6737 case NODE_NTH_REF:
6738 case NODE_BACK_REF:
6739 return false;
6740 case NODE_COLON2:
6741 return setup_args_dup_rest_p(RNODE_COLON2(argn)->nd_head);
6742 case NODE_LIST:
6743 while (argn) {
6744 if (setup_args_dup_rest_p(RNODE_LIST(argn)->nd_head)) {
6745 return true;
6746 }
6747 argn = RNODE_LIST(argn)->nd_next;
6748 }
6749 return false;
6750 default:
6751 return true;
6752 }
6753}
6754
6755static VALUE
6756setup_args(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
6757 unsigned int *flag, struct rb_callinfo_kwarg **keywords)
6758{
6759 VALUE ret;
6760 unsigned int dup_rest = SPLATARRAY_TRUE, initial_dup_rest;
6761
6762 if (argn) {
6763 const NODE *check_arg = nd_type_p(argn, NODE_BLOCK_PASS) ?
6764 RNODE_BLOCK_PASS(argn)->nd_head : argn;
6765
6766 if (check_arg) {
6767 switch(nd_type(check_arg)) {
6768 case(NODE_SPLAT):
6769 // avoid caller side array allocation for f(*arg)
6770 dup_rest = SPLATARRAY_FALSE;
6771 break;
6772 case(NODE_ARGSCAT):
6773 // avoid caller side array allocation for f(1, *arg)
6774 dup_rest = !nd_type_p(RNODE_ARGSCAT(check_arg)->nd_head, NODE_LIST);
6775 break;
6776 case(NODE_ARGSPUSH):
6777 // avoid caller side array allocation for f(*arg, **hash) and f(1, *arg, **hash)
6778 dup_rest = !((nd_type_p(RNODE_ARGSPUSH(check_arg)->nd_head, NODE_SPLAT) ||
6779 (nd_type_p(RNODE_ARGSPUSH(check_arg)->nd_head, NODE_ARGSCAT) &&
6780 nd_type_p(RNODE_ARGSCAT(RNODE_ARGSPUSH(check_arg)->nd_head)->nd_head, NODE_LIST))) &&
6781 nd_type_p(RNODE_ARGSPUSH(check_arg)->nd_body, NODE_HASH) &&
6782 !RNODE_HASH(RNODE_ARGSPUSH(check_arg)->nd_body)->nd_brace);
6783
6784 if (dup_rest == SPLATARRAY_FALSE) {
6785 // require allocation for keyword key/value/splat that may modify splatted argument
6786 NODE *node = RNODE_HASH(RNODE_ARGSPUSH(check_arg)->nd_body)->nd_head;
6787 while (node) {
6788 NODE *key_node = RNODE_LIST(node)->nd_head;
6789 if (key_node && setup_args_dup_rest_p(key_node)) {
6790 dup_rest = SPLATARRAY_TRUE;
6791 break;
6792 }
6793
6794 node = RNODE_LIST(node)->nd_next;
6795 NODE *value_node = RNODE_LIST(node)->nd_head;
6796 if (setup_args_dup_rest_p(value_node)) {
6797 dup_rest = SPLATARRAY_TRUE;
6798 break;
6799 }
6800
6801 node = RNODE_LIST(node)->nd_next;
6802 }
6803 }
6804 break;
6805 default:
6806 break;
6807 }
6808 }
6809
6810 if (check_arg != argn && setup_args_dup_rest_p(RNODE_BLOCK_PASS(argn)->nd_body)) {
6811 // for block pass that may modify splatted argument, dup rest and kwrest if given
6812 dup_rest = SPLATARRAY_TRUE | DUP_SINGLE_KW_SPLAT;
6813 }
6814 }
6815 initial_dup_rest = dup_rest;
6816
6817 if (argn && nd_type_p(argn, NODE_BLOCK_PASS)) {
6818 DECL_ANCHOR(arg_block);
6819 INIT_ANCHOR(arg_block);
6820
6821 if (RNODE_BLOCK_PASS(argn)->forwarding && ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) {
6822 int idx = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size;// - get_local_var_idx(iseq, idDot3);
6823
6824 RUBY_ASSERT(nd_type_p(RNODE_BLOCK_PASS(argn)->nd_head, NODE_ARGSPUSH));
6825 const NODE * arg_node =
6826 RNODE_ARGSPUSH(RNODE_BLOCK_PASS(argn)->nd_head)->nd_head;
6827
6828 int argc = 0;
6829
6830 // Only compile leading args:
6831 // foo(x, y, ...)
6832 // ^^^^
6833 if (nd_type_p(arg_node, NODE_ARGSCAT)) {
6834 argc += setup_args_core(iseq, args, RNODE_ARGSCAT(arg_node)->nd_head, &dup_rest, flag, keywords);
6835 }
6836
6837 *flag |= VM_CALL_FORWARDING;
6838
6839 ADD_GETLOCAL(args, argn, idx, get_lvar_level(iseq));
6840 setup_args_splat_mut(flag, dup_rest, initial_dup_rest);
6841 return INT2FIX(argc);
6842 }
6843 else {
6844 *flag |= VM_CALL_ARGS_BLOCKARG;
6845
6846 NO_CHECK(COMPILE(arg_block, "block", RNODE_BLOCK_PASS(argn)->nd_body));
6847 }
6848
6849 if (LIST_INSN_SIZE_ONE(arg_block)) {
6850 LINK_ELEMENT *elem = FIRST_ELEMENT(arg_block);
6851 if (IS_INSN(elem)) {
6852 INSN *iobj = (INSN *)elem;
6853 if (iobj->insn_id == BIN(getblockparam)) {
6854 iobj->insn_id = BIN(getblockparamproxy);
6855 }
6856 }
6857 }
6858 ret = INT2FIX(setup_args_core(iseq, args, RNODE_BLOCK_PASS(argn)->nd_head, &dup_rest, flag, keywords));
6859 ADD_SEQ(args, arg_block);
6860 }
6861 else {
6862 ret = INT2FIX(setup_args_core(iseq, args, argn, &dup_rest, flag, keywords));
6863 }
6864 setup_args_splat_mut(flag, dup_rest, initial_dup_rest);
6865 return ret;
6866}
6867
6868static void
6869build_postexe_iseq(rb_iseq_t *iseq, LINK_ANCHOR *ret, const void *ptr)
6870{
6871 const NODE *body = ptr;
6872 int line = nd_line(body);
6873 VALUE argc = INT2FIX(0);
6874 const rb_iseq_t *block = NEW_CHILD_ISEQ(body, make_name_for_block(ISEQ_BODY(iseq)->parent_iseq), ISEQ_TYPE_BLOCK, line);
6875
6876 ADD_INSN1(ret, body, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
6877 ADD_CALL_WITH_BLOCK(ret, body, id_core_set_postexe, argc, block);
6878 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block);
6879 iseq_set_local_table(iseq, 0, 0);
6880}
6881
6882static void
6883compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
6884{
6885 const NODE *vars;
6886 LINK_ELEMENT *last;
6887 int line = nd_line(node);
6888 const NODE *line_node = node;
6889 LABEL *fail_label = NEW_LABEL(line), *end_label = NEW_LABEL(line);
6890
6891#if !(defined(NAMED_CAPTURE_BY_SVAR) && NAMED_CAPTURE_BY_SVAR-0)
6892 ADD_INSN1(ret, line_node, getglobal, ID2SYM(idBACKREF));
6893#else
6894 ADD_INSN2(ret, line_node, getspecial, INT2FIX(1) /* '~' */, INT2FIX(0));
6895#endif
6896 ADD_INSN(ret, line_node, dup);
6897 ADD_INSNL(ret, line_node, branchunless, fail_label);
6898
6899 for (vars = node; vars; vars = RNODE_BLOCK(vars)->nd_next) {
6900 INSN *cap;
6901 if (RNODE_BLOCK(vars)->nd_next) {
6902 ADD_INSN(ret, line_node, dup);
6903 }
6904 last = ret->last;
6905 NO_CHECK(COMPILE_POPPED(ret, "capture", RNODE_BLOCK(vars)->nd_head));
6906 last = last->next; /* putobject :var */
6907 cap = new_insn_send(iseq, nd_line(line_node), nd_node_id(line_node), idAREF, INT2FIX(1),
6908 NULL, INT2FIX(0), NULL);
6909 ELEM_INSERT_PREV(last->next, (LINK_ELEMENT *)cap);
6910#if !defined(NAMED_CAPTURE_SINGLE_OPT) || NAMED_CAPTURE_SINGLE_OPT-0
6911 if (!RNODE_BLOCK(vars)->nd_next && vars == node) {
6912 /* only one name */
6913 DECL_ANCHOR(nom);
6914
6915 INIT_ANCHOR(nom);
6916 ADD_INSNL(nom, line_node, jump, end_label);
6917 ADD_LABEL(nom, fail_label);
6918# if 0 /* $~ must be MatchData or nil */
6919 ADD_INSN(nom, line_node, pop);
6920 ADD_INSN(nom, line_node, putnil);
6921# endif
6922 ADD_LABEL(nom, end_label);
6923 (nom->last->next = cap->link.next)->prev = nom->last;
6924 (cap->link.next = nom->anchor.next)->prev = &cap->link;
6925 return;
6926 }
6927#endif
6928 }
6929 ADD_INSNL(ret, line_node, jump, end_label);
6930 ADD_LABEL(ret, fail_label);
6931 ADD_INSN(ret, line_node, pop);
6932 for (vars = node; vars; vars = RNODE_BLOCK(vars)->nd_next) {
6933 last = ret->last;
6934 NO_CHECK(COMPILE_POPPED(ret, "capture", RNODE_BLOCK(vars)->nd_head));
6935 last = last->next; /* putobject :var */
6936 ((INSN*)last)->insn_id = BIN(putnil);
6937 ((INSN*)last)->operand_size = 0;
6938 }
6939 ADD_LABEL(ret, end_label);
6940}
6941
6942static int
6943optimizable_range_item_p(const NODE *n)
6944{
6945 if (!n) return FALSE;
6946 switch (nd_type(n)) {
6947 case NODE_LINE:
6948 return TRUE;
6949 case NODE_INTEGER:
6950 return TRUE;
6951 case NODE_NIL:
6952 return TRUE;
6953 default:
6954 return FALSE;
6955 }
6956}
6957
6958static VALUE
6959optimized_range_item(const NODE *n)
6960{
6961 switch (nd_type(n)) {
6962 case NODE_LINE:
6963 return rb_node_line_lineno_val(n);
6964 case NODE_INTEGER:
6965 return rb_node_integer_literal_val(n);
6966 case NODE_FLOAT:
6967 return rb_node_float_literal_val(n);
6968 case NODE_RATIONAL:
6969 return rb_node_rational_literal_val(n);
6970 case NODE_IMAGINARY:
6971 return rb_node_imaginary_literal_val(n);
6972 case NODE_NIL:
6973 return Qnil;
6974 default:
6975 rb_bug("unexpected node: %s", ruby_node_name(nd_type(n)));
6976 }
6977}
6978
6979static int
6980compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
6981{
6982 const NODE *const node_body = type == NODE_IF ? RNODE_IF(node)->nd_body : RNODE_UNLESS(node)->nd_else;
6983 const NODE *const node_else = type == NODE_IF ? RNODE_IF(node)->nd_else : RNODE_UNLESS(node)->nd_body;
6984
6985 const int line = nd_line(node);
6986 const NODE *line_node = node;
6987 DECL_ANCHOR(cond_seq);
6988 LABEL *then_label, *else_label, *end_label;
6989 VALUE branches = Qfalse;
6990
6991 INIT_ANCHOR(cond_seq);
6992 then_label = NEW_LABEL(line);
6993 else_label = NEW_LABEL(line);
6994 end_label = 0;
6995
6996 NODE *cond = RNODE_IF(node)->nd_cond;
6997 if (nd_type(cond) == NODE_BLOCK) {
6998 cond = RNODE_BLOCK(cond)->nd_head;
6999 }
7000
7001 CHECK(compile_branch_condition(iseq, cond_seq, cond, then_label, else_label));
7002 ADD_SEQ(ret, cond_seq);
7003
7004 if (then_label->refcnt && else_label->refcnt) {
7005 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_IF ? "if" : "unless");
7006 }
7007
7008 if (then_label->refcnt) {
7009 ADD_LABEL(ret, then_label);
7010
7011 DECL_ANCHOR(then_seq);
7012 INIT_ANCHOR(then_seq);
7013 CHECK(COMPILE_(then_seq, "then", node_body, popped));
7014
7015 if (else_label->refcnt) {
7016 const NODE *const coverage_node = node_body ? node_body : node;
7017 add_trace_branch_coverage(
7018 iseq,
7019 ret,
7020 nd_code_loc(coverage_node),
7021 nd_node_id(coverage_node),
7022 0,
7023 type == NODE_IF ? "then" : "else",
7024 branches);
7025 end_label = NEW_LABEL(line);
7026 ADD_INSNL(then_seq, line_node, jump, end_label);
7027 if (!popped) {
7028 ADD_INSN(then_seq, line_node, pop);
7029 }
7030 }
7031 ADD_SEQ(ret, then_seq);
7032 }
7033
7034 if (else_label->refcnt) {
7035 ADD_LABEL(ret, else_label);
7036
7037 DECL_ANCHOR(else_seq);
7038 INIT_ANCHOR(else_seq);
7039 CHECK(COMPILE_(else_seq, "else", node_else, popped));
7040
7041 if (then_label->refcnt) {
7042 const NODE *const coverage_node = node_else ? node_else : node;
7043 add_trace_branch_coverage(
7044 iseq,
7045 ret,
7046 nd_code_loc(coverage_node),
7047 nd_node_id(coverage_node),
7048 1,
7049 type == NODE_IF ? "else" : "then",
7050 branches);
7051 }
7052 ADD_SEQ(ret, else_seq);
7053 }
7054
7055 if (end_label) {
7056 ADD_LABEL(ret, end_label);
7057 }
7058
7059 return COMPILE_OK;
7060}
7061
7062static int
7063compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
7064{
7065 const NODE *vals;
7066 const NODE *node = orig_node;
7067 LABEL *endlabel, *elselabel;
7068 DECL_ANCHOR(head);
7069 DECL_ANCHOR(body_seq);
7070 DECL_ANCHOR(cond_seq);
7071 int only_special_literals = 1;
7072 VALUE literals = rb_hash_new_with_size_and_type(0, 0, &cdhash_type);
7073 int line;
7074 enum node_type type;
7075 const NODE *line_node;
7076 VALUE branches = Qfalse;
7077 int branch_id = 0;
7078
7079 INIT_ANCHOR(head);
7080 INIT_ANCHOR(body_seq);
7081 INIT_ANCHOR(cond_seq);
7082
7083 CHECK(COMPILE(head, "case base", RNODE_CASE(node)->nd_head));
7084
7085 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case");
7086
7087 node = RNODE_CASE(node)->nd_body;
7088 EXPECT_NODE("NODE_CASE", node, NODE_WHEN, COMPILE_NG);
7089 type = nd_type(node);
7090 line = nd_line(node);
7091 line_node = node;
7092
7093 endlabel = NEW_LABEL(line);
7094 elselabel = NEW_LABEL(line);
7095
7096 ADD_SEQ(ret, head); /* case VAL */
7097
7098 while (type == NODE_WHEN) {
7099 LABEL *l1;
7100
7101 l1 = NEW_LABEL(line);
7102 ADD_LABEL(body_seq, l1);
7103 ADD_INSN(body_seq, line_node, pop);
7104
7105 const NODE *const coverage_node = RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node;
7106 add_trace_branch_coverage(
7107 iseq,
7108 body_seq,
7109 nd_code_loc(coverage_node),
7110 nd_node_id(coverage_node),
7111 branch_id++,
7112 "when",
7113 branches);
7114
7115 CHECK(COMPILE_(body_seq, "when body", RNODE_WHEN(node)->nd_body, popped));
7116 ADD_INSNL(body_seq, line_node, jump, endlabel);
7117
7118 vals = RNODE_WHEN(node)->nd_head;
7119 if (vals) {
7120 switch (nd_type(vals)) {
7121 case NODE_LIST:
7122 only_special_literals = when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals);
7123 if (only_special_literals < 0) return COMPILE_NG;
7124 break;
7125 case NODE_SPLAT:
7126 case NODE_ARGSCAT:
7127 case NODE_ARGSPUSH:
7128 only_special_literals = 0;
7129 CHECK(when_splat_vals(iseq, cond_seq, vals, l1, only_special_literals, literals));
7130 break;
7131 default:
7132 UNKNOWN_NODE("NODE_CASE", vals, COMPILE_NG);
7133 }
7134 }
7135 else {
7136 EXPECT_NODE_NONULL("NODE_CASE", node, NODE_LIST, COMPILE_NG);
7137 }
7138
7139 node = RNODE_WHEN(node)->nd_next;
7140 if (!node) {
7141 break;
7142 }
7143 type = nd_type(node);
7144 line = nd_line(node);
7145 line_node = node;
7146 }
7147 /* else */
7148 if (node) {
7149 ADD_LABEL(cond_seq, elselabel);
7150 ADD_INSN(cond_seq, line_node, pop);
7151 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(node), nd_node_id(node), branch_id, "else", branches);
7152 CHECK(COMPILE_(cond_seq, "else", node, popped));
7153 ADD_INSNL(cond_seq, line_node, jump, endlabel);
7154 }
7155 else {
7156 debugs("== else (implicit)\n");
7157 ADD_LABEL(cond_seq, elselabel);
7158 ADD_INSN(cond_seq, orig_node, pop);
7159 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(orig_node), nd_node_id(orig_node), branch_id, "else", branches);
7160 if (!popped) {
7161 ADD_INSN(cond_seq, orig_node, putnil);
7162 }
7163 ADD_INSNL(cond_seq, orig_node, jump, endlabel);
7164 }
7165
7166 if (only_special_literals && ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
7167 ADD_INSN(ret, orig_node, dup);
7168 ADD_INSN2(ret, orig_node, opt_case_dispatch, literals, elselabel);
7169 RB_OBJ_WRITTEN(iseq, Qundef, literals);
7170 LABEL_REF(elselabel);
7171 }
7172
7173 ADD_SEQ(ret, cond_seq);
7174 ADD_SEQ(ret, body_seq);
7175 ADD_LABEL(ret, endlabel);
7176 return COMPILE_OK;
7177}
7178
7179static int
7180compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
7181{
7182 const NODE *vals;
7183 const NODE *val;
7184 const NODE *node = RNODE_CASE2(orig_node)->nd_body;
7185 LABEL *endlabel;
7186 DECL_ANCHOR(body_seq);
7187 VALUE branches = Qfalse;
7188 int branch_id = 0;
7189
7190 branches = decl_branch_base(iseq, PTR2NUM(orig_node), nd_code_loc(orig_node), "case");
7191
7192 INIT_ANCHOR(body_seq);
7193 endlabel = NEW_LABEL(nd_line(node));
7194
7195 while (node && nd_type_p(node, NODE_WHEN)) {
7196 const int line = nd_line(node);
7197 LABEL *l1 = NEW_LABEL(line);
7198 ADD_LABEL(body_seq, l1);
7199
7200 const NODE *const coverage_node = RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node;
7201 add_trace_branch_coverage(
7202 iseq,
7203 body_seq,
7204 nd_code_loc(coverage_node),
7205 nd_node_id(coverage_node),
7206 branch_id++,
7207 "when",
7208 branches);
7209
7210 CHECK(COMPILE_(body_seq, "when", RNODE_WHEN(node)->nd_body, popped));
7211 ADD_INSNL(body_seq, node, jump, endlabel);
7212
7213 vals = RNODE_WHEN(node)->nd_head;
7214 if (!vals) {
7215 EXPECT_NODE_NONULL("NODE_WHEN", node, NODE_LIST, COMPILE_NG);
7216 }
7217 switch (nd_type(vals)) {
7218 case NODE_LIST:
7219 while (vals) {
7220 LABEL *lnext;
7221 val = RNODE_LIST(vals)->nd_head;
7222 lnext = NEW_LABEL(nd_line(val));
7223 debug_compile("== when2\n", (void)0);
7224 CHECK(compile_branch_condition(iseq, ret, val, l1, lnext));
7225 ADD_LABEL(ret, lnext);
7226 vals = RNODE_LIST(vals)->nd_next;
7227 }
7228 break;
7229 case NODE_SPLAT:
7230 case NODE_ARGSCAT:
7231 case NODE_ARGSPUSH:
7232 ADD_INSN(ret, vals, putnil);
7233 CHECK(COMPILE(ret, "when2/cond splat", vals));
7234 ADD_INSN1(ret, vals, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
7235 ADD_INSNL(ret, vals, branchif, l1);
7236 break;
7237 default:
7238 UNKNOWN_NODE("NODE_WHEN", vals, COMPILE_NG);
7239 }
7240 node = RNODE_WHEN(node)->nd_next;
7241 }
7242 /* else */
7243 const NODE *const coverage_node = node ? node : orig_node;
7244 add_trace_branch_coverage(
7245 iseq,
7246 ret,
7247 nd_code_loc(coverage_node),
7248 nd_node_id(coverage_node),
7249 branch_id,
7250 "else",
7251 branches);
7252 CHECK(COMPILE_(ret, "else", node, popped));
7253 ADD_INSNL(ret, orig_node, jump, endlabel);
7254
7255 ADD_SEQ(ret, body_seq);
7256 ADD_LABEL(ret, endlabel);
7257 return COMPILE_OK;
7258}
7259
7260static 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);
7261
7262static 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);
7263static 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);
7264static int iseq_compile_pattern_set_general_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE errmsg, int base_index);
7265static 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);
7266static int iseq_compile_pattern_set_eqq_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int base_index);
7267
7268#define CASE3_BI_OFFSET_DECONSTRUCTED_CACHE 0
7269#define CASE3_BI_OFFSET_ERROR_STRING 1
7270#define CASE3_BI_OFFSET_KEY_ERROR_P 2
7271#define CASE3_BI_OFFSET_KEY_ERROR_MATCHEE 3
7272#define CASE3_BI_OFFSET_KEY_ERROR_KEY 4
7273
7274static int
7275iseq_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)
7276{
7277 const int line = nd_line(node);
7278 const NODE *line_node = node;
7279
7280 switch (nd_type(node)) {
7281 case NODE_ARYPTN: {
7282 /*
7283 * if pattern.use_rest_num?
7284 * rest_num = 0
7285 * end
7286 * if pattern.has_constant_node?
7287 * unless pattern.constant === obj
7288 * goto match_failed
7289 * end
7290 * end
7291 * unless obj.respond_to?(:deconstruct)
7292 * goto match_failed
7293 * end
7294 * d = obj.deconstruct
7295 * unless Array === d
7296 * goto type_error
7297 * end
7298 * min_argc = pattern.pre_args_num + pattern.post_args_num
7299 * if pattern.has_rest_arg?
7300 * unless d.length >= min_argc
7301 * goto match_failed
7302 * end
7303 * else
7304 * unless d.length == min_argc
7305 * goto match_failed
7306 * end
7307 * end
7308 * pattern.pre_args_num.each do |i|
7309 * unless pattern.pre_args[i].match?(d[i])
7310 * goto match_failed
7311 * end
7312 * end
7313 * if pattern.use_rest_num?
7314 * rest_num = d.length - min_argc
7315 * if pattern.has_rest_arg? && pattern.has_rest_arg_id # not `*`, but `*rest`
7316 * unless pattern.rest_arg.match?(d[pattern.pre_args_num, rest_num])
7317 * goto match_failed
7318 * end
7319 * end
7320 * end
7321 * pattern.post_args_num.each do |i|
7322 * j = pattern.pre_args_num + i
7323 * j += rest_num
7324 * unless pattern.post_args[i].match?(d[j])
7325 * goto match_failed
7326 * end
7327 * end
7328 * goto matched
7329 * type_error:
7330 * FrozenCore.raise TypeError
7331 * match_failed:
7332 * goto unmatched
7333 */
7334 const NODE *args = RNODE_ARYPTN(node)->pre_args;
7335 const int pre_args_num = RNODE_ARYPTN(node)->pre_args ? rb_long2int(RNODE_LIST(RNODE_ARYPTN(node)->pre_args)->as.nd_alen) : 0;
7336 const int post_args_num = RNODE_ARYPTN(node)->post_args ? rb_long2int(RNODE_LIST(RNODE_ARYPTN(node)->post_args)->as.nd_alen) : 0;
7337
7338 const int min_argc = pre_args_num + post_args_num;
7339 const int use_rest_num = RNODE_ARYPTN(node)->rest_arg && (NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg) ||
7340 (!NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg) && post_args_num > 0));
7341
7342 LABEL *match_failed, *type_error, *deconstruct, *deconstructed;
7343 int i;
7344 match_failed = NEW_LABEL(line);
7345 type_error = NEW_LABEL(line);
7346 deconstruct = NEW_LABEL(line);
7347 deconstructed = NEW_LABEL(line);
7348
7349 if (use_rest_num) {
7350 ADD_INSN1(ret, line_node, putobject, INT2FIX(0)); /* allocate stack for rest_num */
7351 ADD_INSN(ret, line_node, swap);
7352 if (base_index) {
7353 base_index++;
7354 }
7355 }
7356
7357 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
7358
7359 CHECK(iseq_compile_array_deconstruct(iseq, ret, node, deconstruct, deconstructed, match_failed, type_error, in_single_pattern, base_index, use_deconstructed_cache));
7360
7361 ADD_INSN(ret, line_node, dup);
7362 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
7363 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
7364 ADD_SEND(ret, line_node, RNODE_ARYPTN(node)->rest_arg ? idGE : idEq, INT2FIX(1)); // (1)
7365 if (in_single_pattern) {
7366 CHECK(iseq_compile_pattern_set_length_errmsg(iseq, ret, node,
7367 RNODE_ARYPTN(node)->rest_arg ? rb_fstring_lit("%p length mismatch (given %p, expected %p+)") :
7368 rb_fstring_lit("%p length mismatch (given %p, expected %p)"),
7369 INT2FIX(min_argc), base_index + 1 /* (1) */));
7370 }
7371 ADD_INSNL(ret, line_node, branchunless, match_failed);
7372
7373 for (i = 0; i < pre_args_num; i++) {
7374 ADD_INSN(ret, line_node, dup);
7375 ADD_INSN1(ret, line_node, putobject, INT2FIX(i));
7376 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (2)
7377 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));
7378 args = RNODE_LIST(args)->nd_next;
7379 }
7380
7381 if (RNODE_ARYPTN(node)->rest_arg) {
7382 if (NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg)) {
7383 ADD_INSN(ret, line_node, dup);
7384 ADD_INSN1(ret, line_node, putobject, INT2FIX(pre_args_num));
7385 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
7386 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
7387 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
7388 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1));
7389 ADD_INSN1(ret, line_node, setn, INT2FIX(4));
7390 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (3)
7391
7392 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));
7393 }
7394 else {
7395 if (post_args_num > 0) {
7396 ADD_INSN(ret, line_node, dup);
7397 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
7398 ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc));
7399 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1));
7400 ADD_INSN1(ret, line_node, setn, INT2FIX(2));
7401 ADD_INSN(ret, line_node, pop);
7402 }
7403 }
7404 }
7405
7406 args = RNODE_ARYPTN(node)->post_args;
7407 for (i = 0; i < post_args_num; i++) {
7408 ADD_INSN(ret, line_node, dup);
7409
7410 ADD_INSN1(ret, line_node, putobject, INT2FIX(pre_args_num + i));
7411 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7412 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
7413
7414 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (4)
7415 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));
7416 args = RNODE_LIST(args)->nd_next;
7417 }
7418
7419 ADD_INSN(ret, line_node, pop);
7420 if (use_rest_num) {
7421 ADD_INSN(ret, line_node, pop);
7422 }
7423 ADD_INSNL(ret, line_node, jump, matched);
7424 ADD_INSN(ret, line_node, putnil);
7425 if (use_rest_num) {
7426 ADD_INSN(ret, line_node, putnil);
7427 }
7428
7429 ADD_LABEL(ret, type_error);
7430 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7431 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
7432 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct must return Array"));
7433 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
7434 ADD_INSN(ret, line_node, pop);
7435
7436 ADD_LABEL(ret, match_failed);
7437 ADD_INSN(ret, line_node, pop);
7438 if (use_rest_num) {
7439 ADD_INSN(ret, line_node, pop);
7440 }
7441 ADD_INSNL(ret, line_node, jump, unmatched);
7442
7443 break;
7444 }
7445 case NODE_FNDPTN: {
7446 /*
7447 * if pattern.has_constant_node?
7448 * unless pattern.constant === obj
7449 * goto match_failed
7450 * end
7451 * end
7452 * unless obj.respond_to?(:deconstruct)
7453 * goto match_failed
7454 * end
7455 * d = obj.deconstruct
7456 * unless Array === d
7457 * goto type_error
7458 * end
7459 * unless d.length >= pattern.args_num
7460 * goto match_failed
7461 * end
7462 *
7463 * begin
7464 * len = d.length
7465 * limit = d.length - pattern.args_num
7466 * i = 0
7467 * while i <= limit
7468 * if pattern.args_num.times.all? {|j| pattern.args[j].match?(d[i+j]) }
7469 * if pattern.has_pre_rest_arg_id
7470 * unless pattern.pre_rest_arg.match?(d[0, i])
7471 * goto find_failed
7472 * end
7473 * end
7474 * if pattern.has_post_rest_arg_id
7475 * unless pattern.post_rest_arg.match?(d[i+pattern.args_num, len])
7476 * goto find_failed
7477 * end
7478 * end
7479 * goto find_succeeded
7480 * end
7481 * i+=1
7482 * end
7483 * find_failed:
7484 * goto match_failed
7485 * find_succeeded:
7486 * end
7487 *
7488 * goto matched
7489 * type_error:
7490 * FrozenCore.raise TypeError
7491 * match_failed:
7492 * goto unmatched
7493 */
7494 const NODE *args = RNODE_FNDPTN(node)->args;
7495 const int args_num = RNODE_FNDPTN(node)->args ? rb_long2int(RNODE_LIST(RNODE_FNDPTN(node)->args)->as.nd_alen) : 0;
7496
7497 LABEL *match_failed, *type_error, *deconstruct, *deconstructed;
7498 match_failed = NEW_LABEL(line);
7499 type_error = NEW_LABEL(line);
7500 deconstruct = NEW_LABEL(line);
7501 deconstructed = NEW_LABEL(line);
7502
7503 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
7504
7505 CHECK(iseq_compile_array_deconstruct(iseq, ret, node, deconstruct, deconstructed, match_failed, type_error, in_single_pattern, base_index, use_deconstructed_cache));
7506
7507 ADD_INSN(ret, line_node, dup);
7508 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
7509 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
7510 ADD_SEND(ret, line_node, idGE, INT2FIX(1)); // (1)
7511 if (in_single_pattern) {
7512 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) */));
7513 }
7514 ADD_INSNL(ret, line_node, branchunless, match_failed);
7515
7516 {
7517 LABEL *while_begin = NEW_LABEL(nd_line(node));
7518 LABEL *next_loop = NEW_LABEL(nd_line(node));
7519 LABEL *find_succeeded = NEW_LABEL(line);
7520 LABEL *find_failed = NEW_LABEL(nd_line(node));
7521 int j;
7522
7523 ADD_INSN(ret, line_node, dup); /* allocate stack for len */
7524 ADD_SEND(ret, line_node, idLength, INT2FIX(0)); // (2)
7525
7526 ADD_INSN(ret, line_node, dup); /* allocate stack for limit */
7527 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
7528 ADD_SEND(ret, line_node, idMINUS, INT2FIX(1)); // (3)
7529
7530 ADD_INSN1(ret, line_node, putobject, INT2FIX(0)); /* allocate stack for i */ // (4)
7531
7532 ADD_LABEL(ret, while_begin);
7533
7534 ADD_INSN(ret, line_node, dup);
7535 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
7536 ADD_SEND(ret, line_node, idLE, INT2FIX(1));
7537 ADD_INSNL(ret, line_node, branchunless, find_failed);
7538
7539 for (j = 0; j < args_num; j++) {
7540 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7541 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
7542 if (j != 0) {
7543 ADD_INSN1(ret, line_node, putobject, INT2FIX(j));
7544 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
7545 }
7546 ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (5)
7547
7548 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));
7549 args = RNODE_LIST(args)->nd_next;
7550 }
7551
7552 if (NODE_NAMED_REST_P(RNODE_FNDPTN(node)->pre_rest_arg)) {
7553 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7554 ADD_INSN1(ret, line_node, putobject, INT2FIX(0));
7555 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
7556 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (6)
7557 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));
7558 }
7559 if (NODE_NAMED_REST_P(RNODE_FNDPTN(node)->post_rest_arg)) {
7560 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7561 ADD_INSN1(ret, line_node, topn, INT2FIX(1));
7562 ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num));
7563 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
7564 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
7565 ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (7)
7566 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));
7567 }
7568 ADD_INSNL(ret, line_node, jump, find_succeeded);
7569
7570 ADD_LABEL(ret, next_loop);
7571 ADD_INSN1(ret, line_node, putobject, INT2FIX(1));
7572 ADD_SEND(ret, line_node, idPLUS, INT2FIX(1));
7573 ADD_INSNL(ret, line_node, jump, while_begin);
7574
7575 ADD_LABEL(ret, find_failed);
7576 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(3));
7577 if (in_single_pattern) {
7578 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7579 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("%p does not match to find pattern"));
7580 ADD_INSN1(ret, line_node, topn, INT2FIX(2));
7581 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(2)); // (8)
7582 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (8) */)); // (9)
7583
7584 ADD_INSN1(ret, line_node, putobject, Qfalse);
7585 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (8), (9) */));
7586
7587 ADD_INSN(ret, line_node, pop);
7588 ADD_INSN(ret, line_node, pop);
7589 }
7590 ADD_INSNL(ret, line_node, jump, match_failed);
7591 ADD_INSN1(ret, line_node, dupn, INT2FIX(3));
7592
7593 ADD_LABEL(ret, find_succeeded);
7594 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(3));
7595 }
7596
7597 ADD_INSN(ret, line_node, pop);
7598 ADD_INSNL(ret, line_node, jump, matched);
7599 ADD_INSN(ret, line_node, putnil);
7600
7601 ADD_LABEL(ret, type_error);
7602 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7603 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
7604 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct must return Array"));
7605 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
7606 ADD_INSN(ret, line_node, pop);
7607
7608 ADD_LABEL(ret, match_failed);
7609 ADD_INSN(ret, line_node, pop);
7610 ADD_INSNL(ret, line_node, jump, unmatched);
7611
7612 break;
7613 }
7614 case NODE_HSHPTN: {
7615 /*
7616 * keys = nil
7617 * if pattern.has_kw_args_node? && !pattern.has_kw_rest_arg_node?
7618 * keys = pattern.kw_args_node.keys
7619 * end
7620 * if pattern.has_constant_node?
7621 * unless pattern.constant === obj
7622 * goto match_failed
7623 * end
7624 * end
7625 * unless obj.respond_to?(:deconstruct_keys)
7626 * goto match_failed
7627 * end
7628 * d = obj.deconstruct_keys(keys)
7629 * unless Hash === d
7630 * goto type_error
7631 * end
7632 * if pattern.has_kw_rest_arg_node?
7633 * d = d.dup
7634 * end
7635 * if pattern.has_kw_args_node?
7636 * pattern.kw_args_node.each |k,|
7637 * unless d.key?(k)
7638 * goto match_failed
7639 * end
7640 * end
7641 * pattern.kw_args_node.each |k, pat|
7642 * if pattern.has_kw_rest_arg_node?
7643 * unless pat.match?(d.delete(k))
7644 * goto match_failed
7645 * end
7646 * else
7647 * unless pat.match?(d[k])
7648 * goto match_failed
7649 * end
7650 * end
7651 * end
7652 * else
7653 * unless d.empty?
7654 * goto match_failed
7655 * end
7656 * end
7657 * if pattern.has_kw_rest_arg_node?
7658 * if pattern.no_rest_keyword?
7659 * unless d.empty?
7660 * goto match_failed
7661 * end
7662 * else
7663 * unless pattern.kw_rest_arg_node.match?(d)
7664 * goto match_failed
7665 * end
7666 * end
7667 * end
7668 * goto matched
7669 * type_error:
7670 * FrozenCore.raise TypeError
7671 * match_failed:
7672 * goto unmatched
7673 */
7674 LABEL *match_failed, *type_error;
7675 VALUE keys = Qnil;
7676
7677 match_failed = NEW_LABEL(line);
7678 type_error = NEW_LABEL(line);
7679
7680 if (RNODE_HSHPTN(node)->nd_pkwargs && !RNODE_HSHPTN(node)->nd_pkwrestarg) {
7681 const NODE *kw_args = RNODE_HASH(RNODE_HSHPTN(node)->nd_pkwargs)->nd_head;
7682 keys = rb_ary_new_capa(kw_args ? RNODE_LIST(kw_args)->as.nd_alen/2 : 0);
7683 while (kw_args) {
7684 rb_ary_push(keys, get_symbol_value(iseq, RNODE_LIST(kw_args)->nd_head));
7685 kw_args = RNODE_LIST(RNODE_LIST(kw_args)->nd_next)->nd_next;
7686 }
7687 }
7688
7689 CHECK(iseq_compile_pattern_constant(iseq, ret, node, match_failed, in_single_pattern, base_index));
7690
7691 ADD_INSN(ret, line_node, dup);
7692 ADD_INSN1(ret, line_node, putobject, ID2SYM(rb_intern("deconstruct_keys")));
7693 ADD_SEND(ret, line_node, idRespond_to, INT2FIX(1)); // (1)
7694 if (in_single_pattern) {
7695 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p does not respond to #deconstruct_keys"), base_index + 1 /* (1) */));
7696 }
7697 ADD_INSNL(ret, line_node, branchunless, match_failed);
7698
7699 if (NIL_P(keys)) {
7700 ADD_INSN(ret, line_node, putnil);
7701 }
7702 else {
7703 RB_OBJ_SET_FROZEN_SHAREABLE(keys);
7704 ADD_INSN1(ret, line_node, duparray, keys);
7705 RB_OBJ_WRITTEN(iseq, Qundef, rb_obj_hide(keys));
7706 }
7707 ADD_SEND(ret, line_node, rb_intern("deconstruct_keys"), INT2FIX(1)); // (2)
7708
7709 ADD_INSN(ret, line_node, dup);
7710 ADD_INSN1(ret, line_node, checktype, INT2FIX(T_HASH));
7711 ADD_INSNL(ret, line_node, branchunless, type_error);
7712
7713 if (RNODE_HSHPTN(node)->nd_pkwrestarg) {
7714 ADD_SEND(ret, line_node, rb_intern("dup"), INT2FIX(0));
7715 }
7716
7717 if (RNODE_HSHPTN(node)->nd_pkwargs) {
7718 int i;
7719 int keys_num;
7720 const NODE *args;
7721 args = RNODE_HASH(RNODE_HSHPTN(node)->nd_pkwargs)->nd_head;
7722 if (args) {
7723 DECL_ANCHOR(match_values);
7724 INIT_ANCHOR(match_values);
7725 keys_num = rb_long2int(RNODE_LIST(args)->as.nd_alen) / 2;
7726 for (i = 0; i < keys_num; i++) {
7727 NODE *key_node = RNODE_LIST(args)->nd_head;
7728 NODE *value_node = RNODE_LIST(RNODE_LIST(args)->nd_next)->nd_head;
7729 VALUE key = get_symbol_value(iseq, key_node);
7730
7731 ADD_INSN(ret, line_node, dup);
7732 ADD_INSN1(ret, line_node, putobject, key);
7733 ADD_SEND(ret, line_node, rb_intern("key?"), INT2FIX(1)); // (3)
7734 if (in_single_pattern) {
7735 LABEL *match_succeeded;
7736 match_succeeded = NEW_LABEL(line);
7737
7738 ADD_INSN(ret, line_node, dup);
7739 ADD_INSNL(ret, line_node, branchif, match_succeeded);
7740
7741 VALUE str = rb_str_freeze(rb_sprintf("key not found: %+"PRIsVALUE, key));
7742 ADD_INSN1(ret, line_node, putobject, RB_OBJ_SET_SHAREABLE(str)); // (4)
7743 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 2 /* (3), (4) */));
7744 ADD_INSN1(ret, line_node, putobject, Qtrue); // (5)
7745 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 3 /* (3), (4), (5) */));
7746 ADD_INSN1(ret, line_node, topn, INT2FIX(3)); // (6)
7747 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_MATCHEE + 4 /* (3), (4), (5), (6) */));
7748 ADD_INSN1(ret, line_node, putobject, key); // (7)
7749 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_KEY + 5 /* (3), (4), (5), (6), (7) */));
7750
7751 ADD_INSN1(ret, line_node, adjuststack, INT2FIX(4));
7752
7753 ADD_LABEL(ret, match_succeeded);
7754 }
7755 ADD_INSNL(ret, line_node, branchunless, match_failed);
7756
7757 ADD_INSN(match_values, line_node, dup);
7758 ADD_INSN1(match_values, line_node, putobject, key);
7759 ADD_SEND(match_values, line_node, RNODE_HSHPTN(node)->nd_pkwrestarg ? rb_intern("delete") : idAREF, INT2FIX(1)); // (8)
7760 CHECK(iseq_compile_pattern_match(iseq, match_values, value_node, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (8) */, false));
7761 args = RNODE_LIST(RNODE_LIST(args)->nd_next)->nd_next;
7762 }
7763 ADD_SEQ(ret, match_values);
7764 }
7765 }
7766 else {
7767 ADD_INSN(ret, line_node, dup);
7768 ADD_SEND(ret, line_node, idEmptyP, INT2FIX(0)); // (9)
7769 if (in_single_pattern) {
7770 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p is not empty"), base_index + 1 /* (9) */));
7771 }
7772 ADD_INSNL(ret, line_node, branchunless, match_failed);
7773 }
7774
7775 if (RNODE_HSHPTN(node)->nd_pkwrestarg) {
7776 if (RNODE_HSHPTN(node)->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD) {
7777 ADD_INSN(ret, line_node, dup);
7778 ADD_SEND(ret, line_node, idEmptyP, INT2FIX(0)); // (10)
7779 if (in_single_pattern) {
7780 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("rest of %p is not empty"), base_index + 1 /* (10) */));
7781 }
7782 ADD_INSNL(ret, line_node, branchunless, match_failed);
7783 }
7784 else {
7785 ADD_INSN(ret, line_node, dup); // (11)
7786 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));
7787 }
7788 }
7789
7790 ADD_INSN(ret, line_node, pop);
7791 ADD_INSNL(ret, line_node, jump, matched);
7792 ADD_INSN(ret, line_node, putnil);
7793
7794 ADD_LABEL(ret, type_error);
7795 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
7796 ADD_INSN1(ret, line_node, putobject, rb_eTypeError);
7797 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("deconstruct_keys must return Hash"));
7798 ADD_SEND(ret, line_node, id_core_raise, INT2FIX(2));
7799 ADD_INSN(ret, line_node, pop);
7800
7801 ADD_LABEL(ret, match_failed);
7802 ADD_INSN(ret, line_node, pop);
7803 ADD_INSNL(ret, line_node, jump, unmatched);
7804 break;
7805 }
7806 case NODE_SYM:
7807 case NODE_REGX:
7808 case NODE_LINE:
7809 case NODE_INTEGER:
7810 case NODE_FLOAT:
7811 case NODE_RATIONAL:
7812 case NODE_IMAGINARY:
7813 case NODE_FILE:
7814 case NODE_ENCODING:
7815 case NODE_STR:
7816 case NODE_XSTR:
7817 case NODE_DSTR:
7818 case NODE_DSYM:
7819 case NODE_DREGX:
7820 case NODE_LIST:
7821 case NODE_ZLIST:
7822 case NODE_LAMBDA:
7823 case NODE_DOT2:
7824 case NODE_DOT3:
7825 case NODE_CONST:
7826 case NODE_LVAR:
7827 case NODE_DVAR:
7828 case NODE_IVAR:
7829 case NODE_CVAR:
7830 case NODE_GVAR:
7831 case NODE_TRUE:
7832 case NODE_FALSE:
7833 case NODE_SELF:
7834 case NODE_NIL:
7835 case NODE_COLON2:
7836 case NODE_COLON3:
7837 case NODE_BEGIN:
7838 case NODE_BLOCK:
7839 case NODE_ONCE:
7840 CHECK(COMPILE(ret, "case in literal", node)); // (1)
7841 if (in_single_pattern) {
7842 ADD_INSN1(ret, line_node, dupn, INT2FIX(2));
7843 }
7844 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE)); // (2)
7845 if (in_single_pattern) {
7846 CHECK(iseq_compile_pattern_set_eqq_errmsg(iseq, ret, node, base_index + 2 /* (1), (2) */));
7847 }
7848 ADD_INSNL(ret, line_node, branchif, matched);
7849 ADD_INSNL(ret, line_node, jump, unmatched);
7850 break;
7851 case NODE_LASGN: {
7852 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
7853 ID id = RNODE_LASGN(node)->nd_vid;
7854 int idx = ISEQ_BODY(body->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
7855
7856 if (in_alt_pattern) {
7857 const char *name = rb_id2name(id);
7858 if (name && strlen(name) > 0 && name[0] != '_') {
7859 COMPILE_ERROR(ERROR_ARGS "illegal variable in alternative pattern (%"PRIsVALUE")",
7860 rb_id2str(id));
7861 return COMPILE_NG;
7862 }
7863 }
7864
7865 ADD_SETLOCAL(ret, line_node, idx, get_lvar_level(iseq));
7866 ADD_INSNL(ret, line_node, jump, matched);
7867 break;
7868 }
7869 case NODE_DASGN: {
7870 int idx, lv, ls;
7871 ID id = RNODE_DASGN(node)->nd_vid;
7872
7873 idx = get_dyna_var_idx(iseq, id, &lv, &ls);
7874
7875 if (in_alt_pattern) {
7876 const char *name = rb_id2name(id);
7877 if (name && strlen(name) > 0 && name[0] != '_') {
7878 COMPILE_ERROR(ERROR_ARGS "illegal variable in alternative pattern (%"PRIsVALUE")",
7879 rb_id2str(id));
7880 return COMPILE_NG;
7881 }
7882 }
7883
7884 if (idx < 0) {
7885 COMPILE_ERROR(ERROR_ARGS "NODE_DASGN: unknown id (%"PRIsVALUE")",
7886 rb_id2str(id));
7887 return COMPILE_NG;
7888 }
7889 ADD_SETLOCAL(ret, line_node, ls - idx, lv);
7890 ADD_INSNL(ret, line_node, jump, matched);
7891 break;
7892 }
7893 case NODE_IF:
7894 case NODE_UNLESS: {
7895 LABEL *match_failed;
7896 match_failed = unmatched;
7897 CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_IF(node)->nd_body, unmatched, in_single_pattern, in_alt_pattern, base_index, use_deconstructed_cache));
7898 CHECK(COMPILE(ret, "case in if", RNODE_IF(node)->nd_cond));
7899 if (in_single_pattern) {
7900 LABEL *match_succeeded;
7901 match_succeeded = NEW_LABEL(line);
7902
7903 ADD_INSN(ret, line_node, dup);
7904 if (nd_type_p(node, NODE_IF)) {
7905 ADD_INSNL(ret, line_node, branchif, match_succeeded);
7906 }
7907 else {
7908 ADD_INSNL(ret, line_node, branchunless, match_succeeded);
7909 }
7910
7911 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("guard clause does not return true")); // (1)
7912 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
7913 ADD_INSN1(ret, line_node, putobject, Qfalse);
7914 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
7915
7916 ADD_INSN(ret, line_node, pop);
7917 ADD_INSN(ret, line_node, pop);
7918
7919 ADD_LABEL(ret, match_succeeded);
7920 }
7921 if (nd_type_p(node, NODE_IF)) {
7922 ADD_INSNL(ret, line_node, branchunless, match_failed);
7923 }
7924 else {
7925 ADD_INSNL(ret, line_node, branchif, match_failed);
7926 }
7927 ADD_INSNL(ret, line_node, jump, matched);
7928 break;
7929 }
7930 case NODE_HASH: {
7931 NODE *n;
7932 LABEL *match_failed;
7933 match_failed = NEW_LABEL(line);
7934
7935 n = RNODE_HASH(node)->nd_head;
7936 if (! (nd_type_p(n, NODE_LIST) && RNODE_LIST(n)->as.nd_alen == 2)) {
7937 COMPILE_ERROR(ERROR_ARGS "unexpected node");
7938 return COMPILE_NG;
7939 }
7940
7941 ADD_INSN(ret, line_node, dup); // (1)
7942 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));
7943 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));
7944 ADD_INSN(ret, line_node, putnil);
7945
7946 ADD_LABEL(ret, match_failed);
7947 ADD_INSN(ret, line_node, pop);
7948 ADD_INSNL(ret, line_node, jump, unmatched);
7949 break;
7950 }
7951 case NODE_OR: {
7952 LABEL *match_succeeded, *fin;
7953 match_succeeded = NEW_LABEL(line);
7954 fin = NEW_LABEL(line);
7955
7956 ADD_INSN(ret, line_node, dup); // (1)
7957 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));
7958 ADD_LABEL(ret, match_succeeded);
7959 ADD_INSN(ret, line_node, pop);
7960 ADD_INSNL(ret, line_node, jump, matched);
7961 ADD_INSN(ret, line_node, putnil);
7962 ADD_LABEL(ret, fin);
7963 CHECK(iseq_compile_pattern_each(iseq, ret, RNODE_OR(node)->nd_2nd, matched, unmatched, in_single_pattern, true, base_index, use_deconstructed_cache));
7964 break;
7965 }
7966 default:
7967 UNKNOWN_NODE("NODE_IN", node, COMPILE_NG);
7968 }
7969 return COMPILE_OK;
7970}
7971
7972static int
7973iseq_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)
7974{
7975 LABEL *fin = NEW_LABEL(nd_line(node));
7976 CHECK(iseq_compile_pattern_each(iseq, ret, node, fin, unmatched, in_single_pattern, in_alt_pattern, base_index, use_deconstructed_cache));
7977 ADD_LABEL(ret, fin);
7978 return COMPILE_OK;
7979}
7980
7981static int
7982iseq_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)
7983{
7984 const NODE *line_node = node;
7985
7986 if (RNODE_ARYPTN(node)->nd_pconst) {
7987 ADD_INSN(ret, line_node, dup); // (1)
7988 CHECK(COMPILE(ret, "constant", RNODE_ARYPTN(node)->nd_pconst)); // (2)
7989 if (in_single_pattern) {
7990 ADD_INSN1(ret, line_node, dupn, INT2FIX(2));
7991 }
7992 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE)); // (3)
7993 if (in_single_pattern) {
7994 CHECK(iseq_compile_pattern_set_eqq_errmsg(iseq, ret, node, base_index + 3 /* (1), (2), (3) */));
7995 }
7996 ADD_INSNL(ret, line_node, branchunless, match_failed);
7997 }
7998 return COMPILE_OK;
7999}
8000
8001
8002static int
8003iseq_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)
8004{
8005 const NODE *line_node = node;
8006
8007 // NOTE: this optimization allows us to re-use the #deconstruct value
8008 // (or its absence).
8009 if (use_deconstructed_cache) {
8010 // If value is nil then we haven't tried to deconstruct
8011 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
8012 ADD_INSNL(ret, line_node, branchnil, deconstruct);
8013
8014 // If false then the value is not deconstructable
8015 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
8016 ADD_INSNL(ret, line_node, branchunless, match_failed);
8017
8018 // Drop value, add deconstructed to the stack and jump
8019 ADD_INSN(ret, line_node, pop); // (1)
8020 ADD_INSN1(ret, line_node, topn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE - 1 /* (1) */));
8021 ADD_INSNL(ret, line_node, jump, deconstructed);
8022 }
8023 else {
8024 ADD_INSNL(ret, line_node, jump, deconstruct);
8025 }
8026
8027 ADD_LABEL(ret, deconstruct);
8028 ADD_INSN(ret, line_node, dup);
8029 ADD_INSN1(ret, line_node, putobject, ID2SYM(rb_intern("deconstruct")));
8030 ADD_SEND(ret, line_node, idRespond_to, INT2FIX(1)); // (2)
8031
8032 // Cache the result of respond_to? (in case it's false is stays there, if true - it's overwritten after #deconstruct)
8033 if (use_deconstructed_cache) {
8034 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE + 1 /* (2) */));
8035 }
8036
8037 if (in_single_pattern) {
8038 CHECK(iseq_compile_pattern_set_general_errmsg(iseq, ret, node, rb_fstring_lit("%p does not respond to #deconstruct"), base_index + 1 /* (2) */));
8039 }
8040
8041 ADD_INSNL(ret, line_node, branchunless, match_failed);
8042
8043 ADD_SEND(ret, line_node, rb_intern("deconstruct"), INT2FIX(0));
8044
8045 // Cache the result (if it's cacheable - currently, only top-level array patterns)
8046 if (use_deconstructed_cache) {
8047 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_DECONSTRUCTED_CACHE));
8048 }
8049
8050 ADD_INSN(ret, line_node, dup);
8051 ADD_INSN1(ret, line_node, checktype, INT2FIX(T_ARRAY));
8052 ADD_INSNL(ret, line_node, branchunless, type_error);
8053
8054 ADD_LABEL(ret, deconstructed);
8055
8056 return COMPILE_OK;
8057}
8058
8059static int
8060iseq_compile_pattern_set_general_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE errmsg, int base_index)
8061{
8062 /*
8063 * if match_succeeded?
8064 * goto match_succeeded
8065 * end
8066 * error_string = FrozenCore.sprintf(errmsg, matchee)
8067 * key_error_p = false
8068 * match_succeeded:
8069 */
8070 const int line = nd_line(node);
8071 const NODE *line_node = node;
8072 LABEL *match_succeeded = NEW_LABEL(line);
8073
8074 ADD_INSN(ret, line_node, dup);
8075 ADD_INSNL(ret, line_node, branchif, match_succeeded);
8076
8077 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8078 ADD_INSN1(ret, line_node, putobject, errmsg);
8079 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
8080 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(2)); // (1)
8081 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
8082
8083 ADD_INSN1(ret, line_node, putobject, Qfalse);
8084 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
8085
8086 ADD_INSN(ret, line_node, pop);
8087 ADD_INSN(ret, line_node, pop);
8088 ADD_LABEL(ret, match_succeeded);
8089
8090 return COMPILE_OK;
8091}
8092
8093static int
8094iseq_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)
8095{
8096 /*
8097 * if match_succeeded?
8098 * goto match_succeeded
8099 * end
8100 * error_string = FrozenCore.sprintf(errmsg, matchee, matchee.length, pat.length)
8101 * key_error_p = false
8102 * match_succeeded:
8103 */
8104 const int line = nd_line(node);
8105 const NODE *line_node = node;
8106 LABEL *match_succeeded = NEW_LABEL(line);
8107
8108 ADD_INSN(ret, line_node, dup);
8109 ADD_INSNL(ret, line_node, branchif, match_succeeded);
8110
8111 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8112 ADD_INSN1(ret, line_node, putobject, errmsg);
8113 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
8114 ADD_INSN(ret, line_node, dup);
8115 ADD_SEND(ret, line_node, idLength, INT2FIX(0));
8116 ADD_INSN1(ret, line_node, putobject, pattern_length);
8117 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(4)); // (1)
8118 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
8119
8120 ADD_INSN1(ret, line_node, putobject, Qfalse);
8121 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2/* (1), (2) */));
8122
8123 ADD_INSN(ret, line_node, pop);
8124 ADD_INSN(ret, line_node, pop);
8125 ADD_LABEL(ret, match_succeeded);
8126
8127 return COMPILE_OK;
8128}
8129
8130static int
8131iseq_compile_pattern_set_eqq_errmsg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int base_index)
8132{
8133 /*
8134 * if match_succeeded?
8135 * goto match_succeeded
8136 * end
8137 * error_string = FrozenCore.sprintf("%p === %p does not return true", pat, matchee)
8138 * key_error_p = false
8139 * match_succeeded:
8140 */
8141 const int line = nd_line(node);
8142 const NODE *line_node = node;
8143 LABEL *match_succeeded = NEW_LABEL(line);
8144
8145 ADD_INSN(ret, line_node, dup);
8146 ADD_INSNL(ret, line_node, branchif, match_succeeded);
8147
8148 ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8149 ADD_INSN1(ret, line_node, putobject, rb_fstring_lit("%p === %p does not return true"));
8150 ADD_INSN1(ret, line_node, topn, INT2FIX(3));
8151 ADD_INSN1(ret, line_node, topn, INT2FIX(5));
8152 ADD_SEND(ret, line_node, id_core_sprintf, INT2FIX(3)); // (1)
8153 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 1 /* (1) */)); // (2)
8154
8155 ADD_INSN1(ret, line_node, putobject, Qfalse);
8156 ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 2 /* (1), (2) */));
8157
8158 ADD_INSN(ret, line_node, pop);
8159 ADD_INSN(ret, line_node, pop);
8160
8161 ADD_LABEL(ret, match_succeeded);
8162 ADD_INSN1(ret, line_node, setn, INT2FIX(2));
8163 ADD_INSN(ret, line_node, pop);
8164 ADD_INSN(ret, line_node, pop);
8165
8166 return COMPILE_OK;
8167}
8168
8169static int
8170compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
8171{
8172 const NODE *pattern;
8173 const NODE *node = orig_node;
8174 LABEL *endlabel, *elselabel;
8175 DECL_ANCHOR(head);
8176 DECL_ANCHOR(body_seq);
8177 DECL_ANCHOR(cond_seq);
8178 int line;
8179 enum node_type type;
8180 const NODE *line_node;
8181 VALUE branches = 0;
8182 int branch_id = 0;
8183 bool single_pattern;
8184
8185 INIT_ANCHOR(head);
8186 INIT_ANCHOR(body_seq);
8187 INIT_ANCHOR(cond_seq);
8188
8189 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case");
8190
8191 node = RNODE_CASE3(node)->nd_body;
8192 EXPECT_NODE("NODE_CASE3", node, NODE_IN, COMPILE_NG);
8193 type = nd_type(node);
8194 line = nd_line(node);
8195 line_node = node;
8196 single_pattern = !RNODE_IN(node)->nd_next;
8197
8198 endlabel = NEW_LABEL(line);
8199 elselabel = NEW_LABEL(line);
8200
8201 if (single_pattern) {
8202 /* allocate stack for ... */
8203 ADD_INSN(head, line_node, putnil); /* key_error_key */
8204 ADD_INSN(head, line_node, putnil); /* key_error_matchee */
8205 ADD_INSN1(head, line_node, putobject, Qfalse); /* key_error_p */
8206 ADD_INSN(head, line_node, putnil); /* error_string */
8207 }
8208 ADD_INSN(head, line_node, putnil); /* allocate stack for cached #deconstruct value */
8209
8210 CHECK(COMPILE(head, "case base", RNODE_CASE3(orig_node)->nd_head));
8211
8212 ADD_SEQ(ret, head); /* case VAL */
8213
8214 while (type == NODE_IN) {
8215 LABEL *l1;
8216
8217 if (branch_id) {
8218 ADD_INSN(body_seq, line_node, putnil);
8219 }
8220 l1 = NEW_LABEL(line);
8221 ADD_LABEL(body_seq, l1);
8222 ADD_INSN1(body_seq, line_node, adjuststack, INT2FIX(single_pattern ? 6 : 2));
8223
8224 const NODE *const coverage_node = RNODE_IN(node)->nd_body ? RNODE_IN(node)->nd_body : node;
8225 add_trace_branch_coverage(
8226 iseq,
8227 body_seq,
8228 nd_code_loc(coverage_node),
8229 nd_node_id(coverage_node),
8230 branch_id++,
8231 "in",
8232 branches);
8233
8234 CHECK(COMPILE_(body_seq, "in body", RNODE_IN(node)->nd_body, popped));
8235 ADD_INSNL(body_seq, line_node, jump, endlabel);
8236
8237 pattern = RNODE_IN(node)->nd_head;
8238 if (pattern) {
8239 int pat_line = nd_line(pattern);
8240 LABEL *next_pat = NEW_LABEL(pat_line);
8241 ADD_INSN (cond_seq, pattern, dup); /* dup case VAL */
8242 // NOTE: set base_index (it's "under" the matchee value, so it's position is 2)
8243 CHECK(iseq_compile_pattern_each(iseq, cond_seq, pattern, l1, next_pat, single_pattern, false, 2, true));
8244 ADD_LABEL(cond_seq, next_pat);
8245 LABEL_UNREMOVABLE(next_pat);
8246 }
8247 else {
8248 COMPILE_ERROR(ERROR_ARGS "unexpected node");
8249 return COMPILE_NG;
8250 }
8251
8252 node = RNODE_IN(node)->nd_next;
8253 if (!node) {
8254 break;
8255 }
8256 type = nd_type(node);
8257 line = nd_line(node);
8258 line_node = node;
8259 }
8260 /* else */
8261 if (node) {
8262 ADD_LABEL(cond_seq, elselabel);
8263 ADD_INSN(cond_seq, line_node, pop);
8264 ADD_INSN(cond_seq, line_node, pop); /* discard cached #deconstruct value */
8265 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(node), nd_node_id(node), branch_id, "else", branches);
8266 CHECK(COMPILE_(cond_seq, "else", node, popped));
8267 ADD_INSNL(cond_seq, line_node, jump, endlabel);
8268 ADD_INSN(cond_seq, line_node, putnil);
8269 if (popped) {
8270 ADD_INSN(cond_seq, line_node, putnil);
8271 }
8272 }
8273 else {
8274 debugs("== else (implicit)\n");
8275 ADD_LABEL(cond_seq, elselabel);
8276 add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(orig_node), nd_node_id(orig_node), branch_id, "else", branches);
8277 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8278
8279 if (single_pattern) {
8280 /*
8281 * if key_error_p
8282 * FrozenCore.raise NoMatchingPatternKeyError.new(FrozenCore.sprintf("%p: %s", case_val, error_string), matchee: key_error_matchee, key: key_error_key)
8283 * else
8284 * FrozenCore.raise NoMatchingPatternError, FrozenCore.sprintf("%p: %s", case_val, error_string)
8285 * end
8286 */
8287 LABEL *key_error, *fin;
8288 struct rb_callinfo_kwarg *kw_arg;
8289
8290 key_error = NEW_LABEL(line);
8291 fin = NEW_LABEL(line);
8292
8293 kw_arg = rb_xmalloc_mul_add(2, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
8294 kw_arg->references = 0;
8295 kw_arg->keyword_len = 2;
8296 kw_arg->keywords[0] = ID2SYM(rb_intern("matchee"));
8297 kw_arg->keywords[1] = ID2SYM(rb_intern("key"));
8298
8299 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_P + 2));
8300 ADD_INSNL(cond_seq, orig_node, branchif, key_error);
8301 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternError);
8302 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8303 ADD_INSN1(cond_seq, orig_node, putobject, rb_fstring_lit("%p: %s"));
8304 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(4)); /* case VAL */
8305 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_ERROR_STRING + 6));
8306 ADD_SEND(cond_seq, orig_node, id_core_sprintf, INT2FIX(3));
8307 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(2));
8308 ADD_INSNL(cond_seq, orig_node, jump, fin);
8309
8310 ADD_LABEL(cond_seq, key_error);
8311 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternKeyError);
8312 ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
8313 ADD_INSN1(cond_seq, orig_node, putobject, rb_fstring_lit("%p: %s"));
8314 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(4)); /* case VAL */
8315 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_ERROR_STRING + 6));
8316 ADD_SEND(cond_seq, orig_node, id_core_sprintf, INT2FIX(3));
8317 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_MATCHEE + 4));
8318 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(CASE3_BI_OFFSET_KEY_ERROR_KEY + 5));
8319 ADD_SEND_R(cond_seq, orig_node, rb_intern("new"), INT2FIX(1), NULL, INT2FIX(VM_CALL_KWARG), kw_arg);
8320 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(1));
8321
8322 ADD_LABEL(cond_seq, fin);
8323 }
8324 else {
8325 ADD_INSN1(cond_seq, orig_node, putobject, rb_eNoMatchingPatternError);
8326 ADD_INSN1(cond_seq, orig_node, topn, INT2FIX(2));
8327 ADD_SEND(cond_seq, orig_node, id_core_raise, INT2FIX(2));
8328 }
8329 ADD_INSN1(cond_seq, orig_node, adjuststack, INT2FIX(single_pattern ? 7 : 3));
8330 if (!popped) {
8331 ADD_INSN(cond_seq, orig_node, putnil);
8332 }
8333 ADD_INSNL(cond_seq, orig_node, jump, endlabel);
8334 ADD_INSN1(cond_seq, orig_node, dupn, INT2FIX(single_pattern ? 5 : 1));
8335 if (popped) {
8336 ADD_INSN(cond_seq, line_node, putnil);
8337 }
8338 }
8339
8340 ADD_SEQ(ret, cond_seq);
8341 ADD_SEQ(ret, body_seq);
8342 ADD_LABEL(ret, endlabel);
8343 return COMPILE_OK;
8344}
8345
8346#undef CASE3_BI_OFFSET_DECONSTRUCTED_CACHE
8347#undef CASE3_BI_OFFSET_ERROR_STRING
8348#undef CASE3_BI_OFFSET_KEY_ERROR_P
8349#undef CASE3_BI_OFFSET_KEY_ERROR_MATCHEE
8350#undef CASE3_BI_OFFSET_KEY_ERROR_KEY
8351
8352static int
8353compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
8354{
8355 const int line = (int)nd_line(node);
8356 const NODE *line_node = node;
8357
8358 LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
8359 LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
8360 LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
8361 int prev_loopval_popped = ISEQ_COMPILE_DATA(iseq)->loopval_popped;
8362 VALUE branches = Qfalse;
8363
8365
8366 LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(line); /* next */
8367 LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(line); /* redo */
8368 LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(line); /* break */
8369 LABEL *end_label = NEW_LABEL(line);
8370 LABEL *adjust_label = NEW_LABEL(line);
8371
8372 LABEL *next_catch_label = NEW_LABEL(line);
8373 LABEL *tmp_label = NULL;
8374
8375 ISEQ_COMPILE_DATA(iseq)->loopval_popped = 0;
8376 push_ensure_entry(iseq, &enl, NULL, NULL);
8377
8378 if (RNODE_WHILE(node)->nd_state == 1) {
8379 ADD_INSNL(ret, line_node, jump, next_label);
8380 }
8381 else {
8382 tmp_label = NEW_LABEL(line);
8383 ADD_INSNL(ret, line_node, jump, tmp_label);
8384 }
8385 ADD_LABEL(ret, adjust_label);
8386 ADD_INSN(ret, line_node, putnil);
8387 ADD_LABEL(ret, next_catch_label);
8388 ADD_INSN(ret, line_node, pop);
8389 ADD_INSNL(ret, line_node, jump, next_label);
8390 if (tmp_label) ADD_LABEL(ret, tmp_label);
8391
8392 ADD_LABEL(ret, redo_label);
8393 branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_WHILE ? "while" : "until");
8394
8395 const NODE *const coverage_node = RNODE_WHILE(node)->nd_body ? RNODE_WHILE(node)->nd_body : node;
8396 add_trace_branch_coverage(
8397 iseq,
8398 ret,
8399 nd_code_loc(coverage_node),
8400 nd_node_id(coverage_node),
8401 0,
8402 "body",
8403 branches);
8404
8405 CHECK(COMPILE_POPPED(ret, "while body", RNODE_WHILE(node)->nd_body));
8406 ADD_LABEL(ret, next_label); /* next */
8407
8408 if (type == NODE_WHILE) {
8409 CHECK(compile_branch_condition(iseq, ret, RNODE_WHILE(node)->nd_cond,
8410 redo_label, end_label));
8411 }
8412 else {
8413 /* until */
8414 CHECK(compile_branch_condition(iseq, ret, RNODE_WHILE(node)->nd_cond,
8415 end_label, redo_label));
8416 }
8417
8418 ADD_LABEL(ret, end_label);
8419 ADD_ADJUST_RESTORE(ret, adjust_label);
8420
8421 if (UNDEF_P(RNODE_WHILE(node)->nd_state)) {
8422 /* ADD_INSN(ret, line_node, putundef); */
8423 COMPILE_ERROR(ERROR_ARGS "unsupported: putundef");
8424 return COMPILE_NG;
8425 }
8426 else {
8427 ADD_INSN(ret, line_node, putnil);
8428 }
8429
8430 ADD_LABEL(ret, break_label); /* break */
8431
8432 if (popped) {
8433 ADD_INSN(ret, line_node, pop);
8434 }
8435
8436 ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL,
8437 break_label);
8438 ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL,
8439 next_catch_label);
8440 ADD_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL,
8441 ISEQ_COMPILE_DATA(iseq)->redo_label);
8442
8443 ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
8444 ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
8445 ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
8446 ISEQ_COMPILE_DATA(iseq)->loopval_popped = prev_loopval_popped;
8447 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->prev;
8448 return COMPILE_OK;
8449}
8450
8451static int
8452compile_iter(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8453{
8454 const int line = nd_line(node);
8455 const NODE *line_node = node;
8456 const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
8457 LABEL *retry_label = NEW_LABEL(line);
8458 LABEL *retry_end_l = NEW_LABEL(line);
8459 const rb_iseq_t *child_iseq;
8460
8461 ADD_LABEL(ret, retry_label);
8462 if (nd_type_p(node, NODE_FOR)) {
8463 CHECK(COMPILE(ret, "iter caller (for)", RNODE_FOR(node)->nd_iter));
8464
8465 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
8466 NEW_CHILD_ISEQ(RNODE_FOR(node)->nd_body, make_name_for_block(iseq),
8467 ISEQ_TYPE_BLOCK, line);
8468 ADD_SEND_WITH_BLOCK(ret, line_node, idEach, INT2FIX(0), child_iseq);
8469 }
8470 else {
8471 ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
8472 NEW_CHILD_ISEQ(RNODE_ITER(node)->nd_body, make_name_for_block(iseq),
8473 ISEQ_TYPE_BLOCK, line);
8474 CHECK(COMPILE(ret, "iter caller", RNODE_ITER(node)->nd_iter));
8475 }
8476
8477 {
8478 // We need to put the label "retry_end_l" immediately after the last "send" instruction.
8479 // This because vm_throw checks if the break cont is equal to the index of next insn of the "send".
8480 // (Otherwise, it is considered "break from proc-closure". See "TAG_BREAK" handling in "vm_throw_start".)
8481 //
8482 // Normally, "send" instruction is at the last.
8483 // However, qcall under branch coverage measurement adds some instructions after the "send".
8484 //
8485 // Note that "invokesuper", "invokesuperforward" appears instead of "send".
8486 INSN *iobj;
8487 LINK_ELEMENT *last_elem = LAST_ELEMENT(ret);
8488 iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem);
8489 while (!IS_INSN_ID(iobj, send) && !IS_INSN_ID(iobj, invokesuper) && !IS_INSN_ID(iobj, sendforward) && !IS_INSN_ID(iobj, invokesuperforward)) {
8490 iobj = (INSN*) get_prev_insn(iobj);
8491 }
8492 ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l);
8493
8494 // LINK_ANCHOR has a pointer to the last element, but ELEM_INSERT_NEXT does not update it
8495 // even if we add an insn to the last of LINK_ANCHOR. So this updates it manually.
8496 if (&iobj->link == LAST_ELEMENT(ret)) {
8497 ret->last = (LINK_ELEMENT*) retry_end_l;
8498 }
8499 }
8500
8501 if (popped) {
8502 ADD_INSN(ret, line_node, pop);
8503 }
8504
8505 ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
8506
8507 ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
8508 return COMPILE_OK;
8509}
8510
8511static int
8512compile_for_masgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8513{
8514 /* massign to var in "for"
8515 * (args.length == 1 && Array.try_convert(args[0])) || args
8516 */
8517 const NODE *line_node = node;
8518 const NODE *var = RNODE_FOR_MASGN(node)->nd_var;
8519 LABEL *not_single = NEW_LABEL(nd_line(var));
8520 LABEL *not_ary = NEW_LABEL(nd_line(var));
8521 CHECK(COMPILE(ret, "for var", var));
8522 ADD_INSN(ret, line_node, dup);
8523 ADD_CALL(ret, line_node, idLength, INT2FIX(0));
8524 ADD_INSN1(ret, line_node, putobject, INT2FIX(1));
8525 ADD_CALL(ret, line_node, idEq, INT2FIX(1));
8526 ADD_INSNL(ret, line_node, branchunless, not_single);
8527 ADD_INSN(ret, line_node, dup);
8528 ADD_INSN1(ret, line_node, putobject, INT2FIX(0));
8529 ADD_CALL(ret, line_node, idAREF, INT2FIX(1));
8530 ADD_INSN1(ret, line_node, putobject, rb_cArray);
8531 ADD_INSN(ret, line_node, swap);
8532 ADD_CALL(ret, line_node, rb_intern("try_convert"), INT2FIX(1));
8533 ADD_INSN(ret, line_node, dup);
8534 ADD_INSNL(ret, line_node, branchunless, not_ary);
8535 ADD_INSN(ret, line_node, swap);
8536 ADD_LABEL(ret, not_ary);
8537 ADD_INSN(ret, line_node, pop);
8538 ADD_LABEL(ret, not_single);
8539 return COMPILE_OK;
8540}
8541
8542static int
8543compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8544{
8545 const NODE *line_node = node;
8546 unsigned long throw_flag = 0;
8547
8548 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
8549 /* while/until */
8550 LABEL *splabel = NEW_LABEL(0);
8551 ADD_LABEL(ret, splabel);
8552 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
8553 CHECK(COMPILE_(ret, "break val (while/until)", RNODE_BREAK(node)->nd_stts,
8554 ISEQ_COMPILE_DATA(iseq)->loopval_popped));
8555 add_ensure_iseq(ret, iseq, 0);
8556 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
8557 ADD_ADJUST_RESTORE(ret, splabel);
8558
8559 if (!popped) {
8560 ADD_INSN(ret, line_node, putnil);
8561 }
8562 }
8563 else {
8564 const rb_iseq_t *ip = iseq;
8565
8566 while (ip) {
8567 if (!ISEQ_COMPILE_DATA(ip)) {
8568 ip = 0;
8569 break;
8570 }
8571
8572 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8573 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
8574 }
8575 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8576 throw_flag = 0;
8577 }
8578 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8579 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with break");
8580 return COMPILE_NG;
8581 }
8582 else {
8583 ip = ISEQ_BODY(ip)->parent_iseq;
8584 continue;
8585 }
8586
8587 /* escape from block */
8588 CHECK(COMPILE(ret, "break val (block)", RNODE_BREAK(node)->nd_stts));
8589 ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_BREAK));
8590 if (popped) {
8591 ADD_INSN(ret, line_node, pop);
8592 }
8593 return COMPILE_OK;
8594 }
8595 COMPILE_ERROR(ERROR_ARGS "Invalid break");
8596 return COMPILE_NG;
8597 }
8598 return COMPILE_OK;
8599}
8600
8601static int
8602compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8603{
8604 const NODE *line_node = node;
8605 unsigned long throw_flag = 0;
8606
8607 if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
8608 LABEL *splabel = NEW_LABEL(0);
8609 debugs("next in while loop\n");
8610 ADD_LABEL(ret, splabel);
8611 CHECK(COMPILE(ret, "next val/valid syntax?", RNODE_NEXT(node)->nd_stts));
8612 add_ensure_iseq(ret, iseq, 0);
8613 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
8614 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8615 ADD_ADJUST_RESTORE(ret, splabel);
8616 if (!popped) {
8617 ADD_INSN(ret, line_node, putnil);
8618 }
8619 }
8620 else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
8621 LABEL *splabel = NEW_LABEL(0);
8622 debugs("next in block\n");
8623 ADD_LABEL(ret, splabel);
8624 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
8625 CHECK(COMPILE(ret, "next val", RNODE_NEXT(node)->nd_stts));
8626 add_ensure_iseq(ret, iseq, 0);
8627 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
8628 ADD_ADJUST_RESTORE(ret, splabel);
8629
8630 if (!popped) {
8631 ADD_INSN(ret, line_node, putnil);
8632 }
8633 }
8634 else {
8635 const rb_iseq_t *ip = iseq;
8636
8637 while (ip) {
8638 if (!ISEQ_COMPILE_DATA(ip)) {
8639 ip = 0;
8640 break;
8641 }
8642
8643 throw_flag = VM_THROW_NO_ESCAPE_FLAG;
8644 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8645 /* while loop */
8646 break;
8647 }
8648 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8649 break;
8650 }
8651 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8652 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with next");
8653 return COMPILE_NG;
8654 }
8655
8656 ip = ISEQ_BODY(ip)->parent_iseq;
8657 }
8658 if (ip != 0) {
8659 CHECK(COMPILE(ret, "next val", RNODE_NEXT(node)->nd_stts));
8660 ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_NEXT));
8661
8662 if (popped) {
8663 ADD_INSN(ret, line_node, pop);
8664 }
8665 }
8666 else {
8667 COMPILE_ERROR(ERROR_ARGS "Invalid next");
8668 return COMPILE_NG;
8669 }
8670 }
8671 return COMPILE_OK;
8672}
8673
8674static int
8675compile_redo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8676{
8677 const NODE *line_node = node;
8678
8679 if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
8680 LABEL *splabel = NEW_LABEL(0);
8681 debugs("redo in while");
8682 ADD_LABEL(ret, splabel);
8683 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label);
8684 add_ensure_iseq(ret, iseq, 0);
8685 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
8686 ADD_ADJUST_RESTORE(ret, splabel);
8687 if (!popped) {
8688 ADD_INSN(ret, line_node, putnil);
8689 }
8690 }
8691 else if (ISEQ_BODY(iseq)->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
8692 LABEL *splabel = NEW_LABEL(0);
8693
8694 debugs("redo in block");
8695 ADD_LABEL(ret, splabel);
8696 add_ensure_iseq(ret, iseq, 0);
8697 ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label);
8698 ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
8699 ADD_ADJUST_RESTORE(ret, splabel);
8700
8701 if (!popped) {
8702 ADD_INSN(ret, line_node, putnil);
8703 }
8704 }
8705 else {
8706 const rb_iseq_t *ip = iseq;
8707
8708 while (ip) {
8709 if (!ISEQ_COMPILE_DATA(ip)) {
8710 ip = 0;
8711 break;
8712 }
8713
8714 if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
8715 break;
8716 }
8717 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_BLOCK) {
8718 break;
8719 }
8720 else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
8721 COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with redo");
8722 return COMPILE_NG;
8723 }
8724
8725 ip = ISEQ_BODY(ip)->parent_iseq;
8726 }
8727 if (ip != 0) {
8728 ADD_INSN(ret, line_node, putnil);
8729 ADD_INSN1(ret, line_node, throw, INT2FIX(VM_THROW_NO_ESCAPE_FLAG | TAG_REDO));
8730
8731 if (popped) {
8732 ADD_INSN(ret, line_node, pop);
8733 }
8734 }
8735 else {
8736 COMPILE_ERROR(ERROR_ARGS "Invalid redo");
8737 return COMPILE_NG;
8738 }
8739 }
8740 return COMPILE_OK;
8741}
8742
8743static int
8744compile_retry(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8745{
8746 const NODE *line_node = node;
8747
8748 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
8749 ADD_INSN(ret, line_node, putnil);
8750 ADD_INSN1(ret, line_node, throw, INT2FIX(TAG_RETRY));
8751
8752 if (popped) {
8753 ADD_INSN(ret, line_node, pop);
8754 }
8755 }
8756 else {
8757 COMPILE_ERROR(ERROR_ARGS "Invalid retry");
8758 return COMPILE_NG;
8759 }
8760 return COMPILE_OK;
8761}
8762
8763static int
8764compile_rescue(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8765{
8766 const int line = nd_line(node);
8767 const NODE *line_node = node;
8768 LABEL *lstart = NEW_LABEL(line);
8769 LABEL *lend = NEW_LABEL(line);
8770 LABEL *lcont = NEW_LABEL(line);
8771 const rb_iseq_t *rescue = NEW_CHILD_ISEQ(RNODE_RESCUE(node)->nd_resq,
8772 rb_str_concat(rb_str_new2("rescue in "),
8773 ISEQ_BODY(iseq)->location.label),
8774 ISEQ_TYPE_RESCUE, line);
8775
8776 lstart->rescued = LABEL_RESCUE_BEG;
8777 lend->rescued = LABEL_RESCUE_END;
8778 ADD_LABEL(ret, lstart);
8779
8780 bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
8781 ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
8782 {
8783 CHECK(COMPILE(ret, "rescue head", RNODE_RESCUE(node)->nd_head));
8784 }
8785 ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
8786
8787 ADD_LABEL(ret, lend);
8788 if (RNODE_RESCUE(node)->nd_else) {
8789 ADD_INSN(ret, line_node, pop);
8790 CHECK(COMPILE(ret, "rescue else", RNODE_RESCUE(node)->nd_else));
8791 }
8792 ADD_INSN(ret, line_node, nop);
8793 ADD_LABEL(ret, lcont);
8794
8795 if (popped) {
8796 ADD_INSN(ret, line_node, pop);
8797 }
8798
8799 /* register catch entry */
8800 ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lcont);
8801 ADD_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
8802 return COMPILE_OK;
8803}
8804
8805static int
8806compile_resbody(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8807{
8808 const int line = nd_line(node);
8809 const NODE *line_node = node;
8810 const NODE *resq = node;
8811 const NODE *narg;
8812 LABEL *label_miss, *label_hit;
8813
8814 while (resq) {
8815 label_miss = NEW_LABEL(line);
8816 label_hit = NEW_LABEL(line);
8817
8818 narg = RNODE_RESBODY(resq)->nd_args;
8819 if (narg) {
8820 switch (nd_type(narg)) {
8821 case NODE_LIST:
8822 while (narg) {
8823 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
8824 CHECK(COMPILE(ret, "rescue arg", RNODE_LIST(narg)->nd_head));
8825 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
8826 ADD_INSNL(ret, line_node, branchif, label_hit);
8827 narg = RNODE_LIST(narg)->nd_next;
8828 }
8829 break;
8830 case NODE_SPLAT:
8831 case NODE_ARGSCAT:
8832 case NODE_ARGSPUSH:
8833 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
8834 CHECK(COMPILE(ret, "rescue/cond splat", narg));
8835 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE | VM_CHECKMATCH_ARRAY));
8836 ADD_INSNL(ret, line_node, branchif, label_hit);
8837 break;
8838 default:
8839 UNKNOWN_NODE("NODE_RESBODY", narg, COMPILE_NG);
8840 }
8841 }
8842 else {
8843 ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0);
8844 ADD_INSN1(ret, line_node, putobject, rb_eStandardError);
8845 ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
8846 ADD_INSNL(ret, line_node, branchif, label_hit);
8847 }
8848 ADD_INSNL(ret, line_node, jump, label_miss);
8849 ADD_LABEL(ret, label_hit);
8850 ADD_TRACE(ret, RUBY_EVENT_RESCUE);
8851
8852 if (RNODE_RESBODY(resq)->nd_exc_var) {
8853 CHECK(COMPILE_POPPED(ret, "resbody exc_var", RNODE_RESBODY(resq)->nd_exc_var));
8854 }
8855
8856 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) {
8857 // empty body
8858 ADD_SYNTHETIC_INSN(ret, nd_line(RNODE_RESBODY(resq)->nd_body), -1, putnil);
8859 }
8860 else {
8861 CHECK(COMPILE(ret, "resbody body", RNODE_RESBODY(resq)->nd_body));
8862 }
8863
8864 if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) {
8865 ADD_INSN(ret, line_node, nop);
8866 }
8867 ADD_INSN(ret, line_node, leave);
8868 ADD_LABEL(ret, label_miss);
8869 resq = RNODE_RESBODY(resq)->nd_next;
8870 }
8871 return COMPILE_OK;
8872}
8873
8874static int
8875compile_ensure(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8876{
8877 const int line = nd_line(RNODE_ENSURE(node)->nd_ensr);
8878 const NODE *line_node = node;
8879 DECL_ANCHOR(ensr);
8880 const rb_iseq_t *ensure = NEW_CHILD_ISEQ(RNODE_ENSURE(node)->nd_ensr,
8881 rb_str_concat(rb_str_new2 ("ensure in "), ISEQ_BODY(iseq)->location.label),
8882 ISEQ_TYPE_ENSURE, line);
8883 LABEL *lstart = NEW_LABEL(line);
8884 LABEL *lend = NEW_LABEL(line);
8885 LABEL *lcont = NEW_LABEL(line);
8886 LINK_ELEMENT *last;
8887 int last_leave = 0;
8888 struct ensure_range er;
8890 struct ensure_range *erange;
8891
8892 INIT_ANCHOR(ensr);
8893 CHECK(COMPILE_POPPED(ensr, "ensure ensr", RNODE_ENSURE(node)->nd_ensr));
8894 last = ensr->last;
8895 last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave);
8896
8897 er.begin = lstart;
8898 er.end = lend;
8899 er.next = 0;
8900 push_ensure_entry(iseq, &enl, &er, RNODE_ENSURE(node)->nd_ensr);
8901
8902 ADD_LABEL(ret, lstart);
8903 CHECK(COMPILE_(ret, "ensure head", RNODE_ENSURE(node)->nd_head, (popped | last_leave)));
8904 ADD_LABEL(ret, lend);
8905 ADD_SEQ(ret, ensr);
8906 if (!popped && last_leave) ADD_INSN(ret, line_node, putnil);
8907 ADD_LABEL(ret, lcont);
8908 if (last_leave) ADD_INSN(ret, line_node, pop);
8909
8910 erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
8911 if (lstart->link.next != &lend->link) {
8912 while (erange) {
8913 ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end,
8914 ensure, lcont);
8915 erange = erange->next;
8916 }
8917 }
8918
8919 ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
8920 return COMPILE_OK;
8921}
8922
8923static int
8924compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
8925{
8926 const NODE *line_node = node;
8927
8928 if (iseq) {
8929 enum rb_iseq_type type = ISEQ_BODY(iseq)->type;
8930 const rb_iseq_t *is = iseq;
8931 enum rb_iseq_type t = type;
8932 const NODE *retval = RNODE_RETURN(node)->nd_stts;
8933 LABEL *splabel = 0;
8934
8935 while (t == ISEQ_TYPE_RESCUE || t == ISEQ_TYPE_ENSURE) {
8936 if (!(is = ISEQ_BODY(is)->parent_iseq)) break;
8937 t = ISEQ_BODY(is)->type;
8938 }
8939 switch (t) {
8940 case ISEQ_TYPE_TOP:
8941 case ISEQ_TYPE_MAIN:
8942 if (retval) {
8943 rb_warn("argument of top-level return is ignored");
8944 }
8945 if (is == iseq) {
8946 /* plain top-level, leave directly */
8947 type = ISEQ_TYPE_METHOD;
8948 }
8949 break;
8950 default:
8951 break;
8952 }
8953
8954 if (type == ISEQ_TYPE_METHOD) {
8955 splabel = NEW_LABEL(0);
8956 ADD_LABEL(ret, splabel);
8957 ADD_ADJUST(ret, line_node, 0);
8958 }
8959
8960 CHECK(COMPILE(ret, "return nd_stts (return val)", retval));
8961
8962 if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
8963 add_ensure_iseq(ret, iseq, 1);
8964 ADD_TRACE(ret, RUBY_EVENT_RETURN);
8965 ADD_INSN(ret, line_node, leave);
8966 ADD_ADJUST_RESTORE(ret, splabel);
8967
8968 if (!popped) {
8969 ADD_INSN(ret, line_node, putnil);
8970 }
8971 }
8972 else {
8973 ADD_INSN1(ret, line_node, throw, INT2FIX(TAG_RETURN));
8974 if (popped) {
8975 ADD_INSN(ret, line_node, pop);
8976 }
8977 }
8978 }
8979 return COMPILE_OK;
8980}
8981
8982static bool
8983drop_unreachable_return(LINK_ANCHOR *ret)
8984{
8985 LINK_ELEMENT *i = ret->last, *last;
8986 if (!i) return false;
8987 if (IS_TRACE(i)) i = i->prev;
8988 if (!IS_INSN(i) || !IS_INSN_ID(i, putnil)) return false;
8989 last = i = i->prev;
8990 if (IS_ADJUST(i)) i = i->prev;
8991 if (!IS_INSN(i)) return false;
8992 switch (INSN_OF(i)) {
8993 case BIN(leave):
8994 case BIN(jump):
8995 break;
8996 default:
8997 return false;
8998 }
8999 (ret->last = last->prev)->next = NULL;
9000 return true;
9001}
9002
9003static int
9004compile_evstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9005{
9006 CHECK(COMPILE_(ret, "nd_body", node, popped));
9007
9008 if (!popped && !all_string_result_p(node)) {
9009 const NODE *line_node = node;
9010 const unsigned int flag = VM_CALL_FCALL;
9011
9012 // Note, this dup could be removed if we are willing to change anytostring. It pops
9013 // two VALUEs off the stack when it could work by replacing the top most VALUE.
9014 ADD_INSN(ret, line_node, dup);
9015 ADD_INSN1(ret, line_node, objtostring, new_callinfo(iseq, idTo_s, 0, flag, NULL, FALSE));
9016 ADD_INSN(ret, line_node, anytostring);
9017 }
9018 return COMPILE_OK;
9019}
9020
9021static void
9022compile_lvar(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *line_node, ID id)
9023{
9024 int idx = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
9025
9026 debugs("id: %s idx: %d\n", rb_id2name(id), idx);
9027 ADD_GETLOCAL(ret, line_node, idx, get_lvar_level(iseq));
9028}
9029
9030static LABEL *
9031qcall_branch_start(rb_iseq_t *iseq, LINK_ANCHOR *const recv, VALUE *branches, const NODE *node, const NODE *line_node)
9032{
9033 LABEL *else_label = NEW_LABEL(nd_line(line_node));
9034 VALUE br = 0;
9035
9036 br = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "&.");
9037 *branches = br;
9038 ADD_INSN(recv, line_node, dup);
9039 ADD_INSNL(recv, line_node, branchnil, else_label);
9040 add_trace_branch_coverage(iseq, recv, nd_code_loc(node), nd_node_id(node), 0, "then", br);
9041 return else_label;
9042}
9043
9044static void
9045qcall_branch_end(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *else_label, VALUE branches, const NODE *node, const NODE *line_node)
9046{
9047 LABEL *end_label;
9048 if (!else_label) return;
9049 end_label = NEW_LABEL(nd_line(line_node));
9050 ADD_INSNL(ret, line_node, jump, end_label);
9051 ADD_LABEL(ret, else_label);
9052 add_trace_branch_coverage(iseq, ret, nd_code_loc(node), nd_node_id(node), 1, "else", branches);
9053 ADD_LABEL(ret, end_label);
9054}
9055
9056static int
9057compile_call_precheck_freeze(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const NODE *line_node, int popped)
9058{
9059 /* optimization shortcut
9060 * "literal".freeze -> opt_str_freeze("literal")
9061 */
9062 if (get_nd_recv(node) &&
9063 (nd_type_p(get_nd_recv(node), NODE_STR) || nd_type_p(get_nd_recv(node), NODE_FILE)) &&
9064 (get_node_call_nd_mid(node) == idFreeze || get_node_call_nd_mid(node) == idUMinus) &&
9065 get_nd_args(node) == NULL &&
9066 ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
9067 ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
9068 VALUE str = get_string_value(get_nd_recv(node));
9069 if (get_node_call_nd_mid(node) == idUMinus) {
9070 ADD_INSN2(ret, line_node, opt_str_uminus, str,
9071 new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE));
9072 }
9073 else {
9074 ADD_INSN2(ret, line_node, opt_str_freeze, str,
9075 new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE));
9076 }
9077 RB_OBJ_WRITTEN(iseq, Qundef, str);
9078 if (popped) {
9079 ADD_INSN(ret, line_node, pop);
9080 }
9081 return TRUE;
9082 }
9083 return FALSE;
9084}
9085
9086static int
9087iseq_has_builtin_function_table(const rb_iseq_t *iseq)
9088{
9089 return ISEQ_COMPILE_DATA(iseq)->builtin_function_table != NULL;
9090}
9091
9092static const struct rb_builtin_function *
9093iseq_builtin_function_lookup(const rb_iseq_t *iseq, const char *name)
9094{
9095 int i;
9096 const struct rb_builtin_function *table = ISEQ_COMPILE_DATA(iseq)->builtin_function_table;
9097 for (i=0; table[i].index != -1; i++) {
9098 if (strcmp(table[i].name, name) == 0) {
9099 return &table[i];
9100 }
9101 }
9102 return NULL;
9103}
9104
9105static const char *
9106iseq_builtin_function_name(const enum node_type type, const NODE *recv, ID mid)
9107{
9108 const char *name = rb_id2name(mid);
9109 static const char prefix[] = "__builtin_";
9110 const size_t prefix_len = sizeof(prefix) - 1;
9111
9112 switch (type) {
9113 case NODE_CALL:
9114 if (recv) {
9115 switch (nd_type(recv)) {
9116 case NODE_VCALL:
9117 if (RNODE_VCALL(recv)->nd_mid == rb_intern("__builtin")) {
9118 return name;
9119 }
9120 break;
9121 case NODE_CONST:
9122 if (RNODE_CONST(recv)->nd_vid == rb_intern("Primitive")) {
9123 return name;
9124 }
9125 break;
9126 default: break;
9127 }
9128 }
9129 break;
9130 case NODE_VCALL:
9131 case NODE_FCALL:
9132 if (UNLIKELY(strncmp(prefix, name, prefix_len) == 0)) {
9133 return &name[prefix_len];
9134 }
9135 break;
9136 default: break;
9137 }
9138 return NULL;
9139}
9140
9141static int
9142delegate_call_p(const rb_iseq_t *iseq, unsigned int argc, const LINK_ANCHOR *args, unsigned int *pstart_index)
9143{
9144
9145 if (argc == 0) {
9146 *pstart_index = 0;
9147 return TRUE;
9148 }
9149 else if (argc <= ISEQ_BODY(iseq)->local_table_size) {
9150 unsigned int start=0;
9151
9152 // local_table: [p1, p2, p3, l1, l2, l3]
9153 // arguments: [p3, l1, l2] -> 2
9154 for (start = 0;
9155 argc + start <= ISEQ_BODY(iseq)->local_table_size;
9156 start++) {
9157 const LINK_ELEMENT *elem = FIRST_ELEMENT(args);
9158
9159 for (unsigned int i=start; i-start<argc; i++) {
9160 if (IS_INSN(elem) &&
9161 INSN_OF(elem) == BIN(getlocal)) {
9162 int local_index = FIX2INT(OPERAND_AT(elem, 0));
9163 int local_level = FIX2INT(OPERAND_AT(elem, 1));
9164
9165 if (local_level == 0) {
9166 unsigned int index = ISEQ_BODY(iseq)->local_table_size - (local_index - VM_ENV_DATA_SIZE + 1);
9167 if (0) { // for debug
9168 fprintf(stderr, "lvar:%s (%d), id:%s (%d) local_index:%d, local_size:%d\n",
9169 rb_id2name(ISEQ_BODY(iseq)->local_table[i]), i,
9170 rb_id2name(ISEQ_BODY(iseq)->local_table[index]), index,
9171 local_index, (int)ISEQ_BODY(iseq)->local_table_size);
9172 }
9173 if (i == index) {
9174 elem = elem->next;
9175 continue; /* for */
9176 }
9177 else {
9178 goto next;
9179 }
9180 }
9181 else {
9182 goto fail; // level != 0 is unsupported
9183 }
9184 }
9185 else {
9186 goto fail; // insn is not a getlocal
9187 }
9188 }
9189 goto success;
9190 next:;
9191 }
9192 fail:
9193 return FALSE;
9194 success:
9195 *pstart_index = start;
9196 return TRUE;
9197 }
9198 else {
9199 return FALSE;
9200 }
9201}
9202
9203// Compile Primitive.attr! :leaf, ...
9204static int
9205compile_builtin_attr(rb_iseq_t *iseq, const NODE *node)
9206{
9207 VALUE symbol;
9208 VALUE string;
9209 if (!node) goto no_arg;
9210 while (node) {
9211 if (!nd_type_p(node, NODE_LIST)) goto bad_arg;
9212 const NODE *next = RNODE_LIST(node)->nd_next;
9213
9214 node = RNODE_LIST(node)->nd_head;
9215 if (!node) goto no_arg;
9216 switch (nd_type(node)) {
9217 case NODE_SYM:
9218 symbol = rb_node_sym_string_val(node);
9219 break;
9220 default:
9221 goto bad_arg;
9222 }
9223
9224 if (!SYMBOL_P(symbol)) goto non_symbol_arg;
9225
9226 string = rb_sym2str(symbol);
9227 if (strcmp(RSTRING_PTR(string), "leaf") == 0) {
9228 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_LEAF;
9229 }
9230 else if (strcmp(RSTRING_PTR(string), "inline_block") == 0) {
9231 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_INLINE_BLOCK;
9232 }
9233 else if (strcmp(RSTRING_PTR(string), "use_block") == 0) {
9234 iseq_set_use_block(iseq);
9235 }
9236 else if (strcmp(RSTRING_PTR(string), "c_trace") == 0) {
9237 // Let the iseq act like a C method in backtraces
9238 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_C_TRACE;
9239 }
9240 else if (strcmp(RSTRING_PTR(string), "without_interrupts") == 0) {
9241 ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_WITHOUT_INTERRUPTS;
9242 }
9243 else {
9244 goto unknown_arg;
9245 }
9246 node = next;
9247 }
9248 return COMPILE_OK;
9249 no_arg:
9250 COMPILE_ERROR(ERROR_ARGS "attr!: no argument");
9251 return COMPILE_NG;
9252 non_symbol_arg:
9253 COMPILE_ERROR(ERROR_ARGS "non symbol argument to attr!: %s", rb_builtin_class_name(symbol));
9254 return COMPILE_NG;
9255 unknown_arg:
9256 COMPILE_ERROR(ERROR_ARGS "unknown argument to attr!: %s", RSTRING_PTR(string));
9257 return COMPILE_NG;
9258 bad_arg:
9259 UNKNOWN_NODE("attr!", node, COMPILE_NG);
9260}
9261
9262static int
9263compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, const NODE *line_node, int popped)
9264{
9265 VALUE name;
9266
9267 if (!node) goto no_arg;
9268 if (!nd_type_p(node, NODE_LIST)) goto bad_arg;
9269 if (RNODE_LIST(node)->nd_next) goto too_many_arg;
9270 node = RNODE_LIST(node)->nd_head;
9271 if (!node) goto no_arg;
9272 switch (nd_type(node)) {
9273 case NODE_SYM:
9274 name = rb_node_sym_string_val(node);
9275 break;
9276 default:
9277 goto bad_arg;
9278 }
9279 if (!SYMBOL_P(name)) goto non_symbol_arg;
9280 if (!popped) {
9281 compile_lvar(iseq, ret, line_node, SYM2ID(name));
9282 }
9283 return COMPILE_OK;
9284 no_arg:
9285 COMPILE_ERROR(ERROR_ARGS "arg!: no argument");
9286 return COMPILE_NG;
9287 too_many_arg:
9288 COMPILE_ERROR(ERROR_ARGS "arg!: too many argument");
9289 return COMPILE_NG;
9290 non_symbol_arg:
9291 COMPILE_ERROR(ERROR_ARGS "non symbol argument to arg!: %s",
9292 rb_builtin_class_name(name));
9293 return COMPILE_NG;
9294 bad_arg:
9295 UNKNOWN_NODE("arg!", node, COMPILE_NG);
9296}
9297
9298static NODE *
9299mandatory_node(const rb_iseq_t *iseq, const NODE *cond_node)
9300{
9301 const NODE *node = ISEQ_COMPILE_DATA(iseq)->root_node;
9302 if (nd_type(node) == NODE_IF && RNODE_IF(node)->nd_cond == cond_node) {
9303 return RNODE_IF(node)->nd_body;
9304 }
9305 else {
9306 rb_bug("mandatory_node: can't find mandatory node");
9307 }
9308}
9309
9310static int
9311compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const NODE *line_node)
9312{
9313 // arguments
9314 struct rb_args_info args = {
9315 .pre_args_num = ISEQ_BODY(iseq)->param.lead_num,
9316 };
9317 rb_node_args_t args_node;
9318 rb_node_init(RNODE(&args_node), NODE_ARGS);
9319 args_node.nd_ainfo = args;
9320
9321 // local table without non-mandatory parameters
9322 const int skip_local_size = ISEQ_BODY(iseq)->param.size - ISEQ_BODY(iseq)->param.lead_num;
9323 const int table_size = ISEQ_BODY(iseq)->local_table_size - skip_local_size;
9324
9325 VALUE idtmp = 0;
9326 rb_ast_id_table_t *tbl = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
9327 tbl->size = table_size;
9328
9329 int i;
9330
9331 // lead parameters
9332 for (i=0; i<ISEQ_BODY(iseq)->param.lead_num; i++) {
9333 tbl->ids[i] = ISEQ_BODY(iseq)->local_table[i];
9334 }
9335 // local variables
9336 for (; i<table_size; i++) {
9337 tbl->ids[i] = ISEQ_BODY(iseq)->local_table[i + skip_local_size];
9338 }
9339
9340 rb_node_scope_t scope_node;
9341 rb_node_init(RNODE(&scope_node), NODE_SCOPE);
9342 scope_node.nd_tbl = tbl;
9343 scope_node.nd_body = mandatory_node(iseq, node);
9344 scope_node.nd_parent = NULL;
9345 scope_node.nd_args = &args_node;
9346
9347 VALUE ast_value = rb_ruby_ast_new(RNODE(&scope_node));
9348
9349 const rb_iseq_t *mandatory_only_iseq =
9350 rb_iseq_new_with_opt(ast_value, rb_iseq_base_label(iseq),
9351 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
9352 nd_line(line_node), NULL, 0,
9353 ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option,
9354 ISEQ_BODY(iseq)->variable.script_lines);
9355 RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->mandatory_only_iseq, (VALUE)mandatory_only_iseq);
9356
9357 ALLOCV_END(idtmp);
9358 return COMPILE_OK;
9359}
9360
9361static int
9362compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const NODE *line_node, int popped,
9363 const rb_iseq_t *parent_block, LINK_ANCHOR *args, const char *builtin_func)
9364{
9365 NODE *args_node = get_nd_args(node);
9366
9367 if (parent_block != NULL) {
9368 COMPILE_ERROR(ERROR_ARGS_AT(line_node) "should not call builtins here.");
9369 return COMPILE_NG;
9370 }
9371 else {
9372# define BUILTIN_INLINE_PREFIX "_bi"
9373 char inline_func[sizeof(BUILTIN_INLINE_PREFIX) + DECIMAL_SIZE_OF(int)];
9374 bool cconst = false;
9375 retry:;
9376 const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
9377
9378 if (bf == NULL) {
9379 if (strcmp("cstmt!", builtin_func) == 0 ||
9380 strcmp("cexpr!", builtin_func) == 0) {
9381 // ok
9382 }
9383 else if (strcmp("cconst!", builtin_func) == 0) {
9384 cconst = true;
9385 }
9386 else if (strcmp("cinit!", builtin_func) == 0) {
9387 // ignore
9388 return COMPILE_OK;
9389 }
9390 else if (strcmp("attr!", builtin_func) == 0) {
9391 return compile_builtin_attr(iseq, args_node);
9392 }
9393 else if (strcmp("arg!", builtin_func) == 0) {
9394 return compile_builtin_arg(iseq, ret, args_node, line_node, popped);
9395 }
9396 else if (strcmp("mandatory_only?", builtin_func) == 0) {
9397 if (popped) {
9398 rb_bug("mandatory_only? should be in if condition");
9399 }
9400 else if (!LIST_INSN_SIZE_ZERO(ret)) {
9401 rb_bug("mandatory_only? should be put on top");
9402 }
9403
9404 ADD_INSN1(ret, line_node, putobject, Qfalse);
9405 return compile_builtin_mandatory_only_method(iseq, node, line_node);
9406 }
9407 else if (1) {
9408 rb_bug("can't find builtin function:%s", builtin_func);
9409 }
9410 else {
9411 COMPILE_ERROR(ERROR_ARGS "can't find builtin function:%s", builtin_func);
9412 return COMPILE_NG;
9413 }
9414
9415 int inline_index = nd_line(node);
9416 snprintf(inline_func, sizeof(inline_func), BUILTIN_INLINE_PREFIX "%d", inline_index);
9417 builtin_func = inline_func;
9418 args_node = NULL;
9419 goto retry;
9420 }
9421
9422 if (cconst) {
9423 typedef VALUE(*builtin_func0)(void *, VALUE);
9424 VALUE const_val = (*(builtin_func0)(uintptr_t)bf->func_ptr)(NULL, Qnil);
9425 ADD_INSN1(ret, line_node, putobject, const_val);
9426 return COMPILE_OK;
9427 }
9428
9429 // fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
9430
9431 unsigned int flag = 0;
9432 struct rb_callinfo_kwarg *keywords = NULL;
9433 VALUE argc = setup_args(iseq, args, args_node, &flag, &keywords);
9434
9435 if (FIX2INT(argc) != bf->argc) {
9436 COMPILE_ERROR(ERROR_ARGS "argc is not match for builtin function:%s (expect %d but %d)",
9437 builtin_func, bf->argc, FIX2INT(argc));
9438 return COMPILE_NG;
9439 }
9440
9441 unsigned int start_index;
9442 if (delegate_call_p(iseq, FIX2INT(argc), args, &start_index)) {
9443 ADD_INSN2(ret, line_node, opt_invokebuiltin_delegate, bf, INT2FIX(start_index));
9444 }
9445 else {
9446 ADD_SEQ(ret, args);
9447 ADD_INSN1(ret, line_node, invokebuiltin, bf);
9448 }
9449
9450 if (popped) ADD_INSN(ret, line_node, pop);
9451 return COMPILE_OK;
9452 }
9453}
9454
9455static int
9456compile_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)
9457{
9458 /* call: obj.method(...)
9459 * fcall: func(...)
9460 * vcall: func
9461 */
9462 DECL_ANCHOR(recv);
9463 DECL_ANCHOR(args);
9464 ID mid = get_node_call_nd_mid(node);
9465 VALUE argc;
9466 unsigned int flag = 0;
9467 struct rb_callinfo_kwarg *keywords = NULL;
9468 const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
9469 LABEL *else_label = NULL;
9470 VALUE branches = Qfalse;
9471
9472 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
9473
9474 INIT_ANCHOR(recv);
9475 INIT_ANCHOR(args);
9476
9477#if OPT_SUPPORT_JOKE
9478 if (nd_type_p(node, NODE_VCALL)) {
9479 ID id_bitblt;
9480 ID id_answer;
9481
9482 CONST_ID(id_bitblt, "bitblt");
9483 CONST_ID(id_answer, "the_answer_to_life_the_universe_and_everything");
9484
9485 if (mid == id_bitblt) {
9486 ADD_INSN(ret, line_node, bitblt);
9487 return COMPILE_OK;
9488 }
9489 else if (mid == id_answer) {
9490 ADD_INSN(ret, line_node, answer);
9491 return COMPILE_OK;
9492 }
9493 }
9494 /* only joke */
9495 {
9496 ID goto_id;
9497 ID label_id;
9498
9499 CONST_ID(goto_id, "__goto__");
9500 CONST_ID(label_id, "__label__");
9501
9502 if (nd_type_p(node, NODE_FCALL) &&
9503 (mid == goto_id || mid == label_id)) {
9504 LABEL *label;
9505 st_data_t data;
9506 st_table *labels_table = ISEQ_COMPILE_DATA(iseq)->labels_table;
9507 VALUE label_name;
9508
9509 if (!labels_table) {
9510 labels_table = st_init_numtable();
9511 ISEQ_COMPILE_DATA(iseq)->labels_table = labels_table;
9512 }
9513 {
9514 COMPILE_ERROR(ERROR_ARGS "invalid goto/label format");
9515 return COMPILE_NG;
9516 }
9517
9518 if (mid == goto_id) {
9519 ADD_INSNL(ret, line_node, jump, label);
9520 }
9521 else {
9522 ADD_LABEL(ret, label);
9523 }
9524 return COMPILE_OK;
9525 }
9526 }
9527#endif
9528
9529 const char *builtin_func;
9530 if (UNLIKELY(iseq_has_builtin_function_table(iseq)) &&
9531 (builtin_func = iseq_builtin_function_name(type, get_nd_recv(node), mid)) != NULL) {
9532 return compile_builtin_function_call(iseq, ret, node, line_node, popped, parent_block, args, builtin_func);
9533 }
9534
9535 /* receiver */
9536 if (!assume_receiver) {
9537 if (type == NODE_CALL || type == NODE_OPCALL || type == NODE_QCALL) {
9538 int idx, level;
9539
9540 if (mid == idCall &&
9541 nd_type_p(get_nd_recv(node), NODE_LVAR) &&
9542 iseq_block_param_id_p(iseq, RNODE_LVAR(get_nd_recv(node))->nd_vid, &idx, &level)) {
9543 ADD_INSN2(recv, get_nd_recv(node), getblockparamproxy, INT2FIX(idx + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
9544 }
9545 else if (private_recv_p(node)) {
9546 ADD_INSN(recv, node, putself);
9547 flag |= VM_CALL_FCALL;
9548 }
9549 else {
9550 CHECK(COMPILE(recv, "recv", get_nd_recv(node)));
9551 }
9552
9553 if (type == NODE_QCALL) {
9554 else_label = qcall_branch_start(iseq, recv, &branches, node, line_node);
9555 }
9556 }
9557 else if (type == NODE_FCALL || type == NODE_VCALL) {
9558 ADD_CALL_RECEIVER(recv, line_node);
9559 }
9560 }
9561
9562 /* args */
9563 if (type != NODE_VCALL) {
9564 argc = setup_args(iseq, args, get_nd_args(node), &flag, &keywords);
9565 CHECK(!NIL_P(argc));
9566 }
9567 else {
9568 argc = INT2FIX(0);
9569 }
9570
9571 ADD_SEQ(ret, recv);
9572
9573 bool inline_new = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction &&
9574 mid == rb_intern("new") &&
9575 parent_block == NULL &&
9576 !(flag & VM_CALL_ARGS_BLOCKARG);
9577
9578 if (inline_new) {
9579 ADD_INSN(ret, node, putnil);
9580 ADD_INSN(ret, node, swap);
9581 }
9582
9583 ADD_SEQ(ret, args);
9584
9585 debugp_param("call args argc", argc);
9586 debugp_param("call method", ID2SYM(mid));
9587
9588 switch ((int)type) {
9589 case NODE_VCALL:
9590 flag |= VM_CALL_VCALL;
9591 /* VCALL is funcall, so fall through */
9592 case NODE_FCALL:
9593 flag |= VM_CALL_FCALL;
9594 }
9595
9596 if ((flag & VM_CALL_ARGS_BLOCKARG) && (flag & VM_CALL_KW_SPLAT) && !(flag & VM_CALL_KW_SPLAT_MUT)) {
9597 ADD_INSN(ret, line_node, splatkw);
9598 }
9599
9600 LABEL *not_basic_new = NEW_LABEL(nd_line(node));
9601 LABEL *not_basic_new_finish = NEW_LABEL(nd_line(node));
9602
9603 if (inline_new) {
9604 // Jump unless the receiver uses the "basic" implementation of "new"
9605 VALUE ci;
9606 if (flag & VM_CALL_FORWARDING) {
9607 ci = (VALUE)new_callinfo(iseq, mid, NUM2INT(argc) + 1, flag, keywords, 0);
9608 }
9609 else {
9610 ci = (VALUE)new_callinfo(iseq, mid, NUM2INT(argc), flag, keywords, 0);
9611 }
9612 ADD_INSN2(ret, node, opt_new, ci, not_basic_new);
9613 LABEL_REF(not_basic_new);
9614
9615 // optimized path
9616 ADD_SEND_R(ret, line_node, rb_intern("initialize"), argc, parent_block, INT2FIX(flag | VM_CALL_FCALL), keywords);
9617 ADD_INSNL(ret, line_node, jump, not_basic_new_finish);
9618
9619 ADD_LABEL(ret, not_basic_new);
9620 // Fall back to normal send
9621 ADD_SEND_R(ret, line_node, mid, argc, parent_block, INT2FIX(flag), keywords);
9622 ADD_INSN(ret, line_node, swap);
9623
9624 ADD_LABEL(ret, not_basic_new_finish);
9625 ADD_INSN(ret, line_node, pop);
9626 }
9627 else {
9628 ADD_SEND_R(ret, line_node, mid, argc, parent_block, INT2FIX(flag), keywords);
9629 }
9630
9631 qcall_branch_end(iseq, ret, else_label, branches, node, line_node);
9632 if (popped) {
9633 ADD_INSN(ret, line_node, pop);
9634 }
9635 return COMPILE_OK;
9636}
9637
9638static int
9639compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9640{
9641 const int line = nd_line(node);
9642 VALUE argc;
9643 unsigned int flag = 0;
9644 int asgnflag = 0;
9645 ID id = RNODE_OP_ASGN1(node)->nd_mid;
9646
9647 /*
9648 * a[x] (op)= y
9649 *
9650 * nil # nil
9651 * eval a # nil a
9652 * eval x # nil a x
9653 * dupn 2 # nil a x a x
9654 * send :[] # nil a x a[x]
9655 * eval y # nil a x a[x] y
9656 * send op # nil a x ret
9657 * setn 3 # ret a x ret
9658 * send []= # ret ?
9659 * pop # ret
9660 */
9661
9662 /*
9663 * nd_recv[nd_args->nd_body] (nd_mid)= nd_args->nd_head;
9664 * NODE_OP_ASGN nd_recv
9665 * nd_args->nd_head
9666 * nd_args->nd_body
9667 * nd_mid
9668 */
9669
9670 if (!popped) {
9671 ADD_INSN(ret, node, putnil);
9672 }
9673 asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN1 recv", node, RNODE_OP_ASGN1(node)->nd_recv);
9674 CHECK(asgnflag != -1);
9675 switch (nd_type(RNODE_OP_ASGN1(node)->nd_index)) {
9676 case NODE_ZLIST:
9677 argc = INT2FIX(0);
9678 break;
9679 default:
9680 argc = setup_args(iseq, ret, RNODE_OP_ASGN1(node)->nd_index, &flag, NULL);
9681 CHECK(!NIL_P(argc));
9682 }
9683 int dup_argn = FIX2INT(argc) + 1;
9684 ADD_INSN1(ret, node, dupn, INT2FIX(dup_argn));
9685 flag |= asgnflag;
9686 ADD_SEND_R(ret, node, idAREF, argc, NULL, INT2FIX(flag & ~VM_CALL_ARGS_SPLAT_MUT), NULL);
9687
9688 if (id == idOROP || id == idANDOP) {
9689 /* a[x] ||= y or a[x] &&= y
9690
9691 unless/if a[x]
9692 a[x]= y
9693 else
9694 nil
9695 end
9696 */
9697 LABEL *label = NEW_LABEL(line);
9698 LABEL *lfin = NEW_LABEL(line);
9699
9700 ADD_INSN(ret, node, dup);
9701 if (id == idOROP) {
9702 ADD_INSNL(ret, node, branchif, label);
9703 }
9704 else { /* idANDOP */
9705 ADD_INSNL(ret, node, branchunless, label);
9706 }
9707 ADD_INSN(ret, node, pop);
9708
9709 CHECK(COMPILE(ret, "NODE_OP_ASGN1 nd_rvalue: ", RNODE_OP_ASGN1(node)->nd_rvalue));
9710 if (!popped) {
9711 ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
9712 }
9713 if (flag & VM_CALL_ARGS_SPLAT) {
9714 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
9715 ADD_INSN(ret, node, swap);
9716 ADD_INSN1(ret, node, splatarray, Qtrue);
9717 ADD_INSN(ret, node, swap);
9718 flag |= VM_CALL_ARGS_SPLAT_MUT;
9719 }
9720 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
9721 ADD_SEND_R(ret, node, idASET, argc, NULL, INT2FIX(flag), NULL);
9722 }
9723 else {
9724 ADD_SEND_R(ret, node, idASET, FIXNUM_INC(argc, 1), NULL, INT2FIX(flag), NULL);
9725 }
9726 ADD_INSN(ret, node, pop);
9727 ADD_INSNL(ret, node, jump, lfin);
9728 ADD_LABEL(ret, label);
9729 if (!popped) {
9730 ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
9731 }
9732 ADD_INSN1(ret, node, adjuststack, INT2FIX(dup_argn+1));
9733 ADD_LABEL(ret, lfin);
9734 }
9735 else {
9736 CHECK(COMPILE(ret, "NODE_OP_ASGN1 nd_rvalue: ", RNODE_OP_ASGN1(node)->nd_rvalue));
9737 ADD_SEND(ret, node, id, INT2FIX(1));
9738 if (!popped) {
9739 ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1));
9740 }
9741 if (flag & VM_CALL_ARGS_SPLAT) {
9742 if (flag & VM_CALL_KW_SPLAT) {
9743 ADD_INSN1(ret, node, topn, INT2FIX(2));
9744 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
9745 ADD_INSN1(ret, node, splatarray, Qtrue);
9746 flag |= VM_CALL_ARGS_SPLAT_MUT;
9747 }
9748 ADD_INSN(ret, node, swap);
9749 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
9750 ADD_INSN1(ret, node, setn, INT2FIX(2));
9751 ADD_INSN(ret, node, pop);
9752 }
9753 else {
9754 if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) {
9755 ADD_INSN(ret, node, swap);
9756 ADD_INSN1(ret, node, splatarray, Qtrue);
9757 ADD_INSN(ret, node, swap);
9758 flag |= VM_CALL_ARGS_SPLAT_MUT;
9759 }
9760 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
9761 }
9762 ADD_SEND_R(ret, node, idASET, argc, NULL, INT2FIX(flag), NULL);
9763 }
9764 else {
9765 ADD_SEND_R(ret, node, idASET, FIXNUM_INC(argc, 1), NULL, INT2FIX(flag), NULL);
9766 }
9767 ADD_INSN(ret, node, pop);
9768 }
9769 return COMPILE_OK;
9770}
9771
9772static int
9773compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9774{
9775 const int line = nd_line(node);
9776 ID atype = RNODE_OP_ASGN2(node)->nd_mid;
9777 ID vid = RNODE_OP_ASGN2(node)->nd_vid, aid = rb_id_attrset(vid);
9778 int asgnflag;
9779 LABEL *lfin = NEW_LABEL(line);
9780 LABEL *lcfin = NEW_LABEL(line);
9781 LABEL *lskip = 0;
9782 /*
9783 class C; attr_accessor :c; end
9784 r = C.new
9785 r.a &&= v # asgn2
9786
9787 eval r # r
9788 dup # r r
9789 eval r.a # r o
9790
9791 # or
9792 dup # r o o
9793 if lcfin # r o
9794 pop # r
9795 eval v # r v
9796 swap # v r
9797 topn 1 # v r v
9798 send a= # v ?
9799 jump lfin # v ?
9800
9801 lcfin: # r o
9802 swap # o r
9803
9804 lfin: # o ?
9805 pop # o
9806
9807 # or (popped)
9808 if lcfin # r
9809 eval v # r v
9810 send a= # ?
9811 jump lfin # ?
9812
9813 lcfin: # r
9814
9815 lfin: # ?
9816 pop #
9817
9818 # and
9819 dup # r o o
9820 unless lcfin
9821 pop # r
9822 eval v # r v
9823 swap # v r
9824 topn 1 # v r v
9825 send a= # v ?
9826 jump lfin # v ?
9827
9828 # others
9829 eval v # r o v
9830 send ?? # r w
9831 send a= # w
9832
9833 */
9834
9835 asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN2#recv", node, RNODE_OP_ASGN2(node)->nd_recv);
9836 CHECK(asgnflag != -1);
9837 if (RNODE_OP_ASGN2(node)->nd_aid) {
9838 lskip = NEW_LABEL(line);
9839 ADD_INSN(ret, node, dup);
9840 ADD_INSNL(ret, node, branchnil, lskip);
9841 }
9842 ADD_INSN(ret, node, dup);
9843 ADD_SEND_WITH_FLAG(ret, node, vid, INT2FIX(0), INT2FIX(asgnflag));
9844
9845 if (atype == idOROP || atype == idANDOP) {
9846 if (!popped) {
9847 ADD_INSN(ret, node, dup);
9848 }
9849 if (atype == idOROP) {
9850 ADD_INSNL(ret, node, branchif, lcfin);
9851 }
9852 else { /* idANDOP */
9853 ADD_INSNL(ret, node, branchunless, lcfin);
9854 }
9855 if (!popped) {
9856 ADD_INSN(ret, node, pop);
9857 }
9858 CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", RNODE_OP_ASGN2(node)->nd_value));
9859 if (!popped) {
9860 ADD_INSN(ret, node, swap);
9861 ADD_INSN1(ret, node, topn, INT2FIX(1));
9862 }
9863 ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
9864 ADD_INSNL(ret, node, jump, lfin);
9865
9866 ADD_LABEL(ret, lcfin);
9867 if (!popped) {
9868 ADD_INSN(ret, node, swap);
9869 }
9870
9871 ADD_LABEL(ret, lfin);
9872 }
9873 else {
9874 CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", RNODE_OP_ASGN2(node)->nd_value));
9875 ADD_SEND(ret, node, atype, INT2FIX(1));
9876 if (!popped) {
9877 ADD_INSN(ret, node, swap);
9878 ADD_INSN1(ret, node, topn, INT2FIX(1));
9879 }
9880 ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag));
9881 }
9882 if (lskip && popped) {
9883 ADD_LABEL(ret, lskip);
9884 }
9885 ADD_INSN(ret, node, pop);
9886 if (lskip && !popped) {
9887 ADD_LABEL(ret, lskip);
9888 }
9889 return COMPILE_OK;
9890}
9891
9892static int compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value);
9893
9894static int
9895compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
9896{
9897 const int line = nd_line(node);
9898 LABEL *lfin = 0;
9899 LABEL *lassign = 0;
9900 ID mid;
9901
9902 switch (nd_type(RNODE_OP_CDECL(node)->nd_head)) {
9903 case NODE_COLON3:
9904 ADD_INSN1(ret, node, putobject, rb_cObject);
9905 break;
9906 case NODE_COLON2:
9907 CHECK(COMPILE(ret, "NODE_OP_CDECL/colon2#nd_head", RNODE_COLON2(RNODE_OP_CDECL(node)->nd_head)->nd_head));
9908 break;
9909 default:
9910 COMPILE_ERROR(ERROR_ARGS "%s: invalid node in NODE_OP_CDECL",
9911 ruby_node_name(nd_type(RNODE_OP_CDECL(node)->nd_head)));
9912 return COMPILE_NG;
9913 }
9914 mid = get_node_colon_nd_mid(RNODE_OP_CDECL(node)->nd_head);
9915 /* cref */
9916 if (RNODE_OP_CDECL(node)->nd_aid == idOROP) {
9917 lassign = NEW_LABEL(line);
9918 ADD_INSN(ret, node, dup); /* cref cref */
9919 ADD_INSN3(ret, node, defined, INT2FIX(DEFINED_CONST_FROM),
9920 ID2SYM(mid), Qtrue); /* cref bool */
9921 ADD_INSNL(ret, node, branchunless, lassign); /* cref */
9922 }
9923 ADD_INSN(ret, node, dup); /* cref cref */
9924 ADD_INSN1(ret, node, putobject, Qtrue);
9925 ADD_INSN1(ret, node, getconstant, ID2SYM(mid)); /* cref obj */
9926
9927 if (RNODE_OP_CDECL(node)->nd_aid == idOROP || RNODE_OP_CDECL(node)->nd_aid == idANDOP) {
9928 lfin = NEW_LABEL(line);
9929 if (!popped) ADD_INSN(ret, node, dup); /* cref [obj] obj */
9930 if (RNODE_OP_CDECL(node)->nd_aid == idOROP)
9931 ADD_INSNL(ret, node, branchif, lfin);
9932 else /* idANDOP */
9933 ADD_INSNL(ret, node, branchunless, lfin);
9934 /* cref [obj] */
9935 if (!popped) ADD_INSN(ret, node, pop); /* cref */
9936 if (lassign) ADD_LABEL(ret, lassign);
9937 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value));
9938 /* cref value */
9939 if (popped)
9940 ADD_INSN1(ret, node, topn, INT2FIX(1)); /* cref value cref */
9941 else {
9942 ADD_INSN1(ret, node, dupn, INT2FIX(2)); /* cref value cref value */
9943 ADD_INSN(ret, node, swap); /* cref value value cref */
9944 }
9945 ADD_INSN1(ret, node, setconstant, ID2SYM(mid)); /* cref [value] */
9946 ADD_LABEL(ret, lfin); /* cref [value] */
9947 if (!popped) ADD_INSN(ret, node, swap); /* [value] cref */
9948 ADD_INSN(ret, node, pop); /* [value] */
9949 }
9950 else {
9951 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value));
9952 /* cref obj value */
9953 ADD_CALL(ret, node, RNODE_OP_CDECL(node)->nd_aid, INT2FIX(1));
9954 /* cref value */
9955 ADD_INSN(ret, node, swap); /* value cref */
9956 if (!popped) {
9957 ADD_INSN1(ret, node, topn, INT2FIX(1)); /* value cref value */
9958 ADD_INSN(ret, node, swap); /* value value cref */
9959 }
9960 ADD_INSN1(ret, node, setconstant, ID2SYM(mid));
9961 }
9962 return COMPILE_OK;
9963}
9964
9965static int
9966compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
9967{
9968 const int line = nd_line(node);
9969 LABEL *lfin = NEW_LABEL(line);
9970 LABEL *lassign;
9971
9972 if (type == NODE_OP_ASGN_OR && !nd_type_p(RNODE_OP_ASGN_OR(node)->nd_head, NODE_IVAR)) {
9973 LABEL *lfinish[2];
9974 lfinish[0] = lfin;
9975 lfinish[1] = 0;
9976 defined_expr(iseq, ret, RNODE_OP_ASGN_OR(node)->nd_head, lfinish, Qfalse, false);
9977 lassign = lfinish[1];
9978 if (!lassign) {
9979 lassign = NEW_LABEL(line);
9980 }
9981 ADD_INSNL(ret, node, branchunless, lassign);
9982 }
9983 else {
9984 lassign = NEW_LABEL(line);
9985 }
9986
9987 CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", RNODE_OP_ASGN_OR(node)->nd_head));
9988
9989 if (!popped) {
9990 ADD_INSN(ret, node, dup);
9991 }
9992
9993 if (type == NODE_OP_ASGN_AND) {
9994 ADD_INSNL(ret, node, branchunless, lfin);
9995 }
9996 else {
9997 ADD_INSNL(ret, node, branchif, lfin);
9998 }
9999
10000 if (!popped) {
10001 ADD_INSN(ret, node, pop);
10002 }
10003
10004 ADD_LABEL(ret, lassign);
10005 CHECK(COMPILE_(ret, "NODE_OP_ASGN_AND/OR#nd_value", RNODE_OP_ASGN_OR(node)->nd_value, popped));
10006 ADD_LABEL(ret, lfin);
10007 return COMPILE_OK;
10008}
10009
10010static int
10011compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
10012{
10013 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
10014 DECL_ANCHOR(args);
10015 int argc;
10016 unsigned int flag = 0;
10017 struct rb_callinfo_kwarg *keywords = NULL;
10018 const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
10019 int use_block = 1;
10020
10021 INIT_ANCHOR(args);
10022 ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
10023
10024 if (type == NODE_SUPER) {
10025 VALUE vargc = setup_args(iseq, args, RNODE_SUPER(node)->nd_args, &flag, &keywords);
10026 CHECK(!NIL_P(vargc));
10027 argc = FIX2INT(vargc);
10028 if ((flag & VM_CALL_ARGS_BLOCKARG) && (flag & VM_CALL_KW_SPLAT) && !(flag & VM_CALL_KW_SPLAT_MUT)) {
10029 ADD_INSN(args, node, splatkw);
10030 }
10031
10032 if (flag & VM_CALL_ARGS_BLOCKARG) {
10033 use_block = 0;
10034 }
10035 }
10036 else {
10037 /* NODE_ZSUPER */
10038 int i;
10039 const rb_iseq_t *liseq = body->local_iseq;
10040 const struct rb_iseq_constant_body *const local_body = ISEQ_BODY(liseq);
10041 const struct rb_iseq_param_keyword *const local_kwd = local_body->param.keyword;
10042 int lvar_level = get_lvar_level(iseq);
10043
10044 argc = local_body->param.lead_num;
10045
10046 /* normal arguments */
10047 for (i = 0; i < local_body->param.lead_num; i++) {
10048 int idx = local_body->local_table_size - i;
10049 ADD_GETLOCAL(args, node, idx, lvar_level);
10050 }
10051
10052 /* forward ... */
10053 if (local_body->param.flags.forwardable) {
10054 flag |= VM_CALL_FORWARDING;
10055 int idx = local_body->local_table_size - get_local_var_idx(liseq, idDot3);
10056 ADD_GETLOCAL(args, node, idx, lvar_level);
10057 }
10058
10059 if (local_body->param.flags.has_opt) {
10060 /* optional arguments */
10061 int j;
10062 for (j = 0; j < local_body->param.opt_num; j++) {
10063 int idx = local_body->local_table_size - (i + j);
10064 ADD_GETLOCAL(args, node, idx, lvar_level);
10065 }
10066 i += j;
10067 argc = i;
10068 }
10069 if (local_body->param.flags.has_rest) {
10070 /* rest argument */
10071 int idx = local_body->local_table_size - local_body->param.rest_start;
10072 ADD_GETLOCAL(args, node, idx, lvar_level);
10073 ADD_INSN1(args, node, splatarray, RBOOL(local_body->param.flags.has_post));
10074
10075 argc = local_body->param.rest_start + 1;
10076 flag |= VM_CALL_ARGS_SPLAT;
10077 }
10078 if (local_body->param.flags.has_post) {
10079 /* post arguments */
10080 int post_len = local_body->param.post_num;
10081 int post_start = local_body->param.post_start;
10082
10083 if (local_body->param.flags.has_rest) {
10084 int j;
10085 for (j=0; j<post_len; j++) {
10086 int idx = local_body->local_table_size - (post_start + j);
10087 ADD_GETLOCAL(args, node, idx, lvar_level);
10088 }
10089 ADD_INSN1(args, node, pushtoarray, INT2FIX(j));
10090 flag |= VM_CALL_ARGS_SPLAT_MUT;
10091 /* argc is settled at above */
10092 }
10093 else {
10094 int j;
10095 for (j=0; j<post_len; j++) {
10096 int idx = local_body->local_table_size - (post_start + j);
10097 ADD_GETLOCAL(args, node, idx, lvar_level);
10098 }
10099 argc = post_len + post_start;
10100 }
10101 }
10102
10103 if (local_body->param.flags.has_kw) { /* TODO: support keywords */
10104 int local_size = local_body->local_table_size;
10105 argc++;
10106
10107 ADD_INSN1(args, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10108
10109 if (local_body->param.flags.has_kwrest) {
10110 int idx = local_body->local_table_size - local_kwd->rest_start;
10111 ADD_GETLOCAL(args, node, idx, lvar_level);
10112 RUBY_ASSERT(local_kwd->num > 0);
10113 ADD_SEND (args, node, rb_intern("dup"), INT2FIX(0));
10114 }
10115 else {
10116 ADD_INSN1(args, node, newhash, INT2FIX(0));
10117 }
10118 for (i = 0; i < local_kwd->num; ++i) {
10119 ID id = local_kwd->table[i];
10120 int idx = local_size - get_local_var_idx(liseq, id);
10121 ADD_INSN1(args, node, putobject, ID2SYM(id));
10122 ADD_GETLOCAL(args, node, idx, lvar_level);
10123 }
10124 ADD_SEND(args, node, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
10125 flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT;
10126 }
10127 else if (local_body->param.flags.has_kwrest) {
10128 int idx = local_body->local_table_size - local_kwd->rest_start;
10129 ADD_GETLOCAL(args, node, idx, lvar_level);
10130 argc++;
10131 flag |= VM_CALL_KW_SPLAT;
10132 }
10133 }
10134
10135 if (use_block && parent_block == NULL) {
10136 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
10137 }
10138
10139 flag |= VM_CALL_SUPER | VM_CALL_FCALL;
10140 if (type == NODE_ZSUPER) flag |= VM_CALL_ZSUPER;
10141 ADD_INSN(ret, node, putself);
10142 ADD_SEQ(ret, args);
10143
10144 const struct rb_callinfo * ci = new_callinfo(iseq, 0, argc, flag, keywords, parent_block != NULL);
10145
10146 if (vm_ci_flag(ci) & VM_CALL_FORWARDING) {
10147 ADD_INSN2(ret, node, invokesuperforward, ci, parent_block);
10148 }
10149 else {
10150 ADD_INSN2(ret, node, invokesuper, ci, parent_block);
10151 }
10152
10153 if (popped) {
10154 ADD_INSN(ret, node, pop);
10155 }
10156 return COMPILE_OK;
10157}
10158
10159static int
10160compile_yield(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10161{
10162 DECL_ANCHOR(args);
10163 VALUE argc;
10164 unsigned int flag = 0;
10165 struct rb_callinfo_kwarg *keywords = NULL;
10166
10167 INIT_ANCHOR(args);
10168
10169 switch (ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->type) {
10170 case ISEQ_TYPE_TOP:
10171 case ISEQ_TYPE_MAIN:
10172 case ISEQ_TYPE_CLASS:
10173 COMPILE_ERROR(ERROR_ARGS "Invalid yield");
10174 return COMPILE_NG;
10175 default: /* valid */;
10176 }
10177
10178 if (RNODE_YIELD(node)->nd_head) {
10179 argc = setup_args(iseq, args, RNODE_YIELD(node)->nd_head, &flag, &keywords);
10180 CHECK(!NIL_P(argc));
10181 }
10182 else {
10183 argc = INT2FIX(0);
10184 }
10185
10186 ADD_SEQ(ret, args);
10187 ADD_INSN1(ret, node, invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), flag, keywords, FALSE));
10188 iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
10189
10190 if (popped) {
10191 ADD_INSN(ret, node, pop);
10192 }
10193
10194 int level = 0;
10195 const rb_iseq_t *tmp_iseq = iseq;
10196 for (; tmp_iseq != ISEQ_BODY(iseq)->local_iseq; level++ ) {
10197 tmp_iseq = ISEQ_BODY(tmp_iseq)->parent_iseq;
10198 }
10199 if (level > 0) access_outer_variables(iseq, level, rb_intern("yield"), true);
10200
10201 return COMPILE_OK;
10202}
10203
10204static int
10205compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
10206{
10207 DECL_ANCHOR(recv);
10208 DECL_ANCHOR(val);
10209
10210 INIT_ANCHOR(recv);
10211 INIT_ANCHOR(val);
10212 switch ((int)type) {
10213 case NODE_MATCH:
10214 {
10215 VALUE re = rb_node_regx_string_val(node);
10216 RB_OBJ_SET_FROZEN_SHAREABLE(re);
10217 ADD_INSN1(recv, node, putobject, re);
10218 ADD_INSN2(val, node, getspecial, INT2FIX(0),
10219 INT2FIX(0));
10220 }
10221 break;
10222 case NODE_MATCH2:
10223 CHECK(COMPILE(recv, "receiver", RNODE_MATCH2(node)->nd_recv));
10224 CHECK(COMPILE(val, "value", RNODE_MATCH2(node)->nd_value));
10225 break;
10226 case NODE_MATCH3:
10227 CHECK(COMPILE(recv, "receiver", RNODE_MATCH3(node)->nd_value));
10228 CHECK(COMPILE(val, "value", RNODE_MATCH3(node)->nd_recv));
10229 break;
10230 }
10231
10232 ADD_SEQ(ret, recv);
10233 ADD_SEQ(ret, val);
10234 ADD_SEND(ret, node, idEqTilde, INT2FIX(1));
10235
10236 if (nd_type_p(node, NODE_MATCH2) && RNODE_MATCH2(node)->nd_args) {
10237 compile_named_capture_assign(iseq, ret, RNODE_MATCH2(node)->nd_args);
10238 }
10239
10240 if (popped) {
10241 ADD_INSN(ret, node, pop);
10242 }
10243 return COMPILE_OK;
10244}
10245
10246static int
10247compile_colon2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10248{
10249 if (rb_is_const_id(RNODE_COLON2(node)->nd_mid)) {
10250 /* constant */
10251 VALUE segments;
10252 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache &&
10253 (segments = collect_const_segments(iseq, node))) {
10254 ISEQ_BODY(iseq)->ic_size++;
10255 ADD_INSN1(ret, node, opt_getconstant_path, segments);
10256 RB_OBJ_WRITTEN(iseq, Qundef, segments);
10257 }
10258 else {
10259 /* constant */
10260 DECL_ANCHOR(pref);
10261 DECL_ANCHOR(body);
10262
10263 INIT_ANCHOR(pref);
10264 INIT_ANCHOR(body);
10265 CHECK(compile_const_prefix(iseq, node, pref, body));
10266 if (LIST_INSN_SIZE_ZERO(pref)) {
10267 ADD_INSN(ret, node, putnil);
10268 ADD_SEQ(ret, body);
10269 }
10270 else {
10271 ADD_SEQ(ret, pref);
10272 ADD_SEQ(ret, body);
10273 }
10274 }
10275 }
10276 else {
10277 /* function call */
10278 ADD_CALL_RECEIVER(ret, node);
10279 CHECK(COMPILE(ret, "colon2#nd_head", RNODE_COLON2(node)->nd_head));
10280 ADD_CALL(ret, node, RNODE_COLON2(node)->nd_mid, INT2FIX(1));
10281 }
10282 if (popped) {
10283 ADD_INSN(ret, node, pop);
10284 }
10285 return COMPILE_OK;
10286}
10287
10288static int
10289compile_colon3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10290{
10291 debugi("colon3#nd_mid", RNODE_COLON3(node)->nd_mid);
10292
10293 /* add cache insn */
10294 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
10295 ISEQ_BODY(iseq)->ic_size++;
10296 VALUE segments = rb_ary_new_from_args(2, ID2SYM(idNULL), ID2SYM(RNODE_COLON3(node)->nd_mid));
10297 RB_OBJ_SET_FROZEN_SHAREABLE(segments);
10298 ADD_INSN1(ret, node, opt_getconstant_path, segments);
10299 RB_OBJ_WRITTEN(iseq, Qundef, segments);
10300 }
10301 else {
10302 ADD_INSN1(ret, node, putobject, rb_cObject);
10303 ADD_INSN1(ret, node, putobject, Qtrue);
10304 ADD_INSN1(ret, node, getconstant, ID2SYM(RNODE_COLON3(node)->nd_mid));
10305 }
10306
10307 if (popped) {
10308 ADD_INSN(ret, node, pop);
10309 }
10310 return COMPILE_OK;
10311}
10312
10313static int
10314compile_dots(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const int excl)
10315{
10316 VALUE flag = INT2FIX(excl);
10317 const NODE *b = RNODE_DOT2(node)->nd_beg;
10318 const NODE *e = RNODE_DOT2(node)->nd_end;
10319
10320 if (optimizable_range_item_p(b) && optimizable_range_item_p(e)) {
10321 if (!popped) {
10322 VALUE bv = optimized_range_item(b);
10323 VALUE ev = optimized_range_item(e);
10324 VALUE val = rb_range_new(bv, ev, excl);
10326 ADD_INSN1(ret, node, putobject, val);
10327 RB_OBJ_WRITTEN(iseq, Qundef, val);
10328 }
10329 }
10330 else {
10331 CHECK(COMPILE_(ret, "min", b, popped));
10332 CHECK(COMPILE_(ret, "max", e, popped));
10333 if (!popped) {
10334 ADD_INSN1(ret, node, newrange, flag);
10335 }
10336 }
10337 return COMPILE_OK;
10338}
10339
10340static int
10341compile_errinfo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10342{
10343 if (!popped) {
10344 if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_RESCUE) {
10345 ADD_GETLOCAL(ret, node, LVAR_ERRINFO, 0);
10346 }
10347 else {
10348 const rb_iseq_t *ip = iseq;
10349 int level = 0;
10350 while (ip) {
10351 if (ISEQ_BODY(ip)->type == ISEQ_TYPE_RESCUE) {
10352 break;
10353 }
10354 ip = ISEQ_BODY(ip)->parent_iseq;
10355 level++;
10356 }
10357 if (ip) {
10358 ADD_GETLOCAL(ret, node, LVAR_ERRINFO, level);
10359 }
10360 else {
10361 ADD_INSN(ret, node, putnil);
10362 }
10363 }
10364 }
10365 return COMPILE_OK;
10366}
10367
10368static int
10369compile_kw_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10370{
10371 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
10372 LABEL *end_label = NEW_LABEL(nd_line(node));
10373 const NODE *default_value = get_nd_value(RNODE_KW_ARG(node)->nd_body);
10374
10375 if (default_value == NODE_SPECIAL_REQUIRED_KEYWORD) {
10376 /* required argument. do nothing */
10377 COMPILE_ERROR(ERROR_ARGS "unreachable");
10378 return COMPILE_NG;
10379 }
10380 else if (nd_type_p(default_value, NODE_SYM) ||
10381 nd_type_p(default_value, NODE_REGX) ||
10382 nd_type_p(default_value, NODE_LINE) ||
10383 nd_type_p(default_value, NODE_INTEGER) ||
10384 nd_type_p(default_value, NODE_FLOAT) ||
10385 nd_type_p(default_value, NODE_RATIONAL) ||
10386 nd_type_p(default_value, NODE_IMAGINARY) ||
10387 nd_type_p(default_value, NODE_NIL) ||
10388 nd_type_p(default_value, NODE_TRUE) ||
10389 nd_type_p(default_value, NODE_FALSE)) {
10390 COMPILE_ERROR(ERROR_ARGS "unreachable");
10391 return COMPILE_NG;
10392 }
10393 else {
10394 /* if keywordcheck(_kw_bits, nth_keyword)
10395 * kw = default_value
10396 * end
10397 */
10398 int kw_bits_idx = body->local_table_size - body->param.keyword->bits_start;
10399 int keyword_idx = body->param.keyword->num;
10400
10401 ADD_INSN2(ret, node, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(keyword_idx));
10402 ADD_INSNL(ret, node, branchif, end_label);
10403 CHECK(COMPILE_POPPED(ret, "keyword default argument", RNODE_KW_ARG(node)->nd_body));
10404 ADD_LABEL(ret, end_label);
10405 }
10406 return COMPILE_OK;
10407}
10408
10409static int
10410compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10411{
10412 DECL_ANCHOR(recv);
10413 DECL_ANCHOR(args);
10414 unsigned int flag = 0;
10415 ID mid = RNODE_ATTRASGN(node)->nd_mid;
10416 VALUE argc;
10417 LABEL *else_label = NULL;
10418 VALUE branches = Qfalse;
10419
10420 INIT_ANCHOR(recv);
10421 INIT_ANCHOR(args);
10422 argc = setup_args(iseq, args, RNODE_ATTRASGN(node)->nd_args, &flag, NULL);
10423 CHECK(!NIL_P(argc));
10424
10425 int asgnflag = COMPILE_RECV(recv, "recv", node, RNODE_ATTRASGN(node)->nd_recv);
10426 CHECK(asgnflag != -1);
10427 flag |= (unsigned int)asgnflag;
10428
10429 debugp_param("argc", argc);
10430 debugp_param("nd_mid", ID2SYM(mid));
10431
10432 if (!rb_is_attrset_id(mid)) {
10433 /* safe nav attr */
10434 mid = rb_id_attrset(mid);
10435 else_label = qcall_branch_start(iseq, recv, &branches, node, node);
10436 }
10437 if (!popped) {
10438 ADD_INSN(ret, node, putnil);
10439 ADD_SEQ(ret, recv);
10440 ADD_SEQ(ret, args);
10441
10442 if (flag & VM_CALL_ARGS_SPLAT) {
10443 ADD_INSN(ret, node, dup);
10444 ADD_INSN1(ret, node, putobject, INT2FIX(-1));
10445 ADD_SEND_WITH_FLAG(ret, node, idAREF, INT2FIX(1), INT2FIX(asgnflag));
10446 ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2));
10447 ADD_INSN (ret, node, pop);
10448 }
10449 else {
10450 ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 1));
10451 }
10452 }
10453 else {
10454 ADD_SEQ(ret, recv);
10455 ADD_SEQ(ret, args);
10456 }
10457 ADD_SEND_WITH_FLAG(ret, node, mid, argc, INT2FIX(flag));
10458 qcall_branch_end(iseq, ret, else_label, branches, node, node);
10459 ADD_INSN(ret, node, pop);
10460 return COMPILE_OK;
10461}
10462
10463static int
10464compile_make_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, LINK_ANCHOR *sub, const NODE *value, bool copy)
10465{
10466 ADD_INSN1(ret, value, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10467 ADD_SEQ(ret, sub);
10468
10469 if (copy) {
10470 /*
10471 * NEW_CALL(fcore, rb_intern("make_shareable_copy"),
10472 * NEW_LIST(value, loc), loc);
10473 */
10474 ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
10475 }
10476 else {
10477 /*
10478 * NEW_CALL(fcore, rb_intern("make_shareable"),
10479 * NEW_LIST(value, loc), loc);
10480 */
10481 ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
10482 }
10483
10484 return COMPILE_OK;
10485}
10486
10487static VALUE
10488node_const_decl_val(const NODE *node)
10489{
10490 VALUE path;
10491 switch (nd_type(node)) {
10492 case NODE_CDECL:
10493 if (RNODE_CDECL(node)->nd_vid) {
10494 path = rb_id2str(RNODE_CDECL(node)->nd_vid);
10495 goto end;
10496 }
10497 else {
10498 node = RNODE_CDECL(node)->nd_else;
10499 }
10500 break;
10501 case NODE_COLON2:
10502 break;
10503 case NODE_COLON3:
10504 // ::Const
10505 path = rb_str_new_cstr("::");
10506 rb_str_append(path, rb_id2str(RNODE_COLON3(node)->nd_mid));
10507 goto end;
10508 default:
10509 rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
10511 }
10512
10513 path = rb_ary_new();
10514 if (node) {
10515 for (; node && nd_type_p(node, NODE_COLON2); node = RNODE_COLON2(node)->nd_head) {
10516 rb_ary_push(path, rb_id2str(RNODE_COLON2(node)->nd_mid));
10517 }
10518 if (node && nd_type_p(node, NODE_CONST)) {
10519 // Const::Name
10520 rb_ary_push(path, rb_id2str(RNODE_CONST(node)->nd_vid));
10521 }
10522 else if (node && nd_type_p(node, NODE_COLON3)) {
10523 // ::Const::Name
10524 rb_ary_push(path, rb_id2str(RNODE_COLON3(node)->nd_mid));
10525 rb_ary_push(path, rb_str_new(0, 0));
10526 }
10527 else {
10528 // expression::Name
10529 rb_ary_push(path, rb_str_new_cstr("..."));
10530 }
10531 path = rb_ary_join(rb_ary_reverse(path), rb_str_new_cstr("::"));
10532 }
10533 end:
10534 path = rb_fstring(path);
10535 return path;
10536}
10537
10538static VALUE
10539const_decl_path(NODE *dest)
10540{
10541 VALUE path = Qnil;
10542 if (!nd_type_p(dest, NODE_CALL)) {
10543 path = node_const_decl_val(dest);
10544 }
10545 return path;
10546}
10547
10548static int
10549compile_ensure_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *dest, const NODE *value)
10550{
10551 /*
10552 *. RubyVM::FrozenCore.ensure_shareable(value, const_decl_path(dest))
10553 */
10554 VALUE path = const_decl_path(dest);
10555 ADD_INSN1(ret, value, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
10556 CHECK(COMPILE(ret, "compile_ensure_shareable_node", value));
10557 ADD_INSN1(ret, value, putobject, path);
10558 RB_OBJ_WRITTEN(iseq, Qundef, path);
10559 ADD_SEND_WITH_FLAG(ret, value, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE));
10560
10561 return COMPILE_OK;
10562}
10563
10564#ifndef SHAREABLE_BARE_EXPRESSION
10565#define SHAREABLE_BARE_EXPRESSION 1
10566#endif
10567
10568static int
10569compile_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)
10570{
10571# define compile_shareable_literal_constant_next(node, anchor, value_p, shareable_literal_p) \
10572 compile_shareable_literal_constant(iseq, anchor, shareable, dest, node, level+1, value_p, shareable_literal_p)
10573 VALUE lit = Qnil;
10574 DECL_ANCHOR(anchor);
10575
10576 enum node_type type = node ? nd_type(node) : NODE_NIL;
10577 switch (type) {
10578 case NODE_TRUE:
10579 *value_p = Qtrue;
10580 goto compile;
10581 case NODE_FALSE:
10582 *value_p = Qfalse;
10583 goto compile;
10584 case NODE_NIL:
10585 *value_p = Qnil;
10586 goto compile;
10587 case NODE_SYM:
10588 *value_p = rb_node_sym_string_val(node);
10589 goto compile;
10590 case NODE_REGX:
10591 *value_p = rb_node_regx_string_val(node);
10592 goto compile;
10593 case NODE_LINE:
10594 *value_p = rb_node_line_lineno_val(node);
10595 goto compile;
10596 case NODE_INTEGER:
10597 *value_p = rb_node_integer_literal_val(node);
10598 goto compile;
10599 case NODE_FLOAT:
10600 *value_p = rb_node_float_literal_val(node);
10601 goto compile;
10602 case NODE_RATIONAL:
10603 *value_p = rb_node_rational_literal_val(node);
10604 goto compile;
10605 case NODE_IMAGINARY:
10606 *value_p = rb_node_imaginary_literal_val(node);
10607 goto compile;
10608 case NODE_ENCODING:
10609 *value_p = rb_node_encoding_val(node);
10610
10611 compile:
10612 CHECK(COMPILE(ret, "shareable_literal_constant", node));
10613 *shareable_literal_p = 1;
10614 return COMPILE_OK;
10615
10616 case NODE_DSTR:
10617 CHECK(COMPILE(ret, "shareable_literal_constant", node));
10618 if (shareable == rb_parser_shareable_literal) {
10619 /*
10620 * NEW_CALL(node, idUMinus, 0, loc);
10621 *
10622 * -"#{var}"
10623 */
10624 ADD_SEND_WITH_FLAG(ret, node, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE));
10625 }
10626 *value_p = Qundef;
10627 *shareable_literal_p = 1;
10628 return COMPILE_OK;
10629
10630 case NODE_STR:{
10631 VALUE lit = rb_node_str_string_val(node);
10632 ADD_INSN1(ret, node, putobject, lit);
10633 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10634 *value_p = lit;
10635 *shareable_literal_p = 1;
10636
10637 return COMPILE_OK;
10638 }
10639
10640 case NODE_FILE:{
10641 VALUE lit = rb_node_file_path_val(node);
10642 ADD_INSN1(ret, node, putobject, lit);
10643 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10644 *value_p = lit;
10645 *shareable_literal_p = 1;
10646
10647 return COMPILE_OK;
10648 }
10649
10650 case NODE_ZLIST:{
10651 VALUE lit = rb_ary_new();
10652 OBJ_FREEZE(lit);
10653 ADD_INSN1(ret, node, putobject, lit);
10654 RB_OBJ_WRITTEN(iseq, Qundef, lit);
10655 *value_p = lit;
10656 *shareable_literal_p = 1;
10657
10658 return COMPILE_OK;
10659 }
10660
10661 case NODE_LIST:{
10662 INIT_ANCHOR(anchor);
10663 lit = rb_ary_new();
10664 for (NODE *n = (NODE *)node; n; n = RNODE_LIST(n)->nd_next) {
10665 VALUE val;
10666 int shareable_literal_p2;
10667 NODE *elt = RNODE_LIST(n)->nd_head;
10668 if (elt) {
10669 CHECK(compile_shareable_literal_constant_next(elt, anchor, &val, &shareable_literal_p2));
10670 if (shareable_literal_p2) {
10671 /* noop */
10672 }
10673 else if (RTEST(lit)) {
10674 rb_ary_clear(lit);
10675 lit = Qfalse;
10676 }
10677 }
10678 if (RTEST(lit)) {
10679 if (!UNDEF_P(val)) {
10680 rb_ary_push(lit, val);
10681 }
10682 else {
10683 rb_ary_clear(lit);
10684 lit = Qnil; /* make shareable at runtime */
10685 }
10686 }
10687 }
10688 break;
10689 }
10690 case NODE_HASH:{
10691 if (!RNODE_HASH(node)->nd_brace) {
10692 *value_p = Qundef;
10693 *shareable_literal_p = 0;
10694 return COMPILE_OK;
10695 }
10696 for (NODE *n = RNODE_HASH(node)->nd_head; n; n = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_next) {
10697 if (!RNODE_LIST(n)->nd_head) {
10698 // If the hash node have a keyword splat, fall back to the default case.
10699 goto compile_shareable;
10700 }
10701 }
10702
10703 INIT_ANCHOR(anchor);
10704 lit = rb_hash_new();
10705 for (NODE *n = RNODE_HASH(node)->nd_head; n; n = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_next) {
10706 VALUE key_val = 0;
10707 VALUE value_val = 0;
10708 int shareable_literal_p2;
10709 NODE *key = RNODE_LIST(n)->nd_head;
10710 NODE *val = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head;
10711 CHECK(compile_shareable_literal_constant_next(key, anchor, &key_val, &shareable_literal_p2));
10712 if (shareable_literal_p2) {
10713 /* noop */
10714 }
10715 else if (RTEST(lit)) {
10716 rb_hash_clear(lit);
10717 lit = Qfalse;
10718 }
10719 CHECK(compile_shareable_literal_constant_next(val, anchor, &value_val, &shareable_literal_p2));
10720 if (shareable_literal_p2) {
10721 /* noop */
10722 }
10723 else if (RTEST(lit)) {
10724 rb_hash_clear(lit);
10725 lit = Qfalse;
10726 }
10727 if (RTEST(lit)) {
10728 if (!UNDEF_P(key_val) && !UNDEF_P(value_val)) {
10729 rb_hash_aset(lit, key_val, value_val);
10730 }
10731 else {
10732 rb_hash_clear(lit);
10733 lit = Qnil; /* make shareable at runtime */
10734 }
10735 }
10736 }
10737 break;
10738 }
10739
10740 default:
10741
10742 compile_shareable:
10743 if (shareable == rb_parser_shareable_literal &&
10744 (SHAREABLE_BARE_EXPRESSION || level > 0)) {
10745 CHECK(compile_ensure_shareable_node(iseq, ret, dest, node));
10746 *value_p = Qundef;
10747 *shareable_literal_p = 1;
10748 return COMPILE_OK;
10749 }
10750 CHECK(COMPILE(ret, "shareable_literal_constant", node));
10751 *value_p = Qundef;
10752 *shareable_literal_p = 0;
10753 return COMPILE_OK;
10754 }
10755
10756 /* Array or Hash that does not have keyword splat */
10757 if (!lit) {
10758 if (nd_type(node) == NODE_LIST) {
10759 ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen));
10760 }
10761 else if (nd_type(node) == NODE_HASH) {
10762 long len = RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen;
10765 ADD_INSN1(anchor, node, newhash, LONG2FIX(len));
10766 }
10767 *value_p = Qundef;
10768 *shareable_literal_p = 0;
10769 ADD_SEQ(ret, anchor);
10770 return COMPILE_OK;
10771 }
10772 if (NIL_P(lit)) {
10773 // if shareable_literal, all elements should have been ensured
10774 // as shareable
10775 if (nd_type(node) == NODE_LIST) {
10776 ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen));
10777 }
10778 else if (nd_type(node) == NODE_HASH) {
10779 long len = RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen;
10782 ADD_INSN1(anchor, node, newhash, LONG2FIX(len));
10783 }
10784 CHECK(compile_make_shareable_node(iseq, ret, anchor, node, false));
10785 *value_p = Qundef;
10786 *shareable_literal_p = 1;
10787 }
10788 else {
10790 ADD_INSN1(ret, node, putobject, val);
10791 RB_OBJ_WRITTEN(iseq, Qundef, val);
10792 *value_p = val;
10793 *shareable_literal_p = 1;
10794 }
10795
10796 return COMPILE_OK;
10797}
10798
10799static int
10800compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value)
10801{
10802 int literal_p = 0;
10803 VALUE val;
10804 DECL_ANCHOR(anchor);
10805 INIT_ANCHOR(anchor);
10806
10807 switch (shareable) {
10808 case rb_parser_shareable_none:
10809 CHECK(COMPILE(ret, "compile_shareable_constant_value", value));
10810 return COMPILE_OK;
10811
10812 case rb_parser_shareable_literal:
10813 CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p));
10814 ADD_SEQ(ret, anchor);
10815 return COMPILE_OK;
10816
10817 case rb_parser_shareable_copy:
10818 case rb_parser_shareable_everything:
10819 CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p));
10820 if (!literal_p) {
10821 CHECK(compile_make_shareable_node(iseq, ret, anchor, value, shareable == rb_parser_shareable_copy));
10822 }
10823 else {
10824 ADD_SEQ(ret, anchor);
10825 }
10826 return COMPILE_OK;
10827 default:
10828 rb_bug("unexpected rb_parser_shareability: %d", shareable);
10829 }
10830}
10831
10832static int iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped);
10840static int
10841iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *node, int popped)
10842{
10843 if (node == 0) {
10844 if (!popped) {
10845 int lineno = ISEQ_COMPILE_DATA(iseq)->last_line;
10846 if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq));
10847 debugs("node: NODE_NIL(implicit)\n");
10848 ADD_SYNTHETIC_INSN(ret, lineno, -1, putnil);
10849 }
10850 return COMPILE_OK;
10851 }
10852 return iseq_compile_each0(iseq, ret, node, popped);
10853}
10854
10855static int
10856iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
10857{
10858 const int line = (int)nd_line(node);
10859 const enum node_type type = nd_type(node);
10860 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
10861
10862 if (ISEQ_COMPILE_DATA(iseq)->last_line == line) {
10863 /* ignore */
10864 }
10865 else {
10866 if (nd_fl_newline(node)) {
10867 int event = RUBY_EVENT_LINE;
10868 ISEQ_COMPILE_DATA(iseq)->last_line = line;
10869 if (line > 0 && ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) {
10870 event |= RUBY_EVENT_COVERAGE_LINE;
10871 }
10872 ADD_TRACE(ret, event);
10873 }
10874 }
10875
10876 debug_node_start(node);
10877#undef BEFORE_RETURN
10878#define BEFORE_RETURN debug_node_end()
10879
10880 switch (type) {
10881 case NODE_BLOCK:
10882 CHECK(compile_block(iseq, ret, node, popped));
10883 break;
10884 case NODE_IF:
10885 case NODE_UNLESS:
10886 CHECK(compile_if(iseq, ret, node, popped, type));
10887 break;
10888 case NODE_CASE:
10889 CHECK(compile_case(iseq, ret, node, popped));
10890 break;
10891 case NODE_CASE2:
10892 CHECK(compile_case2(iseq, ret, node, popped));
10893 break;
10894 case NODE_CASE3:
10895 CHECK(compile_case3(iseq, ret, node, popped));
10896 break;
10897 case NODE_WHILE:
10898 case NODE_UNTIL:
10899 CHECK(compile_loop(iseq, ret, node, popped, type));
10900 break;
10901 case NODE_FOR:
10902 case NODE_ITER:
10903 CHECK(compile_iter(iseq, ret, node, popped));
10904 break;
10905 case NODE_FOR_MASGN:
10906 CHECK(compile_for_masgn(iseq, ret, node, popped));
10907 break;
10908 case NODE_BREAK:
10909 CHECK(compile_break(iseq, ret, node, popped));
10910 break;
10911 case NODE_NEXT:
10912 CHECK(compile_next(iseq, ret, node, popped));
10913 break;
10914 case NODE_REDO:
10915 CHECK(compile_redo(iseq, ret, node, popped));
10916 break;
10917 case NODE_RETRY:
10918 CHECK(compile_retry(iseq, ret, node, popped));
10919 break;
10920 case NODE_BEGIN:{
10921 CHECK(COMPILE_(ret, "NODE_BEGIN", RNODE_BEGIN(node)->nd_body, popped));
10922 break;
10923 }
10924 case NODE_RESCUE:
10925 CHECK(compile_rescue(iseq, ret, node, popped));
10926 break;
10927 case NODE_RESBODY:
10928 CHECK(compile_resbody(iseq, ret, node, popped));
10929 break;
10930 case NODE_ENSURE:
10931 CHECK(compile_ensure(iseq, ret, node, popped));
10932 break;
10933
10934 case NODE_AND:
10935 case NODE_OR:{
10936 LABEL *end_label = NEW_LABEL(line);
10937 CHECK(COMPILE(ret, "nd_1st", RNODE_OR(node)->nd_1st));
10938 if (!popped) {
10939 ADD_INSN(ret, node, dup);
10940 }
10941 if (type == NODE_AND) {
10942 ADD_INSNL(ret, node, branchunless, end_label);
10943 }
10944 else {
10945 ADD_INSNL(ret, node, branchif, end_label);
10946 }
10947 if (!popped) {
10948 ADD_INSN(ret, node, pop);
10949 }
10950 CHECK(COMPILE_(ret, "nd_2nd", RNODE_OR(node)->nd_2nd, popped));
10951 ADD_LABEL(ret, end_label);
10952 break;
10953 }
10954
10955 case NODE_MASGN:{
10956 compile_massign(iseq, ret, node, popped);
10957 break;
10958 }
10959
10960 case NODE_LASGN:{
10961 ID id = RNODE_LASGN(node)->nd_vid;
10962 int idx = ISEQ_BODY(body->local_iseq)->local_table_size - get_local_var_idx(iseq, id);
10963
10964 debugs("lvar: %s idx: %d\n", rb_id2name(id), idx);
10965 CHECK(COMPILE(ret, "rvalue", RNODE_LASGN(node)->nd_value));
10966
10967 if (!popped) {
10968 ADD_INSN(ret, node, dup);
10969 }
10970 ADD_SETLOCAL(ret, node, idx, get_lvar_level(iseq));
10971 break;
10972 }
10973 case NODE_DASGN: {
10974 int idx, lv, ls;
10975 ID id = RNODE_DASGN(node)->nd_vid;
10976 CHECK(COMPILE(ret, "dvalue", RNODE_DASGN(node)->nd_value));
10977 debugi("dassn id", rb_id2str(id) ? id : '*');
10978
10979 if (!popped) {
10980 ADD_INSN(ret, node, dup);
10981 }
10982
10983 idx = get_dyna_var_idx(iseq, id, &lv, &ls);
10984
10985 if (idx < 0) {
10986 COMPILE_ERROR(ERROR_ARGS "NODE_DASGN: unknown id (%"PRIsVALUE")",
10987 rb_id2str(id));
10988 goto ng;
10989 }
10990 ADD_SETLOCAL(ret, node, ls - idx, lv);
10991 break;
10992 }
10993 case NODE_GASGN:{
10994 CHECK(COMPILE(ret, "lvalue", RNODE_GASGN(node)->nd_value));
10995
10996 if (!popped) {
10997 ADD_INSN(ret, node, dup);
10998 }
10999 ADD_INSN1(ret, node, setglobal, ID2SYM(RNODE_GASGN(node)->nd_vid));
11000 break;
11001 }
11002 case NODE_IASGN:{
11003 CHECK(COMPILE(ret, "lvalue", RNODE_IASGN(node)->nd_value));
11004 if (!popped) {
11005 ADD_INSN(ret, node, dup);
11006 }
11007 ADD_INSN2(ret, node, setinstancevariable,
11008 ID2SYM(RNODE_IASGN(node)->nd_vid),
11009 get_ivar_ic_value(iseq,RNODE_IASGN(node)->nd_vid));
11010 break;
11011 }
11012 case NODE_CDECL:{
11013 if (RNODE_CDECL(node)->nd_vid) {
11014 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value));
11015
11016 if (!popped) {
11017 ADD_INSN(ret, node, dup);
11018 }
11019
11020 ADD_INSN1(ret, node, putspecialobject,
11021 INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
11022 ADD_INSN1(ret, node, setconstant, ID2SYM(RNODE_CDECL(node)->nd_vid));
11023 }
11024 else {
11025 compile_cpath(ret, iseq, RNODE_CDECL(node)->nd_else);
11026 CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value));
11027 ADD_INSN(ret, node, swap);
11028
11029 if (!popped) {
11030 ADD_INSN1(ret, node, topn, INT2FIX(1));
11031 ADD_INSN(ret, node, swap);
11032 }
11033
11034 ADD_INSN1(ret, node, setconstant, ID2SYM(get_node_colon_nd_mid(RNODE_CDECL(node)->nd_else)));
11035 }
11036 break;
11037 }
11038 case NODE_CVASGN:{
11039 CHECK(COMPILE(ret, "cvasgn val", RNODE_CVASGN(node)->nd_value));
11040 if (!popped) {
11041 ADD_INSN(ret, node, dup);
11042 }
11043 ADD_INSN2(ret, node, setclassvariable,
11044 ID2SYM(RNODE_CVASGN(node)->nd_vid),
11045 get_cvar_ic_value(iseq, RNODE_CVASGN(node)->nd_vid));
11046 break;
11047 }
11048 case NODE_OP_ASGN1:
11049 CHECK(compile_op_asgn1(iseq, ret, node, popped));
11050 break;
11051 case NODE_OP_ASGN2:
11052 CHECK(compile_op_asgn2(iseq, ret, node, popped));
11053 break;
11054 case NODE_OP_CDECL:
11055 CHECK(compile_op_cdecl(iseq, ret, node, popped));
11056 break;
11057 case NODE_OP_ASGN_AND:
11058 case NODE_OP_ASGN_OR:
11059 CHECK(compile_op_log(iseq, ret, node, popped, type));
11060 break;
11061 case NODE_CALL: /* obj.foo */
11062 case NODE_OPCALL: /* foo[] */
11063 if (compile_call_precheck_freeze(iseq, ret, node, node, popped) == TRUE) {
11064 break;
11065 }
11066 case NODE_QCALL: /* obj&.foo */
11067 case NODE_FCALL: /* foo() */
11068 case NODE_VCALL: /* foo (variable or call) */
11069 if (compile_call(iseq, ret, node, type, node, popped, false) == COMPILE_NG) {
11070 goto ng;
11071 }
11072 break;
11073 case NODE_SUPER:
11074 case NODE_ZSUPER:
11075 CHECK(compile_super(iseq, ret, node, popped, type));
11076 break;
11077 case NODE_LIST:{
11078 CHECK(compile_array(iseq, ret, node, popped, TRUE) >= 0);
11079 break;
11080 }
11081 case NODE_ZLIST:{
11082 if (!popped) {
11083 ADD_INSN1(ret, node, newarray, INT2FIX(0));
11084 }
11085 break;
11086 }
11087 case NODE_HASH:
11088 CHECK(compile_hash(iseq, ret, node, FALSE, popped) >= 0);
11089 break;
11090 case NODE_RETURN:
11091 CHECK(compile_return(iseq, ret, node, popped));
11092 break;
11093 case NODE_YIELD:
11094 CHECK(compile_yield(iseq, ret, node, popped));
11095 break;
11096 case NODE_LVAR:{
11097 if (!popped) {
11098 compile_lvar(iseq, ret, node, RNODE_LVAR(node)->nd_vid);
11099 }
11100 break;
11101 }
11102 case NODE_DVAR:{
11103 int lv, idx, ls;
11104 debugi("nd_vid", RNODE_DVAR(node)->nd_vid);
11105 if (!popped) {
11106 idx = get_dyna_var_idx(iseq, RNODE_DVAR(node)->nd_vid, &lv, &ls);
11107 if (idx < 0) {
11108 COMPILE_ERROR(ERROR_ARGS "unknown dvar (%"PRIsVALUE")",
11109 rb_id2str(RNODE_DVAR(node)->nd_vid));
11110 goto ng;
11111 }
11112 ADD_GETLOCAL(ret, node, ls - idx, lv);
11113 }
11114 break;
11115 }
11116 case NODE_GVAR:{
11117 ADD_INSN1(ret, node, getglobal, ID2SYM(RNODE_GVAR(node)->nd_vid));
11118 if (popped) {
11119 ADD_INSN(ret, node, pop);
11120 }
11121 break;
11122 }
11123 case NODE_IVAR:{
11124 debugi("nd_vid", RNODE_IVAR(node)->nd_vid);
11125 if (!popped) {
11126 ADD_INSN2(ret, node, getinstancevariable,
11127 ID2SYM(RNODE_IVAR(node)->nd_vid),
11128 get_ivar_ic_value(iseq, RNODE_IVAR(node)->nd_vid));
11129 }
11130 break;
11131 }
11132 case NODE_CONST:{
11133 debugi("nd_vid", RNODE_CONST(node)->nd_vid);
11134
11135 if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
11136 body->ic_size++;
11137 VALUE segments = rb_ary_new_from_args(1, ID2SYM(RNODE_CONST(node)->nd_vid));
11138 RB_OBJ_SET_FROZEN_SHAREABLE(segments);
11139 ADD_INSN1(ret, node, opt_getconstant_path, segments);
11140 RB_OBJ_WRITTEN(iseq, Qundef, segments);
11141 }
11142 else {
11143 ADD_INSN(ret, node, putnil);
11144 ADD_INSN1(ret, node, putobject, Qtrue);
11145 ADD_INSN1(ret, node, getconstant, ID2SYM(RNODE_CONST(node)->nd_vid));
11146 }
11147
11148 if (popped) {
11149 ADD_INSN(ret, node, pop);
11150 }
11151 break;
11152 }
11153 case NODE_CVAR:{
11154 if (!popped) {
11155 ADD_INSN2(ret, node, getclassvariable,
11156 ID2SYM(RNODE_CVAR(node)->nd_vid),
11157 get_cvar_ic_value(iseq, RNODE_CVAR(node)->nd_vid));
11158 }
11159 break;
11160 }
11161 case NODE_NTH_REF:{
11162 if (!popped) {
11163 if (!RNODE_NTH_REF(node)->nd_nth) {
11164 ADD_INSN(ret, node, putnil);
11165 break;
11166 }
11167 ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
11168 INT2FIX(RNODE_NTH_REF(node)->nd_nth << 1));
11169 }
11170 break;
11171 }
11172 case NODE_BACK_REF:{
11173 if (!popped) {
11174 ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */,
11175 INT2FIX(0x01 | (RNODE_BACK_REF(node)->nd_nth << 1)));
11176 }
11177 break;
11178 }
11179 case NODE_MATCH:
11180 case NODE_MATCH2:
11181 case NODE_MATCH3:
11182 CHECK(compile_match(iseq, ret, node, popped, type));
11183 break;
11184 case NODE_SYM:{
11185 if (!popped) {
11186 ADD_INSN1(ret, node, putobject, rb_node_sym_string_val(node));
11187 }
11188 break;
11189 }
11190 case NODE_LINE:{
11191 if (!popped) {
11192 ADD_INSN1(ret, node, putobject, rb_node_line_lineno_val(node));
11193 }
11194 break;
11195 }
11196 case NODE_ENCODING:{
11197 if (!popped) {
11198 ADD_INSN1(ret, node, putobject, rb_node_encoding_val(node));
11199 }
11200 break;
11201 }
11202 case NODE_INTEGER:{
11203 VALUE lit = rb_node_integer_literal_val(node);
11204 if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit);
11205 debugp_param("integer", lit);
11206 if (!popped) {
11207 ADD_INSN1(ret, node, putobject, lit);
11208 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11209 }
11210 break;
11211 }
11212 case NODE_FLOAT:{
11213 VALUE lit = rb_node_float_literal_val(node);
11214 if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit);
11215 debugp_param("float", lit);
11216 if (!popped) {
11217 ADD_INSN1(ret, node, putobject, lit);
11218 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11219 }
11220 break;
11221 }
11222 case NODE_RATIONAL:{
11223 VALUE lit = rb_node_rational_literal_val(node);
11225 debugp_param("rational", lit);
11226 if (!popped) {
11227 ADD_INSN1(ret, node, putobject, lit);
11228 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11229 }
11230 break;
11231 }
11232 case NODE_IMAGINARY:{
11233 VALUE lit = rb_node_imaginary_literal_val(node);
11235 debugp_param("imaginary", lit);
11236 if (!popped) {
11237 ADD_INSN1(ret, node, putobject, lit);
11238 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11239 }
11240 break;
11241 }
11242 case NODE_FILE:
11243 case NODE_STR:{
11244 debugp_param("nd_lit", get_string_value(node));
11245 if (!popped) {
11246 VALUE lit = get_string_value(node);
11247 const rb_compile_option_t *option = ISEQ_COMPILE_DATA(iseq)->option;
11248 if ((option->debug_frozen_string_literal || RTEST(ruby_debug)) &&
11249 option->frozen_string_literal != ISEQ_FROZEN_STRING_LITERAL_DISABLED) {
11250 lit = rb_str_with_debug_created_info(lit, rb_iseq_path(iseq), line);
11251 RB_OBJ_SET_SHAREABLE(lit);
11252 }
11253 switch (option->frozen_string_literal) {
11254 case ISEQ_FROZEN_STRING_LITERAL_UNSET:
11255 ADD_INSN1(ret, node, dupchilledstring, lit);
11256 break;
11257 case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
11258 ADD_INSN1(ret, node, dupstring, lit);
11259 break;
11260 case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
11261 ADD_INSN1(ret, node, putobject, lit);
11262 break;
11263 default:
11264 rb_bug("invalid frozen_string_literal");
11265 }
11266 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11267 }
11268 break;
11269 }
11270 case NODE_DSTR:{
11271 compile_dstr(iseq, ret, node);
11272
11273 if (popped) {
11274 ADD_INSN(ret, node, pop);
11275 }
11276 break;
11277 }
11278 case NODE_XSTR:{
11279 ADD_CALL_RECEIVER(ret, node);
11280 VALUE str = rb_node_str_string_val(node);
11281 ADD_INSN1(ret, node, putobject, str);
11282 RB_OBJ_WRITTEN(iseq, Qundef, str);
11283 ADD_CALL(ret, node, idBackquote, INT2FIX(1));
11284
11285 if (popped) {
11286 ADD_INSN(ret, node, pop);
11287 }
11288 break;
11289 }
11290 case NODE_DXSTR:{
11291 ADD_CALL_RECEIVER(ret, node);
11292 compile_dstr(iseq, ret, node);
11293 ADD_CALL(ret, node, idBackquote, INT2FIX(1));
11294
11295 if (popped) {
11296 ADD_INSN(ret, node, pop);
11297 }
11298 break;
11299 }
11300 case NODE_EVSTR:
11301 CHECK(compile_evstr(iseq, ret, RNODE_EVSTR(node)->nd_body, popped));
11302 break;
11303 case NODE_REGX:{
11304 if (!popped) {
11305 VALUE lit = rb_node_regx_string_val(node);
11306 RB_OBJ_SET_SHAREABLE(lit);
11307 ADD_INSN1(ret, node, putobject, lit);
11308 RB_OBJ_WRITTEN(iseq, Qundef, lit);
11309 }
11310 break;
11311 }
11312 case NODE_DREGX:
11313 compile_dregx(iseq, ret, node, popped);
11314 break;
11315 case NODE_ONCE:{
11316 int ic_index = body->ise_size++;
11317 const rb_iseq_t *block_iseq;
11318 block_iseq = NEW_CHILD_ISEQ(RNODE_ONCE(node)->nd_body, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, line);
11319
11320 ADD_INSN2(ret, node, once, block_iseq, INT2FIX(ic_index));
11321 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block_iseq);
11322
11323 if (popped) {
11324 ADD_INSN(ret, node, pop);
11325 }
11326 break;
11327 }
11328 case NODE_ARGSCAT:{
11329 if (popped) {
11330 CHECK(COMPILE(ret, "argscat head", RNODE_ARGSCAT(node)->nd_head));
11331 ADD_INSN1(ret, node, splatarray, Qfalse);
11332 ADD_INSN(ret, node, pop);
11333 CHECK(COMPILE(ret, "argscat body", RNODE_ARGSCAT(node)->nd_body));
11334 ADD_INSN1(ret, node, splatarray, Qfalse);
11335 ADD_INSN(ret, node, pop);
11336 }
11337 else {
11338 CHECK(COMPILE(ret, "argscat head", RNODE_ARGSCAT(node)->nd_head));
11339 const NODE *body_node = RNODE_ARGSCAT(node)->nd_body;
11340 if (nd_type_p(body_node, NODE_LIST)) {
11341 CHECK(compile_array(iseq, ret, body_node, popped, FALSE) >= 0);
11342 }
11343 else {
11344 CHECK(COMPILE(ret, "argscat body", body_node));
11345 ADD_INSN(ret, node, concattoarray);
11346 }
11347 }
11348 break;
11349 }
11350 case NODE_ARGSPUSH:{
11351 if (popped) {
11352 CHECK(COMPILE(ret, "argspush head", RNODE_ARGSPUSH(node)->nd_head));
11353 ADD_INSN1(ret, node, splatarray, Qfalse);
11354 ADD_INSN(ret, node, pop);
11355 CHECK(COMPILE_(ret, "argspush body", RNODE_ARGSPUSH(node)->nd_body, popped));
11356 }
11357 else {
11358 CHECK(COMPILE(ret, "argspush head", RNODE_ARGSPUSH(node)->nd_head));
11359 const NODE *body_node = RNODE_ARGSPUSH(node)->nd_body;
11360 if (keyword_node_p(body_node)) {
11361 CHECK(COMPILE_(ret, "array element", body_node, FALSE));
11362 ADD_INSN(ret, node, pushtoarraykwsplat);
11363 }
11364 else if (static_literal_node_p(body_node, iseq, false)) {
11365 ADD_INSN1(ret, body_node, putobject, static_literal_value(body_node, iseq));
11366 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
11367 }
11368 else {
11369 CHECK(COMPILE_(ret, "array element", body_node, FALSE));
11370 ADD_INSN1(ret, node, pushtoarray, INT2FIX(1));
11371 }
11372 }
11373 break;
11374 }
11375 case NODE_SPLAT:{
11376 CHECK(COMPILE(ret, "splat", RNODE_SPLAT(node)->nd_head));
11377 ADD_INSN1(ret, node, splatarray, Qtrue);
11378
11379 if (popped) {
11380 ADD_INSN(ret, node, pop);
11381 }
11382 break;
11383 }
11384 case NODE_DEFN:{
11385 ID mid = RNODE_DEFN(node)->nd_mid;
11386 const rb_iseq_t *method_iseq = NEW_ISEQ(RNODE_DEFN(node)->nd_defn,
11387 rb_id2str(mid),
11388 ISEQ_TYPE_METHOD, line);
11389
11390 debugp_param("defn/iseq", rb_iseqw_new(method_iseq));
11391 ADD_INSN2(ret, node, definemethod, ID2SYM(mid), method_iseq);
11392 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)method_iseq);
11393
11394 if (!popped) {
11395 ADD_INSN1(ret, node, putobject, ID2SYM(mid));
11396 }
11397
11398 break;
11399 }
11400 case NODE_DEFS:{
11401 ID mid = RNODE_DEFS(node)->nd_mid;
11402 const rb_iseq_t * singleton_method_iseq = NEW_ISEQ(RNODE_DEFS(node)->nd_defn,
11403 rb_id2str(mid),
11404 ISEQ_TYPE_METHOD, line);
11405
11406 debugp_param("defs/iseq", rb_iseqw_new(singleton_method_iseq));
11407 CHECK(COMPILE(ret, "defs: recv", RNODE_DEFS(node)->nd_recv));
11408 ADD_INSN2(ret, node, definesmethod, ID2SYM(mid), singleton_method_iseq);
11409 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_method_iseq);
11410
11411 if (!popped) {
11412 ADD_INSN1(ret, node, putobject, ID2SYM(mid));
11413 }
11414 break;
11415 }
11416 case NODE_ALIAS:{
11417 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
11418 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
11419 CHECK(COMPILE(ret, "alias arg1", RNODE_ALIAS(node)->nd_1st));
11420 CHECK(COMPILE(ret, "alias arg2", RNODE_ALIAS(node)->nd_2nd));
11421 ADD_SEND(ret, node, id_core_set_method_alias, INT2FIX(3));
11422
11423 if (popped) {
11424 ADD_INSN(ret, node, pop);
11425 }
11426 break;
11427 }
11428 case NODE_VALIAS:{
11429 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
11430 ADD_INSN1(ret, node, putobject, ID2SYM(RNODE_VALIAS(node)->nd_alias));
11431 ADD_INSN1(ret, node, putobject, ID2SYM(RNODE_VALIAS(node)->nd_orig));
11432 ADD_SEND(ret, node, id_core_set_variable_alias, INT2FIX(2));
11433
11434 if (popped) {
11435 ADD_INSN(ret, node, pop);
11436 }
11437 break;
11438 }
11439 case NODE_UNDEF:{
11440 const rb_parser_ary_t *ary = RNODE_UNDEF(node)->nd_undefs;
11441
11442 for (long i = 0; i < ary->len; i++) {
11443 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
11444 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
11445 CHECK(COMPILE(ret, "undef arg", ary->data[i]));
11446 ADD_SEND(ret, node, id_core_undef_method, INT2FIX(2));
11447
11448 if (i < ary->len - 1) {
11449 ADD_INSN(ret, node, pop);
11450 }
11451 }
11452
11453 if (popped) {
11454 ADD_INSN(ret, node, pop);
11455 }
11456 break;
11457 }
11458 case NODE_CLASS:{
11459 const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(RNODE_CLASS(node)->nd_body,
11460 rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(get_node_colon_nd_mid(RNODE_CLASS(node)->nd_cpath)))),
11461 ISEQ_TYPE_CLASS, line);
11462 const int flags = VM_DEFINECLASS_TYPE_CLASS |
11463 (RNODE_CLASS(node)->nd_super ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
11464 compile_cpath(ret, iseq, RNODE_CLASS(node)->nd_cpath);
11465
11466 CHECK(COMPILE(ret, "super", RNODE_CLASS(node)->nd_super));
11467 ADD_INSN3(ret, node, defineclass, ID2SYM(get_node_colon_nd_mid(RNODE_CLASS(node)->nd_cpath)), class_iseq, INT2FIX(flags));
11468 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq);
11469
11470 if (popped) {
11471 ADD_INSN(ret, node, pop);
11472 }
11473 break;
11474 }
11475 case NODE_MODULE:{
11476 const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(RNODE_MODULE(node)->nd_body,
11477 rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(get_node_colon_nd_mid(RNODE_MODULE(node)->nd_cpath)))),
11478 ISEQ_TYPE_CLASS, line);
11479 const int flags = VM_DEFINECLASS_TYPE_MODULE |
11480 compile_cpath(ret, iseq, RNODE_MODULE(node)->nd_cpath);
11481
11482 ADD_INSN (ret, node, putnil); /* dummy */
11483 ADD_INSN3(ret, node, defineclass, ID2SYM(get_node_colon_nd_mid(RNODE_MODULE(node)->nd_cpath)), module_iseq, INT2FIX(flags));
11484 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)module_iseq);
11485
11486 if (popped) {
11487 ADD_INSN(ret, node, pop);
11488 }
11489 break;
11490 }
11491 case NODE_SCLASS:{
11492 ID singletonclass;
11493 const rb_iseq_t *singleton_class = NEW_ISEQ(RNODE_SCLASS(node)->nd_body, rb_fstring_lit("singleton class"),
11494 ISEQ_TYPE_CLASS, line);
11495
11496 CHECK(COMPILE(ret, "sclass#recv", RNODE_SCLASS(node)->nd_recv));
11497 ADD_INSN (ret, node, putnil);
11498 CONST_ID(singletonclass, "singletonclass");
11499
11500 /* `class << self` in a class body and `class << Foo` (constant
11501 receiver) are stable. All other forms are potentially dynamic. */
11502 int sclass_flags = VM_DEFINECLASS_TYPE_SINGLETON_CLASS;
11503 const NODE *recv = RNODE_SCLASS(node)->nd_recv;
11504 if (!(nd_type_p(recv, NODE_SELF) &&
11505 ISEQ_BODY(iseq)->type == ISEQ_TYPE_CLASS) &&
11506 !cpath_const_p(recv)) {
11507 sclass_flags |= VM_DEFINECLASS_FLAG_DYNAMIC_CREF;
11508 }
11509
11510 ADD_INSN3(ret, node, defineclass,
11511 ID2SYM(singletonclass), singleton_class,
11512 INT2FIX(sclass_flags));
11513 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_class);
11514
11515 if (popped) {
11516 ADD_INSN(ret, node, pop);
11517 }
11518 break;
11519 }
11520 case NODE_COLON2:
11521 CHECK(compile_colon2(iseq, ret, node, popped));
11522 break;
11523 case NODE_COLON3:
11524 CHECK(compile_colon3(iseq, ret, node, popped));
11525 break;
11526 case NODE_DOT2:
11527 CHECK(compile_dots(iseq, ret, node, popped, FALSE));
11528 break;
11529 case NODE_DOT3:
11530 CHECK(compile_dots(iseq, ret, node, popped, TRUE));
11531 break;
11532 case NODE_FLIP2:
11533 case NODE_FLIP3:{
11534 LABEL *lend = NEW_LABEL(line);
11535 LABEL *ltrue = NEW_LABEL(line);
11536 LABEL *lfalse = NEW_LABEL(line);
11537 CHECK(compile_flip_flop(iseq, ret, node, type == NODE_FLIP2,
11538 ltrue, lfalse));
11539 ADD_LABEL(ret, ltrue);
11540 ADD_INSN1(ret, node, putobject, Qtrue);
11541 ADD_INSNL(ret, node, jump, lend);
11542 ADD_LABEL(ret, lfalse);
11543 ADD_INSN1(ret, node, putobject, Qfalse);
11544 ADD_LABEL(ret, lend);
11545 break;
11546 }
11547 case NODE_SELF:{
11548 if (!popped) {
11549 ADD_INSN(ret, node, putself);
11550 }
11551 break;
11552 }
11553 case NODE_NIL:{
11554 if (!popped) {
11555 ADD_INSN(ret, node, putnil);
11556 }
11557 break;
11558 }
11559 case NODE_TRUE:{
11560 if (!popped) {
11561 ADD_INSN1(ret, node, putobject, Qtrue);
11562 }
11563 break;
11564 }
11565 case NODE_FALSE:{
11566 if (!popped) {
11567 ADD_INSN1(ret, node, putobject, Qfalse);
11568 }
11569 break;
11570 }
11571 case NODE_ERRINFO:
11572 CHECK(compile_errinfo(iseq, ret, node, popped));
11573 break;
11574 case NODE_DEFINED:
11575 if (!popped) {
11576 CHECK(compile_defined_expr(iseq, ret, node, Qtrue, false));
11577 }
11578 break;
11579 case NODE_POSTEXE:{
11580 /* compiled to:
11581 * ONCE{ rb_mRubyVMFrozenCore::core#set_postexe{ ... } }
11582 */
11583 int is_index = body->ise_size++;
11585 rb_iseq_new_with_callback_new_callback(build_postexe_iseq, RNODE_POSTEXE(node)->nd_body);
11586 const rb_iseq_t *once_iseq =
11587 NEW_CHILD_ISEQ_WITH_CALLBACK(ifunc, rb_fstring(make_name_for_block(iseq)), ISEQ_TYPE_BLOCK, line);
11588
11589 ADD_INSN2(ret, node, once, once_iseq, INT2FIX(is_index));
11590 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)once_iseq);
11591
11592 if (popped) {
11593 ADD_INSN(ret, node, pop);
11594 }
11595 break;
11596 }
11597 case NODE_KW_ARG:
11598 CHECK(compile_kw_arg(iseq, ret, node, popped));
11599 break;
11600 case NODE_DSYM:{
11601 compile_dstr(iseq, ret, node);
11602 if (!popped) {
11603 ADD_INSN(ret, node, intern);
11604 }
11605 else {
11606 ADD_INSN(ret, node, pop);
11607 }
11608 break;
11609 }
11610 case NODE_ATTRASGN:
11611 CHECK(compile_attrasgn(iseq, ret, node, popped));
11612 break;
11613 case NODE_LAMBDA:{
11614 /* compile same as lambda{...} */
11615 const rb_iseq_t *block = NEW_CHILD_ISEQ(RNODE_LAMBDA(node)->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line);
11616 VALUE argc = INT2FIX(0);
11617
11618 ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
11619 ADD_CALL_WITH_BLOCK(ret, node, idLambda, argc, block);
11620 RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block);
11621
11622 if (popped) {
11623 ADD_INSN(ret, node, pop);
11624 }
11625 break;
11626 }
11627 default:
11628 UNKNOWN_NODE("iseq_compile_each", node, COMPILE_NG);
11629 ng:
11630 debug_node_end();
11631 return COMPILE_NG;
11632 }
11633
11634 debug_node_end();
11635 return COMPILE_OK;
11636}
11637
11638/***************************/
11639/* instruction information */
11640/***************************/
11641
11642static int
11643insn_data_length(INSN *iobj)
11644{
11645 return insn_len(iobj->insn_id);
11646}
11647
11648static int
11649calc_sp_depth(int depth, INSN *insn)
11650{
11651 return comptime_insn_stack_increase(depth, insn->insn_id, insn->operands);
11652}
11653
11654static VALUE
11655opobj_inspect(VALUE obj)
11656{
11657 if (!SPECIAL_CONST_P(obj) && !RBASIC_CLASS(obj)) {
11658 switch (BUILTIN_TYPE(obj)) {
11659 case T_STRING:
11660 obj = rb_str_new_cstr(RSTRING_PTR(obj));
11661 break;
11662 case T_ARRAY:
11663 obj = rb_ary_dup(obj);
11664 break;
11665 default:
11666 break;
11667 }
11668 }
11669 return rb_inspect(obj);
11670}
11671
11672
11673
11674static VALUE
11675insn_data_to_s_detail(INSN *iobj)
11676{
11677 VALUE str = rb_sprintf("%-20s ", insn_name(iobj->insn_id));
11678
11679 if (iobj->operands) {
11680 const char *types = insn_op_types(iobj->insn_id);
11681 int j;
11682
11683 for (j = 0; types[j]; j++) {
11684 char type = types[j];
11685
11686 switch (type) {
11687 case TS_OFFSET: /* label(destination position) */
11688 {
11689 LABEL *lobj = (LABEL *)OPERAND_AT(iobj, j);
11690 rb_str_catf(str, LABEL_FORMAT, lobj->label_no);
11691 break;
11692 }
11693 break;
11694 case TS_ISEQ: /* iseq */
11695 {
11696 rb_iseq_t *iseq = (rb_iseq_t *)OPERAND_AT(iobj, j);
11697 VALUE val = Qnil;
11698 if (0 && iseq) { /* TODO: invalidate now */
11699 val = (VALUE)iseq;
11700 }
11701 rb_str_concat(str, opobj_inspect(val));
11702 }
11703 break;
11704 case TS_LINDEX:
11705 case TS_NUM: /* ulong */
11706 case TS_VALUE: /* VALUE */
11707 {
11708 VALUE v = OPERAND_AT(iobj, j);
11709 if (!CLASS_OF(v))
11710 rb_str_cat2(str, "<hidden>");
11711 else {
11712 rb_str_concat(str, opobj_inspect(v));
11713 }
11714 break;
11715 }
11716 case TS_ID: /* ID */
11717 rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j)));
11718 break;
11719 case TS_IC: /* inline cache */
11720 rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j)));
11721 break;
11722 case TS_IVC: /* inline ivar cache */
11723 rb_str_catf(str, "<ivc:%d>", FIX2INT(OPERAND_AT(iobj, j)));
11724 break;
11725 case TS_ICVARC: /* inline cvar cache */
11726 rb_str_catf(str, "<icvarc:%d>", FIX2INT(OPERAND_AT(iobj, j)));
11727 break;
11728 case TS_ISE: /* inline storage entry */
11729 rb_str_catf(str, "<ise:%d>", FIX2INT(OPERAND_AT(iobj, j)));
11730 break;
11731 case TS_CALLDATA: /* we store these as call infos at compile time */
11732 {
11733 const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, j);
11734 rb_str_cat2(str, "<calldata:");
11735 if (vm_ci_mid(ci)) rb_str_catf(str, "%"PRIsVALUE, rb_id2str(vm_ci_mid(ci)));
11736 rb_str_catf(str, ", %d>", vm_ci_argc(ci));
11737 break;
11738 }
11739 case TS_CDHASH: /* case/when condition cache */
11740 rb_str_cat2(str, "<ch>");
11741 break;
11742 case TS_FUNCPTR:
11743 {
11744 void *func = (void *)OPERAND_AT(iobj, j);
11745#ifdef HAVE_DLADDR
11746 Dl_info info;
11747 if (dladdr(func, &info) && info.dli_sname) {
11748 rb_str_cat2(str, info.dli_sname);
11749 break;
11750 }
11751#endif
11752 rb_str_catf(str, "<%p>", func);
11753 }
11754 break;
11755 case TS_BUILTIN:
11756 rb_str_cat2(str, "<TS_BUILTIN>");
11757 break;
11758 default:{
11759 rb_raise(rb_eSyntaxError, "unknown operand type: %c", type);
11760 }
11761 }
11762 if (types[j + 1]) {
11763 rb_str_cat2(str, ", ");
11764 }
11765 }
11766 }
11767 return str;
11768}
11769
11770static void
11771dump_disasm_list(const LINK_ELEMENT *link)
11772{
11773 dump_disasm_list_with_cursor(link, NULL, NULL);
11774}
11775
11776static void
11777dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, const LABEL *dest)
11778{
11779 int pos = 0;
11780 INSN *iobj;
11781 LABEL *lobj;
11782 VALUE str;
11783
11784 printf("-- raw disasm--------\n");
11785
11786 while (link) {
11787 if (curr) printf(curr == link ? "*" : " ");
11788 switch (link->type) {
11789 case ISEQ_ELEMENT_INSN:
11790 {
11791 iobj = (INSN *)link;
11792 str = insn_data_to_s_detail(iobj);
11793 printf(" %04d %-65s(%4u)\n", pos, StringValueCStr(str), iobj->insn_info.line_no);
11794 pos += insn_data_length(iobj);
11795 break;
11796 }
11797 case ISEQ_ELEMENT_LABEL:
11798 {
11799 lobj = (LABEL *)link;
11800 printf(LABEL_FORMAT" [sp: %d, unremovable: %d, refcnt: %d]%s\n", lobj->label_no, lobj->sp, lobj->unremovable, lobj->refcnt,
11801 dest == lobj ? " <---" : "");
11802 break;
11803 }
11804 case ISEQ_ELEMENT_TRACE:
11805 {
11806 TRACE *trace = (TRACE *)link;
11807 printf(" trace: %0x\n", trace->event);
11808 break;
11809 }
11810 case ISEQ_ELEMENT_ADJUST:
11811 {
11812 ADJUST *adjust = (ADJUST *)link;
11813 printf(" adjust: [label: %d]\n", adjust->label ? adjust->label->label_no : -1);
11814 break;
11815 }
11816 default:
11817 /* ignore */
11818 rb_raise(rb_eSyntaxError, "dump_disasm_list error: %d\n", (int)link->type);
11819 }
11820 link = link->next;
11821 }
11822 printf("---------------------\n");
11823 fflush(stdout);
11824}
11825
11826int
11827rb_insn_len(VALUE insn)
11828{
11829 return insn_len(insn);
11830}
11831
11832const char *
11833rb_insns_name(int i)
11834{
11835 return insn_name(i);
11836}
11837
11838VALUE
11839rb_insns_name_array(void)
11840{
11841 VALUE ary = rb_ary_new_capa(VM_INSTRUCTION_SIZE);
11842 int i;
11843 for (i = 0; i < VM_INSTRUCTION_SIZE; i++) {
11844 rb_ary_push(ary, rb_fstring_cstr(insn_name(i)));
11845 }
11846 return rb_ary_freeze(ary);
11847}
11848
11849static LABEL *
11850register_label(rb_iseq_t *iseq, struct st_table *labels_table, VALUE obj)
11851{
11852 LABEL *label = 0;
11853 st_data_t tmp;
11854 obj = rb_to_symbol_type(obj);
11855
11856 if (st_lookup(labels_table, obj, &tmp) == 0) {
11857 label = NEW_LABEL(0);
11858 st_insert(labels_table, obj, (st_data_t)label);
11859 }
11860 else {
11861 label = (LABEL *)tmp;
11862 }
11863 LABEL_REF(label);
11864 return label;
11865}
11866
11867static VALUE
11868get_exception_sym2type(VALUE sym)
11869{
11870 static VALUE symRescue, symEnsure, symRetry;
11871 static VALUE symBreak, symRedo, symNext;
11872
11873 if (symRescue == 0) {
11874 symRescue = ID2SYM(rb_intern_const("rescue"));
11875 symEnsure = ID2SYM(rb_intern_const("ensure"));
11876 symRetry = ID2SYM(rb_intern_const("retry"));
11877 symBreak = ID2SYM(rb_intern_const("break"));
11878 symRedo = ID2SYM(rb_intern_const("redo"));
11879 symNext = ID2SYM(rb_intern_const("next"));
11880 }
11881
11882 if (sym == symRescue) return CATCH_TYPE_RESCUE;
11883 if (sym == symEnsure) return CATCH_TYPE_ENSURE;
11884 if (sym == symRetry) return CATCH_TYPE_RETRY;
11885 if (sym == symBreak) return CATCH_TYPE_BREAK;
11886 if (sym == symRedo) return CATCH_TYPE_REDO;
11887 if (sym == symNext) return CATCH_TYPE_NEXT;
11888 rb_raise(rb_eSyntaxError, "invalid exception symbol: %+"PRIsVALUE, sym);
11889 return 0;
11890}
11891
11892static int
11893iseq_build_from_ary_exception(rb_iseq_t *iseq, struct st_table *labels_table,
11894 VALUE exception)
11895{
11896 int i;
11897
11898 for (i=0; i<RARRAY_LEN(exception); i++) {
11899 const rb_iseq_t *eiseq;
11900 VALUE v, type;
11901 LABEL *lstart, *lend, *lcont;
11902 unsigned int sp;
11903
11904 v = rb_to_array_type(RARRAY_AREF(exception, i));
11905 if (RARRAY_LEN(v) != 6) {
11906 rb_raise(rb_eSyntaxError, "wrong exception entry");
11907 }
11908 type = get_exception_sym2type(RARRAY_AREF(v, 0));
11909 if (NIL_P(RARRAY_AREF(v, 1))) {
11910 eiseq = NULL;
11911 }
11912 else {
11913 eiseq = rb_iseqw_to_iseq(rb_iseq_load(RARRAY_AREF(v, 1), (VALUE)iseq, Qnil));
11914 }
11915
11916 lstart = register_label(iseq, labels_table, RARRAY_AREF(v, 2));
11917 lend = register_label(iseq, labels_table, RARRAY_AREF(v, 3));
11918 lcont = register_label(iseq, labels_table, RARRAY_AREF(v, 4));
11919 sp = NUM2UINT(RARRAY_AREF(v, 5));
11920
11921 /* TODO: Dirty Hack! Fix me */
11922 if (type == CATCH_TYPE_RESCUE ||
11923 type == CATCH_TYPE_BREAK ||
11924 type == CATCH_TYPE_NEXT) {
11925 ++sp;
11926 }
11927
11928 lcont->sp = sp;
11929
11930 ADD_CATCH_ENTRY(type, lstart, lend, eiseq, lcont);
11931
11932 RB_GC_GUARD(v);
11933 }
11934 return COMPILE_OK;
11935}
11936
11937static struct st_table *
11938insn_make_insn_table(void)
11939{
11940 struct st_table *table;
11941 int i;
11942 table = st_init_numtable_with_size(VM_INSTRUCTION_SIZE);
11943
11944 for (i=0; i<VM_INSTRUCTION_SIZE; i++) {
11945 st_insert(table, ID2SYM(rb_intern_const(insn_name(i))), i);
11946 }
11947
11948 return table;
11949}
11950
11951static const rb_iseq_t *
11952iseq_build_load_iseq(const rb_iseq_t *iseq, VALUE op)
11953{
11954 VALUE iseqw;
11955 const rb_iseq_t *loaded_iseq;
11956
11957 if (RB_TYPE_P(op, T_ARRAY)) {
11958 iseqw = rb_iseq_load(op, (VALUE)iseq, Qnil);
11959 }
11960 else if (CLASS_OF(op) == rb_cISeq) {
11961 iseqw = op;
11962 }
11963 else {
11964 rb_raise(rb_eSyntaxError, "ISEQ is required");
11965 }
11966
11967 loaded_iseq = rb_iseqw_to_iseq(iseqw);
11968 return loaded_iseq;
11969}
11970
11971static VALUE
11972iseq_build_callinfo_from_hash(rb_iseq_t *iseq, VALUE op)
11973{
11974 ID mid = 0;
11975 int orig_argc = 0;
11976 unsigned int flag = 0;
11977 struct rb_callinfo_kwarg *kw_arg = 0;
11978
11979 if (!NIL_P(op)) {
11980 VALUE vmid = rb_hash_aref(op, ID2SYM(rb_intern_const("mid")));
11981 VALUE vflag = rb_hash_aref(op, ID2SYM(rb_intern_const("flag")));
11982 VALUE vorig_argc = rb_hash_aref(op, ID2SYM(rb_intern_const("orig_argc")));
11983 VALUE vkw_arg = rb_hash_aref(op, ID2SYM(rb_intern_const("kw_arg")));
11984
11985 if (!NIL_P(vmid)) mid = SYM2ID(vmid);
11986 if (!NIL_P(vflag)) flag = NUM2UINT(vflag);
11987 if (!NIL_P(vorig_argc)) orig_argc = FIX2INT(vorig_argc);
11988
11989 if (!NIL_P(vkw_arg)) {
11990 int i;
11991 int len = RARRAY_LENINT(vkw_arg);
11992 size_t n = rb_callinfo_kwarg_bytes(len);
11993
11994 kw_arg = xmalloc(n);
11995 kw_arg->references = 0;
11996 kw_arg->keyword_len = len;
11997 for (i = 0; i < len; i++) {
11998 VALUE kw = RARRAY_AREF(vkw_arg, i);
11999 SYM2ID(kw); /* make immortal */
12000 kw_arg->keywords[i] = kw;
12001 }
12002 }
12003 }
12004
12005 const struct rb_callinfo *ci = new_callinfo(iseq, mid, orig_argc, flag, kw_arg, (flag & VM_CALL_ARGS_SIMPLE) == 0);
12006 RB_OBJ_WRITTEN(iseq, Qundef, ci);
12007 return (VALUE)ci;
12008}
12009
12010static rb_event_flag_t
12011event_name_to_flag(VALUE sym)
12012{
12013#define CHECK_EVENT(ev) if (sym == ID2SYM(rb_intern_const(#ev))) return ev;
12014 CHECK_EVENT(RUBY_EVENT_LINE);
12015 CHECK_EVENT(RUBY_EVENT_CLASS);
12016 CHECK_EVENT(RUBY_EVENT_END);
12017 CHECK_EVENT(RUBY_EVENT_CALL);
12018 CHECK_EVENT(RUBY_EVENT_RETURN);
12019 CHECK_EVENT(RUBY_EVENT_B_CALL);
12020 CHECK_EVENT(RUBY_EVENT_B_RETURN);
12021 CHECK_EVENT(RUBY_EVENT_RESCUE);
12022#undef CHECK_EVENT
12023 return RUBY_EVENT_NONE;
12024}
12025
12026static int
12027iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
12028 VALUE body, VALUE node_ids, VALUE labels_wrapper)
12029{
12030 /* TODO: body should be frozen */
12031 long i, len = RARRAY_LEN(body);
12032 struct st_table *labels_table = RTYPEDDATA_DATA(labels_wrapper);
12033 int j;
12034 int line_no = 0, node_id = -1, insn_idx = 0;
12035 int ret = COMPILE_OK;
12036
12037 /*
12038 * index -> LABEL *label
12039 */
12040 static struct st_table *insn_table;
12041
12042 if (insn_table == 0) {
12043 insn_table = insn_make_insn_table();
12044 }
12045
12046 for (i=0; i<len; i++) {
12047 VALUE obj = RARRAY_AREF(body, i);
12048
12049 if (SYMBOL_P(obj)) {
12050 rb_event_flag_t event;
12051 if ((event = event_name_to_flag(obj)) != RUBY_EVENT_NONE) {
12052 ADD_TRACE(anchor, event);
12053 }
12054 else {
12055 LABEL *label = register_label(iseq, labels_table, obj);
12056 ADD_LABEL(anchor, label);
12057 }
12058 }
12059 else if (FIXNUM_P(obj)) {
12060 line_no = NUM2INT(obj);
12061 }
12062 else if (RB_TYPE_P(obj, T_ARRAY)) {
12063 VALUE *argv = 0;
12064 int argc = RARRAY_LENINT(obj) - 1;
12065 st_data_t insn_id;
12066 VALUE insn;
12067
12068 if (node_ids) {
12069 node_id = NUM2INT(rb_ary_entry(node_ids, insn_idx++));
12070 }
12071
12072 insn = (argc < 0) ? Qnil : RARRAY_AREF(obj, 0);
12073 if (st_lookup(insn_table, (st_data_t)insn, &insn_id) == 0) {
12074 /* TODO: exception */
12075 COMPILE_ERROR(iseq, line_no,
12076 "unknown instruction: %+"PRIsVALUE, insn);
12077 ret = COMPILE_NG;
12078 break;
12079 }
12080
12081 if (argc != insn_len((VALUE)insn_id)-1) {
12082 COMPILE_ERROR(iseq, line_no,
12083 "operand size mismatch");
12084 ret = COMPILE_NG;
12085 break;
12086 }
12087
12088 if (argc > 0) {
12089 argv = compile_data_calloc2_type(iseq, VALUE, argc);
12090
12091 // add element before operand setup to make GC root
12092 ADD_ELEM(anchor,
12093 (LINK_ELEMENT*)new_insn_core(iseq, line_no, node_id,
12094 (enum ruby_vminsn_type)insn_id, argc, argv));
12095
12096 for (j=0; j<argc; j++) {
12097 VALUE op = rb_ary_entry(obj, j+1);
12098 switch (insn_op_type((VALUE)insn_id, j)) {
12099 case TS_OFFSET: {
12100 LABEL *label = register_label(iseq, labels_table, op);
12101 argv[j] = (VALUE)label;
12102 break;
12103 }
12104 case TS_LINDEX:
12105 case TS_NUM:
12106 (void)NUM2INT(op);
12107 argv[j] = op;
12108 break;
12109 case TS_VALUE:
12110 argv[j] = op;
12111 RB_OBJ_WRITTEN(iseq, Qundef, op);
12112 break;
12113 case TS_ISEQ:
12114 {
12115 if (op != Qnil) {
12116 VALUE v = (VALUE)iseq_build_load_iseq(iseq, op);
12117 argv[j] = v;
12118 RB_OBJ_WRITTEN(iseq, Qundef, v);
12119 }
12120 else {
12121 argv[j] = 0;
12122 }
12123 }
12124 break;
12125 case TS_ISE:
12126 argv[j] = op;
12127 if (NUM2UINT(op) >= ISEQ_BODY(iseq)->ise_size) {
12128 ISEQ_BODY(iseq)->ise_size = NUM2INT(op) + 1;
12129 }
12130 break;
12131 case TS_IC:
12132 {
12133 VALUE segments = rb_ary_new();
12134 op = rb_to_array_type(op);
12135
12136 for (int i = 0; i < RARRAY_LEN(op); i++) {
12137 VALUE sym = RARRAY_AREF(op, i);
12138 sym = rb_to_symbol_type(sym);
12139 rb_ary_push(segments, sym);
12140 }
12141
12142 RB_GC_GUARD(op);
12143 argv[j] = segments;
12144 RB_OBJ_WRITTEN(iseq, Qundef, segments);
12145 ISEQ_BODY(iseq)->ic_size++;
12146 }
12147 break;
12148 case TS_IVC: /* inline ivar cache */
12149 argv[j] = op;
12150 if (NUM2UINT(op) >= ISEQ_BODY(iseq)->ivc_size) {
12151 ISEQ_BODY(iseq)->ivc_size = NUM2INT(op) + 1;
12152 }
12153 break;
12154 case TS_ICVARC: /* inline cvar cache */
12155 argv[j] = op;
12156 if (NUM2UINT(op) >= ISEQ_BODY(iseq)->icvarc_size) {
12157 ISEQ_BODY(iseq)->icvarc_size = NUM2INT(op) + 1;
12158 }
12159 break;
12160 case TS_CALLDATA:
12161 argv[j] = iseq_build_callinfo_from_hash(iseq, op);
12162 break;
12163 case TS_ID:
12164 argv[j] = rb_to_symbol_type(op);
12165 break;
12166 case TS_CDHASH:
12167 {
12168 int i;
12169 VALUE map = rb_hash_new_with_size_and_type(0, RARRAY_LEN(op)/2, &cdhash_type);
12170
12171 op = rb_to_array_type(op);
12172 for (i=0; i<RARRAY_LEN(op); i+=2) {
12173 VALUE key = RARRAY_AREF(op, i);
12174 VALUE sym = RARRAY_AREF(op, i+1);
12175 LABEL *label =
12176 register_label(iseq, labels_table, sym);
12177 rb_hash_aset(map, key, (VALUE)label | 1);
12178 }
12179 RB_GC_GUARD(op);
12180 RB_OBJ_SET_SHAREABLE(map); // allow mutation while compiling
12181 argv[j] = map;
12182 RB_OBJ_WRITTEN(iseq, Qundef, map);
12183 }
12184 break;
12185 case TS_FUNCPTR:
12186 {
12187#if SIZEOF_VALUE <= SIZEOF_LONG
12188 long funcptr = NUM2LONG(op);
12189#else
12190 LONG_LONG funcptr = NUM2LL(op);
12191#endif
12192 argv[j] = (VALUE)funcptr;
12193 }
12194 break;
12195 default:
12196 rb_raise(rb_eSyntaxError, "unknown operand: %c", insn_op_type((VALUE)insn_id, j));
12197 }
12198 }
12199 }
12200 else {
12201 ADD_ELEM(anchor,
12202 (LINK_ELEMENT*)new_insn_core(iseq, line_no, node_id,
12203 (enum ruby_vminsn_type)insn_id, argc, NULL));
12204 }
12205 }
12206 else {
12207 rb_raise(rb_eTypeError, "unexpected object for instruction");
12208 }
12209 }
12210 RTYPEDDATA_DATA(labels_wrapper) = 0;
12211 RB_GC_GUARD(labels_wrapper);
12212 validate_labels(iseq, labels_table);
12213 if (!ret) return ret;
12214 return iseq_setup(iseq, anchor);
12215}
12216
12217#define CHECK_ARRAY(v) rb_to_array_type(v)
12218#define CHECK_SYMBOL(v) rb_to_symbol_type(v)
12219
12220static int
12221int_param(int *dst, VALUE param, VALUE sym)
12222{
12223 VALUE val = rb_hash_aref(param, sym);
12224 if (FIXNUM_P(val)) {
12225 *dst = FIX2INT(val);
12226 return TRUE;
12227 }
12228 else if (!NIL_P(val)) {
12229 rb_raise(rb_eTypeError, "invalid %+"PRIsVALUE" Fixnum: %+"PRIsVALUE,
12230 sym, val);
12231 }
12232 return FALSE;
12233}
12234
12235static const struct rb_iseq_param_keyword *
12236iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords)
12237{
12238 int i, j;
12239 int len = RARRAY_LENINT(keywords);
12240 int default_len;
12241 VALUE key, sym, default_val;
12242 VALUE *dvs;
12243 ID *ids;
12244 struct rb_iseq_param_keyword *keyword = ZALLOC(struct rb_iseq_param_keyword);
12245
12246 ISEQ_BODY(iseq)->param.flags.has_kw = TRUE;
12247
12248 keyword->num = len;
12249#define SYM(s) ID2SYM(rb_intern_const(#s))
12250 (void)int_param(&keyword->bits_start, params, SYM(kwbits));
12251 i = keyword->bits_start - keyword->num;
12252 ids = (ID *)&ISEQ_BODY(iseq)->local_table[i];
12253#undef SYM
12254
12255 /* required args */
12256 for (i = 0; i < len; i++) {
12257 VALUE val = RARRAY_AREF(keywords, i);
12258
12259 if (!SYMBOL_P(val)) {
12260 goto default_values;
12261 }
12262 ids[i] = SYM2ID(val);
12263 keyword->required_num++;
12264 }
12265
12266 default_values: /* note: we intentionally preserve `i' from previous loop */
12267 default_len = len - i;
12268 if (default_len == 0) {
12269 keyword->table = ids;
12270 return keyword;
12271 }
12272 else if (default_len < 0) {
12274 }
12275
12276 dvs = ALLOC_N(VALUE, (unsigned int)default_len);
12277
12278 for (j = 0; i < len; i++, j++) {
12279 key = RARRAY_AREF(keywords, i);
12280 CHECK_ARRAY(key);
12281
12282 switch (RARRAY_LEN(key)) {
12283 case 1:
12284 sym = RARRAY_AREF(key, 0);
12285 default_val = Qundef;
12286 break;
12287 case 2:
12288 sym = RARRAY_AREF(key, 0);
12289 default_val = RARRAY_AREF(key, 1);
12290 break;
12291 default:
12292 rb_raise(rb_eTypeError, "keyword default has unsupported len %+"PRIsVALUE, key);
12293 }
12294 ids[i] = SYM2ID(sym);
12295 RB_OBJ_WRITE(iseq, &dvs[j], default_val);
12296 }
12297
12298 keyword->table = ids;
12299 keyword->default_values = dvs;
12300
12301 return keyword;
12302}
12303
12304static void
12305iseq_insn_each_object_mark_and_move(VALUE * obj, VALUE _)
12306{
12307 rb_gc_mark_and_move(obj);
12308}
12309
12310void
12311rb_iseq_mark_and_move_insn_storage(struct iseq_compile_data_storage *storage)
12312{
12313 INSN *iobj = 0;
12314 size_t size = sizeof(INSN);
12315 size_t align = ALIGNMENT_SIZE_OF(INSN);
12316 unsigned int pos = 0;
12317
12318 while (storage) {
12319 size_t padding = calc_padding((void *)&storage->buff[pos], align);
12320 size_t offset = pos + size + padding;
12321 if (offset > storage->size || offset > storage->pos) {
12322 pos = 0;
12323 storage = storage->next;
12324 }
12325 else {
12326 pos += (int)padding;
12327
12328 iobj = (INSN *)&storage->buff[pos];
12329
12330 if (iobj->operands) {
12331 iseq_insn_each_markable_object(iobj, iseq_insn_each_object_mark_and_move, (VALUE)0);
12332 }
12333 pos += (int)size;
12334 }
12335 }
12336}
12337
12338static const rb_data_type_t labels_wrapper_type = {
12339 .wrap_struct_name = "compiler/labels_wrapper",
12340 .function = {
12341 .dmark = (RUBY_DATA_FUNC)rb_mark_set,
12342 .dfree = (RUBY_DATA_FUNC)st_free_table,
12343 },
12344 .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
12345};
12346
12347void
12348rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
12349 VALUE exception, VALUE body)
12350{
12351#define SYM(s) ID2SYM(rb_intern_const(#s))
12352 int i, len;
12353 unsigned int arg_size, local_size, stack_max;
12354 ID *tbl;
12355 struct st_table *labels_table = st_init_numtable();
12356 VALUE labels_wrapper = TypedData_Wrap_Struct(0, &labels_wrapper_type, labels_table);
12357 VALUE arg_opt_labels = rb_hash_aref(params, SYM(opt));
12358 VALUE keywords = rb_hash_aref(params, SYM(keyword));
12359 VALUE sym_arg_rest = ID2SYM(rb_intern_const("#arg_rest"));
12360 DECL_ANCHOR(anchor);
12361 INIT_ANCHOR(anchor);
12362
12363 len = RARRAY_LENINT(locals);
12364 ISEQ_BODY(iseq)->local_table_size = len;
12365 ISEQ_BODY(iseq)->local_table = tbl = len > 0 ? (ID *)ALLOC_N(ID, ISEQ_BODY(iseq)->local_table_size) : NULL;
12366
12367 for (i = 0; i < len; i++) {
12368 VALUE lv = RARRAY_AREF(locals, i);
12369
12370 if (sym_arg_rest == lv) {
12371 tbl[i] = 0;
12372 }
12373 else {
12374 tbl[i] = FIXNUM_P(lv) ? (ID)FIX2LONG(lv) : SYM2ID(CHECK_SYMBOL(lv));
12375 }
12376 }
12377
12378#define INT_PARAM(F) int_param(&ISEQ_BODY(iseq)->param.F, params, SYM(F))
12379 if (INT_PARAM(lead_num)) {
12380 ISEQ_BODY(iseq)->param.flags.has_lead = TRUE;
12381 }
12382 if (INT_PARAM(post_num)) ISEQ_BODY(iseq)->param.flags.has_post = TRUE;
12383 if (INT_PARAM(post_start)) ISEQ_BODY(iseq)->param.flags.has_post = TRUE;
12384 if (INT_PARAM(rest_start)) ISEQ_BODY(iseq)->param.flags.has_rest = TRUE;
12385 if (INT_PARAM(block_start)) ISEQ_BODY(iseq)->param.flags.has_block = TRUE;
12386#undef INT_PARAM
12387 {
12388#define INT_PARAM(F) F = (int_param(&x, misc, SYM(F)) ? (unsigned int)x : 0)
12389 int x;
12390 INT_PARAM(arg_size);
12391 INT_PARAM(local_size);
12392 INT_PARAM(stack_max);
12393#undef INT_PARAM
12394 }
12395
12396 VALUE node_ids = Qfalse;
12397#ifdef USE_ISEQ_NODE_ID
12398 node_ids = rb_hash_aref(misc, ID2SYM(rb_intern("node_ids")));
12399 if (!RB_TYPE_P(node_ids, T_ARRAY)) {
12400 rb_raise(rb_eTypeError, "node_ids is not an array");
12401 }
12402#endif
12403
12404 if (RB_TYPE_P(arg_opt_labels, T_ARRAY)) {
12405 len = RARRAY_LENINT(arg_opt_labels);
12406 ISEQ_BODY(iseq)->param.flags.has_opt = !!(len - 1 >= 0);
12407
12408 if (ISEQ_BODY(iseq)->param.flags.has_opt) {
12409 VALUE *opt_table = ALLOC_N(VALUE, len);
12410
12411 for (i = 0; i < len; i++) {
12412 VALUE ent = RARRAY_AREF(arg_opt_labels, i);
12413 LABEL *label = register_label(iseq, labels_table, ent);
12414 opt_table[i] = (VALUE)label;
12415 }
12416
12417 ISEQ_BODY(iseq)->param.opt_num = len - 1;
12418 ISEQ_BODY(iseq)->param.opt_table = opt_table;
12419 }
12420 }
12421 else if (!NIL_P(arg_opt_labels)) {
12422 rb_raise(rb_eTypeError, ":opt param is not an array: %+"PRIsVALUE,
12423 arg_opt_labels);
12424 }
12425
12426 if (RB_TYPE_P(keywords, T_ARRAY)) {
12427 ISEQ_BODY(iseq)->param.keyword = iseq_build_kw(iseq, params, keywords);
12428 }
12429 else if (!NIL_P(keywords)) {
12430 rb_raise(rb_eTypeError, ":keywords param is not an array: %+"PRIsVALUE,
12431 keywords);
12432 }
12433
12434 if (Qtrue == rb_hash_aref(params, SYM(ambiguous_param0))) {
12435 ISEQ_BODY(iseq)->param.flags.ambiguous_param0 = TRUE;
12436 }
12437
12438 if (Qtrue == rb_hash_aref(params, SYM(use_block))) {
12439 ISEQ_BODY(iseq)->param.flags.use_block = TRUE;
12440 }
12441
12442 if (int_param(&i, params, SYM(kwrest))) {
12443 struct rb_iseq_param_keyword *keyword = (struct rb_iseq_param_keyword *)ISEQ_BODY(iseq)->param.keyword;
12444 if (keyword == NULL) {
12445 ISEQ_BODY(iseq)->param.keyword = keyword = ZALLOC(struct rb_iseq_param_keyword);
12446 }
12447 keyword->rest_start = i;
12448 ISEQ_BODY(iseq)->param.flags.has_kwrest = TRUE;
12449 }
12450#undef SYM
12451 iseq_calc_param_size(iseq);
12452
12453 /* exception */
12454 iseq_build_from_ary_exception(iseq, labels_table, exception);
12455
12456 /* body */
12457 iseq_build_from_ary_body(iseq, anchor, body, node_ids, labels_wrapper);
12458
12459 ISEQ_BODY(iseq)->param.size = arg_size;
12460 ISEQ_BODY(iseq)->local_table_size = local_size;
12461 ISEQ_BODY(iseq)->stack_max = stack_max;
12462}
12463
12464/* for parser */
12465
12466int
12467rb_dvar_defined(ID id, const rb_iseq_t *iseq)
12468{
12469 if (iseq) {
12470 const struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
12471 while (body->type == ISEQ_TYPE_BLOCK ||
12472 body->type == ISEQ_TYPE_RESCUE ||
12473 body->type == ISEQ_TYPE_ENSURE ||
12474 body->type == ISEQ_TYPE_EVAL ||
12475 body->type == ISEQ_TYPE_MAIN
12476 ) {
12477 unsigned int i;
12478
12479 for (i = 0; i < body->local_table_size; i++) {
12480 if (body->local_table[i] == id) {
12481 return 1;
12482 }
12483 }
12484 iseq = body->parent_iseq;
12485 body = ISEQ_BODY(iseq);
12486 }
12487 }
12488 return 0;
12489}
12490
12491int
12492rb_local_defined(ID id, const rb_iseq_t *iseq)
12493{
12494 if (iseq) {
12495 unsigned int i;
12496 const struct rb_iseq_constant_body *const body = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq);
12497
12498 for (i=0; i<body->local_table_size; i++) {
12499 if (body->local_table[i] == id) {
12500 return 1;
12501 }
12502 }
12503 }
12504 return 0;
12505}
12506
12507/* ISeq binary format */
12508
12509#ifndef IBF_ISEQ_DEBUG
12510#define IBF_ISEQ_DEBUG 0
12511#endif
12512
12513#ifndef IBF_ISEQ_ENABLE_LOCAL_BUFFER
12514#define IBF_ISEQ_ENABLE_LOCAL_BUFFER 0
12515#endif
12516
12517typedef uint32_t ibf_offset_t;
12518#define IBF_OFFSET(ptr) ((ibf_offset_t)(VALUE)(ptr))
12519
12520#define IBF_MAJOR_VERSION ISEQ_MAJOR_VERSION
12521#ifdef RUBY_DEVEL
12522#define IBF_DEVEL_VERSION 5
12523#define IBF_MINOR_VERSION (ISEQ_MINOR_VERSION * 10000 + IBF_DEVEL_VERSION)
12524#else
12525#define IBF_MINOR_VERSION ISEQ_MINOR_VERSION
12526#endif
12527
12528static const char IBF_ENDIAN_MARK =
12529#ifdef WORDS_BIGENDIAN
12530 'b'
12531#else
12532 'l'
12533#endif
12534 ;
12535
12537 char magic[4]; /* YARB */
12538 uint32_t major_version;
12539 uint32_t minor_version;
12540 uint32_t size;
12541 uint32_t extra_size;
12542
12543 uint32_t iseq_list_size;
12544 uint32_t global_object_list_size;
12545 ibf_offset_t iseq_list_offset;
12546 ibf_offset_t global_object_list_offset;
12547 uint8_t endian;
12548 uint8_t wordsize; /* assume no 2048-bit CPU */
12549};
12550
12552 VALUE str;
12553 st_table *obj_table; /* obj -> obj number */
12554};
12555
12556struct ibf_dump {
12557 st_table *iseq_table; /* iseq -> iseq number */
12558 struct ibf_dump_buffer global_buffer;
12559 struct ibf_dump_buffer *current_buffer;
12560};
12561
12563 const char *buff;
12564 ibf_offset_t size;
12565
12566 VALUE obj_list; /* [obj0, ...] */
12567 unsigned int obj_list_size;
12568 ibf_offset_t obj_list_offset;
12569};
12570
12571struct ibf_load {
12572 const struct ibf_header *header;
12573 VALUE iseq_list; /* [iseq0, ...] */
12574 struct ibf_load_buffer global_buffer;
12575 VALUE loader_obj;
12576 rb_iseq_t *iseq;
12577 VALUE str;
12578 struct ibf_load_buffer *current_buffer;
12579};
12580
12582 long size;
12583 VALUE buffer[1];
12584};
12585
12586static void
12587pinned_list_mark(void *ptr)
12588{
12589 long i;
12590 struct pinned_list *list = (struct pinned_list *)ptr;
12591 for (i = 0; i < list->size; i++) {
12592 if (list->buffer[i]) {
12593 rb_gc_mark(list->buffer[i]);
12594 }
12595 }
12596}
12597
12598static const rb_data_type_t pinned_list_type = {
12599 "pinned_list",
12600 {
12601 pinned_list_mark,
12603 NULL, // No external memory to report,
12604 },
12605 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE
12606};
12607
12608static VALUE
12609pinned_list_fetch(VALUE list, long offset)
12610{
12611 struct pinned_list * ptr;
12612
12613 TypedData_Get_Struct(list, struct pinned_list, &pinned_list_type, ptr);
12614
12615 if (offset >= ptr->size) {
12616 rb_raise(rb_eIndexError, "object index out of range: %ld", offset);
12617 }
12618
12619 return ptr->buffer[offset];
12620}
12621
12622static void
12623pinned_list_store(VALUE list, long offset, VALUE object)
12624{
12625 struct pinned_list * ptr;
12626
12627 TypedData_Get_Struct(list, struct pinned_list, &pinned_list_type, ptr);
12628
12629 if (offset >= ptr->size) {
12630 rb_raise(rb_eIndexError, "object index out of range: %ld", offset);
12631 }
12632
12633 RB_OBJ_WRITE(list, &ptr->buffer[offset], object);
12634}
12635
12636static VALUE
12637pinned_list_new(long size)
12638{
12639 size_t memsize = offsetof(struct pinned_list, buffer) + size * sizeof(VALUE);
12640 VALUE obj_list = rb_data_typed_object_zalloc(0, memsize, &pinned_list_type);
12641 struct pinned_list * ptr = RTYPEDDATA_GET_DATA(obj_list);
12642 ptr->size = size;
12643 return obj_list;
12644}
12645
12646static ibf_offset_t
12647ibf_dump_pos(struct ibf_dump *dump)
12648{
12649 long pos = RSTRING_LEN(dump->current_buffer->str);
12650#if SIZEOF_LONG > SIZEOF_INT
12651 if (pos >= UINT_MAX) {
12652 rb_raise(rb_eRuntimeError, "dump size exceeds");
12653 }
12654#endif
12655 return (unsigned int)pos;
12656}
12657
12658static void
12659ibf_dump_align(struct ibf_dump *dump, size_t align)
12660{
12661 ibf_offset_t pos = ibf_dump_pos(dump);
12662 if (pos % align) {
12663 static const char padding[sizeof(VALUE)];
12664 size_t size = align - ((size_t)pos % align);
12665#if SIZEOF_LONG > SIZEOF_INT
12666 if (pos + size >= UINT_MAX) {
12667 rb_raise(rb_eRuntimeError, "dump size exceeds");
12668 }
12669#endif
12670 for (; size > sizeof(padding); size -= sizeof(padding)) {
12671 rb_str_cat(dump->current_buffer->str, padding, sizeof(padding));
12672 }
12673 rb_str_cat(dump->current_buffer->str, padding, size);
12674 }
12675}
12676
12677static ibf_offset_t
12678ibf_dump_write(struct ibf_dump *dump, const void *buff, unsigned long size)
12679{
12680 ibf_offset_t pos = ibf_dump_pos(dump);
12681#if SIZEOF_LONG > SIZEOF_INT
12682 /* ensure the resulting dump does not exceed UINT_MAX */
12683 if (size >= UINT_MAX || pos + size >= UINT_MAX) {
12684 rb_raise(rb_eRuntimeError, "dump size exceeds");
12685 }
12686#endif
12687 rb_str_cat(dump->current_buffer->str, (const char *)buff, size);
12688 return pos;
12689}
12690
12691static ibf_offset_t
12692ibf_dump_write_byte(struct ibf_dump *dump, unsigned char byte)
12693{
12694 return ibf_dump_write(dump, &byte, sizeof(unsigned char));
12695}
12696
12697static void
12698ibf_dump_overwrite(struct ibf_dump *dump, void *buff, unsigned int size, long offset)
12699{
12700 VALUE str = dump->current_buffer->str;
12701 char *ptr = RSTRING_PTR(str);
12702 if ((unsigned long)(size + offset) > (unsigned long)RSTRING_LEN(str))
12703 rb_bug("ibf_dump_overwrite: overflow");
12704 memcpy(ptr + offset, buff, size);
12705}
12706
12707static const void *
12708ibf_load_ptr(const struct ibf_load *load, ibf_offset_t *offset, int size)
12709{
12710 ibf_offset_t beg = *offset;
12711 *offset += size;
12712 return load->current_buffer->buff + beg;
12713}
12714
12715static void *
12716ibf_load_alloc(const struct ibf_load *load, ibf_offset_t offset, size_t x, size_t y)
12717{
12718 void *buff = ruby_xmalloc2(x, y);
12719 size_t size = x * y;
12720 memcpy(buff, load->current_buffer->buff + offset, size);
12721 return buff;
12722}
12723
12724#define IBF_W_ALIGN(type) (RUBY_ALIGNOF(type) > 1 ? ibf_dump_align(dump, RUBY_ALIGNOF(type)) : (void)0)
12725
12726#define IBF_W(b, type, n) (IBF_W_ALIGN(type), (type *)(VALUE)IBF_WP(b, type, n))
12727#define IBF_WV(variable) ibf_dump_write(dump, &(variable), sizeof(variable))
12728#define IBF_WP(b, type, n) ibf_dump_write(dump, (b), sizeof(type) * (n))
12729#define IBF_R(val, type, n) (type *)ibf_load_alloc(load, IBF_OFFSET(val), sizeof(type), (n))
12730#define IBF_ZERO(variable) memset(&(variable), 0, sizeof(variable))
12731
12732static int
12733ibf_table_lookup(struct st_table *table, st_data_t key)
12734{
12735 st_data_t val;
12736
12737 if (st_lookup(table, key, &val)) {
12738 return (int)val;
12739 }
12740 else {
12741 return -1;
12742 }
12743}
12744
12745static int
12746ibf_table_find_or_insert(struct st_table *table, st_data_t key)
12747{
12748 int index = ibf_table_lookup(table, key);
12749
12750 if (index < 0) { /* not found */
12751 index = (int)table->num_entries;
12752 st_insert(table, key, (st_data_t)index);
12753 }
12754
12755 return index;
12756}
12757
12758/* dump/load generic */
12759
12760static void ibf_dump_object_list(struct ibf_dump *dump, ibf_offset_t *obj_list_offset, unsigned int *obj_list_size);
12761
12762static VALUE ibf_load_object(const struct ibf_load *load, VALUE object_index);
12763static rb_iseq_t *ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq);
12764
12765static st_table *
12766ibf_dump_object_table_new(void)
12767{
12768 st_table *obj_table = st_init_numtable(); /* need free */
12769 st_insert(obj_table, (st_data_t)Qnil, (st_data_t)0); /* 0th is nil */
12770
12771 return obj_table;
12772}
12773
12774static VALUE
12775ibf_dump_object(struct ibf_dump *dump, VALUE obj)
12776{
12777 return ibf_table_find_or_insert(dump->current_buffer->obj_table, (st_data_t)obj);
12778}
12779
12780static VALUE
12781ibf_dump_id(struct ibf_dump *dump, ID id)
12782{
12783 if (id == 0 || rb_id2name(id) == NULL) {
12784 return 0;
12785 }
12786 return ibf_dump_object(dump, rb_id2sym(id));
12787}
12788
12789static ID
12790ibf_load_id(const struct ibf_load *load, const ID id_index)
12791{
12792 if (id_index == 0) {
12793 return 0;
12794 }
12795 VALUE sym = ibf_load_object(load, id_index);
12796 if (rb_integer_type_p(sym)) {
12797 /* Load hidden local variables as indexes */
12798 return NUM2ULONG(sym);
12799 }
12800 return rb_sym2id(sym);
12801}
12802
12803/* dump/load: code */
12804
12805static ibf_offset_t ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq);
12806
12807static int
12808ibf_dump_iseq(struct ibf_dump *dump, const rb_iseq_t *iseq)
12809{
12810 if (iseq == NULL) {
12811 return -1;
12812 }
12813 else {
12814 return ibf_table_find_or_insert(dump->iseq_table, (st_data_t)iseq);
12815 }
12816}
12817
12818static unsigned char
12819ibf_load_byte(const struct ibf_load *load, ibf_offset_t *offset)
12820{
12821 if (*offset >= load->current_buffer->size) { rb_raise(rb_eRuntimeError, "invalid bytecode"); }
12822 return (unsigned char)load->current_buffer->buff[(*offset)++];
12823}
12824
12825/*
12826 * Small uint serialization
12827 * 0x00000000_00000000 - 0x00000000_0000007f: 1byte | XXXX XXX1 |
12828 * 0x00000000_00000080 - 0x00000000_00003fff: 2byte | XXXX XX10 | XXXX XXXX |
12829 * 0x00000000_00004000 - 0x00000000_001fffff: 3byte | XXXX X100 | XXXX XXXX | XXXX XXXX |
12830 * 0x00000000_00020000 - 0x00000000_0fffffff: 4byte | XXXX 1000 | XXXX XXXX | XXXX XXXX | XXXX XXXX |
12831 * ...
12832 * 0x00010000_00000000 - 0x00ffffff_ffffffff: 8byte | 1000 0000 | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX |
12833 * 0x01000000_00000000 - 0xffffffff_ffffffff: 9byte | 0000 0000 | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX | XXXX XXXX |
12834 */
12835static void
12836ibf_dump_write_small_value(struct ibf_dump *dump, VALUE x)
12837{
12838 if (sizeof(VALUE) > 8 || CHAR_BIT != 8) {
12839 ibf_dump_write(dump, &x, sizeof(VALUE));
12840 return;
12841 }
12842
12843 enum { max_byte_length = sizeof(VALUE) + 1 };
12844
12845 unsigned char bytes[max_byte_length];
12846 ibf_offset_t n;
12847
12848 for (n = 0; n < sizeof(VALUE) && (x >> (7 - n)); n++, x >>= 8) {
12849 bytes[max_byte_length - 1 - n] = (unsigned char)x;
12850 }
12851
12852 x <<= 1;
12853 x |= 1;
12854 x <<= n;
12855 bytes[max_byte_length - 1 - n] = (unsigned char)x;
12856 n++;
12857
12858 ibf_dump_write(dump, bytes + max_byte_length - n, n);
12859}
12860
12861static VALUE
12862ibf_load_small_value(const struct ibf_load *load, ibf_offset_t *offset)
12863{
12864 if (sizeof(VALUE) > 8 || CHAR_BIT != 8) {
12865 union { char s[sizeof(VALUE)]; VALUE v; } x;
12866
12867 memcpy(x.s, load->current_buffer->buff + *offset, sizeof(VALUE));
12868 *offset += sizeof(VALUE);
12869
12870 return x.v;
12871 }
12872
12873 enum { max_byte_length = sizeof(VALUE) + 1 };
12874
12875 const unsigned char *buffer = (const unsigned char *)load->current_buffer->buff;
12876 const unsigned char c = buffer[*offset];
12877
12878 ibf_offset_t n =
12879 c & 1 ? 1 :
12880 c == 0 ? 9 : ntz_int32(c) + 1;
12881 VALUE x = (VALUE)c >> n;
12882
12883 if (*offset + n > load->current_buffer->size) {
12884 rb_raise(rb_eRuntimeError, "invalid byte sequence");
12885 }
12886
12887 ibf_offset_t i;
12888 for (i = 1; i < n; i++) {
12889 x <<= 8;
12890 x |= (VALUE)buffer[*offset + i];
12891 }
12892
12893 *offset += n;
12894 return x;
12895}
12896
12897static void
12898ibf_dump_builtin(struct ibf_dump *dump, const struct rb_builtin_function *bf)
12899{
12900 // short: index
12901 // short: name.length
12902 // bytes: name
12903 // // omit argc (only verify with name)
12904 ibf_dump_write_small_value(dump, (VALUE)bf->index);
12905
12906 size_t len = strlen(bf->name);
12907 ibf_dump_write_small_value(dump, (VALUE)len);
12908 ibf_dump_write(dump, bf->name, len);
12909}
12910
12911static const struct rb_builtin_function *
12912ibf_load_builtin(const struct ibf_load *load, ibf_offset_t *offset)
12913{
12914 int i = (int)ibf_load_small_value(load, offset);
12915 int len = (int)ibf_load_small_value(load, offset);
12916 const char *name = (char *)ibf_load_ptr(load, offset, len);
12917
12918 if (0) {
12919 fprintf(stderr, "%.*s!!\n", len, name);
12920 }
12921
12922 const struct rb_builtin_function *table = GET_VM()->builtin_function_table;
12923 if (table == NULL) rb_raise(rb_eArgError, "builtin function table is not provided");
12924 if (strncmp(table[i].name, name, len) != 0) {
12925 rb_raise(rb_eArgError, "builtin function index (%d) mismatch (expect %.*s but %s)",
12926 i, len, name, table[i].name);
12927 }
12928 // fprintf(stderr, "load-builtin: name:%s(%d)\n", table[i].name, table[i].argc);
12929
12930 return &table[i];
12931}
12932
12933static ibf_offset_t
12934ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
12935{
12936 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
12937 const int iseq_size = body->iseq_size;
12938 int code_index;
12939 const VALUE *orig_code = rb_iseq_original_iseq(iseq);
12940
12941 ibf_offset_t offset = ibf_dump_pos(dump);
12942
12943 for (code_index=0; code_index<iseq_size;) {
12944 const VALUE insn = orig_code[code_index++];
12945 const char *types = insn_op_types(insn);
12946 int op_index;
12947
12948 /* opcode */
12949 if (insn >= 0x100) { rb_raise(rb_eRuntimeError, "invalid instruction"); }
12950 ibf_dump_write_small_value(dump, insn);
12951
12952 /* operands */
12953 for (op_index=0; types[op_index]; op_index++, code_index++) {
12954 VALUE op = orig_code[code_index];
12955 VALUE wv;
12956
12957 switch (types[op_index]) {
12958 case TS_CDHASH:
12959 case TS_VALUE:
12960 wv = ibf_dump_object(dump, op);
12961 break;
12962 case TS_ISEQ:
12963 wv = (VALUE)ibf_dump_iseq(dump, (const rb_iseq_t *)op);
12964 break;
12965 case TS_IC:
12966 {
12967 IC ic = (IC)op;
12968 VALUE arr = idlist_to_array(ic->segments);
12969 wv = ibf_dump_object(dump, arr);
12970 }
12971 break;
12972 case TS_ISE:
12973 case TS_IVC:
12974 case TS_ICVARC:
12975 {
12977 wv = is - ISEQ_IS_ENTRY_START(body, types[op_index]);
12978 }
12979 break;
12980 case TS_CALLDATA:
12981 {
12982 goto skip_wv;
12983 }
12984 case TS_ID:
12985 wv = ibf_dump_id(dump, (ID)op);
12986 break;
12987 case TS_FUNCPTR:
12988 rb_raise(rb_eRuntimeError, "TS_FUNCPTR is not supported");
12989 goto skip_wv;
12990 case TS_BUILTIN:
12991 ibf_dump_builtin(dump, (const struct rb_builtin_function *)op);
12992 goto skip_wv;
12993 default:
12994 wv = op;
12995 break;
12996 }
12997 ibf_dump_write_small_value(dump, wv);
12998 skip_wv:;
12999 }
13000 RUBY_ASSERT(insn_len(insn) == op_index+1);
13001 }
13002
13003 return offset;
13004}
13005
13006static int
13007cdhash_copy_i(st_data_t key, st_data_t val, st_data_t arg)
13008{
13009 rb_hash_aset((VALUE)arg, (VALUE)key, (VALUE)val);
13010 return ST_CONTINUE;
13011}
13012
13013static VALUE
13014cdhash_copy(VALUE dest, VALUE src)
13015{
13016 rb_hash_stlike_foreach(src, cdhash_copy_i, dest);
13017 return dest;
13018}
13019
13020static VALUE *
13021ibf_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)
13022{
13023 VALUE iseqv = (VALUE)iseq;
13024 unsigned int code_index;
13025 ibf_offset_t reading_pos = bytecode_offset;
13026 VALUE *code = ZALLOC_N(VALUE, iseq_size);
13027
13028 struct rb_iseq_constant_body *load_body = ISEQ_BODY(iseq);
13029 struct rb_call_data *cd_entries = load_body->call_data;
13030 int ic_index = 0;
13031
13032 load_body->iseq_encoded = code;
13033 load_body->iseq_size = iseq_size;
13034
13035 iseq_bits_t * mark_offset_bits;
13036 if (ISEQ_MBITS_BUFLEN(iseq_size) == 1) {
13037 load_body->mark_bits.single = 0;
13038 mark_offset_bits = &load_body->mark_bits.single;
13039 }
13040 else {
13041 load_body->mark_bits.list = ZALLOC_N(iseq_bits_t, ISEQ_MBITS_BUFLEN(iseq_size));
13042 mark_offset_bits = load_body->mark_bits.list;
13043 }
13044 bool needs_bitmap = false;
13045
13046 for (code_index=0; code_index<iseq_size;) {
13047 /* opcode */
13048 const VALUE insn = code[code_index] = ibf_load_small_value(load, &reading_pos);
13049 const char *types = insn_op_types(insn);
13050 int op_index;
13051
13052 code_index++;
13053
13054 /* operands */
13055 for (op_index=0; types[op_index]; op_index++, code_index++) {
13056 const char operand_type = types[op_index];
13057 switch (operand_type) {
13058 case TS_VALUE:
13059 {
13060 VALUE op = ibf_load_small_value(load, &reading_pos);
13061 VALUE v = ibf_load_object(load, op);
13062 code[code_index] = v;
13063 if (!SPECIAL_CONST_P(v)) {
13064 RB_OBJ_WRITTEN(iseqv, Qundef, v);
13065 ISEQ_MBITS_SET(mark_offset_bits, code_index);
13066 needs_bitmap = true;
13067 }
13068 break;
13069 }
13070 case TS_CDHASH:
13071 {
13072 VALUE op = ibf_load_small_value(load, &reading_pos);
13073 VALUE src = ibf_load_object(load, op);
13074 VALUE v = rb_hash_new_with_size_and_type(0, RHASH_SIZE(src), &cdhash_type);
13075 rb_hash_rehash(cdhash_copy(v, src));
13076 RB_OBJ_SET_SHAREABLE(v);
13077
13078 // Overwrite the existing hash in the object list. This
13079 // is to keep the object alive during load time.
13080 // [Bug #17984] [ruby-core:104259]
13081 pinned_list_store(load->current_buffer->obj_list, (long)op, v);
13082
13083 code[code_index] = v;
13084 ISEQ_MBITS_SET(mark_offset_bits, code_index);
13085 RB_OBJ_WRITTEN(iseqv, Qundef, v);
13086 needs_bitmap = true;
13087 break;
13088 }
13089 case TS_ISEQ:
13090 {
13091 VALUE op = (VALUE)ibf_load_small_value(load, &reading_pos);
13092 VALUE v = (VALUE)ibf_load_iseq(load, (const rb_iseq_t *)op);
13093 code[code_index] = v;
13094 if (!SPECIAL_CONST_P(v)) {
13095 RB_OBJ_WRITTEN(iseqv, Qundef, v);
13096 ISEQ_MBITS_SET(mark_offset_bits, code_index);
13097 needs_bitmap = true;
13098 }
13099 break;
13100 }
13101 case TS_IC:
13102 {
13103 VALUE op = ibf_load_small_value(load, &reading_pos);
13104 VALUE arr = ibf_load_object(load, op);
13105
13106 IC ic = &ISEQ_IS_IC_ENTRY(load_body, ic_index++);
13107 ic->segments = array_to_idlist(arr);
13108
13109 code[code_index] = (VALUE)ic;
13110 }
13111 break;
13112 case TS_ISE:
13113 case TS_ICVARC:
13114 case TS_IVC:
13115 {
13116 unsigned int op = (unsigned int)ibf_load_small_value(load, &reading_pos);
13117
13118 ISE ic = ISEQ_IS_ENTRY_START(load_body, operand_type) + op;
13119 code[code_index] = (VALUE)ic;
13120
13121 if (operand_type == TS_IVC) {
13122 IVC cache = (IVC)ic;
13123
13124 if (insn == BIN(setinstancevariable)) {
13125 ID iv_name = (ID)code[code_index - 1];
13126 cache->iv_set_name = iv_name;
13127 cache->value = IVAR_CACHE_INIT;
13128 }
13129 else {
13130 cache->iv_set_name = 0;
13131 cache->value = rb_getivar_cache_pack(ROOT_SHAPE_ID, ATTR_INDEX_NOT_SET);
13132 }
13133 }
13134
13135 }
13136 break;
13137 case TS_CALLDATA:
13138 {
13139 code[code_index] = (VALUE)cd_entries++;
13140 }
13141 break;
13142 case TS_ID:
13143 {
13144 VALUE op = ibf_load_small_value(load, &reading_pos);
13145 code[code_index] = ibf_load_id(load, (ID)(VALUE)op);
13146 }
13147 break;
13148 case TS_FUNCPTR:
13149 rb_raise(rb_eRuntimeError, "TS_FUNCPTR is not supported");
13150 break;
13151 case TS_BUILTIN:
13152 code[code_index] = (VALUE)ibf_load_builtin(load, &reading_pos);
13153 break;
13154 default:
13155 code[code_index] = ibf_load_small_value(load, &reading_pos);
13156 continue;
13157 }
13158 }
13159 if (insn_len(insn) != op_index+1) {
13160 rb_raise(rb_eRuntimeError, "operand size mismatch");
13161 }
13162 }
13163
13164 if (!needs_bitmap) {
13165 SIZED_FREE_N(load_body->mark_bits.list, ISEQ_MBITS_BUFLEN(iseq_size));
13166 load_body->mark_bits.list = NULL;
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 = header->frozen ? rb_hash_alloc_fixed_size(rb_cHash, len) : 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
14350 if (header->internal) rb_obj_hide(obj);
14351 if (header->frozen) {
14352 RB_OBJ_SET_FROZEN_SHAREABLE(obj);
14353 }
14354
14355 return obj;
14356}
14357
14358static void
14359ibf_dump_object_struct(struct ibf_dump *dump, VALUE obj)
14360{
14361 if (rb_obj_is_kind_of(obj, rb_cRange)) {
14362 struct ibf_object_struct_range range;
14363 VALUE beg, end;
14364 IBF_ZERO(range);
14365 range.len = 3;
14366 range.class_index = 0;
14367
14368 rb_range_values(obj, &beg, &end, &range.excl);
14369 range.beg = (long)ibf_dump_object(dump, beg);
14370 range.end = (long)ibf_dump_object(dump, end);
14371
14372 IBF_W_ALIGN(struct ibf_object_struct_range);
14373 IBF_WV(range);
14374 }
14375 else {
14376 rb_raise(rb_eNotImpError, "ibf_dump_object_struct: unsupported class %"PRIsVALUE,
14377 rb_class_name(CLASS_OF(obj)));
14378 }
14379}
14380
14381static VALUE
14382ibf_load_object_struct(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14383{
14384 const struct ibf_object_struct_range *range = IBF_OBJBODY(struct ibf_object_struct_range, offset);
14385 VALUE beg = ibf_load_object(load, range->beg);
14386 VALUE end = ibf_load_object(load, range->end);
14387 VALUE obj = rb_range_new(beg, end, range->excl);
14388 if (header->internal) rb_obj_hide(obj);
14389 if (header->frozen) RB_OBJ_SET_FROZEN_SHAREABLE(obj);
14390 return obj;
14391}
14392
14393static void
14394ibf_dump_object_bignum(struct ibf_dump *dump, VALUE obj)
14395{
14396 ssize_t len = BIGNUM_LEN(obj);
14397 ssize_t slen = BIGNUM_SIGN(obj) > 0 ? len : len * -1;
14398 BDIGIT *d = BIGNUM_DIGITS(obj);
14399
14400 (void)IBF_W(&slen, ssize_t, 1);
14401 IBF_WP(d, BDIGIT, len);
14402}
14403
14404static VALUE
14405ibf_load_object_bignum(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14406{
14407 const struct ibf_object_bignum *bignum = IBF_OBJBODY(struct ibf_object_bignum, offset);
14408 int sign = bignum->slen > 0;
14409 ssize_t len = sign > 0 ? bignum->slen : -1 * bignum->slen;
14410 const int big_unpack_flags = /* c.f. rb_big_unpack() */
14413 VALUE obj = rb_integer_unpack(bignum->digits, len, sizeof(BDIGIT), 0,
14414 big_unpack_flags |
14415 (sign == 0 ? INTEGER_PACK_NEGATIVE : 0));
14416 if (header->internal) rb_obj_hide(obj);
14417 if (header->frozen) RB_OBJ_SET_FROZEN_SHAREABLE(obj);
14418 return obj;
14419}
14420
14421static void
14422ibf_dump_object_data(struct ibf_dump *dump, VALUE obj)
14423{
14424 if (rb_data_is_encoding(obj)) {
14425 rb_encoding *enc = rb_to_encoding(obj);
14426 const char *name = rb_enc_name(enc);
14427 long len = strlen(name) + 1;
14428 long data[2];
14429 data[0] = IBF_OBJECT_DATA_ENCODING;
14430 data[1] = len;
14431 (void)IBF_W(data, long, 2);
14432 IBF_WP(name, char, len);
14433 }
14434 else {
14435 ibf_dump_object_unsupported(dump, obj);
14436 }
14437}
14438
14439static VALUE
14440ibf_load_object_data(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14441{
14442 const long *body = IBF_OBJBODY(long, offset);
14443 const enum ibf_object_data_type type = (enum ibf_object_data_type)body[0];
14444 /* const long len = body[1]; */
14445 const char *data = (const char *)&body[2];
14446
14447 switch (type) {
14448 case IBF_OBJECT_DATA_ENCODING:
14449 {
14450 VALUE encobj = rb_enc_from_encoding(rb_enc_find(data));
14451 return encobj;
14452 }
14453 }
14454
14455 return ibf_load_object_unsupported(load, header, offset);
14456}
14457
14458static void
14459ibf_dump_object_complex_rational(struct ibf_dump *dump, VALUE obj)
14460{
14461 long data[2];
14462 data[0] = (long)ibf_dump_object(dump, RCOMPLEX(obj)->real);
14463 data[1] = (long)ibf_dump_object(dump, RCOMPLEX(obj)->imag);
14464
14465 (void)IBF_W(data, long, 2);
14466}
14467
14468static VALUE
14469ibf_load_object_complex_rational(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14470{
14471 const struct ibf_object_complex_rational *nums = IBF_OBJBODY(struct ibf_object_complex_rational, offset);
14472 VALUE a = ibf_load_object(load, nums->a);
14473 VALUE b = ibf_load_object(load, nums->b);
14474 VALUE obj = header->type == T_COMPLEX ?
14475 rb_complex_new(a, b) : rb_rational_new(a, b);
14476
14477 if (header->internal) rb_obj_hide(obj);
14478 if (header->frozen) rb_ractor_make_shareable(rb_obj_freeze(obj));
14479 return obj;
14480}
14481
14482static void
14483ibf_dump_object_symbol(struct ibf_dump *dump, VALUE obj)
14484{
14485 ibf_dump_object_string(dump, rb_sym2str(obj));
14486}
14487
14488static VALUE
14489ibf_load_object_symbol(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
14490{
14491 ibf_offset_t reading_pos = offset;
14492
14493 int encindex = (int)ibf_load_small_value(load, &reading_pos);
14494 const long len = (long)ibf_load_small_value(load, &reading_pos);
14495 const char *ptr = load->current_buffer->buff + reading_pos;
14496
14497 if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
14498 VALUE enc_name_str = ibf_load_object(load, encindex - RUBY_ENCINDEX_BUILTIN_MAX);
14499 encindex = rb_enc_find_index(RSTRING_PTR(enc_name_str));
14500 }
14501
14502 ID id = rb_intern3(ptr, len, rb_enc_from_index(encindex));
14503 return ID2SYM(id);
14504}
14505
14506typedef void (*ibf_dump_object_function)(struct ibf_dump *dump, VALUE obj);
14507static const ibf_dump_object_function dump_object_functions[RUBY_T_MASK+1] = {
14508 ibf_dump_object_unsupported, /* T_NONE */
14509 ibf_dump_object_unsupported, /* T_OBJECT */
14510 ibf_dump_object_class, /* T_CLASS */
14511 ibf_dump_object_unsupported, /* T_MODULE */
14512 ibf_dump_object_float, /* T_FLOAT */
14513 ibf_dump_object_string, /* T_STRING */
14514 ibf_dump_object_regexp, /* T_REGEXP */
14515 ibf_dump_object_array, /* T_ARRAY */
14516 ibf_dump_object_hash, /* T_HASH */
14517 ibf_dump_object_struct, /* T_STRUCT */
14518 ibf_dump_object_bignum, /* T_BIGNUM */
14519 ibf_dump_object_unsupported, /* T_FILE */
14520 ibf_dump_object_data, /* T_DATA */
14521 ibf_dump_object_unsupported, /* T_MATCH */
14522 ibf_dump_object_complex_rational, /* T_COMPLEX */
14523 ibf_dump_object_complex_rational, /* T_RATIONAL */
14524 ibf_dump_object_unsupported, /* 0x10 */
14525 ibf_dump_object_unsupported, /* 0x11 T_NIL */
14526 ibf_dump_object_unsupported, /* 0x12 T_TRUE */
14527 ibf_dump_object_unsupported, /* 0x13 T_FALSE */
14528 ibf_dump_object_symbol, /* 0x14 T_SYMBOL */
14529 ibf_dump_object_unsupported, /* T_FIXNUM */
14530 ibf_dump_object_unsupported, /* T_UNDEF */
14531 ibf_dump_object_unsupported, /* 0x17 */
14532 ibf_dump_object_unsupported, /* 0x18 */
14533 ibf_dump_object_unsupported, /* 0x19 */
14534 ibf_dump_object_unsupported, /* T_IMEMO 0x1a */
14535 ibf_dump_object_unsupported, /* T_NODE 0x1b */
14536 ibf_dump_object_unsupported, /* T_ICLASS 0x1c */
14537 ibf_dump_object_unsupported, /* T_ZOMBIE 0x1d */
14538 ibf_dump_object_unsupported, /* 0x1e */
14539 ibf_dump_object_unsupported, /* 0x1f */
14540};
14541
14542static void
14543ibf_dump_object_object_header(struct ibf_dump *dump, const struct ibf_object_header header)
14544{
14545 unsigned char byte =
14546 (header.type << 0) |
14547 (header.special_const << 5) |
14548 (header.frozen << 6) |
14549 (header.internal << 7);
14550
14551 IBF_WV(byte);
14552}
14553
14554static struct ibf_object_header
14555ibf_load_object_object_header(const struct ibf_load *load, ibf_offset_t *offset)
14556{
14557 unsigned char byte = ibf_load_byte(load, offset);
14558
14559 struct ibf_object_header header;
14560 header.type = (byte >> 0) & 0x1f;
14561 header.special_const = (byte >> 5) & 0x01;
14562 header.frozen = (byte >> 6) & 0x01;
14563 header.internal = (byte >> 7) & 0x01;
14564
14565 return header;
14566}
14567
14568static ibf_offset_t
14569ibf_dump_object_object(struct ibf_dump *dump, VALUE obj)
14570{
14571 struct ibf_object_header obj_header;
14572 ibf_offset_t current_offset;
14573 IBF_ZERO(obj_header);
14574 obj_header.type = TYPE(obj);
14575
14576 IBF_W_ALIGN(ibf_offset_t);
14577 current_offset = ibf_dump_pos(dump);
14578
14579 if (SPECIAL_CONST_P(obj) &&
14580 ! (SYMBOL_P(obj) ||
14581 RB_FLOAT_TYPE_P(obj))) {
14582 obj_header.special_const = TRUE;
14583 obj_header.frozen = TRUE;
14584 obj_header.internal = TRUE;
14585 ibf_dump_object_object_header(dump, obj_header);
14586 ibf_dump_write_small_value(dump, obj);
14587 }
14588 else {
14589 obj_header.internal = SPECIAL_CONST_P(obj) ? FALSE : (RBASIC_CLASS(obj) == 0) ? TRUE : FALSE;
14590 obj_header.special_const = FALSE;
14591 obj_header.frozen = OBJ_FROZEN(obj) ? TRUE : FALSE;
14592 ibf_dump_object_object_header(dump, obj_header);
14593 (*dump_object_functions[obj_header.type])(dump, obj);
14594 }
14595
14596 return current_offset;
14597}
14598
14599typedef VALUE (*ibf_load_object_function)(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset);
14600static const ibf_load_object_function load_object_functions[RUBY_T_MASK+1] = {
14601 ibf_load_object_unsupported, /* T_NONE */
14602 ibf_load_object_unsupported, /* T_OBJECT */
14603 ibf_load_object_class, /* T_CLASS */
14604 ibf_load_object_unsupported, /* T_MODULE */
14605 ibf_load_object_float, /* T_FLOAT */
14606 ibf_load_object_string, /* T_STRING */
14607 ibf_load_object_regexp, /* T_REGEXP */
14608 ibf_load_object_array, /* T_ARRAY */
14609 ibf_load_object_hash, /* T_HASH */
14610 ibf_load_object_struct, /* T_STRUCT */
14611 ibf_load_object_bignum, /* T_BIGNUM */
14612 ibf_load_object_unsupported, /* T_FILE */
14613 ibf_load_object_data, /* T_DATA */
14614 ibf_load_object_unsupported, /* T_MATCH */
14615 ibf_load_object_complex_rational, /* T_COMPLEX */
14616 ibf_load_object_complex_rational, /* T_RATIONAL */
14617 ibf_load_object_unsupported, /* 0x10 */
14618 ibf_load_object_unsupported, /* T_NIL */
14619 ibf_load_object_unsupported, /* T_TRUE */
14620 ibf_load_object_unsupported, /* T_FALSE */
14621 ibf_load_object_symbol,
14622 ibf_load_object_unsupported, /* T_FIXNUM */
14623 ibf_load_object_unsupported, /* T_UNDEF */
14624 ibf_load_object_unsupported, /* 0x17 */
14625 ibf_load_object_unsupported, /* 0x18 */
14626 ibf_load_object_unsupported, /* 0x19 */
14627 ibf_load_object_unsupported, /* T_IMEMO 0x1a */
14628 ibf_load_object_unsupported, /* T_NODE 0x1b */
14629 ibf_load_object_unsupported, /* T_ICLASS 0x1c */
14630 ibf_load_object_unsupported, /* T_ZOMBIE 0x1d */
14631 ibf_load_object_unsupported, /* 0x1e */
14632 ibf_load_object_unsupported, /* 0x1f */
14633};
14634
14635static VALUE
14636ibf_load_object(const struct ibf_load *load, VALUE object_index)
14637{
14638 if (object_index == 0) {
14639 return Qnil;
14640 }
14641 else {
14642 VALUE obj = pinned_list_fetch(load->current_buffer->obj_list, (long)object_index);
14643 if (!obj) {
14644 ibf_offset_t *offsets = (ibf_offset_t *)(load->current_buffer->obj_list_offset + load->current_buffer->buff);
14645 ibf_offset_t offset = offsets[object_index];
14646 const struct ibf_object_header header = ibf_load_object_object_header(load, &offset);
14647
14648#if IBF_ISEQ_DEBUG
14649 fprintf(stderr, "ibf_load_object: list=%#x offsets=%p offset=%#x\n",
14650 load->current_buffer->obj_list_offset, (void *)offsets, offset);
14651 fprintf(stderr, "ibf_load_object: type=%#x special=%d frozen=%d internal=%d\n",
14652 header.type, header.special_const, header.frozen, header.internal);
14653#endif
14654 if (offset >= load->current_buffer->size) {
14655 rb_raise(rb_eIndexError, "object offset out of range: %u", offset);
14656 }
14657
14658 if (header.special_const) {
14659 ibf_offset_t reading_pos = offset;
14660
14661 obj = ibf_load_small_value(load, &reading_pos);
14662 }
14663 else {
14664 obj = (*load_object_functions[header.type])(load, &header, offset);
14665 }
14666
14667 pinned_list_store(load->current_buffer->obj_list, (long)object_index, obj);
14668 }
14669#if IBF_ISEQ_DEBUG
14670 fprintf(stderr, "ibf_load_object: index=%#"PRIxVALUE" obj=%#"PRIxVALUE"\n",
14671 object_index, obj);
14672#endif
14673 return obj;
14674 }
14675}
14676
14678{
14679 struct ibf_dump *dump;
14680 VALUE offset_list;
14681};
14682
14683static int
14684ibf_dump_object_list_i(st_data_t key, st_data_t val, st_data_t ptr)
14685{
14686 VALUE obj = (VALUE)key;
14687 struct ibf_dump_object_list_arg *args = (struct ibf_dump_object_list_arg *)ptr;
14688
14689 ibf_offset_t offset = ibf_dump_object_object(args->dump, obj);
14690 rb_ary_push(args->offset_list, UINT2NUM(offset));
14691
14692 return ST_CONTINUE;
14693}
14694
14695static void
14696ibf_dump_object_list(struct ibf_dump *dump, ibf_offset_t *obj_list_offset, unsigned int *obj_list_size)
14697{
14698 st_table *obj_table = dump->current_buffer->obj_table;
14699 VALUE offset_list = rb_ary_hidden_new(obj_table->num_entries);
14700
14701 struct ibf_dump_object_list_arg args;
14702 args.dump = dump;
14703 args.offset_list = offset_list;
14704
14705 st_foreach(obj_table, ibf_dump_object_list_i, (st_data_t)&args);
14706
14707 IBF_W_ALIGN(ibf_offset_t);
14708 *obj_list_offset = ibf_dump_pos(dump);
14709
14710 st_index_t size = obj_table->num_entries;
14711 st_index_t i;
14712
14713 for (i=0; i<size; i++) {
14714 ibf_offset_t offset = NUM2UINT(RARRAY_AREF(offset_list, i));
14715 IBF_WV(offset);
14716 }
14717
14718 *obj_list_size = (unsigned int)size;
14719}
14720
14721static void
14722ibf_dump_mark(void *ptr)
14723{
14724 struct ibf_dump *dump = (struct ibf_dump *)ptr;
14725 rb_gc_mark(dump->global_buffer.str);
14726
14727 rb_mark_set(dump->global_buffer.obj_table);
14728 rb_mark_set(dump->iseq_table);
14729}
14730
14731static void
14732ibf_dump_free(void *ptr)
14733{
14734 struct ibf_dump *dump = (struct ibf_dump *)ptr;
14735 if (dump->global_buffer.obj_table) {
14736 st_free_table(dump->global_buffer.obj_table);
14737 dump->global_buffer.obj_table = 0;
14738 }
14739 if (dump->iseq_table) {
14740 st_free_table(dump->iseq_table);
14741 dump->iseq_table = 0;
14742 }
14743}
14744
14745static size_t
14746ibf_dump_memsize(const void *ptr)
14747{
14748 struct ibf_dump *dump = (struct ibf_dump *)ptr;
14749 size_t size = 0;
14750 if (dump->iseq_table) size += st_memsize(dump->iseq_table);
14751 if (dump->global_buffer.obj_table) size += st_memsize(dump->global_buffer.obj_table);
14752 return size;
14753}
14754
14755static const rb_data_type_t ibf_dump_type = {
14756 "ibf_dump",
14757 {ibf_dump_mark, ibf_dump_free, ibf_dump_memsize,},
14758 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE
14759};
14760
14761static void
14762ibf_dump_setup(struct ibf_dump *dump, VALUE dumper_obj)
14763{
14764 dump->global_buffer.obj_table = NULL; // GC may run before a value is assigned
14765 dump->iseq_table = NULL;
14766
14767 RB_OBJ_WRITE(dumper_obj, &dump->global_buffer.str, rb_str_new(0, 0));
14768 dump->global_buffer.obj_table = ibf_dump_object_table_new();
14769 dump->iseq_table = st_init_numtable(); /* need free */
14770
14771 dump->current_buffer = &dump->global_buffer;
14772}
14773
14774VALUE
14775rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt)
14776{
14777 struct ibf_dump *dump;
14778 struct ibf_header header = {{0}};
14779 VALUE dump_obj;
14780 VALUE str;
14781
14782 if (ISEQ_BODY(iseq)->parent_iseq != NULL ||
14783 ISEQ_BODY(iseq)->local_iseq != iseq) {
14784 rb_raise(rb_eRuntimeError, "should be top of iseq");
14785 }
14786 if (RTEST(ISEQ_COVERAGE(iseq))) {
14787 rb_raise(rb_eRuntimeError, "should not compile with coverage");
14788 }
14789
14790 dump_obj = TypedData_Make_Struct(0, struct ibf_dump, &ibf_dump_type, dump);
14791 ibf_dump_setup(dump, dump_obj);
14792
14793 ibf_dump_write(dump, &header, sizeof(header));
14794 ibf_dump_iseq(dump, iseq);
14795
14796 header.magic[0] = 'Y'; /* YARB */
14797 header.magic[1] = 'A';
14798 header.magic[2] = 'R';
14799 header.magic[3] = 'B';
14800 header.major_version = IBF_MAJOR_VERSION;
14801 header.minor_version = IBF_MINOR_VERSION;
14802 header.endian = IBF_ENDIAN_MARK;
14803 header.wordsize = (uint8_t)SIZEOF_VALUE;
14804 ibf_dump_iseq_list(dump, &header);
14805 ibf_dump_object_list(dump, &header.global_object_list_offset, &header.global_object_list_size);
14806 header.size = ibf_dump_pos(dump);
14807
14808 if (RTEST(opt)) {
14809 VALUE opt_str = opt;
14810 const char *ptr = StringValuePtr(opt_str);
14811 header.extra_size = RSTRING_LENINT(opt_str);
14812 ibf_dump_write(dump, ptr, header.extra_size);
14813 }
14814 else {
14815 header.extra_size = 0;
14816 }
14817
14818 ibf_dump_overwrite(dump, &header, sizeof(header), 0);
14819
14820 str = dump->global_buffer.str;
14821 RB_GC_GUARD(dump_obj);
14822 return str;
14823}
14824
14825static const ibf_offset_t *
14826ibf_iseq_list(const struct ibf_load *load)
14827{
14828 return (const ibf_offset_t *)(load->global_buffer.buff + load->header->iseq_list_offset);
14829}
14830
14831void
14832rb_ibf_load_iseq_complete(rb_iseq_t *iseq)
14833{
14834 struct ibf_load *load = RTYPEDDATA_DATA(iseq->aux.loader.obj);
14835 rb_iseq_t *prev_src_iseq = load->iseq;
14836 ibf_offset_t offset = ibf_iseq_list(load)[iseq->aux.loader.index];
14837 load->iseq = iseq;
14838#if IBF_ISEQ_DEBUG
14839 fprintf(stderr, "rb_ibf_load_iseq_complete: index=%#x offset=%#x size=%#x\n",
14840 iseq->aux.loader.index, offset,
14841 load->header->size);
14842#endif
14843 ibf_load_iseq_each(load, iseq, offset);
14844 ISEQ_COMPILE_DATA_CLEAR(iseq);
14845 FL_UNSET((VALUE)iseq, ISEQ_NOT_LOADED_YET);
14846 rb_iseq_init_trace(iseq);
14847 load->iseq = prev_src_iseq;
14848}
14849
14850#if USE_LAZY_LOAD
14851const rb_iseq_t *
14852rb_iseq_complete(const rb_iseq_t *iseq)
14853{
14854 rb_ibf_load_iseq_complete((rb_iseq_t *)iseq);
14855 return iseq;
14856}
14857#endif
14858
14859static rb_iseq_t *
14860ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq)
14861{
14862 int iseq_index = (int)(VALUE)index_iseq;
14863
14864#if IBF_ISEQ_DEBUG
14865 fprintf(stderr, "ibf_load_iseq: index_iseq=%p iseq_list=%p\n",
14866 (void *)index_iseq, (void *)load->iseq_list);
14867#endif
14868 if (iseq_index == -1) {
14869 return NULL;
14870 }
14871 else {
14872 VALUE iseqv = pinned_list_fetch(load->iseq_list, iseq_index);
14873
14874#if IBF_ISEQ_DEBUG
14875 fprintf(stderr, "ibf_load_iseq: iseqv=%p\n", (void *)iseqv);
14876#endif
14877 if (iseqv) {
14878 return (rb_iseq_t *)iseqv;
14879 }
14880 else {
14881 rb_iseq_t *iseq = iseq_imemo_alloc();
14882#if IBF_ISEQ_DEBUG
14883 fprintf(stderr, "ibf_load_iseq: new iseq=%p\n", (void *)iseq);
14884#endif
14885 FL_SET((VALUE)iseq, ISEQ_NOT_LOADED_YET);
14886 iseq->aux.loader.obj = load->loader_obj;
14887 iseq->aux.loader.index = iseq_index;
14888#if IBF_ISEQ_DEBUG
14889 fprintf(stderr, "ibf_load_iseq: iseq=%p loader_obj=%p index=%d\n",
14890 (void *)iseq, (void *)load->loader_obj, iseq_index);
14891#endif
14892 pinned_list_store(load->iseq_list, iseq_index, (VALUE)iseq);
14893
14894 if (!USE_LAZY_LOAD || GET_VM()->builtin_function_table) {
14895#if IBF_ISEQ_DEBUG
14896 fprintf(stderr, "ibf_load_iseq: loading iseq=%p\n", (void *)iseq);
14897#endif
14898 rb_ibf_load_iseq_complete(iseq);
14899 }
14900
14901#if IBF_ISEQ_DEBUG
14902 fprintf(stderr, "ibf_load_iseq: iseq=%p loaded %p\n",
14903 (void *)iseq, (void *)load->iseq);
14904#endif
14905 return iseq;
14906 }
14907 }
14908}
14909
14910static void
14911ibf_load_setup_bytes(struct ibf_load *load, VALUE loader_obj, const char *bytes, size_t size)
14912{
14913 struct ibf_header *header = (struct ibf_header *)bytes;
14914 load->loader_obj = loader_obj;
14915 load->global_buffer.buff = bytes;
14916 load->header = header;
14917 load->global_buffer.size = header->size;
14918 load->global_buffer.obj_list_offset = header->global_object_list_offset;
14919 load->global_buffer.obj_list_size = header->global_object_list_size;
14920 RB_OBJ_WRITE(loader_obj, &load->iseq_list, pinned_list_new(header->iseq_list_size));
14921 RB_OBJ_WRITE(loader_obj, &load->global_buffer.obj_list, pinned_list_new(load->global_buffer.obj_list_size));
14922 load->iseq = NULL;
14923
14924 load->current_buffer = &load->global_buffer;
14925
14926 if (size < header->size) {
14927 rb_raise(rb_eRuntimeError, "broken binary format");
14928 }
14929 if (strncmp(header->magic, "YARB", 4) != 0) {
14930 rb_raise(rb_eRuntimeError, "unknown binary format");
14931 }
14932 if (header->major_version != IBF_MAJOR_VERSION ||
14933 header->minor_version != IBF_MINOR_VERSION) {
14934 rb_raise(rb_eRuntimeError, "unmatched version file (%u.%u for %u.%u)",
14935 header->major_version, header->minor_version, IBF_MAJOR_VERSION, IBF_MINOR_VERSION);
14936 }
14937 if (header->endian != IBF_ENDIAN_MARK) {
14938 rb_raise(rb_eRuntimeError, "unmatched endian: %c", header->endian);
14939 }
14940 if (header->wordsize != SIZEOF_VALUE) {
14941 rb_raise(rb_eRuntimeError, "unmatched word size: %d", header->wordsize);
14942 }
14943 if (header->iseq_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
14944 rb_raise(rb_eArgError, "unaligned iseq list offset: %u",
14945 header->iseq_list_offset);
14946 }
14947 if (load->global_buffer.obj_list_offset % RUBY_ALIGNOF(ibf_offset_t)) {
14948 rb_raise(rb_eArgError, "unaligned object list offset: %u",
14949 load->global_buffer.obj_list_offset);
14950 }
14951}
14952
14953static void
14954ibf_load_setup(struct ibf_load *load, VALUE loader_obj, VALUE str)
14955{
14956 StringValue(str);
14957
14958 if (RSTRING_LENINT(str) < (int)sizeof(struct ibf_header)) {
14959 rb_raise(rb_eRuntimeError, "broken binary format");
14960 }
14961
14962 if (USE_LAZY_LOAD) {
14963 str = rb_str_new(RSTRING_PTR(str), RSTRING_LEN(str));
14964 }
14965
14966 ibf_load_setup_bytes(load, loader_obj, RSTRING_PTR(str), RSTRING_LEN(str));
14967 RB_OBJ_WRITE(loader_obj, &load->str, str);
14968}
14969
14970static void
14971ibf_loader_mark(void *ptr)
14972{
14973 struct ibf_load *load = (struct ibf_load *)ptr;
14974 rb_gc_mark(load->str);
14975 rb_gc_mark(load->iseq_list);
14976 rb_gc_mark(load->global_buffer.obj_list);
14977}
14978
14979static void
14980ibf_loader_free(void *ptr)
14981{
14982 struct ibf_load *load = (struct ibf_load *)ptr;
14983 SIZED_FREE(load);
14984}
14985
14986static size_t
14987ibf_loader_memsize(const void *ptr)
14988{
14989 return sizeof(struct ibf_load);
14990}
14991
14992static const rb_data_type_t ibf_load_type = {
14993 "ibf_loader",
14994 {ibf_loader_mark, ibf_loader_free, ibf_loader_memsize,},
14995 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
14996};
14997
14998const rb_iseq_t *
14999rb_iseq_ibf_load(VALUE str)
15000{
15001 struct ibf_load *load;
15002 rb_iseq_t *iseq;
15003 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
15004
15005 ibf_load_setup(load, loader_obj, str);
15006 iseq = ibf_load_iseq(load, 0);
15007
15008 RB_GC_GUARD(loader_obj);
15009 return iseq;
15010}
15011
15012const rb_iseq_t *
15013rb_iseq_ibf_load_bytes(const char *bytes, size_t size)
15014{
15015 struct ibf_load *load;
15016 rb_iseq_t *iseq;
15017 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
15018
15019 ibf_load_setup_bytes(load, loader_obj, bytes, size);
15020 iseq = ibf_load_iseq(load, 0);
15021
15022 RB_GC_GUARD(loader_obj);
15023 return iseq;
15024}
15025
15026VALUE
15027rb_iseq_ibf_load_extra_data(VALUE str)
15028{
15029 struct ibf_load *load;
15030 VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
15031 VALUE extra_str;
15032
15033 ibf_load_setup(load, loader_obj, str);
15034 extra_str = rb_str_new(load->global_buffer.buff + load->header->size, load->header->extra_size);
15035 RB_GC_GUARD(loader_obj);
15036 return extra_str;
15037}
15038
15039#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:657
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:894
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1313
#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:468
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:456
#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:33
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:550
#define INTEGER_PACK_NEGATIVE
Interprets the input as a signed negative number (unpack only).
Definition bignum.h:568
#define INTEGER_PACK_LSWORD_FIRST
Stores/interprets the least significant word as the first word.
Definition bignum.h:532
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:1984
int rb_reg_options(VALUE re)
Queries the options of the passed regular expression.
Definition re.c:4360
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:3836
VALUE rb_str_tmp_new(long len)
Allocates a "temporary" string.
Definition string.c:1748
int rb_str_hash_cmp(VALUE str1, VALUE str2)
Compares two strings.
Definition string.c:4199
#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:4185
VALUE rb_str_cat(VALUE dst, const char *src, long srclen)
Destructively appends the passed contents to the string.
Definition string.c:3604
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:3802
int rb_str_cmp(VALUE lhs, VALUE rhs)
Compares two strings, as in strcmp(3).
Definition string.c:4253
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:4073
VALUE rb_str_freeze(VALUE str)
This is the implementation of String#freeze.
Definition string.c:3314
#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:1552
#define DECIMAL_SIZE_OF(expr)
An approximation of decimal representation size.
Definition util.h:48
void ruby_qsort(void *, const size_t, const size_t, int(*)(const void *, const void *, void *), void *)
Reentrant implementation of quick sort.
#define rb_long2int
Just another name of rb_long2int_inline.
Definition long.h:62
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:372
#define ALLOCA_N(type, n)
Definition memory.h:292
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition memory.h:360
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
#define RB_ALLOCV(v, n)
Identical to RB_ALLOCV_N(), except that it allocates a number of bytes and returns a void* .
Definition memory.h:304
VALUE type(ANYARGS)
ANYARGS-ed function type.
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
#define RBIMPL_ATTR_NORETURN()
Wraps (or simulates) [[noreturn]]
Definition noreturn.h:38
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition rarray.h:281
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition rarray.h:386
#define RARRAY_AREF(a, i)
Definition rarray.h:403
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
Definition rarray.h:52
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
Definition rbasic.h:166
#define RUBY_DEFAULT_FREE
This is a value you can set to RData::dfree.
Definition rdata.h:78
void(* RUBY_DATA_FUNC)(void *)
This is the type of callbacks registered to RData.
Definition rdata.h:104
#define RHASH_SIZE(h)
Queries the size of the hash.
Definition rhash.h:69
static VALUE RREGEXP_SRC(VALUE rexp)
Convenient getter function.
Definition rregexp.h:103
#define StringValue(v)
Ensures that the parameter object is a String.
Definition rstring.h:66
#define StringValuePtr(v)
Identical to StringValue, except it returns a char*.
Definition rstring.h:76
static int RSTRING_LENINT(VALUE str)
Identical to RSTRING_LEN(), except it differs for the return type.
Definition rstring.h:438
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
#define RTYPEDDATA_DATA(v)
Convenient getter macro.
Definition rtypeddata.h: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:9064
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