Ruby 4.1.0dev (2026-01-10 revision 0d4538b57d5f835af27d8d29464c32dbdf4593f3)
iseq.c (0d4538b57d5f835af27d8d29464c32dbdf4593f3)
1/**********************************************************************
2
3 iseq.c -
4
5 $Author$
6 created at: 2006-07-11(Tue) 09:00:03 +0900
7
8 Copyright (C) 2006 Koichi Sasada
9
10**********************************************************************/
11
12#define RUBY_VM_INSNS_INFO 1
13/* #define RUBY_MARK_FREE_DEBUG 1 */
14
15#include "ruby/internal/config.h"
16
17#ifdef HAVE_DLADDR
18# include <dlfcn.h>
19#endif
20
21#include "eval_intern.h"
22#include "id.h"
23#include "id_table.h"
24#include "internal.h"
25#include "internal/bits.h"
26#include "internal/class.h"
27#include "internal/compile.h"
28#include "internal/error.h"
29#include "internal/file.h"
30#include "internal/gc.h"
31#include "internal/hash.h"
32#include "internal/io.h"
33#include "internal/ruby_parser.h"
34#include "internal/sanitizers.h"
35#include "internal/set_table.h"
36#include "internal/symbol.h"
37#include "internal/thread.h"
38#include "internal/variable.h"
39#include "iseq.h"
40#include "ruby/util.h"
41#include "vm_core.h"
42#include "ractor_core.h"
43#include "vm_callinfo.h"
44#include "yjit.h"
45#include "ruby/ractor.h"
46#include "builtin.h"
47#include "insns.inc"
48#include "insns_info.inc"
49#include "zjit.h"
50
51VALUE rb_cISeq;
52static VALUE iseqw_new(const rb_iseq_t *iseq);
53static const rb_iseq_t *iseqw_check(VALUE iseqw);
54
55#if VM_INSN_INFO_TABLE_IMPL == 2
56static struct succ_index_table *succ_index_table_create(int max_pos, int *data, int size);
57static unsigned int *succ_index_table_invert(int max_pos, struct succ_index_table *sd, int size);
58static int succ_index_lookup(const struct succ_index_table *sd, int x);
59#endif
60
61#define hidden_obj_p(obj) (!SPECIAL_CONST_P(obj) && !RBASIC(obj)->klass)
62
63static inline VALUE
64obj_resurrect(VALUE obj)
65{
66 if (hidden_obj_p(obj)) {
67 switch (BUILTIN_TYPE(obj)) {
68 case T_STRING:
69 obj = rb_str_resurrect(obj);
70 break;
71 case T_ARRAY:
72 obj = rb_ary_resurrect(obj);
73 break;
74 case T_HASH:
75 obj = rb_hash_resurrect(obj);
76 break;
77 default:
78 break;
79 }
80 }
81 return obj;
82}
83
84static void
85free_arena(struct iseq_compile_data_storage *cur)
86{
87 struct iseq_compile_data_storage *next;
88
89 while (cur) {
90 next = cur->next;
91 ruby_xfree(cur);
92 cur = next;
93 }
94}
95
96static void
97compile_data_free(struct iseq_compile_data *compile_data)
98{
99 if (compile_data) {
100 free_arena(compile_data->node.storage_head);
101 free_arena(compile_data->insn.storage_head);
102 if (compile_data->ivar_cache_table) {
103 rb_id_table_free(compile_data->ivar_cache_table);
104 }
105 ruby_xfree(compile_data);
106 }
107}
108
109static void
110remove_from_constant_cache(ID id, IC ic)
111{
112 rb_vm_t *vm = GET_VM();
113 VALUE lookup_result;
114 st_data_t ic_data = (st_data_t)ic;
115
116 if (rb_id_table_lookup(vm->constant_cache, id, &lookup_result)) {
117 set_table *ics = (set_table *)lookup_result;
118 set_table_delete(ics, &ic_data);
119
120 if (ics->num_entries == 0 &&
121 // See comment in vm_track_constant_cache on why we need this check
122 id != vm->inserting_constant_cache_id) {
123 rb_id_table_delete(vm->constant_cache, id);
124 set_free_table(ics);
125 }
126 }
127}
128
129// When an ISEQ is being freed, all of its associated ICs are going to go away
130// as well. Because of this, we need to iterate over the ICs, and clear them
131// from the VM's constant cache.
132static void
133iseq_clear_ic_references(const rb_iseq_t *iseq)
134{
135 // In some cases (when there is a compilation error), we end up with
136 // ic_size greater than 0, but no allocated is_entries buffer.
137 // If there's no is_entries buffer to loop through, return early.
138 // [Bug #19173]
139 if (!ISEQ_BODY(iseq)->is_entries) {
140 return;
141 }
142
143 for (unsigned int ic_idx = 0; ic_idx < ISEQ_BODY(iseq)->ic_size; ic_idx++) {
144 IC ic = &ISEQ_IS_IC_ENTRY(ISEQ_BODY(iseq), ic_idx);
145
146 // Iterate over the IC's constant path's segments and clean any references to
147 // the ICs out of the VM's constant cache table.
148 const ID *segments = ic->segments;
149
150 // It's possible that segments is NULL if we overallocated an IC but
151 // optimizations removed the instruction using it
152 if (segments == NULL)
153 continue;
154
155 for (int i = 0; segments[i]; i++) {
156 ID id = segments[i];
157 if (id == idNULL) continue;
158 remove_from_constant_cache(id, ic);
159 }
160
161 ruby_xfree((void *)segments);
162 }
163}
164
165
167rb_iseq_local_hooks(const rb_iseq_t *iseq, rb_ractor_t *r, bool create)
168{
169 rb_hook_list_t *hook_list = NULL;
170 st_data_t val;
171 if (st_lookup(rb_ractor_targeted_hooks(r), (st_data_t)iseq, &val)) {
172 hook_list = (rb_hook_list_t*)val;
173 RUBY_ASSERT(hook_list->type == hook_list_type_targeted_iseq);
174 }
175 else if (create) {
176 hook_list = RB_ZALLOC(rb_hook_list_t);
177 hook_list->type = hook_list_type_targeted_iseq;
178 st_insert(rb_ractor_targeted_hooks(r), (st_data_t)iseq, (st_data_t)hook_list);
179 }
180 return hook_list;
181}
182
183void
184rb_iseq_free(const rb_iseq_t *iseq)
185{
186 RUBY_FREE_ENTER("iseq");
187
188 if (iseq && ISEQ_BODY(iseq)) {
189 iseq_clear_ic_references(iseq);
190 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
191#if USE_YJIT
192 rb_yjit_iseq_free(iseq);
193 if (FL_TEST_RAW((VALUE)iseq, ISEQ_TRANSLATED)) {
194 RUBY_ASSERT(rb_yjit_live_iseq_count > 0);
195 rb_yjit_live_iseq_count--;
196 }
197#endif
198#if USE_ZJIT
199 rb_zjit_iseq_free(iseq);
200#endif
201 ruby_xfree((void *)body->iseq_encoded);
202 ruby_xfree((void *)body->insns_info.body);
203 ruby_xfree((void *)body->insns_info.positions);
204#if VM_INSN_INFO_TABLE_IMPL == 2
205 ruby_xfree(body->insns_info.succ_index_table);
206#endif
207 ruby_xfree((void *)body->is_entries);
208 ruby_xfree(body->call_data);
209 ruby_xfree((void *)body->catch_table);
210 ruby_xfree((void *)body->param.opt_table);
211 if (ISEQ_MBITS_BUFLEN(body->iseq_size) > 1 && body->mark_bits.list) {
212 ruby_xfree((void *)body->mark_bits.list);
213 }
214
215 ruby_xfree(body->variable.original_iseq);
216
217 if (body->param.keyword != NULL) {
218 if (body->param.keyword->table != &body->local_table[body->param.keyword->bits_start - body->param.keyword->num])
219 ruby_xfree((void *)body->param.keyword->table);
220 if (body->param.keyword->default_values) {
221 ruby_xfree((void *)body->param.keyword->default_values);
222 }
223 ruby_xfree((void *)body->param.keyword);
224 }
225 if (LIKELY(body->local_table != rb_iseq_shared_exc_local_tbl)) {
226 ruby_xfree((void *)body->local_table);
227 }
228 ruby_xfree((void *)body->lvar_states);
229
230 compile_data_free(ISEQ_COMPILE_DATA(iseq));
231 if (body->outer_variables) rb_id_table_free(body->outer_variables);
232 ruby_xfree(body);
233 }
234
235 RUBY_FREE_LEAVE("iseq");
236}
237
238typedef VALUE iseq_value_itr_t(void *ctx, VALUE obj);
239
240static inline void
241iseq_scan_bits(unsigned int page, iseq_bits_t bits, VALUE *code, VALUE *original_iseq)
242{
243 unsigned int offset;
244 unsigned int page_offset = (page * ISEQ_MBITS_BITLENGTH);
245
246 while (bits) {
247 offset = ntz_intptr(bits);
248 VALUE op = code[page_offset + offset];
249 rb_gc_mark_and_move(&code[page_offset + offset]);
250 VALUE newop = code[page_offset + offset];
251 if (original_iseq && newop != op) {
252 original_iseq[page_offset + offset] = newop;
253 }
254 bits &= bits - 1; // Reset Lowest Set Bit (BLSR)
255 }
256}
257
258static void
259rb_iseq_mark_and_move_each_compile_data_value(const rb_iseq_t *iseq, VALUE *original_iseq)
260{
261 unsigned int size;
262 VALUE *code;
263 const struct iseq_compile_data *const compile_data = ISEQ_COMPILE_DATA(iseq);
264
265 size = compile_data->iseq_size;
266 code = compile_data->iseq_encoded;
267
268 // Embedded VALUEs
269 if (compile_data->mark_bits.list) {
270 if(compile_data->is_single_mark_bit) {
271 iseq_scan_bits(0, compile_data->mark_bits.single, code, original_iseq);
272 }
273 else {
274 for (unsigned int i = 0; i < ISEQ_MBITS_BUFLEN(size); i++) {
275 iseq_bits_t bits = compile_data->mark_bits.list[i];
276 iseq_scan_bits(i, bits, code, original_iseq);
277 }
278 }
279 }
280}
281static void
282rb_iseq_mark_and_move_each_body_value(const rb_iseq_t *iseq, VALUE *original_iseq)
283{
284 unsigned int size;
285 VALUE *code;
286 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
287
288 size = body->iseq_size;
289 code = body->iseq_encoded;
290
291 union iseq_inline_storage_entry *is_entries = body->is_entries;
292
293 if (body->is_entries) {
294 // Skip iterating over ivc caches
295 is_entries += body->ivc_size;
296
297 // ICVARC entries
298 for (unsigned int i = 0; i < body->icvarc_size; i++, is_entries++) {
299 ICVARC icvarc = (ICVARC)is_entries;
300 if (icvarc->entry) {
301 RUBY_ASSERT(!RB_TYPE_P(icvarc->entry->class_value, T_NONE));
302
303 rb_gc_mark_and_move(&icvarc->entry->class_value);
304 }
305 }
306
307 // ISE entries
308 for (unsigned int i = 0; i < body->ise_size; i++, is_entries++) {
309 union iseq_inline_storage_entry *const is = (union iseq_inline_storage_entry *)is_entries;
310 if (is->once.value) {
311 rb_gc_mark_and_move(&is->once.value);
312 }
313 }
314
315 // IC Entries
316 for (unsigned int i = 0; i < body->ic_size; i++, is_entries++) {
317 IC ic = (IC)is_entries;
318 if (ic->entry) {
319 rb_gc_mark_and_move_ptr(&ic->entry);
320 }
321 }
322 }
323
324 // Embedded VALUEs
325 if (body->mark_bits.list) {
326 if (ISEQ_MBITS_BUFLEN(size) == 1) {
327 iseq_scan_bits(0, body->mark_bits.single, code, original_iseq);
328 }
329 else {
330 for (unsigned int i = 0; i < ISEQ_MBITS_BUFLEN(size); i++) {
331 iseq_bits_t bits = body->mark_bits.list[i];
332 iseq_scan_bits(i, bits, code, original_iseq);
333 }
334 }
335 }
336}
337
338static bool
339cc_is_active(const struct rb_callcache *cc, bool reference_updating)
340{
341 if (cc) {
342 if (cc == rb_vm_empty_cc() || rb_vm_empty_cc_for_super()) {
343 return false;
344 }
345
346 if (reference_updating) {
347 cc = (const struct rb_callcache *)rb_gc_location((VALUE)cc);
348 }
349
350 if (vm_cc_markable(cc) && vm_cc_valid(cc)) {
351 const struct rb_callable_method_entry_struct *cme = vm_cc_cme(cc);
352 if (reference_updating) {
353 cme = (const struct rb_callable_method_entry_struct *)rb_gc_location((VALUE)cme);
354 }
355 if (!METHOD_ENTRY_INVALIDATED(cme)) {
356 return true;
357 }
358 }
359 }
360 return false;
361}
362
363void
364rb_iseq_mark_and_move(rb_iseq_t *iseq, bool reference_updating)
365{
366 RUBY_MARK_ENTER("iseq");
367
368 rb_gc_mark_and_move(&iseq->wrapper);
369
370 if (ISEQ_BODY(iseq)) {
371 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
372
373 rb_iseq_mark_and_move_each_body_value(iseq, reference_updating ? ISEQ_ORIGINAL_ISEQ(iseq) : NULL);
374
375 rb_gc_mark_and_move(&body->variable.script_lines);
376 rb_gc_mark_and_move(&body->location.label);
377 rb_gc_mark_and_move(&body->location.base_label);
378 rb_gc_mark_and_move(&body->location.pathobj);
379 if (body->local_iseq) rb_gc_mark_and_move_ptr(&body->local_iseq);
380 if (body->parent_iseq) rb_gc_mark_and_move_ptr(&body->parent_iseq);
381 if (body->mandatory_only_iseq) rb_gc_mark_and_move_ptr(&body->mandatory_only_iseq);
382
383 if (body->call_data) {
384 for (unsigned int i = 0; i < body->ci_size; i++) {
385 struct rb_call_data *cds = body->call_data;
386
387 if (cds[i].ci) rb_gc_mark_and_move_ptr(&cds[i].ci);
388
389 if (cc_is_active(cds[i].cc, reference_updating)) {
390 rb_gc_mark_and_move_ptr(&cds[i].cc);
391 }
392 else if (cds[i].cc != rb_vm_empty_cc()) {
393 cds[i].cc = rb_vm_empty_cc();
394 }
395 }
396 }
397
398 if (body->param.flags.has_kw && body->param.keyword != NULL) {
399 const struct rb_iseq_param_keyword *const keyword = body->param.keyword;
400
401 if (keyword->default_values != NULL) {
402 for (int j = 0, i = keyword->required_num; i < keyword->num; i++, j++) {
403 rb_gc_mark_and_move(&keyword->default_values[j]);
404 }
405 }
406 }
407
408 if (body->catch_table) {
409 struct iseq_catch_table *table = body->catch_table;
410
411 for (unsigned int i = 0; i < table->size; i++) {
412 struct iseq_catch_table_entry *entry;
413 entry = UNALIGNED_MEMBER_PTR(table, entries[i]);
414 if (entry->iseq) {
415 rb_gc_mark_and_move_ptr(&entry->iseq);
416 }
417 }
418 }
419
420 if (reference_updating) {
421#if USE_YJIT
422 rb_yjit_iseq_update_references(iseq);
423#endif
424#if USE_ZJIT
425 rb_zjit_iseq_update_references(body->zjit_payload);
426#endif
427 }
428 else {
429 // TODO: check jit payload
430 if (!rb_gc_checking_shareable()) {
431#if USE_YJIT
432 rb_yjit_iseq_mark(body->yjit_payload);
433#endif
434#if USE_ZJIT
435 rb_zjit_iseq_mark(body->zjit_payload);
436#endif
437 }
438 }
439
440 // TODO: ractor aware coverage
441 if (!rb_gc_checking_shareable()) {
442 rb_gc_mark_and_move(&body->variable.coverage);
443 rb_gc_mark_and_move(&body->variable.pc2branchindex);
444 }
445 }
446
447 if (FL_TEST_RAW((VALUE)iseq, ISEQ_NOT_LOADED_YET)) {
448 if (!rb_gc_checking_shareable()) {
449 rb_gc_mark_and_move(&iseq->aux.loader.obj);
450 }
451 }
452 else if (FL_TEST_RAW((VALUE)iseq, ISEQ_USE_COMPILE_DATA)) {
453 if (!rb_gc_checking_shareable()) {
454 const struct iseq_compile_data *const compile_data = ISEQ_COMPILE_DATA(iseq);
455
456 rb_iseq_mark_and_move_insn_storage(compile_data->insn.storage_head);
457 rb_iseq_mark_and_move_each_compile_data_value(iseq, reference_updating ? ISEQ_ORIGINAL_ISEQ(iseq) : NULL);
458
459 rb_gc_mark_and_move((VALUE *)&compile_data->err_info);
460 rb_gc_mark_and_move((VALUE *)&compile_data->catch_table_ary);
461 }
462 }
463 else {
464 /* executable */
465 VM_ASSERT(ISEQ_EXECUTABLE_P(iseq));
466 }
467
468 RUBY_MARK_LEAVE("iseq");
469}
470
471static size_t
472param_keyword_size(const struct rb_iseq_param_keyword *pkw)
473{
474 size_t size = 0;
475
476 if (!pkw) return size;
477
478 size += sizeof(struct rb_iseq_param_keyword);
479 size += sizeof(VALUE) * (pkw->num - pkw->required_num);
480
481 return size;
482}
483
484size_t
485rb_iseq_memsize(const rb_iseq_t *iseq)
486{
487 size_t size = 0; /* struct already counted as RVALUE size */
488 const struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
489 const struct iseq_compile_data *compile_data;
490
491 /* TODO: should we count original_iseq? */
492
493 if (ISEQ_EXECUTABLE_P(iseq) && body) {
494 size += sizeof(struct rb_iseq_constant_body);
495 size += body->iseq_size * sizeof(VALUE);
496 size += body->insns_info.size * (sizeof(struct iseq_insn_info_entry) + sizeof(unsigned int));
497 size += body->local_table_size * sizeof(ID);
498 size += ISEQ_MBITS_BUFLEN(body->iseq_size) * ISEQ_MBITS_SIZE;
499 if (body->catch_table) {
500 size += iseq_catch_table_bytes(body->catch_table->size);
501 }
502 size += (body->param.opt_num + 1) * sizeof(VALUE);
503 size += param_keyword_size(body->param.keyword);
504
505 /* body->is_entries */
506 size += ISEQ_IS_SIZE(body) * sizeof(union iseq_inline_storage_entry);
507
508 if (ISEQ_BODY(iseq)->is_entries) {
509 /* IC entries constant segments */
510 for (unsigned int ic_idx = 0; ic_idx < body->ic_size; ic_idx++) {
511 IC ic = &ISEQ_IS_IC_ENTRY(body, ic_idx);
512 const ID *ids = ic->segments;
513 if (!ids) continue;
514 while (*ids++) {
515 size += sizeof(ID);
516 }
517 size += sizeof(ID); // null terminator
518 }
519 }
520
521 /* body->call_data */
522 size += body->ci_size * sizeof(struct rb_call_data);
523 // TODO: should we count imemo_callinfo?
524 }
525
526 compile_data = ISEQ_COMPILE_DATA(iseq);
527 if (compile_data) {
528 struct iseq_compile_data_storage *cur;
529
530 size += sizeof(struct iseq_compile_data);
531
532 cur = compile_data->node.storage_head;
533 while (cur) {
534 size += cur->size + offsetof(struct iseq_compile_data_storage, buff);
535 cur = cur->next;
536 }
537 }
538
539 return size;
540}
541
543rb_iseq_constant_body_alloc(void)
544{
545 struct rb_iseq_constant_body *iseq_body;
546 iseq_body = ZALLOC(struct rb_iseq_constant_body);
547 return iseq_body;
548}
549
550static rb_iseq_t *
551iseq_alloc(void)
552{
553 rb_iseq_t *iseq = iseq_imemo_alloc();
554 ISEQ_BODY(iseq) = rb_iseq_constant_body_alloc();
555 return iseq;
556}
557
558VALUE
559rb_iseq_pathobj_new(VALUE path, VALUE realpath)
560{
561 VALUE pathobj;
562 VM_ASSERT(RB_TYPE_P(path, T_STRING));
563 VM_ASSERT(NIL_P(realpath) || RB_TYPE_P(realpath, T_STRING));
564
565 if (path == realpath ||
566 (!NIL_P(realpath) && rb_str_cmp(path, realpath) == 0)) {
567 pathobj = rb_fstring(path);
568 }
569 else {
570 if (!NIL_P(realpath)) {
571 realpath = rb_fstring(realpath);
572 }
573 VALUE fpath = rb_fstring(path);
574
575 pathobj = rb_ary_new_from_args(2, fpath, realpath);
576 rb_ary_freeze(pathobj);
577 RB_OBJ_SET_SHAREABLE(pathobj);
578 }
579 return pathobj;
580}
581
582void
583rb_iseq_pathobj_set(const rb_iseq_t *iseq, VALUE path, VALUE realpath)
584{
585 RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->location.pathobj,
586 rb_iseq_pathobj_new(path, realpath));
587}
588
589// Make a dummy iseq for a dummy frame that exposes a path for profilers to inspect
590rb_iseq_t *
591rb_iseq_alloc_with_dummy_path(VALUE fname)
592{
593 rb_iseq_t *dummy_iseq = iseq_alloc();
594
595 ISEQ_BODY(dummy_iseq)->type = ISEQ_TYPE_TOP;
596
597 if (!RB_OBJ_SHAREABLE_P(fname)) {
598 RB_OBJ_SET_FROZEN_SHAREABLE(fname);
599 }
600
601 RB_OBJ_WRITE(dummy_iseq, &ISEQ_BODY(dummy_iseq)->location.pathobj, fname);
602 RB_OBJ_WRITE(dummy_iseq, &ISEQ_BODY(dummy_iseq)->location.label, fname);
603
604 return dummy_iseq;
605}
606
607static rb_iseq_location_t *
608iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id)
609{
610 rb_iseq_location_t *loc = &ISEQ_BODY(iseq)->location;
611
612 rb_iseq_pathobj_set(iseq, path, realpath);
613 RB_OBJ_WRITE(iseq, &loc->label, name);
614 RB_OBJ_WRITE(iseq, &loc->base_label, name);
615 loc->first_lineno = first_lineno;
616
617 if (ISEQ_BODY(iseq)->local_iseq == iseq && strcmp(RSTRING_PTR(name), "initialize") == 0) {
618 ISEQ_BODY(iseq)->param.flags.use_block = 1;
619 }
620
621 if (code_location) {
622 loc->node_id = node_id;
623 loc->code_location = *code_location;
624 }
625 else {
626 loc->code_location.beg_pos.lineno = 0;
627 loc->code_location.beg_pos.column = 0;
628 loc->code_location.end_pos.lineno = -1;
629 loc->code_location.end_pos.column = -1;
630 }
631
632 return loc;
633}
634
635static void
636set_relation(rb_iseq_t *iseq, const rb_iseq_t *piseq)
637{
638 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
639 const VALUE type = body->type;
640
641 /* set class nest stack */
642 if (type == ISEQ_TYPE_TOP) {
643 body->local_iseq = iseq;
644 }
645 else if (type == ISEQ_TYPE_METHOD || type == ISEQ_TYPE_CLASS) {
646 body->local_iseq = iseq;
647 }
648 else if (piseq) {
649 RB_OBJ_WRITE(iseq, &body->local_iseq, ISEQ_BODY(piseq)->local_iseq);
650 }
651
652 if (piseq) {
653 RB_OBJ_WRITE(iseq, &body->parent_iseq, piseq);
654 }
655
656 if (type == ISEQ_TYPE_MAIN) {
657 body->local_iseq = iseq;
658 }
659}
660
661static struct iseq_compile_data_storage *
662new_arena(void)
663{
664 struct iseq_compile_data_storage * new_arena =
666 ALLOC_N(char, INITIAL_ISEQ_COMPILE_DATA_STORAGE_BUFF_SIZE +
667 offsetof(struct iseq_compile_data_storage, buff));
668
669 new_arena->pos = 0;
670 new_arena->next = 0;
671 new_arena->size = INITIAL_ISEQ_COMPILE_DATA_STORAGE_BUFF_SIZE;
672
673 return new_arena;
674}
675
676static int
677prepare_node_id(const NODE *node)
678{
679 if (!node) return -1;
680
681 if (nd_type(node) == NODE_SCOPE && RNODE_SCOPE(node)->nd_parent) {
682 return nd_node_id(RNODE_SCOPE(node)->nd_parent);
683 }
684
685 return nd_node_id(node);
686}
687
688static VALUE
689prepare_iseq_build(rb_iseq_t *iseq,
690 VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id,
691 const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type type,
692 VALUE script_lines, const rb_compile_option_t *option)
693{
694 VALUE coverage = Qfalse;
695 VALUE err_info = Qnil;
696 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
697
698 if (parent && (type == ISEQ_TYPE_MAIN || type == ISEQ_TYPE_TOP))
699 err_info = Qfalse;
700
701 body->type = type;
702 set_relation(iseq, parent);
703
704 name = rb_fstring(name);
705 iseq_location_setup(iseq, name, path, realpath, first_lineno, code_location, node_id);
706 if (iseq != body->local_iseq) {
707 RB_OBJ_WRITE(iseq, &body->location.base_label, ISEQ_BODY(body->local_iseq)->location.label);
708 }
709 ISEQ_COVERAGE_SET(iseq, Qnil);
710 ISEQ_ORIGINAL_ISEQ_CLEAR(iseq);
711 body->variable.flip_count = 0;
712
713 if (NIL_P(script_lines)) {
714 RB_OBJ_WRITE(iseq, &body->variable.script_lines, Qnil);
715 }
716 else {
717 RB_OBJ_WRITE(iseq, &body->variable.script_lines, rb_ractor_make_shareable(script_lines));
718 }
719
720 ISEQ_COMPILE_DATA_ALLOC(iseq);
721 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, err_info);
722 RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, Qnil);
723
724 ISEQ_COMPILE_DATA(iseq)->node.storage_head = ISEQ_COMPILE_DATA(iseq)->node.storage_current = new_arena();
725 ISEQ_COMPILE_DATA(iseq)->insn.storage_head = ISEQ_COMPILE_DATA(iseq)->insn.storage_current = new_arena();
726 ISEQ_COMPILE_DATA(iseq)->isolated_depth = isolated_depth;
727 ISEQ_COMPILE_DATA(iseq)->option = option;
728 ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = NULL;
729 ISEQ_COMPILE_DATA(iseq)->builtin_function_table = GET_VM()->builtin_function_table;
730
731 if (option->coverage_enabled) {
732 VALUE coverages = rb_get_coverages();
733 if (RTEST(coverages)) {
734 coverage = rb_hash_lookup(coverages, rb_iseq_path(iseq));
735 if (NIL_P(coverage)) coverage = Qfalse;
736 }
737 }
738 ISEQ_COVERAGE_SET(iseq, coverage);
739 if (coverage && ISEQ_BRANCH_COVERAGE(iseq))
740 ISEQ_PC2BRANCHINDEX_SET(iseq, rb_ary_hidden_new(0));
741
742 return Qtrue;
743}
744
745#if VM_CHECK_MODE > 0 && VM_INSN_INFO_TABLE_IMPL > 0
746static void validate_get_insn_info(const rb_iseq_t *iseq);
747#endif
748
749void
750rb_iseq_insns_info_encode_positions(const rb_iseq_t *iseq)
751{
752#if VM_INSN_INFO_TABLE_IMPL == 2
753 /* create succ_index_table */
754 struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
755 int size = body->insns_info.size;
756 int max_pos = body->iseq_size;
757 int *data = (int *)body->insns_info.positions;
758 if (body->insns_info.succ_index_table) ruby_xfree(body->insns_info.succ_index_table);
759 body->insns_info.succ_index_table = succ_index_table_create(max_pos, data, size);
760#if VM_CHECK_MODE == 0
761 ruby_xfree(body->insns_info.positions);
762 body->insns_info.positions = NULL;
763#endif
764#endif
765}
766
767#if VM_INSN_INFO_TABLE_IMPL == 2
768unsigned int *
769rb_iseq_insns_info_decode_positions(const struct rb_iseq_constant_body *body)
770{
771 int size = body->insns_info.size;
772 int max_pos = body->iseq_size;
773 struct succ_index_table *sd = body->insns_info.succ_index_table;
774 return succ_index_table_invert(max_pos, sd, size);
775}
776#endif
777
778void
779rb_iseq_init_trace(rb_iseq_t *iseq)
780{
781 iseq->aux.exec.global_trace_events = 0;
782 if (ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS) {
783 rb_iseq_trace_set(iseq, ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS);
784 }
785}
786
787static VALUE
788finish_iseq_build(rb_iseq_t *iseq)
789{
790 struct iseq_compile_data *data = ISEQ_COMPILE_DATA(iseq);
791 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
792 VALUE err = data->err_info;
793 ISEQ_COMPILE_DATA_CLEAR(iseq);
794 compile_data_free(data);
795
796#if VM_CHECK_MODE > 0 && VM_INSN_INFO_TABLE_IMPL > 0
797 validate_get_insn_info(iseq);
798#endif
799
800 if (RTEST(err)) {
801 VALUE path = pathobj_path(body->location.pathobj);
802 if (err == Qtrue) err = rb_exc_new_cstr(rb_eSyntaxError, "compile error");
803 rb_funcallv(err, rb_intern("set_backtrace"), 1, &path);
804 rb_exc_raise(err);
805 }
806
807 RB_DEBUG_COUNTER_INC(iseq_num);
808 RB_DEBUG_COUNTER_ADD(iseq_cd_num, ISEQ_BODY(iseq)->ci_size);
809
810 rb_iseq_init_trace(iseq);
811 return Qtrue;
812}
813
814static rb_compile_option_t COMPILE_OPTION_DEFAULT = {
815 .inline_const_cache = OPT_INLINE_CONST_CACHE,
816 .peephole_optimization = OPT_PEEPHOLE_OPTIMIZATION,
817 .tailcall_optimization = OPT_TAILCALL_OPTIMIZATION,
818 .specialized_instruction = OPT_SPECIALISED_INSTRUCTION,
819 .operands_unification = OPT_OPERANDS_UNIFICATION,
820 .instructions_unification = OPT_INSTRUCTIONS_UNIFICATION,
821 .frozen_string_literal = OPT_FROZEN_STRING_LITERAL,
822 .debug_frozen_string_literal = OPT_DEBUG_FROZEN_STRING_LITERAL,
823 .coverage_enabled = TRUE,
824};
825
826static const rb_compile_option_t COMPILE_OPTION_FALSE = {
827 .frozen_string_literal = -1, // unspecified
828};
829
830int
831rb_iseq_opt_frozen_string_literal(void)
832{
833 return COMPILE_OPTION_DEFAULT.frozen_string_literal;
834}
835
836static void
837set_compile_option_from_hash(rb_compile_option_t *option, VALUE opt)
838{
839#define SET_COMPILE_OPTION(o, h, mem) \
840 { VALUE flag = rb_hash_aref((h), ID2SYM(rb_intern(#mem))); \
841 if (flag == Qtrue) { (o)->mem = 1; } \
842 else if (flag == Qfalse) { (o)->mem = 0; } \
843 }
844#define SET_COMPILE_OPTION_NUM(o, h, mem) \
845 { VALUE num = rb_hash_aref((h), ID2SYM(rb_intern(#mem))); \
846 if (!NIL_P(num)) (o)->mem = NUM2INT(num); \
847 }
848 SET_COMPILE_OPTION(option, opt, inline_const_cache);
849 SET_COMPILE_OPTION(option, opt, peephole_optimization);
850 SET_COMPILE_OPTION(option, opt, tailcall_optimization);
851 SET_COMPILE_OPTION(option, opt, specialized_instruction);
852 SET_COMPILE_OPTION(option, opt, operands_unification);
853 SET_COMPILE_OPTION(option, opt, instructions_unification);
854 SET_COMPILE_OPTION(option, opt, frozen_string_literal);
855 SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal);
856 SET_COMPILE_OPTION(option, opt, coverage_enabled);
857 SET_COMPILE_OPTION_NUM(option, opt, debug_level);
858#undef SET_COMPILE_OPTION
859#undef SET_COMPILE_OPTION_NUM
860}
861
862static rb_compile_option_t *
863set_compile_option_from_ast(rb_compile_option_t *option, const rb_ast_body_t *ast)
864{
865#define SET_COMPILE_OPTION(o, a, mem) \
866 ((a)->mem < 0 ? 0 : ((o)->mem = (a)->mem > 0))
867 SET_COMPILE_OPTION(option, ast, coverage_enabled);
868#undef SET_COMPILE_OPTION
869 if (ast->frozen_string_literal >= 0) {
870 option->frozen_string_literal = ast->frozen_string_literal;
871 }
872 return option;
873}
874
875static void
876make_compile_option(rb_compile_option_t *option, VALUE opt)
877{
878 if (NIL_P(opt)) {
879 *option = COMPILE_OPTION_DEFAULT;
880 }
881 else if (opt == Qfalse) {
882 *option = COMPILE_OPTION_FALSE;
883 }
884 else if (opt == Qtrue) {
885 int i;
886 for (i = 0; i < (int)(sizeof(rb_compile_option_t) / sizeof(int)); ++i)
887 ((int *)option)[i] = 1;
888 }
889 else if (RB_TYPE_P(opt, T_HASH)) {
890 *option = COMPILE_OPTION_DEFAULT;
891 set_compile_option_from_hash(option, opt);
892 }
893 else {
894 rb_raise(rb_eTypeError, "Compile option must be Hash/true/false/nil");
895 }
896}
897
898static VALUE
899make_compile_option_value(rb_compile_option_t *option)
900{
901 VALUE opt = rb_hash_new_with_size(11);
902#define SET_COMPILE_OPTION(o, h, mem) \
903 rb_hash_aset((h), ID2SYM(rb_intern(#mem)), RBOOL((o)->mem))
904#define SET_COMPILE_OPTION_NUM(o, h, mem) \
905 rb_hash_aset((h), ID2SYM(rb_intern(#mem)), INT2NUM((o)->mem))
906 {
907 SET_COMPILE_OPTION(option, opt, inline_const_cache);
908 SET_COMPILE_OPTION(option, opt, peephole_optimization);
909 SET_COMPILE_OPTION(option, opt, tailcall_optimization);
910 SET_COMPILE_OPTION(option, opt, specialized_instruction);
911 SET_COMPILE_OPTION(option, opt, operands_unification);
912 SET_COMPILE_OPTION(option, opt, instructions_unification);
913 SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal);
914 SET_COMPILE_OPTION(option, opt, coverage_enabled);
915 SET_COMPILE_OPTION_NUM(option, opt, debug_level);
916 }
917#undef SET_COMPILE_OPTION
918#undef SET_COMPILE_OPTION_NUM
919 VALUE frozen_string_literal = option->frozen_string_literal == -1 ? Qnil : RBOOL(option->frozen_string_literal);
920 rb_hash_aset(opt, ID2SYM(rb_intern("frozen_string_literal")), frozen_string_literal);
921 return opt;
922}
923
924rb_iseq_t *
925rb_iseq_new(const VALUE ast_value, VALUE name, VALUE path, VALUE realpath,
926 const rb_iseq_t *parent, enum rb_iseq_type type)
927{
928 return rb_iseq_new_with_opt(ast_value, name, path, realpath, 0, parent,
929 0, type, &COMPILE_OPTION_DEFAULT,
930 Qnil);
931}
932
933static int
934ast_line_count(const VALUE ast_value)
935{
936 rb_ast_t *ast = rb_ruby_ast_data_get(ast_value);
937 return ast->body.line_count;
938}
939
940static VALUE
941iseq_setup_coverage(VALUE coverages, VALUE path, int line_count)
942{
943 if (line_count >= 0) {
944 int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count;
945
946 VALUE coverage = rb_default_coverage(len);
947 rb_hash_aset(coverages, path, coverage);
948
949 return coverage;
950 }
951
952 return Qnil;
953}
954
955static inline void
956iseq_new_setup_coverage(VALUE path, int line_count)
957{
958 VALUE coverages = rb_get_coverages();
959
960 if (RTEST(coverages)) {
961 iseq_setup_coverage(coverages, path, line_count);
962 }
963}
964
965rb_iseq_t *
966rb_iseq_new_top(const VALUE ast_value, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent)
967{
968 iseq_new_setup_coverage(path, ast_line_count(ast_value));
969
970 return rb_iseq_new_with_opt(ast_value, name, path, realpath, 0, parent, 0,
971 ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT,
972 Qnil);
973}
974
978rb_iseq_t *
979pm_iseq_new_top(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, int *error_state)
980{
981 iseq_new_setup_coverage(path, (int) (node->parser->newline_list.size - 1));
982
983 return pm_iseq_new_with_opt(node, name, path, realpath, 0, parent, 0,
984 ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT, error_state);
985}
986
987rb_iseq_t *
988rb_iseq_new_main(const VALUE ast_value, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt)
989{
990 iseq_new_setup_coverage(path, ast_line_count(ast_value));
991
992 return rb_iseq_new_with_opt(ast_value, rb_fstring_lit("<main>"),
993 path, realpath, 0,
994 parent, 0, ISEQ_TYPE_MAIN, opt ? &COMPILE_OPTION_DEFAULT : &COMPILE_OPTION_FALSE,
995 Qnil);
996}
997
1002rb_iseq_t *
1003pm_iseq_new_main(pm_scope_node_t *node, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt, int *error_state)
1004{
1005 iseq_new_setup_coverage(path, (int) (node->parser->newline_list.size - 1));
1006
1007 return pm_iseq_new_with_opt(node, rb_fstring_lit("<main>"),
1008 path, realpath, 0,
1009 parent, 0, ISEQ_TYPE_MAIN, opt ? &COMPILE_OPTION_DEFAULT : &COMPILE_OPTION_FALSE, error_state);
1010}
1011
1012rb_iseq_t *
1013rb_iseq_new_eval(const VALUE ast_value, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth)
1014{
1015 if (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) {
1016 VALUE coverages = rb_get_coverages();
1017 if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) {
1018 iseq_setup_coverage(coverages, path, ast_line_count(ast_value) + first_lineno - 1);
1019 }
1020 }
1021
1022 return rb_iseq_new_with_opt(ast_value, name, path, realpath, first_lineno,
1023 parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT,
1024 Qnil);
1025}
1026
1027rb_iseq_t *
1028pm_iseq_new_eval(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath,
1029 int first_lineno, const rb_iseq_t *parent, int isolated_depth, int *error_state)
1030{
1031 if (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) {
1032 VALUE coverages = rb_get_coverages();
1033 if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) {
1034 iseq_setup_coverage(coverages, path, ((int) (node->parser->newline_list.size - 1)) + first_lineno - 1);
1035 }
1036 }
1037
1038 return pm_iseq_new_with_opt(node, name, path, realpath, first_lineno,
1039 parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT, error_state);
1040}
1041
1042static inline rb_iseq_t *
1043iseq_translate(rb_iseq_t *iseq)
1044{
1045 if (rb_respond_to(rb_cISeq, rb_intern("translate"))) {
1046 VALUE v1 = iseqw_new(iseq);
1047 VALUE v2 = rb_funcall(rb_cISeq, rb_intern("translate"), 1, v1);
1048 if (v1 != v2 && CLASS_OF(v2) == rb_cISeq) {
1049 iseq = (rb_iseq_t *)iseqw_check(v2);
1050 }
1051 }
1052
1053 return iseq;
1054}
1055
1056rb_iseq_t *
1057rb_iseq_new_with_opt(VALUE ast_value, VALUE name, VALUE path, VALUE realpath,
1058 int first_lineno, const rb_iseq_t *parent, int isolated_depth,
1059 enum rb_iseq_type type, const rb_compile_option_t *option,
1060 VALUE script_lines)
1061{
1062 rb_ast_t *ast = rb_ruby_ast_data_get(ast_value);
1063 rb_ast_body_t *body = ast ? &ast->body : NULL;
1064 const NODE *node = body ? body->root : 0;
1065 /* TODO: argument check */
1066 rb_iseq_t *iseq = iseq_alloc();
1067 rb_compile_option_t new_opt;
1068
1069 if (!option) option = &COMPILE_OPTION_DEFAULT;
1070 if (body) {
1071 new_opt = *option;
1072 option = set_compile_option_from_ast(&new_opt, body);
1073 }
1074
1075 if (!NIL_P(script_lines)) {
1076 // noop
1077 }
1078 else if (body && body->script_lines) {
1079 script_lines = rb_parser_build_script_lines_from(body->script_lines);
1080 }
1081 else if (parent) {
1082 script_lines = ISEQ_BODY(parent)->variable.script_lines;
1083 }
1084
1085 prepare_iseq_build(iseq, name, path, realpath, first_lineno, node ? &node->nd_loc : NULL, prepare_node_id(node),
1086 parent, isolated_depth, type, script_lines, option);
1087
1088 rb_iseq_compile_node(iseq, node);
1089 finish_iseq_build(iseq);
1090 RB_GC_GUARD(ast_value);
1091
1092 return iseq_translate(iseq);
1093}
1094
1096 rb_iseq_t *iseq;
1097 pm_scope_node_t *node;
1098};
1099
1100VALUE
1101pm_iseq_new_with_opt_try(VALUE d)
1102{
1103 struct pm_iseq_new_with_opt_data *data = (struct pm_iseq_new_with_opt_data *)d;
1104
1105 // This can compile child iseqs, which can raise syntax errors
1106 pm_iseq_compile_node(data->iseq, data->node);
1107
1108 // This raises an exception if there is a syntax error
1109 finish_iseq_build(data->iseq);
1110
1111 return Qundef;
1112}
1113
1126rb_iseq_t *
1127pm_iseq_new_with_opt(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath,
1128 int first_lineno, const rb_iseq_t *parent, int isolated_depth,
1129 enum rb_iseq_type type, const rb_compile_option_t *option, int *error_state)
1130{
1131 rb_iseq_t *iseq = iseq_alloc();
1132 ISEQ_BODY(iseq)->prism = true;
1133
1134 rb_compile_option_t next_option;
1135 if (!option) option = &COMPILE_OPTION_DEFAULT;
1136
1137 next_option = *option;
1138 next_option.coverage_enabled = node->coverage_enabled < 0 ? 0 : node->coverage_enabled > 0;
1139 option = &next_option;
1140
1141 pm_location_t *location = &node->base.location;
1142 int32_t start_line = node->parser->start_line;
1143
1144 pm_line_column_t start = pm_newline_list_line_column(&node->parser->newline_list, location->start, start_line);
1145 pm_line_column_t end = pm_newline_list_line_column(&node->parser->newline_list, location->end, start_line);
1146
1147 rb_code_location_t code_location = (rb_code_location_t) {
1148 .beg_pos = { .lineno = (int) start.line, .column = (int) start.column },
1149 .end_pos = { .lineno = (int) end.line, .column = (int) end.column }
1150 };
1151
1152 prepare_iseq_build(iseq, name, path, realpath, first_lineno, &code_location, node->ast_node->node_id,
1153 parent, isolated_depth, type, node->script_lines == NULL ? Qnil : *node->script_lines, option);
1154
1155 struct pm_iseq_new_with_opt_data data = {
1156 .iseq = iseq,
1157 .node = node
1158 };
1159 rb_protect(pm_iseq_new_with_opt_try, (VALUE)&data, error_state);
1160
1161 if (*error_state) return NULL;
1162
1163 return iseq_translate(iseq);
1164}
1165
1166rb_iseq_t *
1167rb_iseq_new_with_callback(
1168 const struct rb_iseq_new_with_callback_callback_func * ifunc,
1169 VALUE name, VALUE path, VALUE realpath,
1170 int first_lineno, const rb_iseq_t *parent,
1171 enum rb_iseq_type type, const rb_compile_option_t *option)
1172{
1173 /* TODO: argument check */
1174 rb_iseq_t *iseq = iseq_alloc();
1175
1176 if (!option) option = &COMPILE_OPTION_DEFAULT;
1177 prepare_iseq_build(iseq, name, path, realpath, first_lineno, NULL, -1, parent, 0, type, Qnil, option);
1178
1179 rb_iseq_compile_callback(iseq, ifunc);
1180 finish_iseq_build(iseq);
1181
1182 return iseq;
1183}
1184
1185const rb_iseq_t *
1186rb_iseq_load_iseq(VALUE fname)
1187{
1188 VALUE iseqv = rb_check_funcall(rb_cISeq, rb_intern("load_iseq"), 1, &fname);
1189
1190 if (!SPECIAL_CONST_P(iseqv) && RBASIC_CLASS(iseqv) == rb_cISeq) {
1191 return iseqw_check(iseqv);
1192 }
1193
1194 return NULL;
1195}
1196
1197const rb_iseq_t *
1198rb_iseq_compile_iseq(VALUE str, VALUE fname)
1199{
1200 VALUE args[] = {
1201 str, fname
1202 };
1203 VALUE iseqv = rb_check_funcall(rb_cISeq, rb_intern("compile"), 2, args);
1204
1205 if (!SPECIAL_CONST_P(iseqv) && RBASIC_CLASS(iseqv) == rb_cISeq) {
1206 return iseqw_check(iseqv);
1207 }
1208
1209 return NULL;
1210}
1211
1212#define CHECK_ARRAY(v) rb_to_array_type(v)
1213#define CHECK_HASH(v) rb_to_hash_type(v)
1214#define CHECK_STRING(v) rb_str_to_str(v)
1215#define CHECK_SYMBOL(v) rb_to_symbol_type(v)
1216static inline VALUE CHECK_INTEGER(VALUE v) {(void)NUM2LONG(v); return v;}
1217
1218static enum rb_iseq_type
1219iseq_type_from_sym(VALUE type)
1220{
1221 const ID id_top = rb_intern("top");
1222 const ID id_method = rb_intern("method");
1223 const ID id_block = rb_intern("block");
1224 const ID id_class = rb_intern("class");
1225 const ID id_rescue = rb_intern("rescue");
1226 const ID id_ensure = rb_intern("ensure");
1227 const ID id_eval = rb_intern("eval");
1228 const ID id_main = rb_intern("main");
1229 const ID id_plain = rb_intern("plain");
1230 /* ensure all symbols are static or pinned down before
1231 * conversion */
1232 const ID typeid = rb_check_id(&type);
1233 if (typeid == id_top) return ISEQ_TYPE_TOP;
1234 if (typeid == id_method) return ISEQ_TYPE_METHOD;
1235 if (typeid == id_block) return ISEQ_TYPE_BLOCK;
1236 if (typeid == id_class) return ISEQ_TYPE_CLASS;
1237 if (typeid == id_rescue) return ISEQ_TYPE_RESCUE;
1238 if (typeid == id_ensure) return ISEQ_TYPE_ENSURE;
1239 if (typeid == id_eval) return ISEQ_TYPE_EVAL;
1240 if (typeid == id_main) return ISEQ_TYPE_MAIN;
1241 if (typeid == id_plain) return ISEQ_TYPE_PLAIN;
1242 return (enum rb_iseq_type)-1;
1243}
1244
1245static VALUE
1246iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt)
1247{
1248 rb_iseq_t *iseq = iseq_alloc();
1249
1250 VALUE magic, version1, version2, format_type, misc;
1251 VALUE name, path, realpath, code_location, node_id;
1252 VALUE type, body, locals, params, exception;
1253
1254 st_data_t iseq_type;
1255 rb_compile_option_t option;
1256 int i = 0;
1257 rb_code_location_t tmp_loc = { {0, 0}, {-1, -1} };
1258
1259 /* [magic, major_version, minor_version, format_type, misc,
1260 * label, path, first_lineno,
1261 * type, locals, args, exception_table, body]
1262 */
1263
1264 data = CHECK_ARRAY(data);
1265
1266 magic = CHECK_STRING(rb_ary_entry(data, i++));
1267 version1 = CHECK_INTEGER(rb_ary_entry(data, i++));
1268 version2 = CHECK_INTEGER(rb_ary_entry(data, i++));
1269 format_type = CHECK_INTEGER(rb_ary_entry(data, i++));
1270 misc = CHECK_HASH(rb_ary_entry(data, i++));
1271 ((void)magic, (void)version1, (void)version2, (void)format_type);
1272
1273 name = CHECK_STRING(rb_ary_entry(data, i++));
1274 path = CHECK_STRING(rb_ary_entry(data, i++));
1275 realpath = rb_ary_entry(data, i++);
1276 realpath = NIL_P(realpath) ? Qnil : CHECK_STRING(realpath);
1277 int first_lineno = RB_NUM2INT(rb_ary_entry(data, i++));
1278
1279 type = CHECK_SYMBOL(rb_ary_entry(data, i++));
1280 locals = CHECK_ARRAY(rb_ary_entry(data, i++));
1281 params = CHECK_HASH(rb_ary_entry(data, i++));
1282 exception = CHECK_ARRAY(rb_ary_entry(data, i++));
1283 body = CHECK_ARRAY(rb_ary_entry(data, i++));
1284
1285 ISEQ_BODY(iseq)->local_iseq = iseq;
1286
1287 iseq_type = iseq_type_from_sym(type);
1288 if (iseq_type == (enum rb_iseq_type)-1) {
1289 rb_raise(rb_eTypeError, "unsupported type: :%"PRIsVALUE, rb_sym2str(type));
1290 }
1291
1292 node_id = rb_hash_aref(misc, ID2SYM(rb_intern("node_id")));
1293
1294 code_location = rb_hash_aref(misc, ID2SYM(rb_intern("code_location")));
1295 if (RB_TYPE_P(code_location, T_ARRAY) && RARRAY_LEN(code_location) == 4) {
1296 tmp_loc.beg_pos.lineno = NUM2INT(rb_ary_entry(code_location, 0));
1297 tmp_loc.beg_pos.column = NUM2INT(rb_ary_entry(code_location, 1));
1298 tmp_loc.end_pos.lineno = NUM2INT(rb_ary_entry(code_location, 2));
1299 tmp_loc.end_pos.column = NUM2INT(rb_ary_entry(code_location, 3));
1300 }
1301
1302 if (SYM2ID(rb_hash_aref(misc, ID2SYM(rb_intern("parser")))) == rb_intern("prism")) {
1303 ISEQ_BODY(iseq)->prism = true;
1304 }
1305
1306 make_compile_option(&option, opt);
1307 option.peephole_optimization = FALSE; /* because peephole optimization can modify original iseq */
1308 prepare_iseq_build(iseq, name, path, realpath, first_lineno, &tmp_loc, NUM2INT(node_id),
1309 parent, 0, (enum rb_iseq_type)iseq_type, Qnil, &option);
1310
1311 rb_iseq_build_from_ary(iseq, misc, locals, params, exception, body);
1312
1313 finish_iseq_build(iseq);
1314
1315 return iseqw_new(iseq);
1316}
1317
1318/*
1319 * :nodoc:
1320 */
1321static VALUE
1322iseq_s_load(int argc, VALUE *argv, VALUE self)
1323{
1324 VALUE data, opt=Qnil;
1325 rb_scan_args(argc, argv, "11", &data, &opt);
1326 return iseq_load(data, NULL, opt);
1327}
1328
1329VALUE
1330rb_iseq_load(VALUE data, VALUE parent, VALUE opt)
1331{
1332 return iseq_load(data, RTEST(parent) ? (rb_iseq_t *)parent : NULL, opt);
1333}
1334
1335static rb_iseq_t *
1336rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, VALUE opt)
1337{
1338 rb_iseq_t *iseq = NULL;
1339 rb_compile_option_t option;
1340#if !defined(__GNUC__) || (__GNUC__ == 4 && __GNUC_MINOR__ == 8)
1341# define INITIALIZED volatile /* suppress warnings by gcc 4.8 */
1342#else
1343# define INITIALIZED /* volatile */
1344#endif
1345 VALUE (*parse)(VALUE vparser, VALUE fname, VALUE file, int start);
1346 int ln;
1347 VALUE INITIALIZED ast_value;
1348 rb_ast_t *ast;
1349 VALUE name = rb_fstring_lit("<compiled>");
1350
1351 /* safe results first */
1352 make_compile_option(&option, opt);
1353 ln = NUM2INT(line);
1354 StringValueCStr(file);
1355 if (RB_TYPE_P(src, T_FILE)) {
1356 parse = rb_parser_compile_file_path;
1357 }
1358 else {
1359 parse = rb_parser_compile_string_path;
1360 StringValue(src);
1361 }
1362 {
1363 const VALUE parser = rb_parser_new();
1364 const rb_iseq_t *outer_scope = rb_iseq_new(Qnil, name, name, Qnil, 0, ISEQ_TYPE_TOP);
1365 VALUE outer_scope_v = (VALUE)outer_scope;
1366 rb_parser_set_context(parser, outer_scope, FALSE);
1367 if (ruby_vm_keep_script_lines) rb_parser_set_script_lines(parser);
1368 RB_GC_GUARD(outer_scope_v);
1369 ast_value = (*parse)(parser, file, src, ln);
1370 }
1371
1372 ast = rb_ruby_ast_data_get(ast_value);
1373
1374 if (!ast || !ast->body.root) {
1375 rb_ast_dispose(ast);
1376 rb_exc_raise(GET_EC()->errinfo);
1377 }
1378 else {
1379 iseq = rb_iseq_new_with_opt(ast_value, name, file, realpath, ln,
1380 NULL, 0, ISEQ_TYPE_TOP, &option,
1381 Qnil);
1382 rb_ast_dispose(ast);
1383 }
1384
1385 return iseq;
1386}
1387
1388static rb_iseq_t *
1389pm_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, VALUE opt)
1390{
1391 rb_iseq_t *iseq = NULL;
1392 rb_compile_option_t option;
1393 int ln;
1394 VALUE name = rb_fstring_lit("<compiled>");
1395
1396 /* safe results first */
1397 make_compile_option(&option, opt);
1398 ln = NUM2INT(line);
1399 StringValueCStr(file);
1400
1401 bool parse_file = false;
1402 if (RB_TYPE_P(src, T_FILE)) {
1403 parse_file = true;
1404 src = rb_io_path(src);
1405 }
1406 else {
1407 src = StringValue(src);
1408 }
1409
1410 pm_parse_result_t result = { 0 };
1411 pm_options_line_set(&result.options, NUM2INT(line));
1412 pm_options_scopes_init(&result.options, 1);
1413 result.node.coverage_enabled = 1;
1414
1415 switch (option.frozen_string_literal) {
1416 case ISEQ_FROZEN_STRING_LITERAL_UNSET:
1417 break;
1418 case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
1420 break;
1421 case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
1423 break;
1424 default:
1425 rb_bug("pm_iseq_compile_with_option: invalid frozen_string_literal=%d", option.frozen_string_literal);
1426 break;
1427 }
1428
1429 VALUE script_lines;
1430 VALUE error;
1431
1432 if (parse_file) {
1433 error = pm_load_parse_file(&result, src, ruby_vm_keep_script_lines ? &script_lines : NULL);
1434 }
1435 else {
1436 error = pm_parse_string(&result, src, file, ruby_vm_keep_script_lines ? &script_lines : NULL);
1437 }
1438
1439 RB_GC_GUARD(src);
1440
1441 if (error == Qnil) {
1442 int error_state;
1443 iseq = pm_iseq_new_with_opt(&result.node, name, file, realpath, ln, NULL, 0, ISEQ_TYPE_TOP, &option, &error_state);
1444
1445 pm_parse_result_free(&result);
1446
1447 if (error_state) {
1448 RUBY_ASSERT(iseq == NULL);
1449 rb_jump_tag(error_state);
1450 }
1451 }
1452 else {
1453 pm_parse_result_free(&result);
1454 rb_exc_raise(error);
1455 }
1456
1457 return iseq;
1458}
1459
1460VALUE
1461rb_iseq_path(const rb_iseq_t *iseq)
1462{
1463 return pathobj_path(ISEQ_BODY(iseq)->location.pathobj);
1464}
1465
1466VALUE
1467rb_iseq_realpath(const rb_iseq_t *iseq)
1468{
1469 return pathobj_realpath(ISEQ_BODY(iseq)->location.pathobj);
1470}
1471
1472VALUE
1473rb_iseq_absolute_path(const rb_iseq_t *iseq)
1474{
1475 return rb_iseq_realpath(iseq);
1476}
1477
1478int
1479rb_iseq_from_eval_p(const rb_iseq_t *iseq)
1480{
1481 return NIL_P(rb_iseq_realpath(iseq));
1482}
1483
1484VALUE
1485rb_iseq_label(const rb_iseq_t *iseq)
1486{
1487 return ISEQ_BODY(iseq)->location.label;
1488}
1489
1490VALUE
1491rb_iseq_base_label(const rb_iseq_t *iseq)
1492{
1493 return ISEQ_BODY(iseq)->location.base_label;
1494}
1495
1496VALUE
1497rb_iseq_first_lineno(const rb_iseq_t *iseq)
1498{
1499 return RB_INT2NUM(ISEQ_BODY(iseq)->location.first_lineno);
1500}
1501
1502VALUE
1503rb_iseq_method_name(const rb_iseq_t *iseq)
1504{
1505 struct rb_iseq_constant_body *const body = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq);
1506
1507 if (body->type == ISEQ_TYPE_METHOD) {
1508 return body->location.base_label;
1509 }
1510 else {
1511 return Qnil;
1512 }
1513}
1514
1515void
1516rb_iseq_code_location(const rb_iseq_t *iseq, int *beg_pos_lineno, int *beg_pos_column, int *end_pos_lineno, int *end_pos_column)
1517{
1518 const rb_code_location_t *loc = &ISEQ_BODY(iseq)->location.code_location;
1519 if (beg_pos_lineno) *beg_pos_lineno = loc->beg_pos.lineno;
1520 if (beg_pos_column) *beg_pos_column = loc->beg_pos.column;
1521 if (end_pos_lineno) *end_pos_lineno = loc->end_pos.lineno;
1522 if (end_pos_column) *end_pos_column = loc->end_pos.column;
1523}
1524
1525static ID iseq_type_id(enum rb_iseq_type type);
1526
1527VALUE
1528rb_iseq_type(const rb_iseq_t *iseq)
1529{
1530 return ID2SYM(iseq_type_id(ISEQ_BODY(iseq)->type));
1531}
1532
1533VALUE
1534rb_iseq_coverage(const rb_iseq_t *iseq)
1535{
1536 return ISEQ_COVERAGE(iseq);
1537}
1538
1539static int
1540remove_coverage_i(void *vstart, void *vend, size_t stride, void *data)
1541{
1542 VALUE v = (VALUE)vstart;
1543 for (; v != (VALUE)vend; v += stride) {
1544 void *ptr = rb_asan_poisoned_object_p(v);
1545 rb_asan_unpoison_object(v, false);
1546
1547 if (rb_obj_is_iseq(v)) {
1548 rb_iseq_t *iseq = (rb_iseq_t *)v;
1549 ISEQ_COVERAGE_SET(iseq, Qnil);
1550 }
1551
1552 asan_poison_object_if(ptr, v);
1553 }
1554 return 0;
1555}
1556
1557void
1558rb_iseq_remove_coverage_all(void)
1559{
1560 rb_objspace_each_objects(remove_coverage_i, NULL);
1561}
1562
1563/* define wrapper class methods (RubyVM::InstructionSequence) */
1564
1565static void
1566iseqw_mark_and_move(void *ptr)
1567{
1568 rb_gc_mark_and_move((VALUE *)ptr);
1569}
1570
1571static size_t
1572iseqw_memsize(const void *ptr)
1573{
1574 return rb_iseq_memsize(*(const rb_iseq_t **)ptr);
1575}
1576
1577static const rb_data_type_t iseqw_data_type = {
1578 "T_IMEMO/iseq",
1579 {
1580 iseqw_mark_and_move,
1582 iseqw_memsize,
1583 iseqw_mark_and_move,
1584 },
1585 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED
1586};
1587
1588static VALUE
1589iseqw_new(const rb_iseq_t *iseq)
1590{
1591 if (iseq->wrapper) {
1592 if (*(const rb_iseq_t **)rb_check_typeddata(iseq->wrapper, &iseqw_data_type) != iseq) {
1593 rb_raise(rb_eTypeError, "wrong iseq wrapper: %" PRIsVALUE " for %p",
1594 iseq->wrapper, (void *)iseq);
1595 }
1596 return iseq->wrapper;
1597 }
1598 else {
1599 rb_iseq_t **ptr;
1600 VALUE obj = TypedData_Make_Struct(rb_cISeq, rb_iseq_t *, &iseqw_data_type, ptr);
1601 RB_OBJ_WRITE(obj, ptr, iseq);
1602
1603 /* cache a wrapper object */
1604 RB_OBJ_SET_FROZEN_SHAREABLE((VALUE)obj);
1605 RB_OBJ_WRITE((VALUE)iseq, &iseq->wrapper, obj);
1606
1607 return obj;
1608 }
1609}
1610
1611VALUE
1612rb_iseqw_new(const rb_iseq_t *iseq)
1613{
1614 return iseqw_new(iseq);
1615}
1616
1622static VALUE
1623iseqw_s_compile_parser(int argc, VALUE *argv, VALUE self, bool prism)
1624{
1625 VALUE src, file = Qnil, path = Qnil, line = Qnil, opt = Qnil;
1626 int i;
1627
1628 i = rb_scan_args(argc, argv, "1*:", &src, NULL, &opt);
1629 if (i > 4+NIL_P(opt)) rb_error_arity(argc, 1, 5);
1630 switch (i) {
1631 case 5: opt = argv[--i];
1632 case 4: line = argv[--i];
1633 case 3: path = argv[--i];
1634 case 2: file = argv[--i];
1635 }
1636
1637 if (NIL_P(file)) file = rb_fstring_lit("<compiled>");
1638 if (NIL_P(path)) path = file;
1639 if (NIL_P(line)) line = INT2FIX(1);
1640
1641 Check_Type(path, T_STRING);
1642 Check_Type(file, T_STRING);
1643
1644 rb_iseq_t *iseq;
1645 if (prism) {
1646 iseq = pm_iseq_compile_with_option(src, file, path, line, opt);
1647 }
1648 else {
1649 iseq = rb_iseq_compile_with_option(src, file, path, line, opt);
1650 }
1651
1652 return iseqw_new(iseq);
1653}
1654
1655/*
1656 * call-seq:
1657 * InstructionSequence.compile(source[, file[, path[, line[, options]]]]) -> iseq
1658 * InstructionSequence.new(source[, file[, path[, line[, options]]]]) -> iseq
1659 *
1660 * Takes +source+, which can be a string of Ruby code, or an open +File+ object.
1661 * that contains Ruby source code.
1662 *
1663 * Optionally takes +file+, +path+, and +line+ which describe the file path,
1664 * real path and first line number of the ruby code in +source+ which are
1665 * metadata attached to the returned +iseq+.
1666 *
1667 * +file+ is used for +__FILE__+ and exception backtrace. +path+ is used for
1668 * +require_relative+ base. It is recommended these should be the same full
1669 * path.
1670 *
1671 * +options+, which can be +true+, +false+ or a +Hash+, is used to
1672 * modify the default behavior of the Ruby iseq compiler.
1673 *
1674 * For details regarding valid compile options see ::compile_option=.
1675 *
1676 * RubyVM::InstructionSequence.compile("a = 1 + 2")
1677 * #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
1678 *
1679 * path = "test.rb"
1680 * RubyVM::InstructionSequence.compile(File.read(path), path, File.expand_path(path))
1681 * #=> <RubyVM::InstructionSequence:<compiled>@test.rb:1>
1682 *
1683 * file = File.open("test.rb")
1684 * RubyVM::InstructionSequence.compile(file)
1685 * #=> <RubyVM::InstructionSequence:<compiled>@<compiled>:1>
1686 *
1687 * path = File.expand_path("test.rb")
1688 * RubyVM::InstructionSequence.compile(File.read(path), path, path)
1689 * #=> <RubyVM::InstructionSequence:<compiled>@/absolute/path/to/test.rb:1>
1690 *
1691 */
1692static VALUE
1693iseqw_s_compile(int argc, VALUE *argv, VALUE self)
1694{
1695 return iseqw_s_compile_parser(argc, argv, self, rb_ruby_prism_p());
1696}
1697
1698/*
1699 * call-seq:
1700 * InstructionSequence.compile_parsey(source[, file[, path[, line[, options]]]]) -> iseq
1701 *
1702 * Takes +source+, which can be a string of Ruby code, or an open +File+ object.
1703 * that contains Ruby source code. It parses and compiles using parse.y.
1704 *
1705 * Optionally takes +file+, +path+, and +line+ which describe the file path,
1706 * real path and first line number of the ruby code in +source+ which are
1707 * metadata attached to the returned +iseq+.
1708 *
1709 * +file+ is used for +__FILE__+ and exception backtrace. +path+ is used for
1710 * +require_relative+ base. It is recommended these should be the same full
1711 * path.
1712 *
1713 * +options+, which can be +true+, +false+ or a +Hash+, is used to
1714 * modify the default behavior of the Ruby iseq compiler.
1715 *
1716 * For details regarding valid compile options see ::compile_option=.
1717 *
1718 * RubyVM::InstructionSequence.compile_parsey("a = 1 + 2")
1719 * #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
1720 *
1721 * path = "test.rb"
1722 * RubyVM::InstructionSequence.compile_parsey(File.read(path), path, File.expand_path(path))
1723 * #=> <RubyVM::InstructionSequence:<compiled>@test.rb:1>
1724 *
1725 * file = File.open("test.rb")
1726 * RubyVM::InstructionSequence.compile_parsey(file)
1727 * #=> <RubyVM::InstructionSequence:<compiled>@<compiled>:1>
1728 *
1729 * path = File.expand_path("test.rb")
1730 * RubyVM::InstructionSequence.compile_parsey(File.read(path), path, path)
1731 * #=> <RubyVM::InstructionSequence:<compiled>@/absolute/path/to/test.rb:1>
1732 *
1733 */
1734static VALUE
1735iseqw_s_compile_parsey(int argc, VALUE *argv, VALUE self)
1736{
1737 return iseqw_s_compile_parser(argc, argv, self, false);
1738}
1739
1740/*
1741 * call-seq:
1742 * InstructionSequence.compile_prism(source[, file[, path[, line[, options]]]]) -> iseq
1743 *
1744 * Takes +source+, which can be a string of Ruby code, or an open +File+ object.
1745 * that contains Ruby source code. It parses and compiles using prism.
1746 *
1747 * Optionally takes +file+, +path+, and +line+ which describe the file path,
1748 * real path and first line number of the ruby code in +source+ which are
1749 * metadata attached to the returned +iseq+.
1750 *
1751 * +file+ is used for +__FILE__+ and exception backtrace. +path+ is used for
1752 * +require_relative+ base. It is recommended these should be the same full
1753 * path.
1754 *
1755 * +options+, which can be +true+, +false+ or a +Hash+, is used to
1756 * modify the default behavior of the Ruby iseq compiler.
1757 *
1758 * For details regarding valid compile options see ::compile_option=.
1759 *
1760 * RubyVM::InstructionSequence.compile_prism("a = 1 + 2")
1761 * #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
1762 *
1763 * path = "test.rb"
1764 * RubyVM::InstructionSequence.compile_prism(File.read(path), path, File.expand_path(path))
1765 * #=> <RubyVM::InstructionSequence:<compiled>@test.rb:1>
1766 *
1767 * file = File.open("test.rb")
1768 * RubyVM::InstructionSequence.compile_prism(file)
1769 * #=> <RubyVM::InstructionSequence:<compiled>@<compiled>:1>
1770 *
1771 * path = File.expand_path("test.rb")
1772 * RubyVM::InstructionSequence.compile_prism(File.read(path), path, path)
1773 * #=> <RubyVM::InstructionSequence:<compiled>@/absolute/path/to/test.rb:1>
1774 *
1775 */
1776static VALUE
1777iseqw_s_compile_prism(int argc, VALUE *argv, VALUE self)
1778{
1779 return iseqw_s_compile_parser(argc, argv, self, true);
1780}
1781
1782/*
1783 * call-seq:
1784 * InstructionSequence.compile_file(file[, options]) -> iseq
1785 *
1786 * Takes +file+, a String with the location of a Ruby source file, reads,
1787 * parses and compiles the file, and returns +iseq+, the compiled
1788 * InstructionSequence with source location metadata set.
1789 *
1790 * Optionally takes +options+, which can be +true+, +false+ or a +Hash+, to
1791 * modify the default behavior of the Ruby iseq compiler.
1792 *
1793 * For details regarding valid compile options see ::compile_option=.
1794 *
1795 * # /tmp/hello.rb
1796 * puts "Hello, world!"
1797 *
1798 * # elsewhere
1799 * RubyVM::InstructionSequence.compile_file("/tmp/hello.rb")
1800 * #=> <RubyVM::InstructionSequence:<main>@/tmp/hello.rb>
1801 */
1802static VALUE
1803iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
1804{
1805 VALUE file, opt = Qnil;
1806 VALUE parser, f, exc = Qnil, ret;
1807 rb_ast_t *ast;
1808 VALUE ast_value;
1809 rb_compile_option_t option;
1810 int i;
1811
1812 i = rb_scan_args(argc, argv, "1*:", &file, NULL, &opt);
1813 if (i > 1+NIL_P(opt)) rb_error_arity(argc, 1, 2);
1814 switch (i) {
1815 case 2: opt = argv[--i];
1816 }
1817 FilePathValue(file);
1818 file = rb_fstring(file); /* rb_io_t->pathv gets frozen anyways */
1819
1820 f = rb_file_open_str(file, "r");
1821
1822 rb_execution_context_t *ec = GET_EC();
1823 VALUE v = rb_vm_push_frame_fname(ec, file);
1824
1825 parser = rb_parser_new();
1826 rb_parser_set_context(parser, NULL, FALSE);
1827 ast_value = rb_parser_load_file(parser, file);
1828 ast = rb_ruby_ast_data_get(ast_value);
1829 if (!ast->body.root) exc = GET_EC()->errinfo;
1830
1831 rb_io_close(f);
1832 if (!ast->body.root) {
1833 rb_ast_dispose(ast);
1834 rb_exc_raise(exc);
1835 }
1836
1837 make_compile_option(&option, opt);
1838
1839 ret = iseqw_new(rb_iseq_new_with_opt(ast_value, rb_fstring_lit("<main>"),
1840 file,
1841 rb_realpath_internal(Qnil, file, 1),
1842 1, NULL, 0, ISEQ_TYPE_TOP, &option,
1843 Qnil));
1844 rb_ast_dispose(ast);
1845 RB_GC_GUARD(ast_value);
1846
1847 rb_vm_pop_frame(ec);
1848 RB_GC_GUARD(v);
1849 return ret;
1850}
1851
1852/*
1853 * call-seq:
1854 * InstructionSequence.compile_file_prism(file[, options]) -> iseq
1855 *
1856 * Takes +file+, a String with the location of a Ruby source file, reads,
1857 * parses and compiles the file, and returns +iseq+, the compiled
1858 * InstructionSequence with source location metadata set. It parses and
1859 * compiles using prism.
1860 *
1861 * Optionally takes +options+, which can be +true+, +false+ or a +Hash+, to
1862 * modify the default behavior of the Ruby iseq compiler.
1863 *
1864 * For details regarding valid compile options see ::compile_option=.
1865 *
1866 * # /tmp/hello.rb
1867 * puts "Hello, world!"
1868 *
1869 * # elsewhere
1870 * RubyVM::InstructionSequence.compile_file_prism("/tmp/hello.rb")
1871 * #=> <RubyVM::InstructionSequence:<main>@/tmp/hello.rb>
1872 */
1873static VALUE
1874iseqw_s_compile_file_prism(int argc, VALUE *argv, VALUE self)
1875{
1876 VALUE file, opt = Qnil, ret;
1877 rb_compile_option_t option;
1878 int i;
1879
1880 i = rb_scan_args(argc, argv, "1*:", &file, NULL, &opt);
1881 if (i > 1+NIL_P(opt)) rb_error_arity(argc, 1, 2);
1882 switch (i) {
1883 case 2: opt = argv[--i];
1884 }
1885 FilePathValue(file);
1886 file = rb_fstring(file); /* rb_io_t->pathv gets frozen anyways */
1887
1888 rb_execution_context_t *ec = GET_EC();
1889 VALUE v = rb_vm_push_frame_fname(ec, file);
1890
1891 pm_parse_result_t result = { 0 };
1892 result.options.line = 1;
1893 result.node.coverage_enabled = 1;
1894
1895 VALUE script_lines;
1896 VALUE error = pm_load_parse_file(&result, file, ruby_vm_keep_script_lines ? &script_lines : NULL);
1897
1898 if (error == Qnil) {
1899 make_compile_option(&option, opt);
1900
1901 int error_state;
1902 rb_iseq_t *iseq = pm_iseq_new_with_opt(&result.node, rb_fstring_lit("<main>"),
1903 file,
1904 rb_realpath_internal(Qnil, file, 1),
1905 1, NULL, 0, ISEQ_TYPE_TOP, &option, &error_state);
1906
1907 pm_parse_result_free(&result);
1908
1909 if (error_state) {
1910 RUBY_ASSERT(iseq == NULL);
1911 rb_jump_tag(error_state);
1912 }
1913
1914 ret = iseqw_new(iseq);
1915 rb_vm_pop_frame(ec);
1916 RB_GC_GUARD(v);
1917 return ret;
1918 }
1919 else {
1920 pm_parse_result_free(&result);
1921 rb_vm_pop_frame(ec);
1922 RB_GC_GUARD(v);
1923 rb_exc_raise(error);
1924 }
1925}
1926
1927/*
1928 * call-seq:
1929 * InstructionSequence.compile_option = options
1930 *
1931 * Sets the default values for various optimizations in the Ruby iseq
1932 * compiler.
1933 *
1934 * Possible values for +options+ include +true+, which enables all options,
1935 * +false+ which disables all options, and +nil+ which leaves all options
1936 * unchanged.
1937 *
1938 * You can also pass a +Hash+ of +options+ that you want to change, any
1939 * options not present in the hash will be left unchanged.
1940 *
1941 * Possible option names (which are keys in +options+) which can be set to
1942 * +true+ or +false+ include:
1943 *
1944 * * +:inline_const_cache+
1945 * * +:instructions_unification+
1946 * * +:operands_unification+
1947 * * +:peephole_optimization+
1948 * * +:specialized_instruction+
1949 * * +:tailcall_optimization+
1950 *
1951 * Additionally, +:debug_level+ can be set to an integer.
1952 *
1953 * These default options can be overwritten for a single run of the iseq
1954 * compiler by passing any of the above values as the +options+ parameter to
1955 * ::new, ::compile and ::compile_file.
1956 */
1957static VALUE
1958iseqw_s_compile_option_set(VALUE self, VALUE opt)
1959{
1960 rb_compile_option_t option;
1961 make_compile_option(&option, opt);
1962 COMPILE_OPTION_DEFAULT = option;
1963 return opt;
1964}
1965
1966/*
1967 * call-seq:
1968 * InstructionSequence.compile_option -> options
1969 *
1970 * Returns a hash of default options used by the Ruby iseq compiler.
1971 *
1972 * For details, see InstructionSequence.compile_option=.
1973 */
1974static VALUE
1975iseqw_s_compile_option_get(VALUE self)
1976{
1977 return make_compile_option_value(&COMPILE_OPTION_DEFAULT);
1978}
1979
1980static const rb_iseq_t *
1981iseqw_check(VALUE iseqw)
1982{
1983 rb_iseq_t **iseq_ptr;
1984 TypedData_Get_Struct(iseqw, rb_iseq_t *, &iseqw_data_type, iseq_ptr);
1985 rb_iseq_t *iseq = *iseq_ptr;
1986
1987 if (!ISEQ_BODY(iseq)) {
1988 rb_ibf_load_iseq_complete(iseq);
1989 }
1990
1991 if (!ISEQ_BODY(iseq)->location.label) {
1992 rb_raise(rb_eTypeError, "uninitialized InstructionSequence");
1993 }
1994 return iseq;
1995}
1996
1997const rb_iseq_t *
1998rb_iseqw_to_iseq(VALUE iseqw)
1999{
2000 return iseqw_check(iseqw);
2001}
2002
2003/*
2004 * call-seq:
2005 * iseq.eval -> obj
2006 *
2007 * Evaluates the instruction sequence and returns the result.
2008 *
2009 * RubyVM::InstructionSequence.compile("1 + 2").eval #=> 3
2010 */
2011static VALUE
2012iseqw_eval(VALUE self)
2013{
2014 const rb_iseq_t *iseq = iseqw_check(self);
2015 if (0 == ISEQ_BODY(iseq)->iseq_size) {
2016 rb_raise(rb_eTypeError, "attempt to evaluate dummy InstructionSequence");
2017 }
2018 return rb_iseq_eval(iseq, rb_current_box());
2019}
2020
2021/*
2022 * Returns a human-readable string representation of this instruction
2023 * sequence, including the #label and #path.
2024 */
2025static VALUE
2026iseqw_inspect(VALUE self)
2027{
2028 const rb_iseq_t *iseq = iseqw_check(self);
2029 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
2030 VALUE klass = rb_class_name(rb_obj_class(self));
2031
2032 if (!body->location.label) {
2033 return rb_sprintf("#<%"PRIsVALUE": uninitialized>", klass);
2034 }
2035 else {
2036 return rb_sprintf("<%"PRIsVALUE":%"PRIsVALUE"@%"PRIsVALUE":%d>",
2037 klass,
2038 body->location.label, rb_iseq_path(iseq),
2039 FIX2INT(rb_iseq_first_lineno(iseq)));
2040 }
2041}
2042
2043/*
2044 * Returns the path of this instruction sequence.
2045 *
2046 * <code><compiled></code> if the iseq was evaluated from a string.
2047 *
2048 * For example, using irb:
2049 *
2050 * iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
2051 * #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
2052 * iseq.path
2053 * #=> "<compiled>"
2054 *
2055 * Using ::compile_file:
2056 *
2057 * # /tmp/method.rb
2058 * def hello
2059 * puts "hello, world"
2060 * end
2061 *
2062 * # in irb
2063 * > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
2064 * > iseq.path #=> /tmp/method.rb
2065 */
2066static VALUE
2067iseqw_path(VALUE self)
2068{
2069 return rb_iseq_path(iseqw_check(self));
2070}
2071
2072/*
2073 * Returns the absolute path of this instruction sequence.
2074 *
2075 * +nil+ if the iseq was evaluated from a string.
2076 *
2077 * For example, using ::compile_file:
2078 *
2079 * # /tmp/method.rb
2080 * def hello
2081 * puts "hello, world"
2082 * end
2083 *
2084 * # in irb
2085 * > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
2086 * > iseq.absolute_path #=> /tmp/method.rb
2087 */
2088static VALUE
2089iseqw_absolute_path(VALUE self)
2090{
2091 return rb_iseq_realpath(iseqw_check(self));
2092}
2093
2094/* Returns the label of this instruction sequence.
2095 *
2096 * <code><main></code> if it's at the top level, <code><compiled></code> if it
2097 * was evaluated from a string.
2098 *
2099 * For example, using irb:
2100 *
2101 * iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
2102 * #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
2103 * iseq.label
2104 * #=> "<compiled>"
2105 *
2106 * Using ::compile_file:
2107 *
2108 * # /tmp/method.rb
2109 * def hello
2110 * puts "hello, world"
2111 * end
2112 *
2113 * # in irb
2114 * > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
2115 * > iseq.label #=> <main>
2116 */
2117static VALUE
2118iseqw_label(VALUE self)
2119{
2120 return rb_iseq_label(iseqw_check(self));
2121}
2122
2123/* Returns the base label of this instruction sequence.
2124 *
2125 * For example, using irb:
2126 *
2127 * iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
2128 * #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
2129 * iseq.base_label
2130 * #=> "<compiled>"
2131 *
2132 * Using ::compile_file:
2133 *
2134 * # /tmp/method.rb
2135 * def hello
2136 * puts "hello, world"
2137 * end
2138 *
2139 * # in irb
2140 * > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
2141 * > iseq.base_label #=> <main>
2142 */
2143static VALUE
2144iseqw_base_label(VALUE self)
2145{
2146 return rb_iseq_base_label(iseqw_check(self));
2147}
2148
2149/* Returns the number of the first source line where the instruction sequence
2150 * was loaded from.
2151 *
2152 * For example, using irb:
2153 *
2154 * iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
2155 * #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
2156 * iseq.first_lineno
2157 * #=> 1
2158 */
2159static VALUE
2160iseqw_first_lineno(VALUE self)
2161{
2162 return rb_iseq_first_lineno(iseqw_check(self));
2163}
2164
2165static VALUE iseq_data_to_ary(const rb_iseq_t *iseq);
2166
2167/*
2168 * call-seq:
2169 * iseq.to_a -> ary
2170 *
2171 * Returns an Array with 14 elements representing the instruction sequence
2172 * with the following data:
2173 *
2174 * [magic]
2175 * A string identifying the data format. <b>Always
2176 * +YARVInstructionSequence/SimpleDataFormat+.</b>
2177 *
2178 * [major_version]
2179 * The major version of the instruction sequence.
2180 *
2181 * [minor_version]
2182 * The minor version of the instruction sequence.
2183 *
2184 * [format_type]
2185 * A number identifying the data format. <b>Always 1</b>.
2186 *
2187 * [misc]
2188 * A hash containing:
2189 *
2190 * [+:arg_size+]
2191 * the total number of arguments taken by the method or the block (0 if
2192 * _iseq_ doesn't represent a method or block)
2193 * [+:local_size+]
2194 * the number of local variables + 1
2195 * [+:stack_max+]
2196 * used in calculating the stack depth at which a SystemStackError is
2197 * thrown.
2198 *
2199 * [#label]
2200 * The name of the context (block, method, class, module, etc.) that this
2201 * instruction sequence belongs to.
2202 *
2203 * <code><main></code> if it's at the top level, <code><compiled></code> if
2204 * it was evaluated from a string.
2205 *
2206 * [#path]
2207 * The relative path to the Ruby file where the instruction sequence was
2208 * loaded from.
2209 *
2210 * <code><compiled></code> if the iseq was evaluated from a string.
2211 *
2212 * [#absolute_path]
2213 * The absolute path to the Ruby file where the instruction sequence was
2214 * loaded from.
2215 *
2216 * +nil+ if the iseq was evaluated from a string.
2217 *
2218 * [#first_lineno]
2219 * The number of the first source line where the instruction sequence was
2220 * loaded from.
2221 *
2222 * [type]
2223 * The type of the instruction sequence.
2224 *
2225 * Valid values are +:top+, +:method+, +:block+, +:class+, +:rescue+,
2226 * +:ensure+, +:eval+, +:main+, and +plain+.
2227 *
2228 * [locals]
2229 * An array containing the names of all arguments and local variables as
2230 * symbols.
2231 *
2232 * [params]
2233 * An Hash object containing parameter information.
2234 *
2235 * More info about these values can be found in +vm_core.h+.
2236 *
2237 * [catch_table]
2238 * A list of exceptions and control flow operators (rescue, next, redo,
2239 * break, etc.).
2240 *
2241 * [bytecode]
2242 * An array of arrays containing the instruction names and operands that
2243 * make up the body of the instruction sequence.
2244 *
2245 * Note that this format is MRI specific and version dependent.
2246 *
2247 */
2248static VALUE
2249iseqw_to_a(VALUE self)
2250{
2251 const rb_iseq_t *iseq = iseqw_check(self);
2252 return iseq_data_to_ary(iseq);
2253}
2254
2255#if VM_INSN_INFO_TABLE_IMPL == 1 /* binary search */
2256static const struct iseq_insn_info_entry *
2257get_insn_info_binary_search(const rb_iseq_t *iseq, size_t pos)
2258{
2259 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
2260 size_t size = body->insns_info.size;
2261 const struct iseq_insn_info_entry *insns_info = body->insns_info.body;
2262 const unsigned int *positions = body->insns_info.positions;
2263 const int debug = 0;
2264
2265 if (debug) {
2266 printf("size: %"PRIuSIZE"\n", size);
2267 printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
2268 (size_t)0, positions[0], insns_info[0].line_no, pos);
2269 }
2270
2271 if (size == 0) {
2272 return NULL;
2273 }
2274 else if (size == 1) {
2275 return &insns_info[0];
2276 }
2277 else {
2278 size_t l = 1, r = size - 1;
2279 while (l <= r) {
2280 size_t m = l + (r - l) / 2;
2281 if (positions[m] == pos) {
2282 return &insns_info[m];
2283 }
2284 if (positions[m] < pos) {
2285 l = m + 1;
2286 }
2287 else {
2288 r = m - 1;
2289 }
2290 }
2291 if (l >= size) {
2292 return &insns_info[size-1];
2293 }
2294 if (positions[l] > pos) {
2295 return &insns_info[l-1];
2296 }
2297 return &insns_info[l];
2298 }
2299}
2300
2301static const struct iseq_insn_info_entry *
2302get_insn_info(const rb_iseq_t *iseq, size_t pos)
2303{
2304 return get_insn_info_binary_search(iseq, pos);
2305}
2306#endif
2307
2308#if VM_INSN_INFO_TABLE_IMPL == 2 /* succinct bitvector */
2309static const struct iseq_insn_info_entry *
2310get_insn_info_succinct_bitvector(const rb_iseq_t *iseq, size_t pos)
2311{
2312 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
2313 size_t size = body->insns_info.size;
2314 const struct iseq_insn_info_entry *insns_info = body->insns_info.body;
2315 const int debug = 0;
2316
2317 if (debug) {
2318#if VM_CHECK_MODE > 0
2319 const unsigned int *positions = body->insns_info.positions;
2320 printf("size: %"PRIuSIZE"\n", size);
2321 printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
2322 (size_t)0, positions[0], insns_info[0].line_no, pos);
2323#else
2324 printf("size: %"PRIuSIZE"\n", size);
2325 printf("insns_info[%"PRIuSIZE"]: line: %d, pos: %"PRIuSIZE"\n",
2326 (size_t)0, insns_info[0].line_no, pos);
2327#endif
2328 }
2329
2330 if (size == 0) {
2331 return NULL;
2332 }
2333 else if (size == 1) {
2334 return &insns_info[0];
2335 }
2336 else {
2337 int index;
2338 VM_ASSERT(body->insns_info.succ_index_table != NULL);
2339 index = succ_index_lookup(body->insns_info.succ_index_table, (int)pos);
2340 return &insns_info[index-1];
2341 }
2342}
2343
2344static const struct iseq_insn_info_entry *
2345get_insn_info(const rb_iseq_t *iseq, size_t pos)
2346{
2347 return get_insn_info_succinct_bitvector(iseq, pos);
2348}
2349#endif
2350
2351#if VM_CHECK_MODE > 0 || VM_INSN_INFO_TABLE_IMPL == 0
2352static const struct iseq_insn_info_entry *
2353get_insn_info_linear_search(const rb_iseq_t *iseq, size_t pos)
2354{
2355 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
2356 size_t i = 0, size = body->insns_info.size;
2357 const struct iseq_insn_info_entry *insns_info = body->insns_info.body;
2358 const unsigned int *positions = body->insns_info.positions;
2359 const int debug = 0;
2360
2361 if (debug) {
2362 printf("size: %"PRIuSIZE"\n", size);
2363 printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
2364 i, positions[i], insns_info[i].line_no, pos);
2365 }
2366
2367 if (size == 0) {
2368 return NULL;
2369 }
2370 else if (size == 1) {
2371 return &insns_info[0];
2372 }
2373 else {
2374 for (i=1; i<size; i++) {
2375 if (debug) printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
2376 i, positions[i], insns_info[i].line_no, pos);
2377
2378 if (positions[i] == pos) {
2379 return &insns_info[i];
2380 }
2381 if (positions[i] > pos) {
2382 return &insns_info[i-1];
2383 }
2384 }
2385 }
2386 return &insns_info[i-1];
2387}
2388#endif
2389
2390#if VM_INSN_INFO_TABLE_IMPL == 0 /* linear search */
2391static const struct iseq_insn_info_entry *
2392get_insn_info(const rb_iseq_t *iseq, size_t pos)
2393{
2394 return get_insn_info_linear_search(iseq, pos);
2395}
2396#endif
2397
2398#if VM_CHECK_MODE > 0 && VM_INSN_INFO_TABLE_IMPL > 0
2399static void
2400validate_get_insn_info(const rb_iseq_t *iseq)
2401{
2402 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
2403 size_t i;
2404 for (i = 0; i < body->iseq_size; i++) {
2405 if (get_insn_info_linear_search(iseq, i) != get_insn_info(iseq, i)) {
2406 rb_bug("validate_get_insn_info: get_insn_info_linear_search(iseq, %"PRIuSIZE") != get_insn_info(iseq, %"PRIuSIZE")", i, i);
2407 }
2408 }
2409}
2410#endif
2411
2412unsigned int
2413rb_iseq_line_no(const rb_iseq_t *iseq, size_t pos)
2414{
2415 const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pos);
2416
2417 if (entry) {
2418 return entry->line_no;
2419 }
2420 else {
2421 return 0;
2422 }
2423}
2424
2425#ifdef USE_ISEQ_NODE_ID
2426int
2427rb_iseq_node_id(const rb_iseq_t *iseq, size_t pos)
2428{
2429 const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pos);
2430
2431 if (entry) {
2432 return entry->node_id;
2433 }
2434 else {
2435 return 0;
2436 }
2437}
2438#endif
2439
2441rb_iseq_event_flags(const rb_iseq_t *iseq, size_t pos)
2442{
2443 const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pos);
2444 if (entry) {
2445 return entry->events;
2446 }
2447 else {
2448 return 0;
2449 }
2450}
2451
2452static void rb_iseq_trace_flag_cleared(const rb_iseq_t *iseq, size_t pos);
2453
2454// Clear tracing event flags and turn off tracing for a given instruction as needed.
2455// This is currently used after updating a one-shot line coverage for the current instruction.
2456void
2457rb_iseq_clear_event_flags(const rb_iseq_t *iseq, size_t pos, rb_event_flag_t reset)
2458{
2459 RB_VM_LOCKING() {
2460 rb_vm_barrier();
2461
2462 struct iseq_insn_info_entry *entry = (struct iseq_insn_info_entry *)get_insn_info(iseq, pos);
2463 if (entry) {
2464 entry->events &= ~reset;
2465 if (!(entry->events & iseq->aux.exec.global_trace_events)) {
2466 rb_iseq_trace_flag_cleared(iseq, pos);
2467 }
2468 }
2469 }
2470}
2471
2472static VALUE
2473local_var_name(const rb_iseq_t *diseq, VALUE level, VALUE op)
2474{
2475 VALUE i;
2476 VALUE name;
2477 ID lid;
2478 int idx;
2479
2480 for (i = 0; i < level; i++) {
2481 diseq = ISEQ_BODY(diseq)->parent_iseq;
2482 }
2483 idx = ISEQ_BODY(diseq)->local_table_size - (int)op - 1;
2484 lid = ISEQ_BODY(diseq)->local_table[idx];
2485 name = rb_id2str(lid);
2486 if (!name) {
2487 name = rb_str_new_cstr("?");
2488 }
2489 else if (!rb_is_local_id(lid)) {
2490 name = rb_str_inspect(name);
2491 }
2492 else {
2493 name = rb_str_dup(name);
2494 }
2495 rb_str_catf(name, "@%d", idx);
2496 return name;
2497}
2498
2499int rb_insn_unified_local_var_level(VALUE);
2500VALUE rb_dump_literal(VALUE lit);
2501
2502VALUE
2503rb_insn_operand_intern(const rb_iseq_t *iseq,
2504 VALUE insn, int op_no, VALUE op,
2505 int len, size_t pos, const VALUE *pnop, VALUE child)
2506{
2507 const char *types = insn_op_types(insn);
2508 char type = types[op_no];
2509 VALUE ret = Qundef;
2510
2511 switch (type) {
2512 case TS_OFFSET: /* LONG */
2513 ret = rb_sprintf("%"PRIdVALUE, (VALUE)(pos + len + op));
2514 break;
2515
2516 case TS_NUM: /* ULONG */
2517 if (insn == BIN(defined) && op_no == 0) {
2518 enum defined_type deftype = (enum defined_type)op;
2519 switch (deftype) {
2520 case DEFINED_FUNC:
2521 ret = rb_fstring_lit("func");
2522 break;
2523 case DEFINED_REF:
2524 ret = rb_fstring_lit("ref");
2525 break;
2526 case DEFINED_CONST_FROM:
2527 ret = rb_fstring_lit("constant-from");
2528 break;
2529 default:
2530 ret = rb_iseq_defined_string(deftype);
2531 break;
2532 }
2533 if (ret) break;
2534 }
2535 else if (insn == BIN(checktype) && op_no == 0) {
2536 const char *type_str = rb_type_str((enum ruby_value_type)op);
2537 if (type_str) {
2538 ret = rb_str_new_cstr(type_str); break;
2539 }
2540 }
2541 ret = rb_sprintf("%"PRIuVALUE, op);
2542 break;
2543
2544 case TS_LINDEX:{
2545 int level;
2546 if (types[op_no+1] == TS_NUM && pnop) {
2547 ret = local_var_name(iseq, *pnop, op - VM_ENV_DATA_SIZE);
2548 }
2549 else if ((level = rb_insn_unified_local_var_level(insn)) >= 0) {
2550 ret = local_var_name(iseq, (VALUE)level, op - VM_ENV_DATA_SIZE);
2551 }
2552 else {
2553 ret = rb_inspect(INT2FIX(op));
2554 }
2555 break;
2556 }
2557 case TS_ID: /* ID (symbol) */
2558 ret = rb_inspect(ID2SYM(op));
2559 break;
2560
2561 case TS_VALUE: /* VALUE */
2562 op = obj_resurrect(op);
2563 if (insn == BIN(defined) && op_no == 1 && FIXNUM_P(op)) {
2564 /* should be DEFINED_REF */
2565 int type = NUM2INT(op);
2566 if (type) {
2567 if (type & 1) {
2568 ret = rb_sprintf(":$%c", (type >> 1));
2569 }
2570 else {
2571 ret = rb_sprintf(":$%d", (type >> 1));
2572 }
2573 break;
2574 }
2575 }
2576 ret = rb_dump_literal(op);
2577 if (CLASS_OF(op) == rb_cISeq) {
2578 if (child) {
2579 rb_ary_push(child, op);
2580 }
2581 }
2582 break;
2583
2584 case TS_ISEQ: /* iseq */
2585 {
2586 if (op) {
2587 const rb_iseq_t *iseq = rb_iseq_check((rb_iseq_t *)op);
2588 ret = ISEQ_BODY(iseq)->location.label;
2589 if (child) {
2590 rb_ary_push(child, (VALUE)iseq);
2591 }
2592 }
2593 else {
2594 ret = rb_str_new2("nil");
2595 }
2596 break;
2597 }
2598
2599 case TS_IC:
2600 {
2601 ret = rb_sprintf("<ic:%"PRIdPTRDIFF" ", (union iseq_inline_storage_entry *)op - ISEQ_BODY(iseq)->is_entries);
2602 const ID *segments = ((IC)op)->segments;
2603 rb_str_cat2(ret, rb_id2name(*segments++));
2604 while (*segments) {
2605 rb_str_catf(ret, "::%s", rb_id2name(*segments++));
2606 }
2607 rb_str_cat2(ret, ">");
2608 }
2609 break;
2610 case TS_IVC:
2611 case TS_ICVARC:
2612 case TS_ISE:
2613 ret = rb_sprintf("<is:%"PRIdPTRDIFF">", (union iseq_inline_storage_entry *)op - ISEQ_BODY(iseq)->is_entries);
2614 break;
2615
2616 case TS_CALLDATA:
2617 {
2618 struct rb_call_data *cd = (struct rb_call_data *)op;
2619 const struct rb_callinfo *ci = cd->ci;
2620 VALUE ary = rb_ary_new();
2621 ID mid = vm_ci_mid(ci);
2622
2623 if (mid) {
2624 rb_ary_push(ary, rb_sprintf("mid:%"PRIsVALUE, rb_id2str(mid)));
2625 }
2626
2627 rb_ary_push(ary, rb_sprintf("argc:%d", vm_ci_argc(ci)));
2628
2629 if (vm_ci_flag(ci) & VM_CALL_KWARG) {
2630 const struct rb_callinfo_kwarg *kw_args = vm_ci_kwarg(ci);
2631 VALUE kw_ary = rb_ary_new_from_values(kw_args->keyword_len, kw_args->keywords);
2632 rb_ary_push(ary, rb_sprintf("kw:[%"PRIsVALUE"]", rb_ary_join(kw_ary, rb_str_new2(","))));
2633 }
2634
2635 if (vm_ci_flag(ci)) {
2636 VALUE flags = rb_ary_new();
2637# define CALL_FLAG(n) if (vm_ci_flag(ci) & VM_CALL_##n) rb_ary_push(flags, rb_str_new2(#n))
2638 CALL_FLAG(ARGS_SPLAT);
2639 CALL_FLAG(ARGS_SPLAT_MUT);
2640 CALL_FLAG(ARGS_BLOCKARG);
2641 CALL_FLAG(FCALL);
2642 CALL_FLAG(VCALL);
2643 CALL_FLAG(ARGS_SIMPLE);
2644 CALL_FLAG(TAILCALL);
2645 CALL_FLAG(SUPER);
2646 CALL_FLAG(ZSUPER);
2647 CALL_FLAG(KWARG);
2648 CALL_FLAG(KW_SPLAT);
2649 CALL_FLAG(KW_SPLAT_MUT);
2650 CALL_FLAG(FORWARDING);
2651 CALL_FLAG(OPT_SEND); /* maybe not reachable */
2652 rb_ary_push(ary, rb_ary_join(flags, rb_str_new2("|")));
2653 }
2654
2655 ret = rb_sprintf("<calldata!%"PRIsVALUE">", rb_ary_join(ary, rb_str_new2(", ")));
2656 }
2657 break;
2658
2659 case TS_CDHASH:
2660 ret = rb_str_new2("<cdhash>");
2661 break;
2662
2663 case TS_FUNCPTR:
2664 {
2665#ifdef HAVE_DLADDR
2666 Dl_info info;
2667 if (dladdr((void *)op, &info) && info.dli_sname) {
2668 ret = rb_str_new_cstr(info.dli_sname);
2669 break;
2670 }
2671#endif
2672 ret = rb_str_new2("<funcptr>");
2673 }
2674 break;
2675
2676 case TS_BUILTIN:
2677 {
2678 const struct rb_builtin_function *bf = (const struct rb_builtin_function *)op;
2679 ret = rb_sprintf("<builtin!%s/%d>",
2680 bf->name, bf->argc);
2681 }
2682 break;
2683
2684 default:
2685 rb_bug("unknown operand type: %c", type);
2686 }
2687 return ret;
2688}
2689
2690static VALUE
2691right_strip(VALUE str)
2692{
2693 const char *beg = RSTRING_PTR(str), *end = RSTRING_END(str);
2694 while (end-- > beg && *end == ' ');
2695 rb_str_set_len(str, end - beg + 1);
2696 return str;
2697}
2698
2703int
2704rb_iseq_disasm_insn(VALUE ret, const VALUE *code, size_t pos,
2705 const rb_iseq_t *iseq, VALUE child)
2706{
2707 VALUE insn = code[pos];
2708 int len = insn_len(insn);
2709 int j;
2710 const char *types = insn_op_types(insn);
2711 VALUE str = rb_str_new(0, 0);
2712 const char *insn_name_buff;
2713
2714 insn_name_buff = insn_name(insn);
2715 if (1) {
2716 extern const int rb_vm_max_insn_name_size;
2717 rb_str_catf(str, "%04"PRIuSIZE" %-*s ", pos, rb_vm_max_insn_name_size, insn_name_buff);
2718 }
2719 else {
2720 rb_str_catf(str, "%04"PRIuSIZE" %-28.*s ", pos,
2721 (int)strcspn(insn_name_buff, "_"), insn_name_buff);
2722 }
2723
2724 for (j = 0; types[j]; j++) {
2725 VALUE opstr = rb_insn_operand_intern(iseq, insn, j, code[pos + j + 1],
2726 len, pos, &code[pos + j + 2],
2727 child);
2728 rb_str_concat(str, opstr);
2729
2730 if (types[j + 1]) {
2731 rb_str_cat2(str, ", ");
2732 }
2733 }
2734
2735 {
2736 unsigned int line_no = rb_iseq_line_no(iseq, pos);
2737 unsigned int prev = pos == 0 ? 0 : rb_iseq_line_no(iseq, pos - 1);
2738 if (line_no && line_no != prev) {
2739 long slen = RSTRING_LEN(str);
2740 slen = (slen > 70) ? 0 : (70 - slen);
2741 str = rb_str_catf(str, "%*s(%4d)", (int)slen, "", line_no);
2742 }
2743 }
2744
2745 {
2746 rb_event_flag_t events = rb_iseq_event_flags(iseq, pos);
2747 if (events) {
2748 str = rb_str_catf(str, "[%s%s%s%s%s%s%s%s%s%s%s%s]",
2749 events & RUBY_EVENT_LINE ? "Li" : "",
2750 events & RUBY_EVENT_CLASS ? "Cl" : "",
2751 events & RUBY_EVENT_END ? "En" : "",
2752 events & RUBY_EVENT_CALL ? "Ca" : "",
2753 events & RUBY_EVENT_RETURN ? "Re" : "",
2754 events & RUBY_EVENT_C_CALL ? "Cc" : "",
2755 events & RUBY_EVENT_C_RETURN ? "Cr" : "",
2756 events & RUBY_EVENT_B_CALL ? "Bc" : "",
2757 events & RUBY_EVENT_B_RETURN ? "Br" : "",
2758 events & RUBY_EVENT_RESCUE ? "Rs" : "",
2759 events & RUBY_EVENT_COVERAGE_LINE ? "Cli" : "",
2760 events & RUBY_EVENT_COVERAGE_BRANCH ? "Cbr" : "");
2761 }
2762 }
2763
2764 right_strip(str);
2765 if (ret) {
2766 rb_str_cat2(str, "\n");
2767 rb_str_concat(ret, str);
2768 }
2769 else {
2770 printf("%.*s\n", (int)RSTRING_LEN(str), RSTRING_PTR(str));
2771 }
2772 return len;
2773}
2774
2775static const char *
2776catch_type(int type)
2777{
2778 switch (type) {
2779 case CATCH_TYPE_RESCUE:
2780 return "rescue";
2781 case CATCH_TYPE_ENSURE:
2782 return "ensure";
2783 case CATCH_TYPE_RETRY:
2784 return "retry";
2785 case CATCH_TYPE_BREAK:
2786 return "break";
2787 case CATCH_TYPE_REDO:
2788 return "redo";
2789 case CATCH_TYPE_NEXT:
2790 return "next";
2791 default:
2792 rb_bug("unknown catch type: %d", type);
2793 return 0;
2794 }
2795}
2796
2797static VALUE
2798iseq_inspect(const rb_iseq_t *iseq)
2799{
2800 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
2801 if (!body->location.label) {
2802 return rb_sprintf("#<ISeq: uninitialized>");
2803 }
2804 else {
2805 const rb_code_location_t *loc = &body->location.code_location;
2806 return rb_sprintf("#<ISeq:%"PRIsVALUE"@%"PRIsVALUE":%d (%d,%d)-(%d,%d)>",
2807 body->location.label, rb_iseq_path(iseq),
2808 loc->beg_pos.lineno,
2809 loc->beg_pos.lineno,
2810 loc->beg_pos.column,
2811 loc->end_pos.lineno,
2812 loc->end_pos.column);
2813 }
2814}
2815
2816static const rb_data_type_t tmp_set = {
2817 "tmpset",
2818 {(void (*)(void *))rb_mark_set, (void (*)(void *))st_free_table, 0, 0,},
2820};
2821
2822static VALUE
2823rb_iseq_disasm_recursive(const rb_iseq_t *iseq, VALUE indent)
2824{
2825 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
2826 VALUE *code;
2827 VALUE str = rb_str_new(0, 0);
2828 VALUE child = rb_ary_hidden_new(3);
2829 unsigned int size;
2830 unsigned int i;
2831 long l;
2832 size_t n;
2833 enum {header_minlen = 72};
2834 st_table *done_iseq = 0;
2835 VALUE done_iseq_wrapper = Qnil;
2836 const char *indent_str;
2837 long indent_len;
2838
2839 size = body->iseq_size;
2840
2841 indent_len = RSTRING_LEN(indent);
2842 indent_str = RSTRING_PTR(indent);
2843
2844 rb_str_cat(str, indent_str, indent_len);
2845 rb_str_cat2(str, "== disasm: ");
2846
2847 rb_str_append(str, iseq_inspect(iseq));
2848 if ((l = RSTRING_LEN(str) - indent_len) < header_minlen) {
2849 rb_str_modify_expand(str, header_minlen - l);
2850 memset(RSTRING_END(str), '=', header_minlen - l);
2851 }
2852 if (iseq->body->builtin_attrs) {
2853#define disasm_builtin_attr(str, iseq, attr) \
2854 if (iseq->body->builtin_attrs & BUILTIN_ATTR_ ## attr) { \
2855 rb_str_cat2(str, " " #attr); \
2856 }
2857 disasm_builtin_attr(str, iseq, LEAF);
2858 disasm_builtin_attr(str, iseq, SINGLE_NOARG_LEAF);
2859 disasm_builtin_attr(str, iseq, INLINE_BLOCK);
2860 disasm_builtin_attr(str, iseq, C_TRACE);
2861 }
2862 rb_str_cat2(str, "\n");
2863
2864 /* show catch table information */
2865 if (body->catch_table) {
2866 rb_str_cat(str, indent_str, indent_len);
2867 rb_str_cat2(str, "== catch table\n");
2868 }
2869 if (body->catch_table) {
2870 rb_str_cat_cstr(indent, "| ");
2871 indent_str = RSTRING_PTR(indent);
2872 for (i = 0; i < body->catch_table->size; i++) {
2873 const struct iseq_catch_table_entry *entry =
2874 UNALIGNED_MEMBER_PTR(body->catch_table, entries[i]);
2875 rb_str_cat(str, indent_str, indent_len);
2876 rb_str_catf(str,
2877 "| catch type: %-6s st: %04d ed: %04d sp: %04d cont: %04d\n",
2878 catch_type((int)entry->type), (int)entry->start,
2879 (int)entry->end, (int)entry->sp, (int)entry->cont);
2880 if (entry->iseq && !(done_iseq && st_is_member(done_iseq, (st_data_t)entry->iseq))) {
2881 rb_str_concat(str, rb_iseq_disasm_recursive(rb_iseq_check(entry->iseq), indent));
2882 if (!done_iseq) {
2883 done_iseq = st_init_numtable();
2884 done_iseq_wrapper = TypedData_Wrap_Struct(0, &tmp_set, done_iseq);
2885 }
2886 st_insert(done_iseq, (st_data_t)entry->iseq, (st_data_t)0);
2887 indent_str = RSTRING_PTR(indent);
2888 }
2889 }
2890 rb_str_resize(indent, indent_len);
2891 indent_str = RSTRING_PTR(indent);
2892 }
2893 if (body->catch_table) {
2894 rb_str_cat(str, indent_str, indent_len);
2895 rb_str_cat2(str, "|-------------------------------------"
2896 "-----------------------------------\n");
2897 }
2898
2899 /* show local table information */
2900 if (body->local_table) {
2901 const struct rb_iseq_param_keyword *const keyword = body->param.keyword;
2902 rb_str_cat(str, indent_str, indent_len);
2903 rb_str_catf(str,
2904 "local table (size: %d, argc: %d "
2905 "[opts: %d, rest: %d, post: %d, block: %d, kw: %d@%d, kwrest: %d])\n",
2906 body->local_table_size,
2907 body->param.lead_num,
2908 body->param.opt_num,
2909 body->param.flags.has_rest ? body->param.rest_start : -1,
2910 body->param.post_num,
2911 body->param.flags.has_block ? body->param.block_start : -1,
2912 body->param.flags.has_kw ? keyword->num : -1,
2913 body->param.flags.has_kw ? keyword->required_num : -1,
2914 body->param.flags.has_kwrest ? keyword->rest_start : -1);
2915
2916 for (i = body->local_table_size; i > 0;) {
2917 int li = body->local_table_size - --i - 1;
2918 long width;
2919 VALUE name = local_var_name(iseq, 0, i);
2920 char argi[0x100];
2921 char opti[0x100];
2922
2923 opti[0] = '\0';
2924 if (body->param.flags.has_opt) {
2925 int argc = body->param.lead_num;
2926 int opts = body->param.opt_num;
2927 if (li >= argc && li < argc + opts) {
2928 snprintf(opti, sizeof(opti), "Opt=%"PRIdVALUE,
2929 body->param.opt_table[li - argc]);
2930 }
2931 }
2932
2933 snprintf(argi, sizeof(argi), "%s%s%s%s%s%s", /* arg, opts, rest, post, kwrest, block */
2934 (body->param.lead_num > li) ? (body->param.flags.ambiguous_param0 ? "AmbiguousArg" : "Arg") : "",
2935 opti,
2936 (body->param.flags.has_rest && body->param.rest_start == li) ? (body->param.flags.anon_rest ? "AnonRest" : "Rest") : "",
2937 (body->param.flags.has_post && body->param.post_start <= li && li < body->param.post_start + body->param.post_num) ? "Post" : "",
2938 (body->param.flags.has_kwrest && keyword->rest_start == li) ? (body->param.flags.anon_kwrest ? "AnonKwrest" : "Kwrest") : "",
2939 (body->param.flags.has_block && body->param.block_start == li) ? "Block" : "");
2940
2941 rb_str_cat(str, indent_str, indent_len);
2942 rb_str_catf(str, "[%2d] ", i + 1);
2943 width = RSTRING_LEN(str) + 11;
2944 rb_str_append(str, name);
2945 if (*argi) rb_str_catf(str, "<%s>", argi);
2946 if ((width -= RSTRING_LEN(str)) > 0) rb_str_catf(str, "%*s", (int)width, "");
2947 }
2948 rb_str_cat_cstr(right_strip(str), "\n");
2949 }
2950
2951 /* show each line */
2952 code = rb_iseq_original_iseq(iseq);
2953 for (n = 0; n < size;) {
2954 rb_str_cat(str, indent_str, indent_len);
2955 n += rb_iseq_disasm_insn(str, code, n, iseq, child);
2956 }
2957
2958 for (l = 0; l < RARRAY_LEN(child); l++) {
2959 VALUE isv = rb_ary_entry(child, l);
2960 if (done_iseq && st_is_member(done_iseq, (st_data_t)isv)) continue;
2961 rb_str_cat_cstr(str, "\n");
2962 rb_str_concat(str, rb_iseq_disasm_recursive(rb_iseq_check((rb_iseq_t *)isv), indent));
2963 indent_str = RSTRING_PTR(indent);
2964 }
2965 RB_GC_GUARD(done_iseq_wrapper);
2966
2967 return str;
2968}
2969
2970VALUE
2971rb_iseq_disasm(const rb_iseq_t *iseq)
2972{
2973 VALUE str = rb_iseq_disasm_recursive(iseq, rb_str_new(0, 0));
2974 rb_str_resize(str, RSTRING_LEN(str));
2975 return str;
2976}
2977
2978/*
2979 * Estimates the number of instance variables that will be set on
2980 * a given `class` with the initialize method defined in
2981 * `initialize_iseq`
2982 */
2983attr_index_t
2984rb_estimate_iv_count(VALUE klass, const rb_iseq_t * initialize_iseq)
2985{
2986 struct rb_id_table * iv_names = rb_id_table_create(0);
2987
2988 for (unsigned int i = 0; i < ISEQ_BODY(initialize_iseq)->ivc_size; i++) {
2989 IVC cache = (IVC)&ISEQ_BODY(initialize_iseq)->is_entries[i];
2990
2991 if (cache->iv_set_name) {
2992 rb_id_table_insert(iv_names, cache->iv_set_name, Qtrue);
2993 }
2994 }
2995
2996 attr_index_t count = (attr_index_t)rb_id_table_size(iv_names);
2997
2998 VALUE superclass = rb_class_superclass(klass);
2999 count += RCLASS_MAX_IV_COUNT(superclass);
3000
3001 rb_id_table_free(iv_names);
3002
3003 return count;
3004}
3005
3006/*
3007 * call-seq:
3008 * iseq.disasm -> str
3009 * iseq.disassemble -> str
3010 *
3011 * Returns the instruction sequence as a +String+ in human readable form.
3012 *
3013 * puts RubyVM::InstructionSequence.compile('1 + 2').disasm
3014 *
3015 * Produces:
3016 *
3017 * == disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
3018 * 0000 trace 1 ( 1)
3019 * 0002 putobject 1
3020 * 0004 putobject 2
3021 * 0006 opt_plus <ic:1>
3022 * 0008 leave
3023 */
3024static VALUE
3025iseqw_disasm(VALUE self)
3026{
3027 return rb_iseq_disasm(iseqw_check(self));
3028}
3029
3030static int
3031iseq_iterate_children(const rb_iseq_t *iseq, void (*iter_func)(const rb_iseq_t *child_iseq, void *data), void *data)
3032{
3033 unsigned int i;
3034 VALUE *code = rb_iseq_original_iseq(iseq);
3035 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
3036 const rb_iseq_t *child;
3037 VALUE all_children = rb_obj_hide(rb_ident_hash_new());
3038
3039 if (body->catch_table) {
3040 for (i = 0; i < body->catch_table->size; i++) {
3041 const struct iseq_catch_table_entry *entry =
3042 UNALIGNED_MEMBER_PTR(body->catch_table, entries[i]);
3043 child = entry->iseq;
3044 if (child) {
3045 if (NIL_P(rb_hash_aref(all_children, (VALUE)child))) {
3046 rb_hash_aset(all_children, (VALUE)child, Qtrue);
3047 (*iter_func)(child, data);
3048 }
3049 }
3050 }
3051 }
3052
3053 for (i=0; i<body->iseq_size;) {
3054 VALUE insn = code[i];
3055 int len = insn_len(insn);
3056 const char *types = insn_op_types(insn);
3057 int j;
3058
3059 for (j=0; types[j]; j++) {
3060 switch (types[j]) {
3061 case TS_ISEQ:
3062 child = (const rb_iseq_t *)code[i+j+1];
3063 if (child) {
3064 if (NIL_P(rb_hash_aref(all_children, (VALUE)child))) {
3065 rb_hash_aset(all_children, (VALUE)child, Qtrue);
3066 (*iter_func)(child, data);
3067 }
3068 }
3069 break;
3070 default:
3071 break;
3072 }
3073 }
3074 i += len;
3075 }
3076
3077 return (int)RHASH_SIZE(all_children);
3078}
3079
3080static void
3081yield_each_children(const rb_iseq_t *child_iseq, void *data)
3082{
3083 rb_yield(iseqw_new(child_iseq));
3084}
3085
3086/*
3087 * call-seq:
3088 * iseq.each_child{|child_iseq| ...} -> iseq
3089 *
3090 * Iterate all direct child instruction sequences.
3091 * Iteration order is implementation/version defined
3092 * so that people should not rely on the order.
3093 */
3094static VALUE
3095iseqw_each_child(VALUE self)
3096{
3097 const rb_iseq_t *iseq = iseqw_check(self);
3098 iseq_iterate_children(iseq, yield_each_children, NULL);
3099 return self;
3100}
3101
3102static void
3103push_event_info(const rb_iseq_t *iseq, rb_event_flag_t events, int line, VALUE ary)
3104{
3105#define C(ev, cstr, l) if (events & ev) rb_ary_push(ary, rb_ary_new_from_args(2, l, ID2SYM(rb_intern(cstr))));
3106 C(RUBY_EVENT_CLASS, "class", rb_iseq_first_lineno(iseq));
3107 C(RUBY_EVENT_CALL, "call", rb_iseq_first_lineno(iseq));
3108 C(RUBY_EVENT_B_CALL, "b_call", rb_iseq_first_lineno(iseq));
3109 C(RUBY_EVENT_LINE, "line", INT2FIX(line));
3110 C(RUBY_EVENT_END, "end", INT2FIX(line));
3111 C(RUBY_EVENT_RETURN, "return", INT2FIX(line));
3112 C(RUBY_EVENT_B_RETURN, "b_return", INT2FIX(line));
3113 C(RUBY_EVENT_RESCUE, "rescue", INT2FIX(line));
3114#undef C
3115}
3116
3117/*
3118 * call-seq:
3119 * iseq.trace_points -> ary
3120 *
3121 * Return trace points in the instruction sequence.
3122 * Return an array of [line, event_symbol] pair.
3123 */
3124static VALUE
3125iseqw_trace_points(VALUE self)
3126{
3127 const rb_iseq_t *iseq = iseqw_check(self);
3128 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
3129 unsigned int i;
3130 VALUE ary = rb_ary_new();
3131
3132 for (i=0; i<body->insns_info.size; i++) {
3133 const struct iseq_insn_info_entry *entry = &body->insns_info.body[i];
3134 if (entry->events) {
3135 push_event_info(iseq, entry->events, entry->line_no, ary);
3136 }
3137 }
3138 return ary;
3139}
3140
3141/*
3142 * Returns the instruction sequence containing the given proc or method.
3143 *
3144 * For example, using irb:
3145 *
3146 * # a proc
3147 * > p = proc { num = 1 + 2 }
3148 * > RubyVM::InstructionSequence.of(p)
3149 * > #=> <RubyVM::InstructionSequence:block in irb_binding@(irb)>
3150 *
3151 * # for a method
3152 * > def foo(bar); puts bar; end
3153 * > RubyVM::InstructionSequence.of(method(:foo))
3154 * > #=> <RubyVM::InstructionSequence:foo@(irb)>
3155 *
3156 * Using ::compile_file:
3157 *
3158 * # /tmp/iseq_of.rb
3159 * def hello
3160 * puts "hello, world"
3161 * end
3162 *
3163 * $a_global_proc = proc { str = 'a' + 'b' }
3164 *
3165 * # in irb
3166 * > require '/tmp/iseq_of.rb'
3167 *
3168 * # first the method hello
3169 * > RubyVM::InstructionSequence.of(method(:hello))
3170 * > #=> #<RubyVM::InstructionSequence:0x007fb73d7cb1d0>
3171 *
3172 * # then the global proc
3173 * > RubyVM::InstructionSequence.of($a_global_proc)
3174 * > #=> #<RubyVM::InstructionSequence:0x007fb73d7caf78>
3175 */
3176static VALUE
3177iseqw_s_of(VALUE klass, VALUE body)
3178{
3179 const rb_iseq_t *iseq = NULL;
3180
3181 if (rb_frame_info_p(body)) {
3182 iseq = rb_get_iseq_from_frame_info(body);
3183 }
3184 else if (rb_obj_is_proc(body)) {
3185 iseq = vm_proc_iseq(body);
3186
3187 if (!rb_obj_is_iseq((VALUE)iseq)) {
3188 iseq = NULL;
3189 }
3190 }
3191 else if (rb_obj_is_method(body)) {
3192 iseq = rb_method_iseq(body);
3193 }
3194 else if (rb_typeddata_is_instance_of(body, &iseqw_data_type)) {
3195 return body;
3196 }
3197
3198 return iseq ? iseqw_new(iseq) : Qnil;
3199}
3200
3201/*
3202 * call-seq:
3203 * InstructionSequence.disasm(body) -> str
3204 * InstructionSequence.disassemble(body) -> str
3205 *
3206 * Takes +body+, a +Method+ or +Proc+ object, and returns a +String+
3207 * with the human readable instructions for +body+.
3208 *
3209 * For a +Method+ object:
3210 *
3211 * # /tmp/method.rb
3212 * def hello
3213 * puts "hello, world"
3214 * end
3215 *
3216 * puts RubyVM::InstructionSequence.disasm(method(:hello))
3217 *
3218 * Produces:
3219 *
3220 * == disasm: <RubyVM::InstructionSequence:hello@/tmp/method.rb>============
3221 * 0000 trace 8 ( 1)
3222 * 0002 trace 1 ( 2)
3223 * 0004 putself
3224 * 0005 putstring "hello, world"
3225 * 0007 send :puts, 1, nil, 8, <ic:0>
3226 * 0013 trace 16 ( 3)
3227 * 0015 leave ( 2)
3228 *
3229 * For a +Proc+ object:
3230 *
3231 * # /tmp/proc.rb
3232 * p = proc { num = 1 + 2 }
3233 * puts RubyVM::InstructionSequence.disasm(p)
3234 *
3235 * Produces:
3236 *
3237 * == disasm: <RubyVM::InstructionSequence:block in <main>@/tmp/proc.rb>===
3238 * == catch table
3239 * | catch type: redo st: 0000 ed: 0012 sp: 0000 cont: 0000
3240 * | catch type: next st: 0000 ed: 0012 sp: 0000 cont: 0012
3241 * |------------------------------------------------------------------------
3242 * local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
3243 * [ 2] num
3244 * 0000 trace 1 ( 1)
3245 * 0002 putobject 1
3246 * 0004 putobject 2
3247 * 0006 opt_plus <ic:1>
3248 * 0008 dup
3249 * 0009 setlocal num, 0
3250 * 0012 leave
3251 *
3252 */
3253static VALUE
3254iseqw_s_disasm(VALUE klass, VALUE body)
3255{
3256 VALUE iseqw = iseqw_s_of(klass, body);
3257 return NIL_P(iseqw) ? Qnil : rb_iseq_disasm(iseqw_check(iseqw));
3258}
3259
3260static VALUE
3261register_label(struct st_table *table, unsigned long idx)
3262{
3263 VALUE sym = rb_str_intern(rb_sprintf("label_%lu", idx));
3264 st_insert(table, idx, sym);
3265 return sym;
3266}
3267
3268static VALUE
3269exception_type2symbol(VALUE type)
3270{
3271 ID id;
3272 switch (type) {
3273 case CATCH_TYPE_RESCUE: CONST_ID(id, "rescue"); break;
3274 case CATCH_TYPE_ENSURE: CONST_ID(id, "ensure"); break;
3275 case CATCH_TYPE_RETRY: CONST_ID(id, "retry"); break;
3276 case CATCH_TYPE_BREAK: CONST_ID(id, "break"); break;
3277 case CATCH_TYPE_REDO: CONST_ID(id, "redo"); break;
3278 case CATCH_TYPE_NEXT: CONST_ID(id, "next"); break;
3279 default:
3280 rb_bug("unknown exception type: %d", (int)type);
3281 }
3282 return ID2SYM(id);
3283}
3284
3285static int
3286cdhash_each(VALUE key, VALUE value, VALUE ary)
3287{
3288 rb_ary_push(ary, obj_resurrect(key));
3289 rb_ary_push(ary, value);
3290 return ST_CONTINUE;
3291}
3292
3293static const rb_data_type_t label_wrapper = {
3294 "label_wrapper",
3295 {(void (*)(void *))rb_mark_tbl, (void (*)(void *))st_free_table, 0, 0,},
3297};
3298
3299#define DECL_ID(name) \
3300 static ID id_##name
3301
3302#define INIT_ID(name) \
3303 id_##name = rb_intern(#name)
3304
3305static VALUE
3306iseq_type_id(enum rb_iseq_type type)
3307{
3308 DECL_ID(top);
3309 DECL_ID(method);
3310 DECL_ID(block);
3311 DECL_ID(class);
3312 DECL_ID(rescue);
3313 DECL_ID(ensure);
3314 DECL_ID(eval);
3315 DECL_ID(main);
3316 DECL_ID(plain);
3317
3318 if (id_top == 0) {
3319 INIT_ID(top);
3320 INIT_ID(method);
3321 INIT_ID(block);
3322 INIT_ID(class);
3323 INIT_ID(rescue);
3324 INIT_ID(ensure);
3325 INIT_ID(eval);
3326 INIT_ID(main);
3327 INIT_ID(plain);
3328 }
3329
3330 switch (type) {
3331 case ISEQ_TYPE_TOP: return id_top;
3332 case ISEQ_TYPE_METHOD: return id_method;
3333 case ISEQ_TYPE_BLOCK: return id_block;
3334 case ISEQ_TYPE_CLASS: return id_class;
3335 case ISEQ_TYPE_RESCUE: return id_rescue;
3336 case ISEQ_TYPE_ENSURE: return id_ensure;
3337 case ISEQ_TYPE_EVAL: return id_eval;
3338 case ISEQ_TYPE_MAIN: return id_main;
3339 case ISEQ_TYPE_PLAIN: return id_plain;
3340 };
3341
3342 rb_bug("unsupported iseq type: %d", (int)type);
3343}
3344
3345static VALUE
3346iseq_data_to_ary(const rb_iseq_t *iseq)
3347{
3348 VALUE iseq_value = (VALUE)iseq;
3349 unsigned int i;
3350 long l;
3351 const struct rb_iseq_constant_body *const iseq_body = ISEQ_BODY(iseq);
3352 const struct iseq_insn_info_entry *prev_insn_info;
3353 unsigned int pos;
3354 int last_line = 0;
3355 VALUE *seq, *iseq_original;
3356
3357 VALUE val = rb_ary_new();
3358 ID type; /* Symbol */
3359 VALUE locals = rb_ary_new();
3360 VALUE params = rb_hash_new();
3361 VALUE body = rb_ary_new(); /* [[:insn1, ...], ...] */
3362 VALUE nbody;
3363 VALUE exception = rb_ary_new(); /* [[....]] */
3364 VALUE misc = rb_hash_new();
3365
3366 static ID insn_syms[VM_BARE_INSTRUCTION_SIZE]; /* w/o-trace only */
3367 struct st_table *labels_table = st_init_numtable();
3368 VALUE labels_wrapper = TypedData_Wrap_Struct(0, &label_wrapper, labels_table);
3369
3370 if (insn_syms[0] == 0) {
3371 int i;
3372 for (i=0; i<numberof(insn_syms); i++) {
3373 insn_syms[i] = rb_intern(insn_name(i));
3374 }
3375 }
3376
3377 /* type */
3378 type = iseq_type_id(iseq_body->type);
3379
3380 /* locals */
3381 for (i=0; i<iseq_body->local_table_size; i++) {
3382 ID lid = iseq_body->local_table[i];
3383 if (lid) {
3384 if (lid != idItImplicit && rb_id2str(lid)) {
3385 rb_ary_push(locals, ID2SYM(lid));
3386 }
3387 else { /* hidden variable from id_internal() */
3388 rb_ary_push(locals, ULONG2NUM(iseq_body->local_table_size-i+1));
3389 }
3390 }
3391 else {
3392 rb_ary_push(locals, ID2SYM(rb_intern("#arg_rest")));
3393 }
3394 }
3395
3396 /* params */
3397 {
3398 const struct rb_iseq_param_keyword *const keyword = iseq_body->param.keyword;
3399 int j;
3400
3401 if (iseq_body->param.flags.has_opt) {
3402 int len = iseq_body->param.opt_num + 1;
3403 VALUE arg_opt_labels = rb_ary_new2(len);
3404
3405 for (j = 0; j < len; j++) {
3406 VALUE l = register_label(labels_table, iseq_body->param.opt_table[j]);
3407 rb_ary_push(arg_opt_labels, l);
3408 }
3409 rb_hash_aset(params, ID2SYM(rb_intern("opt")), arg_opt_labels);
3410 }
3411
3412 /* commit */
3413 if (iseq_body->param.flags.has_lead) rb_hash_aset(params, ID2SYM(rb_intern("lead_num")), INT2FIX(iseq_body->param.lead_num));
3414 if (iseq_body->param.flags.has_post) rb_hash_aset(params, ID2SYM(rb_intern("post_num")), INT2FIX(iseq_body->param.post_num));
3415 if (iseq_body->param.flags.has_post) rb_hash_aset(params, ID2SYM(rb_intern("post_start")), INT2FIX(iseq_body->param.post_start));
3416 if (iseq_body->param.flags.has_rest) rb_hash_aset(params, ID2SYM(rb_intern("rest_start")), INT2FIX(iseq_body->param.rest_start));
3417 if (iseq_body->param.flags.has_block) rb_hash_aset(params, ID2SYM(rb_intern("block_start")), INT2FIX(iseq_body->param.block_start));
3418 if (iseq_body->param.flags.has_kw) {
3419 VALUE keywords = rb_ary_new();
3420 int i, j;
3421 for (i=0; i<keyword->required_num; i++) {
3422 rb_ary_push(keywords, ID2SYM(keyword->table[i]));
3423 }
3424 for (j=0; i<keyword->num; i++, j++) {
3425 VALUE key = rb_ary_new_from_args(1, ID2SYM(keyword->table[i]));
3426 if (!UNDEF_P(keyword->default_values[j])) {
3427 rb_ary_push(key, keyword->default_values[j]);
3428 }
3429 rb_ary_push(keywords, key);
3430 }
3431
3432 rb_hash_aset(params, ID2SYM(rb_intern("kwbits")),
3433 INT2FIX(keyword->bits_start));
3434 rb_hash_aset(params, ID2SYM(rb_intern("keyword")), keywords);
3435 }
3436 if (iseq_body->param.flags.has_kwrest) rb_hash_aset(params, ID2SYM(rb_intern("kwrest")), INT2FIX(keyword->rest_start));
3437 if (iseq_body->param.flags.ambiguous_param0) rb_hash_aset(params, ID2SYM(rb_intern("ambiguous_param0")), Qtrue);
3438 if (iseq_body->param.flags.use_block) rb_hash_aset(params, ID2SYM(rb_intern("use_block")), Qtrue);
3439 }
3440
3441 /* body */
3442 iseq_original = rb_iseq_original_iseq((rb_iseq_t *)iseq);
3443
3444 for (seq = iseq_original; seq < iseq_original + iseq_body->iseq_size; ) {
3445 VALUE insn = *seq++;
3446 int j, len = insn_len(insn);
3447 VALUE *nseq = seq + len - 1;
3448 VALUE ary = rb_ary_new2(len);
3449
3450 rb_ary_push(ary, ID2SYM(insn_syms[insn%numberof(insn_syms)]));
3451 for (j=0; j<len-1; j++, seq++) {
3452 enum ruby_insn_type_chars op_type = insn_op_type(insn, j);
3453
3454 switch (op_type) {
3455 case TS_OFFSET: {
3456 unsigned long idx = nseq - iseq_original + *seq;
3457 rb_ary_push(ary, register_label(labels_table, idx));
3458 break;
3459 }
3460 case TS_LINDEX:
3461 case TS_NUM:
3462 rb_ary_push(ary, INT2FIX(*seq));
3463 break;
3464 case TS_VALUE:
3465 rb_ary_push(ary, obj_resurrect(*seq));
3466 break;
3467 case TS_ISEQ:
3468 {
3469 const rb_iseq_t *iseq = (rb_iseq_t *)*seq;
3470 if (iseq) {
3471 VALUE val = iseq_data_to_ary(rb_iseq_check(iseq));
3472 rb_ary_push(ary, val);
3473 }
3474 else {
3475 rb_ary_push(ary, Qnil);
3476 }
3477 }
3478 break;
3479 case TS_IC:
3480 {
3481 VALUE list = rb_ary_new();
3482 const ID *ids = ((IC)*seq)->segments;
3483 while (*ids) {
3484 rb_ary_push(list, ID2SYM(*ids++));
3485 }
3486 rb_ary_push(ary, list);
3487 }
3488 break;
3489 case TS_IVC:
3490 case TS_ICVARC:
3491 case TS_ISE:
3492 {
3493 union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)*seq;
3494 rb_ary_push(ary, INT2FIX(is - ISEQ_IS_ENTRY_START(ISEQ_BODY(iseq), op_type)));
3495 }
3496 break;
3497 case TS_CALLDATA:
3498 {
3499 struct rb_call_data *cd = (struct rb_call_data *)*seq;
3500 const struct rb_callinfo *ci = cd->ci;
3501 VALUE e = rb_hash_new();
3502 int argc = vm_ci_argc(ci);
3503
3504 ID mid = vm_ci_mid(ci);
3505 rb_hash_aset(e, ID2SYM(rb_intern("mid")), mid ? ID2SYM(mid) : Qnil);
3506 rb_hash_aset(e, ID2SYM(rb_intern("flag")), UINT2NUM(vm_ci_flag(ci)));
3507
3508 if (vm_ci_flag(ci) & VM_CALL_KWARG) {
3509 const struct rb_callinfo_kwarg *kwarg = vm_ci_kwarg(ci);
3510 int i;
3511 VALUE kw = rb_ary_new2((long)kwarg->keyword_len);
3512
3513 argc -= kwarg->keyword_len;
3514 for (i = 0; i < kwarg->keyword_len; i++) {
3515 rb_ary_push(kw, kwarg->keywords[i]);
3516 }
3517 rb_hash_aset(e, ID2SYM(rb_intern("kw_arg")), kw);
3518 }
3519
3520 rb_hash_aset(e, ID2SYM(rb_intern("orig_argc")),
3521 INT2FIX(argc));
3522 rb_ary_push(ary, e);
3523 }
3524 break;
3525 case TS_ID:
3526 rb_ary_push(ary, ID2SYM(*seq));
3527 break;
3528 case TS_CDHASH:
3529 {
3530 VALUE hash = *seq;
3531 VALUE val = rb_ary_new();
3532 int i;
3533
3534 rb_hash_foreach(hash, cdhash_each, val);
3535
3536 for (i=0; i<RARRAY_LEN(val); i+=2) {
3537 VALUE pos = FIX2INT(rb_ary_entry(val, i+1));
3538 unsigned long idx = nseq - iseq_original + pos;
3539
3540 rb_ary_store(val, i+1,
3541 register_label(labels_table, idx));
3542 }
3543 rb_ary_push(ary, val);
3544 }
3545 break;
3546 case TS_FUNCPTR:
3547 {
3548#if SIZEOF_VALUE <= SIZEOF_LONG
3549 VALUE val = LONG2NUM((SIGNED_VALUE)*seq);
3550#else
3551 VALUE val = LL2NUM((SIGNED_VALUE)*seq);
3552#endif
3553 rb_ary_push(ary, val);
3554 }
3555 break;
3556 case TS_BUILTIN:
3557 {
3558 VALUE val = rb_hash_new();
3559#if SIZEOF_VALUE <= SIZEOF_LONG
3560 VALUE func_ptr = LONG2NUM((SIGNED_VALUE)((RB_BUILTIN)*seq)->func_ptr);
3561#else
3562 VALUE func_ptr = LL2NUM((SIGNED_VALUE)((RB_BUILTIN)*seq)->func_ptr);
3563#endif
3564 rb_hash_aset(val, ID2SYM(rb_intern("func_ptr")), func_ptr);
3565 rb_hash_aset(val, ID2SYM(rb_intern("argc")), INT2NUM(((RB_BUILTIN)*seq)->argc));
3566 rb_hash_aset(val, ID2SYM(rb_intern("index")), INT2NUM(((RB_BUILTIN)*seq)->index));
3567 rb_hash_aset(val, ID2SYM(rb_intern("name")), rb_str_new_cstr(((RB_BUILTIN)*seq)->name));
3568 rb_ary_push(ary, val);
3569 }
3570 break;
3571 default:
3572 rb_bug("unknown operand: %c", insn_op_type(insn, j));
3573 }
3574 }
3575 rb_ary_push(body, ary);
3576 }
3577
3578 nbody = body;
3579
3580 /* exception */
3581 if (iseq_body->catch_table) for (i=0; i<iseq_body->catch_table->size; i++) {
3582 VALUE ary = rb_ary_new();
3583 const struct iseq_catch_table_entry *entry =
3584 UNALIGNED_MEMBER_PTR(iseq_body->catch_table, entries[i]);
3585 rb_ary_push(ary, exception_type2symbol(entry->type));
3586 if (entry->iseq) {
3587 rb_ary_push(ary, iseq_data_to_ary(rb_iseq_check(entry->iseq)));
3588 }
3589 else {
3590 rb_ary_push(ary, Qnil);
3591 }
3592 rb_ary_push(ary, register_label(labels_table, entry->start));
3593 rb_ary_push(ary, register_label(labels_table, entry->end));
3594 rb_ary_push(ary, register_label(labels_table, entry->cont));
3595 rb_ary_push(ary, UINT2NUM(entry->sp));
3596 rb_ary_push(exception, ary);
3597 }
3598
3599 /* make body with labels and insert line number */
3600 body = rb_ary_new();
3601 prev_insn_info = NULL;
3602#ifdef USE_ISEQ_NODE_ID
3603 VALUE node_ids = rb_ary_new();
3604#endif
3605
3606 for (l=0, pos=0; l<RARRAY_LEN(nbody); l++) {
3607 const struct iseq_insn_info_entry *info;
3608 VALUE ary = RARRAY_AREF(nbody, l);
3609 st_data_t label;
3610
3611 if (st_lookup(labels_table, pos, &label)) {
3612 rb_ary_push(body, (VALUE)label);
3613 }
3614
3615 info = get_insn_info(iseq, pos);
3616#ifdef USE_ISEQ_NODE_ID
3617 rb_ary_push(node_ids, INT2FIX(info->node_id));
3618#endif
3619
3620 if (prev_insn_info != info) {
3621 int line = info->line_no;
3622 rb_event_flag_t events = info->events;
3623
3624 if (line > 0 && last_line != line) {
3625 rb_ary_push(body, INT2FIX(line));
3626 last_line = line;
3627 }
3628#define CHECK_EVENT(ev) if (events & ev) rb_ary_push(body, ID2SYM(rb_intern(#ev)));
3629 CHECK_EVENT(RUBY_EVENT_LINE);
3630 CHECK_EVENT(RUBY_EVENT_CLASS);
3631 CHECK_EVENT(RUBY_EVENT_END);
3632 CHECK_EVENT(RUBY_EVENT_CALL);
3633 CHECK_EVENT(RUBY_EVENT_RETURN);
3634 CHECK_EVENT(RUBY_EVENT_B_CALL);
3635 CHECK_EVENT(RUBY_EVENT_B_RETURN);
3636 CHECK_EVENT(RUBY_EVENT_RESCUE);
3637#undef CHECK_EVENT
3638 prev_insn_info = info;
3639 }
3640
3641 rb_ary_push(body, ary);
3642 pos += RARRAY_LENINT(ary); /* reject too huge data */
3643 }
3644 RB_GC_GUARD(nbody);
3645 RB_GC_GUARD(labels_wrapper);
3646
3647 rb_hash_aset(misc, ID2SYM(rb_intern("arg_size")), INT2FIX(iseq_body->param.size));
3648 rb_hash_aset(misc, ID2SYM(rb_intern("local_size")), INT2FIX(iseq_body->local_table_size));
3649 rb_hash_aset(misc, ID2SYM(rb_intern("stack_max")), INT2FIX(iseq_body->stack_max));
3650 rb_hash_aset(misc, ID2SYM(rb_intern("node_id")), INT2FIX(iseq_body->location.node_id));
3651 rb_hash_aset(misc, ID2SYM(rb_intern("code_location")),
3652 rb_ary_new_from_args(4,
3653 INT2FIX(iseq_body->location.code_location.beg_pos.lineno),
3654 INT2FIX(iseq_body->location.code_location.beg_pos.column),
3655 INT2FIX(iseq_body->location.code_location.end_pos.lineno),
3656 INT2FIX(iseq_body->location.code_location.end_pos.column)));
3657#ifdef USE_ISEQ_NODE_ID
3658 rb_hash_aset(misc, ID2SYM(rb_intern("node_ids")), node_ids);
3659#endif
3660 rb_hash_aset(misc, ID2SYM(rb_intern("parser")), iseq_body->prism ? ID2SYM(rb_intern("prism")) : ID2SYM(rb_intern("parse.y")));
3661
3662 /*
3663 * [:magic, :major_version, :minor_version, :format_type, :misc,
3664 * :name, :path, :absolute_path, :start_lineno, :type, :locals, :args,
3665 * :catch_table, :bytecode]
3666 */
3667 rb_ary_push(val, rb_str_new2("YARVInstructionSequence/SimpleDataFormat"));
3668 rb_ary_push(val, INT2FIX(ISEQ_MAJOR_VERSION)); /* major */
3669 rb_ary_push(val, INT2FIX(ISEQ_MINOR_VERSION)); /* minor */
3670 rb_ary_push(val, INT2FIX(1));
3671 rb_ary_push(val, misc);
3672 rb_ary_push(val, iseq_body->location.label);
3673 rb_ary_push(val, rb_iseq_path(iseq));
3674 rb_ary_push(val, rb_iseq_realpath(iseq));
3675 rb_ary_push(val, RB_INT2NUM(iseq_body->location.first_lineno));
3676 rb_ary_push(val, ID2SYM(type));
3677 rb_ary_push(val, locals);
3678 rb_ary_push(val, params);
3679 rb_ary_push(val, exception);
3680 rb_ary_push(val, body);
3681
3682 RB_GC_GUARD(iseq_value);
3683
3684 return val;
3685}
3686
3687VALUE
3688rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc)
3689{
3690 int i, r;
3691 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
3692 const struct rb_iseq_param_keyword *const keyword = body->param.keyword;
3693 VALUE a, args = rb_ary_new2(body->param.size);
3694 ID req, opt, rest, block, key, keyrest;
3695#define PARAM_TYPE(type) rb_ary_push(a = rb_ary_new2(2), ID2SYM(type))
3696#define PARAM_ID(i) body->local_table[(i)]
3697#define PARAM(i, type) ( \
3698 PARAM_TYPE(type), \
3699 PARAM_ID(i) != idItImplicit && rb_id2str(PARAM_ID(i)) ? \
3700 rb_ary_push(a, ID2SYM(PARAM_ID(i))) : \
3701 a)
3702
3703 CONST_ID(req, "req");
3704 CONST_ID(opt, "opt");
3705
3706 if (body->param.flags.forwardable) {
3707 // [[:rest, :*], [:keyrest, :**], [:block, :&]]
3708 CONST_ID(rest, "rest");
3709 CONST_ID(keyrest, "keyrest");
3710 CONST_ID(block, "block");
3711 rb_ary_push(args, rb_ary_new_from_args(2, ID2SYM(rest), ID2SYM(idMULT)));
3712 rb_ary_push(args, rb_ary_new_from_args(2, ID2SYM(keyrest), ID2SYM(idPow)));
3713 rb_ary_push(args, rb_ary_new_from_args(2, ID2SYM(block), ID2SYM(idAnd)));
3714 }
3715
3716 if (is_proc) {
3717 for (i = 0; i < body->param.lead_num; i++) {
3718 rb_ary_push(args, PARAM(i, opt));
3719 }
3720 }
3721 else {
3722 for (i = 0; i < body->param.lead_num; i++) {
3723 rb_ary_push(args, PARAM(i, req));
3724 }
3725 }
3726 r = body->param.lead_num + body->param.opt_num;
3727 for (; i < r; i++) {
3728 rb_ary_push(args, PARAM(i, opt));
3729 }
3730 if (body->param.flags.has_rest) {
3731 CONST_ID(rest, "rest");
3732 rb_ary_push(args, PARAM(body->param.rest_start, rest));
3733 }
3734 r = body->param.post_start + body->param.post_num;
3735 if (is_proc) {
3736 for (i = body->param.post_start; i < r; i++) {
3737 rb_ary_push(args, PARAM(i, opt));
3738 }
3739 }
3740 else {
3741 for (i = body->param.post_start; i < r; i++) {
3742 rb_ary_push(args, PARAM(i, req));
3743 }
3744 }
3745 if (body->param.flags.accepts_no_kwarg) {
3746 ID nokey;
3747 CONST_ID(nokey, "nokey");
3748 PARAM_TYPE(nokey);
3749 rb_ary_push(args, a);
3750 }
3751 if (body->param.flags.has_kw) {
3752 i = 0;
3753 if (keyword->required_num > 0) {
3754 ID keyreq;
3755 CONST_ID(keyreq, "keyreq");
3756 for (; i < keyword->required_num; i++) {
3757 PARAM_TYPE(keyreq);
3758 if (rb_id2str(keyword->table[i])) {
3759 rb_ary_push(a, ID2SYM(keyword->table[i]));
3760 }
3761 rb_ary_push(args, a);
3762 }
3763 }
3764 CONST_ID(key, "key");
3765 for (; i < keyword->num; i++) {
3766 PARAM_TYPE(key);
3767 if (rb_id2str(keyword->table[i])) {
3768 rb_ary_push(a, ID2SYM(keyword->table[i]));
3769 }
3770 rb_ary_push(args, a);
3771 }
3772 }
3773 if (body->param.flags.has_kwrest || body->param.flags.ruby2_keywords) {
3774 ID param;
3775 CONST_ID(keyrest, "keyrest");
3776 PARAM_TYPE(keyrest);
3777 if (body->param.flags.has_kwrest &&
3778 rb_id2str(param = PARAM_ID(keyword->rest_start))) {
3779 rb_ary_push(a, ID2SYM(param));
3780 }
3781 else if (body->param.flags.ruby2_keywords) {
3782 rb_ary_push(a, ID2SYM(idPow));
3783 }
3784 rb_ary_push(args, a);
3785 }
3786 if (body->param.flags.has_block) {
3787 CONST_ID(block, "block");
3788 rb_ary_push(args, PARAM(body->param.block_start, block));
3789 }
3790 return args;
3791}
3792
3793VALUE
3794rb_iseq_defined_string(enum defined_type type)
3795{
3796 static const char expr_names[][18] = {
3797 "nil",
3798 "instance-variable",
3799 "local-variable",
3800 "global-variable",
3801 "class variable",
3802 "constant",
3803 "method",
3804 "yield",
3805 "super",
3806 "self",
3807 "true",
3808 "false",
3809 "assignment",
3810 "expression",
3811 };
3812 const char *estr;
3813
3814 if ((unsigned)(type - 1) >= (unsigned)numberof(expr_names)) rb_bug("unknown defined type %d", type);
3815 estr = expr_names[type - 1];
3816 return rb_fstring_cstr(estr);
3817}
3818
3819// A map from encoded_insn to insn_data: decoded insn number, its len,
3820// decoded ZJIT insn number, non-trace version of encoded insn,
3821// trace version, and zjit version.
3822static st_table *encoded_insn_data;
3823typedef struct insn_data_struct {
3824 int insn;
3825 int insn_len;
3826 void *notrace_encoded_insn;
3827 void *trace_encoded_insn;
3828#if USE_ZJIT
3829 int zjit_insn;
3830 void *zjit_encoded_insn;
3831#endif
3832} insn_data_t;
3833static insn_data_t insn_data[VM_BARE_INSTRUCTION_SIZE];
3834
3835void
3836rb_free_encoded_insn_data(void)
3837{
3838 st_free_table(encoded_insn_data);
3839}
3840
3841// Initialize a table to decode bare, trace, and zjit instructions.
3842// This function also determines which instructions are used when TracePoint is enabled.
3843void
3844rb_vm_encoded_insn_data_table_init(void)
3845{
3846#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
3847 const void * const *table = rb_vm_get_insns_address_table();
3848#define INSN_CODE(insn) ((VALUE)table[insn])
3849#else
3850#define INSN_CODE(insn) ((VALUE)(insn))
3851#endif
3852 encoded_insn_data = st_init_numtable_with_size(VM_BARE_INSTRUCTION_SIZE);
3853
3854 for (int insn = 0; insn < VM_BARE_INSTRUCTION_SIZE; insn++) {
3855 insn_data[insn].insn = insn;
3856 insn_data[insn].insn_len = insn_len(insn);
3857
3858 // When tracing :return events, we convert opt_invokebuiltin_delegate_leave + leave into
3859 // opt_invokebuiltin_delegate + trace_leave, presumably because we don't want to fire
3860 // :return events before invokebuiltin. https://github.com/ruby/ruby/pull/3256
3861 int notrace_insn = (insn != BIN(opt_invokebuiltin_delegate_leave)) ? insn : BIN(opt_invokebuiltin_delegate);
3862 insn_data[insn].notrace_encoded_insn = (void *)INSN_CODE(notrace_insn);
3863 insn_data[insn].trace_encoded_insn = (void *)INSN_CODE(notrace_insn + VM_BARE_INSTRUCTION_SIZE);
3864
3865 st_data_t key1 = (st_data_t)INSN_CODE(insn);
3866 st_data_t key2 = (st_data_t)INSN_CODE(insn + VM_BARE_INSTRUCTION_SIZE);
3867 st_add_direct(encoded_insn_data, key1, (st_data_t)&insn_data[insn]);
3868 st_add_direct(encoded_insn_data, key2, (st_data_t)&insn_data[insn]);
3869
3870#if USE_ZJIT
3871 int zjit_insn = vm_bare_insn_to_zjit_insn(insn);
3872 insn_data[insn].zjit_insn = zjit_insn;
3873 insn_data[insn].zjit_encoded_insn = (insn != zjit_insn) ? (void *)INSN_CODE(zjit_insn) : 0;
3874
3875 if (insn != zjit_insn) {
3876 st_data_t key3 = (st_data_t)INSN_CODE(zjit_insn);
3877 st_add_direct(encoded_insn_data, key3, (st_data_t)&insn_data[insn]);
3878 }
3879#endif
3880 }
3881}
3882
3883// Decode an insn address to an insn. This returns bare instructions
3884// even if they're trace/zjit instructions. Use rb_vm_insn_addr2opcode
3885// to decode trace/zjit instructions as is.
3886int
3887rb_vm_insn_addr2insn(const void *addr)
3888{
3889 st_data_t key = (st_data_t)addr;
3890 st_data_t val;
3891
3892 if (st_lookup(encoded_insn_data, key, &val)) {
3893 insn_data_t *e = (insn_data_t *)val;
3894 return (int)e->insn;
3895 }
3896
3897 rb_bug("rb_vm_insn_addr2insn: invalid insn address: %p", addr);
3898}
3899
3900// Decode an insn address to an insn. Unlike rb_vm_insn_addr2insn,
3901// this function can return trace/zjit opcode variants.
3902int
3903rb_vm_insn_addr2opcode(const void *addr)
3904{
3905 st_data_t key = (st_data_t)addr;
3906 st_data_t val;
3907
3908 if (st_lookup(encoded_insn_data, key, &val)) {
3909 insn_data_t *e = (insn_data_t *)val;
3910 int opcode = e->insn;
3911 if (addr == e->trace_encoded_insn) {
3912 opcode += VM_BARE_INSTRUCTION_SIZE;
3913 }
3914#if USE_ZJIT
3915 else if (addr == e->zjit_encoded_insn) {
3916 opcode = e->zjit_insn;
3917 }
3918#endif
3919 return opcode;
3920 }
3921
3922 rb_bug("rb_vm_insn_addr2opcode: invalid insn address: %p", addr);
3923}
3924
3925// Decode `ISEQ_BODY(iseq)->iseq_encoded[i]` to an insn. This returns
3926// bare instructions even if they're trace/zjit instructions. Use
3927// rb_vm_insn_addr2opcode to decode trace/zjit instructions as is.
3928int
3929rb_vm_insn_decode(const VALUE encoded)
3930{
3931#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
3932 int insn = rb_vm_insn_addr2insn((void *)encoded);
3933#else
3934 int insn = (int)encoded;
3935#endif
3936 return insn;
3937}
3938
3939// Turn on or off tracing for a given instruction address
3940static inline int
3941encoded_iseq_trace_instrument(VALUE *iseq_encoded_insn, rb_event_flag_t turnon, bool remain_traced)
3942{
3943 st_data_t key = (st_data_t)*iseq_encoded_insn;
3944 st_data_t val;
3945
3946 if (st_lookup(encoded_insn_data, key, &val)) {
3947 insn_data_t *e = (insn_data_t *)val;
3948 if (remain_traced && key == (st_data_t)e->trace_encoded_insn) {
3949 turnon = 1;
3950 }
3951 *iseq_encoded_insn = (VALUE) (turnon ? e->trace_encoded_insn : e->notrace_encoded_insn);
3952 return e->insn_len;
3953 }
3954
3955 rb_bug("trace_instrument: invalid insn address: %p", (void *)*iseq_encoded_insn);
3956}
3957
3958// Turn off tracing for an instruction at pos after tracing event flags are cleared
3959static void
3960rb_iseq_trace_flag_cleared(const rb_iseq_t *iseq, size_t pos)
3961{
3962 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
3963 VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
3964 encoded_iseq_trace_instrument(&iseq_encoded[pos], 0, false);
3965}
3966
3967// We need to fire call events on instructions with b_call events if the block
3968// is running as a method. So, if we are listening for call events, then
3969// instructions that have b_call events need to become trace variants.
3970// Use this function when making decisions about recompiling to trace variants.
3971static inline rb_event_flag_t
3972add_bmethod_events(rb_event_flag_t events)
3973{
3974 if (events & RUBY_EVENT_CALL) {
3975 events |= RUBY_EVENT_B_CALL;
3976 }
3977 if (events & RUBY_EVENT_RETURN) {
3978 events |= RUBY_EVENT_B_RETURN;
3979 }
3980 return events;
3981}
3982
3983// Note, to support call/return events for bmethods, turnon_event can have more events than tpval.
3984static int
3985iseq_add_local_tracepoint(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line, rb_ractor_t *r)
3986{
3987 unsigned int pc;
3988 int n = 0;
3989 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
3990 VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
3991 rb_iseq_t *iseq_mut = (rb_iseq_t*)iseq;
3992
3993 VM_ASSERT(ISEQ_EXECUTABLE_P(iseq));
3994 ASSERT_vm_locking_with_barrier();
3995
3996 for (pc=0; pc<body->iseq_size;) {
3997 const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pc);
3998 rb_event_flag_t pc_events = entry->events;
3999 rb_event_flag_t target_events = turnon_events;
4000 unsigned int line = (int)entry->line_no;
4001
4002 if (target_line == 0 || target_line == line) {
4003 /* ok */
4004 }
4005 else {
4006 target_events &= ~RUBY_EVENT_LINE;
4007 }
4008
4009 if (pc_events & target_events) {
4010 n++;
4011 }
4012 pc += encoded_iseq_trace_instrument(&iseq_encoded[pc], pc_events & (target_events | iseq->aux.exec.global_trace_events), true);
4013 }
4014
4015 if (n > 0) {
4016 rb_hook_list_t *hook_list = rb_iseq_local_hooks(iseq, r, true);
4017 rb_hook_list_connect_local_tracepoint(hook_list, tpval, target_line);
4018 iseq_mut->aux.exec.local_hooks_cnt++;
4019 }
4020
4021 return n;
4022}
4023
4025 rb_event_flag_t turnon_events;
4026 VALUE tpval;
4027 unsigned int target_line;
4028 int n;
4029 rb_ractor_t *r;
4030};
4031
4032static void
4033iseq_add_local_tracepoint_i(const rb_iseq_t *iseq, void *p)
4034{
4036 data->n += iseq_add_local_tracepoint(iseq, data->turnon_events, data->tpval, data->target_line, data->r);
4037 iseq_iterate_children(iseq, iseq_add_local_tracepoint_i, p);
4038}
4039
4040int
4041rb_iseq_add_local_tracepoint_recursively(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line, bool target_bmethod)
4042{
4043 ASSERT_vm_locking_with_barrier();
4045 if (target_bmethod) {
4046 turnon_events = add_bmethod_events(turnon_events);
4047 }
4048 data.turnon_events = turnon_events;
4049 data.tpval = tpval;
4050 data.target_line = target_line;
4051 data.n = 0;
4052 data.r = GET_RACTOR();
4053
4054 iseq_add_local_tracepoint_i(iseq, (void *)&data);
4055 if (0) fprintf(stderr, "Iseq disasm:\n:%s", RSTRING_PTR(rb_iseq_disasm(iseq))); /* for debug */
4056 return data.n;
4057}
4058
4059static int
4060iseq_remove_local_tracepoint(const rb_iseq_t *iseq, VALUE tpval, rb_ractor_t *r)
4061{
4062 int n = 0;
4063 unsigned int num_hooks_left;
4064 unsigned int pc;
4065 const struct rb_iseq_constant_body *body;
4066 rb_iseq_t *iseq_mut = (rb_iseq_t*)iseq;
4067 rb_hook_list_t *hook_list;
4068 VALUE *iseq_encoded;
4069 ASSERT_vm_locking_with_barrier();
4070
4071 hook_list = rb_iseq_local_hooks(iseq, r, false);
4072
4073 if (hook_list) {
4074 rb_event_flag_t local_events = 0;
4075
4076 rb_event_flag_t prev_events = hook_list->events;
4077 if (rb_hook_list_remove_local_tracepoint(hook_list, tpval)) {
4078 RUBY_ASSERT(iseq->aux.exec.local_hooks_cnt > 0);
4079 iseq_mut->aux.exec.local_hooks_cnt--;
4080 local_events = hook_list->events; // remaining events for this ractor
4081 num_hooks_left = rb_hook_list_count(hook_list);
4082 if (local_events == 0 && prev_events != 0) {
4083 st_delete(rb_ractor_targeted_hooks(r), (st_data_t*)&iseq, NULL);
4084 rb_hook_list_free(hook_list);
4085 }
4086
4087 if (iseq->aux.exec.local_hooks_cnt == num_hooks_left) {
4088 body = ISEQ_BODY(iseq);
4089 iseq_encoded = (VALUE *)body->iseq_encoded;
4090 local_events = add_bmethod_events(local_events);
4091 for (pc = 0; pc<body->iseq_size;) {
4092 rb_event_flag_t pc_events = rb_iseq_event_flags(iseq, pc);
4093 pc += encoded_iseq_trace_instrument(&iseq_encoded[pc], pc_events & (local_events | iseq->aux.exec.global_trace_events), false);
4094 }
4095 }
4096
4097 n++;
4098 }
4099 }
4100 return n;
4101}
4102
4104 VALUE tpval;
4105 int n;
4106 rb_ractor_t *r;
4107};
4108
4109static void
4110iseq_remove_local_tracepoint_i(const rb_iseq_t *iseq, void *p)
4111{
4113 data->n += iseq_remove_local_tracepoint(iseq, data->tpval, data->r);
4114 iseq_iterate_children(iseq, iseq_remove_local_tracepoint_i, p);
4115}
4116
4117int
4118rb_iseq_remove_local_tracepoint_recursively(const rb_iseq_t *iseq, VALUE tpval, rb_ractor_t *r)
4119{
4121 ASSERT_vm_locking_with_barrier();
4122 data.tpval = tpval;
4123 data.n = 0;
4124 data.r = r;
4125
4126 iseq_remove_local_tracepoint_i(iseq, (void *)&data);
4127 return data.n;
4128}
4129
4130void
4131rb_iseq_trace_set(const rb_iseq_t *iseq, rb_event_flag_t turnon_events)
4132{
4133 if (iseq->aux.exec.global_trace_events == turnon_events) {
4134 return;
4135 }
4136
4137 if (!ISEQ_EXECUTABLE_P(iseq)) {
4138 /* this is building ISeq */
4139 return;
4140 }
4141 else {
4142 // NOTE: this does not need VM barrier if it's a new ISEQ
4143 unsigned int pc;
4144 const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
4145
4146 VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
4147 rb_event_flag_t enabled_events;
4148 rb_hook_list_t *local_hooks = rb_iseq_local_hooks(iseq, GET_RACTOR(), false);
4149 rb_event_flag_t local_events = local_hooks ? local_hooks->events : 0;
4150 ((rb_iseq_t *)iseq)->aux.exec.global_trace_events = turnon_events;
4151 enabled_events = add_bmethod_events(turnon_events | local_events);
4152
4153 for (pc=0; pc<body->iseq_size;) {
4154 rb_event_flag_t pc_events = rb_iseq_event_flags(iseq, pc);
4155 pc += encoded_iseq_trace_instrument(&iseq_encoded[pc], pc_events & enabled_events, true);
4156 }
4157 }
4158}
4159
4160void rb_vm_cc_general(const struct rb_callcache *cc);
4161
4162static bool
4163clear_attr_cc(VALUE v)
4164{
4165 ASSERT_vm_locking_with_barrier();
4166 if (imemo_type_p(v, imemo_callcache) && vm_cc_ivar_p((const struct rb_callcache *)v)) {
4167 rb_vm_cc_general((struct rb_callcache *)v);
4168 return true;
4169 }
4170 else {
4171 return false;
4172 }
4173}
4174
4175static bool
4176clear_bf_cc(VALUE v)
4177{
4178 ASSERT_vm_locking_with_barrier();
4179 if (imemo_type_p(v, imemo_callcache) && vm_cc_bf_p((const struct rb_callcache *)v)) {
4180 rb_vm_cc_general((struct rb_callcache *)v);
4181 return true;
4182 }
4183 else {
4184 return false;
4185 }
4186}
4187
4188static int
4189clear_attr_ccs_i(void *vstart, void *vend, size_t stride, void *data)
4190{
4191 VALUE v = (VALUE)vstart;
4192 for (; v != (VALUE)vend; v += stride) {
4193 void *ptr = rb_asan_poisoned_object_p(v);
4194 rb_asan_unpoison_object(v, false);
4195 clear_attr_cc(v);
4196 asan_poison_object_if(ptr, v);
4197 }
4198 return 0;
4199}
4200
4201void
4202rb_clear_attr_ccs(void)
4203{
4204 RB_VM_LOCKING() {
4205 rb_vm_barrier();
4206 rb_objspace_each_objects(clear_attr_ccs_i, NULL);
4207 }
4208}
4209
4210static int
4211clear_bf_ccs_i(void *vstart, void *vend, size_t stride, void *data)
4212{
4213 VALUE v = (VALUE)vstart;
4214 for (; v != (VALUE)vend; v += stride) {
4215 void *ptr = rb_asan_poisoned_object_p(v);
4216 rb_asan_unpoison_object(v, false);
4217 clear_bf_cc(v);
4218 asan_poison_object_if(ptr, v);
4219 }
4220 return 0;
4221}
4222
4223void
4224rb_clear_bf_ccs(void)
4225{
4226 ASSERT_vm_locking_with_barrier();
4227 rb_objspace_each_objects(clear_bf_ccs_i, NULL);
4228}
4229
4230static int
4231trace_set_i(void *vstart, void *vend, size_t stride, void *data)
4232{
4233 rb_event_flag_t turnon_events = *(rb_event_flag_t *)data;
4234
4235 VALUE v = (VALUE)vstart;
4236 for (; v != (VALUE)vend; v += stride) {
4237 void *ptr = rb_asan_poisoned_object_p(v);
4238 rb_asan_unpoison_object(v, false);
4239
4240 if (rb_obj_is_iseq(v)) {
4241 rb_iseq_trace_set(rb_iseq_check((rb_iseq_t *)v), turnon_events);
4242 }
4243 else if (clear_attr_cc(v)) {
4244 }
4245 else if (clear_bf_cc(v)) {
4246 }
4247
4248 asan_poison_object_if(ptr, v);
4249 }
4250 return 0;
4251}
4252
4253void
4254rb_iseq_trace_set_all(rb_event_flag_t turnon_events)
4255{
4256 RB_VM_LOCKING() {
4257 rb_vm_barrier();
4258 rb_objspace_each_objects(trace_set_i, &turnon_events);
4259 }
4260}
4261
4262VALUE
4263rb_iseqw_local_variables(VALUE iseqval)
4264{
4265 return rb_iseq_local_variables(iseqw_check(iseqval));
4266}
4267
4268/*
4269 * call-seq:
4270 * iseq.to_binary(extra_data = nil) -> binary str
4271 *
4272 * Returns serialized iseq binary format data as a String object.
4273 * A corresponding iseq object is created by
4274 * RubyVM::InstructionSequence.load_from_binary() method.
4275 *
4276 * String extra_data will be saved with binary data.
4277 * You can access this data with
4278 * RubyVM::InstructionSequence.load_from_binary_extra_data(binary).
4279 *
4280 * Note that the translated binary data is not portable.
4281 * You can not move this binary data to another machine.
4282 * You can not use the binary data which is created by another
4283 * version/another architecture of Ruby.
4284 */
4285static VALUE
4286iseqw_to_binary(int argc, VALUE *argv, VALUE self)
4287{
4288 VALUE opt = !rb_check_arity(argc, 0, 1) ? Qnil : argv[0];
4289 return rb_iseq_ibf_dump(iseqw_check(self), opt);
4290}
4291
4292/*
4293 * call-seq:
4294 * RubyVM::InstructionSequence.load_from_binary(binary) -> iseq
4295 *
4296 * Load an iseq object from binary format String object
4297 * created by RubyVM::InstructionSequence.to_binary.
4298 *
4299 * This loader does not have a verifier, so that loading broken/modified
4300 * binary causes critical problem.
4301 *
4302 * You should not load binary data provided by others.
4303 * You should use binary data translated by yourself.
4304 */
4305static VALUE
4306iseqw_s_load_from_binary(VALUE self, VALUE str)
4307{
4308 return iseqw_new(rb_iseq_ibf_load(str));
4309}
4310
4311/*
4312 * call-seq:
4313 * RubyVM::InstructionSequence.load_from_binary_extra_data(binary) -> str
4314 *
4315 * Load extra data embed into binary format String object.
4316 */
4317static VALUE
4318iseqw_s_load_from_binary_extra_data(VALUE self, VALUE str)
4319{
4320 return rb_iseq_ibf_load_extra_data(str);
4321}
4322
4323#if VM_INSN_INFO_TABLE_IMPL == 2
4324
4325/* An implementation of succinct bit-vector for insn_info table.
4326 *
4327 * A succinct bit-vector is a small and efficient data structure that provides
4328 * a bit-vector augmented with an index for O(1) rank operation:
4329 *
4330 * rank(bv, n): the number of 1's within a range from index 0 to index n
4331 *
4332 * This can be used to lookup insn_info table from PC.
4333 * For example, consider the following iseq and insn_info_table:
4334 *
4335 * iseq insn_info_table
4336 * PC insn+operand position lineno event
4337 * 0: insn1 0: 1 [Li]
4338 * 2: insn2 2: 2 [Li] <= (A)
4339 * 5: insn3 8: 3 [Li] <= (B)
4340 * 8: insn4
4341 *
4342 * In this case, a succinct bit-vector whose indexes 0, 2, 8 is "1" and
4343 * other indexes is "0", i.e., "101000001", is created.
4344 * To lookup the lineno of insn2, calculate rank("10100001", 2) = 2, so
4345 * the line (A) is the entry in question.
4346 * To lookup the lineno of insn4, calculate rank("10100001", 8) = 3, so
4347 * the line (B) is the entry in question.
4348 *
4349 * A naive implementation of succinct bit-vector works really well
4350 * not only for large size but also for small size. However, it has
4351 * tiny overhead for very small size. So, this implementation consist
4352 * of two parts: one part is the "immediate" table that keeps rank result
4353 * as a raw table, and the other part is a normal succinct bit-vector.
4354 */
4355
4356#define IMMEDIATE_TABLE_SIZE 54 /* a multiple of 9, and < 128 */
4357
4358struct succ_index_table {
4359 uint64_t imm_part[IMMEDIATE_TABLE_SIZE / 9];
4360 struct succ_dict_block {
4361 unsigned int rank;
4362 uint64_t small_block_ranks; /* 9 bits * 7 = 63 bits */
4363 uint64_t bits[512/64];
4364 } succ_part[FLEX_ARY_LEN];
4365};
4366
4367#define imm_block_rank_set(v, i, r) (v) |= (uint64_t)(r) << (7 * (i))
4368#define imm_block_rank_get(v, i) (((int)((v) >> ((i) * 7))) & 0x7f)
4369#define small_block_rank_set(v, i, r) (v) |= (uint64_t)(r) << (9 * ((i) - 1))
4370#define small_block_rank_get(v, i) ((i) == 0 ? 0 : (((int)((v) >> (((i) - 1) * 9))) & 0x1ff))
4371
4372static struct succ_index_table *
4373succ_index_table_create(int max_pos, int *data, int size)
4374{
4375 const int imm_size = (max_pos < IMMEDIATE_TABLE_SIZE ? max_pos + 8 : IMMEDIATE_TABLE_SIZE) / 9;
4376 const int succ_size = (max_pos < IMMEDIATE_TABLE_SIZE ? 0 : (max_pos - IMMEDIATE_TABLE_SIZE + 511)) / 512;
4377 struct succ_index_table *sd =
4378 rb_xcalloc_mul_add_mul(
4379 imm_size, sizeof(uint64_t),
4380 succ_size, sizeof(struct succ_dict_block));
4381 int i, j, k, r;
4382
4383 r = 0;
4384 for (j = 0; j < imm_size; j++) {
4385 for (i = 0; i < 9; i++) {
4386 if (r < size && data[r] == j * 9 + i) r++;
4387 imm_block_rank_set(sd->imm_part[j], i, r);
4388 }
4389 }
4390 for (k = 0; k < succ_size; k++) {
4391 struct succ_dict_block *sd_block = &sd->succ_part[k];
4392 int small_rank = 0;
4393 sd_block->rank = r;
4394 for (j = 0; j < 8; j++) {
4395 uint64_t bits = 0;
4396 if (j) small_block_rank_set(sd_block->small_block_ranks, j, small_rank);
4397 for (i = 0; i < 64; i++) {
4398 if (r < size && data[r] == k * 512 + j * 64 + i + IMMEDIATE_TABLE_SIZE) {
4399 bits |= ((uint64_t)1) << i;
4400 r++;
4401 }
4402 }
4403 sd_block->bits[j] = bits;
4404 small_rank += rb_popcount64(bits);
4405 }
4406 }
4407 return sd;
4408}
4409
4410static unsigned int *
4411succ_index_table_invert(int max_pos, struct succ_index_table *sd, int size)
4412{
4413 const int imm_size = (max_pos < IMMEDIATE_TABLE_SIZE ? max_pos + 8 : IMMEDIATE_TABLE_SIZE) / 9;
4414 const int succ_size = (max_pos < IMMEDIATE_TABLE_SIZE ? 0 : (max_pos - IMMEDIATE_TABLE_SIZE + 511)) / 512;
4415 unsigned int *positions = ALLOC_N(unsigned int, size), *p;
4416 int i, j, k, r = -1;
4417 p = positions;
4418 for (j = 0; j < imm_size; j++) {
4419 for (i = 0; i < 9; i++) {
4420 int nr = imm_block_rank_get(sd->imm_part[j], i);
4421 if (r != nr) *p++ = j * 9 + i;
4422 r = nr;
4423 }
4424 }
4425 for (k = 0; k < succ_size; k++) {
4426 for (j = 0; j < 8; j++) {
4427 for (i = 0; i < 64; i++) {
4428 if (sd->succ_part[k].bits[j] & (((uint64_t)1) << i)) {
4429 *p++ = k * 512 + j * 64 + i + IMMEDIATE_TABLE_SIZE;
4430 }
4431 }
4432 }
4433 }
4434 return positions;
4435}
4436
4437static int
4438succ_index_lookup(const struct succ_index_table *sd, int x)
4439{
4440 if (x < IMMEDIATE_TABLE_SIZE) {
4441 const int i = x / 9;
4442 const int j = x % 9;
4443 return imm_block_rank_get(sd->imm_part[i], j);
4444 }
4445 else {
4446 const int block_index = (x - IMMEDIATE_TABLE_SIZE) / 512;
4447 const struct succ_dict_block *block = &sd->succ_part[block_index];
4448 const int block_bit_index = (x - IMMEDIATE_TABLE_SIZE) % 512;
4449 const int small_block_index = block_bit_index / 64;
4450 const int small_block_popcount = small_block_rank_get(block->small_block_ranks, small_block_index);
4451 const int popcnt = rb_popcount64(block->bits[small_block_index] << (63 - block_bit_index % 64));
4452
4453 return block->rank + small_block_popcount + popcnt;
4454 }
4455}
4456#endif
4457
4458
4459/*
4460 * call-seq:
4461 * iseq.script_lines -> array or nil
4462 *
4463 * It returns recorded script lines if it is available.
4464 * The script lines are not limited to the iseq range, but
4465 * are entire lines of the source file.
4466 *
4467 * Note that this is an API for ruby internal use, debugging,
4468 * and research. Do not use this for any other purpose.
4469 * The compatibility is not guaranteed.
4470 */
4471static VALUE
4472iseqw_script_lines(VALUE self)
4473{
4474 const rb_iseq_t *iseq = iseqw_check(self);
4475 return ISEQ_BODY(iseq)->variable.script_lines;
4476}
4477
4478/*
4479 * Document-class: RubyVM::InstructionSequence
4480 *
4481 * The InstructionSequence class represents a compiled sequence of
4482 * instructions for the Virtual Machine used in MRI. Not all implementations of Ruby
4483 * may implement this class, and for the implementations that implement it,
4484 * the methods defined and behavior of the methods can change in any version.
4485 *
4486 * With it, you can get a handle to the instructions that make up a method or
4487 * a proc, compile strings of Ruby code down to VM instructions, and
4488 * disassemble instruction sequences to strings for easy inspection. It is
4489 * mostly useful if you want to learn how YARV works, but it also lets
4490 * you control various settings for the Ruby iseq compiler.
4491 *
4492 * You can find the source for the VM instructions in +insns.def+ in the Ruby
4493 * source.
4494 *
4495 * The instruction sequence results will almost certainly change as Ruby
4496 * changes, so example output in this documentation may be different from what
4497 * you see.
4498 *
4499 * Of course, this class is MRI specific.
4500 */
4501
4502void
4503Init_ISeq(void)
4504{
4505 /* declare ::RubyVM::InstructionSequence */
4506 rb_cISeq = rb_define_class_under(rb_cRubyVM, "InstructionSequence", rb_cObject);
4507 rb_undef_alloc_func(rb_cISeq);
4508 rb_define_method(rb_cISeq, "inspect", iseqw_inspect, 0);
4509 rb_define_method(rb_cISeq, "disasm", iseqw_disasm, 0);
4510 rb_define_method(rb_cISeq, "disassemble", iseqw_disasm, 0);
4511 rb_define_method(rb_cISeq, "to_a", iseqw_to_a, 0);
4512 rb_define_method(rb_cISeq, "eval", iseqw_eval, 0);
4513
4514 rb_define_method(rb_cISeq, "to_binary", iseqw_to_binary, -1);
4515 rb_define_singleton_method(rb_cISeq, "load_from_binary", iseqw_s_load_from_binary, 1);
4516 rb_define_singleton_method(rb_cISeq, "load_from_binary_extra_data", iseqw_s_load_from_binary_extra_data, 1);
4517
4518 /* location APIs */
4519 rb_define_method(rb_cISeq, "path", iseqw_path, 0);
4520 rb_define_method(rb_cISeq, "absolute_path", iseqw_absolute_path, 0);
4521 rb_define_method(rb_cISeq, "label", iseqw_label, 0);
4522 rb_define_method(rb_cISeq, "base_label", iseqw_base_label, 0);
4523 rb_define_method(rb_cISeq, "first_lineno", iseqw_first_lineno, 0);
4524 rb_define_method(rb_cISeq, "trace_points", iseqw_trace_points, 0);
4525 rb_define_method(rb_cISeq, "each_child", iseqw_each_child, 0);
4526
4527#if 0 /* TBD */
4528 rb_define_private_method(rb_cISeq, "marshal_dump", iseqw_marshal_dump, 0);
4529 rb_define_private_method(rb_cISeq, "marshal_load", iseqw_marshal_load, 1);
4530 /* disable this feature because there is no verifier. */
4531 rb_define_singleton_method(rb_cISeq, "load", iseq_s_load, -1);
4532#endif
4533 (void)iseq_s_load;
4534
4535 rb_define_singleton_method(rb_cISeq, "compile", iseqw_s_compile, -1);
4536 rb_define_singleton_method(rb_cISeq, "compile_parsey", iseqw_s_compile_parsey, -1);
4537 rb_define_singleton_method(rb_cISeq, "compile_prism", iseqw_s_compile_prism, -1);
4538 rb_define_singleton_method(rb_cISeq, "compile_file_prism", iseqw_s_compile_file_prism, -1);
4539 rb_define_singleton_method(rb_cISeq, "new", iseqw_s_compile, -1);
4540 rb_define_singleton_method(rb_cISeq, "compile_file", iseqw_s_compile_file, -1);
4541 rb_define_singleton_method(rb_cISeq, "compile_option", iseqw_s_compile_option_get, 0);
4542 rb_define_singleton_method(rb_cISeq, "compile_option=", iseqw_s_compile_option_set, 1);
4543 rb_define_singleton_method(rb_cISeq, "disasm", iseqw_s_disasm, 1);
4544 rb_define_singleton_method(rb_cISeq, "disassemble", iseqw_s_disasm, 1);
4545 rb_define_singleton_method(rb_cISeq, "of", iseqw_s_of, 1);
4546
4547 // script lines
4548 rb_define_method(rb_cISeq, "script_lines", iseqw_script_lines, 0);
4549
4550 rb_undef_method(CLASS_OF(rb_cISeq), "translate");
4551 rb_undef_method(CLASS_OF(rb_cISeq), "load_iseq");
4552}
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
#define rb_define_private_method(klass, mid, func, arity)
Defines klass#mid and makes it private.
#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_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
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1622
void rb_undef_method(VALUE klass, const char *name)
Defines an undef of a method.
Definition class.c:2770
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Retrieves argument from argc and argv to given VALUE references according to the format string.
Definition class.c:3248
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1674
#define T_FILE
Old name of RUBY_T_FILE.
Definition value_type.h:62
#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 rb_str_cat2
Old name of rb_str_cat_cstr.
Definition string.h:1682
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
#define ULONG2NUM
Old name of RB_ULONG2NUM.
Definition long.h:60
#define SYM2ID
Old name of RB_SYM2ID.
Definition symbol.h:45
#define ZALLOC
Old name of RB_ZALLOC.
Definition memory.h:402
#define LL2NUM
Old name of RB_LL2NUM.
Definition long_long.h:30
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:205
#define T_NONE
Old name of RUBY_T_NONE.
Definition value_type.h:74
#define FIX2INT
Old name of RB_FIX2INT.
Definition int.h:41
#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_TEST_RAW
Old name of RB_FL_TEST_RAW.
Definition fl_type.h:130
#define LONG2NUM
Old name of RB_LONG2NUM.
Definition long.h:50
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define NIL_P
Old name of RB_NIL_P.
#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 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 rb_ary_new2
Old name of rb_ary_new_capa.
Definition array.h:657
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:653
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1418
void * rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type)
Identical to rb_typeddata_is_kind_of(), except it raises exceptions instead of returning false.
Definition error.c:1404
VALUE rb_eSyntaxError
SyntaxError exception.
Definition error.c:1435
VALUE rb_class_superclass(VALUE klass)
Queries the parent of the given class.
Definition object.c:2314
VALUE rb_cObject
Object class.
Definition object.c:61
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:100
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:264
VALUE rb_inspect(VALUE obj)
Generates a human-readable textual representation of the given object.
Definition object.c:686
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:603
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1117
Defines RBIMPL_HAS_BUILTIN.
VALUE rb_ary_new_from_values(long n, const VALUE *elts)
Identical to rb_ary_new_from_args(), except how objects are passed.
VALUE rb_ary_resurrect(VALUE ary)
I guess there is no use case of this function in extension libraries, but this is a routine identical...
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_hidden_new(long capa)
Allocates a hidden (no class) empty 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.
static int rb_check_arity(int argc, int min, int max)
Ensures that the passed integer is in the passed range.
Definition error.h:284
VALUE rb_file_open_str(VALUE fname, const char *fmode)
Identical to rb_file_open(), except it takes the pathname as a Ruby's string instead of C's.
Definition io.c:7298
VALUE rb_io_close(VALUE io)
Closes the IO.
Definition io.c:5780
int rb_is_local_id(ID id)
Classifies the given ID, then sees if it is a local variable.
Definition symbol.c:1109
VALUE rb_obj_is_method(VALUE recv)
Queries if the given object is a method.
Definition proc.c:1813
VALUE rb_obj_is_proc(VALUE recv)
Queries if the given object is a proc.
Definition proc.c:120
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:3798
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1497
#define rb_exc_new_cstr(exc, str)
Identical to rb_exc_new(), except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1669
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition string.c:1997
VALUE rb_str_cat(VALUE dst, const char *src, long srclen)
Destructively appends the passed contents to the string.
Definition string.c:3566
VALUE rb_str_resurrect(VALUE str)
Like rb_str_dup(), but always create an instance of rb_cString regardless of the given object's class...
Definition string.c:2015
void rb_str_set_len(VALUE str, long len)
Overwrites the length of the string.
Definition string.c:3388
VALUE rb_str_inspect(VALUE str)
Generates a "readable" version of the receiver.
Definition string.c:7229
int rb_str_cmp(VALUE lhs, VALUE rhs)
Compares two strings, as in strcmp(3).
Definition string.c:4215
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:4035
#define rb_str_cat_cstr(buf, str)
Identical to rb_str_cat(), except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1655
void rb_str_modify_expand(VALUE str, long capa)
Identical to rb_str_modify(), except it additionally expands the capacity of the receiver.
Definition string.c:2745
#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:1513
VALUE rb_str_intern(VALUE str)
Identical to rb_to_symbol(), except it assumes the receiver being an instance of RString.
Definition symbol.c:937
VALUE rb_class_name(VALUE obj)
Queries the name of the given object's class.
Definition variable.c:500
int rb_respond_to(VALUE obj, ID mid)
Queries if the object responds to the method.
Definition vm_method.c:3403
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1651
VALUE rb_check_funcall(VALUE recv, ID mid, int argc, const VALUE *argv)
Identical to rb_funcallv(), except it returns RUBY_Qundef instead of raising rb_eNoMethodError.
Definition vm_eval.c:686
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
Definition symbol.c:1133
VALUE rb_sym2str(VALUE symbol)
Obtain a frozen string representation of a symbol (not including the leading colon).
Definition symbol.c:993
VALUE rb_io_path(VALUE io)
Returns the path for the given IO.
Definition io.c:3001
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:1547
#define RB_NUM2INT
Just another name of rb_num2int_inline.
Definition int.h:38
#define RB_INT2NUM
Just another name of rb_int2num_inline.
Definition int.h:37
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1372
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
#define RB_ZALLOC(type)
Shorthand of RB_ZALLOC_N with n=1.
Definition memory.h:249
VALUE type(ANYARGS)
ANYARGS-ed function type.
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
PRISM_EXPORTED_FUNCTION void pm_options_line_set(pm_options_t *options, int32_t line)
Set the line option on the given options struct.
Definition options.c:40
PRISM_EXPORTED_FUNCTION void pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal)
Set the frozen string literal option on the given options struct.
Definition options.c:48
PRISM_EXPORTED_FUNCTION bool pm_options_scopes_init(pm_options_t *options, size_t scopes_count)
Allocate and zero out the scopes array on the given options struct.
Definition options.c:172
#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
#define RARRAY_AREF(a, i)
Definition rarray.h:403
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
Definition rbasic.h:166
#define RHASH_SIZE(h)
Queries the size of the hash.
Definition rhash.h:69
#define StringValue(v)
Ensures that the parameter object is a String.
Definition rstring.h:66
static char * RSTRING_END(VALUE str)
Queries the end of the contents pointer of the string.
Definition rstring.h:409
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
#define RUBY_TYPED_DEFAULT_FREE
This is a value you can set to rb_data_type_struct::dfree.
Definition rtypeddata.h:80
#define RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
Definition rtypeddata.h:119
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:729
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:507
#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:554
#define FilePathValue(v)
Ensures that the parameter object is a path.
Definition ruby.h:90
#define RTEST
This is an old name of RB_TEST.
Definition iseq.h:287
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:293
Definition vm_core.h:288
Definition iseq.h:258
A line and column in a string.
uint32_t column
The column number.
int32_t line
The line number.
This represents a range of bytes in the source string to which a node or token corresponds.
Definition ast.h:544
const uint8_t * start
A pointer to the start location of the range in the source.
Definition ast.h:546
const uint8_t * end
A pointer to the end location of the range in the source.
Definition ast.h:549
size_t size
The number of offsets in the list.
uint32_t node_id
The unique identifier for this node, which is deterministic based on the source.
Definition ast.h:1069
pm_location_t location
This is the location of the node in the source.
Definition ast.h:1075
int32_t line
The line within the file that the parse starts on.
Definition options.h:130
pm_scope_node_t node
The resulting scope node that will hold the generated AST.
pm_options_t options
The options that will be passed to the parser.
int32_t start_line
The line number at the start of the parse.
Definition parser.h:812
pm_newline_list_t newline_list
This is the list of newline offsets in the source file.
Definition parser.h:792
VALUE * script_lines
This is a pointer to the list of script lines for the ISEQs that will be associated with this scope n...
Definition method.h:63
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:211
Definition st.h:79
Definition vm_core.h:297
intptr_t SIGNED_VALUE
A signed integer type that has the same width with VALUE.
Definition value.h:63
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static void Check_Type(VALUE v, enum ruby_value_type t)
Identical to RB_TYPE_P(), except it raises exceptions on predication failure.
Definition value_type.h:433
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_value_type
C-level type of an object.
Definition value_type.h:113