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