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