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