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