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