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