Ruby 4.0.0dev (2025-11-26 revision 38022961ae399015c542c409948aa4885e8e56e0)
load.c (38022961ae399015c542c409948aa4885e8e56e0)
1/*
2 * load methods from eval.c
3 */
4
5#include "dln.h"
6#include "eval_intern.h"
7#include "internal.h"
8#include "internal/box.h"
9#include "internal/dir.h"
10#include "internal/error.h"
11#include "internal/eval.h"
12#include "internal/file.h"
13#include "internal/hash.h"
14#include "internal/load.h"
15#include "internal/ruby_parser.h"
16#include "internal/thread.h"
17#include "internal/variable.h"
18#include "iseq.h"
19#include "probes.h"
20#include "darray.h"
21#include "ruby/encoding.h"
22#include "ruby/util.h"
23#include "ractor_core.h"
24#include "vm_core.h"
25
26#define IS_RBEXT(e) (strcmp((e), ".rb") == 0)
27#define IS_SOEXT(e) (strcmp((e), ".so") == 0 || strcmp((e), ".o") == 0)
28#define IS_DLEXT(e) (strcmp((e), DLEXT) == 0)
29
30#if SIZEOF_VALUE <= SIZEOF_LONG
31# define SVALUE2NUM(x) LONG2NUM((long)(x))
32# define NUM2SVALUE(x) (SIGNED_VALUE)NUM2LONG(x)
33#elif SIZEOF_VALUE <= SIZEOF_LONG_LONG
34# define SVALUE2NUM(x) LL2NUM((LONG_LONG)(x))
35# define NUM2SVALUE(x) (SIGNED_VALUE)NUM2LL(x)
36#else
37# error Need integer for VALUE
38#endif
39
40enum {
41 loadable_ext_rb = (0+ /* .rb extension is the first in both tables */
42 1) /* offset by rb_find_file_ext() */
43};
44
45static const char *const loadable_ext[] = {
46 ".rb", DLEXT,
47 0
48};
49
50static const char *const ruby_ext[] = {
51 ".rb",
52 0
53};
54
55enum expand_type {
56 EXPAND_ALL,
57 EXPAND_RELATIVE,
58 EXPAND_HOME,
59 EXPAND_NON_CACHE
60};
61
62/* Construct expanded load path and store it to cache.
63 We rebuild load path partially if the cache is invalid.
64 We don't cache non string object and expand it every time. We ensure that
65 string objects in $LOAD_PATH are frozen.
66 */
67static void
68rb_construct_expanded_load_path(rb_box_t *box, enum expand_type type, int *has_relative, int *has_non_cache)
69{
70 VALUE load_path = box->load_path;
71 VALUE expanded_load_path = box->expanded_load_path;
72 VALUE snapshot;
73 VALUE ary;
74 long i;
75
76 ary = rb_ary_hidden_new(RARRAY_LEN(load_path));
77 for (i = 0; i < RARRAY_LEN(load_path); ++i) {
78 VALUE path, as_str, expanded_path;
79 int is_string, non_cache;
80 char *as_cstr;
81 as_str = path = RARRAY_AREF(load_path, i);
82 is_string = RB_TYPE_P(path, T_STRING) ? 1 : 0;
83 non_cache = !is_string ? 1 : 0;
84 as_str = rb_get_path_check_to_string(path);
85 as_cstr = RSTRING_PTR(as_str);
86
87 if (!non_cache) {
88 if ((type == EXPAND_RELATIVE &&
89 rb_is_absolute_path(as_cstr)) ||
90 (type == EXPAND_HOME &&
91 (!as_cstr[0] || as_cstr[0] != '~')) ||
92 (type == EXPAND_NON_CACHE)) {
93 /* Use cached expanded path. */
94 rb_ary_push(ary, RARRAY_AREF(expanded_load_path, i));
95 continue;
96 }
97 }
98 if (!*has_relative && !rb_is_absolute_path(as_cstr))
99 *has_relative = 1;
100 if (!*has_non_cache && non_cache)
101 *has_non_cache = 1;
102 /* Freeze only string object. We expand other objects every time. */
103 if (is_string)
104 rb_str_freeze(path);
105 as_str = rb_get_path_check_convert(as_str);
106 expanded_path = rb_check_realpath(Qnil, as_str, NULL);
107 if (NIL_P(expanded_path)) expanded_path = as_str;
108 rb_ary_push(ary, rb_fstring(expanded_path));
109 }
110 rb_ary_freeze(ary);
111 box->expanded_load_path = ary;
112 snapshot = box->load_path_snapshot;
113 load_path = box->load_path;
114 rb_ary_replace(snapshot, load_path);
115}
116
117static VALUE
118get_expanded_load_path(rb_box_t *box)
119{
120 VALUE check_cache;
121 const VALUE non_cache = Qtrue;
122 const VALUE load_path_snapshot = box->load_path_snapshot;
123 const VALUE load_path = box->load_path;
124
125 if (!rb_ary_shared_with_p(load_path_snapshot, load_path)) {
126 /* The load path was modified. Rebuild the expanded load path. */
127 int has_relative = 0, has_non_cache = 0;
128 rb_construct_expanded_load_path(box, EXPAND_ALL, &has_relative, &has_non_cache);
129 if (has_relative) {
130 box->load_path_check_cache = rb_dir_getwd_ospath();
131 }
132 else if (has_non_cache) {
133 /* Non string object. */
134 box->load_path_check_cache = non_cache;
135 }
136 else {
137 box->load_path_check_cache = 0;
138 }
139 }
140 else if ((check_cache = box->load_path_check_cache) == non_cache) {
141 int has_relative = 1, has_non_cache = 1;
142 /* Expand only non-cacheable objects. */
143 rb_construct_expanded_load_path(box, EXPAND_NON_CACHE,
144 &has_relative, &has_non_cache);
145 }
146 else if (check_cache) {
147 int has_relative = 1, has_non_cache = 1;
148 VALUE cwd = rb_dir_getwd_ospath();
149 if (!rb_str_equal(check_cache, cwd)) {
150 /* Current working directory or filesystem encoding was changed.
151 Expand relative load path and non-cacheable objects again. */
152 box->load_path_check_cache = cwd;
153 rb_construct_expanded_load_path(box, EXPAND_RELATIVE,
154 &has_relative, &has_non_cache);
155 }
156 else {
157 /* Expand only tilde (User HOME) and non-cacheable objects. */
158 rb_construct_expanded_load_path(box, EXPAND_HOME,
159 &has_relative, &has_non_cache);
160 }
161 }
162 return box->expanded_load_path;
163}
164
165VALUE
166rb_get_expanded_load_path(void)
167{
168 return get_expanded_load_path((rb_box_t *)rb_loading_box());
169}
170
171static VALUE
172load_path_getter(ID _x, VALUE * _y)
173{
174 return rb_loading_box()->load_path;
175}
176
177static VALUE
178get_LOADED_FEATURES(ID _x, VALUE *_y)
179{
180 return rb_loading_box()->loaded_features;
181}
182
183static void
184reset_loaded_features_snapshot(const rb_box_t *box)
185{
186 VALUE snapshot = box->loaded_features_snapshot;
187 VALUE loaded_features = box->loaded_features;
188 rb_ary_replace(snapshot, loaded_features);
189}
190
191static struct st_table *
192get_loaded_features_index_raw(const rb_box_t *box)
193{
194 return box->loaded_features_index;
195}
196
197static st_data_t
198feature_key(const char *str, size_t len)
199{
200 return st_hash(str, len, 0xfea7009e);
201}
202
203static bool
204is_rbext_path(VALUE feature_path)
205{
206 long len = RSTRING_LEN(feature_path);
207 long rbext_len = rb_strlen_lit(".rb");
208 if (len <= rbext_len) return false;
209 return IS_RBEXT(RSTRING_PTR(feature_path) + len - rbext_len);
210}
211
212typedef rb_darray(long) feature_indexes_t;
213
214struct features_index_add_single_args {
215 const rb_box_t *box;
216 VALUE offset;
217 bool rb;
218};
219
220static int
221features_index_add_single_callback(st_data_t *key, st_data_t *value, st_data_t raw_args, int existing)
222{
223 struct features_index_add_single_args *args = (struct features_index_add_single_args *)raw_args;
224 const rb_box_t *box = args->box;
225 VALUE offset = args->offset;
226 bool rb = args->rb;
227
228 if (existing) {
229 VALUE this_feature_index = *value;
230
231 if (FIXNUM_P(this_feature_index)) {
232 VALUE loaded_features = box->loaded_features;
233 VALUE this_feature_path = RARRAY_AREF(loaded_features, FIX2LONG(this_feature_index));
234
235 feature_indexes_t feature_indexes;
236 rb_darray_make(&feature_indexes, 2);
237 int top = (rb && !is_rbext_path(this_feature_path)) ? 1 : 0;
238 rb_darray_set(feature_indexes, top^0, FIX2LONG(this_feature_index));
239 rb_darray_set(feature_indexes, top^1, FIX2LONG(offset));
240
241 RUBY_ASSERT(rb_darray_size(feature_indexes) == 2);
242 // assert feature_indexes does not look like a special const
243 RUBY_ASSERT(!SPECIAL_CONST_P((VALUE)feature_indexes));
244
245 *value = (st_data_t)feature_indexes;
246 }
247 else {
248 feature_indexes_t feature_indexes = (feature_indexes_t)this_feature_index;
249 long pos = -1;
250
251 if (rb) {
252 VALUE loaded_features = box->loaded_features;
253 for (size_t i = 0; i < rb_darray_size(feature_indexes); ++i) {
254 long idx = rb_darray_get(feature_indexes, i);
255 VALUE this_feature_path = RARRAY_AREF(loaded_features, idx);
256 Check_Type(this_feature_path, T_STRING);
257 if (!is_rbext_path(this_feature_path)) {
258 pos = i;
259 break;
260 }
261 }
262 }
263
264 rb_darray_append(&feature_indexes, FIX2LONG(offset));
265 /* darray may realloc which will change the pointer */
266 *value = (st_data_t)feature_indexes;
267
268 if (pos >= 0) {
269 long *ptr = rb_darray_data_ptr(feature_indexes);
270 long len = rb_darray_size(feature_indexes);
271 MEMMOVE(ptr + pos + 1, ptr + pos, long, len - pos - 1);
272 ptr[pos] = FIX2LONG(offset);
273 }
274 }
275 }
276 else {
277 *value = offset;
278 }
279
280 return ST_CONTINUE;
281}
282
283static void
284features_index_add_single(const rb_box_t *box, const char* str, size_t len, VALUE offset, bool rb)
285{
286 struct st_table *features_index;
287 st_data_t short_feature_key;
288
289 Check_Type(offset, T_FIXNUM);
290 short_feature_key = feature_key(str, len);
291
292 features_index = get_loaded_features_index_raw(box);
293
294 struct features_index_add_single_args args = {
295 .box = box,
296 .offset = offset,
297 .rb = rb,
298 };
299
300 st_update(features_index, short_feature_key, features_index_add_single_callback, (st_data_t)&args);
301}
302
303/* Add to the loaded-features index all the required entries for
304 `feature`, located at `offset` in $LOADED_FEATURES. We add an
305 index entry at each string `short_feature` for which
306 feature == "#{prefix}#{short_feature}#{ext}"
307 where `ext` is empty or matches %r{^\.[^./]*$}, and `prefix` is empty
308 or ends in '/'. This maintains the invariant that `rb_feature_p()`
309 relies on for its fast lookup.
310*/
311static void
312features_index_add(const rb_box_t *box, VALUE feature, VALUE offset)
313{
314 RUBY_ASSERT(rb_ractor_main_p());
315
316 const char *feature_str, *feature_end, *ext, *p;
317 bool rb = false;
318
319 feature_str = StringValuePtr(feature);
320 feature_end = feature_str + RSTRING_LEN(feature);
321
322 for (ext = feature_end; ext > feature_str; ext--)
323 if (*ext == '.' || *ext == '/')
324 break;
325 if (*ext != '.')
326 ext = NULL;
327 else
328 rb = IS_RBEXT(ext);
329 /* Now `ext` points to the only string matching %r{^\.[^./]*$} that is
330 at the end of `feature`, or is NULL if there is no such string. */
331
332 p = ext ? ext : feature_end;
333 while (1) {
334 p--;
335 while (p >= feature_str && *p != '/')
336 p--;
337 if (p < feature_str)
338 break;
339 /* Now *p == '/'. We reach this point for every '/' in `feature`. */
340 features_index_add_single(box, p + 1, feature_end - p - 1, offset, false);
341 if (ext) {
342 features_index_add_single(box, p + 1, ext - p - 1, offset, rb);
343 }
344 }
345 features_index_add_single(box, feature_str, feature_end - feature_str, offset, false);
346 if (ext) {
347 features_index_add_single(box, feature_str, ext - feature_str, offset, rb);
348 }
349}
350
351static int
352loaded_features_index_clear_i(st_data_t key, st_data_t val, st_data_t arg)
353{
354 VALUE obj = (VALUE)val;
355 if (!SPECIAL_CONST_P(obj)) {
356 rb_darray_free((void *)obj);
357 }
358 return ST_DELETE;
359}
360
361static st_table *
362get_loaded_features_index(const rb_box_t *box)
363{
364 int i;
365 VALUE features = box->loaded_features;
366 const VALUE snapshot = box->loaded_features_snapshot;
367
368 if (!rb_ary_shared_with_p(snapshot, features)) {
369 /* The sharing was broken; something (other than us in rb_provide_feature())
370 modified loaded_features. Rebuild the index. */
371 st_foreach(box->loaded_features_index, loaded_features_index_clear_i, 0);
372
373 VALUE realpaths = box->loaded_features_realpaths;
374 VALUE realpath_map = box->loaded_features_realpath_map;
375 VALUE previous_realpath_map = rb_hash_dup(realpath_map);
376 rb_hash_clear(realpaths);
377 rb_hash_clear(realpath_map);
378
379 /* We have to make a copy of features here because the StringValue call
380 * below could call a Ruby method, which could modify $LOADED_FEATURES
381 * and cause it to be corrupt. */
382 features = rb_ary_resurrect(features);
383 for (i = 0; i < RARRAY_LEN(features); i++) {
384 VALUE entry, as_str;
385 as_str = entry = rb_ary_entry(features, i);
386 StringValue(as_str);
387 as_str = rb_fstring(as_str);
388 if (as_str != entry)
389 rb_ary_store(features, i, as_str);
390 features_index_add(box, as_str, INT2FIX(i));
391 }
392 /* The user modified $LOADED_FEATURES, so we should restore the changes. */
393 if (!rb_ary_shared_with_p(features, box->loaded_features)) {
394 rb_ary_replace(box->loaded_features, features);
395 }
396 reset_loaded_features_snapshot(box);
397
398 features = box->loaded_features_snapshot;
399 long j = RARRAY_LEN(features);
400 for (i = 0; i < j; i++) {
401 VALUE as_str = rb_ary_entry(features, i);
402 VALUE realpath = rb_hash_aref(previous_realpath_map, as_str);
403 if (NIL_P(realpath)) {
404 realpath = rb_check_realpath(Qnil, as_str, NULL);
405 if (NIL_P(realpath)) realpath = as_str;
406 realpath = rb_fstring(realpath);
407 }
408 rb_hash_aset(realpaths, realpath, Qtrue);
409 rb_hash_aset(realpath_map, as_str, realpath);
410 }
411 }
412 return box->loaded_features_index;
413}
414
415/* This searches `load_path` for a value such that
416 name == "#{load_path[i]}/#{feature}"
417 if `feature` is a suffix of `name`, or otherwise
418 name == "#{load_path[i]}/#{feature}#{ext}"
419 for an acceptable string `ext`. It returns
420 `load_path[i].to_str` if found, else 0.
421
422 If type is 's', then `ext` is acceptable only if IS_DLEXT(ext);
423 if 'r', then only if IS_RBEXT(ext); otherwise `ext` may be absent
424 or have any value matching `%r{^\.[^./]*$}`.
425*/
426static VALUE
427loaded_feature_path(const char *name, long vlen, const char *feature, long len,
428 int type, VALUE load_path)
429{
430 long i;
431 long plen;
432 const char *e;
433
434 if (vlen < len+1) return 0;
435 if (strchr(feature, '.') && !strncmp(name+(vlen-len), feature, len)) {
436 plen = vlen - len;
437 }
438 else {
439 for (e = name + vlen; name != e && *e != '.' && *e != '/'; --e);
440 if (*e != '.' ||
441 e-name < len ||
442 strncmp(e-len, feature, len))
443 return 0;
444 plen = e - name - len;
445 }
446 if (plen > 0 && name[plen-1] != '/') {
447 return 0;
448 }
449 if (type == 's' ? !IS_DLEXT(&name[plen+len]) :
450 type == 'r' ? !IS_RBEXT(&name[plen+len]) :
451 0) {
452 return 0;
453 }
454 /* Now name == "#{prefix}/#{feature}#{ext}" where ext is acceptable
455 (possibly empty) and prefix is some string of length plen. */
456
457 if (plen > 0) --plen; /* exclude '.' */
458 for (i = 0; i < RARRAY_LEN(load_path); ++i) {
459 VALUE p = RARRAY_AREF(load_path, i);
460 const char *s = StringValuePtr(p);
461 long n = RSTRING_LEN(p);
462
463 if (n != plen) continue;
464 if (n && strncmp(name, s, n)) continue;
465 return p;
466 }
467 return 0;
468}
469
471 const char *name;
472 long len;
473 int type;
474 VALUE load_path;
475 const char *result;
476};
477
478static int
479loaded_feature_path_i(st_data_t v, st_data_t b, st_data_t f)
480{
481 const char *s = (const char *)v;
482 struct loaded_feature_searching *fp = (struct loaded_feature_searching *)f;
483 VALUE p = loaded_feature_path(s, strlen(s), fp->name, fp->len,
484 fp->type, fp->load_path);
485 if (!p) return ST_CONTINUE;
486 fp->result = s;
487 return ST_STOP;
488}
489
490/*
491 * Returns the type of already provided feature.
492 * 'r': ruby script (".rb")
493 * 's': shared object (".so"/"."DLEXT)
494 * 'u': unsuffixed
495 */
496static int
497rb_feature_p(const rb_box_t *box, const char *feature, const char *ext, int rb, int expanded, const char **fn)
498{
499 VALUE features, this_feature_index = Qnil, v, p, load_path = 0;
500 const char *f, *e;
501 long i, len, elen, n;
502 st_table *loading_tbl, *features_index;
503 st_data_t data;
504 st_data_t key;
505 int type;
506
507 if (fn) *fn = 0;
508 if (ext) {
509 elen = strlen(ext);
510 len = strlen(feature) - elen;
511 type = rb ? 'r' : 's';
512 }
513 else {
514 len = strlen(feature);
515 elen = 0;
516 type = 0;
517 }
518 features = box->loaded_features;
519 features_index = get_loaded_features_index(box);
520
521 key = feature_key(feature, strlen(feature));
522 /* We search `features` for an entry such that either
523 "#{features[i]}" == "#{load_path[j]}/#{feature}#{e}"
524 for some j, or
525 "#{features[i]}" == "#{feature}#{e}"
526 Here `e` is an "allowed" extension -- either empty or one
527 of the extensions accepted by IS_RBEXT, IS_SOEXT, or
528 IS_DLEXT. Further, if `ext && rb` then `IS_RBEXT(e)`,
529 and if `ext && !rb` then `IS_SOEXT(e) || IS_DLEXT(e)`.
530
531 If `expanded`, then only the latter form (without load_path[j])
532 is accepted. Otherwise either form is accepted, *unless* `ext`
533 is false and an otherwise-matching entry of the first form is
534 preceded by an entry of the form
535 "#{features[i2]}" == "#{load_path[j2]}/#{feature}#{e2}"
536 where `e2` matches %r{^\.[^./]*$} but is not an allowed extension.
537 After a "distractor" entry of this form, only entries of the
538 form "#{feature}#{e}" are accepted.
539
540 In `rb_provide_feature()` and `get_loaded_features_index()` we
541 maintain an invariant that the array `this_feature_index` will
542 point to every entry in `features` which has the form
543 "#{prefix}#{feature}#{e}"
544 where `e` is empty or matches %r{^\.[^./]*$}, and `prefix` is empty
545 or ends in '/'. This includes both match forms above, as well
546 as any distractors, so we may ignore all other entries in `features`.
547 */
548 if (st_lookup(features_index, key, &data) && !NIL_P(this_feature_index = (VALUE)data)) {
549 for (size_t i = 0; ; i++) {
550 long index;
551 if (FIXNUM_P(this_feature_index)) {
552 if (i > 0) break;
553 index = FIX2LONG(this_feature_index);
554 }
555 else {
556 feature_indexes_t feature_indexes = (feature_indexes_t)this_feature_index;
557 if (i >= rb_darray_size(feature_indexes)) break;
558 index = rb_darray_get(feature_indexes, i);
559 }
560
561 if (index >= RARRAY_LEN(features)) continue;
562 v = RARRAY_AREF(features, index);
563 f = StringValuePtr(v);
564 if ((n = RSTRING_LEN(v)) < len) continue;
565 if (strncmp(f, feature, len) != 0) {
566 if (expanded) continue;
567 if (!load_path) load_path = get_expanded_load_path((rb_box_t *)box);
568 if (!(p = loaded_feature_path(f, n, feature, len, type, load_path)))
569 continue;
570 expanded = 1;
571 f += RSTRING_LEN(p) + 1;
572 }
573 if (!*(e = f + len)) {
574 if (ext) continue;
575 return 'u';
576 }
577 if (*e != '.') continue;
578 if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) {
579 return 's';
580 }
581 if ((rb || !ext) && (IS_RBEXT(e))) {
582 return 'r';
583 }
584 }
585 }
586
587 loading_tbl = box->loading_table;
588 f = 0;
589 if (!expanded && !rb_is_absolute_path(feature)) {
590 struct loaded_feature_searching fs;
591 fs.name = feature;
592 fs.len = len;
593 fs.type = type;
594 fs.load_path = load_path ? load_path : get_expanded_load_path((rb_box_t *)box);
595 fs.result = 0;
596 st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs);
597 if ((f = fs.result) != 0) {
598 if (fn) *fn = f;
599 goto loading;
600 }
601 }
602 if (st_get_key(loading_tbl, (st_data_t)feature, &data)) {
603 if (fn) *fn = (const char*)data;
604 goto loading;
605 }
606 else {
607 VALUE bufstr;
608 char *buf;
609 static const char so_ext[][4] = {
610 ".so", ".o",
611 };
612
613 if (ext && *ext) return 0;
614 bufstr = rb_str_tmp_new(len + DLEXT_MAXLEN);
615 buf = RSTRING_PTR(bufstr);
616 MEMCPY(buf, feature, char, len);
617 for (i = 0; (e = loadable_ext[i]) != 0; i++) {
618 strlcpy(buf + len, e, DLEXT_MAXLEN + 1);
619 if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
620 rb_str_resize(bufstr, 0);
621 if (fn) *fn = (const char*)data;
622 return i ? 's' : 'r';
623 }
624 }
625 for (i = 0; i < numberof(so_ext); i++) {
626 strlcpy(buf + len, so_ext[i], DLEXT_MAXLEN + 1);
627 if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
628 rb_str_resize(bufstr, 0);
629 if (fn) *fn = (const char*)data;
630 return 's';
631 }
632 }
633 rb_str_resize(bufstr, 0);
634 }
635 return 0;
636
637 loading:
638 if (!ext) return 'u';
639 return !IS_RBEXT(ext) ? 's' : 'r';
640}
641
642int
643rb_provided(const char *feature)
644{
645 return rb_feature_provided(feature, 0);
646}
647
648static int
649feature_provided(rb_box_t *box, const char *feature, const char **loading)
650{
651 const char *ext = strrchr(feature, '.');
652 VALUE fullpath = 0;
653
654 if (*feature == '.' &&
655 (feature[1] == '/' || strncmp(feature+1, "./", 2) == 0)) {
656 fullpath = rb_file_expand_path_fast(rb_get_path(rb_str_new2(feature)), Qnil);
657 feature = RSTRING_PTR(fullpath);
658 }
659 if (ext && !strchr(ext, '/')) {
660 if (IS_RBEXT(ext)) {
661 if (rb_feature_p(box, feature, ext, TRUE, FALSE, loading)) return TRUE;
662 return FALSE;
663 }
664 else if (IS_SOEXT(ext) || IS_DLEXT(ext)) {
665 if (rb_feature_p(box, feature, ext, FALSE, FALSE, loading)) return TRUE;
666 return FALSE;
667 }
668 }
669 if (rb_feature_p(box, feature, 0, TRUE, FALSE, loading))
670 return TRUE;
671 RB_GC_GUARD(fullpath);
672 return FALSE;
673}
674
675int
676rb_feature_provided(const char *feature, const char **loading)
677{
678 rb_box_t *box = (rb_box_t *)rb_current_box();
679 return feature_provided(box, feature, loading);
680}
681
682static void
683rb_provide_feature(const rb_box_t *box, VALUE feature)
684{
685 VALUE features;
686
687 features = box->loaded_features;
688 if (OBJ_FROZEN(features)) {
689 rb_raise(rb_eRuntimeError,
690 "$LOADED_FEATURES is frozen; cannot append feature");
691 }
692 feature = rb_fstring(feature);
693
694 get_loaded_features_index(box);
695 // If loaded_features and loaded_features_snapshot share the same backing
696 // array, pushing into it would cause the whole array to be copied.
697 // To avoid this we first clear loaded_features_snapshot.
698 rb_ary_clear(box->loaded_features_snapshot);
699 rb_ary_push(features, feature);
700 features_index_add(box, feature, INT2FIX(RARRAY_LEN(features)-1));
701 reset_loaded_features_snapshot(box);
702}
703
704void
705rb_provide(const char *feature)
706{
707 /*
708 * rb_provide() must use rb_current_box to store provided features
709 * in the current box's loaded_features, etc.
710 */
711 rb_provide_feature(rb_current_box(), rb_fstring_cstr(feature));
712}
713
714NORETURN(static void load_failed(VALUE));
715
716static inline VALUE
717realpath_internal_cached(VALUE hash, VALUE path)
718{
719 VALUE ret = rb_hash_aref(hash, path);
720 if(RTEST(ret)) {
721 return ret;
722 }
723
724 VALUE realpath = rb_realpath_internal(Qnil, path, 1);
725 rb_hash_aset(hash, rb_fstring(path), rb_fstring(realpath));
726 return realpath;
727}
728
729static inline void
730load_iseq_eval(rb_execution_context_t *ec, VALUE fname)
731{
732 const rb_box_t *box = rb_loading_box();
733 const rb_iseq_t *iseq = rb_iseq_load_iseq(fname);
734
735 if (!iseq) {
736 rb_execution_context_t *ec = GET_EC();
737 VALUE v = rb_vm_push_frame_fname(ec, fname);
738
739 VALUE realpath_map = box->loaded_features_realpath_map;
740
741 if (rb_ruby_prism_p()) {
742 pm_parse_result_t result = { 0 };
743 result.options.line = 1;
744 result.node.coverage_enabled = 1;
745
746 VALUE error = pm_load_parse_file(&result, fname, NULL);
747
748 if (error == Qnil) {
749 int error_state;
750 iseq = pm_iseq_new_top(&result.node, rb_fstring_lit("<top (required)>"), fname, realpath_internal_cached(realpath_map, fname), NULL, &error_state);
751
752 pm_parse_result_free(&result);
753
754 if (error_state) {
755 RUBY_ASSERT(iseq == NULL);
756 rb_jump_tag(error_state);
757 }
758 }
759 else {
760 rb_vm_pop_frame(ec);
761 RB_GC_GUARD(v);
762 pm_parse_result_free(&result);
763 rb_exc_raise(error);
764 }
765 }
766 else {
767 rb_ast_t *ast;
768 VALUE ast_value;
769 VALUE parser = rb_parser_new();
770 rb_parser_set_context(parser, NULL, FALSE);
771 ast_value = rb_parser_load_file(parser, fname);
772 ast = rb_ruby_ast_data_get(ast_value);
773
774 iseq = rb_iseq_new_top(ast_value, rb_fstring_lit("<top (required)>"),
775 fname, realpath_internal_cached(realpath_map, fname), NULL);
776 rb_ast_dispose(ast);
777 }
778
779 rb_vm_pop_frame(ec);
780 RB_GC_GUARD(v);
781 }
782 rb_exec_event_hook_script_compiled(ec, iseq, Qnil);
783
784 rb_iseq_eval(iseq, box);
785}
786
787static inline enum ruby_tag_type
788load_wrapping(rb_execution_context_t *ec, VALUE fname, VALUE load_wrapper)
789{
790 enum ruby_tag_type state;
791 rb_box_t *box;
792 rb_thread_t *th = rb_ec_thread_ptr(ec);
793 volatile VALUE wrapper = th->top_wrapper;
794 volatile VALUE self = th->top_self;
795#if !defined __GNUC__
796 rb_thread_t *volatile th0 = th;
797#endif
798
799 ec->errinfo = Qnil; /* ensure */
800
801 /* load in module as toplevel */
802 if (BOX_OBJ_P(load_wrapper)) {
803 box = rb_get_box_t(load_wrapper);
804 if (!box->top_self) {
805 box->top_self = rb_obj_clone(rb_vm_top_self());
806 }
807 th->top_self = box->top_self;
808 }
809 else {
810 th->top_self = rb_obj_clone(rb_vm_top_self());
811 }
812 th->top_wrapper = load_wrapper;
813 rb_extend_object(th->top_self, th->top_wrapper);
814
815 EC_PUSH_TAG(ec);
816 state = EC_EXEC_TAG();
817 if (state == TAG_NONE) {
818 load_iseq_eval(ec, fname);
819 }
820 EC_POP_TAG();
821
822#if !defined __GNUC__
823 th = th0;
824 fname = RB_GC_GUARD(fname);
825#endif
826 th->top_self = self;
827 th->top_wrapper = wrapper;
828 return state;
829}
830
831static inline void
832raise_load_if_failed(rb_execution_context_t *ec, enum ruby_tag_type state)
833{
834 if (state) {
835 rb_vm_jump_tag_but_local_jump(state);
836 }
837
838 if (!NIL_P(ec->errinfo)) {
839 rb_exc_raise(ec->errinfo);
840 }
841}
842
843static void
844rb_load_internal(VALUE fname, VALUE wrap)
845{
846 VALUE box_value;
847 rb_execution_context_t *ec = GET_EC();
848 const rb_box_t *box = rb_loading_box();
849 enum ruby_tag_type state = TAG_NONE;
850 if (RTEST(wrap)) {
851 if (!RB_TYPE_P(wrap, T_MODULE)) {
852 wrap = rb_module_new();
853 }
854 state = load_wrapping(ec, fname, wrap);
855 }
856 else if (BOX_OPTIONAL_P(box)) {
857 box_value = box->box_object;
858 state = load_wrapping(ec, fname, box_value);
859 }
860 else {
861 load_iseq_eval(ec, fname);
862 }
863 raise_load_if_failed(ec, state);
864}
865
866void
867rb_load(VALUE fname, int wrap)
868{
869 VALUE tmp = rb_find_file(FilePathValue(fname));
870 if (!tmp) load_failed(fname);
871 rb_load_internal(tmp, RBOOL(wrap));
872}
873
874void
875rb_load_protect(VALUE fname, int wrap, int *pstate)
876{
877 enum ruby_tag_type state;
878
879 EC_PUSH_TAG(GET_EC());
880 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
881 rb_load(fname, wrap);
882 }
883 EC_POP_TAG();
884
885 if (state != TAG_NONE) *pstate = state;
886}
887
888static VALUE
889load_entrypoint_internal(VALUE fname, VALUE wrap)
890{
891 VALUE path, orig_fname;
892
893 orig_fname = rb_get_path_check_to_string(fname);
894 fname = rb_str_encode_ospath(orig_fname);
895 RUBY_DTRACE_HOOK(LOAD_ENTRY, RSTRING_PTR(orig_fname));
896
897 path = rb_find_file(fname);
898 if (!path) {
899 if (!rb_file_load_ok(RSTRING_PTR(fname)))
900 load_failed(orig_fname);
901 path = fname;
902 }
903 rb_load_internal(path, wrap);
904
905 RUBY_DTRACE_HOOK(LOAD_RETURN, RSTRING_PTR(orig_fname));
906
907 return Qtrue;
908}
909
910VALUE
911rb_load_entrypoint(VALUE args)
912{
913 VALUE fname, wrap;
914 if (RARRAY_LEN(args) != 2) {
915 rb_bug("invalid arguments: %ld", RARRAY_LEN(args));
916 }
917 fname = rb_ary_entry(args, 0);
918 wrap = rb_ary_entry(args, 1);
919 return load_entrypoint_internal(fname, wrap);
920}
921
922/*
923 * call-seq:
924 * load(filename, wrap=false) -> true
925 *
926 * Loads and executes the Ruby program in the file _filename_.
927 *
928 * If the filename is an absolute path (e.g. starts with '/'), the file
929 * will be loaded directly using the absolute path.
930 *
931 * If the filename is an explicit relative path (e.g. starts with './' or
932 * '../'), the file will be loaded using the relative path from the current
933 * directory.
934 *
935 * Otherwise, the file will be searched for in the library
936 * directories listed in <code>$LOAD_PATH</code> (<code>$:</code>).
937 * If the file is found in a directory, it will attempt to load the file
938 * relative to that directory. If the file is not found in any of the
939 * directories in <code>$LOAD_PATH</code>, the file will be loaded using
940 * the relative path from the current directory.
941 *
942 * If the file doesn't exist when there is an attempt to load it, a
943 * LoadError will be raised.
944 *
945 * If the optional _wrap_ parameter is +true+, the loaded script will
946 * be executed under an anonymous module. If the optional _wrap_ parameter
947 * is a module, the loaded script will be executed under the given module.
948 * In no circumstance will any local variables in the loaded file be
949 * propagated to the loading environment.
950 */
951
952static VALUE
953rb_f_load(int argc, VALUE *argv, VALUE _)
954{
955 VALUE fname, wrap;
956 rb_scan_args(argc, argv, "11", &fname, &wrap);
957 return load_entrypoint_internal(fname, wrap);
958}
959
960static char *
961load_lock(const rb_box_t *box, const char *ftptr, bool warn)
962{
963 st_data_t data;
964 st_table *loading_tbl = box->loading_table;
965
966 if (!st_lookup(loading_tbl, (st_data_t)ftptr, &data)) {
967 /* partial state */
968 ftptr = ruby_strdup(ftptr);
969 data = (st_data_t)rb_thread_shield_new();
970 st_insert(loading_tbl, (st_data_t)ftptr, data);
971 return (char *)ftptr;
972 }
973
974 if (warn && rb_thread_shield_owned((VALUE)data)) {
975 VALUE warning = rb_warning_string("loading in progress, circular require considered harmful - %s", ftptr);
976 rb_backtrace_each(rb_str_append, warning);
977 rb_warning("%"PRIsVALUE, warning);
978 }
979 switch (rb_thread_shield_wait((VALUE)data)) {
980 case Qfalse:
981 case Qnil:
982 return 0;
983 }
984 return (char *)ftptr;
985}
986
987static int
988release_thread_shield(st_data_t *key, st_data_t *value, st_data_t done, int existing)
989{
990 VALUE thread_shield = (VALUE)*value;
991 if (!existing) return ST_STOP;
992 if (done) {
993 rb_thread_shield_destroy(thread_shield);
994 /* Delete the entry even if there are waiting threads, because they
995 * won't load the file and won't delete the entry. */
996 }
997 else if (rb_thread_shield_release(thread_shield)) {
998 /* still in-use */
999 return ST_CONTINUE;
1000 }
1001 xfree((char *)*key);
1002 return ST_DELETE;
1003}
1004
1005static void
1006load_unlock(const rb_box_t *box, const char *ftptr, int done)
1007{
1008 if (ftptr) {
1009 st_data_t key = (st_data_t)ftptr;
1010 st_table *loading_tbl = box->loading_table;
1011
1012 st_update(loading_tbl, key, release_thread_shield, done);
1013 }
1014}
1015
1016static VALUE rb_require_string_internal(VALUE fname, bool resurrect);
1017
1018/*
1019 * call-seq:
1020 * require(name) -> true or false
1021 *
1022 * Loads the given +name+, returning +true+ if successful and +false+ if the
1023 * feature is already loaded.
1024 *
1025 * If the filename neither resolves to an absolute path nor starts with
1026 * './' or '../', the file will be searched for in the library
1027 * directories listed in <code>$LOAD_PATH</code> (<code>$:</code>).
1028 * If the filename starts with './' or '../', resolution is based on Dir.pwd.
1029 *
1030 * If the filename has the extension ".rb", it is loaded as a source file; if
1031 * the extension is ".so", ".o", or the default shared library extension on
1032 * the current platform, Ruby loads the shared library as a Ruby extension.
1033 * Otherwise, Ruby tries adding ".rb", ".so", and so on to the name until
1034 * found. If the file named cannot be found, a LoadError will be raised.
1035 *
1036 * For Ruby extensions the filename given may use ".so" or ".o". For example,
1037 * on macOS the socket extension is "socket.bundle" and
1038 * <code>require 'socket.so'</code> will load the socket extension.
1039 *
1040 * The absolute path of the loaded file is added to
1041 * <code>$LOADED_FEATURES</code> (<code>$"</code>). A file will not be
1042 * loaded again if its path already appears in <code>$"</code>. For example,
1043 * <code>require 'a'; require './a'</code> will not load <code>a.rb</code>
1044 * again.
1045 *
1046 * require "my-library.rb"
1047 * require "db-driver"
1048 *
1049 * Any constants or globals within the loaded source file will be available
1050 * in the calling program's global namespace. However, local variables will
1051 * not be propagated to the loading environment.
1052 *
1053 */
1054
1055VALUE
1057{
1058 return rb_require_string(fname);
1059}
1060
1061VALUE
1062rb_require_relative_entrypoint(VALUE fname)
1063{
1064 VALUE base = rb_current_realfilepath();
1065 if (NIL_P(base)) {
1066 rb_loaderror("cannot infer basepath");
1067 }
1068 base = rb_file_dirname(base);
1069 return rb_require_string_internal(rb_file_absolute_path(fname, base), false);
1070}
1071
1072/*
1073 * call-seq:
1074 * require_relative(string) -> true or false
1075 *
1076 * Ruby tries to load the library named _string_ relative to the directory
1077 * containing the requiring file. If the file does not exist a LoadError is
1078 * raised. Returns +true+ if the file was loaded and +false+ if the file was
1079 * already loaded before.
1080 */
1081VALUE
1082rb_f_require_relative(VALUE obj, VALUE fname)
1083{
1084 return rb_require_relative_entrypoint(fname);
1085}
1086
1087typedef int (*feature_func)(const rb_box_t *box, const char *feature, const char *ext, int rb, int expanded, const char **fn);
1088
1089static int
1090search_required(const rb_box_t *box, VALUE fname, volatile VALUE *path, feature_func rb_feature_p)
1091{
1092 VALUE tmp;
1093 char *ext, *ftptr;
1094 int ft = 0;
1095 const char *loading;
1096
1097 *path = 0;
1098 ext = strrchr(ftptr = RSTRING_PTR(fname), '.');
1099 if (ext && !strchr(ext, '/')) {
1100 if (IS_RBEXT(ext)) {
1101 if (rb_feature_p(box, ftptr, ext, TRUE, FALSE, &loading)) {
1102 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1103 return 'r';
1104 }
1105 if ((tmp = rb_find_file(fname)) != 0) {
1106 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
1107 if (!rb_feature_p(box, ftptr, ext, TRUE, TRUE, &loading) || loading)
1108 *path = tmp;
1109 return 'r';
1110 }
1111 return 0;
1112 }
1113 else if (IS_SOEXT(ext)) {
1114 if (rb_feature_p(box, ftptr, ext, FALSE, FALSE, &loading)) {
1115 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1116 return 's';
1117 }
1118 tmp = rb_str_subseq(fname, 0, ext - RSTRING_PTR(fname));
1119 rb_str_cat2(tmp, DLEXT);
1120 OBJ_FREEZE(tmp);
1121 if ((tmp = rb_find_file(tmp)) != 0) {
1122 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
1123 if (!rb_feature_p(box, ftptr, ext, FALSE, TRUE, &loading) || loading)
1124 *path = tmp;
1125 return 's';
1126 }
1127 }
1128 else if (IS_DLEXT(ext)) {
1129 if (rb_feature_p(box, ftptr, ext, FALSE, FALSE, &loading)) {
1130 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1131 return 's';
1132 }
1133 if ((tmp = rb_find_file(fname)) != 0) {
1134 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
1135 if (!rb_feature_p(box, ftptr, ext, FALSE, TRUE, &loading) || loading)
1136 *path = tmp;
1137 return 's';
1138 }
1139 }
1140 }
1141 else if ((ft = rb_feature_p(box, ftptr, 0, FALSE, FALSE, &loading)) == 'r') {
1142 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1143 return 'r';
1144 }
1145 tmp = fname;
1146 const unsigned int type = rb_find_file_ext(&tmp, ft == 's' ? ruby_ext : loadable_ext);
1147
1148 // Check if it's a statically linked extension when
1149 // not already a feature and not found as a dynamic library.
1150 if (!ft && type != loadable_ext_rb) {
1151 rb_vm_t *vm = GET_VM();
1152 if (vm->static_ext_inits) {
1153 VALUE lookup_name = tmp;
1154 // Append ".so" if not already present so for example "etc" can find "etc.so".
1155 // We always register statically linked extensions with a ".so" extension.
1156 // See encinit.c and extinit.c (generated at build-time).
1157 if (!ext) {
1158 lookup_name = rb_str_dup(lookup_name);
1159 rb_str_cat_cstr(lookup_name, ".so");
1160 }
1161 ftptr = RSTRING_PTR(lookup_name);
1162 if (st_lookup(vm->static_ext_inits, (st_data_t)ftptr, NULL)) {
1163 *path = rb_filesystem_str_new_cstr(ftptr);
1164 RB_GC_GUARD(lookup_name);
1165 return 's';
1166 }
1167 }
1168 }
1169
1170 switch (type) {
1171 case 0:
1172 if (ft)
1173 goto feature_present;
1174 ftptr = RSTRING_PTR(tmp);
1175 return rb_feature_p(box, ftptr, 0, FALSE, TRUE, 0);
1176
1177 default:
1178 if (ft) {
1179 goto feature_present;
1180 }
1181 /* fall through */
1182 case loadable_ext_rb:
1183 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
1184 if (rb_feature_p(box, ftptr, ext, type == loadable_ext_rb, TRUE, &loading) && !loading)
1185 break;
1186 *path = tmp;
1187 }
1188 return type > loadable_ext_rb ? 's' : 'r';
1189
1190 feature_present:
1191 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1192 return ft;
1193}
1194
1195static void
1196load_failed(VALUE fname)
1197{
1198 rb_load_fail(fname, "cannot load such file");
1199}
1200
1201static VALUE
1202load_ext(VALUE path, VALUE fname)
1203{
1204 VALUE loaded = path;
1205 const rb_box_t *box = rb_loading_box();
1206 if (BOX_USER_P(box)) {
1207 loaded = rb_box_local_extension(box->box_object, fname, path);
1208 }
1209 rb_scope_visibility_set(METHOD_VISI_PUBLIC);
1210 void *handle = dln_load_feature(RSTRING_PTR(loaded), RSTRING_PTR(fname));
1211 RB_GC_GUARD(loaded);
1212 RB_GC_GUARD(fname);
1213 return (VALUE)handle;
1214}
1215
1216static VALUE
1217run_static_ext_init(VALUE vm_ptr, VALUE feature_value)
1218{
1219 rb_vm_t *vm = (rb_vm_t *)vm_ptr;
1220 const char *feature = RSTRING_PTR(feature_value);
1221 st_data_t key = (st_data_t)feature;
1222 st_data_t init_func;
1223
1224 if (vm->static_ext_inits && st_delete(vm->static_ext_inits, &key, &init_func)) {
1225 ((void (*)(void))init_func)();
1226 return Qtrue;
1227 }
1228 return Qfalse;
1229}
1230
1231static int
1232no_feature_p(const rb_box_t *box, const char *feature, const char *ext, int rb, int expanded, const char **fn)
1233{
1234 return 0;
1235}
1236
1237// Documented in doc/globals.md
1238VALUE
1239rb_resolve_feature_path(VALUE klass, VALUE fname)
1240{
1241 VALUE path;
1242 int found;
1243 VALUE sym;
1244 const rb_box_t *box = rb_loading_box();
1245
1246 fname = rb_get_path(fname);
1247 path = rb_str_encode_ospath(fname);
1248 found = search_required(box, path, &path, no_feature_p);
1249
1250 switch (found) {
1251 case 'r':
1252 sym = ID2SYM(rb_intern("rb"));
1253 break;
1254 case 's':
1255 sym = ID2SYM(rb_intern("so"));
1256 break;
1257 default:
1258 return Qnil;
1259 }
1260
1261 return rb_ary_new_from_args(2, sym, path);
1262}
1263
1264static void
1265ext_config_push(rb_thread_t *th, volatile struct rb_ext_config *prev)
1266{
1267 *prev = th->ext_config;
1268 th->ext_config = (struct rb_ext_config){0};
1269}
1270
1271static void
1272ext_config_pop(rb_thread_t *th, volatile struct rb_ext_config *prev)
1273{
1274 th->ext_config = *prev;
1275}
1276
1277void
1279{
1280 GET_THREAD()->ext_config.ractor_safe = flag;
1281}
1282
1283/*
1284 * returns
1285 * 0: if already loaded (false)
1286 * 1: successfully loaded (true)
1287 * <0: not found (LoadError)
1288 * >1: exception
1289 */
1290static int
1291require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool warn)
1292{
1293 volatile int result = -1;
1294 rb_thread_t *th = rb_ec_thread_ptr(ec);
1295 const rb_box_t *box = rb_loading_box();
1296 volatile const struct {
1297 VALUE wrapper, self, errinfo;
1299 const rb_box_t *box;
1300 } saved = {
1301 th->top_wrapper, th->top_self, ec->errinfo,
1302 ec, box,
1303 };
1304 enum ruby_tag_type state;
1305 char *volatile ftptr = 0;
1306 VALUE path;
1307 volatile VALUE saved_path;
1308 volatile VALUE realpath = 0;
1309 VALUE realpaths = box->loaded_features_realpaths;
1310 VALUE realpath_map = box->loaded_features_realpath_map;
1311 volatile bool reset_ext_config = false;
1312 volatile struct rb_ext_config prev_ext_config;
1313
1314 path = rb_str_encode_ospath(fname);
1315 RUBY_DTRACE_HOOK(REQUIRE_ENTRY, RSTRING_PTR(fname));
1316 saved_path = path;
1317
1318 EC_PUSH_TAG(ec);
1319 ec->errinfo = Qnil; /* ensure */
1320 th->top_wrapper = 0;
1321 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1322 VALUE handle;
1323 int found;
1324
1325 RUBY_DTRACE_HOOK(FIND_REQUIRE_ENTRY, RSTRING_PTR(fname));
1326 found = search_required(box, path, &saved_path, rb_feature_p);
1327 RUBY_DTRACE_HOOK(FIND_REQUIRE_RETURN, RSTRING_PTR(fname));
1328 path = saved_path;
1329
1330 if (found) {
1331 if (!path || !(ftptr = load_lock(box, RSTRING_PTR(path), warn))) {
1332 result = 0;
1333 }
1334 else if (!*ftptr) {
1335 result = TAG_RETURN;
1336 }
1337 else if (found == 's' && RTEST(rb_vm_call_cfunc_in_box(Qnil, run_static_ext_init, (VALUE)th->vm, path, path, box))) {
1338 result = TAG_RETURN;
1339 }
1340 else if (RTEST(rb_hash_aref(realpaths,
1341 realpath = realpath_internal_cached(realpath_map, path)))) {
1342 result = 0;
1343 }
1344 else {
1345 switch (found) {
1346 case 'r':
1347 // iseq_eval_in_box will be called with the loading box eventually
1348 if (BOX_OPTIONAL_P(box)) {
1349 // check with BOX_OPTIONAL_P (not BOX_USER_P) for NS1::xxx naming
1350 // it is not expected for the main box
1351 // TODO: no need to use load_wrapping() here?
1352 load_wrapping(saved.ec, path, box->box_object);
1353 }
1354 else {
1355 load_iseq_eval(saved.ec, path);
1356 }
1357 break;
1358
1359 case 's':
1360 reset_ext_config = true;
1361 ext_config_push(th, &prev_ext_config);
1362 handle = rb_vm_call_cfunc_in_box(box->top_self, load_ext, path, fname, path, box);
1363 rb_hash_aset(box->ruby_dln_libmap, path, SVALUE2NUM((SIGNED_VALUE)handle));
1364 break;
1365 }
1366 result = TAG_RETURN;
1367 }
1368 }
1369 }
1370 EC_POP_TAG();
1371
1372 ec = saved.ec;
1373 box = saved.box;
1374 rb_thread_t *th2 = rb_ec_thread_ptr(ec);
1375 th2->top_self = saved.self;
1376 th2->top_wrapper = saved.wrapper;
1377 if (reset_ext_config) ext_config_pop(th2, &prev_ext_config);
1378
1379 path = saved_path;
1380 if (ftptr) load_unlock(box, RSTRING_PTR(path), !state);
1381
1382 if (state) {
1383 if (state == TAG_FATAL || state == TAG_THROW) {
1384 EC_JUMP_TAG(ec, state);
1385 }
1386 else if (exception) {
1387 /* usually state == TAG_RAISE only, except for
1388 * rb_iseq_load_iseq in load_iseq_eval case */
1389 VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
1390 if (!NIL_P(exc)) ec->errinfo = exc;
1391 return TAG_RAISE;
1392 }
1393 else if (state == TAG_RETURN) {
1394 return TAG_RAISE;
1395 }
1396 RB_GC_GUARD(fname);
1397 /* never TAG_RETURN */
1398 return state;
1399 }
1400 if (!NIL_P(ec->errinfo)) {
1401 if (!exception) return TAG_RAISE;
1402 rb_exc_raise(ec->errinfo);
1403 }
1404
1405 if (result == TAG_RETURN) {
1406 rb_provide_feature(box, path);
1407 VALUE real = realpath;
1408 if (real) {
1409 real = rb_fstring(real);
1410 rb_hash_aset(realpaths, real, Qtrue);
1411 }
1412 }
1413 ec->errinfo = saved.errinfo;
1414
1415 RUBY_DTRACE_HOOK(REQUIRE_RETURN, RSTRING_PTR(fname));
1416
1417 return result;
1418}
1419
1420int
1421rb_require_internal_silent(VALUE fname)
1422{
1423 if (!rb_ractor_main_p()) {
1424 return NUM2INT(rb_ractor_require(fname, true));
1425 }
1426
1427 rb_execution_context_t *ec = GET_EC();
1428 return require_internal(ec, fname, 1, false);
1429}
1430
1431int
1432rb_require_internal(VALUE fname)
1433{
1434 rb_execution_context_t *ec = GET_EC();
1435 return require_internal(ec, fname, 1, RTEST(ruby_verbose));
1436}
1437
1438int
1439ruby_require_internal(const char *fname, unsigned int len)
1440{
1441 struct RString fake = {RBASIC_INIT};
1442 VALUE str = rb_setup_fake_str(&fake, fname, len, 0);
1443 rb_execution_context_t *ec = GET_EC();
1444 int result = require_internal(ec, str, 0, RTEST(ruby_verbose));
1445 rb_set_errinfo(Qnil);
1446 return result == TAG_RETURN ? 1 : result ? -1 : 0;
1447}
1448
1449VALUE
1451{
1452 return rb_require_string_internal(FilePathValue(fname), false);
1453}
1454
1455static VALUE
1456rb_require_string_internal(VALUE fname, bool resurrect)
1457{
1458 rb_execution_context_t *ec = GET_EC();
1459
1460 // main ractor check
1461 if (!rb_ractor_main_p()) {
1462 if (resurrect) fname = rb_str_resurrect(fname);
1463 return rb_ractor_require(fname, false);
1464 }
1465 else {
1466 int result = require_internal(ec, fname, 1, RTEST(ruby_verbose));
1467
1468 if (result > TAG_RETURN) {
1469 EC_JUMP_TAG(ec, result);
1470 }
1471 if (result < 0) {
1472 if (resurrect) fname = rb_str_resurrect(fname);
1473 load_failed(fname);
1474 }
1475
1476 return RBOOL(result);
1477 }
1478}
1479
1480VALUE
1481rb_require(const char *fname)
1482{
1483 struct RString fake = {RBASIC_INIT};
1484 VALUE str = rb_setup_fake_str(&fake, fname, strlen(fname), 0);
1485 return rb_require_string_internal(str, true);
1486}
1487
1488static int
1489register_init_ext(st_data_t *key, st_data_t *value, st_data_t init, int existing)
1490{
1491 const char *name = (char *)*key;
1492 if (existing) {
1493 /* already registered */
1494 rb_warn("%s is already registered", name);
1495 }
1496 else {
1497 *value = (st_data_t)init;
1498 }
1499 return ST_CONTINUE;
1500}
1501
1502// Private API for statically linked extensions.
1503// Used with the ext/Setup file, the --with-setup and
1504// --with-static-linked-ext configuration option, etc.
1505void
1506ruby_init_ext(const char *name, void (*init)(void))
1507{
1508 st_table *inits_table;
1509 rb_vm_t *vm = GET_VM();
1510 const rb_box_t *box = rb_loading_box();
1511
1512 if (feature_provided((rb_box_t *)box, name, 0))
1513 return;
1514
1515 inits_table = vm->static_ext_inits;
1516 if (!inits_table) {
1517 inits_table = st_init_strtable();
1518 vm->static_ext_inits = inits_table;
1519 }
1520 st_update(inits_table, (st_data_t)name, register_init_ext, (st_data_t)init);
1521}
1522
1523/*
1524 * call-seq:
1525 * mod.autoload(const, filename) -> nil
1526 *
1527 * Registers _filename_ to be loaded (using Kernel::require)
1528 * the first time that _const_ (which may be a String or
1529 * a symbol) is accessed in the namespace of _mod_.
1530 *
1531 * module A
1532 * end
1533 * A.autoload(:B, "b")
1534 * A::B.doit # autoloads "b"
1535 *
1536 * If _const_ in _mod_ is defined as autoload, the file name to be
1537 * loaded is replaced with _filename_. If _const_ is defined but not
1538 * as autoload, does nothing.
1539 *
1540 * Files that are currently being loaded must not be registered for
1541 * autoload.
1542 */
1543
1544static VALUE
1545rb_mod_autoload(VALUE mod, VALUE sym, VALUE file)
1546{
1547 ID id = rb_to_id(sym);
1548
1549 FilePathValue(file);
1550 rb_autoload_str(mod, id, file);
1551 return Qnil;
1552}
1553
1554/*
1555 * call-seq:
1556 * mod.autoload?(name, inherit=true) -> String or nil
1557 *
1558 * Returns _filename_ to be loaded if _name_ is registered as
1559 * +autoload+ in the namespace of _mod_ or one of its ancestors.
1560 *
1561 * module A
1562 * end
1563 * A.autoload(:B, "b")
1564 * A.autoload?(:B) #=> "b"
1565 *
1566 * If +inherit+ is false, the lookup only checks the autoloads in the receiver:
1567 *
1568 * class A
1569 * autoload :CONST, "const.rb"
1570 * end
1571 *
1572 * class B < A
1573 * end
1574 *
1575 * B.autoload?(:CONST) #=> "const.rb", found in A (ancestor)
1576 * B.autoload?(:CONST, false) #=> nil, not found in B itself
1577 *
1578 */
1579
1580static VALUE
1581rb_mod_autoload_p(int argc, VALUE *argv, VALUE mod)
1582{
1583 int recur = (rb_check_arity(argc, 1, 2) == 1) ? TRUE : RTEST(argv[1]);
1584 VALUE sym = argv[0];
1585
1586 ID id = rb_check_id(&sym);
1587 if (!id) {
1588 return Qnil;
1589 }
1590 return rb_autoload_at_p(mod, id, recur);
1591}
1592
1593/*
1594 * call-seq:
1595 * autoload(const, filename) -> nil
1596 *
1597 * Registers _filename_ to be loaded (using Kernel::require)
1598 * the first time that _const_ (which may be a String or
1599 * a symbol) is accessed.
1600 *
1601 * autoload(:MyModule, "/usr/local/lib/modules/my_module.rb")
1602 *
1603 * If _const_ is defined as autoload, the file name to be loaded is
1604 * replaced with _filename_. If _const_ is defined but not as
1605 * autoload, does nothing.
1606 *
1607 * Files that are currently being loaded must not be registered for
1608 * autoload.
1609 */
1610
1611static VALUE
1612rb_f_autoload(VALUE obj, VALUE sym, VALUE file)
1613{
1614 VALUE klass = rb_class_real(rb_vm_cbase());
1615 if (!klass) {
1616 rb_raise(rb_eTypeError, "Can not set autoload on singleton class");
1617 }
1618 return rb_mod_autoload(klass, sym, file);
1619}
1620
1621/*
1622 * call-seq:
1623 * autoload?(name, inherit=true) -> String or nil
1624 *
1625 * Returns _filename_ to be loaded if _name_ is registered as
1626 * +autoload+ in the current namespace or one of its ancestors.
1627 *
1628 * autoload(:B, "b")
1629 * autoload?(:B) #=> "b"
1630 *
1631 * module C
1632 * autoload(:D, "d")
1633 * autoload?(:D) #=> "d"
1634 * autoload?(:B) #=> nil
1635 * end
1636 *
1637 * class E
1638 * autoload(:F, "f")
1639 * autoload?(:F) #=> "f"
1640 * autoload?(:B) #=> "b"
1641 * end
1642 */
1643
1644static VALUE
1645rb_f_autoload_p(int argc, VALUE *argv, VALUE obj)
1646{
1647 /* use rb_vm_cbase() as same as rb_f_autoload. */
1648 VALUE klass = rb_vm_cbase();
1649 if (NIL_P(klass)) {
1650 return Qnil;
1651 }
1652 return rb_mod_autoload_p(argc, argv, klass);
1653}
1654
1655void *
1656rb_ext_resolve_symbol(const char* fname, const char* symbol)
1657{
1658 VALUE handle;
1659 VALUE resolved;
1660 VALUE path;
1661 char *ext;
1662 VALUE fname_str = rb_str_new_cstr(fname);
1663 const rb_box_t *box = rb_loading_box();
1664
1665 resolved = rb_resolve_feature_path((VALUE)NULL, fname_str);
1666 if (NIL_P(resolved)) {
1667 ext = strrchr(fname, '.');
1668 if (!ext || !IS_SOEXT(ext)) {
1669 rb_str_cat_cstr(fname_str, ".so");
1670 }
1671 if (rb_feature_p(box, fname, 0, FALSE, FALSE, 0)) {
1672 return dln_symbol(NULL, symbol);
1673 }
1674 return NULL;
1675 }
1676 if (RARRAY_LEN(resolved) != 2 || rb_ary_entry(resolved, 0) != ID2SYM(rb_intern("so"))) {
1677 return NULL;
1678 }
1679 path = rb_ary_entry(resolved, 1);
1680 handle = rb_hash_lookup(box->ruby_dln_libmap, path);
1681 if (NIL_P(handle)) {
1682 return NULL;
1683 }
1684 return dln_symbol((void *)NUM2SVALUE(handle), symbol);
1685}
1686
1687void
1688Init_load(void)
1689{
1690 static const char var_load_path[] = "$:";
1691 ID id_load_path = rb_intern2(var_load_path, sizeof(var_load_path)-1);
1692
1693 rb_define_hooked_variable(var_load_path, 0, load_path_getter, rb_gvar_readonly_setter);
1694 rb_gvar_box_ready(var_load_path);
1695 rb_alias_variable(rb_intern_const("$-I"), id_load_path);
1696 rb_alias_variable(rb_intern_const("$LOAD_PATH"), id_load_path);
1697
1698 rb_define_virtual_variable("$\"", get_LOADED_FEATURES, 0);
1699 rb_gvar_box_ready("$\"");
1700 rb_define_virtual_variable("$LOADED_FEATURES", get_LOADED_FEATURES, 0); // TODO: rb_alias_variable ?
1701 rb_gvar_box_ready("$LOADED_FEATURES");
1702
1703 rb_define_global_function("load", rb_f_load, -1);
1705 rb_define_global_function("require_relative", rb_f_require_relative, 1);
1706 rb_define_method(rb_cModule, "autoload", rb_mod_autoload, 2);
1707 rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, -1);
1708 rb_define_global_function("autoload", rb_f_autoload, 2);
1709 rb_define_global_function("autoload?", rb_f_autoload_p, -1);
1710}
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_global_function(mid, func, arity)
Defines rb_mKernel #mid.
void rb_extend_object(VALUE obj, VALUE module)
Extend the object with the module.
Definition eval.c:1878
VALUE rb_module_new(void)
Creates a new, anonymous module.
Definition class.c:1666
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 T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define OBJ_FROZEN
Old name of RB_OBJ_FROZEN.
Definition fl_type.h:136
#define rb_str_cat2
Old name of rb_str_cat_cstr.
Definition string.h:1682
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
Definition fl_type.h:134
#define T_FIXNUM
Old name of RUBY_T_FIXNUM.
Definition value_type.h:63
#define T_MODULE
Old name of RUBY_T_MODULE.
Definition value_type.h:70
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define FIX2LONG
Old name of RB_FIX2LONG.
Definition long.h:46
#define NIL_P
Old name of RB_NIL_P.
#define FIXNUM_P
Old name of RB_FIXNUM_P.
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:683
#define ruby_verbose
This variable controls whether the interpreter is in debug mode.
Definition error.h:475
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1431
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1429
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:466
void rb_loaderror(const char *fmt,...)
Raises an instance of rb_eLoadError.
Definition error.c:3816
void rb_warning(const char *fmt,...)
Issues a warning.
Definition error.c:497
VALUE rb_cModule
Module class.
Definition object.c:62
VALUE rb_class_real(VALUE klass)
Finds a "real" class.
Definition object.c:256
VALUE rb_obj_clone(VALUE obj)
Produces a shallow copy of the given object.
Definition object.c:528
Encoding relates APIs.
VALUE rb_ary_shared_with_p(VALUE lhs, VALUE rhs)
Queries if the passed two arrays share the same backend storage.
VALUE rb_ary_resurrect(VALUE ary)
I guess there is no use case of this function in extension libraries, but this is a routine identical...
VALUE rb_ary_replace(VALUE copy, VALUE orig)
Replaces the contents of the former object with the contents of the latter.
VALUE rb_ary_hidden_new(long capa)
Allocates a hidden (no class) empty array.
VALUE rb_ary_clear(VALUE ary)
Destructively removes everything form an array.
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
VALUE rb_ary_freeze(VALUE obj)
Freeze an array, preventing further modifications.
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
void rb_ary_store(VALUE ary, long key, VALUE val)
Destructively stores the passed value to the passed array's passed index.
static int rb_check_arity(int argc, int min, int max)
Ensures that the passed integer is in the passed range.
Definition error.h:284
void rb_provide(const char *feature)
Declares that the given feature is already provided by someone else.
Definition load.c:705
VALUE rb_f_require(VALUE self, VALUE feature)
Identical to rb_require_string(), except it ignores the first argument for no reason.
Definition load.c:1056
void rb_ext_ractor_safe(bool flag)
Asserts that the extension library that calls this function is aware of Ractor.
Definition load.c:1278
VALUE rb_require_string(VALUE feature)
Finds and loads the given feature, if absent.
Definition load.c:1450
int rb_feature_provided(const char *feature, const char **loading)
Identical to rb_provided(), except it additionally returns the "canonical" name of the loaded feature...
Definition load.c:676
void rb_load_protect(VALUE path, int wrap, int *state)
Identical to rb_load(), except it avoids potential global escapes.
Definition load.c:875
int rb_provided(const char *feature)
Queries if the given feature has already been loaded into the execution context.
Definition load.c:643
void rb_load(VALUE path, int wrap)
Loads and executes the Ruby program in the given file.
Definition load.c:867
void * rb_ext_resolve_symbol(const char *feature, const char *symbol)
Resolves and returns a symbol of a function in the native extension specified by the feature and symb...
Definition load.c:1656
VALUE rb_str_append(VALUE dst, VALUE src)
Identical to rb_str_buf_append(), except it converts the right hand side before concatenating.
Definition string.c:3795
VALUE rb_str_tmp_new(long len)
Allocates a "temporary" string.
Definition string.c:1744
VALUE rb_str_subseq(VALUE str, long beg, long len)
Identical to rb_str_substr(), except the numbers are interpreted as byte offsets instead of character...
Definition string.c:3151
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition string.c:1994
VALUE rb_str_resurrect(VALUE str)
Like rb_str_dup(), but always create an instance of rb_cString regardless of the given object's class...
Definition string.c:2012
VALUE rb_filesystem_str_new_cstr(const char *ptr)
Identical to rb_filesystem_str_new(), except it assumes the passed pointer is a pointer to a C string...
Definition string.c:1427
VALUE rb_str_equal(VALUE str1, VALUE str2)
Equality of two strings.
Definition string.c:4265
#define rb_strlen_lit(str)
Length of a string literal.
Definition string.h:1691
VALUE rb_str_freeze(VALUE str)
This is the implementation of String#freeze.
Definition string.c:3276
#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
void rb_alias_variable(ID dst, ID src)
Aliases a global variable.
Definition variable.c:1153
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:284
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
Definition symbol.c:1133
ID rb_to_id(VALUE str)
Definition string.c:12498
rb_gvar_setter_t rb_gvar_readonly_setter
This function just raises rb_eNameError.
Definition variable.h:135
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 MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:372
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
#define MEMMOVE(p1, p2, type, n)
Handy macro to call memmove.
Definition memory.h:384
void rb_define_hooked_variable(const char *q, VALUE *w, type *e, void_type *r)
Define a function-backended global variable.
VALUE type(ANYARGS)
ANYARGS-ed function type.
void rb_define_virtual_variable(const char *q, type *w, void_type *e)
Define a function-backended global variable.
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
#define RARRAY_AREF(a, i)
Definition rarray.h:403
#define StringValue(v)
Ensures that the parameter object is a String.
Definition rstring.h:66
#define StringValuePtr(v)
Identical to StringValue, except it returns a char*.
Definition rstring.h:76
VALUE rb_require(const char *feature)
Identical to rb_require_string(), except it takes C's string instead of Ruby's.
Definition load.c:1481
#define FilePathValue(v)
Ensures that the parameter object is a path.
Definition ruby.h:90
#define RTEST
This is an old name of RB_TEST.
#define _(args)
This was a transition path from K&R to ANSI.
Definition stdarg.h:35
Ruby's String.
Definition rstring.h:196
int32_t line
The line within the file that the parse starts on.
Definition options.h:127
pm_scope_node_t node
The resulting scope node that will hold the generated AST.
pm_options_t options
The options that will be passed to the parser.
Internal header for Ruby Box.
Definition box.h:14
Definition st.h:79
intptr_t SIGNED_VALUE
A signed integer type that has the same width with VALUE.
Definition value.h:63
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 void Check_Type(VALUE v, enum ruby_value_type t)
Identical to RB_TYPE_P(), except it raises exceptions on predication failure.
Definition value_type.h:433
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