Ruby 4.1.0dev (2026-11-10 revision c65a8e03f14ed911e519028419ba3b2b9fbad6d3)
vm.c (c65a8e03f14ed911e519028419ba3b2b9fbad6d3)
1/**********************************************************************
2
3 Vm.c -
4
5 $Author$
6
7 Copyright (C) 2004-2007 Koichi Sasada
8
9**********************************************************************/
10
11#define vm_exec rb_vm_exec
12
13#include "eval_intern.h"
14#include "internal.h"
15#include "internal/box.h"
16#include "internal/class.h"
17#include "internal/compile.h"
18#include "internal/cont.h"
19#include "internal/error.h"
20#include "internal/encoding.h"
21#include "internal/eval.h"
22#include "internal/gc.h"
23#include "internal/inits.h"
24#include "internal/jit.h"
25#include "internal/missing.h"
26#include "internal/object.h"
27#include "internal/proc.h"
28#include "internal/ractor.h"
29#include "internal/re.h"
30#include "internal/ruby_parser.h"
31#include "internal/st.h"
32#include "internal/symbol.h"
33#include "internal/thread.h"
34#include "internal/transcode.h"
35#include "internal/vm.h"
36#include "internal/sanitizers.h"
37#include "internal/variable.h"
38#include "iseq.h"
39#include "symbol.h" // This includes a macro for a more performant rb_id2sym.
40#include "yjit.h"
41#include "insns.inc"
42#include "zjit.h"
43#include "ruby/st.h"
44#include "ruby/vm.h"
45#include "vm_core.h"
46#include "vm_callinfo.h"
47#include "vm_debug.h"
48#include "ruby/debug.h"
49#include "vm_exec.h"
50#include "vm_insnhelper.h"
51#include "ractor_core.h"
52#include "vm_sync.h"
53#include "shape.h"
54
55#include "builtin.h"
56
57#include "probes.h"
58#include "probes_helper.h"
59
60#if defined(_MSC_VER) && !defined(__clang__)
61/* Favor speed over the default -Os, which makes vm_exec_core dispatch
62 * instructions by a binary search instead of a jump table. This must
63 * follow ruby/internal/memory.h, whose `#pragma optimize("", on)`
64 * restores the command line options. */
65#pragma optimize("t", on)
66#endif
67
68static void *native_main_thread_stack_top;
69
70bool ruby_vm_during_cleanup = false;
71
72VALUE rb_str_concat_literals(size_t, const VALUE*);
73
75
76extern const char *const rb_debug_counter_names[];
77
78PUREFUNC(static inline const VALUE *VM_EP_LEP(const VALUE *));
79static inline const VALUE *
80VM_EP_LEP(const VALUE *ep)
81{
82 while (!VM_ENV_LOCAL_P(ep)) {
83 ep = VM_ENV_PREV_EP(ep);
84 }
85 return ep;
86}
87
88static inline const rb_control_frame_t *
89rb_vm_search_cf_from_ep(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const VALUE * const ep)
90{
91 if (!ep) {
92 return NULL;
93 }
94 else {
95 const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(ec); /* end of control frame pointer */
96
97 while (cfp < eocfp) {
98 if (cfp->ep == ep) {
99 return cfp;
100 }
101 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
102 }
103
104 return NULL;
105 }
106}
107
108#if VM_CHECK_MODE > 0
109// ruby_box_crashed defined in internal/box.h
110#define VM_BOX_CRASHED() {ruby_box_crashed = true;}
111#define VM_BOX_ASSERT(expr, msg) \
112 if (!(expr)) { ruby_box_crashed = true; rb_bug(msg); }
113#else
114#define VM_BOX_CRASHED() {}
115#define VM_BOX_ASSERT(expr, msg) ((void)0)
116#endif
117
118static const VALUE *
119VM_EP_RUBY_LEP(const rb_execution_context_t *ec, const rb_control_frame_t *current_cfp)
120{
121 // rb_vmdebug_box_env_dump_raw() simulates this function
122 const VALUE *ep = current_cfp->ep;
123 const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(ec); /* end of control frame pointer */
124 const rb_control_frame_t *cfp = current_cfp;
125
164 while (VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_IFUNC) ||
165 VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CFUNC)) {
166 bool from_ifunc = VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_IFUNC);
167
168 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
169 VM_BOX_ASSERT(RUBY_VM_VALID_CONTROL_FRAME_P(cfp, eocfp), "Valid caller control frame expected");
170 if (!RUBY_VM_VALID_CONTROL_FRAME_P(cfp, eocfp)) {
171 return NULL;
172 }
173
174 VM_BOX_ASSERT(cfp->ep, "Caller control frame should have a valid env");
175 ep = cfp->ep;
176 if (!ep) {
177 return NULL;
178 }
179
180 if (from_ifunc && VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CFUNC)) {
181 return ep;
182 }
183 }
184
185 while (!VM_ENV_LOCAL_P(ep)) {
186 ep = VM_ENV_PREV_EP(ep);
187 }
188
189 return ep;
190}
191
192const VALUE *
193rb_vm_ep_local_ep(const VALUE *ep)
194{
195 return VM_EP_LEP(ep);
196}
197
198PUREFUNC(static inline const VALUE *VM_CF_LEP(const rb_control_frame_t * const cfp));
199static inline const VALUE *
200VM_CF_LEP(const rb_control_frame_t * const cfp)
201{
202 return VM_EP_LEP(cfp->ep);
203}
204
205static inline const VALUE *
206VM_CF_PREV_EP(const rb_control_frame_t * const cfp)
207{
208 return VM_ENV_PREV_EP(cfp->ep);
209}
210
211PUREFUNC(static inline VALUE VM_CF_BLOCK_HANDLER(const rb_control_frame_t * const cfp));
212static inline VALUE
213VM_CF_BLOCK_HANDLER(const rb_control_frame_t * const cfp)
214{
215 const VALUE *ep;
216 if (VM_ENV_BOXED_P(cfp->ep)) {
217 VM_ASSERT(VM_ENV_LOCAL_P(cfp->ep));
218 /* Never set black_handler for VM_FRAME_MAGIC_TOP or VM_FRAME_MAGIC_CLASS
219 * and the specval is used for boxes (rb_box_t) in these case
220 */
221 return VM_BLOCK_HANDLER_NONE;
222 }
223 ep = VM_CF_LEP(cfp);
224 return VM_ENV_BLOCK_HANDLER(ep);
225}
226
227int
228rb_vm_cframe_keyword_p(const rb_control_frame_t *cfp)
229{
230 return VM_FRAME_CFRAME_KW_P(cfp);
231}
232
233VALUE
234rb_vm_frame_block_handler(const rb_control_frame_t *cfp)
235{
236 return VM_CF_BLOCK_HANDLER(cfp);
237}
238
239#if VM_CHECK_MODE > 0
240static int
241VM_CFP_IN_HEAP_P(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
242{
243 const VALUE *start = ec->vm_stack;
244 const VALUE *end = (VALUE *)ec->vm_stack + ec->vm_stack_size;
245 VM_ASSERT(start != NULL);
246
247 if (start <= (VALUE *)cfp && (VALUE *)cfp < end) {
248 return FALSE;
249 }
250 else {
251 return TRUE;
252 }
253}
254
255static int
256VM_EP_IN_HEAP_P(const rb_execution_context_t *ec, const VALUE *ep)
257{
258 const VALUE *start = ec->vm_stack;
259 const VALUE *end = (VALUE *)ec->cfp;
260 VM_ASSERT(start != NULL);
261
262 if (start <= ep && ep < end) {
263 return FALSE;
264 }
265 else {
266 return TRUE;
267 }
268}
269
270static int
271vm_ep_in_heap_p_(const rb_execution_context_t *ec, const VALUE *ep)
272{
273 if (VM_EP_IN_HEAP_P(ec, ep)) {
274 VALUE envval = ep[VM_ENV_DATA_INDEX_ENV]; /* VM_ENV_ENVVAL(ep); */
275
276 if (!UNDEF_P(envval)) {
277 const rb_env_t *env = (const rb_env_t *)envval;
278
279 VM_ASSERT(imemo_type_p(envval, imemo_env));
280 VM_ASSERT(VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED));
281 VM_ASSERT(env->ep == ep);
282 }
283 return TRUE;
284 }
285 else {
286 return FALSE;
287 }
288}
289
290int
291rb_vm_ep_in_heap_p(const VALUE *ep)
292{
293 const rb_execution_context_t *ec = rb_current_execution_context(false);
294 if (ec == NULL || ec->vm_stack == NULL) return TRUE;
295 return vm_ep_in_heap_p_(ec, ep);
296}
297#endif
298
299static struct rb_captured_block *
300VM_CFP_TO_CAPTURED_BLOCK(const rb_control_frame_t *cfp)
301{
302 VM_ASSERT(!VM_CFP_IN_HEAP_P(GET_EC(), cfp));
303 return (struct rb_captured_block *)&cfp->self;
304}
305
306static rb_control_frame_t *
307VM_CAPTURED_BLOCK_TO_CFP(const struct rb_captured_block *captured)
308{
309 rb_control_frame_t *cfp = ((rb_control_frame_t *)((VALUE *)(captured) - 3));
310 VM_ASSERT(!VM_CFP_IN_HEAP_P(GET_EC(), cfp));
311 VM_ASSERT(sizeof(rb_control_frame_t)/sizeof(VALUE) == 7 + VM_DEBUG_BP_CHECK ? 1 : 0);
312 return cfp;
313}
314
315static int
316VM_BH_FROM_CFP_P(VALUE block_handler, const rb_control_frame_t *cfp)
317{
318 const struct rb_captured_block *captured = VM_CFP_TO_CAPTURED_BLOCK(cfp);
319 return VM_TAGGED_PTR_REF(block_handler, 0x03) == captured;
320}
321
322static VALUE
323vm_passed_block_handler(rb_execution_context_t *ec)
324{
325 VALUE block_handler = ec->passed_block_handler;
326 ec->passed_block_handler = VM_BLOCK_HANDLER_NONE;
327 vm_block_handler_verify(block_handler);
328 return block_handler;
329}
330
331static rb_cref_t *
332vm_cref_new0(VALUE klass, rb_method_visibility_t visi, int module_func, rb_cref_t *prev_cref, int pushed_by_eval, int use_prev_prev, int singleton)
333{
334 VALUE refinements = Qnil;
335 int omod_shared = FALSE;
336
337 /* scope */
338 rb_scope_visibility_t scope_visi;
339 scope_visi.method_visi = visi;
340 scope_visi.module_func = module_func;
341
342 /* refinements */
343 if (prev_cref != NULL && prev_cref != (void *)1 /* TODO: why CREF_NEXT(cref) is 1? */) {
344 refinements = CREF_REFINEMENTS(prev_cref);
345
346 if (!NIL_P(refinements)) {
347 omod_shared = TRUE;
348 CREF_OMOD_SHARED_SET(prev_cref);
349 }
350 }
351
352 VM_ASSERT(singleton || klass);
353
354 rb_cref_t *cref = SHAREABLE_IMEMO_NEW(rb_cref_t, imemo_cref, refinements);
355 /* A cref is born shareable, so possibly-unshareable children (a singleton cref's
356 * self, `using`'s refinements hash) go through the write barrier to record a shref;
357 * a plain store would let the owner's local GC collect the child under the pinned
358 * cref. next is always a cref (shareable): plain store. */
359 if (!SPECIAL_CONST_P(refinements)) RB_OBJ_WRITTEN(cref, Qundef, refinements);
360 if (klass) {
361 RB_OBJ_WRITE(cref, &cref->klass_or_self, klass);
362 }
363 else {
364 cref->klass_or_self = 0;
365 }
366 cref->next = use_prev_prev ? CREF_NEXT(prev_cref) : prev_cref;
367 *((rb_scope_visibility_t *)&cref->scope_visi) = scope_visi;
368
369 if (pushed_by_eval) CREF_PUSHED_BY_EVAL_SET(cref);
370 if (omod_shared) CREF_OMOD_SHARED_SET(cref);
371 if (singleton) CREF_SINGLETON_SET(cref);
372
373 return cref;
374}
375
376static rb_cref_t *
377vm_cref_new(VALUE klass, rb_method_visibility_t visi, int module_func, rb_cref_t *prev_cref, int pushed_by_eval, int singleton)
378{
379 return vm_cref_new0(klass, visi, module_func, prev_cref, pushed_by_eval, FALSE, singleton);
380}
381
382static int
383ref_delete_symkey(VALUE key, VALUE value, VALUE unused)
384{
385 return SYMBOL_P(key) ? ST_DELETE : ST_CONTINUE;
386}
387
388rb_cref_t *
389rb_vm_cref_dup(const rb_cref_t *cref)
390{
391 const rb_scope_visibility_t *visi = CREF_SCOPE_VISI(cref);
392 rb_cref_t *next_cref = CREF_NEXT(cref), *new_cref;
393 int pushed_by_eval = CREF_PUSHED_BY_EVAL(cref);
394 int singleton = CREF_SINGLETON(cref);
395
396 new_cref = vm_cref_new(cref->klass_or_self, visi->method_visi, visi->module_func, next_cref, pushed_by_eval, singleton);
397
398 if (!NIL_P(CREF_REFINEMENTS(cref))) {
399 VALUE ref = rb_hash_dup(CREF_REFINEMENTS(cref));
400 rb_hash_foreach(ref, ref_delete_symkey, Qnil);
401 CREF_REFINEMENTS_SET(new_cref, ref);
402 CREF_OMOD_SHARED_UNSET(new_cref);
403 }
404
405 return new_cref;
406}
407
408rb_cref_t *
409rb_vm_cref_dup_without_refinements(const rb_cref_t *cref)
410{
411 const rb_scope_visibility_t *visi = CREF_SCOPE_VISI(cref);
412 rb_cref_t *next_cref = CREF_NEXT(cref), *new_cref;
413 int pushed_by_eval = CREF_PUSHED_BY_EVAL(cref);
414 int singleton = CREF_SINGLETON(cref);
415
416 new_cref = vm_cref_new(cref->klass_or_self, visi->method_visi, visi->module_func, next_cref, pushed_by_eval, singleton);
417
418 if (!NIL_P(CREF_REFINEMENTS(cref))) {
419 CREF_REFINEMENTS_SET(new_cref, Qnil);
420 CREF_OMOD_SHARED_UNSET(new_cref);
421 }
422
423 return new_cref;
424}
425
426static rb_cref_t *
427vm_cref_new_toplevel(rb_execution_context_t *ec)
428{
429 rb_cref_t *cref = vm_cref_new(rb_cObject, METHOD_VISI_PRIVATE /* toplevel visibility is private */, FALSE, NULL, FALSE, FALSE);
430 VALUE top_wrapper = rb_ec_thread_ptr(ec)->top_wrapper;
431
432 if (top_wrapper) {
433 cref = vm_cref_new(top_wrapper, METHOD_VISI_PRIVATE, FALSE, cref, FALSE, FALSE);
434 }
435
436 return cref;
437}
438
439rb_cref_t *
440rb_vm_cref_new_toplevel(void)
441{
442 return vm_cref_new_toplevel(GET_EC());
443}
444
445static void
446vm_cref_dump(const char *mesg, const rb_cref_t *cref)
447{
448 ruby_debug_printf("vm_cref_dump: %s (%p)\n", mesg, (void *)cref);
449
450 while (cref) {
451 VALUE path = rb_class_path(CREF_CLASS(cref));
452 ruby_debug_printf("= cref| klass: %.*s\n",
453 RSTRING_LENINT(path), RSTRING_PTR(path));
454 cref = CREF_NEXT(cref);
455 }
456}
457
458void
459rb_vm_block_ep_update(VALUE obj, const struct rb_block *dst, const VALUE *ep)
460{
461 *((const VALUE **)&dst->as.captured.ep) = ep;
462 RB_OBJ_WRITTEN(obj, Qundef, VM_ENV_ENVVAL(ep));
463}
464
465static void
466vm_bind_update_env(VALUE bindval, rb_binding_t *bind, VALUE envval)
467{
468 const rb_env_t *env = (rb_env_t *)envval;
469 RB_OBJ_WRITE(bindval, &bind->block.as.captured.code.iseq, env->iseq);
470 rb_vm_block_ep_update(bindval, &bind->block, env->ep);
471}
472
473#if VM_COLLECT_USAGE_DETAILS
474static void vm_collect_usage_operand(int insn, int n, VALUE op);
475static void vm_collect_usage_insn(int insn);
476static void vm_collect_usage_register(int reg, int isset);
477#endif
478
479static VALUE vm_make_env_object(const rb_execution_context_t *ec, rb_control_frame_t *cfp);
480static VALUE vm_invoke_bmethod(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
481 int argc, const VALUE *argv, int kw_splat, VALUE block_handler,
483static VALUE vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, int kw_splat, VALUE block_handler, const rb_cref_t *cref);
484
485#if USE_YJIT
486// Counter to serve as a proxy for execution time, total number of calls
487static unsigned int yjit_total_entry_hits = 0;
488
489// Number of calls used to estimate how hot an ISEQ is
490#define YJIT_CALL_COUNT_INTERV 20u
491
493static inline bool
494rb_yjit_threshold_hit(const rb_iseq_t *iseq, unsigned int entry_calls)
495{
496 yjit_total_entry_hits += 1;
497
498 // Record the number of calls at the beginning of the interval
499 if (entry_calls + YJIT_CALL_COUNT_INTERV == rb_yjit_call_threshold) {
500 ISEQ_BODY(iseq)->yjit_calls_at_interv = yjit_total_entry_hits;
501 }
502
503 // Try to estimate the total time taken (total number of calls) to reach 20 calls to this ISEQ
504 // This give us a ratio of how hot/cold this ISEQ is
505 if (entry_calls == rb_yjit_call_threshold) {
506 // We expect threshold 1 to compile everything immediately
507 if (rb_yjit_call_threshold < YJIT_CALL_COUNT_INTERV) {
508 return true;
509 }
510
511 unsigned int num_calls = yjit_total_entry_hits - ISEQ_BODY(iseq)->yjit_calls_at_interv;
512
513 // Reject ISEQs that don't get called often enough
514 if (num_calls > rb_yjit_cold_threshold) {
515 rb_yjit_incr_counter("cold_iseq_entry");
516 return false;
517 }
518
519 return true;
520 }
521
522 return false;
523}
524#else
525#define rb_yjit_threshold_hit(iseq, entry_calls) false
526#endif
527
528#if USE_YJIT
529// Generate JIT code that supports the following kinds of ISEQ entries:
530// * The first ISEQ on vm_exec (e.g. <main>, or Ruby methods/blocks
531// called by a C method). The current frame has VM_FRAME_FLAG_FINISH.
532// The current vm_exec stops if JIT code returns a non-Qundef value.
533// * ISEQs called by the interpreter on vm_sendish (e.g. Ruby methods or
534// blocks called by a Ruby frame that isn't compiled or side-exited).
535// The current frame doesn't have VM_FRAME_FLAG_FINISH. The current
536// vm_exec does NOT stop whether JIT code returns Qundef or not.
537static inline rb_jit_func_t
538yjit_compile(rb_execution_context_t *ec)
539{
540 const rb_iseq_t *iseq = CFP_ISEQ(ec->cfp);
541 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
542
543 // Increment the ISEQ's call counter and trigger JIT compilation if not compiled.
544 // Stop incrementing when not compiling (out of executable memory) so that
545 // ISEQs that failed to compile don't keep dirtying CoW pages after fork.
546 if (body->jit_entry == NULL && rb_yjit_compiling_p) {
547 body->jit_entry_calls++;
548 if (rb_yjit_threshold_hit(iseq, body->jit_entry_calls)) {
549 rb_yjit_compile_iseq(iseq, ec, false);
550 }
551 }
552 return body->jit_entry;
553}
554#else
555# define yjit_compile(ec) ((rb_jit_func_t)0)
556#endif
557
558#if USE_ZJIT
559static inline rb_jit_func_t
560zjit_compile(rb_execution_context_t *ec)
561{
562 const rb_iseq_t *iseq = CFP_ISEQ(ec->cfp);
563 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
564
565 if (body->jit_entry == NULL && rb_zjit_compiling_p) {
566 body->jit_entry_calls++;
567
568 // At profile-threshold, rewrite some of the YARV instructions
569 // to zjit_* instructions to profile these instructions.
570 if (body->jit_entry_calls == rb_zjit_profile_threshold) {
571 rb_zjit_profile_enable(iseq);
572 }
573
574 // At call-threshold, compile the ISEQ with ZJIT.
575 if (body->jit_entry_calls == rb_zjit_call_threshold) {
576 rb_zjit_compile_iseq(iseq, ec, false);
577 }
578 }
579 return body->jit_entry;
580}
581#else
582# define zjit_compile(ec) ((rb_jit_func_t)0)
583#endif
584
585// Return the call threshold of the enabled JIT, or 0 when no JIT is enabled.
586unsigned int
587rb_jit_call_threshold(void)
588{
589#if USE_YJIT
590 if (rb_yjit_enabled_p) {
591 return rb_yjit_call_threshold;
592 }
593#endif
594#if USE_ZJIT
595 if (rb_zjit_enabled_p) {
596 return rb_zjit_call_threshold;
597 }
598#endif
599 return 0;
600}
601
602#if USE_YJIT || USE_ZJIT
603// Execute JIT code compiled by yjit_compile() or zjit_compile()
604static inline VALUE
605jit_exec(rb_execution_context_t *ec)
606{
607#if USE_YJIT
608 if (rb_yjit_enabled_p) {
609 rb_jit_func_t func = yjit_compile(ec);
610 if (func) {
611 return func(ec, ec->cfp);
612 }
613 return Qundef;
614 }
615#endif
616
617#if USE_ZJIT
618 void *zjit_entry = rb_zjit_entry;
619 if (zjit_entry) {
620 rb_jit_func_t func = zjit_compile(ec);
621 if (func) {
622 VALUE result = ((rb_zjit_func_t)zjit_entry)(ec, ec->cfp, func);
623 return result;
624 }
625 }
626#endif
627 return Qundef;
628}
629
630// Generate JIT code that supports the following kind of ISEQ entry:
631// * The first ISEQ pushed by vm_exec_handle_exception. The frame would
632// point to a location specified by a catch table, and it doesn't have
633// VM_FRAME_FLAG_FINISH. The current vm_exec stops if JIT code returns
634// a non-Qundef value. So you should not return a non-Qundef value
635// until ec->cfp is changed to a frame with VM_FRAME_FLAG_FINISH.
636static inline rb_jit_func_t
637jit_compile_exception(rb_execution_context_t *ec)
638{
639 const rb_iseq_t *iseq = CFP_ISEQ(ec->cfp);
640 struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
641
642#if USE_ZJIT
643 // rb_zjit_compiling_p is false until ZJIT is enabled, so no
644 // rb_zjit_enabled_p check is needed here.
645 if (body->jit_exception == NULL && rb_zjit_compiling_p) {
646 body->jit_exception_calls++;
647
648 // At profile-threshold, rewrite some of the YARV instructions
649 // to zjit_* instructions to profile these instructions.
650 if (body->jit_exception_calls == rb_zjit_profile_threshold) {
651 rb_zjit_profile_enable(iseq);
652 }
653
654 // At call-threshold, compile the ISEQ with ZJIT.
655 if (body->jit_exception_calls == rb_zjit_call_threshold) {
656 rb_zjit_compile_iseq(iseq, ec, true);
657 }
658 }
659#endif
660
661#if USE_YJIT
662 // Increment the ISEQ's call counter and trigger JIT compilation if not compiled.
663 // Like the ZJIT branch above, no rb_yjit_enabled_p check is needed here.
664 if (body->jit_exception == NULL && rb_yjit_compiling_p) {
665 body->jit_exception_calls++;
666 if (body->jit_exception_calls == rb_yjit_call_threshold) {
667 rb_yjit_compile_iseq(iseq, ec, true);
668 }
669 }
670#endif
671 return body->jit_exception;
672}
673
674// Execute JIT code compiled by jit_compile_exception()
675static inline VALUE
676jit_exec_exception(rb_execution_context_t *ec)
677{
678 rb_jit_func_t func = jit_compile_exception(ec);
679 if (func) {
680 // Call the JIT code
681 return func(ec, ec->cfp);
682 }
683 else {
684 return Qundef;
685 }
686}
687#else
688# define jit_compile_exception(ec) ((rb_jit_func_t)0)
689# define jit_exec(ec) Qundef
690# define jit_exec_exception(ec) Qundef
691#endif
692
693static void add_opt_method_entry(const rb_method_entry_t *me);
694
695#define RB_TYPE_2_P(obj, type1, type2) \
696 (RB_TYPE_P(obj, type1) || RB_TYPE_P(obj, type2))
697#define RB_TYPE_3_P(obj, type1, type2, type3) \
698 (RB_TYPE_P(obj, type1) || RB_TYPE_P(obj, type2) || RB_TYPE_P(obj, type3))
699
700#define VM_ASSERT_TYPE(obj, type) \
701 VM_ASSERT(RB_TYPE_P(obj, type), #obj ": %s", rb_obj_info(obj))
702#define VM_ASSERT_TYPE2(obj, type1, type2) \
703 VM_ASSERT(RB_TYPE_2_P(obj, type1, type2), #obj ": %s", rb_obj_info(obj))
704#define VM_ASSERT_TYPE3(obj, type1, type2, type3) \
705 VM_ASSERT(RB_TYPE_3_P(obj, type1, type2, type3), #obj ": %s", rb_obj_info(obj))
706
707static const rb_box_t * current_box_on_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp);
708
709#include "vm_insnhelper.c"
710
711#include "vm_exec.c"
712
713#include "vm_method.c"
714#include "vm_eval.c"
715
716#define PROCDEBUG 0
717
718VALUE rb_cRubyVM;
720VALUE rb_mRubyVMFrozenCore;
721VALUE rb_block_param_proxy;
722
723VALUE ruby_vm_const_missing_count = 0;
724rb_vm_t *ruby_current_vm_ptr = NULL;
725rb_ractor_t *ruby_single_main_ractor;
726bool ruby_vm_keep_script_lines;
727
728#ifdef RB_THREAD_LOCAL_SPECIFIER
729RB_THREAD_LOCAL_SPECIFIER rb_execution_context_t *ruby_current_ec;
730
731#ifdef RUBY_NT_SERIAL
732RB_THREAD_LOCAL_SPECIFIER rb_atomic_t ruby_nt_serial;
733#endif
734
735// no-inline decl on vm_core.h
737rb_current_ec_noinline(void)
738{
739 return ruby_current_ec;
740}
741
742void
743rb_current_ec_set(rb_execution_context_t *ec)
744{
745 ruby_current_ec = ec;
746}
747
749rb_current_ec(void)
750{
751 return ruby_current_ec;
752}
753#else
754native_tls_key_t ruby_current_ec_key;
755
756// no-inline decl on vm_core.h
758rb_current_ec_noinline(void)
759{
760 return native_tls_get(ruby_current_ec_key);
761}
762
763#endif
764
765rb_event_flag_t ruby_vm_event_flags = 0;
766rb_event_flag_t ruby_vm_event_enabled_global_flags = 0;
767unsigned int ruby_vm_c_events_enabled = 0;
768unsigned int ruby_vm_iseq_events_enabled = 0;
769
770rb_serial_t ruby_vm_constant_cache_invalidations = 0;
771rb_serial_t ruby_vm_constant_cache_misses = 0;
772rb_serial_t ruby_vm_global_cvar_state = 1;
773
774const struct rb_callcache rb_vm_empty_cc = {
775 .flags = T_IMEMO | (imemo_callcache << FL_USHIFT) | VM_CALLCACHE_UNMARKABLE,
776 .klass = Qundef,
777 .cme_ = NULL,
778 .call_ = vm_call_general,
779 .aux_ = {
780 .v = Qfalse,
781 }
782};
783
784/* Installed instead of a real cc at call sites of methods that opted out of
785 * call caching: dispatch resolves the cme on every call rather than leaving a
786 * cc behind on the receiver's class. */
787const struct rb_callcache rb_vm_uncached_cc = {
788 .flags = T_IMEMO | (imemo_callcache << FL_USHIFT) | VM_CALLCACHE_UNMARKABLE,
789 .klass = Qundef,
790 .cme_ = NULL,
791 .call_ = vm_call_uncached,
792 .aux_ = {
793 .v = Qfalse,
794 }
795};
796
797const struct rb_callcache rb_vm_empty_cc_for_super = {
798 .flags = T_IMEMO | (imemo_callcache << FL_USHIFT) | VM_CALLCACHE_UNMARKABLE,
799 .klass = Qundef,
800 .cme_ = NULL,
801 .call_ = vm_call_super_method,
802 .aux_ = {
803 .v = Qfalse,
804 }
805};
806
807static void thread_free(void *ptr);
808
809void
810rb_vm_inc_const_missing_count(void)
811{
812 ruby_vm_const_missing_count +=1;
813}
814
815int
816rb_dtrace_setup(rb_execution_context_t *ec, VALUE klass, ID id,
817 struct ruby_dtrace_method_hook_args *args)
818{
820 if (!klass) {
821 if (!ec) ec = GET_EC();
822 if (!rb_ec_frame_method_id_and_class(ec, &id, 0, &klass) || !klass)
823 return FALSE;
824 }
825 if (RB_TYPE_P(klass, T_ICLASS)) {
826 klass = RBASIC(klass)->klass;
827 }
828 else if (RCLASS_SINGLETON_P(klass)) {
829 klass = RCLASS_ATTACHED_OBJECT(klass);
830 if (NIL_P(klass)) return FALSE;
831 }
832 type = BUILTIN_TYPE(klass);
833 if (type == T_CLASS || type == T_ICLASS || type == T_MODULE) {
834 VALUE name = rb_class_path(klass);
835 const char *classname, *filename;
836 const char *methodname = rb_id2name(id);
837 if (methodname && (filename = rb_source_location_cstr(&args->line_no)) != 0) {
838 if (NIL_P(name) || !(classname = StringValuePtr(name)))
839 classname = "<unknown>";
840 args->classname = classname;
841 args->methodname = methodname;
842 args->filename = filename;
843 args->klass = klass;
844 args->name = name;
845 return TRUE;
846 }
847 }
848 return FALSE;
849}
850
851extern unsigned int redblack_buffer_size;
852
853/*
854 * call-seq:
855 * RubyVM.stat -> Hash
856 * RubyVM.stat(hsh) -> hsh
857 * RubyVM.stat(Symbol) -> Numeric
858 *
859 * Returns a Hash containing implementation-dependent counters inside the VM.
860 *
861 * This hash includes information about method/constant caches:
862 *
863 * {
864 * :constant_cache_invalidations=>2,
865 * :constant_cache_misses=>14,
866 * :global_cvar_state=>27
867 * }
868 *
869 * If <tt>USE_DEBUG_COUNTER</tt> is enabled, debug counters will be included.
870 *
871 * The contents of the hash are implementation specific and may be changed in
872 * the future.
873 *
874 * This method is only expected to work on C Ruby.
875 */
876static VALUE
877vm_stat(int argc, VALUE *argv, VALUE self)
878{
879 static VALUE sym_constant_cache_invalidations, sym_constant_cache_misses, sym_global_cvar_state, sym_next_shape_id;
880 static VALUE sym_shape_cache_size;
881 VALUE arg = Qnil;
882 VALUE hash = Qnil, key = Qnil;
883
884 if (rb_check_arity(argc, 0, 1) == 1) {
885 arg = argv[0];
886 if (SYMBOL_P(arg))
887 key = arg;
888 else if (RB_TYPE_P(arg, T_HASH))
889 hash = arg;
890 else
891 rb_raise(rb_eTypeError, "non-hash or symbol given");
892 }
893 else {
894 hash = rb_hash_new();
895 }
896
897#define S(s) sym_##s = ID2SYM(rb_intern_const(#s))
898 S(constant_cache_invalidations);
899 S(constant_cache_misses);
900 S(global_cvar_state);
901 S(next_shape_id);
902 S(shape_cache_size);
903#undef S
904
905#define SET(name, attr) \
906 if (key == sym_##name) \
907 return SERIALT2NUM(attr); \
908 else if (hash != Qnil) \
909 rb_hash_aset(hash, sym_##name, SERIALT2NUM(attr));
910
911 SET(constant_cache_invalidations, ruby_vm_constant_cache_invalidations);
912 SET(constant_cache_misses, ruby_vm_constant_cache_misses);
913 SET(global_cvar_state, ruby_vm_global_cvar_state);
914 SET(next_shape_id, (rb_serial_t)rb_shapes_count());
915 SET(shape_cache_size, (rb_serial_t)rb_shapes_cache_size());
916#undef SET
917
918#if USE_DEBUG_COUNTER
919 ruby_debug_counter_show_at_exit(FALSE);
920 for (size_t i = 0; i < RB_DEBUG_COUNTER_MAX; i++) {
921 const VALUE name = rb_sym_intern_ascii_cstr(rb_debug_counter_names[i]);
922 const VALUE boxed_value = SIZET2NUM(rb_debug_counter[i]);
923
924 if (key == name) {
925 return boxed_value;
926 }
927 else if (hash != Qnil) {
928 rb_hash_aset(hash, name, boxed_value);
929 }
930 }
931#endif
932
933 if (!NIL_P(key)) { /* matched key should return above */
934 rb_raise(rb_eArgError, "unknown key: %"PRIsVALUE, rb_sym2str(key));
935 }
936
937 return hash;
938}
939
940/* control stack frame */
941
942static void
943vm_set_top_stack(rb_execution_context_t *ec, const rb_iseq_t *iseq, const rb_box_t *box)
944{
945 if (ISEQ_BODY(iseq)->type != ISEQ_TYPE_TOP) {
946 rb_raise(rb_eTypeError, "Not a toplevel InstructionSequence");
947 }
948
949 /* for return */
950 vm_push_frame(ec, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH,
951 rb_ec_thread_ptr(ec)->top_self,
952 GC_GUARDED_PTR(box),
953 (VALUE)vm_cref_new_toplevel(ec), /* cref or me */
954 ISEQ_BODY(iseq)->iseq_encoded, ec->cfp->sp,
955 ISEQ_BODY(iseq)->local_table_size, ISEQ_BODY(iseq)->stack_max);
956}
957
958static void
959vm_set_eval_stack(rb_execution_context_t *ec, const rb_iseq_t *iseq, const rb_cref_t *cref, const struct rb_block *base_block)
960{
961 vm_push_frame(ec, iseq, VM_FRAME_MAGIC_EVAL | VM_FRAME_FLAG_FINISH,
962 vm_block_self(base_block), VM_GUARDED_PREV_EP(vm_block_ep(base_block)),
963 (VALUE)cref, /* cref or me */
964 ISEQ_BODY(iseq)->iseq_encoded,
965 ec->cfp->sp, ISEQ_BODY(iseq)->local_table_size,
966 ISEQ_BODY(iseq)->stack_max);
967}
968
969static void
970vm_set_main_stack(rb_execution_context_t *ec, const rb_iseq_t *iseq)
971{
972 VALUE toplevel_binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
973 rb_binding_t *bind;
974
975 GetBindingPtr(toplevel_binding, bind);
976 RUBY_ASSERT_MESG(bind, "TOPLEVEL_BINDING is not built");
977
978 vm_set_eval_stack(ec, iseq, 0, &bind->block);
979
980 /* save binding */
981 if (ISEQ_BODY(iseq)->local_table_size > 0) {
982 vm_bind_update_env(toplevel_binding, bind, vm_make_env_object(ec, ec->cfp));
983 }
984}
985
987rb_vm_get_binding_creatable_next_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
988{
989 while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
990 if (CFP_ISEQ(cfp)) {
991 return (rb_control_frame_t *)cfp;
992 }
993 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
994 }
995 return 0;
996}
997
999rb_vm_get_ruby_level_next_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
1000{
1001 while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
1002 if (VM_FRAME_RUBYFRAME_P(cfp)) {
1003 return (rb_control_frame_t *)cfp;
1004 }
1005 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
1006 }
1007 return 0;
1008}
1009
1010static rb_control_frame_t *
1011vm_get_ruby_level_caller_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
1012{
1013 if (VM_FRAME_RUBYFRAME_P(cfp)) {
1014 return (rb_control_frame_t *)cfp;
1015 }
1016
1017 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
1018
1019 while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
1020 if (VM_FRAME_RUBYFRAME_P(cfp)) {
1021 return (rb_control_frame_t *)cfp;
1022 }
1023
1024 if (VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_PASSED) == FALSE) {
1025 break;
1026 }
1027 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
1028 }
1029 return 0;
1030}
1031
1032void
1033rb_vm_pop_cfunc_frame(void)
1034{
1035 rb_execution_context_t *ec = GET_EC();
1036 rb_control_frame_t *cfp = ec->cfp;
1037 const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
1038
1039 EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, cfp->self, me->def->original_id, me->called_id, me->owner, Qnil);
1040 RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, me->owner, me->def->original_id);
1041 vm_pop_frame(ec, cfp, cfp->ep);
1042}
1043
1044void
1045rb_vm_rewind_cfp(rb_execution_context_t *ec, rb_control_frame_t *cfp)
1046{
1047 /* check skipped frame */
1048 while (ec->cfp != cfp) {
1049#if VMDEBUG
1050 printf("skipped frame: %s\n", vm_frametype_name(ec->cfp));
1051#endif
1052 if (VM_FRAME_TYPE(ec->cfp) != VM_FRAME_MAGIC_CFUNC) {
1053 rb_vm_pop_frame(ec);
1054 }
1055 else { /* unlikely path */
1056 rb_vm_pop_cfunc_frame();
1057 }
1058 }
1059}
1060
1061/* at exit */
1062
1063void
1064ruby_vm_at_exit(void (*func)(rb_vm_t *))
1065{
1066 rb_vm_t *vm = GET_VM();
1068 nl->func = func;
1069 nl->next = vm->at_exit;
1070 vm->at_exit = nl;
1071}
1072
1073static void
1074ruby_vm_run_at_exit_hooks(rb_vm_t *vm)
1075{
1076 rb_at_exit_list *l = vm->at_exit;
1077
1078 while (l) {
1079 rb_at_exit_list* t = l->next;
1080 rb_vm_at_exit_func *func = l->func;
1081 ruby_xfree(l);
1082 l = t;
1083 (*func)(vm);
1084 }
1085}
1086
1087/* Env */
1088
1089static VALUE check_env_value(const rb_env_t *env);
1090
1091static int
1092check_env(const rb_env_t *env)
1093{
1094 fputs("---\n", stderr);
1095 ruby_debug_printf("envptr: %p\n", (void *)&env->ep[0]);
1096 ruby_debug_printf("envval: %10p ", (void *)env->ep[1]);
1097 dp(env->ep[1]);
1098 ruby_debug_printf("ep: %10p\n", (void *)env->ep);
1099 if (rb_vm_env_prev_env(env)) {
1100 fputs(">>\n", stderr);
1101 check_env_value(rb_vm_env_prev_env(env));
1102 fputs("<<\n", stderr);
1103 }
1104 return 1;
1105}
1106
1107static VALUE
1108check_env_value(const rb_env_t *env)
1109{
1110 if (check_env(env)) {
1111 return (VALUE)env;
1112 }
1113 rb_bug("invalid env");
1114 return Qnil; /* unreachable */
1115}
1116
1117static VALUE
1118vm_block_handler_escape(const rb_execution_context_t *ec, VALUE block_handler)
1119{
1120 switch (vm_block_handler_type(block_handler)) {
1121 case block_handler_type_ifunc:
1122 case block_handler_type_iseq:
1123 return rb_vm_make_proc(ec, VM_BH_TO_CAPT_BLOCK(block_handler), rb_cProc);
1124
1125 case block_handler_type_symbol:
1126 case block_handler_type_proc:
1127 return block_handler;
1128 }
1129 VM_UNREACHABLE(vm_block_handler_escape);
1130 return Qnil;
1131}
1132
1133// child_ep is the on-stack ep of a frame whose prev ep is cfp->ep, or NULL.
1134static VALUE
1135vm_make_env_each(const rb_execution_context_t * const ec, rb_control_frame_t *const cfp, const VALUE *const child_ep)
1136{
1137 const VALUE * const ep = cfp->ep;
1138 VALUE *env_body, *env_ep;
1139 int local_size, env_size;
1140
1141 if (VM_ENV_ESCAPED_P(ep)) {
1142 return VM_ENV_ENVVAL(ep);
1143 }
1144
1145 if (!VM_ENV_LOCAL_P(ep)) {
1146 const VALUE *prev_ep = VM_ENV_PREV_EP(ep);
1147 if (!VM_ENV_ESCAPED_P(prev_ep)) {
1148 rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
1149
1150 while (prev_cfp->ep != prev_ep) {
1151 prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(prev_cfp);
1152 VM_ASSERT(prev_cfp->ep != NULL);
1153 }
1154
1155 // Updates ep[VM_ENV_DATA_INDEX_SPECVAL] to point to the heap ep of prev_cfp
1156 vm_make_env_each(ec, prev_cfp, ep);
1157 VM_ASSERT(VM_ENV_PREV_EP(ep) == prev_cfp->ep);
1158 }
1159 }
1160 else {
1161 VM_ASSERT(VM_ENV_LOCAL_P(ep));
1162 VALUE block_handler = VM_ENV_BLOCK_HANDLER(ep);
1163
1164 if (block_handler != VM_BLOCK_HANDLER_NONE) {
1165 VALUE blockprocval = vm_block_handler_escape(ec, block_handler);
1166 VM_STACK_ENV_WRITE(ep, VM_ENV_DATA_INDEX_SPECVAL, blockprocval);
1167 }
1168 }
1169
1170 const rb_iseq_t *iseq = CFP_ISEQ(cfp);
1171 if (!VM_FRAME_RUBYFRAME_P(cfp)) {
1172 local_size = VM_ENV_DATA_SIZE;
1173 }
1174 else {
1175 local_size = ISEQ_BODY(iseq)->local_table_size;
1176 if (ISEQ_BODY(iseq)->param.flags.forwardable && VM_ENV_LOCAL_P(cfp->ep)) {
1177 int ci_offset = local_size - ISEQ_BODY(iseq)->param.size + VM_ENV_DATA_SIZE;
1178
1179 CALL_INFO ci = (CALL_INFO)VM_CF_LEP(cfp)[-ci_offset];
1180 local_size += vm_ci_argc(ci);
1181 }
1182 local_size += VM_ENV_DATA_SIZE;
1183 }
1184
1185 // Invalidate JIT code that assumes cfp->ep == vm_base_ptr(cfp).
1186 // This is done before creating the imemo_env because VM_STACK_ENV_WRITE
1187 // below leaves the on-stack ep in a state that is unsafe to GC.
1188 // Once the enabled JIT has recorded this iseq's escape, the invalidations
1189 // are no longer useful and can slow down Ractors.
1190 if (VM_FRAME_RUBYFRAME_P(cfp) &&
1191 !rbimpl_atomic_load(&ISEQ_BODY(iseq)->jit_ep_escape_recorded, RBIMPL_ATOMIC_RELAXED)) {
1192 if (rb_yjit_enabled_p) rb_yjit_invalidate_ep_is_bp(iseq);
1193 if (rb_zjit_enabled_p) rb_zjit_invalidate_no_ep_escape(iseq);
1194 }
1195
1196 /*
1197 * # local variables on a stack frame (N == local_size)
1198 * [lvar1, lvar2, ..., lvarN, SPECVAL]
1199 * ^
1200 * ep[0]
1201 *
1202 * # moved local variables
1203 * [lvar1, lvar2, ..., lvarN, SPECVAL, Envval, BlockProcval (if needed)]
1204 * ^ ^
1205 * env->env[0] ep[0]
1206 */
1207
1208 env_size = local_size +
1209 1 /* envval */;
1210
1211 // Careful with order in the following sequence. Each allocation can move objects.
1212 env_body = ALLOC_N(VALUE, env_size);
1213 rb_env_t *env = IMEMO_NEW(rb_env_t, imemo_env, 0);
1214
1215 // Set up env without WB since it's brand new (similar to newobj_init(), newobj_fill())
1216 MEMCPY(env_body, ep - (local_size - 1 /* specval */), VALUE, local_size);
1217
1218 env_ep = &env_body[local_size - 1 /* specval */];
1219 env_ep[VM_ENV_DATA_INDEX_ENV] = (VALUE)env;
1220
1221 env->iseq = (rb_iseq_t *)(VM_FRAME_RUBYFRAME_P(cfp) ? iseq : NULL);
1222 env->ep = env_ep;
1223 env->env = env_body;
1224 env->env_size = env_size;
1225 VM_ENV_FLAGS_SET(env_ep, VM_ENV_FLAG_ESCAPED | VM_ENV_FLAG_WB_REQUIRED);
1226
1227 // Ensure the heap ep is fully initialized before cfp->ep and child_ep point to it,
1228 // as rb_profile_frames() might read it from a signal handler.
1229#if defined HAVE_DECL_ATOMIC_SIGNAL_FENCE && HAVE_DECL_ATOMIC_SIGNAL_FENCE
1230 atomic_signal_fence(memory_order_seq_cst);
1231#endif
1232
1233 cfp->ep = env_ep;
1234
1235 // The child frame must point to the heap ep before the on-stack ep flags are
1236 // overwritten below, otherwise rb_profile_frames() called from a signal handler
1237 // could walk from the child frame to the overwritten on-stack ep and crash.
1238 if (child_ep) {
1239 VM_FORCE_WRITE_SPECIAL_CONST(&child_ep[VM_ENV_DATA_INDEX_SPECVAL], VM_GUARDED_PREV_EP(env_ep));
1240 }
1241
1242 // Ensure the stores to cfp->ep and child_ep above are not reordered after the
1243 // overwrite of the on-stack ep flags below.
1244#if defined HAVE_DECL_ATOMIC_SIGNAL_FENCE && HAVE_DECL_ATOMIC_SIGNAL_FENCE
1245 atomic_signal_fence(memory_order_seq_cst);
1246#endif
1247
1248 VM_STACK_ENV_WRITE(ep, 0, (VALUE)env); /* GC mark */
1249
1250#if 0
1251 for (i = 0; i < local_size; i++) {
1252 if (VM_FRAME_RUBYFRAME_P(cfp)) {
1253 /* clear value stack for GC */
1254 ep[-local_size + i] = 0;
1255 }
1256 }
1257#endif
1258
1259 return (VALUE)env;
1260}
1261
1262static VALUE
1263vm_make_env_object(const rb_execution_context_t *ec, rb_control_frame_t *cfp)
1264{
1265 VALUE envval = vm_make_env_each(ec, cfp, NULL);
1266
1267 if (PROCDEBUG) {
1268 check_env_value((const rb_env_t *)envval);
1269 }
1270
1271 return envval;
1272}
1273
1274void
1275rb_vm_stack_to_heap(rb_execution_context_t *ec)
1276{
1277 rb_control_frame_t *cfp = ec->cfp;
1278 while ((cfp = rb_vm_get_binding_creatable_next_cfp(ec, cfp)) != 0) {
1279 vm_make_env_object(ec, cfp);
1280 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
1281 }
1282}
1283
1284const rb_env_t *
1285rb_vm_env_prev_env(const rb_env_t *env)
1286{
1287 const VALUE *ep = env->ep;
1288
1289 if (VM_ENV_LOCAL_P(ep)) {
1290 return NULL;
1291 }
1292 else {
1293 const VALUE *prev_ep = VM_ENV_PREV_EP(ep);
1294 return VM_ENV_ENVVAL_PTR(prev_ep);
1295 }
1296}
1297
1298static int
1299collect_local_variables_in_iseq(const rb_iseq_t *iseq, const struct local_var_list *vars)
1300{
1301 unsigned int i;
1302 if (!iseq) return 0;
1303 for (i = 0; i < ISEQ_BODY(iseq)->local_table_size; i++) {
1304 local_var_list_add(vars, ISEQ_BODY(iseq)->local_table[i]);
1305 }
1306 return 1;
1307}
1308
1309static void
1310collect_local_variables_in_env(const rb_env_t *env, const struct local_var_list *vars)
1311{
1312 do {
1313 if (VM_ENV_FLAGS(env->ep, VM_ENV_FLAG_ISOLATED)) break;
1314 collect_local_variables_in_iseq(env->iseq, vars);
1315 } while ((env = rb_vm_env_prev_env(env)) != NULL);
1316}
1317
1318static int
1319vm_collect_local_variables_in_heap(const VALUE *ep, const struct local_var_list *vars)
1320{
1321 if (VM_ENV_ESCAPED_P(ep)) {
1322 collect_local_variables_in_env(VM_ENV_ENVVAL_PTR(ep), vars);
1323 return 1;
1324 }
1325 else {
1326 return 0;
1327 }
1328}
1329
1330VALUE
1331rb_vm_env_local_variables(const rb_env_t *env)
1332{
1333 struct local_var_list vars;
1334 local_var_list_init(&vars);
1335 collect_local_variables_in_env(env, &vars);
1336 return local_var_list_finish(&vars);
1337}
1338
1339VALUE
1340rb_vm_env_numbered_parameters(const rb_env_t *env)
1341{
1342 struct local_var_list vars;
1343 local_var_list_init(&vars);
1344 // if (VM_ENV_FLAGS(env->ep, VM_ENV_FLAG_ISOLATED)) break; // TODO: is this needed?
1345 const rb_iseq_t *iseq = env->iseq;
1346 unsigned int i;
1347 if (!iseq) return 0;
1348 for (i = 0; i < ISEQ_BODY(iseq)->local_table_size; i++) {
1349 numparam_list_add(&vars, ISEQ_BODY(iseq)->local_table[i]);
1350 }
1351 return local_var_list_finish(&vars);
1352}
1353
1354VALUE
1355rb_iseq_local_variables(const rb_iseq_t *iseq)
1356{
1357 struct local_var_list vars;
1358 local_var_list_init(&vars);
1359 while (collect_local_variables_in_iseq(iseq, &vars)) {
1360 iseq = ISEQ_BODY(iseq)->parent_iseq;
1361 }
1362 return local_var_list_finish(&vars);
1363}
1364
1365/* Proc */
1366
1367static VALUE
1368vm_proc_create_from_captured(VALUE klass,
1369 const struct rb_captured_block *captured,
1370 enum rb_block_type block_type,
1371 int8_t is_from_method, int8_t is_lambda)
1372{
1373 VALUE procval = rb_proc_alloc(klass, block_type);
1374 rb_proc_t *proc = RTYPEDDATA_DATA(procval);
1375
1376 VM_ASSERT(VM_EP_IN_HEAP_P(GET_EC(), captured->ep));
1377
1378 /* copy block */
1379 RB_OBJ_WRITE(procval, &proc->block.as.captured.code.val, captured->code.val);
1380 RB_OBJ_WRITE(procval, &proc->block.as.captured.self, captured->self);
1381 rb_vm_block_ep_update(procval, &proc->block, captured->ep);
1382
1383 vm_block_type_set(&proc->block, block_type);
1384 proc->header.is_from_method = is_from_method;
1385 proc->header.is_lambda = is_lambda;
1386
1387 return procval;
1388}
1389
1390void
1391rb_vm_block_copy(VALUE obj, const struct rb_block *dst, const struct rb_block *src)
1392{
1393 /* copy block */
1394 switch (vm_block_type(src)) {
1395 case block_type_iseq:
1396 case block_type_ifunc:
1397 RB_OBJ_WRITE(obj, &dst->as.captured.self, src->as.captured.self);
1398 RB_OBJ_WRITE(obj, &dst->as.captured.code.val, src->as.captured.code.val);
1399 rb_vm_block_ep_update(obj, dst, src->as.captured.ep);
1400 break;
1401 case block_type_symbol:
1402 RB_OBJ_WRITE(obj, &dst->as.symbol, src->as.symbol);
1403 break;
1404 case block_type_proc:
1405 RB_OBJ_WRITE(obj, &dst->as.proc, src->as.proc);
1406 break;
1407 }
1408}
1409
1410static VALUE
1411proc_create(VALUE klass, const struct rb_block *block, int8_t is_from_method, int8_t is_lambda)
1412{
1413 VALUE procval = rb_proc_alloc(klass, block->type);
1414 rb_proc_t *proc = RTYPEDDATA_DATA(procval);
1415
1416 VM_ASSERT(VM_EP_IN_HEAP_P(GET_EC(), vm_block_ep(block)));
1417 rb_vm_block_copy(procval, &proc->block, block);
1418 vm_block_type_set(&proc->block, block->type);
1419 proc->header.is_from_method = is_from_method;
1420 proc->header.is_lambda = is_lambda;
1421
1422 return procval;
1423}
1424
1425VALUE
1426rb_proc_dup_0(VALUE self)
1427{
1428 VALUE procval;
1429 rb_proc_t *src;
1430
1431 GetProcPtr(self, src);
1432
1433 switch (vm_block_type(&src->block)) {
1434 case block_type_ifunc:
1435 procval = rb_func_proc_dup(self);
1436 break;
1437 default:
1438 procval = proc_create(rb_obj_class(self), &src->block, src->header.is_from_method, src->header.is_lambda);
1439 break;
1440 }
1441
1442 if (src->header.is_refined) {
1443 rb_proc_t *dst;
1444 GetProcPtr(procval, dst);
1445 dst->header.is_refined = 1;
1446 }
1447
1448 if (RB_OBJ_SHAREABLE_P(self)) RB_OBJ_SET_SHAREABLE(procval);
1449 RB_GC_GUARD(self); /* for: body = rb_proc_dup(body) */
1450 return procval;
1451}
1452
1453VALUE
1454rb_proc_dup(VALUE self)
1455{
1456 VALUE procval = rb_proc_dup_0(self);
1457 VALUE recipe = rb_proc_refinements_recipe(self);
1458 if (!NIL_P(recipe)) rb_proc_set_refinements_recipe(procval, recipe);
1459 return procval;
1460}
1461
1462/* Proc#refined: build a Proc that runs `iseq` with the refinements of
1463 * `recipe`, sharing self's environment. `iseq` is normally self's own block
1464 * iseq, which the copy replaces on the first call. */
1465VALUE
1466rb_proc_dup_with_iseq_and_recipe(VALUE self, const rb_iseq_t *iseq, VALUE recipe)
1467{
1468 rb_proc_t *src;
1469 GetProcPtr(self, src);
1470 VM_ASSERT(vm_block_type(&src->block) == block_type_iseq);
1471
1472 struct rb_block block = src->block;
1473 block.as.captured.code.iseq = iseq;
1474
1475 VALUE procval = proc_create(rb_obj_class(self), &block, src->header.is_from_method, src->header.is_lambda);
1476 rb_proc_set_refinements_recipe(procval, recipe);
1477
1478 RB_GC_GUARD(self);
1479 return procval;
1480}
1481
1483 VALUE ary;
1484 VALUE read_only;
1485 bool yield;
1486};
1487
1488static VALUE
1489ID2NUM(ID id)
1490{
1491 if (SIZEOF_VOIDP > SIZEOF_LONG)
1492 return ULL2NUM(id);
1493 else
1494 return ULONG2NUM(id);
1495}
1496
1497static ID
1498NUM2ID(VALUE num)
1499{
1500 if (SIZEOF_VOIDP > SIZEOF_LONG)
1501 return (ID)NUM2ULL(num);
1502 else
1503 return (ID)NUM2ULONG(num);
1504}
1505
1506static enum rb_id_table_iterator_result
1507collect_outer_variable_names(ID id, VALUE val, void *ptr)
1508{
1510
1511 if (id == rb_intern("yield")) {
1512 data->yield = true;
1513 }
1514 else {
1515 VALUE *store;
1516 if (val == Qtrue /* write */) {
1517 store = &data->ary;
1518 }
1519 else {
1520 store = &data->read_only;
1521 }
1522 if (*store == Qfalse) *store = rb_ary_new();
1523 rb_ary_push(*store, ID2NUM(id));
1524 }
1525 return ID_TABLE_CONTINUE;
1526}
1527
1528static const rb_env_t *
1529env_copy(const VALUE *src_ep, VALUE read_only_variables)
1530{
1531 const rb_env_t *src_env = (rb_env_t *)VM_ENV_ENVVAL(src_ep);
1532 VM_ASSERT(src_env->ep == src_ep);
1533
1534 VALUE *env_body = ZALLOC_N(VALUE, src_env->env_size); // fill with Qfalse
1535 VALUE *ep = &env_body[src_env->env_size - 2];
1536 const rb_env_t *copied_env = vm_env_new(ep, env_body, src_env->env_size, src_env->iseq);
1537
1538 // Copy after allocations above, since they can move objects in src_ep.
1539 VALUE svar_val = src_ep[VM_ENV_DATA_INDEX_ME_CREF];
1540 if (imemo_type_p(svar_val, imemo_svar)) {
1541 const struct vm_svar *svar = (struct vm_svar *)svar_val;
1542
1543 if (svar->cref_or_me) {
1544 svar_val = svar->cref_or_me;
1545 }
1546 else {
1547 svar_val = Qfalse;
1548 }
1549 }
1550 RB_OBJ_WRITE(copied_env, &ep[VM_ENV_DATA_INDEX_ME_CREF], svar_val);
1551
1552 ep[VM_ENV_DATA_INDEX_FLAGS] = src_ep[VM_ENV_DATA_INDEX_FLAGS] | VM_ENV_FLAG_ISOLATED;
1553 if (!VM_ENV_LOCAL_P(src_ep)) {
1554 VM_ENV_FLAGS_SET(ep, VM_ENV_FLAG_LOCAL);
1555 }
1556
1557 if (read_only_variables) {
1558 for (int i=RARRAY_LENINT(read_only_variables)-1; i>=0; i--) {
1559 ID id = NUM2ID(RARRAY_AREF(read_only_variables, i));
1560
1561 const struct rb_iseq_constant_body *body = ISEQ_BODY(src_env->iseq);
1562 for (unsigned int j=0; j<body->local_table_size; j++) {
1563 if (id == body->local_table[j]) {
1564 // check reassignment
1565 if (iseq_lvar_state_get(iseq_lvar_states(body), j) == lvar_reassigned) {
1566 VALUE name = rb_id2str(id);
1567 VALUE msg = rb_sprintf("cannot make a shareable Proc because "
1568 "the outer variable '%" PRIsVALUE "' may be reassigned.", name);
1569 rb_exc_raise(rb_exc_new_str(rb_eRactorIsolationError, msg));
1570 }
1571
1572 // check shareable
1573 VALUE v = src_env->env[j];
1574 if (!rb_ractor_shareable_p(v)) {
1575 VALUE name = rb_id2str(id);
1576 VALUE msg = rb_sprintf("cannot make a shareable Proc because it can refer"
1577 " unshareable object %+" PRIsVALUE " from ", v);
1578 if (name)
1579 rb_str_catf(msg, "variable '%" PRIsVALUE "'", name);
1580 else
1581 rb_str_cat_cstr(msg, "a hidden variable");
1582 rb_exc_raise(rb_exc_new_str(rb_eRactorIsolationError, msg));
1583 }
1584 RB_OBJ_WRITE((VALUE)copied_env, &env_body[j], v);
1585 rb_ary_delete_at(read_only_variables, i);
1586 break;
1587 }
1588 }
1589 }
1590 }
1591
1592 if (!VM_ENV_LOCAL_P(src_ep)) {
1593 const VALUE *prev_ep = VM_ENV_PREV_EP(src_env->ep);
1594 const rb_env_t *new_prev_env = env_copy(prev_ep, read_only_variables);
1595 ep[VM_ENV_DATA_INDEX_SPECVAL] = VM_GUARDED_PREV_EP(new_prev_env->ep);
1596 RB_OBJ_WRITTEN(copied_env, Qundef, new_prev_env);
1597 VM_ENV_FLAGS_UNSET(ep, VM_ENV_FLAG_LOCAL);
1598 }
1599 else if (VM_ENV_BOXED_P(src_ep)) {
1600 // A TOP/CLASS local env stores its box, not a block handler, in the
1601 // SPECVAL slot (VM_ENV_BOX). Preserve it: method lookup inside the
1602 // isolated proc reads the box back via rb_current_box(), and a
1603 // cleared slot dereferences a NULL box.
1604 ep[VM_ENV_DATA_INDEX_SPECVAL] = src_ep[VM_ENV_DATA_INDEX_SPECVAL];
1605 }
1606 else {
1607 ep[VM_ENV_DATA_INDEX_SPECVAL] = VM_BLOCK_HANDLER_NONE;
1608 }
1609
1610 RB_OBJ_SET_SHAREABLE((VALUE)copied_env);
1611 return copied_env;
1612}
1613
1614static void
1615proc_isolate_env(VALUE self, rb_proc_t *proc, VALUE read_only_variables)
1616{
1617 const struct rb_captured_block *captured = &proc->block.as.captured;
1618 const rb_env_t *env = env_copy(captured->ep, read_only_variables);
1619 *((const VALUE **)&proc->block.as.captured.ep) = env->ep;
1620 RB_OBJ_WRITTEN(self, Qundef, env);
1621}
1622
1623static VALUE
1624proc_shared_outer_variables(struct rb_id_table *outer_variables, const char *message)
1625{
1626 struct collect_outer_variable_name_data data = {
1627 .ary = Qfalse,
1628 .read_only = Qfalse,
1629 .yield = false,
1630 };
1631 rb_id_table_foreach(outer_variables, collect_outer_variable_names, (void *)&data);
1632
1633 if (data.ary != Qfalse) {
1634 VALUE str = rb_sprintf("can not %s because it accesses outer variables", message);
1635 VALUE ary = data.ary;
1636 const char *sep = " (";
1637 for (rb_len_t i = 0; i < RARRAY_LEN(ary); i++) {
1638 VALUE name = rb_id2str(NUM2ID(RARRAY_AREF(ary, i)));
1639 if (!name) continue;
1640 rb_str_cat_cstr(str, sep);
1641 sep = ", ";
1642 rb_str_append(str, name);
1643 }
1644 if (*sep == ',') rb_str_cat_cstr(str, ")");
1645 rb_str_cat_cstr(str, data.yield ? " and uses 'yield'." : ".");
1646 rb_exc_raise(rb_exc_new_str(rb_eRactorIsolationError, str));
1647 }
1648 else if (data.yield) {
1649 rb_raise(rb_eRactorIsolationError, "can not %s because it uses 'yield'.", message);
1650 }
1651
1652 return data.read_only;
1653}
1654
1655VALUE
1656rb_proc_ractor_make_shareable(VALUE self, VALUE replace_self)
1657{
1658 const rb_iseq_t *iseq = vm_proc_iseq(self);
1659
1660 if (iseq) {
1661 rb_proc_t *proc = (rb_proc_t *)RTYPEDDATA_DATA(self);
1662
1663 if (proc->block.type != block_type_iseq) rb_raise(rb_eRuntimeError, "not supported yet");
1664
1665 if (!UNDEF_P(replace_self)) {
1666 RB_OBJ_WRITE(self, &proc->block.as.captured.self, replace_self);
1667 }
1668
1669 if (!rb_ractor_shareable_p(vm_block_self(&proc->block))) {
1670 rb_raise(rb_eRactorIsolationError,
1671 "Proc's self is not shareable: %" PRIsVALUE,
1672 self);
1673 }
1674
1675 VALUE read_only_variables = Qfalse;
1676
1677 if (ISEQ_BODY(iseq)->outer_variables) {
1678 read_only_variables =
1679 proc_shared_outer_variables(ISEQ_BODY(iseq)->outer_variables, "make a Proc shareable");
1680 }
1681
1682 proc_isolate_env(self, proc, read_only_variables);
1683 proc->header.is_isolated = TRUE;
1684 }
1685 else {
1686 const struct rb_block *block = vm_proc_block(self);
1687 if (block->type != block_type_symbol) rb_raise(rb_eRuntimeError, "not supported yet");
1688
1689 VALUE proc_self = vm_block_self(block);
1690 if (!rb_ractor_shareable_p(proc_self)) {
1691 rb_raise(rb_eRactorIsolationError,
1692 "Proc's self is not shareable: %" PRIsVALUE,
1693 self);
1694 }
1695 }
1696
1698 return self;
1699}
1700
1701VALUE
1702rb_vm_make_proc_lambda(const rb_execution_context_t *ec, const struct rb_captured_block *captured, VALUE klass, int8_t is_lambda)
1703{
1704 VALUE procval;
1705 enum imemo_type code_type = imemo_type(captured->code.val);
1706
1707 if (!VM_ENV_ESCAPED_P(captured->ep)) {
1708 rb_control_frame_t *cfp = VM_CAPTURED_BLOCK_TO_CFP(captured);
1709 vm_make_env_object(ec, cfp);
1710 }
1711
1712 VM_ASSERT(VM_EP_IN_HEAP_P(ec, captured->ep));
1713 VM_ASSERT(code_type == imemo_iseq || code_type == imemo_ifunc);
1714
1715 procval = vm_proc_create_from_captured(klass, captured,
1716 code_type == imemo_iseq ? block_type_iseq : block_type_ifunc,
1717 FALSE, is_lambda);
1718
1719 if (code_type == imemo_ifunc) {
1720 struct vm_ifunc *ifunc = (struct vm_ifunc *)captured->code.val;
1721 if (ifunc->svar_lep) {
1722 VALUE ep0 = ifunc->svar_lep[0];
1723 if (RB_TYPE_P(ep0, T_IMEMO) && imemo_type_p(ep0, imemo_env)) {
1724 // `ep0 == imemo_env` means this ep is escaped to heap (in env object).
1725 const rb_env_t *env = (const rb_env_t *)ep0;
1726 ifunc->svar_lep = (VALUE *)env->ep;
1727 }
1728 else {
1729 VM_ASSERT(FIXNUM_P(ep0));
1730 if (ep0 & VM_ENV_FLAG_ESCAPED) {
1731 // ok. do nothing
1732 }
1733 else {
1734 ifunc->svar_lep = NULL;
1735 }
1736 }
1737 }
1738 }
1739
1740 return procval;
1741}
1742
1743/* Binding */
1744
1745VALUE
1746rb_vm_make_binding(const rb_execution_context_t *ec, const rb_control_frame_t *src_cfp)
1747{
1748 rb_control_frame_t *cfp = rb_vm_get_binding_creatable_next_cfp(ec, src_cfp);
1749 rb_control_frame_t *ruby_level_cfp = rb_vm_get_ruby_level_next_cfp(ec, src_cfp);
1750 VALUE bindval, envval;
1751 rb_binding_t *bind;
1752
1753 if (cfp == 0 || ruby_level_cfp == 0) {
1754 rb_raise(rb_eRuntimeError, "Can't create Binding Object on top of Fiber.");
1755 }
1756 if (!VM_FRAME_RUBYFRAME_P(src_cfp) &&
1757 !VM_FRAME_RUBYFRAME_P(RUBY_VM_PREVIOUS_CONTROL_FRAME(src_cfp))) {
1758 rb_raise(rb_eRuntimeError, "Cannot create Binding object for non-Ruby caller");
1759 }
1760
1761 envval = vm_make_env_object(ec, cfp);
1762 bindval = rb_binding_alloc(rb_cBinding);
1763 GetBindingPtr(bindval, bind);
1764 vm_bind_update_env(bindval, bind, envval);
1765 RB_OBJ_WRITE(bindval, &bind->block.as.captured.self, cfp->self);
1766 RB_OBJ_WRITE(bindval, &bind->block.as.captured.code.iseq, CFP_ISEQ(cfp));
1767 RB_OBJ_WRITE(bindval, &bind->pathobj, ISEQ_BODY(CFP_ISEQ(ruby_level_cfp))->location.pathobj);
1768 bind->no_realpath = ISEQ_BODY(CFP_ISEQ(ruby_level_cfp))->no_realpath;
1769 bind->first_lineno = rb_vm_get_sourceline(ruby_level_cfp);
1770
1771 return bindval;
1772}
1773
1774const VALUE *
1775rb_binding_add_dynavars(VALUE bindval, rb_binding_t *bind, int dyncount, const ID *dynvars)
1776{
1777 VALUE envval, pathobj = bind->pathobj;
1778 VALUE path = pathobj_path(pathobj);
1779 VALUE realpath = pathobj_realpath(pathobj, bind->no_realpath);
1780 const struct rb_block *base_block;
1781 const rb_env_t *env;
1782 rb_execution_context_t *ec = GET_EC();
1783 const rb_iseq_t *base_iseq, *iseq;
1784 rb_node_scope_t tmp_node;
1785
1786 if (dyncount < 0) return 0;
1787
1788 base_block = &bind->block;
1789 base_iseq = vm_block_iseq(base_block);
1790
1791 VALUE idtmp = 0;
1792 rb_ast_id_table_t *dyns = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + dyncount * sizeof(ID));
1793 dyns->size = dyncount;
1794 MEMCPY(dyns->ids, dynvars, ID, dyncount);
1795
1796 rb_node_init(RNODE(&tmp_node), NODE_SCOPE);
1797 tmp_node.nd_tbl = dyns;
1798 tmp_node.nd_body = 0;
1799 tmp_node.nd_parent = NULL;
1800 tmp_node.nd_args = 0;
1801
1802 VALUE ast_value = rb_ruby_ast_new(RNODE(&tmp_node));
1803
1804 if (base_iseq) {
1805 iseq = rb_iseq_new(ast_value, ISEQ_BODY(base_iseq)->location.label, path, realpath, base_iseq, ISEQ_TYPE_EVAL);
1806 }
1807 else {
1808 VALUE tempstr = rb_fstring_lit("<temp>");
1809 iseq = rb_iseq_new_top(ast_value, tempstr, tempstr, tempstr, NULL);
1810 }
1811 tmp_node.nd_tbl = 0; /* reset table */
1812 ALLOCV_END(idtmp);
1813
1814 vm_set_eval_stack(ec, iseq, 0, base_block);
1815 vm_bind_update_env(bindval, bind, envval = vm_make_env_object(ec, ec->cfp));
1816 rb_vm_pop_frame(ec);
1817
1818 env = (const rb_env_t *)envval;
1819 return env->env;
1820}
1821
1822/* C -> Ruby: block */
1823
1824static inline void
1825invoke_block(rb_execution_context_t *ec, const rb_iseq_t *iseq, VALUE self, const struct rb_captured_block *captured, const rb_cref_t *cref, VALUE type, int opt_pc)
1826{
1827 int arg_size = ISEQ_BODY(iseq)->param.size;
1828
1829 vm_push_frame(ec, iseq, type | VM_FRAME_FLAG_FINISH, self,
1830 VM_GUARDED_PREV_EP(captured->ep),
1831 (VALUE)cref, /* cref or method */
1832 ISEQ_BODY(iseq)->iseq_encoded + opt_pc,
1833 ec->cfp->sp + arg_size,
1834 ISEQ_BODY(iseq)->local_table_size - arg_size,
1835 ISEQ_BODY(iseq)->stack_max);
1836}
1837
1838static inline void
1839invoke_bmethod(rb_execution_context_t *ec, const rb_iseq_t *iseq, VALUE self, const struct rb_captured_block *captured, const rb_callable_method_entry_t *me, VALUE type, int opt_pc)
1840{
1841 /* bmethod call from outside the VM */
1842 int arg_size = ISEQ_BODY(iseq)->param.size;
1843
1844 VM_ASSERT(me->def->type == VM_METHOD_TYPE_BMETHOD);
1845
1846 vm_push_frame(ec, iseq, type | VM_FRAME_FLAG_BMETHOD, self,
1847 VM_GUARDED_PREV_EP(captured->ep),
1848 (VALUE)me,
1849 ISEQ_BODY(iseq)->iseq_encoded + opt_pc,
1850 ec->cfp->sp + 1 /* self */ + arg_size,
1851 ISEQ_BODY(iseq)->local_table_size - arg_size,
1852 ISEQ_BODY(iseq)->stack_max);
1853
1854 VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);
1855}
1856
1857ALWAYS_INLINE(static VALUE
1858 invoke_iseq_block_from_c(rb_execution_context_t *ec, const struct rb_captured_block *captured,
1859 VALUE self, int argc, const VALUE *argv, int kw_splat, VALUE passed_block_handler,
1860 const rb_cref_t *cref, int is_lambda, const rb_callable_method_entry_t *me));
1861
1862static inline VALUE
1863invoke_iseq_block_from_c(rb_execution_context_t *ec, const struct rb_captured_block *captured,
1864 VALUE self, int argc, const VALUE *argv, int kw_splat, VALUE passed_block_handler,
1865 const rb_cref_t *cref, int is_lambda, const rb_callable_method_entry_t *me)
1866{
1867 const rb_iseq_t *iseq = rb_iseq_check(captured->code.iseq);
1868 int opt_pc;
1869 VALUE type = VM_FRAME_MAGIC_BLOCK | (is_lambda ? VM_FRAME_FLAG_LAMBDA : 0);
1870 rb_control_frame_t *cfp = ec->cfp;
1871 VALUE *sp = cfp->sp;
1872 int flags = (kw_splat ? VM_CALL_KW_SPLAT : 0);
1873 VALUE *use_argv = (VALUE *)argv;
1874 VALUE av[2];
1875
1876 stack_check(ec);
1877
1878 if (UNLIKELY(argc > VM_ARGC_STACK_MAX) &&
1879 (VM_ARGC_STACK_MAX >= 1 ||
1880 /* Skip ruby array for potential autosplat case */
1881 (argc != 1 || is_lambda))) {
1882 use_argv = vm_argv_ruby_array(av, argv, &flags, &argc, kw_splat);
1883 }
1884
1885 CHECK_VM_STACK_OVERFLOW(cfp, argc + 1);
1886 vm_check_canary(ec, sp);
1887
1888 VALUE *stack_argv = sp;
1889 if (me) {
1890 *sp = self; // bemthods need `self` on the VM stack
1891 stack_argv++;
1892 }
1893 cfp->sp = stack_argv + argc;
1894 MEMCPY(stack_argv, use_argv, VALUE, argc); // restrict: new stack space
1895
1896 opt_pc = vm_yield_setup_args(ec, iseq, argc, stack_argv, flags, passed_block_handler,
1897 (is_lambda ? arg_setup_method : arg_setup_block));
1898 cfp->sp = sp;
1899
1900 if (me == NULL) {
1901 invoke_block(ec, iseq, self, captured, cref, type, opt_pc);
1902 }
1903 else {
1904 invoke_bmethod(ec, iseq, self, captured, me, type, opt_pc);
1905 }
1906
1907 return vm_exec(ec);
1908}
1909
1910static VALUE
1911invoke_block_from_c_bh(rb_execution_context_t *ec, VALUE block_handler,
1912 int argc, const VALUE *argv,
1913 int kw_splat, VALUE passed_block_handler, const rb_cref_t *cref,
1914 int is_lambda, int force_blockarg)
1915{
1916 again:
1917 switch (vm_block_handler_type(block_handler)) {
1918 case block_handler_type_iseq:
1919 {
1920 const struct rb_captured_block *captured = VM_BH_TO_ISEQ_BLOCK(block_handler);
1921 return invoke_iseq_block_from_c(ec, captured, captured->self,
1922 argc, argv, kw_splat, passed_block_handler,
1923 cref, is_lambda, NULL);
1924 }
1925 case block_handler_type_ifunc:
1926 return vm_yield_with_cfunc(ec, VM_BH_TO_IFUNC_BLOCK(block_handler),
1927 VM_BH_TO_IFUNC_BLOCK(block_handler)->self,
1928 argc, argv, kw_splat, passed_block_handler, NULL);
1929 case block_handler_type_symbol:
1930 return vm_yield_with_symbol(ec, VM_BH_TO_SYMBOL(block_handler),
1931 argc, argv, kw_splat, passed_block_handler);
1932 case block_handler_type_proc:
1933 {
1934 VALUE procval = VM_BH_TO_PROC(block_handler);
1935 rb_proc_t *po;
1936 GetProcPtr(procval, po);
1937 if (po->header.is_refined) cref = rb_proc_refinements_cref_for_call(procval);
1938 if (force_blockarg == FALSE) {
1939 is_lambda = po->header.is_lambda;
1940 }
1941 block_handler = vm_block_to_block_handler(&po->block);
1942 goto again;
1943 }
1944 }
1945 VM_UNREACHABLE(invoke_block_from_c_splattable);
1946 return Qundef;
1947}
1948
1949static inline VALUE
1950check_block_handler(rb_execution_context_t *ec)
1951{
1952 VALUE block_handler = VM_CF_BLOCK_HANDLER(ec->cfp);
1953 vm_block_handler_verify(block_handler);
1954 if (UNLIKELY(block_handler == VM_BLOCK_HANDLER_NONE)) {
1955 rb_vm_localjump_error("no block given", Qnil, 0);
1956 }
1957
1958 return block_handler;
1959}
1960
1961static VALUE
1962vm_yield_with_cref(rb_execution_context_t *ec, int argc, const VALUE *argv, int kw_splat, const rb_cref_t *cref, int is_lambda)
1963{
1964 return invoke_block_from_c_bh(ec, check_block_handler(ec),
1965 argc, argv, kw_splat, VM_BLOCK_HANDLER_NONE,
1966 cref, is_lambda, FALSE);
1967}
1968
1969static VALUE
1970vm_yield(rb_execution_context_t *ec, int argc, const VALUE *argv, int kw_splat)
1971{
1972 return vm_yield_with_cref(ec, argc, argv, kw_splat, NULL, FALSE);
1973}
1974
1975static VALUE
1976vm_yield_with_block(rb_execution_context_t *ec, int argc, const VALUE *argv, VALUE block_handler, int kw_splat)
1977{
1978 return invoke_block_from_c_bh(ec, check_block_handler(ec),
1979 argc, argv, kw_splat, block_handler,
1980 NULL, FALSE, FALSE);
1981}
1982
1983static VALUE
1984vm_yield_force_blockarg(rb_execution_context_t *ec, VALUE args)
1985{
1986 return invoke_block_from_c_bh(ec, check_block_handler(ec), 1, &args,
1987 RB_NO_KEYWORDS, VM_BLOCK_HANDLER_NONE, NULL, FALSE, TRUE);
1988}
1989
1990ALWAYS_INLINE(static VALUE
1991 invoke_block_from_c_proc(rb_execution_context_t *ec, const rb_proc_t *proc,
1992 VALUE self, int argc, const VALUE *argv,
1993 int kw_splat, VALUE passed_block_handler, int is_lambda,
1994 const rb_cref_t *cref,
1995 const rb_callable_method_entry_t *me));
1996
1997static inline VALUE
1998invoke_block_from_c_proc(rb_execution_context_t *ec, const rb_proc_t *proc,
1999 VALUE self, int argc, const VALUE *argv,
2000 int kw_splat, VALUE passed_block_handler, int is_lambda,
2001 const rb_cref_t *cref,
2003{
2004 const struct rb_block *block = &proc->block;
2005
2006 again:
2007 switch (vm_block_type(block)) {
2008 case block_type_iseq:
2009 return invoke_iseq_block_from_c(ec, &block->as.captured, self, argc, argv, kw_splat, passed_block_handler, cref, is_lambda, me);
2010 case block_type_ifunc:
2011 if (kw_splat == 1) {
2012 VALUE keyword_hash = argv[argc-1];
2013 if (!RB_TYPE_P(keyword_hash, T_HASH)) {
2014 keyword_hash = rb_to_hash_type(keyword_hash);
2015 }
2016 if (RHASH_EMPTY_P(keyword_hash)) {
2017 argc--;
2018 kw_splat = 0;
2019 }
2020 else {
2021 ((VALUE *)argv)[argc-1] = rb_hash_dup(keyword_hash);
2022 }
2023 }
2024 return vm_yield_with_cfunc(ec, &block->as.captured, self, argc, argv, kw_splat, passed_block_handler, me);
2025 case block_type_symbol:
2026 return vm_yield_with_symbol(ec, block->as.symbol, argc, argv, kw_splat, passed_block_handler);
2027 case block_type_proc:
2028 is_lambda = block_proc_is_lambda(block->as.proc);
2029 block = vm_proc_block(block->as.proc);
2030 goto again;
2031 }
2032 VM_UNREACHABLE(invoke_block_from_c_proc);
2033 return Qundef;
2034}
2035
2036static VALUE
2037vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
2038 int argc, const VALUE *argv, int kw_splat, VALUE passed_block_handler,
2039 const rb_cref_t *cref)
2040{
2041 return invoke_block_from_c_proc(ec, proc, self, argc, argv, kw_splat, passed_block_handler, proc->header.is_lambda, cref, NULL);
2042}
2043
2044static VALUE
2045vm_invoke_bmethod(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
2046 int argc, const VALUE *argv, int kw_splat, VALUE block_handler, const rb_callable_method_entry_t *me)
2047{
2048 /* bmethod procs never carry a refinement cref (Proc#refined rejects them) */
2049 return invoke_block_from_c_proc(ec, proc, self, argc, argv, kw_splat, block_handler, TRUE, NULL, me);
2050}
2051
2052VALUE
2053rb_vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc,
2054 int argc, const VALUE *argv, int kw_splat, VALUE passed_block_handler,
2055 const rb_cref_t *cref)
2056{
2057 VALUE self = vm_block_self(&proc->block);
2058 vm_block_handler_verify(passed_block_handler);
2059
2060 if (proc->header.is_from_method) {
2061 return vm_invoke_bmethod(ec, proc, self, argc, argv, kw_splat, passed_block_handler, NULL);
2062 }
2063 else {
2064 return vm_invoke_proc(ec, proc, self, argc, argv, kw_splat, passed_block_handler, cref);
2065 }
2066}
2067
2068VALUE
2069rb_vm_invoke_proc_with_self(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
2070 int argc, const VALUE *argv, int kw_splat, VALUE passed_block_handler,
2071 const rb_cref_t *cref)
2072{
2073 vm_block_handler_verify(passed_block_handler);
2074
2075 if (proc->header.is_from_method) {
2076 return vm_invoke_bmethod(ec, proc, self, argc, argv, kw_splat, passed_block_handler, NULL);
2077 }
2078 else {
2079 return vm_invoke_proc(ec, proc, self, argc, argv, kw_splat, passed_block_handler, cref);
2080 }
2081}
2082
2083/* special variable */
2084
2085VALUE *
2086rb_vm_svar_lep(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
2087{
2088 while (!CFP_PC(cfp) || !CFP_ISEQ(cfp)) {
2089 if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_IFUNC) {
2090 struct vm_ifunc *ifunc = (struct vm_ifunc *)CFP_ISEQ(cfp);
2091 return ifunc->svar_lep;
2092 }
2093 else {
2094 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
2095 }
2096
2097 if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
2098 return NULL;
2099 }
2100 }
2101
2102 return (VALUE *)VM_CF_LEP(cfp);
2103}
2104
2105static VALUE
2106vm_cfp_svar_get(const rb_execution_context_t *ec, rb_control_frame_t *cfp, VALUE key)
2107{
2108 return lep_svar_get(ec, rb_vm_svar_lep(ec, cfp), key);
2109}
2110
2111static void
2112vm_cfp_svar_set(const rb_execution_context_t *ec, rb_control_frame_t *cfp, VALUE key, const VALUE val)
2113{
2114 lep_svar_set(ec, rb_vm_svar_lep(ec, cfp), key, val);
2115}
2116
2117static VALUE
2118vm_svar_get(const rb_execution_context_t *ec, VALUE key)
2119{
2120 return vm_cfp_svar_get(ec, ec->cfp, key);
2121}
2122
2123static void
2124vm_svar_set(const rb_execution_context_t *ec, VALUE key, VALUE val)
2125{
2126 vm_cfp_svar_set(ec, ec->cfp, key, val);
2127}
2128
2129VALUE
2131{
2132 return vm_svar_get(GET_EC(), VM_SVAR_BACKREF);
2133}
2134
2135void
2137{
2138 vm_svar_set(GET_EC(), VM_SVAR_BACKREF, val);
2139}
2140
2141VALUE
2143{
2144 return vm_svar_get(GET_EC(), VM_SVAR_LASTLINE);
2145}
2146
2147void
2149{
2150 vm_svar_set(GET_EC(), VM_SVAR_LASTLINE, val);
2151}
2152
2153void
2154rb_lastline_set_up(VALUE val, unsigned int up)
2155{
2156 rb_control_frame_t * cfp = GET_EC()->cfp;
2157
2158 for(unsigned int i = 0; i < up; i++) {
2159 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
2160 }
2161 vm_cfp_svar_set(GET_EC(), cfp, VM_SVAR_LASTLINE, val);
2162}
2163
2164/* misc */
2165
2166const char *
2168{
2169 const rb_execution_context_t *ec = GET_EC();
2170 const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
2171
2172 if (cfp) {
2173 return RSTRING_PTR(rb_iseq_path(CFP_ISEQ(cfp)));
2174 }
2175 else {
2176 return 0;
2177 }
2178}
2179
2180int
2182{
2183 const rb_execution_context_t *ec = GET_EC();
2184 const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
2185
2186 if (cfp) {
2187 return rb_vm_get_sourceline(cfp);
2188 }
2189 else {
2190 return 0;
2191 }
2192}
2193
2194VALUE
2195rb_source_location(int *pline)
2196{
2197 const rb_execution_context_t *ec = GET_EC();
2198 const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
2199
2200 if (cfp && VM_FRAME_RUBYFRAME_P(cfp)) {
2201 if (pline) *pline = rb_vm_get_sourceline(cfp);
2202 return rb_iseq_path(CFP_ISEQ(cfp));
2203 }
2204 else {
2205 if (pline) *pline = 0;
2206 return Qnil;
2207 }
2208}
2209
2210const char *
2211rb_source_location_cstr(int *pline)
2212{
2213 VALUE path = rb_source_location(pline);
2214 if (NIL_P(path)) return NULL;
2215 return RSTRING_PTR(path);
2216}
2217
2218rb_cref_t *
2219rb_vm_cref(void)
2220{
2221 const rb_execution_context_t *ec = GET_EC();
2222 return vm_ec_cref(ec);
2223}
2224
2225rb_cref_t *
2226rb_vm_cref_replace_with_duplicated_cref(void)
2227{
2228 const rb_execution_context_t *ec = GET_EC();
2229 const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
2230 rb_cref_t *cref = vm_cref_replace_with_duplicated_cref(cfp->ep);
2231 ASSUME(cref);
2232 return cref;
2233}
2234
2235const rb_cref_t *
2236rb_vm_cref_in_context(VALUE self, VALUE cbase)
2237{
2238 const rb_execution_context_t *ec = GET_EC();
2239 const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
2240 const rb_cref_t *cref;
2241 if (!cfp || cfp->self != self) return NULL;
2242 if (!vm_env_cref_by_cref(cfp->ep)) return NULL;
2243 cref = vm_get_cref(cfp->ep);
2244 if (CREF_CLASS(cref) != cbase) return NULL;
2245 return cref;
2246}
2247
2248#if 0
2249void
2250debug_cref(rb_cref_t *cref)
2251{
2252 while (cref) {
2253 dp(CREF_CLASS(cref));
2254 printf("%ld\n", CREF_VISI(cref));
2255 cref = CREF_NEXT(cref);
2256 }
2257}
2258#endif
2259
2260VALUE
2261rb_vm_cbase(void)
2262{
2263 const rb_execution_context_t *ec = GET_EC();
2264 const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
2265
2266 if (cfp == 0) {
2267 rb_raise(rb_eRuntimeError, "Can't call on top of Fiber or Thread");
2268 }
2269 return vm_get_cbase(cfp->ep);
2270}
2271
2272/* jump */
2273
2274static VALUE
2275make_localjump_error(const char *mesg, VALUE value, int reason)
2276{
2279 ID id;
2280
2281 switch (reason) {
2282 case TAG_BREAK:
2283 CONST_ID(id, "break");
2284 break;
2285 case TAG_REDO:
2286 CONST_ID(id, "redo");
2287 break;
2288 case TAG_RETRY:
2289 CONST_ID(id, "retry");
2290 break;
2291 case TAG_NEXT:
2292 CONST_ID(id, "next");
2293 break;
2294 case TAG_RETURN:
2295 CONST_ID(id, "return");
2296 break;
2297 default:
2298 CONST_ID(id, "noreason");
2299 break;
2300 }
2301 rb_iv_set(exc, "@exit_value", value);
2302 rb_iv_set(exc, "@reason", ID2SYM(id));
2303 return exc;
2304}
2305
2306void
2307rb_vm_localjump_error(const char *mesg, VALUE value, int reason)
2308{
2309 VALUE exc = make_localjump_error(mesg, value, reason);
2310 rb_exc_raise(exc);
2311}
2312
2313VALUE
2314rb_vm_make_jump_tag_but_local_jump(enum ruby_tag_type state, VALUE val)
2315{
2316 const char *mesg;
2317
2318 switch (state) {
2319 case TAG_RETURN:
2320 mesg = "unexpected return";
2321 break;
2322 case TAG_BREAK:
2323 mesg = "unexpected break";
2324 break;
2325 case TAG_NEXT:
2326 mesg = "unexpected next";
2327 break;
2328 case TAG_REDO:
2329 mesg = "unexpected redo";
2330 val = Qnil;
2331 break;
2332 case TAG_RETRY:
2333 mesg = "retry outside of rescue clause";
2334 val = Qnil;
2335 break;
2336 default:
2337 return Qnil;
2338 }
2339 if (UNDEF_P(val)) {
2340 val = GET_EC()->tag->retval;
2341 }
2342 return make_localjump_error(mesg, val, state);
2343}
2344
2345void
2346rb_vm_jump_tag_but_local_jump(enum ruby_tag_type state)
2347{
2348 VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
2349 if (!NIL_P(exc)) rb_exc_raise(exc);
2350 EC_JUMP_TAG(GET_EC(), state);
2351}
2352
2353static rb_control_frame_t *
2354next_not_local_frame(rb_control_frame_t *cfp)
2355{
2356 while (VM_ENV_LOCAL_P(cfp->ep)) {
2357 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
2358 }
2359 return cfp;
2360}
2361
2362NORETURN(static void vm_iter_break(rb_execution_context_t *ec, VALUE val));
2363
2364static void
2365vm_iter_break(rb_execution_context_t *ec, VALUE val)
2366{
2367 rb_control_frame_t *cfp = next_not_local_frame(ec->cfp);
2368 const VALUE *ep = VM_CF_PREV_EP(cfp);
2369 const rb_control_frame_t *target_cfp = rb_vm_search_cf_from_ep(ec, cfp, ep);
2370
2371 if (!target_cfp) {
2372 rb_vm_localjump_error("unexpected break", val, TAG_BREAK);
2373 }
2374
2375 ec->errinfo = (VALUE)THROW_DATA_NEW(val, target_cfp, TAG_BREAK);
2376 EC_JUMP_TAG(ec, TAG_BREAK);
2377}
2378
2379void
2381{
2382 vm_iter_break(GET_EC(), Qnil);
2383}
2384
2385void
2387{
2388 vm_iter_break(GET_EC(), val);
2389}
2390
2391/* optimization: redefine management */
2392
2393short ruby_vm_redefined_flag[BOP_LAST_];
2394static st_table *vm_opt_method_def_table = 0;
2395static st_table *vm_opt_mid_table = 0;
2396
2397static void
2398rb_free_vm_opt_tables(void)
2399{
2400 st_free_table(vm_opt_method_def_table);
2401 st_free_table(vm_opt_mid_table);
2402}
2403
2404static int
2405vm_redefinition_check_flag(VALUE klass)
2406{
2407 if (klass == rb_cInteger) return INTEGER_REDEFINED_OP_FLAG;
2408 if (klass == rb_cFloat) return FLOAT_REDEFINED_OP_FLAG;
2409 if (klass == rb_cString) return STRING_REDEFINED_OP_FLAG;
2410 if (klass == rb_cArray) return ARRAY_REDEFINED_OP_FLAG;
2411 if (klass == rb_cHash) return HASH_REDEFINED_OP_FLAG;
2412 if (klass == rb_cSymbol) return SYMBOL_REDEFINED_OP_FLAG;
2413#if 0
2414 if (klass == rb_cTime) return TIME_REDEFINED_OP_FLAG;
2415#endif
2416 if (klass == rb_cRegexp) return REGEXP_REDEFINED_OP_FLAG;
2417 if (klass == rb_cNilClass) return NIL_REDEFINED_OP_FLAG;
2418 if (klass == rb_cTrueClass) return TRUE_REDEFINED_OP_FLAG;
2419 if (klass == rb_cFalseClass) return FALSE_REDEFINED_OP_FLAG;
2420 if (klass == rb_cProc) return PROC_REDEFINED_OP_FLAG;
2421 return 0;
2422}
2423
2424int
2425rb_vm_check_optimizable_mid(VALUE mid)
2426{
2427 if (!vm_opt_mid_table) {
2428 return FALSE;
2429 }
2430
2431 return st_lookup(vm_opt_mid_table, mid, NULL);
2432}
2433
2434static int
2435vm_redefinition_check_method_type(const rb_method_entry_t *me)
2436{
2437 if (me->called_id != me->def->original_id) {
2438 return FALSE;
2439 }
2440
2441 if (METHOD_ENTRY_BASIC(me)) return TRUE;
2442
2443 const rb_method_definition_t *def = me->def;
2444 switch (def->type) {
2445 case VM_METHOD_TYPE_CFUNC:
2446 case VM_METHOD_TYPE_OPTIMIZED:
2447 return TRUE;
2448 default:
2449 return FALSE;
2450 }
2451}
2452
2453static void
2454vm_warn_redefinition_opt_method(const rb_method_entry_t *me)
2455{
2457 "Redefining '%s#%s' disables interpreter and JIT optimizations",
2458 rb_class2name(me->owner),
2459 rb_id2name(me->called_id));
2460}
2461
2462/* When perf_warnings is non-NULL the caller holds the VM lock, where neither
2463 * warning nor formatting the message is allowed, so the method entry is
2464 * appended there for it to warn about after releasing the lock.
2465 * See method_entry_warnings_emit(). */
2466static void
2467rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me, VALUE klass, VALUE *perf_warnings)
2468{
2469 st_data_t bop;
2470 if (RB_TYPE_P(klass, T_ICLASS) && RICLASS_IS_ORIGIN_P(klass) &&
2471 RB_TYPE_P(RBASIC_CLASS(klass), T_CLASS)) {
2472 klass = RBASIC_CLASS(klass);
2473 }
2474
2475 if (vm_redefinition_check_method_type(me)) {
2476 if (st_lookup(vm_opt_method_def_table, (st_data_t)me->def, &bop)) {
2477 int flag = vm_redefinition_check_flag(klass);
2478 if (flag != 0) {
2479 if (!NIL_P(ruby_verbose) && rb_warning_category_enabled_p(RB_WARN_CATEGORY_PERFORMANCE)) {
2480 if (perf_warnings) {
2481 if (!*perf_warnings) *perf_warnings = rb_ary_hidden_new(1);
2482 rb_ary_push(*perf_warnings, (VALUE)me);
2483 }
2484 else {
2485 vm_warn_redefinition_opt_method(me);
2486 }
2487 }
2488 rb_yjit_bop_redefined(flag, (enum ruby_basic_operators)bop);
2489 rb_zjit_bop_redefined(flag, (enum ruby_basic_operators)bop);
2490 ruby_vm_redefined_flag[bop] |= (flag | ANY_REDEFINED_OP_FLAG);
2491 }
2492 }
2493 }
2494}
2495
2496static enum rb_id_table_iterator_result
2497check_redefined_method(ID mid, VALUE value, void *data)
2498{
2499 VALUE klass = (VALUE)data;
2500 const rb_method_entry_t *me = (rb_method_entry_t *)value;
2501 const rb_method_entry_t *newme = rb_method_entry(klass, mid);
2502
2503 if (newme != me) rb_vm_check_redefinition_opt_method(me, me->owner, NULL);
2504
2505 return ID_TABLE_CONTINUE;
2506}
2507
2508void
2509rb_vm_check_redefinition_by_prepend(VALUE klass)
2510{
2511 if (!vm_redefinition_check_flag(klass)) return;
2512 rb_id_table_foreach(RCLASS_M_TBL(RCLASS_ORIGIN(klass)), check_redefined_method, (void *)klass);
2513}
2514
2515static void
2516add_opt_method_entry_bop(const rb_method_entry_t *me, ID mid, enum ruby_basic_operators bop)
2517{
2518 st_insert(vm_opt_method_def_table, (st_data_t)me->def, (st_data_t)bop);
2519 st_insert(vm_opt_mid_table, (st_data_t)mid, (st_data_t)Qtrue);
2520}
2521
2522static void
2523add_opt_method(VALUE klass, ID mid, enum ruby_basic_operators bop)
2524{
2525 const rb_method_entry_t *me = rb_method_entry_at(klass, mid);
2526
2527 if (me && vm_redefinition_check_method_type(me)) {
2528 add_opt_method_entry_bop(me, mid, bop);
2529 }
2530 else {
2531 rb_bug("undefined optimized method: %s", rb_id2name(mid));
2532 }
2533}
2534
2535static enum ruby_basic_operators vm_redefinition_bop_for_id(ID mid);
2536
2537static void
2538add_opt_method_entry(const rb_method_entry_t *me)
2539{
2540 if (me && vm_redefinition_check_method_type(me)) {
2541 ID mid = me->called_id;
2542 enum ruby_basic_operators bop = vm_redefinition_bop_for_id(mid);
2543 if ((int)bop >= 0) {
2544 add_opt_method_entry_bop(me, mid, bop);
2545 }
2546 }
2547}
2548
2549void
2550rb_vm_init_redefined_flag(void)
2551{
2552 ID mid;
2553 enum ruby_basic_operators bop;
2554
2555#define OP(mid_, bop_) (mid = id##mid_, bop = BOP_##bop_, ruby_vm_redefined_flag[bop] = 0)
2556#define C(k) add_opt_method(rb_c##k, mid, bop)
2557 OP(PLUS, PLUS), (C(Integer), C(Float), C(String), C(Array));
2558 OP(MINUS, MINUS), (C(Integer), C(Float));
2559 OP(MULT, MULT), (C(Integer), C(Float));
2560 OP(DIV, DIV), (C(Integer), C(Float));
2561 OP(MOD, MOD), (C(Integer), C(Float));
2562 OP(Eq, EQ), (C(Integer), C(Float), C(String), C(Symbol), C(NilClass), C(FalseClass), C(TrueClass));
2563 OP(Eqq, EQQ), (C(Integer), C(Float), C(Symbol), C(String),
2564 C(NilClass), C(TrueClass), C(FalseClass));
2565 OP(LT, LT), (C(Integer), C(Float));
2566 OP(LE, LE), (C(Integer), C(Float));
2567 OP(GT, GT), (C(Integer), C(Float));
2568 OP(GE, GE), (C(Integer), C(Float));
2569 OP(LTLT, LTLT), (C(String), C(Array));
2570 OP(GTGT, GTGT), (C(Integer));
2571 OP(AREF, AREF), (C(Array), C(Hash), C(Integer));
2572 OP(ASET, ASET), (C(Array), C(Hash));
2573 OP(Length, LENGTH), (C(Array), C(String), C(Hash));
2574 OP(Size, SIZE), (C(Array), C(String), C(Hash));
2575 OP(EmptyP, EMPTY_P), (C(Array), C(String), C(Hash));
2576 OP(Succ, SUCC), (C(Integer), C(String));
2577 OP(EqTilde, MATCH), (C(Regexp), C(String));
2578 OP(Freeze, FREEZE), (C(String), C(Array), C(Hash));
2579 OP(UMinus, UMINUS), (C(String));
2580 OP(Max, MAX), (C(Array));
2581 OP(Min, MIN), (C(Array));
2582 OP(Hash, HASH), (C(Array));
2583 OP(Call, CALL), (C(Proc));
2584 OP(Yield, YIELD), (C(Proc));
2585 OP(And, AND), (C(Integer));
2586 OP(Or, OR), (C(Integer));
2587 OP(NilP, NIL_P), (C(NilClass));
2588 OP(Cmp, CMP), (C(Integer), C(Float), C(String));
2589 OP(Default, DEFAULT), (C(Hash));
2590 OP(IncludeP, INCLUDE_P), (C(Array));
2591#undef C
2592#undef OP
2593}
2594
2595static enum ruby_basic_operators
2596vm_redefinition_bop_for_id(ID mid)
2597{
2598 switch (mid) {
2599#define OP(mid_, bop_) case id##mid_: return BOP_##bop_
2600 OP(PLUS, PLUS);
2601 OP(MINUS, MINUS);
2602 OP(MULT, MULT);
2603 OP(DIV, DIV);
2604 OP(MOD, MOD);
2605 OP(Eq, EQ);
2606 OP(Eqq, EQQ);
2607 OP(LT, LT);
2608 OP(LE, LE);
2609 OP(GT, GT);
2610 OP(GE, GE);
2611 OP(LTLT, LTLT);
2612 OP(AREF, AREF);
2613 OP(ASET, ASET);
2614 OP(Length, LENGTH);
2615 OP(Size, SIZE);
2616 OP(EmptyP, EMPTY_P);
2617 OP(Succ, SUCC);
2618 OP(EqTilde, MATCH);
2619 OP(Freeze, FREEZE);
2620 OP(UMinus, UMINUS);
2621 OP(Max, MAX);
2622 OP(Min, MIN);
2623 OP(Hash, HASH);
2624 OP(Call, CALL);
2625 OP(Yield, YIELD);
2626 OP(And, AND);
2627 OP(Or, OR);
2628 OP(NilP, NIL_P);
2629 OP(Cmp, CMP);
2630 OP(Default, DEFAULT);
2631 OP(Pack, PACK);
2632#undef OP
2633 }
2634 return -1;
2635}
2636
2637/* for vm development */
2638
2639#if VMDEBUG
2640static const char *
2641vm_frametype_name(const rb_control_frame_t *cfp)
2642{
2643 switch (VM_FRAME_TYPE(cfp)) {
2644 case VM_FRAME_MAGIC_METHOD: return "method";
2645 case VM_FRAME_MAGIC_BLOCK: return "block";
2646 case VM_FRAME_MAGIC_CLASS: return "class";
2647 case VM_FRAME_MAGIC_TOP: return "top";
2648 case VM_FRAME_MAGIC_CFUNC: return "cfunc";
2649 case VM_FRAME_MAGIC_IFUNC: return "ifunc";
2650 case VM_FRAME_MAGIC_EVAL: return "eval";
2651 case VM_FRAME_MAGIC_RESCUE: return "rescue";
2652 default:
2653 rb_bug("unknown frame");
2654 }
2655}
2656#endif
2657
2658static VALUE
2659frame_return_value(const struct vm_throw_data *err)
2660{
2661 if (THROW_DATA_P(err) &&
2662 THROW_DATA_STATE(err) == TAG_BREAK &&
2663 THROW_DATA_CONSUMED_P(err) == FALSE) {
2664 return THROW_DATA_VAL(err);
2665 }
2666 else {
2667 return Qnil;
2668 }
2669}
2670
2671#if 0
2672/* for debug */
2673static const char *
2674frame_name(const rb_control_frame_t *cfp)
2675{
2676 unsigned long type = VM_FRAME_TYPE(cfp);
2677#define C(t) if (type == VM_FRAME_MAGIC_##t) return #t
2678 C(METHOD);
2679 C(BLOCK);
2680 C(CLASS);
2681 C(TOP);
2682 C(CFUNC);
2683 C(PROC);
2684 C(IFUNC);
2685 C(EVAL);
2686 C(LAMBDA);
2687 C(RESCUE);
2688 C(DUMMY);
2689#undef C
2690 return "unknown";
2691}
2692#endif
2693
2694// cfp_returning_with_value:
2695// Whether cfp is the last frame in the unwinding process for a non-local return.
2696static void
2697hook_before_rewind(rb_execution_context_t *ec, bool cfp_returning_with_value, int state, struct vm_throw_data *err)
2698{
2699 if (state == TAG_RAISE && RBASIC(err)->klass == rb_eSysStackError) {
2700 return;
2701 }
2702 else {
2703 const rb_iseq_t *iseq = CFP_ISEQ(ec->cfp);
2704 rb_hook_list_t *local_hooks = NULL;
2705 unsigned int local_hooks_cnt = iseq->aux.exec.local_hooks_cnt;
2706 if (RB_UNLIKELY(local_hooks_cnt > 0)) {
2707 local_hooks = rb_iseq_local_hooks(iseq, rb_ec_ractor_ptr(ec), false);
2708 }
2709
2710 switch (VM_FRAME_TYPE(ec->cfp)) {
2711 case VM_FRAME_MAGIC_METHOD:
2712 RUBY_DTRACE_METHOD_RETURN_HOOK(ec, 0, 0);
2713 EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_RETURN, ec->cfp->self, 0, 0, 0, frame_return_value(err));
2714
2715 if (UNLIKELY(local_hooks && local_hooks->events & RUBY_EVENT_RETURN)) {
2716 rb_exec_event_hook_orig(ec, local_hooks, RUBY_EVENT_RETURN,
2717 ec->cfp->self, 0, 0, 0, frame_return_value(err), TRUE);
2718 }
2719
2720 THROW_DATA_CONSUMED_SET(err);
2721 break;
2722 case VM_FRAME_MAGIC_BLOCK:
2723 if (VM_FRAME_BMETHOD_P(ec->cfp)) {
2724 VALUE bmethod_return_value = frame_return_value(err);
2725 if (cfp_returning_with_value) {
2726 // Non-local return terminating at a BMETHOD control frame.
2727 bmethod_return_value = THROW_DATA_VAL(err);
2728 }
2729
2730
2731 EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_B_RETURN, ec->cfp->self, 0, 0, 0, bmethod_return_value);
2732 if (UNLIKELY(local_hooks && local_hooks->events & RUBY_EVENT_B_RETURN)) {
2733 rb_exec_event_hook_orig(ec, local_hooks, RUBY_EVENT_B_RETURN,
2734 ec->cfp->self, 0, 0, 0, bmethod_return_value, TRUE);
2735 }
2736
2737 const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(ec->cfp);
2738
2739 EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_RETURN, ec->cfp->self,
2740 rb_vm_frame_method_entry(ec->cfp)->def->original_id,
2741 rb_vm_frame_method_entry(ec->cfp)->called_id,
2742 rb_vm_frame_method_entry(ec->cfp)->owner,
2743 bmethod_return_value);
2744
2745 VM_ASSERT(me->def->type == VM_METHOD_TYPE_BMETHOD);
2746 unsigned int local_hooks_cnt = me->def->body.bmethod.local_hooks_cnt;
2747 if (UNLIKELY(local_hooks_cnt > 0)) {
2748 local_hooks = rb_method_def_local_hooks(me->def, rb_ec_ractor_ptr(ec), false);
2749 if (local_hooks && local_hooks->events & RUBY_EVENT_RETURN) {
2750 rb_exec_event_hook_orig(ec, local_hooks, RUBY_EVENT_RETURN, ec->cfp->self,
2751 rb_vm_frame_method_entry(ec->cfp)->def->original_id,
2752 rb_vm_frame_method_entry(ec->cfp)->called_id,
2753 rb_vm_frame_method_entry(ec->cfp)->owner,
2754 bmethod_return_value, TRUE);
2755 }
2756 }
2757
2758 THROW_DATA_CONSUMED_SET(err);
2759 }
2760 else {
2761 EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_B_RETURN, ec->cfp->self, 0, 0, 0, frame_return_value(err));
2762 if (UNLIKELY(local_hooks && local_hooks->events & RUBY_EVENT_B_RETURN)) {
2763 rb_exec_event_hook_orig(ec, local_hooks, RUBY_EVENT_B_RETURN,
2764 ec->cfp->self, 0, 0, 0, frame_return_value(err), TRUE);
2765 }
2766 THROW_DATA_CONSUMED_SET(err);
2767 }
2768 break;
2769 case VM_FRAME_MAGIC_CLASS:
2770 EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_END, ec->cfp->self, 0, 0, 0, Qnil);
2771 break;
2772 }
2773 }
2774}
2775
2776/* evaluator body */
2777
2778/* finish
2779 VMe (h1) finish
2780 VM finish F1 F2
2781 cfunc finish F1 F2 C1
2782 rb_funcall finish F1 F2 C1
2783 VMe finish F1 F2 C1
2784 VM finish F1 F2 C1 F3
2785
2786 F1 - F3 : pushed by VM
2787 C1 : pushed by send insn (CFUNC)
2788
2789 struct CONTROL_FRAME {
2790 VALUE *pc; // cfp[0], program counter
2791 VALUE *sp; // cfp[1], stack pointer
2792 rb_iseq_t *iseq; // cfp[2], iseq
2793 VALUE self; // cfp[3], self
2794 const VALUE *ep; // cfp[4], env pointer
2795 const void *block_code; // cfp[5], block code
2796 };
2797
2798 struct rb_captured_block {
2799 VALUE self;
2800 VALUE *ep;
2801 union code;
2802 };
2803
2804 struct METHOD_ENV {
2805 VALUE param0;
2806 ...
2807 VALUE paramN;
2808 VALUE lvar1;
2809 ...
2810 VALUE lvarM;
2811 VALUE cref; // ep[-2]
2812 VALUE special; // ep[-1]
2813 VALUE flags; // ep[ 0] == lep[0]
2814 };
2815
2816 struct BLOCK_ENV {
2817 VALUE block_param0;
2818 ...
2819 VALUE block_paramN;
2820 VALUE block_lvar1;
2821 ...
2822 VALUE block_lvarM;
2823 VALUE cref; // ep[-2]
2824 VALUE special; // ep[-1]
2825 VALUE flags; // ep[ 0]
2826 };
2827
2828 struct CLASS_ENV {
2829 VALUE class_lvar0;
2830 ...
2831 VALUE class_lvarN;
2832 VALUE cref;
2833 VALUE prev_ep; // for frame jump
2834 VALUE flags;
2835 };
2836
2837 struct C_METHOD_CONTROL_FRAME {
2838 VALUE *pc; // 0
2839 VALUE *sp; // stack pointer
2840 rb_iseq_t *iseq; // cmi
2841 VALUE self; // ?
2842 VALUE *ep; // ep == lep
2843 void *code; //
2844 };
2845
2846 struct C_BLOCK_CONTROL_FRAME {
2847 VALUE *pc; // point only "finish" insn
2848 VALUE *sp; // sp
2849 rb_iseq_t *iseq; // ?
2850 VALUE self; //
2851 VALUE *ep; // ep
2852 void *code; //
2853 };
2854 */
2855
2856static inline VALUE
2857vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state, VALUE errinfo);
2858static inline VALUE
2859vm_exec_loop(rb_execution_context_t *ec, enum ruby_tag_type state, struct rb_vm_tag *tag, VALUE result);
2860
2861// for non-Emscripten Wasm build, use vm_exec with optimized setjmp for runtime performance
2862#if defined(__wasm__) && !defined(__EMSCRIPTEN__)
2863
2864struct rb_vm_exec_context {
2865 rb_execution_context_t *const ec;
2866 struct rb_vm_tag *const tag;
2867
2868 VALUE result;
2869};
2870
2871static void
2872vm_exec_bottom_main(void *context)
2873{
2874 struct rb_vm_exec_context *ctx = context;
2875 rb_execution_context_t *ec = ctx->ec;
2876
2877 ctx->result = vm_exec_loop(ec, TAG_NONE, ctx->tag, vm_exec_core(ec));
2878}
2879
2880static void
2881vm_exec_bottom_rescue(void *context)
2882{
2883 struct rb_vm_exec_context *ctx = context;
2884 rb_execution_context_t *ec = ctx->ec;
2885
2886 ctx->result = vm_exec_loop(ec, rb_ec_tag_state(ec), ctx->tag, ec->errinfo);
2887}
2888#endif
2889
2890VALUE
2891vm_exec(rb_execution_context_t *ec)
2892{
2893 VALUE result = Qundef;
2894
2895 EC_PUSH_TAG(ec);
2896
2897 _tag.retval = Qnil;
2898
2899#if defined(__wasm__) && !defined(__EMSCRIPTEN__)
2900 struct rb_vm_exec_context ctx = {
2901 .ec = ec,
2902 .tag = &_tag,
2903 };
2904 struct rb_wasm_try_catch try_catch;
2905
2906 EC_REPUSH_TAG();
2907
2908 rb_wasm_try_catch_init(&try_catch, vm_exec_bottom_main, vm_exec_bottom_rescue, &ctx);
2909
2910 rb_wasm_try_catch_loop_run(&try_catch, &RB_VM_TAG_JMPBUF_GET(_tag.buf));
2911
2912 result = ctx.result;
2913#else
2914 enum ruby_tag_type state;
2915 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
2916 if (UNDEF_P(result = jit_exec(ec))) {
2917 result = vm_exec_core(ec);
2918 }
2919 /* fallback to the VM */
2920 result = vm_exec_loop(ec, TAG_NONE, &_tag, result);
2921 }
2922 else {
2923 result = vm_exec_loop(ec, state, &_tag, ec->errinfo);
2924 }
2925#endif
2926
2927 EC_POP_TAG();
2928 return result;
2929}
2930
2931static inline VALUE
2932vm_exec_loop(rb_execution_context_t *ec, enum ruby_tag_type state,
2933 struct rb_vm_tag *tag, VALUE result)
2934{
2935 if (state == TAG_NONE) { /* no jumps, result is discarded */
2936 goto vm_loop_start;
2937 }
2938
2939 rb_ec_raised_reset(ec, RAISED_STACKOVERFLOW | RAISED_NOMEMORY);
2940 while (UNDEF_P(result = vm_exec_handle_exception(ec, state, result))) {
2941 // caught a jump, exec the handler. JIT code in jit_exec_exception()
2942 // may return Qundef to run remaining frames with vm_exec_core().
2943 if (UNDEF_P(result = jit_exec_exception(ec))) {
2944 result = vm_exec_core(ec);
2945 }
2946 vm_loop_start:
2947 VM_ASSERT(ec->tag == tag);
2948 /* when caught `throw`, `tag.state` is set. */
2949 if ((state = tag->state) == TAG_NONE) break;
2950 tag->state = TAG_NONE;
2951 }
2952
2953 return result;
2954}
2955
2956#if USE_ZJIT
2957typedef struct {
2958 const uint8_t *bytes;
2959 size_t pos; // in bits
2960} zjit_stack_map_reader_t;
2961
2962// Read n <= 8 bits. Touches the next byte only when the bits straddle it.
2963static inline unsigned int
2964zjit_stack_map_read_bits(zjit_stack_map_reader_t *r, int n)
2965{
2966 size_t byte = r->pos / 8;
2967 int shift = r->pos % 8;
2968 unsigned int bits = r->bytes[byte] >> shift;
2969 if (shift + n > 8) bits |= (unsigned int)r->bytes[byte + 1] << (8 - shift);
2970 r->pos += n;
2971 return bits & ((1u << n) - 1);
2972}
2973
2974static inline int64_t
2975zjit_stack_map_read_varint(zjit_stack_map_reader_t *r)
2976{
2977 const int chunk_bits = ZJIT_STACK_MAP_VARINT_BITS + 1; // payload, then continuation bit
2978 const unsigned int payload_mask = (1u << ZJIT_STACK_MAP_VARINT_BITS) - 1;
2979 uint64_t value = 0;
2980 unsigned int chunk;
2981 int shift = 0;
2982 do {
2983 chunk = zjit_stack_map_read_bits(r, chunk_bits);
2984 value |= (uint64_t)(chunk & payload_mask) << shift;
2985 shift += ZJIT_STACK_MAP_VARINT_BITS;
2986 } while (chunk >> ZJIT_STACK_MAP_VARINT_BITS);
2987 return (int64_t)value;
2988}
2989
2990// Materialize JITFrame-enabled CFP into interpreter-compatible CFP
2991static void
2992zjit_materialize_frames(const rb_execution_context_t *ec, rb_control_frame_t *cfp, bool materialize_target)
2993{
2994 if (!rb_zjit_enabled_p) return;
2995 const rb_control_frame_t *end_cfp = ec->tag->cfp;
2996 VM_ASSERT(cfp <= end_cfp);
2997
2998 while (true) {
2999 // If materialize_target is false, we skip materializing ec->tag->cfp.
3000 //
3001 // When JIT code calls a C function that does the same number of setjmps and
3002 // longjmps, e.g. rb_hash_aref, it calls zjit_materialize_frames but goes
3003 // back to the JIT code. In that case, we don't want to materialize the frame
3004 // and clear cfp->jit_return, which will still be used by the JIT code.
3005 //
3006 // When JIT code calls a C function that does more longjmps than setjmps,
3007 // it would not go back to the JIT code. So ec->tag->cfp should be materialized
3008 // in that case.
3009 if (cfp == end_cfp && !materialize_target) break;
3010
3011 if (CFP_ZJIT_FRAME_P(cfp)) {
3012 const zjit_jit_frame_t *jit_frame = CFP_ZJIT_FRAME(cfp);
3013 cfp->pc = jit_frame->pc;
3014 cfp->_iseq = (rb_iseq_t *)jit_frame->iseq;
3015 if (ZJIT_FRAME_MATERIALIZE_BLOCK_CODE_P(jit_frame)) {
3016 cfp->block_code = NULL;
3017 }
3018
3019 // Materialize Ruby stack slots kept off the VM stack. On side exit,
3020 // the exiting frame is already written by compile_exit_save_state()
3021 // and skipped here after materialize_exit_trampoline clears its
3022 // jit_return, so this restores older ZJIT frames from stack maps.
3023 if (jit_frame->stack_map) {
3024 VALUE *stack = cfp->sp;
3025 zjit_stack_map_reader_t reader = { jit_frame->stack_map, 1 }; // bit 0 is materialize_block_code
3026#define WRITE_ONE(value) do { --stack; *stack = (value); } while (0)
3027 for (int32_t i = 0;; i++) {
3028 unsigned int op = zjit_stack_map_read_bits(&reader, ZJIT_STACK_MAP_OP_BITS);
3029 if (op == ZJIT_STACK_MAP_OP_END) break;
3030 switch (op) {
3031 case ZJIT_STACK_MAP_OP_VREG:
3032 // Decode a native stack slot offset generated by ZJIT's backend.
3033 // It's an offset from NATIVE_BASE_PTR, which is copied into
3034 // cfp->jit_return, to the native stack slot holding the value.
3035 WRITE_ONE(((VALUE *)cfp->jit_return)[-zjit_stack_map_read_varint(&reader)]);
3036 break;
3037 case ZJIT_STACK_MAP_OP_IMM: {
3038 VALUE value = (VALUE)zjit_stack_map_read_varint(&reader);
3039 RUBY_ASSERT(RB_SPECIAL_CONST_P(value)); // We don't GC mark stack map entries.
3040 WRITE_ONE(value);
3041 break;
3042 }
3043 case ZJIT_STACK_MAP_OP_NIL:
3044 WRITE_ONE(Qnil);
3045 break;
3046 case ZJIT_STACK_MAP_OP_SKIP:
3047 stack -= zjit_stack_map_read_varint(&reader);
3048 break;
3049 case ZJIT_STACK_MAP_OP_BASE_PTR: {
3050 // This has to be the first code to align the write cursor for other entries
3051 RUBY_ASSERT_ALWAYS(0 == i, "base_ptr stack map code only makes sense at 0");
3052 int64_t slot_index = zjit_stack_map_read_varint(&reader);
3053 int64_t stack_size = zjit_stack_map_read_varint(&reader);
3054 VALUE *base_ptr = (VALUE *)((VALUE *)cfp->jit_return)[-slot_index];
3055 stack = base_ptr + stack_size;
3056 break;
3057 }
3058 default:
3059 rb_bug("unknown ZJIT stackmap opcode: %u", op);
3060 break;
3061 }
3062 }
3063#undef WRITE_ONE
3064 }
3065 cfp->jit_return = 0;
3066 }
3067 if (end_cfp == cfp) break;
3068 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
3069 }
3070}
3071
3072void
3073rb_zjit_materialize_frames(const rb_execution_context_t *ec, rb_control_frame_t *cfp)
3074{
3075 zjit_materialize_frames(ec, cfp, true);
3076}
3077
3078void
3079rb_zjit_materialize_frames_for_longjmp(const rb_execution_context_t *ec, rb_control_frame_t *cfp)
3080{
3081 // A ZJIT frame active before the tag's setjmp is below it on the native
3082 // stack and survives longjmp. Materialize only the frames unwound above it.
3083 zjit_materialize_frames(ec, cfp, !ec->tag->zjit_frame_active);
3084}
3085#endif
3086
3087static inline VALUE
3088vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state, VALUE errinfo)
3089{
3090 struct vm_throw_data *err = (struct vm_throw_data *)errinfo;
3091
3092 for (;;) {
3093 unsigned int i;
3094 const struct iseq_catch_table_entry *entry;
3095 const struct iseq_catch_table *ct;
3096 unsigned long epc, cont_pc, cont_sp;
3097 const rb_iseq_t *catch_iseq;
3098 VALUE type;
3099 const rb_control_frame_t *escape_cfp;
3100
3101 cont_pc = cont_sp = 0;
3102 catch_iseq = NULL;
3103
3104 while (CFP_PC(ec->cfp) == 0 || CFP_ISEQ(ec->cfp) == 0) {
3105 if (UNLIKELY(VM_FRAME_TYPE(ec->cfp) == VM_FRAME_MAGIC_CFUNC)) {
3106 EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_C_RETURN, ec->cfp->self,
3107 rb_vm_frame_method_entry(ec->cfp)->def->original_id,
3108 rb_vm_frame_method_entry(ec->cfp)->called_id,
3109 rb_vm_frame_method_entry(ec->cfp)->owner, Qnil);
3110 RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec,
3111 rb_vm_frame_method_entry(ec->cfp)->owner,
3112 rb_vm_frame_method_entry(ec->cfp)->def->original_id);
3113 }
3114 rb_vm_pop_frame(ec);
3115 }
3116
3117 rb_control_frame_t *const cfp = ec->cfp;
3118 epc = CFP_PC(cfp) - ISEQ_BODY(CFP_ISEQ(cfp))->iseq_encoded;
3119
3120 escape_cfp = NULL;
3121 if (state == TAG_BREAK || state == TAG_RETURN) {
3122 escape_cfp = THROW_DATA_CATCH_FRAME(err);
3123
3124 if (cfp == escape_cfp) {
3125 if (state == TAG_RETURN) {
3126 if (!VM_FRAME_FINISHED_P(cfp)) {
3127 THROW_DATA_CATCH_FRAME_SET(err, cfp + 1);
3128 THROW_DATA_STATE_SET(err, state = TAG_BREAK);
3129 }
3130 else {
3131 ct = ISEQ_BODY(CFP_ISEQ(cfp))->catch_table;
3132 if (ct) for (i = 0; i < ct->size; i++) {
3133 entry = UNALIGNED_MEMBER_PTR(ct, entries[i]);
3134 if (entry->start < epc && entry->end >= epc) {
3135 if (entry->type == CATCH_TYPE_ENSURE) {
3136 catch_iseq = entry->iseq;
3137 cont_pc = entry->cont;
3138 cont_sp = entry->sp;
3139 break;
3140 }
3141 }
3142 }
3143 if (catch_iseq == NULL) {
3144 ec->errinfo = Qnil;
3145 THROW_DATA_CATCH_FRAME_SET(err, cfp + 1);
3146 // cfp == escape_cfp here so calling with cfp_returning_with_value = true
3147 hook_before_rewind(ec, true, state, err);
3148 rb_vm_pop_frame(ec);
3149 return THROW_DATA_VAL(err);
3150 }
3151 }
3152 /* through */
3153 }
3154 else {
3155 /* TAG_BREAK */
3156 *cfp->sp++ = THROW_DATA_VAL(err);
3157 ec->errinfo = Qnil;
3158 rb_zjit_materialize_frames(ec, cfp);
3159 return Qundef;
3160 }
3161 }
3162 }
3163
3164 if (state == TAG_RAISE) {
3165 ct = ISEQ_BODY(CFP_ISEQ(cfp))->catch_table;
3166 if (ct) for (i = 0; i < ct->size; i++) {
3167 entry = UNALIGNED_MEMBER_PTR(ct, entries[i]);
3168 if (entry->start < epc && entry->end >= epc) {
3169
3170 if (entry->type == CATCH_TYPE_RESCUE ||
3171 entry->type == CATCH_TYPE_ENSURE) {
3172 catch_iseq = entry->iseq;
3173 cont_pc = entry->cont;
3174 cont_sp = entry->sp;
3175 break;
3176 }
3177 }
3178 }
3179 }
3180 else if (state == TAG_RETRY) {
3181 ct = ISEQ_BODY(CFP_ISEQ(cfp))->catch_table;
3182 if (ct) for (i = 0; i < ct->size; i++) {
3183 entry = UNALIGNED_MEMBER_PTR(ct, entries[i]);
3184 if (entry->start < epc && entry->end >= epc) {
3185
3186 if (entry->type == CATCH_TYPE_ENSURE) {
3187 catch_iseq = entry->iseq;
3188 cont_pc = entry->cont;
3189 cont_sp = entry->sp;
3190 break;
3191 }
3192 else if (entry->type == CATCH_TYPE_RETRY) {
3193 const rb_control_frame_t *escape_cfp;
3194 escape_cfp = THROW_DATA_CATCH_FRAME(err);
3195 if (cfp == escape_cfp) {
3196 rb_zjit_materialize_frames(ec, cfp);
3197 cfp->pc = ISEQ_BODY(CFP_ISEQ(cfp))->iseq_encoded + entry->cont;
3198 ec->errinfo = Qnil;
3199 return Qundef;
3200 }
3201 }
3202 }
3203 }
3204 }
3205 else if ((state == TAG_BREAK && !escape_cfp) ||
3206 (state == TAG_REDO) ||
3207 (state == TAG_NEXT)) {
3208 type = (const enum rb_catch_type[TAG_MASK]) {
3209 [TAG_BREAK] = CATCH_TYPE_BREAK,
3210 [TAG_NEXT] = CATCH_TYPE_NEXT,
3211 [TAG_REDO] = CATCH_TYPE_REDO,
3212 /* otherwise = dontcare */
3213 }[state];
3214
3215 ct = ISEQ_BODY(CFP_ISEQ(cfp))->catch_table;
3216 if (ct) for (i = 0; i < ct->size; i++) {
3217 entry = UNALIGNED_MEMBER_PTR(ct, entries[i]);
3218
3219 if (entry->start < epc && entry->end >= epc) {
3220 if (entry->type == CATCH_TYPE_ENSURE) {
3221 catch_iseq = entry->iseq;
3222 cont_pc = entry->cont;
3223 cont_sp = entry->sp;
3224 break;
3225 }
3226 else if (entry->type == type) {
3227 rb_zjit_materialize_frames(ec, cfp);
3228 cfp->pc = ISEQ_BODY(CFP_ISEQ(cfp))->iseq_encoded + entry->cont;
3229 cfp->sp = vm_base_ptr(cfp) + entry->sp;
3230
3231 if (state != TAG_REDO) {
3232 *cfp->sp++ = THROW_DATA_VAL(err);
3233 }
3234 ec->errinfo = Qnil;
3235 VM_ASSERT(ec->tag->state == TAG_NONE);
3236 return Qundef;
3237 }
3238 }
3239 }
3240 }
3241 else {
3242 ct = ISEQ_BODY(CFP_ISEQ(cfp))->catch_table;
3243 if (ct) for (i = 0; i < ct->size; i++) {
3244 entry = UNALIGNED_MEMBER_PTR(ct, entries[i]);
3245 if (entry->start < epc && entry->end >= epc) {
3246
3247 if (entry->type == CATCH_TYPE_ENSURE) {
3248 catch_iseq = entry->iseq;
3249 cont_pc = entry->cont;
3250 cont_sp = entry->sp;
3251 break;
3252 }
3253 }
3254 }
3255 }
3256
3257 if (catch_iseq != NULL) { /* found catch table */
3258 /* enter catch scope */
3259 const int arg_size = 1;
3260
3261 rb_iseq_check(catch_iseq);
3262 rb_zjit_materialize_frames(ec, cfp); // vm_base_ptr looks at cfp->_iseq
3263 cfp->sp = vm_base_ptr(cfp) + cont_sp;
3264 cfp->pc = ISEQ_BODY(CFP_ISEQ(cfp))->iseq_encoded + cont_pc;
3265
3266 /* push block frame */
3267 cfp->sp[0] = (VALUE)err;
3268 vm_push_frame(ec, catch_iseq, VM_FRAME_MAGIC_RESCUE,
3269 cfp->self,
3270 VM_GUARDED_PREV_EP(cfp->ep),
3271 0, /* cref or me */
3272 ISEQ_BODY(catch_iseq)->iseq_encoded,
3273 cfp->sp + arg_size /* push value */,
3274 ISEQ_BODY(catch_iseq)->local_table_size - arg_size,
3275 ISEQ_BODY(catch_iseq)->stack_max);
3276
3277 state = 0;
3278 ec->tag->state = TAG_NONE;
3279 ec->errinfo = Qnil;
3280
3281 return Qundef;
3282 }
3283 else {
3284 hook_before_rewind(ec, (cfp == escape_cfp), state, err);
3285
3286 if (VM_FRAME_FINISHED_P(ec->cfp)) {
3287 rb_vm_pop_frame(ec);
3288 ec->errinfo = (VALUE)err;
3289 rb_vm_tag_jmpbuf_deinit(&ec->tag->buf);
3290 ec->tag = ec->tag->prev;
3291 EC_JUMP_TAG(ec, state);
3292 }
3293 else {
3294 rb_vm_pop_frame(ec);
3295 }
3296 }
3297 }
3298}
3299
3300/* misc */
3301
3302VALUE
3303rb_iseq_eval(const rb_iseq_t *iseq, const rb_box_t *box)
3304{
3305 rb_execution_context_t *ec = GET_EC();
3306 VALUE val;
3307 vm_set_top_stack(ec, iseq, box);
3308 val = vm_exec(ec);
3309 return val;
3310}
3311
3312VALUE
3313rb_iseq_eval_main(const rb_iseq_t *iseq)
3314{
3315 rb_execution_context_t *ec = GET_EC();
3316 VALUE val;
3317 vm_set_main_stack(ec, iseq);
3318 val = vm_exec(ec);
3319 return val;
3320}
3321
3322int
3323rb_vm_control_frame_id_and_class(const rb_control_frame_t *cfp, ID *idp, ID *called_idp, VALUE *klassp)
3324{
3325 const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
3326
3327 if (me) {
3328 if (idp) *idp = me->def->original_id;
3329 if (called_idp) *called_idp = me->called_id;
3330 if (klassp) *klassp = me->owner;
3331 return TRUE;
3332 }
3333 else {
3334 return FALSE;
3335 }
3336}
3337
3338int
3339rb_ec_frame_method_id_and_class(const rb_execution_context_t *ec, ID *idp, ID *called_idp, VALUE *klassp)
3340{
3341 return rb_vm_control_frame_id_and_class(ec->cfp, idp, called_idp, klassp);
3342}
3343
3344int
3346{
3347 return rb_ec_frame_method_id_and_class(GET_EC(), idp, 0, klassp);
3348}
3349
3350VALUE
3351rb_vm_call_cfunc(VALUE recv, VALUE (*func)(VALUE), VALUE arg,
3352 VALUE block_handler, VALUE filename)
3353{
3354 rb_execution_context_t *ec = GET_EC();
3355 const rb_control_frame_t *reg_cfp = ec->cfp;
3356 const rb_iseq_t *iseq = rb_iseq_new(Qnil, filename, filename, Qnil, 0, ISEQ_TYPE_TOP);
3357 const rb_box_t *box = rb_current_box();
3358 VALUE val;
3359
3360 vm_push_frame(ec, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH,
3361 recv, GC_GUARDED_PTR(box),
3362 (VALUE)vm_cref_new_toplevel(ec), /* cref or me */
3363 0, reg_cfp->sp, 0, 0);
3364
3365 val = (*func)(arg);
3366
3367 rb_vm_pop_frame(ec);
3368 return val;
3369}
3370
3371/* Ruby::Box */
3372
3373VALUE
3374rb_vm_call_cfunc_in_box(VALUE recv, VALUE (*func)(VALUE, VALUE), VALUE arg1, VALUE arg2,
3375 VALUE filename, const rb_box_t *box)
3376{
3377 rb_execution_context_t *ec = GET_EC();
3378 const rb_control_frame_t *reg_cfp = ec->cfp;
3379 const rb_iseq_t *iseq = rb_iseq_new(Qnil, filename, filename, Qnil, 0, ISEQ_TYPE_TOP);
3380 VALUE val;
3381
3382 vm_push_frame(ec, iseq, VM_FRAME_MAGIC_TOP | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH,
3383 recv, GC_GUARDED_PTR(box),
3384 (VALUE)vm_cref_new_toplevel(ec), /* cref or me */
3385 0, reg_cfp->sp, 0, 0);
3386
3387 val = (*func)(arg1, arg2);
3388
3389 rb_vm_pop_frame(ec);
3390 return val;
3391}
3392
3393void
3394rb_vm_frame_flag_set_box_require(const rb_execution_context_t *ec)
3395{
3396 VM_ASSERT(rb_box_available());
3397 VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_BOX_REQUIRE);
3398}
3399
3400static const rb_box_t *current_box_on_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp);
3401
3411static const rb_box_t *
3412caller_user_box_on_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
3413{
3414 const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(ec);
3415
3416 while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, eocfp)) {
3417 const rb_box_t *box = current_box_on_cfp(ec, cfp);
3418 if (BOX_USER_P(box))
3419 return box;
3420 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
3421 }
3422 return NULL;
3423}
3424
3425static const rb_box_t *
3426current_box_on_cfp(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
3427{
3429 const rb_box_t *box;
3430 const VALUE *lep = VM_EP_RUBY_LEP(ec, cfp);
3431 VM_BOX_ASSERT(lep, "lep should be valid");
3432 VM_BOX_ASSERT(rb_box_available(), "box should be available here");
3433
3434 if (VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_METHOD) || VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_CFUNC)) {
3435 cme = check_method_entry(lep[VM_ENV_DATA_INDEX_ME_CREF], TRUE);
3436 VM_BOX_ASSERT(cme, "cme should be valid");
3437 VM_BOX_ASSERT(cme->def, "cme->def shold be valid");
3438 if (cme->def->type == VM_METHOD_TYPE_ISEQ &&
3439 (ISEQ_BODY(cme->def->body.iseq.iseqptr)->builtin_attrs & BUILTIN_ATTR_CALLER_USER_BOX)) {
3440 const rb_control_frame_t *owner_cfp = rb_vm_search_cf_from_ep(ec, cfp, lep);
3441 if (owner_cfp) {
3442 box = caller_user_box_on_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(owner_cfp));
3443 if (box)
3444 return box;
3445 }
3446 }
3447 return cme->def->box;
3448 }
3449 else if (VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_TOP) || VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_CLASS)) {
3450 VM_BOX_ASSERT(VM_ENV_LOCAL_P(lep), "lep should be local on MAGIC_TOP or MAGIC_CLASS frames");
3451 return VM_ENV_BOX(lep);
3452 }
3453 else if (VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_DUMMY)) {
3454 // No valid local ep found (just after process boot?)
3455 // return the root box (the only valid box) until the main is initialized
3456 box = rb_main_box();
3457 if (box)
3458 return box;
3459 return rb_root_box();
3460 }
3461 else {
3462 VM_BOX_CRASHED();
3463 rb_bug("BUG: Local ep without cme/box, flags: %08lX", (unsigned long)lep[VM_ENV_DATA_INDEX_FLAGS]);
3464 }
3466}
3467
3468const rb_box_t *
3469rb_vm_current_box(const rb_execution_context_t *ec)
3470{
3471 return current_box_on_cfp(ec, ec->cfp);
3472}
3473
3474static const rb_control_frame_t *
3475find_loader_control_frame(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, const rb_control_frame_t *end_cfp)
3476{
3477 while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) {
3478 if (!VM_ENV_FRAME_TYPE_P(cfp->ep, VM_FRAME_MAGIC_CFUNC))
3479 break;
3480 if (!BOX_MASTER_P(current_box_on_cfp(ec, cfp)))
3481 break;
3482 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
3483 }
3484 VM_ASSERT(RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp));
3485 return cfp;
3486}
3487
3488const rb_box_t *
3489rb_vm_loading_box(const rb_execution_context_t *ec)
3490{
3491 const rb_control_frame_t *cfp, *current_cfp, *end_cfp;
3492
3493 if (!rb_box_available() || !ec)
3494 return rb_root_box();
3495
3496 cfp = ec->cfp;
3497 current_cfp = cfp;
3498 end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
3499
3500 while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) {
3501 if (VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_BOX_REQUIRE)) {
3502 if (RTEST(cfp->self) && BOX_OBJ_P(cfp->self)) {
3503 // Box#require, #require_relative, #load
3504 return rb_get_box_t(cfp->self);
3505 }
3506 // Kernel#require, #require_relative, #load
3507 cfp = find_loader_control_frame(ec, cfp, end_cfp);
3508 return current_box_on_cfp(ec, cfp);
3509 }
3510 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
3511 }
3512 // no require/load with explicit boxes.
3513 return current_box_on_cfp(ec, current_cfp);
3514}
3515
3516/* vm */
3517
3518void
3519rb_vm_update_references(void *ptr)
3520{
3521 if (ptr) {
3522 rb_vm_t *vm = ptr;
3523
3524 rb_gc_update_moved(&vm->self);
3525 rb_gc_update_moved(&vm->orig_progname);
3526 rb_gc_update_moved(&vm->cc_refinement_set);
3527
3528 if (vm->root_box)
3529 rb_box_gc_update_references(vm->root_box);
3530 if (vm->main_box)
3531 rb_box_gc_update_references(vm->main_box);
3532
3533 rb_gc_update_values(RUBY_NSIG, vm->trap_list.cmd);
3534
3535 if (vm->coverages) {
3536 rb_gc_update_moved(&vm->coverages);
3537 rb_gc_update_moved(&vm->cme2counter);
3538 rb_gc_update_moved(&vm->me_set);
3539 }
3540 }
3541}
3542
3543void
3544rb_vm_each_stack_value(void *ptr, void (*cb)(VALUE, void*), void *ctx)
3545{
3546 if (ptr) {
3547 rb_vm_t *vm = ptr;
3548 rb_ractor_t *r = 0;
3549 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
3550 VM_ASSERT(rb_ractor_status_p(r, ractor_blocking) ||
3551 rb_ractor_status_p(r, ractor_running));
3552 if (r->threads.cnt > 0) {
3553 rb_thread_t *th = 0;
3554 ccan_list_for_each(&r->threads.set, th, lt_node) {
3555 VM_ASSERT(th != NULL);
3556 rb_execution_context_t * ec = th->ec;
3557 if (ec->vm_stack) {
3558 VALUE *p = ec->vm_stack;
3559 VALUE *sp = ec->cfp->sp;
3560 while (p < sp) {
3561 if (!RB_SPECIAL_CONST_P(*p)) {
3562 cb(*p, ctx);
3563 }
3564 p++;
3565 }
3566 }
3567 }
3568 }
3569 }
3570 }
3571}
3572
3573static enum rb_id_table_iterator_result
3574vm_mark_negative_cme(VALUE val, void *dmy)
3575{
3576 rb_gc_mark(val);
3577 return ID_TABLE_CONTINUE;
3578}
3579
3580
3581void
3582rb_vm_mark(void *ptr)
3583{
3584 RUBY_MARK_ENTER("vm");
3585 RUBY_GC_INFO("-------------------------------------------------\n");
3586 if (ptr) {
3587 rb_vm_t *vm = ptr;
3588 rb_ractor_t *r = 0;
3589 long i;
3590
3591 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
3592 // ractor.set only contains blocking or running ractors
3593 VM_ASSERT(rb_ractor_status_p(r, ractor_blocking) ||
3594 rb_ractor_status_p(r, ractor_running));
3595 rb_gc_mark(rb_ractor_self(r));
3596 }
3597
3598 rb_gc_mark_movable(vm->self);
3599
3600 if (vm->root_box) {
3601 rb_box_entry_mark(vm->root_box);
3602 }
3603 if (vm->main_box) {
3604 rb_box_entry_mark(vm->main_box);
3605 }
3606
3607 rb_gc_mark_movable(vm->orig_progname);
3608 rb_gc_mark_movable(vm->coverages);
3609 rb_gc_mark_movable(vm->cme2counter);
3610 rb_gc_mark_movable(vm->me_set);
3611 rb_gc_mark_movable(vm->cc_refinement_set);
3612
3613 rb_hook_list_mark(&vm->global_hooks);
3614
3615 rb_id_table_foreach_values(&vm->negative_cme_table, vm_mark_negative_cme, NULL);
3616 rb_mark_tbl_no_pin(&vm->overloaded_cme_table);
3617 for (i=0; i<VM_GLOBAL_CC_CACHE_TABLE_SIZE; i++) {
3618 const struct rb_callcache *cc = vm->global_cc_cache_table[i];
3619
3620 if (cc != NULL) {
3621 if (!vm_cc_invalidated_p(cc)) {
3622 rb_gc_mark((VALUE)cc);
3623 }
3624 else {
3625 vm->global_cc_cache_table[i] = NULL;
3626 }
3627 }
3628 }
3629 }
3630
3631 RUBY_MARK_LEAVE("vm");
3632}
3633
3634#undef rb_vm_register_special_exception
3635void
3636rb_vm_register_special_exception_str(enum ruby_special_exceptions sp, VALUE cls, VALUE mesg)
3637{
3638 rb_vm_t *vm = GET_VM();
3639 VALUE exc = rb_exc_new3(cls, rb_obj_freeze(mesg));
3640 OBJ_FREEZE(exc);
3641 ((VALUE *)vm->special_exceptions)[sp] = exc;
3642 rb_vm_register_global_object(exc);
3643}
3644
3645void rb_objspace_free_objects(void *objspace);
3646
3647int
3649{
3650 {
3651 // wait for native threads still winding down a dead coroutine: their
3652 // reclaim frees through the objspace this function is about to
3653 // destroy (see coroutine_thread_terminated)
3654 void rb_thread_sched_wait_winding(rb_vm_t *vm);
3655 rb_thread_sched_wait_winding(vm);
3656 }
3657
3658 RUBY_FREE_ENTER("vm");
3659 ruby_vm_during_cleanup = true;
3660 rb_gc_stash_cleanup_objspace();
3661
3662 if (vm) {
3663 rb_thread_t *th = vm->ractor.main_thread;
3664
3665 if (rb_free_at_exit) {
3666 rb_free_encoded_insn_data();
3667 rb_free_global_enc_table();
3668 rb_free_loaded_builtin_table();
3669 rb_free_global_symbol_table();
3670
3671 rb_free_shared_fiber_pool();
3672 rb_free_transcoder_table();
3673 rb_free_vm_opt_tables();
3674 rb_free_warning();
3675 rb_free_rb_global_tbl();
3676
3677 rb_id_table_free_items(&vm->negative_cme_table);
3678 st_free_embedded_table(&vm->overloaded_cme_table);
3679
3680 // TODO: Is this ignorable for classext->m_tbl ?
3681 // rb_id_table_free(RCLASS(rb_mRubyVMFrozenCore)->m_tbl);
3682
3683 st_free_embedded_table(&vm->static_ext_inits);
3684
3685 rb_id_table_free_items(&vm->constant_cache);
3686 set_free_embedded_table(&vm->unused_block_warning_table);
3687
3688 rb_thread_free_native_thread(th);
3689
3690#ifndef HAVE_SETPROCTITLE
3691 ruby_free_proctitle();
3692#endif
3693 }
3694 else {
3695 rb_fiber_reset_root_local_storage(th);
3696 thread_free(th);
3697 }
3698
3699 void *objspace = vm->ractor.main_ractor ? vm->ractor.main_ractor->objspace : NULL;
3700
3701 rb_vm_living_threads_init(vm);
3702 ruby_vm_run_at_exit_hooks(vm);
3703 st_free_embedded_table(&vm->ci_table);
3704 RB_ALTSTACK_FREE(vm->main_altstack);
3705
3706 if (objspace) {
3707 if (rb_free_at_exit) {
3708 rb_objspace_free_objects(objspace);
3709 rb_free_generic_fields_tbl_();
3710 rb_free_default_rand_key();
3711 }
3712 rb_objspace_free(objspace);
3713 }
3714
3715 if (rb_free_at_exit) {
3716 free(vm->gc.registered_addrs.registry);
3717 }
3718 rb_native_mutex_destroy(&vm->once_lock);
3719 rb_native_cond_destroy(&vm->once_cond);
3720 /* after freeing objspace, you *can't* use ruby_xfree() */
3721 ruby_current_vm_ptr = NULL;
3722
3723 if (rb_free_at_exit) {
3724#if USE_YJIT
3725 rb_yjit_free_at_exit();
3726#endif
3727 }
3728
3729 rb_box_unload_local_extensions();
3730 }
3731 RUBY_FREE_LEAVE("vm");
3732 return 0;
3733}
3734
3735
3736// Used for VM memsize reporting. Returns the size of the at_exit list by
3737// looping through the linked list and adding up the size of the structs.
3738static enum rb_id_table_iterator_result
3739vm_memsize_constant_cache_i(ID id, VALUE ics, void *size)
3740{
3741 *((size_t *) size) += rb_set_memsize((set_table *) ics);
3742 return ID_TABLE_CONTINUE;
3743}
3744
3745// Returns a size_t representing the memory footprint of the VM's constant
3746// cache, which is the memsize of the table as well as the memsize of all of the
3747// nested tables.
3748static size_t
3749vm_memsize_constant_cache(void)
3750{
3751 rb_vm_t *vm = GET_VM();
3752 size_t size = rb_id_table_memsize(&vm->constant_cache) - sizeof(struct rb_id_table);
3753
3754 rb_id_table_foreach(&vm->constant_cache, vm_memsize_constant_cache_i, &size);
3755 return size;
3756}
3757
3758static size_t
3759vm_memsize_at_exit_list(rb_at_exit_list *at_exit)
3760{
3761 size_t size = 0;
3762
3763 while (at_exit) {
3764 size += sizeof(rb_at_exit_list);
3765 at_exit = at_exit->next;
3766 }
3767
3768 return size;
3769}
3770
3771// Used for VM memsize reporting. Returns the size of the builtin function
3772// table if it has been defined.
3773static size_t
3774vm_memsize_builtin_function_table(const struct rb_builtin_function *builtin_function_table)
3775{
3776 return builtin_function_table == NULL ? 0 : sizeof(struct rb_builtin_function);
3777}
3778
3779// Reports the memsize of the VM struct object and the structs that are
3780// associated with it.
3781static size_t
3782vm_memsize(const void *ptr)
3783{
3784 rb_vm_t *vm = GET_VM();
3785
3786 return (
3787 sizeof(rb_vm_t) +
3788 rb_vm_memsize_postponed_job_queue() +
3789 vm_memsize_at_exit_list(vm->at_exit) +
3790 (rb_st_memsize(&vm->ci_table) - sizeof(struct st_table)) +
3791 vm_memsize_builtin_function_table(vm->builtin_function_table) +
3792 (rb_id_table_memsize(&vm->negative_cme_table) - sizeof(struct rb_id_table)) +
3793 (rb_st_memsize(&vm->overloaded_cme_table) - sizeof(struct st_table)) +
3794 vm_memsize_constant_cache()
3795 );
3796
3797 // TODO
3798 // struct { struct ccan_list_head set; } ractor;
3799 // void *main_altstack; #ifdef USE_SIGALTSTACK
3800 // struct rb_objspace *objspace;
3801}
3802
3803const rb_data_type_t ruby_vm_data_type = {
3804 "VM",
3805 {0, 0, vm_memsize,},
3806 0, 0, RUBY_TYPED_THREAD_SAFE_FREE
3807};
3808
3809#define vm_data_type ruby_vm_data_type
3810
3811static VALUE
3812vm_default_params(void)
3813{
3814 rb_vm_t *vm = GET_VM();
3815 VALUE result = rb_hash_new_capa(4);
3816#define SET(name) rb_hash_aset(result, ID2SYM(rb_intern(#name)), SIZET2NUM(vm->default_params.name));
3817 SET(thread_vm_stack_size);
3818 SET(thread_machine_stack_size);
3819 SET(fiber_vm_stack_size);
3820 SET(fiber_machine_stack_size);
3821#undef SET
3822 rb_obj_freeze(result);
3823 return result;
3824}
3825
3826static size_t
3827get_param(const char *name, size_t default_value, size_t min_value)
3828{
3829 const char *envval;
3830 size_t result = default_value;
3831 if ((envval = getenv(name)) != 0) {
3832 long val = atol(envval);
3833 if (val < (long)min_value) {
3834 val = (long)min_value;
3835 }
3836 result = (size_t)(((val -1 + RUBY_VM_SIZE_ALIGN) / RUBY_VM_SIZE_ALIGN) * RUBY_VM_SIZE_ALIGN);
3837 }
3838 if (0) ruby_debug_printf("%s: %"PRIuSIZE"\n", name, result); /* debug print */
3839
3840 return result;
3841}
3842
3843static void
3844check_machine_stack_size(size_t *sizep)
3845{
3846#ifdef PTHREAD_STACK_MIN
3847 size_t size = *sizep;
3848#endif
3849
3850#ifdef PTHREAD_STACK_MIN
3851 if (size < (size_t)PTHREAD_STACK_MIN) {
3852 *sizep = (size_t)PTHREAD_STACK_MIN * 2;
3853 }
3854#endif
3855}
3856
3857static void
3858vm_default_params_setup(rb_vm_t *vm)
3859{
3860 vm->default_params.thread_vm_stack_size =
3861 get_param("RUBY_THREAD_VM_STACK_SIZE",
3862 RUBY_VM_THREAD_VM_STACK_SIZE,
3863 RUBY_VM_THREAD_VM_STACK_SIZE_MIN);
3864
3865 vm->default_params.thread_machine_stack_size =
3866 get_param("RUBY_THREAD_MACHINE_STACK_SIZE",
3867 RUBY_VM_THREAD_MACHINE_STACK_SIZE,
3868 RUBY_VM_THREAD_MACHINE_STACK_SIZE_MIN);
3869
3870 vm->default_params.fiber_vm_stack_size =
3871 get_param("RUBY_FIBER_VM_STACK_SIZE",
3872 RUBY_VM_FIBER_VM_STACK_SIZE,
3873 RUBY_VM_FIBER_VM_STACK_SIZE_MIN);
3874
3875 vm->default_params.fiber_machine_stack_size =
3876 get_param("RUBY_FIBER_MACHINE_STACK_SIZE",
3877 RUBY_VM_FIBER_MACHINE_STACK_SIZE,
3878 RUBY_VM_FIBER_MACHINE_STACK_SIZE_MIN);
3879
3880 /* environment dependent check */
3881 check_machine_stack_size(&vm->default_params.thread_machine_stack_size);
3882 check_machine_stack_size(&vm->default_params.fiber_machine_stack_size);
3883}
3884
3885static void
3886vm_init2(rb_vm_t *vm)
3887{
3888 rb_vm_living_threads_init(vm);
3889 vm->thread_report_on_exception = 1;
3890 vm->src_encoding_index = -1;
3891
3892 vm_default_params_setup(vm);
3893}
3894
3895void
3896rb_execution_context_update(rb_execution_context_t *ec)
3897{
3898 /* update VM stack */
3899 if (ec->vm_stack) {
3900 long i;
3901 VM_ASSERT(ec->cfp);
3902 VALUE *p = ec->vm_stack;
3903 VALUE *sp = ec->cfp->sp;
3904 rb_control_frame_t *cfp = ec->cfp;
3905 rb_control_frame_t *limit_cfp = (void *)(ec->vm_stack + ec->vm_stack_size);
3906
3907 // ZJIT leaves uninitialized slots on the VM stack, so we cannot
3908 // safely use rb_gc_location on such slots.
3909 if (!rb_zjit_enabled_p) {
3910 for (i = 0; i < (long)(sp - p); i++) {
3911 rb_gc_update_moved(&p[i]);
3912 }
3913 }
3914
3915 while (cfp != limit_cfp) {
3916 const VALUE *ep = cfp->ep;
3917 rb_gc_update_moved(&cfp->self);
3918 if (CFP_ZJIT_FRAME_P(cfp)) {
3919 const zjit_jit_frame_t *jit_frame = CFP_ZJIT_FRAME(cfp);
3920 rb_zjit_jit_frame_update_references((zjit_jit_frame_t *)jit_frame);
3921 // materialize_block_code means cfp->block_code is lazy and may
3922 // still contain stale data from a previous frame. Otherwise it
3923 // was initialized by ZJIT and may have been written later by
3924 // vm_caller_setup_arg_block (ISEQ frames) or rb_iterate0 (C frames).
3925 if (!ZJIT_FRAME_MATERIALIZE_BLOCK_CODE_P(jit_frame)) {
3926 rb_gc_update_moved_ptr(&cfp->block_code);
3927 }
3928 }
3929 else {
3930 rb_gc_update_moved_ptr(&cfp->_iseq);
3931 rb_gc_update_moved_ptr(&cfp->block_code);
3932 }
3933
3934 if (!VM_ENV_LOCAL_P(ep)) {
3935 const VALUE *prev_ep = VM_ENV_PREV_EP(ep);
3936 if (VM_ENV_FLAGS(prev_ep, VM_ENV_FLAG_ESCAPED)) {
3937 rb_gc_update_moved((VALUE *)&prev_ep[VM_ENV_DATA_INDEX_ENV]);
3938 }
3939
3940 if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED)) {
3941 rb_gc_update_moved((VALUE *)&ep[VM_ENV_DATA_INDEX_ENV]);
3942 rb_gc_update_moved((VALUE *)&ep[VM_ENV_DATA_INDEX_ME_CREF]);
3943 }
3944 }
3945
3946 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
3947 }
3948 }
3949
3950 rb_gc_update_moved(&ec->storage);
3951
3952 rb_gc_update_moved(&ec->gen_fields_cache.obj);
3953 rb_gc_update_moved(&ec->gen_fields_cache.fields_obj);
3954}
3955
3956static enum rb_id_table_iterator_result
3957mark_local_storage_i(VALUE local, void *data)
3958{
3959 rb_gc_mark_ctx((const struct rb_gc_mark_ctx *)data, local);
3960 return ID_TABLE_CONTINUE;
3961}
3962
3963void
3964rb_execution_context_mark_ctx(const struct rb_gc_mark_ctx *ctx, const rb_execution_context_t *ec)
3965{
3966 /* mark VM stack */
3967 if (ec->vm_stack) {
3968 VM_ASSERT(ec->cfp);
3969 VALUE *p = ec->vm_stack;
3970 VALUE *sp = ec->cfp->sp;
3971 rb_control_frame_t *cfp = ec->cfp;
3972 rb_control_frame_t *limit_cfp = (void *)(ec->vm_stack + ec->vm_stack_size);
3973
3974 for (long i = 0; i < (long)(sp - p); i++) {
3975 if (rb_zjit_enabled_p) {
3976 // ZJIT leaves uninitialized slots on the VM stack, so we need
3977 // to mark such slots conservatively.
3978 rb_gc_mark_maybe_ctx(ctx, p[i]);
3979 }
3980 else {
3981 rb_gc_mark_movable_ctx(ctx, p[i]);
3982 }
3983 }
3984
3985 while (cfp != limit_cfp) {
3986 const VALUE *ep = cfp->ep;
3987 VM_ASSERT(!!VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED) == vm_ep_in_heap_p_(ec, ep));
3988
3989 rb_gc_mark_movable_ctx(ctx, cfp->self);
3990 if (CFP_ZJIT_FRAME_P(cfp)) {
3991 const zjit_jit_frame_t *jit_frame = CFP_ZJIT_FRAME(cfp);
3992 rb_gc_mark_movable_ctx(ctx, (VALUE)jit_frame->iseq);
3993 // materialize_block_code means cfp->block_code is lazy and may
3994 // still contain stale data from a previous frame. Otherwise it
3995 // was initialized by ZJIT and may have been written later by
3996 // vm_caller_setup_arg_block (ISEQ frames) or rb_iterate0 (C frames).
3997 if (!ZJIT_FRAME_MATERIALIZE_BLOCK_CODE_P(jit_frame)) {
3998 rb_gc_mark_movable_ctx(ctx, (VALUE)cfp->block_code);
3999 }
4000 }
4001 else {
4002 rb_gc_mark_movable_ctx(ctx, (VALUE)cfp->_iseq);
4003 rb_gc_mark_movable_ctx(ctx, (VALUE)cfp->block_code);
4004 }
4005
4006 if (VM_ENV_LOCAL_P(ep) && VM_ENV_BOXED_P(ep)) {
4007 const rb_box_t *box = VM_ENV_BOX(ep);
4008 if (BOX_USER_P(box)) {
4009 rb_gc_mark_movable_ctx(ctx, box->box_object);
4010 }
4011 }
4012
4013 if (!VM_ENV_LOCAL_P(ep)) {
4014 const VALUE *prev_ep = VM_ENV_PREV_EP(ep);
4015 if (VM_ENV_FLAGS(prev_ep, VM_ENV_FLAG_ESCAPED)) {
4016 rb_gc_mark_movable_ctx(ctx, prev_ep[VM_ENV_DATA_INDEX_ENV]);
4017 }
4018
4019 if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED)) {
4020 rb_gc_mark_movable_ctx(ctx, ep[VM_ENV_DATA_INDEX_ENV]);
4021 rb_gc_mark_ctx(ctx, ep[VM_ENV_DATA_INDEX_ME_CREF]);
4022 }
4023 }
4024
4025 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
4026 }
4027 }
4028
4029 /* mark machine stack */
4030 if (ec->machine.stack_start && ec->machine.stack_end &&
4031 /* marked for current ec at the first stage of marking */
4032 ec != rb_gc_get_ec()) {
4033 rb_gc_mark_machine_context_ctx(ctx, ec);
4034 }
4035
4036 rb_gc_mark_ctx(ctx, ec->errinfo);
4037 rb_gc_mark_ctx(ctx, ec->root_svar);
4038 if (ec->local_storage) {
4039 rb_id_table_foreach_values(ec->local_storage, mark_local_storage_i, (void *)ctx);
4040 }
4041 rb_gc_mark_ctx(ctx, ec->local_storage_recursive_hash);
4042 rb_gc_mark_ctx(ctx, ec->local_storage_recursive_hash_for_trace);
4043 rb_gc_mark_ctx(ctx, ec->private_const_reference);
4044
4045 rb_gc_mark_movable_ctx(ctx, ec->storage);
4046}
4047
4048void rb_fiber_mark_self(const struct rb_gc_mark_ctx *ctx, const rb_fiber_t *fib);
4049void rb_fiber_update_self(rb_fiber_t *fib);
4050void rb_threadptr_root_fiber_setup(rb_thread_t *th);
4051void rb_root_fiber_obj_setup(rb_thread_t *th, void *objspace);
4052void rb_threadptr_root_fiber_release(rb_thread_t *th);
4053
4054static void
4055thread_compact(void *ptr)
4056{
4057 rb_thread_t *th = ptr;
4058
4059 rb_gc_update_moved(&th->self);
4060}
4061
4062/* Mark the heap objects a thread owns (the caller handles ec and fiber). Split
4063 * out of thread_mark so that a local GC can root them straight from the Ractor's
4064 * local roots (rb_ractor_mark_local_roots). */
4065void
4066rb_thread_mark_owned_roots(const struct rb_gc_mark_ctx *ctx, rb_thread_t *th)
4067{
4068 /* mark ruby objects */
4069 switch (th->invoke_type) {
4070 case thread_invoke_type_proc:
4071 case thread_invoke_type_ractor_proc:
4072 rb_gc_mark_ctx(ctx, th->invoke_arg.proc.proc);
4073 rb_gc_mark_ctx(ctx, th->invoke_arg.proc.args);
4074 break;
4075 case thread_invoke_type_func:
4076 rb_gc_mark_maybe_ctx(ctx, (VALUE)th->invoke_arg.func.arg);
4077 break;
4078 default:
4079 break;
4080 }
4081
4082 rb_gc_mark_ctx(ctx, th->thgroup);
4083 rb_gc_mark_ctx(ctx, th->value);
4084 rb_gc_mark_ctx(ctx, th->pending_interrupt_queue);
4085 rb_gc_mark_ctx(ctx, th->pending_interrupt_mask_stack);
4086 rb_gc_mark_ctx(ctx, th->top_self);
4087 rb_gc_mark_ctx(ctx, th->top_wrapper);
4088 rb_gc_mark_ctx(ctx, th->last_status);
4089 rb_gc_mark_ctx(ctx, th->locking_mutex);
4090 rb_gc_mark_ctx(ctx, th->name);
4091 rb_gc_mark_ctx(ctx, th->scheduler);
4092
4093 rb_threadptr_interrupt_exec_task_mark(th);
4094}
4095
4096static void
4097thread_mark(void *ptr)
4098{
4099 rb_thread_t *th = ptr;
4100 const struct rb_gc_mark_ctx ctx = rb_gc_current_mark_ctx();
4101 RUBY_MARK_ENTER("thread");
4102
4103 // ec is null when setting up the thread in rb_threadptr_root_fiber_setup
4104 if (th->ec) {
4105 rb_fiber_mark_self(&ctx, th->ec->fiber_ptr);
4106 }
4107
4108 /* A live thread wrapper keeps its Ractor object alive (and through its dfree the
4109 * rb_ractor_t), so an inherited Thread keeps a dead Ractor alive just as it does
4110 * upstream. */
4111 if (th->ractor) rb_gc_mark_ctx(&ctx, rb_ractor_self(th->ractor));
4112 if (th->root_fiber) rb_fiber_mark_self(&ctx, th->root_fiber);
4113
4114 RUBY_ASSERT(th->ec == NULL || th->ec == rb_fiberptr_get_ec(th->ec->fiber_ptr));
4115
4116 rb_thread_mark_owned_roots(&ctx, th);
4117
4118 RUBY_MARK_LEAVE("thread");
4119}
4120
4121void rb_threadptr_sched_free(rb_thread_t *th); // thread_*.c
4122
4123static void
4124thread_free(void *ptr)
4125{
4126 rb_thread_t *th = ptr;
4127
4128 /* The final self collection sweeps the wrapper of the very thread running it; the
4129 * struct is still under that thread's feet (GET_EC resolves through it), so the
4130 * thread frees the struct itself at its last step (rb_ractor_postmortem_free). */
4131 if (th->ec != NULL && th->ec == rb_current_execution_context(false)) {
4132 th->self = 0;
4133 return;
4134 }
4135 rb_thread_free_body(ptr);
4136}
4137
4138void
4139rb_thread_free_body(void *ptr)
4140{
4141 rb_thread_t *th = ptr;
4142 RUBY_FREE_ENTER("thread");
4143
4144 rb_threadptr_sched_free(th);
4145 // destroyed here rather than during teardown: nothing can interrupt a
4146 // thread that is unreachable and off its Ractor's living set
4147 rb_native_mutex_destroy(&th->interrupt_lock);
4148
4149 if (th->locking_mutex != Qfalse) {
4150 rb_bug("thread_free: locking_mutex must be NULL (%p:%p)", (void *)th, (void *)th->locking_mutex);
4151 }
4152 if (th->keeping_mutexes != NULL) {
4153 rb_bug("thread_free: keeping_mutexes must be NULL (%p:%p)", (void *)th, (void *)th->keeping_mutexes);
4154 }
4155
4156 ruby_xfree(th->specific_storage);
4157
4158 if (th->vm && th->vm->ractor.main_thread == th) {
4159 RUBY_GC_INFO("MRI main thread\n");
4160 }
4161 else {
4162 // ruby_xfree(th->nt);
4163 // TODO: MN system collect nt, but without MN system it should be freed here.
4164 if (!th->main_thread) {
4165 ruby_xfree(th);
4166 }
4167 }
4168
4169 RUBY_FREE_LEAVE("thread");
4170}
4171
4172static size_t
4173thread_memsize(const void *ptr)
4174{
4175 const rb_thread_t *th = ptr;
4176 size_t size = sizeof(rb_thread_t);
4177
4178 if (!th->root_fiber) {
4179 size += th->ec->vm_stack_size * sizeof(VALUE);
4180 }
4181 if (th->ec->local_storage) {
4182 size += rb_id_table_memsize(th->ec->local_storage);
4183 }
4184 return size;
4185}
4186
4187#define thread_data_type ruby_threadptr_data_type
4188const rb_data_type_t ruby_threadptr_data_type = {
4189 "VM/thread",
4190 {
4191 thread_mark,
4192 thread_free,
4193 thread_memsize,
4194 thread_compact,
4195 },
4196 0, 0, RUBY_TYPED_THREAD_SAFE_FREE
4197};
4198
4199VALUE
4200rb_obj_is_thread(VALUE obj)
4201{
4202 return RBOOL(rb_typeddata_is_kind_of(obj, &thread_data_type));
4203}
4204
4205static VALUE
4206thread_alloc(VALUE klass, void *objspace)
4207{
4208 return rb_data_typed_object_zalloc_in_objspace(objspace, klass, sizeof(rb_thread_t), &thread_data_type);
4209}
4210
4211void
4212rb_ec_set_vm_stack(rb_execution_context_t *ec, VALUE *stack, size_t size)
4213{
4214 ec->vm_stack = stack;
4215 ec->vm_stack_size = size;
4216}
4217
4218void
4219rb_ec_initialize_vm_stack(rb_execution_context_t *ec, VALUE *stack, size_t size)
4220{
4221 rb_ec_set_vm_stack(ec, stack, size);
4222
4223#if VM_CHECK_MODE > 0
4224 MEMZERO(stack, VALUE, size); // malloc memory could have the VM canary in it
4225#endif
4226
4227 ec->cfp = (void *)(ec->vm_stack + ec->vm_stack_size);
4228
4229 vm_push_frame(ec,
4230 NULL /* dummy iseq */,
4231 VM_FRAME_MAGIC_DUMMY | VM_ENV_FLAG_LOCAL | VM_FRAME_FLAG_FINISH | VM_FRAME_FLAG_CFRAME /* dummy frame */,
4232 Qnil /* dummy self */, VM_BLOCK_HANDLER_NONE /* dummy block ptr */,
4233 0 /* dummy cref/me */,
4234 0 /* dummy pc */, ec->vm_stack, 0, 0
4235 );
4236}
4237
4238void
4239rb_ec_clear_vm_stack(rb_execution_context_t *ec)
4240{
4241 // set cfp to NULL before clearing the stack in case `thread_profile_frames`
4242 // gets called in this middle of `rb_ec_set_vm_stack` via signal handler.
4243 ec->cfp = NULL;
4244
4245 // Ensure the store to ec->cfp above is not reordered after the stores below.
4246#if defined HAVE_DECL_ATOMIC_SIGNAL_FENCE && HAVE_DECL_ATOMIC_SIGNAL_FENCE
4247 atomic_signal_fence(memory_order_seq_cst);
4248#endif
4249
4250 rb_ec_set_vm_stack(ec, NULL, 0);
4251}
4252
4253void
4254rb_ec_close(rb_execution_context_t *ec)
4255{
4256 // Fiber storage is not accessible from outside the running fiber, so it is safe to clear it here.
4257 ec->storage = Qnil;
4258}
4259
4260static void
4261th_init(rb_thread_t *th, VALUE self, rb_vm_t *vm)
4262{
4263 const rb_box_t *box = rb_current_box();
4264
4265 th->self = self;
4266
4267 ccan_list_head_init(&th->interrupt_exec_tasks);
4268 // initialized here (not at thread creation) so that every Thread object
4269 // -- including allocated-but-never-started ones -- owns a valid mutex:
4270 // thread_free destroys it unconditionally
4271 rb_native_mutex_initialize(&th->interrupt_lock);
4272
4273 rb_threadptr_root_fiber_setup(th);
4274
4275 /* All threads are blocking until a non-blocking fiber is scheduled */
4276 th->blocking = 1;
4277 th->scheduler = Qnil;
4278
4279 if (self == 0) {
4280 size_t size = vm->default_params.thread_vm_stack_size / sizeof(VALUE);
4281 VALUE *stack = ALLOC_N(VALUE, size);
4282 rb_ec_initialize_vm_stack(th->ec, stack, size);
4283 rb_thread_malloc_stack_set(th, stack, size);
4284 }
4285 else {
4286 VM_ASSERT(th->ec->cfp == NULL);
4287 VM_ASSERT(th->ec->vm_stack == NULL);
4288 VM_ASSERT(th->ec->vm_stack_size == 0);
4289 }
4290
4291 th->status = THREAD_RUNNABLE;
4292 th->last_status = Qnil;
4293 th->top_wrapper = 0;
4294 if (box->top_self) {
4295 th->top_self = box->top_self;
4296 }
4297 else {
4298 th->top_self = 0;
4299 }
4300 th->value = Qundef;
4301
4302 th->ec->errinfo = Qnil;
4303 th->ec->root_svar = Qfalse;
4304 th->ec->local_storage_recursive_hash = Qnil;
4305 th->ec->local_storage_recursive_hash_for_trace = Qnil;
4306
4307 th->ec->storage = Qnil;
4308 th->ec->ractor_id = rb_ractor_id(th->ractor);
4309
4310#if OPT_CALL_THREADED_CODE
4311 th->retval = Qundef;
4312#endif
4313 th->name = Qnil;
4314 th->report_on_exception = vm->thread_report_on_exception;
4315 th->ext_config.ractor_safe = true;
4316
4317#if USE_RUBY_DEBUG_LOG
4318 static rb_atomic_t thread_serial = 1;
4319 th->serial = RUBY_ATOMIC_FETCH_ADD(thread_serial, 1);
4320
4321 RUBY_DEBUG_LOG("th:%u", th->serial);
4322#endif
4323}
4324
4325VALUE
4326rb_thread_alloc_in_objspace(VALUE klass, void *objspace)
4327{
4328 VALUE self = thread_alloc(klass, objspace);
4329 rb_thread_t *target_th = rb_thread_ptr(self);
4330 target_th->ractor = GET_RACTOR();
4331 th_init(target_th, self, target_th->vm = GET_VM());
4332 rb_root_fiber_obj_setup(target_th, objspace);
4333 return self;
4334}
4335
4336VALUE
4337rb_thread_alloc(VALUE klass)
4338{
4339 return rb_thread_alloc_in_objspace(klass, GET_RACTOR()->objspace);
4340}
4341
4342#define REWIND_CFP(expr) do { \
4343 rb_execution_context_t *ec__ = GET_EC(); \
4344 VALUE *const curr_sp = (ec__->cfp++)->sp; \
4345 VALUE *const saved_sp = ec__->cfp->sp; \
4346 ec__->cfp->sp = curr_sp; \
4347 expr; \
4348 (ec__->cfp--)->sp = saved_sp; \
4349} while (0)
4350
4351static VALUE
4352m_core_set_method_alias(VALUE self, VALUE cbase, VALUE sym1, VALUE sym2)
4353{
4354 REWIND_CFP({
4355 rb_alias(cbase, SYM2ID(sym1), SYM2ID(sym2));
4356 });
4357 return Qnil;
4358}
4359
4360static VALUE
4361m_core_set_variable_alias(VALUE self, VALUE sym1, VALUE sym2)
4362{
4363 REWIND_CFP({
4364 rb_alias_variable(SYM2ID(sym1), SYM2ID(sym2));
4365 });
4366 return Qnil;
4367}
4368
4369static VALUE
4370m_core_undef_method(VALUE self, VALUE cbase, VALUE sym)
4371{
4372 REWIND_CFP({
4373 ID mid = SYM2ID(sym);
4374 rb_undef(cbase, mid);
4375 rb_clear_method_cache(self, mid);
4376 });
4377 return Qnil;
4378}
4379
4380static VALUE
4381m_core_set_postexe(VALUE self)
4382{
4383 rb_ractor_ensure_main_ractor("can not use END{} in non-main Ractors");
4384 rb_set_end_proc(rb_call_end_proc, rb_block_proc());
4385 return Qnil;
4386}
4387
4388static VALUE
4389m_core_iseq_path(VALUE self, VALUE frozen_string_literal_option_value)
4390{
4391 rb_execution_context_t *ec = GET_EC();
4392 rb_control_frame_t *cfp = ec->cfp;
4393 cfp = vm_get_ruby_level_caller_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp));
4394 if (cfp == NULL) {
4395 return Qnil;
4396 }
4397 const rb_iseq_t *iseq = CFP_ISEQ(cfp);
4398 VALUE path = rb_iseq_path(iseq);
4399 if (RTEST(path)) {
4400 int frozen_string_literal_option = NUM2INT(frozen_string_literal_option_value);
4401 switch (frozen_string_literal_option) {
4402 case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
4403 RUBY_ASSERT(OBJ_FROZEN(path));
4404 break;
4405 case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
4406 path = rb_str_resurrect(path);
4407 break;
4408 case ISEQ_FROZEN_STRING_LITERAL_UNSET:
4409 path = rb_ec_str_resurrect(ec, path, true);
4410 break;
4411 default:
4413 }
4414 }
4415 return path;
4416}
4417
4418static VALUE
4419core_hash_merge(VALUE hash, long argc, const VALUE *argv, bool dup)
4420{
4421 if (NIL_P(hash)) {
4422 hash = rb_cHash_empty_frozen;
4423 }
4424 else {
4425 hash = rb_to_hash_type(hash);
4426 Check_Type(hash, T_HASH);
4427 }
4428
4429 return rb_hash_merge2_bulk(hash, argc, argv, dup);
4430}
4431
4432static VALUE
4433m_core_hash_merge_ptr(int argc, VALUE *argv, VALUE recv)
4434{
4435 VALUE hash = argv[0];
4436 VM_ASSERT(argc % 2 == 1);
4437
4438 REWIND_CFP(hash = core_hash_merge(hash, argc - 1, argv + 1, true));
4439
4440 return hash;
4441}
4442
4443static VALUE
4444m_core_hash_merge_bang_ptr(int argc, VALUE *argv, VALUE recv)
4445{
4446 VALUE hash = argv[0];
4447 VM_ASSERT(argc % 2 == 1);
4448
4449 REWIND_CFP(hash = core_hash_merge(hash, argc - 1, argv + 1, false));
4450
4451 return hash;
4452}
4453
4454static VALUE
4455core_hash_merge_kwd(VALUE hash, VALUE kw, bool dup)
4456{
4457 kw = rb_to_hash_type(kw);
4458 if (NIL_P(hash)) {
4459 return dup ? rb_hash_resurrect(kw) : kw;
4460 }
4461 else {
4462 hash = rb_to_hash_type(hash);
4463 Check_Type(hash, T_HASH);
4464 return rb_hash_merge2(hash, kw, dup);
4465 }
4466}
4467
4468static VALUE
4469m_core_hash_merge_kwd(VALUE recv, VALUE hash, VALUE kw)
4470{
4471 // We don't own `hash` so we can't mutate it, nor just return it.
4472 if (NIL_P(kw)) {
4473 if (NIL_P(hash)) {
4474 // If we knew that we're dealing with keyword arguments, and not a hash literal,
4475 // we could return nil here.
4476 return rb_hash_new();
4477 }
4478
4479 hash = rb_hash_resurrect(hash);
4480 }
4481 else {
4482 REWIND_CFP(hash = core_hash_merge_kwd(hash, kw, true));
4483 }
4484 VM_ASSERT(CLASS_OF(hash));
4485 return hash;
4486}
4487
4488static VALUE
4489m_core_hash_merge_bang_kwd(VALUE recv, VALUE hash, VALUE kw)
4490{
4491 // We own `hash` we should mutate it in place if possible.
4492 if (NIL_P(kw)) {
4493 if (NIL_P(hash)) {
4494 hash = rb_hash_new();
4495 }
4496 else {
4497 hash = rb_hash_resurrect(hash);
4498 }
4499 }
4500 else {
4501 REWIND_CFP(hash = core_hash_merge_kwd(hash, kw, false));
4502 }
4503 VM_ASSERT(CLASS_OF(hash));
4504 return hash;
4505}
4506
4507static VALUE
4508m_core_make_shareable(VALUE recv, VALUE obj)
4509{
4510 return rb_ractor_make_shareable(obj);
4511}
4512
4513static VALUE
4514m_core_make_shareable_copy(VALUE recv, VALUE obj)
4515{
4517}
4518
4519static VALUE
4520m_core_ensure_shareable(VALUE recv, VALUE obj, VALUE name)
4521{
4522 return rb_ractor_ensure_shareable(obj, name);
4523}
4524
4525extern VALUE *rb_gc_stack_start;
4526extern size_t rb_gc_stack_maxsize;
4527
4528/* debug functions */
4529
4530/* :nodoc: */
4531static VALUE
4532sdr(VALUE self)
4533{
4534 rb_vm_bugreport(NULL, stderr);
4535 return Qnil;
4536}
4537
4538/* :nodoc: */
4539static VALUE
4540nsdr(VALUE self)
4541{
4542 VALUE ary = rb_ary_new();
4543#ifdef HAVE_BACKTRACE
4544#include <execinfo.h>
4545#define MAX_NATIVE_TRACE 1024
4546 static void *trace[MAX_NATIVE_TRACE];
4547 int n = (int)backtrace(trace, MAX_NATIVE_TRACE);
4548 char **syms = backtrace_symbols(trace, n);
4549 int i;
4550
4551 if (syms == 0) {
4552 rb_memerror();
4553 }
4554
4555 for (i=0; i<n; i++) {
4556 rb_ary_push(ary, rb_str_new2(syms[i]));
4557 }
4558 free(syms); /* OK */
4559#endif
4560 return ary;
4561}
4562
4563#if VM_COLLECT_USAGE_DETAILS
4564static VALUE usage_analysis_insn_start(VALUE self);
4565static VALUE usage_analysis_operand_start(VALUE self);
4566static VALUE usage_analysis_register_start(VALUE self);
4567static VALUE usage_analysis_insn_stop(VALUE self);
4568static VALUE usage_analysis_operand_stop(VALUE self);
4569static VALUE usage_analysis_register_stop(VALUE self);
4570static VALUE usage_analysis_insn_running(VALUE self);
4571static VALUE usage_analysis_operand_running(VALUE self);
4572static VALUE usage_analysis_register_running(VALUE self);
4573static VALUE usage_analysis_insn_clear(VALUE self);
4574static VALUE usage_analysis_operand_clear(VALUE self);
4575static VALUE usage_analysis_register_clear(VALUE self);
4576#endif
4577
4578static VALUE
4579f_raise(int c, VALUE *v, VALUE _)
4580{
4581 return rb_f_raise(c, v);
4582}
4583
4584static VALUE
4585f_proc(VALUE _)
4586{
4587 return rb_block_proc();
4588}
4589
4590static VALUE
4591f_lambda(VALUE _)
4592{
4593 return rb_block_lambda();
4594}
4595
4596static VALUE
4597f_sprintf(int c, const VALUE *v, VALUE _)
4598{
4599 return rb_f_sprintf(c, v);
4600}
4601
4602/* :nodoc: */
4603static VALUE
4604vm_mtbl(VALUE self, VALUE obj, VALUE sym)
4605{
4606 vm_mtbl_dump(CLASS_OF(obj), RTEST(sym) ? SYM2ID(sym) : 0);
4607 return Qnil;
4608}
4609
4610/* :nodoc: */
4611static VALUE
4612vm_mtbl2(VALUE self, VALUE obj, VALUE sym)
4613{
4614 vm_mtbl_dump(obj, RTEST(sym) ? SYM2ID(sym) : 0);
4615 return Qnil;
4616}
4617
4618/*
4619 * call-seq:
4620 * RubyVM.keep_script_lines -> true or false
4621 *
4622 * Return current +keep_script_lines+ status. Now it only returns
4623 * +true+ of +false+, but it can return other objects in future.
4624 *
4625 * Note that this is an API for ruby internal use, debugging,
4626 * and research. Do not use this for any other purpose.
4627 * The compatibility is not guaranteed.
4628 */
4629static VALUE
4630vm_keep_script_lines(VALUE self)
4631{
4632 return RBOOL(ruby_vm_keep_script_lines);
4633}
4634
4635/*
4636 * call-seq:
4637 * RubyVM.keep_script_lines = true / false
4638 *
4639 * It set +keep_script_lines+ flag. If the flag is set, all
4640 * loaded scripts are recorded in a interpreter process.
4641 *
4642 * Note that this is an API for ruby internal use, debugging,
4643 * and research. Do not use this for any other purpose.
4644 * The compatibility is not guaranteed.
4645 */
4646static VALUE
4647vm_keep_script_lines_set(VALUE self, VALUE flags)
4648{
4649 ruby_vm_keep_script_lines = RTEST(flags);
4650 return flags;
4651}
4652
4653void
4654Init_VM(void)
4655{
4656 VALUE opts;
4657 VALUE klass;
4658 VALUE fcore;
4659
4660 /*
4661 * Document-class: RubyVM
4662 *
4663 * The RubyVM module only exists on MRI. +RubyVM+ is not defined in
4664 * other Ruby implementations such as JRuby and TruffleRuby.
4665 *
4666 * The RubyVM module provides some access to MRI internals.
4667 * This module is for very limited purposes, such as debugging,
4668 * prototyping, and research. Normal users must not use it.
4669 * This module is not portable between Ruby implementations.
4670 */
4671 rb_cRubyVM = rb_define_class("RubyVM", rb_cObject);
4672 rb_undef_alloc_func(rb_cRubyVM);
4673 rb_undef_method(CLASS_OF(rb_cRubyVM), "new");
4674 rb_define_singleton_method(rb_cRubyVM, "stat", vm_stat, -1);
4675 rb_define_singleton_method(rb_cRubyVM, "keep_script_lines", vm_keep_script_lines, 0);
4676 rb_define_singleton_method(rb_cRubyVM, "keep_script_lines=", vm_keep_script_lines_set, 1);
4677
4678#if USE_DEBUG_COUNTER
4679 rb_define_singleton_method(rb_cRubyVM, "reset_debug_counters", rb_debug_counter_reset, 0);
4680 rb_define_singleton_method(rb_cRubyVM, "show_debug_counters", rb_debug_counter_show, 0);
4681#endif
4682
4683 /* FrozenCore (hidden) */
4685 rb_set_class_path(fcore, rb_cRubyVM, "FrozenCore");
4686 rb_vm_register_global_object(rb_mod_name(fcore));
4687 klass = rb_singleton_class(fcore);
4688 rb_define_method_id(klass, id_core_set_method_alias, m_core_set_method_alias, 3);
4689 rb_define_method_id(klass, id_core_set_variable_alias, m_core_set_variable_alias, 2);
4690 rb_define_method_id(klass, id_core_undef_method, m_core_undef_method, 2);
4691 rb_define_method_id(klass, id_core_set_postexe, m_core_set_postexe, 0);
4692 rb_define_method_id(klass, id_core_iseq_path, m_core_iseq_path, 1);
4693 rb_define_method_id(klass, id_core_hash_merge_ptr, m_core_hash_merge_ptr, -1);
4694 rb_define_method_id(klass, id_core_hash_merge_bang_ptr, m_core_hash_merge_bang_ptr, -1);
4695 rb_define_method_id(klass, id_core_hash_merge_kwd, m_core_hash_merge_kwd, 2);
4696 rb_define_method_id(klass, id_core_hash_merge_bang_kwd, m_core_hash_merge_bang_kwd, 2);
4697 rb_define_method_id(klass, id_core_raise, f_raise, -1);
4698 rb_define_method_id(klass, id_core_sprintf, f_sprintf, -1);
4699 rb_define_method_id(klass, idProc, f_proc, 0);
4700 rb_define_method_id(klass, idLambda, f_lambda, 0);
4701 rb_define_method(klass, "make_shareable", m_core_make_shareable, 1);
4702 rb_define_method(klass, "make_shareable_copy", m_core_make_shareable_copy, 1);
4703 rb_define_method(klass, "ensure_shareable", m_core_ensure_shareable, 2);
4704 rb_obj_freeze(fcore);
4705 RBASIC_CLEAR_CLASS(klass);
4706 rb_obj_freeze(klass);
4707 rb_vm_register_global_object(fcore);
4708 rb_mRubyVMFrozenCore = fcore;
4709
4710 /*
4711 * Document-class: Thread
4712 *
4713 * Threads are the Ruby implementation for a concurrent programming model.
4714 *
4715 * Programs that require multiple threads of execution are a perfect
4716 * candidate for Ruby's Thread class.
4717 *
4718 * For example, we can create a new thread separate from the main thread's
4719 * execution using ::new.
4720 *
4721 * thr = Thread.new { puts "What's the big deal" }
4722 *
4723 * Then we are able to pause the execution of the main thread and allow
4724 * our new thread to finish, using #join:
4725 *
4726 * thr.join #=> "What's the big deal"
4727 *
4728 * If we don't call +thr.join+ before the main thread terminates, then all
4729 * other threads including +thr+ will be killed.
4730 *
4731 * Alternatively, you can use an array for handling multiple threads at
4732 * once, like in the following example:
4733 *
4734 * threads = []
4735 * threads << Thread.new { puts "What's the big deal" }
4736 * threads << Thread.new { 3.times { puts "Threads are fun!" } }
4737 *
4738 * After creating a few threads we wait for them all to finish
4739 * consecutively.
4740 *
4741 * threads.each { |thr| thr.join }
4742 *
4743 * To retrieve the last value of a thread, use #value
4744 *
4745 * thr = Thread.new { sleep 1; "Useful value" }
4746 * thr.value #=> "Useful value"
4747 *
4748 * === Thread initialization
4749 *
4750 * In order to create new threads, Ruby provides ::new, ::start, and
4751 * ::fork. A block must be provided with each of these methods, otherwise
4752 * a ThreadError will be raised.
4753 *
4754 * When subclassing the Thread class, the +initialize+ method of your
4755 * subclass will be ignored by ::start and ::fork. Otherwise, be sure to
4756 * call super in your +initialize+ method.
4757 *
4758 * === Thread termination
4759 *
4760 * For terminating threads, Ruby provides a variety of ways to do this.
4761 *
4762 * The class method ::kill, is meant to exit a given thread:
4763 *
4764 * thr = Thread.new { sleep }
4765 * Thread.kill(thr) # sends exit() to thr
4766 *
4767 * Alternatively, you can use the instance method #exit, or any of its
4768 * aliases #kill or #terminate.
4769 *
4770 * thr.exit
4771 *
4772 * === Thread status
4773 *
4774 * Ruby provides a few instance methods for querying the state of a given
4775 * thread. To get a string with the current thread's state use #status
4776 *
4777 * thr = Thread.new { sleep }
4778 * thr.status # => "sleep"
4779 * thr.exit
4780 * thr.status # => false
4781 *
4782 * You can also use #alive? to tell if the thread is running or sleeping,
4783 * and #stop? if the thread is dead or sleeping.
4784 *
4785 * === Thread variables and scope
4786 *
4787 * Since threads are created with blocks, the same rules apply to other
4788 * Ruby blocks for variable scope. Any local variables created within this
4789 * block are accessible to only this thread.
4790 *
4791 * ==== Fiber-local vs. Thread-local
4792 *
4793 * Each fiber has its own bucket for Thread#[] storage. When you set a
4794 * new fiber-local it is only accessible within this Fiber. To illustrate:
4795 *
4796 * Thread.new {
4797 * Thread.current[:foo] = "bar"
4798 * Fiber.new {
4799 * p Thread.current[:foo] # => nil
4800 * }.resume
4801 * }.join
4802 *
4803 * This example uses #[] for getting and #[]= for setting fiber-locals,
4804 * you can also use #keys to list the fiber-locals for a given
4805 * thread and #key? to check if a fiber-local exists.
4806 *
4807 * When it comes to thread-locals, they are accessible within the entire
4808 * scope of the thread. Given the following example:
4809 *
4810 * Thread.new{
4811 * Thread.current.thread_variable_set(:foo, 1)
4812 * p Thread.current.thread_variable_get(:foo) # => 1
4813 * Fiber.new{
4814 * Thread.current.thread_variable_set(:foo, 2)
4815 * p Thread.current.thread_variable_get(:foo) # => 2
4816 * }.resume
4817 * p Thread.current.thread_variable_get(:foo) # => 2
4818 * }.join
4819 *
4820 * You can see that the thread-local +:foo+ carried over into the fiber
4821 * and was changed to +2+ by the end of the thread.
4822 *
4823 * This example makes use of #thread_variable_set to create new
4824 * thread-locals, and #thread_variable_get to reference them.
4825 *
4826 * There is also #thread_variables to list all thread-locals, and
4827 * #thread_variable? to check if a given thread-local exists.
4828 *
4829 * === Exception handling
4830 *
4831 * When an unhandled exception is raised inside a thread, it will
4832 * terminate. By default, this exception will not propagate to other
4833 * threads. The exception is stored and when another thread calls #value
4834 * or #join, the exception will be re-raised in that thread.
4835 *
4836 * t = Thread.new{ raise 'something went wrong' }
4837 * t.value #=> RuntimeError: something went wrong
4838 *
4839 * An exception can be raised from outside the thread using the
4840 * Thread#raise instance method, which takes the same parameters as
4841 * Kernel#raise.
4842 *
4843 * Setting Thread.abort_on_exception = true, Thread#abort_on_exception =
4844 * true, or $DEBUG = true will cause a subsequent unhandled exception
4845 * raised in a thread to be automatically re-raised in the main thread.
4846 *
4847 * With the addition of the class method ::handle_interrupt, you can now
4848 * handle exceptions asynchronously with threads.
4849 *
4850 * === Scheduling
4851 *
4852 * Ruby provides a few ways to support scheduling threads in your program.
4853 *
4854 * The first way is by using the class method ::stop, to put the current
4855 * running thread to sleep and schedule the execution of another thread.
4856 *
4857 * Once a thread is asleep, you can use the instance method #wakeup to
4858 * mark your thread as eligible for scheduling.
4859 *
4860 * You can also try ::pass, which attempts to pass execution to another
4861 * thread but is dependent on the OS whether a running thread will switch
4862 * or not. The same goes for #priority, which lets you hint to the thread
4863 * scheduler which threads you want to take precedence when passing
4864 * execution. This method is also dependent on the OS and may be ignored
4865 * on some platforms.
4866 *
4867 */
4868 rb_cThread = rb_define_class("Thread", rb_cObject);
4870
4871#if VM_COLLECT_USAGE_DETAILS
4872 /* ::RubyVM::USAGE_ANALYSIS_* */
4873#define define_usage_analysis_hash(name) /* shut up rdoc -C */ \
4874 rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_" #name, rb_hash_new())
4875 define_usage_analysis_hash(INSN);
4876 define_usage_analysis_hash(REGS);
4877 define_usage_analysis_hash(INSN_BIGRAM);
4878
4879 rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_INSN_START", usage_analysis_insn_start, 0);
4880 rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_OPERAND_START", usage_analysis_operand_start, 0);
4881 rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_REGISTER_START", usage_analysis_register_start, 0);
4882 rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_INSN_STOP", usage_analysis_insn_stop, 0);
4883 rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_OPERAND_STOP", usage_analysis_operand_stop, 0);
4884 rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_REGISTER_STOP", usage_analysis_register_stop, 0);
4885 rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_INSN_RUNNING", usage_analysis_insn_running, 0);
4886 rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_OPERAND_RUNNING", usage_analysis_operand_running, 0);
4887 rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_REGISTER_RUNNING", usage_analysis_register_running, 0);
4888 rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_INSN_CLEAR", usage_analysis_insn_clear, 0);
4889 rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_OPERAND_CLEAR", usage_analysis_operand_clear, 0);
4890 rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_REGISTER_CLEAR", usage_analysis_register_clear, 0);
4891#endif
4892
4893 /* ::RubyVM::OPTS
4894 * An Array of VM build options.
4895 * This constant is MRI specific.
4896 */
4897 rb_define_const(rb_cRubyVM, "OPTS", opts = rb_ary_new());
4898
4899#if OPT_DIRECT_THREADED_CODE
4900 rb_ary_push(opts, rb_str_new2("direct threaded code"));
4901#elif OPT_TOKEN_THREADED_CODE
4902 rb_ary_push(opts, rb_str_new2("token threaded code"));
4903#elif OPT_CALL_THREADED_CODE
4904 rb_ary_push(opts, rb_str_new2("call threaded code"));
4905#endif
4906
4907#if OPT_OPERANDS_UNIFICATION
4908 rb_ary_push(opts, rb_str_new2("operands unification"));
4909#endif
4910#if OPT_INSTRUCTIONS_UNIFICATION
4911 rb_ary_push(opts, rb_str_new2("instructions unification"));
4912#endif
4913#if OPT_INLINE_METHOD_CACHE
4914 rb_ary_push(opts, rb_str_new2("inline method cache"));
4915#endif
4916
4917 /* ::RubyVM::INSTRUCTION_NAMES
4918 * A list of bytecode instruction names in MRI.
4919 * This constant is MRI specific.
4920 */
4921 rb_define_const(rb_cRubyVM, "INSTRUCTION_NAMES", rb_insns_name_array());
4922
4923 /* ::RubyVM::DEFAULT_PARAMS
4924 * This constant exposes the VM's default parameters.
4925 * Note that changing these values does not affect VM execution.
4926 * Specification is not stable and you should not depend on this value.
4927 * Of course, this constant is MRI specific.
4928 */
4929 rb_define_const(rb_cRubyVM, "DEFAULT_PARAMS", vm_default_params());
4930
4931 /* debug functions ::RubyVM::SDR(), ::RubyVM::NSDR() */
4932#if VMDEBUG
4933 rb_define_singleton_method(rb_cRubyVM, "SDR", sdr, 0);
4934 rb_define_singleton_method(rb_cRubyVM, "NSDR", nsdr, 0);
4935 rb_define_singleton_method(rb_cRubyVM, "mtbl", vm_mtbl, 2);
4936 rb_define_singleton_method(rb_cRubyVM, "mtbl2", vm_mtbl2, 2);
4937#else
4938 (void)sdr;
4939 (void)nsdr;
4940 (void)vm_mtbl;
4941 (void)vm_mtbl2;
4942#endif
4943
4944 /* VM bootstrap: phase 2 */
4945 {
4946 rb_vm_t *vm = ruby_current_vm_ptr;
4947 rb_thread_t *th = GET_THREAD();
4948 VALUE filename = rb_fstring_lit("<main>");
4949 const rb_iseq_t *iseq = rb_iseq_new(Qnil, filename, filename, Qnil, 0, ISEQ_TYPE_TOP);
4950
4951 // Ractor setup
4952 rb_ractor_main_setup(vm, th->ractor, th);
4953
4954 /* create vm object */
4955 vm->self = TypedData_Wrap_Struct(rb_cRubyVM, &vm_data_type, vm);
4956
4957 /* create main thread */
4958 th->self = TypedData_Wrap_Struct(rb_cThread, &thread_data_type, th);
4959 vm->ractor.main_thread = th;
4960 vm->ractor.main_ractor = th->ractor;
4961 th->vm = vm;
4962 th->top_wrapper = 0;
4963 th->top_self = rb_vm_top_self();
4964
4965 rb_root_fiber_obj_setup(th, th->ractor->objspace);
4966
4967 rb_vm_register_global_object((VALUE)iseq);
4968 th->ec->cfp->_iseq = iseq;
4969 th->ec->cfp->pc = ISEQ_BODY(iseq)->iseq_encoded;
4970 th->ec->cfp->self = th->top_self;
4971
4972 VM_ENV_FLAGS_UNSET(th->ec->cfp->ep, VM_FRAME_FLAG_CFRAME);
4973 VM_STACK_ENV_WRITE(th->ec->cfp->ep, VM_ENV_DATA_INDEX_ME_CREF, (VALUE)vm_cref_new(rb_cObject, METHOD_VISI_PRIVATE, FALSE, NULL, FALSE, FALSE));
4974
4975 /*
4976 * The Binding of the top level scope
4977 */
4978 rb_define_global_const("TOPLEVEL_BINDING", rb_binding_new());
4979
4980#ifdef _WIN32
4981 rb_objspace_gc_enable(vm->ractor.main_ractor->objspace);
4982#endif
4983 }
4984
4985 rb_block_param_proxy = rb_obj_alloc(rb_cObject);
4986 VALUE proxy_singleton = rb_singleton_class(rb_block_param_proxy);
4987 rb_add_method_optimized(proxy_singleton, idCall, OPTIMIZED_METHOD_TYPE_BLOCK_CALL, 0, METHOD_VISI_PUBLIC);
4988 rb_add_method_optimized(proxy_singleton, idAREF, OPTIMIZED_METHOD_TYPE_BLOCK_CALL, 0, METHOD_VISI_PUBLIC);
4989 rb_add_method_optimized(proxy_singleton, idYield, OPTIMIZED_METHOD_TYPE_BLOCK_CALL, 0, METHOD_VISI_PUBLIC);
4990 rb_add_method_optimized(proxy_singleton, idEqq, OPTIMIZED_METHOD_TYPE_BLOCK_CALL, 0, METHOD_VISI_PUBLIC);
4991 rb_ractor_make_shareable(rb_block_param_proxy);
4992 rb_vm_register_global_object(rb_block_param_proxy);
4993
4994 /* vm_backtrace.c */
4995 Init_vm_backtrace();
4996}
4997
4998void
4999rb_vm_set_progname(VALUE filename)
5000{
5001 rb_thread_t *th = GET_VM()->ractor.main_thread;
5002 rb_control_frame_t *cfp = (void *)(th->ec->vm_stack + th->ec->vm_stack_size);
5003 --cfp;
5004
5005 filename = rb_str_new_frozen(filename);
5006 rb_iseq_pathobj_set(CFP_ISEQ(cfp), filename, rb_iseq_realpath(CFP_ISEQ(cfp)));
5007}
5008
5009extern const struct st_hash_type rb_fstring_hash_type;
5010
5011static rb_vm_t _vm;
5012static rb_thread_t _main_thread = {
5013 .vm = &_vm,
5014 .main_thread = 1,
5015};
5016
5017void
5018Init_BareVM(void)
5019{
5020 /* VM bootstrap: phase 1 */
5021 rb_vm_t *vm = &_vm;
5022 rb_thread_t *th = &_main_thread;
5023
5024 // setup the VM
5025 vm_init2(vm);
5026
5027 ruby_current_vm_ptr = vm;
5028 /* The boot objspace belongs to the main Ractor, so the main Ractor has to exist
5029 * before rb_gc_init_objspaces allocates it. */
5030 vm->ractor.main_ractor = rb_ractor_main_alloc();
5031 rb_native_mutex_initialize(&vm->gc.registered_addrs.lock);
5032 rb_gc_init_objspaces();
5033 vm->ractor.main_ractor->newobj_cache = rb_gc_ractor_cache_alloc(vm->ractor.main_ractor);
5034 rb_id_table_init(&vm->negative_cme_table, 16);
5035 st_init_existing_numtable_with_size(&vm->overloaded_cme_table, 0);
5036 st_init_existing_strtable_with_size(&vm->static_ext_inits, 0);
5037 set_init_embedded_numtable_with_size(&vm->unused_block_warning_table, 0);
5038 vm->global_hooks.type = hook_list_type_global;
5039
5040 // setup main thread
5041 th->nt = ZALLOC(struct rb_native_thread);
5042 th->ractor = vm->ractor.main_ractor;
5043 Init_native_thread(th);
5044 rb_jit_cont_init();
5045 th_init(th, 0, vm);
5046
5047 rb_ractor_set_current_ec(th->ractor, th->ec);
5048
5049 /* n.b. native_main_thread_stack_top is set by the INIT_STACK macro */
5050 ruby_thread_init_stack(th, native_main_thread_stack_top);
5051
5052 // setup ractor system
5053 rb_native_mutex_initialize(&vm->ractor.sync.lock);
5054 rb_native_cond_initialize(&vm->ractor.sync.terminate_cond);
5055 rb_native_mutex_initialize(&vm->ractor.generic_fields_lock);
5056 vm->gc.orphan_merge_pjob = POSTPONED_JOB_HANDLE_INVALID;
5057
5058 vm_opt_method_def_table = st_init_numtable();
5059 vm_opt_mid_table = st_init_numtable();
5060
5061}
5062
5063void
5065{
5066 native_main_thread_stack_top = addr;
5067}
5068
5069#ifndef _WIN32
5070#include <unistd.h>
5071#include <sys/mman.h>
5072#endif
5073
5074
5075void
5076rb_vm_register_global_object(VALUE obj)
5077{
5079 if (RB_SPECIAL_CONST_P(obj)) {
5080 return;
5081 }
5082
5083 switch (RB_BUILTIN_TYPE(obj)) {
5084 case T_CLASS:
5085 case T_MODULE:
5086 if (FL_TEST(obj, RCLASS_IS_ROOT)) {
5087 return;
5088 }
5089 FL_SET(obj, RCLASS_IS_ROOT);
5090 break;
5091 default:
5092 break;
5093 }
5094 /* Register in the current Ractor's own pin list (a raw array). No lock: only the
5095 * owner appends and only the owner's GC marks it; the merge that inherits a list
5096 * runs stop-the-world. */
5097 rb_ractor_t *cr = GET_RACTOR();
5098 if (cr->registered_marks_cnt == cr->registered_marks_capa) {
5099 size_t nc = cr->registered_marks_capa ? cr->registered_marks_capa * 2 : 64;
5100 VALUE *p = realloc(cr->registered_marks, nc * sizeof(VALUE));
5101 if (!p) rb_bug("rb_vm_register_global_object: out of memory");
5102 cr->registered_marks = p;
5103 cr->registered_marks_capa = nc;
5104 }
5105 cr->registered_marks[cr->registered_marks_cnt++] = obj;
5106 RB_GC_GUARD(obj);
5107}
5108
5109VALUE rb_cc_refinement_set_create(void);
5110
5111void
5112Init_vm_objects(void)
5113{
5114 rb_vm_t *vm = GET_VM();
5115 st_init_existing_table_with_size(&vm->ci_table, &vm_ci_hashtype, 0);
5116 vm->cc_refinement_set = rb_cc_refinement_set_create();
5117}
5118
5119// Whether JIT is enabled or not, we need to load/undef `#with_jit` for other builtins.
5120#include "jit_hook.rbinc"
5121#include "jit_undef.rbinc"
5122
5123// Stub for builtin function when not building YJIT units
5124#if !USE_YJIT
5125void Init_builtin_yjit(void) {}
5126#endif
5127
5128// Stub for builtin function when not building ZJIT units
5129#if !USE_ZJIT
5130void Init_builtin_zjit(void) {}
5131#endif
5132
5133/* top self */
5134
5135static VALUE
5136main_to_s(VALUE obj)
5137{
5138 return rb_str_new2("main");
5139}
5140
5141VALUE
5142rb_vm_top_self(void)
5143{
5144 const rb_box_t *box = rb_current_box();
5145 VM_ASSERT(box);
5146 VM_ASSERT(box->top_self);
5147 return box->top_self;
5148}
5149
5150void
5151Init_top_self(void)
5152{
5153 rb_vm_t *vm = GET_VM();
5154 vm->root_box = (rb_box_t *)rb_root_box();
5155 vm->root_box->top_self = rb_obj_alloc(rb_cObject);
5156 rb_define_singleton_method(vm->root_box->top_self, "to_s", main_to_s, 0);
5157 rb_define_alias(rb_singleton_class(vm->root_box->top_self), "inspect", "to_s");
5158}
5159
5160VALUE *
5162{
5163 rb_ractor_t *cr = GET_RACTOR();
5164 return &cr->verbose;
5165}
5166
5167VALUE *
5169{
5170 rb_ractor_t *cr = GET_RACTOR();
5171 return &cr->debug;
5172}
5173
5174bool rb_free_at_exit = false;
5175
5176bool
5177ruby_free_at_exit_p(void)
5178{
5179 return rb_free_at_exit;
5180}
5181
5182/* iseq.c */
5183VALUE rb_insn_operand_intern(const rb_iseq_t *iseq,
5184 VALUE insn, int op_no, VALUE op,
5185 int len, size_t pos, VALUE *pnop, VALUE child);
5186
5187#if VM_COLLECT_USAGE_DETAILS
5188
5189#define HASH_ASET(h, k, v) rb_hash_aset((h), (st_data_t)(k), (st_data_t)(v))
5190
5191/* uh = {
5192 * insn(Fixnum) => ihash(Hash)
5193 * }
5194 * ihash = {
5195 * -1(Fixnum) => count, # insn usage
5196 * 0(Fixnum) => ophash, # operand usage
5197 * }
5198 * ophash = {
5199 * val(interned string) => count(Fixnum)
5200 * }
5201 */
5202static void
5203vm_analysis_insn(int insn)
5204{
5205 ID usage_hash;
5206 ID bigram_hash;
5207 static int prev_insn = -1;
5208
5209 VALUE uh;
5210 VALUE ihash;
5211 VALUE cv;
5212
5213 CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN");
5214 CONST_ID(bigram_hash, "USAGE_ANALYSIS_INSN_BIGRAM");
5215 uh = rb_const_get(rb_cRubyVM, usage_hash);
5216 if (NIL_P(ihash = rb_hash_aref(uh, INT2FIX(insn)))) {
5217 ihash = rb_hash_new();
5218 HASH_ASET(uh, INT2FIX(insn), ihash);
5219 }
5220 if (NIL_P(cv = rb_hash_aref(ihash, INT2FIX(-1)))) {
5221 cv = INT2FIX(0);
5222 }
5223 HASH_ASET(ihash, INT2FIX(-1), INT2FIX(FIX2INT(cv) + 1));
5224
5225 /* calc bigram */
5226 if (prev_insn != -1) {
5227 VALUE bi;
5228 VALUE ary[2];
5229 VALUE cv;
5230
5231 ary[0] = INT2FIX(prev_insn);
5232 ary[1] = INT2FIX(insn);
5233 bi = rb_ary_new4(2, &ary[0]);
5234
5235 uh = rb_const_get(rb_cRubyVM, bigram_hash);
5236 if (NIL_P(cv = rb_hash_aref(uh, bi))) {
5237 cv = INT2FIX(0);
5238 }
5239 HASH_ASET(uh, bi, INT2FIX(FIX2INT(cv) + 1));
5240 }
5241 prev_insn = insn;
5242}
5243
5244static void
5245vm_analysis_operand(int insn, int n, VALUE op)
5246{
5247 ID usage_hash;
5248
5249 VALUE uh;
5250 VALUE ihash;
5251 VALUE ophash;
5252 VALUE valstr;
5253 VALUE cv;
5254
5255 CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN");
5256
5257 uh = rb_const_get(rb_cRubyVM, usage_hash);
5258 if (NIL_P(ihash = rb_hash_aref(uh, INT2FIX(insn)))) {
5259 ihash = rb_hash_new();
5260 HASH_ASET(uh, INT2FIX(insn), ihash);
5261 }
5262 if (NIL_P(ophash = rb_hash_aref(ihash, INT2FIX(n)))) {
5263 ophash = rb_hash_new();
5264 HASH_ASET(ihash, INT2FIX(n), ophash);
5265 }
5266 /* intern */
5267 valstr = rb_insn_operand_intern(CFP_ISEQ(GET_EC()->cfp), insn, n, op, 0, 0, 0, 0);
5268
5269 /* set count */
5270 if (NIL_P(cv = rb_hash_aref(ophash, valstr))) {
5271 cv = INT2FIX(0);
5272 }
5273 HASH_ASET(ophash, valstr, INT2FIX(FIX2INT(cv) + 1));
5274}
5275
5276static void
5277vm_analysis_register(int reg, int isset)
5278{
5279 ID usage_hash;
5280 VALUE uh;
5281 VALUE valstr;
5282 static const char regstrs[][5] = {
5283 "pc", /* 0 */
5284 "sp", /* 1 */
5285 "ep", /* 2 */
5286 "cfp", /* 3 */
5287 "self", /* 4 */
5288 "iseq", /* 5 */
5289 };
5290 static const char getsetstr[][4] = {
5291 "get",
5292 "set",
5293 };
5294 static VALUE syms[sizeof(regstrs) / sizeof(regstrs[0])][2];
5295
5296 VALUE cv;
5297
5298 CONST_ID(usage_hash, "USAGE_ANALYSIS_REGS");
5299 if (syms[0] == 0) {
5300 char buff[0x10];
5301 int i;
5302
5303 for (i = 0; i < (int)(sizeof(regstrs) / sizeof(regstrs[0])); i++) {
5304 int j;
5305 for (j = 0; j < 2; j++) {
5306 snprintf(buff, 0x10, "%d %s %-4s", i, getsetstr[j], regstrs[i]);
5307 syms[i][j] = ID2SYM(rb_intern(buff));
5308 }
5309 }
5310 }
5311 valstr = syms[reg][isset];
5312
5313 uh = rb_const_get(rb_cRubyVM, usage_hash);
5314 if (NIL_P(cv = rb_hash_aref(uh, valstr))) {
5315 cv = INT2FIX(0);
5316 }
5317 HASH_ASET(uh, valstr, INT2FIX(FIX2INT(cv) + 1));
5318}
5319
5320#undef HASH_ASET
5321
5322static void (*ruby_vm_collect_usage_func_insn)(int insn) = NULL;
5323static void (*ruby_vm_collect_usage_func_operand)(int insn, int n, VALUE op) = NULL;
5324static void (*ruby_vm_collect_usage_func_register)(int reg, int isset) = NULL;
5325
5326/* :nodoc: */
5327static VALUE
5328usage_analysis_insn_start(VALUE self)
5329{
5330 ruby_vm_collect_usage_func_insn = vm_analysis_insn;
5331 return Qnil;
5332}
5333
5334/* :nodoc: */
5335static VALUE
5336usage_analysis_operand_start(VALUE self)
5337{
5338 ruby_vm_collect_usage_func_operand = vm_analysis_operand;
5339 return Qnil;
5340}
5341
5342/* :nodoc: */
5343static VALUE
5344usage_analysis_register_start(VALUE self)
5345{
5346 ruby_vm_collect_usage_func_register = vm_analysis_register;
5347 return Qnil;
5348}
5349
5350/* :nodoc: */
5351static VALUE
5352usage_analysis_insn_stop(VALUE self)
5353{
5354 ruby_vm_collect_usage_func_insn = 0;
5355 return Qnil;
5356}
5357
5358/* :nodoc: */
5359static VALUE
5360usage_analysis_operand_stop(VALUE self)
5361{
5362 ruby_vm_collect_usage_func_operand = 0;
5363 return Qnil;
5364}
5365
5366/* :nodoc: */
5367static VALUE
5368usage_analysis_register_stop(VALUE self)
5369{
5370 ruby_vm_collect_usage_func_register = 0;
5371 return Qnil;
5372}
5373
5374/* :nodoc: */
5375static VALUE
5376usage_analysis_insn_running(VALUE self)
5377{
5378 return RBOOL(ruby_vm_collect_usage_func_insn != 0);
5379}
5380
5381/* :nodoc: */
5382static VALUE
5383usage_analysis_operand_running(VALUE self)
5384{
5385 return RBOOL(ruby_vm_collect_usage_func_operand != 0);
5386}
5387
5388/* :nodoc: */
5389static VALUE
5390usage_analysis_register_running(VALUE self)
5391{
5392 return RBOOL(ruby_vm_collect_usage_func_register != 0);
5393}
5394
5395static VALUE
5396usage_analysis_clear(VALUE self, ID usage_hash)
5397{
5398 VALUE uh;
5399 uh = rb_const_get(self, usage_hash);
5400 rb_hash_clear(uh);
5401
5402 return Qtrue;
5403}
5404
5405
5406/* :nodoc: */
5407static VALUE
5408usage_analysis_insn_clear(VALUE self)
5409{
5410 ID usage_hash;
5411 ID bigram_hash;
5412
5413 CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN");
5414 CONST_ID(bigram_hash, "USAGE_ANALYSIS_INSN_BIGRAM");
5415 usage_analysis_clear(rb_cRubyVM, usage_hash);
5416 return usage_analysis_clear(rb_cRubyVM, bigram_hash);
5417}
5418
5419/* :nodoc: */
5420static VALUE
5421usage_analysis_operand_clear(VALUE self)
5422{
5423 ID usage_hash;
5424
5425 CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN");
5426 return usage_analysis_clear(self, usage_hash);
5427}
5428
5429/* :nodoc: */
5430static VALUE
5431usage_analysis_register_clear(VALUE self)
5432{
5433 ID usage_hash;
5434
5435 CONST_ID(usage_hash, "USAGE_ANALYSIS_REGS");
5436 return usage_analysis_clear(self, usage_hash);
5437}
5438
5439/* @param insn instruction number */
5440static void
5441vm_collect_usage_insn(int insn)
5442{
5443 if (RUBY_DTRACE_INSN_ENABLED()) {
5444 RUBY_DTRACE_INSN(rb_insns_name(insn));
5445 }
5446 if (ruby_vm_collect_usage_func_insn)
5447 (*ruby_vm_collect_usage_func_insn)(insn);
5448}
5449
5450/* @param insn instruction number
5451 * @param n n-th operand
5452 * @param op operand value
5453 */
5454static void
5455vm_collect_usage_operand(int insn, int n, VALUE op)
5456{
5457 if (RUBY_DTRACE_INSN_OPERAND_ENABLED()) {
5458 VALUE valstr;
5459
5460 valstr = rb_insn_operand_intern(CFP_ISEQ(GET_EC()->cfp), insn, n, op, 0, 0, 0, 0);
5461
5462 RUBY_DTRACE_INSN_OPERAND(RSTRING_PTR(valstr), rb_insns_name(insn));
5463 RB_GC_GUARD(valstr);
5464 }
5465 if (ruby_vm_collect_usage_func_operand)
5466 (*ruby_vm_collect_usage_func_operand)(insn, n, op);
5467}
5468
5469/* @param reg register id. see code of vm_analysis_register() */
5470/* @param isset 0: read, 1: write */
5471static void
5472vm_collect_usage_register(int reg, int isset)
5473{
5474 if (ruby_vm_collect_usage_func_register)
5475 (*ruby_vm_collect_usage_func_register)(reg, isset);
5476}
5477#endif
5478
5479#include "vm_call_iseq_optimized.inc" /* required from vm_insnhelper.c */
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
Definition atomic.h:69
#define RUBY_ATOMIC_FETCH_ADD(var, val)
Atomically replaces the value pointed by var with the result of addition of val to the old value of v...
Definition atomic.h:118
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_method_id(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
#define RUBY_EVENT_END
Encountered an end of a class clause.
Definition event.h:40
#define RUBY_EVENT_B_RETURN
Encountered a next statement.
Definition event.h:56
#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
uint32_t rb_event_flag_t
Represents event(s).
Definition event.h:108
VALUE rb_class_new(VALUE super)
Creates a new, anonymous class.
Definition class.c:853
VALUE rb_singleton_class(VALUE obj)
Finds or creates the singleton class of the passed object.
Definition class.c:3017
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition class.c:3060
void rb_undef_method(VALUE klass, const char *name)
Defines an undef of a method.
Definition class.c:2863
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1700
#define NUM2ULONG
Old name of RB_NUM2ULONG.
Definition long.h:52
#define ALLOCV
Old name of RB_ALLOCV.
Definition memory.h:407
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:403
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define OBJ_FROZEN
Old name of RB_OBJ_FROZEN.
Definition fl_type.h:133
#define T_IMEMO
Old name of RUBY_T_IMEMO.
Definition value_type.h:67
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
Definition fl_type.h:131
#define ULONG2NUM
Old name of RB_ULONG2NUM.
Definition long.h:60
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
Definition assume.h:29
#define SYM2ID
Old name of RB_SYM2ID.
Definition symbol.h:45
#define ZALLOC
Old name of RB_ZALLOC.
Definition memory.h:405
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:205
#define rb_ary_new4
Old name of rb_ary_new_from_values.
Definition array.h:659
#define SIZET2NUM
Old name of RB_SIZE2NUM.
Definition size_t.h:62
#define rb_exc_new2
Old name of rb_exc_new_cstr.
Definition error.h:37
#define FIX2INT
Old name of RB_FIX2INT.
Definition int.h:41
#define T_MODULE
Old name of RUBY_T_MODULE.
Definition value_type.h:70
#define ZALLOC_N
Old name of RB_ZALLOC_N.
Definition memory.h:404
#define ASSUME
Old name of RBIMPL_ASSUME.
Definition assume.h:27
#define T_ICLASS
Old name of RUBY_T_ICLASS.
Definition value_type.h:66
#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:402
#define FL_SET
Old name of RB_FL_SET.
Definition fl_type.h:125
#define rb_exc_new3
Old name of rb_exc_new_str.
Definition error.h:38
#define ULL2NUM
Old name of RB_ULL2NUM.
Definition long_long.h:31
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define NIL_P
Old name of RB_NIL_P.
#define NUM2ULL
Old name of RB_NUM2ULL.
Definition long_long.h:35
#define T_CLASS
Old name of RUBY_T_CLASS.
Definition value_type.h:58
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
Definition value_type.h:85
#define FL_TEST
Old name of RB_FL_TEST.
Definition fl_type.h:127
#define FIXNUM_P
Old name of RB_FIXNUM_P.
#define FL_USHIFT
Old name of RUBY_FL_USHIFT.
Definition fl_type.h:67
#define CONST_ID
Old name of RUBY_CONST_ID.
Definition symbol.h:47
#define ALLOCV_END
Old name of RB_ALLOCV_END.
Definition memory.h:409
#define SYMBOL_P
Old name of RB_SYMBOL_P.
Definition value_type.h:88
void ruby_init_stack(void *addr)
Set stack bottom of Ruby implementation.
Definition vm.c:5064
VALUE rb_eLocalJumpError
LocalJumpError exception.
Definition eval.c:50
void rb_category_warn(rb_warning_category_t category, const char *fmt,...)
Identical to rb_category_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:474
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:678
void rb_iter_break(void)
Breaks from a block.
Definition vm.c:2380
#define ruby_verbose
This variable controls whether the interpreter is in debug mode.
Definition error.h:476
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1457
void rb_iter_break_value(VALUE val)
Identical to rb_iter_break(), except it additionally takes the "value" of this breakage.
Definition vm.c:2386
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1455
VALUE * rb_ruby_verbose_ptr(void)
This is an implementation detail of ruby_verbose.
Definition vm.c:5161
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Identical to rb_exc_new_cstr(), except it takes a Ruby's string instead of C's.
Definition error.c:1508
VALUE * rb_ruby_debug_ptr(void)
This is an implementation detail of ruby_debug.
Definition vm.c:5168
VALUE rb_eSysStackError
SystemStackError exception.
Definition eval.c:51
@ RB_WARN_CATEGORY_PERFORMANCE
Warning is for performance issues (not enabled by -w).
Definition error.h:54
VALUE rb_cTime
Time class.
Definition time.c:702
VALUE rb_cArray
Array class.
VALUE rb_cObject
Object class.
Definition object.c:60
VALUE rb_obj_alloc(VALUE klass)
Allocates an instance of the given class.
Definition object.c:2264
VALUE rb_cInteger
Module class.
Definition numeric.c:202
VALUE rb_cNilClass
NilClass class.
Definition object.c:65
VALUE rb_cBinding
Binding class.
Definition proc.c:45
VALUE rb_cRegexp
Regexp class.
Definition re.c:3404
VALUE rb_cHash
Hash class.
Definition hash.c:123
VALUE rb_cFalseClass
FalseClass class.
Definition object.c:67
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:230
VALUE rb_cSymbol
Symbol class.
Definition string.c:86
VALUE rb_cBasicObject
BasicObject class.
Definition object.c:58
VALUE rb_cThread
Thread class.
Definition vm.c:719
VALUE rb_obj_freeze(VALUE obj)
Same as RB_OBJ_FREEZE(), but returns the given object.
Definition object.c:1305
VALUE rb_cFloat
Float class.
Definition numeric.c:201
VALUE rb_cProc
Proc class.
Definition proc.c:46
VALUE rb_cTrueClass
TrueClass class.
Definition object.c:66
VALUE rb_cString
String class.
Definition string.c:85
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:504
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:492
#define RUBY_ASSERT_ALWAYS(expr,...)
A variant of RUBY_ASSERT that does not interface with RUBY_DEBUG.
Definition assert.h:199
#define RUBY_ASSERT_MESG(expr,...)
Asserts that the expression is truthy.
Definition assert.h:186
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
Defines RBIMPL_HAS_BUILTIN.
VALUE rb_ary_delete_at(VALUE ary, rb_len_t pos)
Destructively removes an element which resides at the specific index of the passed array.
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_hidden_new(rb_len_t 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.
void rb_undef(VALUE mod, ID mid)
Inserts a method entry that hides previous method definition of the given name.
Definition vm_method.c:2585
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_backref_get(void)
Queries the last match, or Regexp.last_match, or the $~.
Definition vm.c:2130
void rb_lastline_set(VALUE str)
Updates $_.
Definition vm.c:2148
VALUE rb_lastline_get(void)
Queries the last line, or the $_.
Definition vm.c:2142
void rb_backref_set(VALUE md)
Updates $~.
Definition vm.c:2136
VALUE rb_block_proc(void)
Constructs a Proc object from implicitly passed components.
Definition proc.c:1580
VALUE rb_block_lambda(void)
Identical to rb_proc_new(), except it returns a lambda.
Definition proc.c:1599
VALUE rb_binding_new(void)
Snapshots the current execution context and turn it into an instance of rb_cBinding.
Definition proc.c:906
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:4005
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
Definition string.c:1567
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:2068
#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:1681
VALUE rb_const_get(VALUE space, ID name)
Identical to rb_const_defined(), except it returns the actual defined value.
Definition variable.c:3515
void rb_set_class_path(VALUE klass, VALUE space, const char *name)
Names a class.
Definition variable.c:457
VALUE rb_mod_name(VALUE mod)
Queries the name of a module.
Definition variable.c:150
void rb_alias_variable(ID dst, ID src)
Aliases a global variable.
Definition variable.c:1230
VALUE rb_class_path(VALUE mod)
Identical to rb_mod_name(), except it returns #<Class: ...> style inspection for anonymous modules.
Definition variable.c:396
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1887
const char * rb_sourcefile(void)
Resembles __FILE__.
Definition vm.c:2167
void rb_alias(VALUE klass, ID dst, ID src)
Resembles alias.
Definition vm_method.c:3081
int rb_frame_method_id_and_class(ID *idp, VALUE *klassp)
Resembles __method__.
Definition vm.c:3345
int rb_sourceline(void)
Resembles __LINE__.
Definition vm.c:2181
VALUE rb_sym2str(VALUE symbol)
Obtain a frozen string representation of a symbol (not including the leading colon).
Definition symbol.c:1154
void rb_define_global_const(const char *name, VALUE val)
Identical to rb_define_const(), except it defines that of "global", i.e.
Definition variable.c:4104
VALUE rb_iv_set(VALUE obj, const char *name, VALUE val)
Assigns to an instance variable.
Definition variable.c:4582
int len
Length of the buffer.
Definition io.h:8
VALUE rb_ractor_make_shareable_copy(VALUE obj)
Identical to rb_ractor_make_shareable(), except it returns a (deep) copy of the passed one instead of...
Definition ractor.c:2029
#define RB_OBJ_SET_SHAREABLE(obj)
Wrapper of rb_obj_set_shareable().
Definition ractor.h:290
static bool rb_ractor_shareable_p(VALUE obj)
Queries if multiple Ractors can share the passed object or not.
Definition ractor.h:269
#define RB_OBJ_SHAREABLE_P(obj)
Queries if the passed object has previously classified as shareable or not.
Definition ractor.h:255
static VALUE RB_OBJ_SET_FROZEN_SHAREABLE(VALUE obj)
Freezes and marks the object as shareable.
Definition ractor.h:301
VALUE rb_ractor_make_shareable(VALUE obj)
Destructively transforms the passed object so that multiple Ractors can share it.
Definition ractor.c:2018
void ruby_vm_at_exit(void(*func)(ruby_vm_t *))
ruby_vm_at_exit registers a function func to be invoked when a VM passed away.
Definition vm.c:1064
int ruby_vm_destruct(ruby_vm_t *vm)
Destructs the passed VM.
Definition vm.c:3648
VALUE rb_f_sprintf(int argc, const VALUE *argv)
Identical to rb_str_format(), except how the arguments are arranged.
Definition sprintf.c:232
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:375
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition memory.h:363
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:170
VALUE type(ANYARGS)
ANYARGS-ed function type.
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:50
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition rarray.h:280
#define RARRAY_AREF(a, i)
References an object in an array.
Definition rarray.h:421
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
Definition rbasic.h:166
#define RBASIC(obj)
Convenient casting macro.
Definition rbasic.h:40
#define RHASH_EMPTY_P(h)
Checks if the hash is empty.
Definition rhash.h:67
#define StringValuePtr(v)
Identical to StringValue, except it returns a char*.
Definition rstring.h:76
static int RSTRING_LENINT(VALUE str)
Identical to RSTRING_LEN(), except it differs for the return type.
Definition rstring.h:438
#define RTYPEDDATA_DATA(v)
Convenient getter macro.
Definition rtypeddata.h:106
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:557
const char * rb_class2name(VALUE klass)
Queries the name of the passed class.
Definition variable.c:522
#define RB_NO_KEYWORDS
Do not pass keywords.
Definition scan_args.h:69
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
#define RTEST
This is an old name of RB_TEST.
#define _(args)
This was a transition path from K&R to ANSI.
Definition stdarg.h:35
Definition proc.c:31
Definition iseq.h:360
Internal header for Ruby Box.
Definition box.h:14
Definition method.h:63
CREF (Class REFerence)
Definition method.h:45
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:242
Definition method.h:55
const rb_iseq_t * iseqptr
iseq pointer, should be separated from iseqval
Definition method.h:143
Definition st.h:79
IFUNC (Internal FUNCtion)
Definition imemo.h:89
SVAR (Special VARiable)
Definition imemo.h:54
const VALUE cref_or_me
class reference or rb_method_entry_t
Definition imemo.h:56
THROW_DATA.
Definition imemo.h:63
void rb_native_cond_initialize(rb_nativethread_cond_t *cond)
Fills the passed condition variable with an initial value.
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_initialize.
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_destroy.
void rb_native_cond_destroy(rb_nativethread_cond_t *cond)
Destroys the passed condition variable.
long rb_len_t
A signed integer type for lengths and indices of objects such as String and Array.
Definition value.h:142
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 enum ruby_value_type RB_BUILTIN_TYPE(VALUE obj)
Queries the type of the object.
Definition value_type.h:182
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:425
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