Ruby 4.1.0dev (2026-08-28 revision 5eb9a6925b805a17dced976d5b741afe5086aab1)
box.c (5eb9a6925b805a17dced976d5b741afe5086aab1)
1/* indent-tabs-mode: nil */
2
3#include "eval_intern.h"
4#include "internal.h"
5#include "internal/box.h"
6#include "internal/class.h"
7#include "internal/eval.h"
8#include "internal/error.h"
9#include "internal/file.h"
10#include "internal/gc.h"
11#include "internal/hash.h"
12#include "internal/io.h"
13#include "internal/load.h"
14#include "internal/st.h"
15#include "internal/variable.h"
16#include "iseq.h"
18#include "ruby/util.h"
19#include "vm_core.h"
20#include "darray.h"
21#include "zjit.h"
22
23#include "builtin.h"
24
25#include <stdio.h>
26
27#ifdef HAVE_FCNTL_H
28#include <fcntl.h>
29#endif
30#ifdef HAVE_SYS_SENDFILE_H
31# include <sys/sendfile.h>
32#endif
33#ifdef HAVE_COPYFILE_H
34#include <copyfile.h>
35#endif
36
38VALUE rb_cBoxEntry = 0;
39VALUE rb_mBoxLoader = 0;
40
41static rb_box_t master_box[1]; /* Initialize in initialize_master_box() */
42static rb_box_t *root_box;
43static rb_box_t *main_box;
44
45static rb_box_gem_flags_t box_gem_flags[1];
46
47static char *tmp_dir;
48static bool tmp_dir_has_dirsep;
49
50#define BOX_TMP_PREFIX "_ruby_box_"
51
52#ifndef MAXPATHLEN
53# define MAXPATHLEN 1024
54#endif
55
56#if defined(_WIN32)
57# define DIRSEP "\\"
58#else
59# define DIRSEP "/"
60#endif
61
62bool ruby_box_enabled = false; // extern
63bool ruby_box_init_done = false; // extern
64bool ruby_box_crashed = false; // extern, changed only in vm.c
65
66VALUE rb_resolve_feature_path(VALUE klass, VALUE fname);
67static VALUE rb_box_inspect(VALUE obj);
68
69void
70rb_box_set_gem_flags(rb_box_gem_flags_t *flags)
71{
72
73 box_gem_flags->gem = flags->gem;
74 box_gem_flags->error_highlight = flags->error_highlight;
75 box_gem_flags->did_you_mean = flags->did_you_mean;
76 box_gem_flags->syntax_suggest = flags->syntax_suggest;
77}
78
79void
80rb_box_init_done(void)
81{
82 ruby_box_init_done = true;
83}
84
85const rb_box_t *
86rb_master_box(void)
87{
88 return master_box;
89}
90
91const rb_box_t *
92rb_root_box(void)
93{
94 if (!root_box) // The root box isn't initialized yet - The Ruby runtime is in setup.
95 return master_box;
96 return root_box;
97}
98
99const rb_box_t *
100rb_main_box(void)
101{
102 return main_box;
103}
104
105// NOTE: must be called by Ruby thread
106const rb_box_t *
107rb_current_box(void)
108{
109 /*
110 * If RUBY_BOX is not set, the master box is the only available one.
111 *
112 * While the root/main boxes are not initialized, the master box is
113 * the only valid box.
114 * This early return is to avoid accessing EC before its setup.
115 */
116 if (!root_box)
117 return master_box;
118 if (!main_box)
119 return root_box;
120
121 return rb_vm_current_box(GET_EC());
122}
123
124// NOTE: must be called by Ruby thread
125const rb_box_t *
126rb_loading_box(void)
127{
128 if (!root_box)
129 return master_box;
130 if (!main_box)
131 return root_box;
132
133 return rb_vm_loading_box(GET_EC());
134}
135
136// NOTE: must be called by Ruby thread
137const rb_box_t *
138rb_current_box_in_crash_report(void)
139{
140 if (ruby_box_crashed)
141 return NULL;
142 return rb_current_box();
143}
144
145static long box_id_counter = 0;
146
147static long
148box_generate_id(void)
149{
150 long id;
151 RB_VM_LOCKING() {
152 id = ++box_id_counter;
153 }
154 return id;
155}
156
157static VALUE
158box_main_to_s(VALUE obj)
159{
160 return rb_str_new2("main");
161}
162
163static void
164box_entry_initialize(rb_box_t *box)
165{
166 const rb_box_t *master = rb_master_box();
167
168 // These will be updated immediately
169 box->box_object = 0;
170 box->box_id = 0;
171
172 box->top_self = rb_obj_alloc(rb_cObject);
173 rb_define_singleton_method(box->top_self, "to_s", box_main_to_s, 0);
174 rb_define_alias(rb_singleton_class(box->top_self), "inspect", "to_s");
175 box->load_path = rb_ary_dup(master->load_path);
176 box->expanded_load_path = rb_ary_dup(master->expanded_load_path);
177 box->load_path_snapshot = rb_ary_new();
178 box->load_path_check_cache = 0;
179 box->loaded_features = rb_ary_dup(master->loaded_features);
180 box->loaded_features_snapshot = rb_ary_new();
181 box->loaded_features_index = st_init_numtable();
182 box->loaded_features_realpaths = rb_hash_dup(master->loaded_features_realpaths);
183 box->loaded_features_realpath_map = rb_hash_dup(master->loaded_features_realpath_map);
184 box->loading_table = st_init_strtable();
185 box->ruby_dln_libmap = rb_hash_new();
186 box->gvar_tbl = rb_hash_new();
187 box->classext_cow_classes = st_init_numtable();
188
189 box->is_user = true;
190 box->is_optional = true;
191}
192
193void
194rb_box_gc_update_references(void *ptr)
195{
196 rb_box_t *box = (rb_box_t *)ptr;
197 if (!box) return;
198
199 if (box->box_object)
200 box->box_object = rb_gc_location(box->box_object);
201 if (box->top_self)
202 box->top_self = rb_gc_location(box->top_self);
203 box->load_path = rb_gc_location(box->load_path);
204 box->expanded_load_path = rb_gc_location(box->expanded_load_path);
205 box->load_path_snapshot = rb_gc_location(box->load_path_snapshot);
206 if (box->load_path_check_cache) {
207 box->load_path_check_cache = rb_gc_location(box->load_path_check_cache);
208 }
209 box->loaded_features = rb_gc_location(box->loaded_features);
210 box->loaded_features_snapshot = rb_gc_location(box->loaded_features_snapshot);
211 box->loaded_features_realpaths = rb_gc_location(box->loaded_features_realpaths);
212 box->loaded_features_realpath_map = rb_gc_location(box->loaded_features_realpath_map);
213 box->ruby_dln_libmap = rb_gc_location(box->ruby_dln_libmap);
214 box->gvar_tbl = rb_gc_location(box->gvar_tbl);
215}
216
217void
218rb_box_entry_mark(void *ptr)
219{
220 const rb_box_t *box = (rb_box_t *)ptr;
221 if (!box) return;
222
223 rb_gc_mark(box->box_object);
224 rb_gc_mark(box->top_self);
225 rb_gc_mark(box->load_path);
226 rb_gc_mark(box->expanded_load_path);
227 rb_gc_mark(box->load_path_snapshot);
228 rb_gc_mark(box->load_path_check_cache);
229 rb_gc_mark(box->loaded_features);
230 rb_gc_mark(box->loaded_features_snapshot);
231 rb_gc_mark(box->loaded_features_realpaths);
232 rb_gc_mark(box->loaded_features_realpath_map);
233 if (box->loading_table) {
234 rb_mark_tbl(box->loading_table);
235 }
236 rb_gc_mark(box->ruby_dln_libmap);
237 rb_gc_mark(box->gvar_tbl);
238 if (box->classext_cow_classes) {
239 rb_mark_set(box->classext_cow_classes);
240 }
241}
242
243static int
244free_loading_table_entry(st_data_t key, st_data_t value, st_data_t arg)
245{
246 xfree((char *)key);
247 return ST_DELETE;
248}
249
250static int
251free_loaded_feature_index_i(st_data_t key, st_data_t value, st_data_t arg)
252{
253 if (!FIXNUM_P(value)) {
254 rb_darray_free_sized((void *)value, long);
255 }
256 return ST_CONTINUE;
257}
258
259static void
260free_box_st_tables(void *ptr)
261{
262 rb_box_t *box = (rb_box_t *)ptr;
263 if (box->loading_table) {
264 st_foreach(box->loading_table, free_loading_table_entry, 0);
265 st_free_table(box->loading_table);
266 box->loading_table = 0;
267 }
268
269 if (box->loaded_features_index) {
270 st_foreach(box->loaded_features_index, free_loaded_feature_index_i, 0);
271 st_free_table(box->loaded_features_index);
272 }
273}
274
275static int
276free_classext_for_box(st_data_t key, st_data_t _value, st_data_t box_arg)
277{
278 rb_classext_t *ext;
279 VALUE obj = (VALUE)key;
280 const rb_box_t *box = (const rb_box_t *)box_arg;
281
282 if (RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE)) {
283 ext = rb_class_unlink_classext(obj, box);
284 rb_class_classext_free(obj, ext, false);
285 }
286 else if (RB_TYPE_P(obj, T_ICLASS)) {
287 ext = rb_class_unlink_classext(obj, box);
288 rb_iclass_classext_free(obj, ext, false);
289 }
290 else {
291 rb_bug("Invalid type of object in classext_cow_classes: %s", rb_type_str(BUILTIN_TYPE(obj)));
292 }
293 return ST_CONTINUE;
294}
295
296static void
297box_entry_free(void *ptr)
298{
299 const rb_box_t *box = (const rb_box_t *)ptr;
300
301 if (box->classext_cow_classes) {
302 st_foreach(box->classext_cow_classes, free_classext_for_box, (st_data_t)box);
303 }
304
305 free_box_st_tables(ptr);
306 SIZED_FREE(box);
307}
308
309static size_t
310box_entry_memsize(const void *ptr)
311{
312 size_t size = sizeof(rb_box_t);
313 const rb_box_t *box = (const rb_box_t *)ptr;
314 if (box->loaded_features_index) {
315 size += rb_st_memsize(box->loaded_features_index);
316 }
317 if (box->loading_table) {
318 size += rb_st_memsize(box->loading_table);
319 }
320 return size;
321}
322
323static const rb_data_type_t rb_box_data_type = {
324 "Ruby::Box::Entry",
325 {
326 rb_box_entry_mark,
327 box_entry_free,
328 box_entry_memsize,
329 rb_box_gc_update_references,
330 },
331 0, 0, RUBY_TYPED_FREE_IMMEDIATELY // TODO: enable RUBY_TYPED_WB_PROTECTED when inserting write barriers
332};
333
334static const rb_data_type_t rb_master_box_data_type = {
335 "Ruby::Box::Master",
336 {
337 rb_box_entry_mark,
338 free_box_st_tables,
339 box_entry_memsize,
340 rb_box_gc_update_references,
341 },
342 &rb_box_data_type, 0, RUBY_TYPED_THREAD_SAFE_FREE // TODO: enable RUBY_TYPED_WB_PROTECTED when inserting write barriers
343};
344
345VALUE
346rb_box_entry_alloc(VALUE klass)
347{
348 rb_box_t *entry;
349 VALUE obj = TypedData_Make_Struct(klass, rb_box_t, &rb_box_data_type, entry);
350 box_entry_initialize(entry);
351 return obj;
352}
353
354static rb_box_t *
355get_box_struct_internal(VALUE entry)
356{
357 rb_box_t *sval;
358 TypedData_Get_Struct(entry, rb_box_t, &rb_box_data_type, sval);
359 return sval;
360}
361
362rb_box_t *
363rb_get_box_t(VALUE box)
364{
365 VALUE entry;
366 ID id_box_entry;
367
368 VM_ASSERT(box);
369
370 if (NIL_P(box))
371 return (rb_box_t *)rb_root_box();
372
373 VM_ASSERT(BOX_OBJ_P(box));
374
375 CONST_ID(id_box_entry, "__box_entry__");
376 entry = rb_attr_get(box, id_box_entry);
377 return get_box_struct_internal(entry);
378}
379
380VALUE
381rb_get_box_object(rb_box_t *box)
382{
383 VM_ASSERT(box && box->box_object);
384 return box->box_object;
385}
386
387/*
388 * call-seq:
389 * Ruby::Box.new -> new_box
390 *
391 * Returns a new Ruby::Box object.
392 */
393static VALUE
394box_initialize(VALUE box_value)
395{
396 rb_box_t *box;
397 rb_classext_t *object_classext;
398 VALUE entry;
399 ID id_box_entry;
400 CONST_ID(id_box_entry, "__box_entry__");
401
402 if (!rb_box_available()) {
403 rb_raise(rb_eRuntimeError, "Ruby Box is disabled. Set RUBY_BOX=1 environment variable to use Ruby::Box.");
404 }
405
406 entry = rb_class_new_instance_pass_kw(0, NULL, rb_cBoxEntry);
407 box = get_box_struct_internal(entry);
408
409 box->box_object = box_value;
410 box->box_id = box_generate_id();
411 rb_define_singleton_method(box->load_path, "resolve_feature_path", rb_resolve_feature_path, 1);
412
413 // Set the Ruby::Box object unique/consistent from any boxes to have just single
414 // constant table from any view of every (including main) box.
415 // If a code in the box adds a constant, the constant will be visible even from root/main.
416 RCLASS_SET_PRIME_CLASSEXT_WRITABLE(box_value, true);
417
418 // Get a clean constant table of Object even by writable one
419 // because ns was just created, so it has not touched any constants yet.
420 object_classext = RCLASS_EXT_WRITABLE_IN_BOX(rb_cObject, box);
421 RCLASS_SET_CONST_TBL(box_value, RCLASSEXT_CONST_TBL(object_classext), true);
422
423 rb_ivar_set(box_value, id_box_entry, entry);
424
425 if (ruby_box_init_done) {
426 if (box_gem_flags->gem) {
427 rb_vm_call_cfunc_in_box(Qnil, rb_define_gem_modules, (VALUE)box_gem_flags, Qnil,
428 rb_str_new_cstr("before_prelude.user.dummy"), (const rb_box_t *)box);
429 rb_load_gem_prelude((VALUE)box);
430 }
431 }
432
433 // Invalidate ZJIT code that assumes only the root box is active
434 rb_zjit_invalidate_root_box();
435
436 return box_value;
437}
438
439/*
440 * call-seq:
441 * Ruby::Box.enabled? -> true or false
442 *
443 * Returns +true+ if Ruby::Box is enabled.
444 */
445static VALUE
446rb_box_s_getenabled(VALUE recv)
447{
448 return RBOOL(rb_box_available());
449}
450
451/*
452 * call-seq:
453 * Ruby::Box.current -> box, nil or false
454 *
455 * Returns the current box.
456 * Returns +nil+ if Ruby Box is not enabled.
457 */
458static VALUE
459rb_box_s_current(VALUE recv)
460{
461 const rb_box_t *box;
462
463 if (!rb_box_available())
464 return Qnil;
465
466 box = rb_vm_current_box(GET_EC());
467 VM_ASSERT(box && box->box_object);
468 return box->box_object;
469}
470
471/*
472 * call-seq:
473 * load_path -> array
474 *
475 * Returns box local load path.
476 */
477static VALUE
478rb_box_load_path(VALUE box)
479{
480 VM_ASSERT(BOX_OBJ_P(box));
481 return rb_get_box_t(box)->load_path;
482}
483
484#ifdef _WIN32
485UINT rb_w32_system_tmpdir(WCHAR *path, UINT len);
486#endif
487
488/* Copied from mjit.c Ruby 3.0.3 */
489static char *
490system_default_tmpdir(void)
491{
492 // c.f. ext/etc/etc.c:etc_systmpdir()
493#ifdef _WIN32
494 WCHAR tmppath[_MAX_PATH];
495 UINT len = rb_w32_system_tmpdir(tmppath, numberof(tmppath));
496 if (len) {
497 int blen = WideCharToMultiByte(CP_UTF8, 0, tmppath, len, NULL, 0, NULL, NULL);
498 char *tmpdir = xmalloc(blen + 1);
499 WideCharToMultiByte(CP_UTF8, 0, tmppath, len, tmpdir, blen, NULL, NULL);
500 tmpdir[blen] = '\0';
501 return tmpdir;
502 }
503#elif defined _CS_DARWIN_USER_TEMP_DIR
504 char path[MAXPATHLEN];
505 size_t len = confstr(_CS_DARWIN_USER_TEMP_DIR, path, sizeof(path));
506 if (len > 0) {
507 char *tmpdir = xmalloc(len);
508 if (len > sizeof(path)) {
509 confstr(_CS_DARWIN_USER_TEMP_DIR, tmpdir, len);
510 }
511 else {
512 memcpy(tmpdir, path, len);
513 }
514 return tmpdir;
515 }
516#endif
517 return 0;
518}
519
520static int
521check_tmpdir(const char *dir)
522{
523 struct stat st;
524
525 if (!dir) return FALSE;
526 if (stat(dir, &st)) return FALSE;
527#ifndef S_ISDIR
528# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
529#endif
530 if (!S_ISDIR(st.st_mode)) return FALSE;
531#ifndef _WIN32
532# ifndef S_IWOTH
533# define S_IWOTH 002
534# endif
535 if (st.st_mode & S_IWOTH) {
536# ifdef S_ISVTX
537 if (!(st.st_mode & S_ISVTX)) return FALSE;
538# else
539 return FALSE;
540# endif
541 }
542 if (access(dir, W_OK)) return FALSE;
543#endif
544 return TRUE;
545}
546
547static char *
548system_tmpdir(void)
549{
550 char *tmpdir;
551# define RETURN_ENV(name) \
552 if (check_tmpdir(tmpdir = getenv(name))) return ruby_strdup(tmpdir)
553 RETURN_ENV("TMPDIR");
554 RETURN_ENV("TMP");
555 tmpdir = system_default_tmpdir();
556 if (check_tmpdir(tmpdir)) return tmpdir;
557 return ruby_strdup("/tmp");
558# undef RETURN_ENV
559}
560
561/* end of copy */
562
563static int
564sprint_ext_filename(char *str, size_t size, long box_id, const char *prefix, const char *basename)
565{
566 if (tmp_dir_has_dirsep) {
567 return snprintf(str, size, "%s%sp%"PRI_PIDT_PREFIX"u_%ld_%s", tmp_dir, prefix, getpid(), box_id, basename);
568 }
569 return snprintf(str, size, "%s%s%sp%"PRI_PIDT_PREFIX"u_%ld_%s", tmp_dir, DIRSEP, prefix, getpid(), box_id, basename);
570}
571
572enum copy_error_type {
573 COPY_ERROR_NONE,
574 COPY_ERROR_SRC_OPEN,
575 COPY_ERROR_DST_OPEN,
576 COPY_ERROR_SRC_READ,
577 COPY_ERROR_DST_WRITE,
578 COPY_ERROR_SRC_STAT,
579 COPY_ERROR_DST_CHMOD,
580 COPY_ERROR_SYSERR
581};
582
583static const char *
584copy_ext_file_error(char *message, size_t size, int copy_retvalue)
585{
586#ifdef _WIN32
587 int error = GetLastError();
588 char *p = message;
589 size_t len = snprintf(message, size, "%d: ", error);
590
591#define format_message(sublang) FormatMessage(\
592 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, \
593 NULL, error, MAKELANGID(LANG_NEUTRAL, (sublang)), \
594 message + len, size - len, NULL)
595 if (format_message(SUBLANG_ENGLISH_US) == 0)
596 format_message(SUBLANG_DEFAULT);
597 for (p = message + len; *p; p++) {
598 if (*p == '\n' || *p == '\r')
599 *p = ' ';
600 }
601#else
602 switch (copy_retvalue) {
603 case COPY_ERROR_SRC_OPEN:
604 strlcpy(message, "can't open the extension path", size);
605 break;
606 case COPY_ERROR_DST_OPEN:
607 strlcpy(message, "can't open the file to write", size);
608 break;
609 case COPY_ERROR_SRC_READ:
610 strlcpy(message, "failed to read the extension path", size);
611 break;
612 case COPY_ERROR_DST_WRITE:
613 strlcpy(message, "failed to write the extension path", size);
614 break;
615 case COPY_ERROR_SRC_STAT:
616 strlcpy(message, "failed to stat the extension path to copy permissions", size);
617 break;
618 case COPY_ERROR_DST_CHMOD:
619 strlcpy(message, "failed to set permissions to the copied extension path", size);
620 break;
621 case COPY_ERROR_SYSERR:
622 strlcpy(message, strerror(errno), size);
623 break;
624 case COPY_ERROR_NONE: /* shouldn't be called */
625 default:
626 rb_bug("unknown return value of copy_ext_file: %d", copy_retvalue);
627 }
628#endif
629 return message;
630}
631
632#ifndef _WIN32
633static enum copy_error_type
634copy_stream(int src_fd, int dst_fd)
635{
636 char buffer[1024];
637 ssize_t rsize;
638
639 while ((rsize = read(src_fd, buffer, sizeof(buffer))) != 0) {
640 if (rsize < 0) return COPY_ERROR_SRC_READ;
641 for (size_t written = 0; written < (size_t)rsize;) {
642 ssize_t wsize = write(dst_fd, buffer+written, rsize-written);
643 if (wsize < 0) return COPY_ERROR_DST_WRITE;
644 written += (size_t)wsize;
645 }
646 }
647 return COPY_ERROR_NONE;
648}
649#endif
650
651static enum copy_error_type
652copy_ext_file(const char *src_path, const char *dst_path)
653{
654#if defined(_WIN32)
655 WCHAR *w_src = rb_w32_mbstr_to_wstr(CP_UTF8, src_path, -1, NULL);
656 WCHAR *w_dst = rb_w32_mbstr_to_wstr(CP_UTF8, dst_path, -1, NULL);
657 if (!w_src || !w_dst) {
658 free(w_src);
659 free(w_dst);
660 rb_memerror();
661 }
662
663 enum copy_error_type rvalue = CopyFileW(w_src, w_dst, TRUE) ?
664 COPY_ERROR_NONE : COPY_ERROR_SYSERR;
665 free(w_src);
666 free(w_dst);
667 return rvalue;
668#else
669# ifdef O_BINARY
670 const int bin = O_BINARY;
671# else
672 const int bin = 0;
673# endif
674# ifdef O_CLOEXEC
675 const int cloexec = O_CLOEXEC;
676# else
677 const int cloexec = 0;
678# endif
679 const int src_fd = open(src_path, O_RDONLY|cloexec|bin);
680 if (src_fd < 0) return COPY_ERROR_SRC_OPEN;
681 if (!cloexec) rb_maygvl_fd_fix_cloexec(src_fd);
682
683 struct stat src_st;
684 if (fstat(src_fd, &src_st)) {
685 close(src_fd);
686 return COPY_ERROR_SRC_STAT;
687 }
688
689 const int dst_fd = open(dst_path, O_WRONLY|O_CREAT|O_EXCL|cloexec|bin, S_IRWXU);
690 if (dst_fd < 0) {
691 close(src_fd);
692 return COPY_ERROR_DST_OPEN;
693 }
694 if (!cloexec) rb_maygvl_fd_fix_cloexec(dst_fd);
695
696 enum copy_error_type ret = COPY_ERROR_NONE;
697
698 if (fchmod(dst_fd, src_st.st_mode & 0777)) {
699 ret = COPY_ERROR_DST_CHMOD;
700 goto done;
701 }
702
703 const size_t count_max = (SIZE_MAX >> 1) + 1;
704 (void)count_max;
705
706# ifdef HAVE_COPY_FILE_RANGE
707 for (;;) {
708 ssize_t written = copy_file_range(src_fd, NULL, dst_fd, NULL, count_max, 0);
709 if (written == 0) goto done;
710 if (written < 0) break;
711 }
712# endif
713# ifdef HAVE_FCOPYFILE
714 if (fcopyfile(src_fd, dst_fd, NULL, COPYFILE_DATA) == 0) {
715 goto done;
716 }
717# endif
718# ifdef USE_SENDFILE
719 for (;;) {
720 ssize_t written = sendfile(src_fd, dst_fd, NULL count_max);
721 if (written == 0) goto done;
722 if (written < 0) break;
723 }
724# endif
725 ret = copy_stream(src_fd, dst_fd);
726
727 done:
728 close(src_fd);
729 if (dst_fd >= 0) close(dst_fd);
730 if (ret != COPY_ERROR_NONE) unlink(dst_path);
731 return ret;
732#endif
733}
734
735#if defined __CYGWIN__ || defined DOSISH
736#define isdirsep(x) ((x) == '/' || (x) == '\\')
737#else
738#define isdirsep(x) ((x) == '/')
739#endif
740
741#define IS_SOEXT(e) (strcmp((e), ".so") == 0 || strcmp((e), ".o") == 0)
742#define IS_DLEXT(e) (strcmp((e), DLEXT) == 0)
743
744static void
745fname_without_suffix(const char *fname, char *rvalue, size_t rsize)
746{
747 size_t len = strlen(fname);
748 const char *pos;
749 for (pos = fname + len; pos > fname; pos--) {
750 if (IS_SOEXT(pos) || IS_DLEXT(pos)) {
751 len = pos - fname;
752 break;
753 }
754 if (fname + len - pos > DLEXT_MAXLEN) break;
755 }
756 if (len > rsize - 1) len = rsize - 1;
757 memcpy(rvalue, fname, len);
758 rvalue[len] = '\0';
759}
760
761static void
762escaped_basename(const char *path, const char *fname, char *rvalue, size_t rsize)
763{
764 char *pos;
765 const char *leaf = path, *found;
766 // `leaf + 1` looks uncomfortable (when leaf == path), but fname must not be the top-dir itself
767 while ((found = strstr(leaf + 1, fname)) != NULL) {
768 leaf = found; // find the last occurrence for the path like /etc/my-crazy-lib-dir/etc.so
769 }
770 strlcpy(rvalue, leaf, rsize);
771 for (pos = rvalue; *pos; pos++) {
772 if (isdirsep(*pos)) {
773 *pos = '+';
774 }
775 }
776}
777
778static void
779box_ext_cleanup_mark(void *p)
780{
781 rb_gc_mark((VALUE)p);
782}
783
784static void
785box_ext_cleanup_free(void *p)
786{
787 VALUE path = (VALUE)p;
788 unlink(RSTRING_PTR(path));
789}
790
791static const rb_data_type_t box_ext_cleanup_type = {
792 "box_ext_cleanup",
793 {box_ext_cleanup_mark, box_ext_cleanup_free},
795};
796
797void
798rb_box_cleanup_local_extension(VALUE cleanup)
799{
800 void *p = DATA_PTR(cleanup);
801 DATA_PTR(cleanup) = NULL;
802#ifndef _WIN32
803 if (p) box_ext_cleanup_free(p);
804#endif
805 (void)p;
806}
807
808#if defined(_WIN32)
810 struct box_local_ext_list *next;
811 HMODULE handle;
812};
813static struct box_local_ext_list *box_local_exts;
814#endif
815
816void
817rb_box_defer_unload_local_extension(void *handle)
818{
819#if defined(_WIN32)
820 /* A box-local copy of an extension DLL must stay loaded as long as
821 * objects created by it can be finalized; unloading is deferred to
822 * rb_box_unload_local_extensions after the objspace is destructed.
823 * The loaded copy cannot be deleted on Windows, so the file is also
824 * removed there instead of in box_ext_cleanup_free. */
825 struct box_local_ext_list *ext = malloc(sizeof(struct box_local_ext_list));
826 if (!ext) return;
827 ext->handle = (HMODULE)handle;
828 ext->next = box_local_exts;
829 box_local_exts = ext;
830#endif
831}
832
833void
834rb_box_unload_local_extensions(void)
835{
836#if defined(_WIN32)
837 struct box_local_ext_list *ext = box_local_exts;
838 box_local_exts = NULL;
839 while (ext) {
840 struct box_local_ext_list *next = ext->next;
841 WCHAR module_path[MAXPATHLEN];
842 DWORD len = GetModuleFileNameW(ext->handle, module_path, numberof(module_path));
843
844 FreeLibrary(ext->handle);
845 if (len > 0 && len < numberof(module_path)) DeleteFileW(module_path);
846 free(ext);
847 ext = next;
848 }
849#endif
850}
851
852VALUE
853rb_box_local_extension(VALUE box_value, VALUE fname, VALUE path, VALUE *cleanup)
854{
855 char ext_path[MAXPATHLEN], fname2[MAXPATHLEN], basename[MAXPATHLEN];
856 int wrote;
857 const char *src_path = RSTRING_PTR(path), *fname_ptr = RSTRING_PTR(fname);
858 rb_box_t *box = rb_get_box_t(box_value);
859
860 fname_without_suffix(fname_ptr, fname2, sizeof(fname2));
861 escaped_basename(src_path, fname2, basename, sizeof(basename));
862
863 wrote = sprint_ext_filename(ext_path, sizeof(ext_path), box->box_id, BOX_TMP_PREFIX, basename);
864 if (wrote >= (int)sizeof(ext_path)) {
865 rb_bug("Extension file path in the box was too long");
866 }
867 VALUE new_path = rb_str_new_cstr(ext_path);
868 *cleanup = TypedData_Wrap_Struct(0, &box_ext_cleanup_type, NULL);
869 enum copy_error_type copy_error = copy_ext_file(src_path, ext_path);
870 if (copy_error) {
871 char message[1024];
872 copy_ext_file_error(message, sizeof(message), copy_error);
873 rb_raise(rb_eLoadError, "can't prepare the extension file for Ruby Box (%s from %"PRIsVALUE"): %s", ext_path, path, message);
874 }
875 DATA_PTR(*cleanup) = (void *)new_path;
876 return new_path;
877}
878
879static VALUE
880rb_box_load(int argc, VALUE *argv, VALUE box)
881{
882 VALUE fname, wrap;
883 rb_scan_args(argc, argv, "11", &fname, &wrap);
884
885 rb_vm_frame_flag_set_box_require(GET_EC());
886
887 return rb_load_entrypoint(fname, wrap);
888}
889
890static VALUE
891rb_box_require(VALUE box, VALUE fname)
892{
893 rb_vm_frame_flag_set_box_require(GET_EC());
894
895 return rb_require_string(fname);
896}
897
898static VALUE
899rb_box_require_relative(VALUE box, VALUE fname)
900{
901 rb_vm_frame_flag_set_box_require(GET_EC());
902
903 return rb_require_relative_entrypoint(fname);
904}
905
906static void
907initialize_master_box(void)
908{
909 rb_vm_t *vm = GET_VM();
910 rb_box_t *master = (rb_box_t *)rb_master_box();
911
912 master->load_path = rb_ary_new();
913 master->expanded_load_path = rb_ary_hidden_new(0);
914 master->load_path_snapshot = rb_ary_hidden_new(0);
915 master->load_path_check_cache = 0;
916 rb_define_singleton_method(master->load_path, "resolve_feature_path", rb_resolve_feature_path, 1);
917
918 master->loaded_features = rb_ary_new();
919 master->loaded_features_snapshot = rb_ary_hidden_new(0);
920 master->loaded_features_index = st_init_numtable();
921 master->loaded_features_realpaths = rb_hash_new();
922 rb_obj_hide(master->loaded_features_realpaths);
923 master->loaded_features_realpath_map = rb_hash_new();
924 rb_obj_hide(master->loaded_features_realpath_map);
925
926 master->ruby_dln_libmap = rb_hash_new();
927 master->gvar_tbl = rb_hash_new();
928 master->classext_cow_classes = NULL; // classext CoW never happen on the master box
929
930 vm->master_box = master;
931
932 if (rb_box_available()) {
933 VALUE master_box, entry;
934 ID id_box_entry;
935 CONST_ID(id_box_entry, "__box_entry__");
936
937 master_box = rb_obj_alloc(rb_cBox);
938 RCLASS_SET_PRIME_CLASSEXT_WRITABLE(master_box, true);
939 RCLASS_SET_CONST_TBL(master_box, RCLASSEXT_CONST_TBL(RCLASS_EXT_PRIME(rb_cObject)), true);
940
941 master->box_id = box_generate_id();
942 master->box_object = master_box;
943
944 entry = TypedData_Wrap_Struct(rb_cBoxEntry, &rb_master_box_data_type, master);
945 rb_ivar_set(master_box, id_box_entry, entry);
946
947 rb_gc_register_mark_object(master_box);
948 rb_gc_register_mark_object(entry);
949 }
950 else {
951 master->box_id = 1;
952 master->box_object = Qnil;
953 }
954}
955
956static VALUE
957rb_box_eval(VALUE box_value, VALUE str)
958{
959 const rb_iseq_t *iseq;
960 const rb_box_t *box;
961
962 StringValue(str);
963
964 iseq = rb_iseq_compile_iseq(str, rb_str_new_cstr("eval"));
965 VM_ASSERT(iseq);
966
967 box = (const rb_box_t *)rb_get_box_t(box_value);
968
969 return rb_iseq_eval(iseq, box);
970}
971
972static int box_experimental_warned = 0;
973
974RUBY_EXTERN const char ruby_api_version_name[];
975
976static VALUE
977box_value_initialize(bool root, bool user, bool optional)
978{
979 rb_box_t *box;
980 VALUE box_value = rb_class_new_instance(0, NULL, rb_cBox);
981
982 VM_ASSERT(BOX_OBJ_P(box_value));
983
984 box = rb_get_box_t(box_value);
985 box->box_object = box_value;
986 box->is_root = root;
987 box->is_user = user;
988 box->is_optional = optional;
989 return box_value;
990}
991
992void
993rb_initialize_mandatory_boxes(void)
994{
995 VALUE root_box_value, main_box_value;
996 rb_vm_t *vm = GET_VM();
997
998 VM_ASSERT(rb_box_available());
999
1000 if (!box_experimental_warned) {
1002 "Ruby::Box is experimental, and the behavior may change in the future!\n"
1003 "See https://docs.ruby-lang.org/en/%s/Ruby/Box.html for known issues, etc.",
1004 ruby_api_version_name);
1005 box_experimental_warned = 1;
1006 }
1007
1008 root_box_value = box_value_initialize(true, false, false);
1009 main_box_value = box_value_initialize(false, true, false);
1010
1011 rb_const_set(rb_cBox, rb_intern("ROOT"), root_box_value);
1012 rb_const_set(rb_cBox, rb_intern("MAIN"), main_box_value);
1013
1014 vm->root_box = root_box = rb_get_box_t(root_box_value);
1015 vm->main_box = main_box = rb_get_box_t(main_box_value);
1016
1017 // create the writable classext of ::Object explicitly to finalize the set of visible top-level constants
1018 RCLASS_EXT_WRITABLE_IN_BOX(rb_cObject, root_box);
1019 RCLASS_EXT_WRITABLE_IN_BOX(rb_cObject, main_box);
1020}
1021
1022static VALUE
1023rb_box_inspect(VALUE obj)
1024{
1025 rb_box_t *box;
1026 VALUE r;
1027 if (obj == Qfalse) {
1028 r = rb_str_new_cstr("#<Ruby::Box:master>");
1029 return r;
1030 }
1031 box = rb_get_box_t(obj);
1032 r = rb_str_new_cstr("#<Ruby::Box:");
1033 rb_str_concat(r, rb_funcall(LONG2NUM(box->box_id), rb_intern("to_s"), 0));
1034 if (BOX_MASTER_P(box)) {
1035 rb_str_cat_cstr(r, ",master");
1036 }
1037 if (BOX_ROOT_P(box)) {
1038 rb_str_cat_cstr(r, ",root");
1039 }
1040 if (BOX_USER_P(box)) {
1041 rb_str_cat_cstr(r, ",user");
1042 }
1043 if (BOX_MAIN_P(box)) {
1044 rb_str_cat_cstr(r, ",main");
1045 }
1046 else if (BOX_OPTIONAL_P(box)) {
1047 rb_str_cat_cstr(r, ",optional");
1048 }
1049 rb_str_cat_cstr(r, ">");
1050 return r;
1051}
1052
1053static VALUE
1054rb_box_loading_func(int argc, VALUE *argv, VALUE _self)
1055{
1056 rb_vm_frame_flag_set_box_require(GET_EC());
1057 return rb_call_super(argc, argv);
1058}
1059
1060static void
1061box_define_loader_method(const char *name)
1062{
1063 rb_define_private_method(rb_mBoxLoader, name, rb_box_loading_func, -1);
1064 rb_define_singleton_method(rb_mBoxLoader, name, rb_box_loading_func, -1);
1065}
1066
1067void
1068Init_master_box(void)
1069{
1070 master_box->loading_table = st_init_strtable();
1071}
1072
1073void
1074Init_enable_box(void)
1075{
1076 const char *env = getenv("RUBY_BOX");
1077 if (env && strlen(env) == 1 && env[0] == '1') {
1078 ruby_box_enabled = true;
1079 }
1080 else {
1081 ruby_box_init_done = true;
1082 }
1083}
1084
1085/* :nodoc: */
1086static VALUE
1087rb_box_s_master(VALUE recv)
1088{
1089 return master_box->box_object;
1090}
1091
1092/* :nodoc: */
1093static VALUE
1094rb_box_s_root(VALUE recv)
1095{
1096 return root_box->box_object;
1097}
1098
1099/* :nodoc: */
1100static VALUE
1101rb_box_s_main(VALUE recv)
1102{
1103 return main_box->box_object;
1104}
1105
1106/* :nodoc: */
1107static VALUE
1108rb_box_master_p(VALUE box_value)
1109{
1110 const rb_box_t *box = (const rb_box_t *)rb_get_box_t(box_value);
1111 return RBOOL(BOX_MASTER_P(box));
1112}
1113
1114/* :nodoc: */
1115static VALUE
1116rb_box_root_p(VALUE box_value)
1117{
1118 const rb_box_t *box = (const rb_box_t *)rb_get_box_t(box_value);
1119 return RBOOL(BOX_ROOT_P(box));
1120}
1121
1122/* :nodoc: */
1123static VALUE
1124rb_box_main_p(VALUE box_value)
1125{
1126 const rb_box_t *box = (const rb_box_t *)rb_get_box_t(box_value);
1127 return RBOOL(BOX_MAIN_P(box));
1128}
1129
1130#if RUBY_DEBUG
1131
1132static const char *
1133classname(VALUE klass)
1134{
1135 VALUE p;
1136 if (!klass) {
1137 return "Qfalse";
1138 }
1139 p = RCLASSEXT_CLASSPATH(RCLASS_EXT_PRIME(klass));
1140 if (RTEST(p))
1141 return RSTRING_PTR(p);
1142 if (RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE) || RB_TYPE_P(klass, T_ICLASS))
1143 return "AnyClassValue";
1144 return "NonClassValue";
1145}
1146
1147static enum rb_id_table_iterator_result
1148dump_classext_methods_i(ID mid, VALUE _val, void *data)
1149{
1150 VALUE ary = (VALUE)data;
1151 rb_ary_push(ary, rb_id2str(mid));
1152 return ID_TABLE_CONTINUE;
1153}
1154
1155static enum rb_id_table_iterator_result
1156dump_classext_constants_i(ID mid, VALUE _val, void *data)
1157{
1158 VALUE ary = (VALUE)data;
1159 rb_ary_push(ary, rb_id2str(mid));
1160 return ID_TABLE_CONTINUE;
1161}
1162
1163static void
1164dump_classext_i(rb_classext_t *ext, bool is_prime, VALUE _recv, void *data)
1165{
1166 char buf[4096];
1167 struct rb_id_table *tbl;
1168 VALUE ary, res = (VALUE)data;
1169
1170 snprintf(buf, 4096, "Ruby::Box %ld:%s classext %p\n",
1171 RCLASSEXT_BOX(ext)->box_id, is_prime ? " prime" : "", (void *)ext);
1172 rb_str_cat_cstr(res, buf);
1173
1174 snprintf(buf, 2048, " Super: %s\n", classname(RCLASSEXT_SUPER(ext)));
1175 rb_str_cat_cstr(res, buf);
1176
1177 tbl = RCLASSEXT_M_TBL(ext);
1178 if (tbl) {
1179 ary = rb_ary_new_capa((long)rb_id_table_size(tbl));
1180 rb_id_table_foreach(RCLASSEXT_M_TBL(ext), dump_classext_methods_i, (void *)ary);
1181 rb_ary_sort_bang(ary);
1182 snprintf(buf, 4096, " Methods(%ld): ", RARRAY_LEN(ary));
1183 rb_str_cat_cstr(res, buf);
1184 rb_str_concat(res, rb_ary_join(ary, rb_str_new_cstr(",")));
1185 rb_str_cat_cstr(res, "\n");
1186 }
1187 else {
1188 rb_str_cat_cstr(res, " Methods(0): .\n");
1189 }
1190
1191 tbl = RCLASSEXT_CONST_TBL(ext);
1192 if (tbl) {
1193 ary = rb_ary_new_capa((long)rb_id_table_size(tbl));
1194 rb_id_table_foreach(tbl, dump_classext_constants_i, (void *)ary);
1195 rb_ary_sort_bang(ary);
1196 snprintf(buf, 4096, " Constants(%ld): ", RARRAY_LEN(ary));
1197 rb_str_cat_cstr(res, buf);
1198 rb_str_concat(res, rb_ary_join(ary, rb_str_new_cstr(",")));
1199 rb_str_cat_cstr(res, "\n");
1200 }
1201 else {
1202 rb_str_cat_cstr(res, " Constants(0): .\n");
1203 }
1204}
1205
1206/* :nodoc: */
1207static VALUE
1208rb_f_dump_classext(VALUE recv, VALUE klass)
1209{
1210 /*
1211 * The desired output String value is:
1212 * Class: 0x88800932 (String) [singleton]
1213 * Prime classext box(2,main), readable(t), writable(f)
1214 * Non-prime classexts: 3
1215 * Box 2: prime classext 0x88800933
1216 * Super: Object
1217 * Methods(43): aaaaa, bbbb, cccc, dddd, eeeee, ffff, gggg, hhhhh, ...
1218 * Constants(12): FOO, Bar, ...
1219 * Box 5: classext 0x88800934
1220 * Super: Object
1221 * Methods(43): aaaaa, bbbb, cccc, dddd, eeeee, ffff, gggg, hhhhh, ...
1222 * Constants(12): FOO, Bar, ...
1223 */
1224 char buf[2048];
1225 VALUE res;
1226 const rb_classext_t *ext;
1227 const rb_box_t *box;
1228 st_table *classext_tbl;
1229
1230 if (!(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE))) {
1231 snprintf(buf, 2048, "Non-class/module value: %p (%s)\n", (void *)klass, rb_type_str(BUILTIN_TYPE(klass)));
1232 return rb_str_new_cstr(buf);
1233 }
1234
1235 if (RB_TYPE_P(klass, T_CLASS)) {
1236 snprintf(buf, 2048, "Class: %p (%s)%s\n",
1237 (void *)klass, classname(klass), RCLASS_SINGLETON_P(klass) ? " [singleton]" : "");
1238 }
1239 else {
1240 snprintf(buf, 2048, "Module: %p (%s)\n", (void *)klass, classname(klass));
1241 }
1242 res = rb_str_new_cstr(buf);
1243
1244 ext = RCLASS_EXT_PRIME(klass);
1245 box = RCLASSEXT_BOX(ext);
1246 snprintf(buf, 2048, "Prime classext box(%ld,%s), readable(%s), writable(%s)\n",
1247 box->box_id,
1248 BOX_MASTER_P(box) ? "master" : (BOX_ROOT_P(box) ? "root" : (BOX_MAIN_P(box) ? "main" : "optional")),
1249 RCLASS_PRIME_CLASSEXT_READABLE_P(klass) ? "t" : "f",
1250 RCLASS_PRIME_CLASSEXT_WRITABLE_P(klass) ? "t" : "f");
1251 rb_str_cat_cstr(res, buf);
1252
1253 classext_tbl = RCLASS_CLASSEXT_TBL(klass);
1254 if (!classext_tbl) {
1255 rb_str_cat_cstr(res, "Non-prime classexts: 0\n");
1256 }
1257 else {
1258 snprintf(buf, 2048, "Non-prime classexts: %zu\n", st_table_size(classext_tbl));
1259 rb_str_cat_cstr(res, buf);
1260 }
1261
1262 rb_class_classext_foreach(klass, dump_classext_i, (void *)res);
1263
1264 return res;
1265}
1266
1267#endif /* RUBY_DEBUG */
1268
1269/*
1270 * Document-class: Ruby::Box
1271 *
1272 * :markup: markdown
1273 * :include: doc/language/box.md
1274 */
1275void
1276Init_Box(void)
1277{
1278 tmp_dir = system_tmpdir();
1279 tmp_dir_has_dirsep = (strcmp(tmp_dir + (strlen(tmp_dir) - strlen(DIRSEP)), DIRSEP) == 0);
1280
1281 VALUE mRuby = rb_define_module("Ruby");
1282
1283 rb_cBox = rb_define_class_under(mRuby, "Box", rb_cModule);
1284 rb_define_method(rb_cBox, "initialize", box_initialize, 0);
1285
1286 /* :nodoc: */
1287 rb_cBoxEntry = rb_define_class_under(rb_cBox, "Entry", rb_cObject);
1288 rb_define_alloc_func(rb_cBoxEntry, rb_box_entry_alloc);
1289
1290 initialize_master_box();
1291
1292 /* :nodoc: */
1293 rb_mBoxLoader = rb_define_module_under(rb_cBox, "Loader");
1294 box_define_loader_method("require");
1295 box_define_loader_method("require_relative");
1296 box_define_loader_method("load");
1297
1298 if (rb_box_available()) {
1299 rb_include_module(rb_cObject, rb_mBoxLoader);
1300
1301 rb_define_singleton_method(rb_cBox, "master", rb_box_s_master, 0);
1302 rb_define_singleton_method(rb_cBox, "root", rb_box_s_root, 0);
1303 rb_define_singleton_method(rb_cBox, "main", rb_box_s_main, 0);
1304 rb_define_method(rb_cBox, "master?", rb_box_master_p, 0);
1305 rb_define_method(rb_cBox, "root?", rb_box_root_p, 0);
1306 rb_define_method(rb_cBox, "main?", rb_box_main_p, 0);
1307
1308#if RUBY_DEBUG
1309 rb_define_global_function("dump_classext", rb_f_dump_classext, 1);
1310#endif
1311 }
1312
1313 rb_define_singleton_method(rb_cBox, "enabled?", rb_box_s_getenabled, 0);
1314 rb_define_singleton_method(rb_cBox, "current", rb_box_s_current, 0);
1315
1316 rb_define_method(rb_cBox, "load_path", rb_box_load_path, 0);
1317 rb_define_method(rb_cBox, "load", rb_box_load, -1);
1318 rb_define_method(rb_cBox, "require", rb_box_require, 1);
1319 rb_define_method(rb_cBox, "require_relative", rb_box_require_relative, 1);
1320 rb_define_method(rb_cBox, "eval", rb_box_eval, 1);
1321
1322 rb_define_method(rb_cBox, "inspect", rb_box_inspect, 0);
1323}
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
#define rb_define_private_method(klass, mid, func, arity)
Defines klass#mid and makes it private.
#define rb_define_global_function(mid, func, arity)
Defines rb_mKernel #mid.
#define RUBY_EXTERN
Declaration of externally visible global variables.
Definition dllexport.h:45
Ruby-level global variables / constants, visible from C.
void rb_include_module(VALUE klass, VALUE module)
Includes a module to a class.
Definition class.c:1608
VALUE rb_singleton_class(VALUE obj)
Finds or creates the singleton class of the passed object.
Definition class.c:2865
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition class.c:2908
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Retrieves argument from argc and argv to given VALUE references according to the format string.
Definition class.c:3198
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1676
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define T_MODULE
Old name of RUBY_T_MODULE.
Definition value_type.h:70
#define T_ICLASS
Old name of RUBY_T_ICLASS.
Definition value_type.h:66
#define LONG2NUM
Old name of RB_LONG2NUM.
Definition long.h:50
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define NIL_P
Old name of RB_NIL_P.
#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 FIXNUM_P
Old name of RB_FIXNUM_P.
#define CONST_ID
Old name of RUBY_CONST_ID.
Definition symbol.h:47
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:478
VALUE rb_eLoadError
LoadError exception.
Definition error.c:1449
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1429
@ RB_WARN_CATEGORY_EXPERIMENTAL
Warning is for experimental features.
Definition error.h:51
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:2250
VALUE rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
Allocates, then initialises an instance of the given class.
Definition object.c:2291
VALUE rb_cBox
Ruby::Box class.
Definition box.c:37
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:94
VALUE rb_class_new_instance_pass_kw(int argc, const VALUE *argv, VALUE klass)
Identical to rb_class_new_instance(), except it passes the passed keywords if any to the #initialize ...
Definition object.c:2268
VALUE rb_cModule
Module class.
Definition object.c:61
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1123
VALUE rb_call_super(int argc, const VALUE *argv)
This resembles ruby's super.
Definition vm_eval.c:363
VALUE rb_ary_dup(VALUE ary)
Duplicates an array.
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_new_capa(long capa)
Identical to rb_ary_new(), except it additionally specifies how many rooms of objects it should alloc...
VALUE rb_ary_hidden_new(long capa)
Allocates a hidden (no class) empty array.
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
VALUE rb_ary_sort_bang(VALUE ary)
Destructively sorts the passed array in-place, according to each elements' <=> result.
VALUE rb_ary_join(VALUE ary, VALUE sep)
Recursively stringises the elements of the passed array, flattens that result, then joins the sequenc...
VALUE rb_require_string(VALUE feature)
Finds and loads the given feature, if absent.
Definition load.c:1459
VALUE rb_str_concat(VALUE dst, VALUE src)
Identical to rb_str_append(), except it also accepts an integer as a codepoint.
Definition string.c:4134
#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:1657
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1515
VALUE rb_ivar_set(VALUE obj, ID name, VALUE val)
Identical to rb_iv_set(), except it accepts the name as an ID instead of a C string.
Definition variable.c:2059
void rb_const_set(VALUE space, ID name, VALUE val)
Names a constant.
Definition variable.c:3886
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
int len
Length of the buffer.
Definition io.h:8
char * ruby_strdup(const char *str)
This is our own version of strdup(3) that uses ruby_xmalloc() instead of system malloc (benefits our ...
Definition util.c:515
#define PRI_PIDT_PREFIX
A rb_sprintf() format prefix to be used for a pid_t parameter.
Definition pid_t.h:38
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:50
#define StringValue(v)
Ensures that the parameter object is a String.
Definition rstring.h:66
#define RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
Definition rtypeddata.h:122
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:773
#define DATA_PTR(obj)
Convenient casting macro for backward compatibility.
Definition rtypeddata.h:435
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:557
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:604
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
#define RTEST
This is an old name of RB_TEST.
Internal header for Ruby Box.
Definition box.h:14
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:238
Definition st.h:79
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 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