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