Ruby: load.c Source File

Ruby 4.0.5p0 (2026-05-20 revision 64336ffd0ee9e1f4c05891695a3d7b49cb709721)

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