Ruby 3.5.0dev (2025-07-24 revision 9256442615db227ab8ccd18b0ca65da980de7eaf)
load.c (9256442615db227ab8ccd18b0ca65da980de7eaf)
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 RUBY_ASSERT(rb_ractor_main_p());
376
377 const char *feature_str, *feature_end, *ext, *p;
378 bool rb = false;
379
380 feature_str = StringValuePtr(feature);
381 feature_end = feature_str + RSTRING_LEN(feature);
382
383 for (ext = feature_end; ext > feature_str; ext--)
384 if (*ext == '.' || *ext == '/')
385 break;
386 if (*ext != '.')
387 ext = NULL;
388 else
389 rb = IS_RBEXT(ext);
390 /* Now `ext` points to the only string matching %r{^\.[^./]*$} that is
391 at the end of `feature`, or is NULL if there is no such string. */
392
393 p = ext ? ext : feature_end;
394 while (1) {
395 p--;
396 while (p >= feature_str && *p != '/')
397 p--;
398 if (p < feature_str)
399 break;
400 /* Now *p == '/'. We reach this point for every '/' in `feature`. */
401 features_index_add_single(vm_ns, p + 1, feature_end - p - 1, offset, false);
402 if (ext) {
403 features_index_add_single(vm_ns, p + 1, ext - p - 1, offset, rb);
404 }
405 }
406 features_index_add_single(vm_ns, feature_str, feature_end - feature_str, offset, false);
407 if (ext) {
408 features_index_add_single(vm_ns, feature_str, ext - feature_str, offset, rb);
409 }
410}
411
412static int
413loaded_features_index_clear_i(st_data_t key, st_data_t val, st_data_t arg)
414{
415 VALUE obj = (VALUE)val;
416 if (!SPECIAL_CONST_P(obj)) {
417 rb_darray_free((void *)obj);
418 }
419 return ST_DELETE;
420}
421
422void
423rb_free_loaded_features_index(rb_vm_t *vm)
424{
425 /* Destructs vm->loaded_features_index directly because this is only for
426 the VM destruction */
427 st_foreach(vm->loaded_features_index, loaded_features_index_clear_i, 0);
428 st_free_table(vm->loaded_features_index);
429}
430
431
432
433static st_table *
434get_loaded_features_index(vm_ns_t *vm_ns)
435{
436 int i;
437 VALUE features = CURRENT_NS_LOADED_FEATURES(vm_ns);
438 const VALUE snapshot = CURRENT_NS_LOADED_FEATURES_SNAPSHOT(vm_ns);
439
440 if (!rb_ary_shared_with_p(snapshot, features)) {
441 /* The sharing was broken; something (other than us in rb_provide_feature())
442 modified loaded_features. Rebuild the index. */
443 st_foreach(CURRENT_NS_LOADED_FEATURES_INDEX(vm_ns), loaded_features_index_clear_i, 0);
444
445 VALUE realpaths = CURRENT_NS_LOADED_FEATURES_REALPATHS(vm_ns);
446 VALUE realpath_map = CURRENT_NS_LOADED_FEATURES_REALPATH_MAP(vm_ns);
447 VALUE previous_realpath_map = rb_hash_dup(realpath_map);
448 rb_hash_clear(realpaths);
449 rb_hash_clear(realpath_map);
450 for (i = 0; i < RARRAY_LEN(features); i++) {
451 VALUE entry, as_str;
452 as_str = entry = rb_ary_entry(features, i);
453 StringValue(as_str);
454 as_str = rb_fstring(as_str);
455 if (as_str != entry)
456 rb_ary_store(features, i, as_str);
457 features_index_add(vm_ns, as_str, INT2FIX(i));
458 }
459 reset_loaded_features_snapshot(vm_ns);
460
461 features = CURRENT_NS_LOADED_FEATURES_SNAPSHOT(vm_ns);
462 long j = RARRAY_LEN(features);
463 for (i = 0; i < j; i++) {
464 VALUE as_str = rb_ary_entry(features, i);
465 VALUE realpath = rb_hash_aref(previous_realpath_map, as_str);
466 if (NIL_P(realpath)) {
467 realpath = rb_check_realpath(Qnil, as_str, NULL);
468 if (NIL_P(realpath)) realpath = as_str;
469 realpath = rb_fstring(realpath);
470 }
471 rb_hash_aset(realpaths, realpath, Qtrue);
472 rb_hash_aset(realpath_map, as_str, realpath);
473 }
474 }
475 return CURRENT_NS_LOADED_FEATURES_INDEX(vm_ns);
476}
477
478/* This searches `load_path` for a value such that
479 name == "#{load_path[i]}/#{feature}"
480 if `feature` is a suffix of `name`, or otherwise
481 name == "#{load_path[i]}/#{feature}#{ext}"
482 for an acceptable string `ext`. It returns
483 `load_path[i].to_str` if found, else 0.
484
485 If type is 's', then `ext` is acceptable only if IS_DLEXT(ext);
486 if 'r', then only if IS_RBEXT(ext); otherwise `ext` may be absent
487 or have any value matching `%r{^\.[^./]*$}`.
488*/
489static VALUE
490loaded_feature_path(const char *name, long vlen, const char *feature, long len,
491 int type, VALUE load_path)
492{
493 long i;
494 long plen;
495 const char *e;
496
497 if (vlen < len+1) return 0;
498 if (strchr(feature, '.') && !strncmp(name+(vlen-len), feature, len)) {
499 plen = vlen - len;
500 }
501 else {
502 for (e = name + vlen; name != e && *e != '.' && *e != '/'; --e);
503 if (*e != '.' ||
504 e-name < len ||
505 strncmp(e-len, feature, len))
506 return 0;
507 plen = e - name - len;
508 }
509 if (plen > 0 && name[plen-1] != '/') {
510 return 0;
511 }
512 if (type == 's' ? !IS_DLEXT(&name[plen+len]) :
513 type == 'r' ? !IS_RBEXT(&name[plen+len]) :
514 0) {
515 return 0;
516 }
517 /* Now name == "#{prefix}/#{feature}#{ext}" where ext is acceptable
518 (possibly empty) and prefix is some string of length plen. */
519
520 if (plen > 0) --plen; /* exclude '.' */
521 for (i = 0; i < RARRAY_LEN(load_path); ++i) {
522 VALUE p = RARRAY_AREF(load_path, i);
523 const char *s = StringValuePtr(p);
524 long n = RSTRING_LEN(p);
525
526 if (n != plen) continue;
527 if (n && strncmp(name, s, n)) continue;
528 return p;
529 }
530 return 0;
531}
532
534 const char *name;
535 long len;
536 int type;
537 VALUE load_path;
538 const char *result;
539};
540
541static int
542loaded_feature_path_i(st_data_t v, st_data_t b, st_data_t f)
543{
544 const char *s = (const char *)v;
545 struct loaded_feature_searching *fp = (struct loaded_feature_searching *)f;
546 VALUE p = loaded_feature_path(s, strlen(s), fp->name, fp->len,
547 fp->type, fp->load_path);
548 if (!p) return ST_CONTINUE;
549 fp->result = s;
550 return ST_STOP;
551}
552
553/*
554 * Returns the type of already provided feature.
555 * 'r': ruby script (".rb")
556 * 's': shared object (".so"/"."DLEXT)
557 * 'u': unsuffixed
558 */
559static int
560rb_feature_p(vm_ns_t *vm_ns, const char *feature, const char *ext, int rb, int expanded, const char **fn)
561{
562 VALUE features, this_feature_index = Qnil, v, p, load_path = 0;
563 const char *f, *e;
564 long i, len, elen, n;
565 st_table *loading_tbl, *features_index;
566 st_data_t data;
567 st_data_t key;
568 int type;
569
570 if (fn) *fn = 0;
571 if (ext) {
572 elen = strlen(ext);
573 len = strlen(feature) - elen;
574 type = rb ? 'r' : 's';
575 }
576 else {
577 len = strlen(feature);
578 elen = 0;
579 type = 0;
580 }
581 features = get_loaded_features(vm_ns);
582 features_index = get_loaded_features_index(vm_ns);
583
584 key = feature_key(feature, strlen(feature));
585 /* We search `features` for an entry such that either
586 "#{features[i]}" == "#{load_path[j]}/#{feature}#{e}"
587 for some j, or
588 "#{features[i]}" == "#{feature}#{e}"
589 Here `e` is an "allowed" extension -- either empty or one
590 of the extensions accepted by IS_RBEXT, IS_SOEXT, or
591 IS_DLEXT. Further, if `ext && rb` then `IS_RBEXT(e)`,
592 and if `ext && !rb` then `IS_SOEXT(e) || IS_DLEXT(e)`.
593
594 If `expanded`, then only the latter form (without load_path[j])
595 is accepted. Otherwise either form is accepted, *unless* `ext`
596 is false and an otherwise-matching entry of the first form is
597 preceded by an entry of the form
598 "#{features[i2]}" == "#{load_path[j2]}/#{feature}#{e2}"
599 where `e2` matches %r{^\.[^./]*$} but is not an allowed extension.
600 After a "distractor" entry of this form, only entries of the
601 form "#{feature}#{e}" are accepted.
602
603 In `rb_provide_feature()` and `get_loaded_features_index()` we
604 maintain an invariant that the array `this_feature_index` will
605 point to every entry in `features` which has the form
606 "#{prefix}#{feature}#{e}"
607 where `e` is empty or matches %r{^\.[^./]*$}, and `prefix` is empty
608 or ends in '/'. This includes both match forms above, as well
609 as any distractors, so we may ignore all other entries in `features`.
610 */
611 if (st_lookup(features_index, key, &data) && !NIL_P(this_feature_index = (VALUE)data)) {
612 for (size_t i = 0; ; i++) {
613 long index;
614 if (FIXNUM_P(this_feature_index)) {
615 if (i > 0) break;
616 index = FIX2LONG(this_feature_index);
617 }
618 else {
619 feature_indexes_t feature_indexes = (feature_indexes_t)this_feature_index;
620 if (i >= rb_darray_size(feature_indexes)) break;
621 index = rb_darray_get(feature_indexes, i);
622 }
623
624 v = RARRAY_AREF(features, index);
625 f = StringValuePtr(v);
626 if ((n = RSTRING_LEN(v)) < len) continue;
627 if (strncmp(f, feature, len) != 0) {
628 if (expanded) continue;
629 if (!load_path) load_path = get_expanded_load_path(vm_ns);
630 if (!(p = loaded_feature_path(f, n, feature, len, type, load_path)))
631 continue;
632 expanded = 1;
633 f += RSTRING_LEN(p) + 1;
634 }
635 if (!*(e = f + len)) {
636 if (ext) continue;
637 return 'u';
638 }
639 if (*e != '.') continue;
640 if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) {
641 return 's';
642 }
643 if ((rb || !ext) && (IS_RBEXT(e))) {
644 return 'r';
645 }
646 }
647 }
648
649 loading_tbl = get_loading_table(vm_ns);
650 f = 0;
651 if (!expanded && !rb_is_absolute_path(feature)) {
652 struct loaded_feature_searching fs;
653 fs.name = feature;
654 fs.len = len;
655 fs.type = type;
656 fs.load_path = load_path ? load_path : get_expanded_load_path(vm_ns);
657 fs.result = 0;
658 st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs);
659 if ((f = fs.result) != 0) {
660 if (fn) *fn = f;
661 goto loading;
662 }
663 }
664 if (st_get_key(loading_tbl, (st_data_t)feature, &data)) {
665 if (fn) *fn = (const char*)data;
666 goto loading;
667 }
668 else {
669 VALUE bufstr;
670 char *buf;
671 static const char so_ext[][4] = {
672 ".so", ".o",
673 };
674
675 if (ext && *ext) return 0;
676 bufstr = rb_str_tmp_new(len + DLEXT_MAXLEN);
677 buf = RSTRING_PTR(bufstr);
678 MEMCPY(buf, feature, char, len);
679 for (i = 0; (e = loadable_ext[i]) != 0; i++) {
680 strlcpy(buf + len, e, DLEXT_MAXLEN + 1);
681 if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
682 rb_str_resize(bufstr, 0);
683 if (fn) *fn = (const char*)data;
684 return i ? 's' : 'r';
685 }
686 }
687 for (i = 0; i < numberof(so_ext); i++) {
688 strlcpy(buf + len, so_ext[i], DLEXT_MAXLEN + 1);
689 if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
690 rb_str_resize(bufstr, 0);
691 if (fn) *fn = (const char*)data;
692 return 's';
693 }
694 }
695 rb_str_resize(bufstr, 0);
696 }
697 return 0;
698
699 loading:
700 if (!ext) return 'u';
701 return !IS_RBEXT(ext) ? 's' : 'r';
702}
703
704int
705rb_provided(const char *feature)
706{
707 return rb_feature_provided(feature, 0);
708}
709
710static int
711feature_provided(vm_ns_t *vm_ns, const char *feature, const char **loading)
712{
713 const char *ext = strrchr(feature, '.');
714 VALUE fullpath = 0;
715
716 if (*feature == '.' &&
717 (feature[1] == '/' || strncmp(feature+1, "./", 2) == 0)) {
718 fullpath = rb_file_expand_path_fast(rb_get_path(rb_str_new2(feature)), Qnil);
719 feature = RSTRING_PTR(fullpath);
720 }
721 if (ext && !strchr(ext, '/')) {
722 if (IS_RBEXT(ext)) {
723 if (rb_feature_p(vm_ns, feature, ext, TRUE, FALSE, loading)) return TRUE;
724 return FALSE;
725 }
726 else if (IS_SOEXT(ext) || IS_DLEXT(ext)) {
727 if (rb_feature_p(vm_ns, feature, ext, FALSE, FALSE, loading)) return TRUE;
728 return FALSE;
729 }
730 }
731 if (rb_feature_p(vm_ns, feature, 0, TRUE, FALSE, loading))
732 return TRUE;
733 RB_GC_GUARD(fullpath);
734 return FALSE;
735}
736
737int
738rb_feature_provided(const char *feature, const char **loading)
739{
740 GET_vm_ns();
741 return feature_provided(vm_ns, feature, loading);
742}
743
744static void
745rb_provide_feature(vm_ns_t *vm_ns, VALUE feature)
746{
747 VALUE features;
748
749 features = get_loaded_features(vm_ns);
750 if (OBJ_FROZEN(features)) {
751 rb_raise(rb_eRuntimeError,
752 "$LOADED_FEATURES is frozen; cannot append feature");
753 }
754 feature = rb_fstring(feature);
755
756 get_loaded_features_index(vm_ns);
757 // If loaded_features and loaded_features_snapshot share the same backing
758 // array, pushing into it would cause the whole array to be copied.
759 // To avoid this we first clear loaded_features_snapshot.
760 rb_ary_clear(CURRENT_NS_LOADED_FEATURES_SNAPSHOT(vm_ns));
761 rb_ary_push(features, feature);
762 features_index_add(vm_ns, feature, INT2FIX(RARRAY_LEN(features)-1));
763 reset_loaded_features_snapshot(vm_ns);
764}
765
766void
767rb_provide(const char *feature)
768{
769 /*
770 * rb_provide() must use rb_current_namespace to store provided features
771 * in the current namespace's loaded_features, etc.
772 */
773 GET_vm_ns();
774 rb_provide_feature(vm_ns, rb_fstring_cstr(feature));
775}
776
777NORETURN(static void load_failed(VALUE));
778
779static inline VALUE
780realpath_internal_cached(VALUE hash, VALUE path)
781{
782 VALUE ret = rb_hash_aref(hash, path);
783 if(RTEST(ret)) {
784 return ret;
785 }
786
787 VALUE realpath = rb_realpath_internal(Qnil, path, 1);
788 rb_hash_aset(hash, rb_fstring(path), rb_fstring(realpath));
789 return realpath;
790}
791
793 const rb_iseq_t *iseq;
794 bool in_builtin;
795};
796
797static VALUE
798iseq_eval_in_namespace(VALUE arg)
799{
800 struct iseq_eval_in_namespace_data *data = (struct iseq_eval_in_namespace_data *)arg;
801 if (rb_namespace_available() && data->in_builtin) {
802 return rb_iseq_eval_with_refinement(data->iseq, rb_mNamespaceRefiner);
803 }
804 else {
805 return rb_iseq_eval(data->iseq);
806 }
807}
808
809static inline void
810load_iseq_eval(rb_execution_context_t *ec, VALUE fname)
811{
812 GET_loading_vm_ns();
813 const rb_namespace_t *loading_ns = rb_loading_namespace();
814 const rb_iseq_t *iseq = rb_iseq_load_iseq(fname);
815
816 if (!iseq) {
817 rb_execution_context_t *ec = GET_EC();
818 VALUE v = rb_vm_push_frame_fname(ec, fname);
819
820 VALUE realpath_map = get_loaded_features_realpath_map(vm_ns);
821
822 if (rb_ruby_prism_p()) {
823 pm_parse_result_t result = { 0 };
824 result.options.line = 1;
825 result.node.coverage_enabled = 1;
826
827 VALUE error = pm_load_parse_file(&result, fname, NULL);
828
829 if (error == Qnil) {
830 int error_state;
831 iseq = pm_iseq_new_top(&result.node, rb_fstring_lit("<top (required)>"), fname, realpath_internal_cached(realpath_map, fname), NULL, &error_state);
832
833 pm_parse_result_free(&result);
834
835 if (error_state) {
836 RUBY_ASSERT(iseq == NULL);
837 rb_jump_tag(error_state);
838 }
839 }
840 else {
841 rb_vm_pop_frame(ec);
842 RB_GC_GUARD(v);
843 pm_parse_result_free(&result);
844 rb_exc_raise(error);
845 }
846 }
847 else {
848 rb_ast_t *ast;
849 VALUE ast_value;
850 VALUE parser = rb_parser_new();
851 rb_parser_set_context(parser, NULL, FALSE);
852 ast_value = rb_parser_load_file(parser, fname);
853 ast = rb_ruby_ast_data_get(ast_value);
854
855 iseq = rb_iseq_new_top(ast_value, rb_fstring_lit("<top (required)>"),
856 fname, realpath_internal_cached(realpath_map, fname), NULL);
857 rb_ast_dispose(ast);
858 }
859
860 rb_vm_pop_frame(ec);
861 RB_GC_GUARD(v);
862 }
863 rb_exec_event_hook_script_compiled(ec, iseq, Qnil);
864
865 if (loading_ns) {
866 struct iseq_eval_in_namespace_data arg = {
867 .iseq = iseq,
868 .in_builtin = NAMESPACE_BUILTIN_P(loading_ns),
869 };
870 rb_namespace_exec(loading_ns, iseq_eval_in_namespace, (VALUE)&arg);
871 }
872 else {
873 rb_iseq_eval(iseq);
874 }
875}
876
877static inline enum ruby_tag_type
878load_wrapping(rb_execution_context_t *ec, VALUE fname, VALUE load_wrapper)
879{
880 enum ruby_tag_type state;
881 rb_namespace_t *ns;
882 rb_thread_t *th = rb_ec_thread_ptr(ec);
883 volatile VALUE wrapper = th->top_wrapper;
884 volatile VALUE self = th->top_self;
885#if !defined __GNUC__
886 rb_thread_t *volatile th0 = th;
887#endif
888
889 ec->errinfo = Qnil; /* ensure */
890
891 /* load in module as toplevel */
892 if (IS_NAMESPACE(load_wrapper)) {
893 ns = rb_get_namespace_t(load_wrapper);
894 if (!ns->top_self) {
895 ns->top_self = rb_obj_clone(rb_vm_top_self());
896 }
897 th->top_self = ns->top_self;
898 }
899 else {
900 th->top_self = rb_obj_clone(rb_vm_top_self());
901 }
902 th->top_wrapper = load_wrapper;
903 rb_extend_object(th->top_self, th->top_wrapper);
904
905 EC_PUSH_TAG(ec);
906 state = EC_EXEC_TAG();
907 if (state == TAG_NONE) {
908 load_iseq_eval(ec, fname);
909 }
910 EC_POP_TAG();
911
912#if !defined __GNUC__
913 th = th0;
914 fname = RB_GC_GUARD(fname);
915#endif
916 th->top_self = self;
917 th->top_wrapper = wrapper;
918 return state;
919}
920
921static inline void
922raise_load_if_failed(rb_execution_context_t *ec, enum ruby_tag_type state)
923{
924 if (state) {
925 rb_vm_jump_tag_but_local_jump(state);
926 }
927
928 if (!NIL_P(ec->errinfo)) {
929 rb_exc_raise(ec->errinfo);
930 }
931}
932
933static void
934rb_load_internal(VALUE fname, VALUE wrap)
935{
936 VALUE namespace;
937 rb_execution_context_t *ec = GET_EC();
938 const rb_namespace_t *ns = rb_loading_namespace();
939 enum ruby_tag_type state = TAG_NONE;
940 if (RTEST(wrap)) {
941 if (!RB_TYPE_P(wrap, T_MODULE)) {
942 wrap = rb_module_new();
943 }
944 state = load_wrapping(ec, fname, wrap);
945 }
946 else if (NAMESPACE_OPTIONAL_P(ns)) {
947 namespace = ns->ns_object;
948 state = load_wrapping(ec, fname, namespace);
949 }
950 else {
951 load_iseq_eval(ec, fname);
952 }
953 raise_load_if_failed(ec, state);
954}
955
956void
957rb_load(VALUE fname, int wrap)
958{
959 VALUE tmp = rb_find_file(FilePathValue(fname));
960 if (!tmp) load_failed(fname);
961 rb_load_internal(tmp, RBOOL(wrap));
962}
963
964void
965rb_load_protect(VALUE fname, int wrap, int *pstate)
966{
967 enum ruby_tag_type state;
968
969 EC_PUSH_TAG(GET_EC());
970 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
971 rb_load(fname, wrap);
972 }
973 EC_POP_TAG();
974
975 if (state != TAG_NONE) *pstate = state;
976}
977
978static VALUE
979load_entrypoint_internal(VALUE fname, VALUE wrap)
980{
981 VALUE path, orig_fname;
982
983 orig_fname = rb_get_path_check_to_string(fname);
984 fname = rb_str_encode_ospath(orig_fname);
985 RUBY_DTRACE_HOOK(LOAD_ENTRY, RSTRING_PTR(orig_fname));
986
987 path = rb_find_file(fname);
988 if (!path) {
989 if (!rb_file_load_ok(RSTRING_PTR(fname)))
990 load_failed(orig_fname);
991 path = fname;
992 }
993 rb_load_internal(path, wrap);
994
995 RUBY_DTRACE_HOOK(LOAD_RETURN, RSTRING_PTR(orig_fname));
996
997 return Qtrue;
998}
999
1000VALUE
1001rb_load_entrypoint(VALUE args)
1002{
1003 VALUE fname, wrap;
1004 if (RARRAY_LEN(args) != 2) {
1005 rb_bug("invalid arguments: %ld", RARRAY_LEN(args));
1006 }
1007 fname = rb_ary_entry(args, 0);
1008 wrap = rb_ary_entry(args, 1);
1009 return load_entrypoint_internal(fname, wrap);
1010}
1011
1012/*
1013 * call-seq:
1014 * load(filename, wrap=false) -> true
1015 *
1016 * Loads and executes the Ruby program in the file _filename_.
1017 *
1018 * If the filename is an absolute path (e.g. starts with '/'), the file
1019 * will be loaded directly using the absolute path.
1020 *
1021 * If the filename is an explicit relative path (e.g. starts with './' or
1022 * '../'), the file will be loaded using the relative path from the current
1023 * directory.
1024 *
1025 * Otherwise, the file will be searched for in the library
1026 * directories listed in <code>$LOAD_PATH</code> (<code>$:</code>).
1027 * If the file is found in a directory, it will attempt to load the file
1028 * relative to that directory. If the file is not found in any of the
1029 * directories in <code>$LOAD_PATH</code>, the file will be loaded using
1030 * the relative path from the current directory.
1031 *
1032 * If the file doesn't exist when there is an attempt to load it, a
1033 * LoadError will be raised.
1034 *
1035 * If the optional _wrap_ parameter is +true+, the loaded script will
1036 * be executed under an anonymous module. If the optional _wrap_ parameter
1037 * is a module, the loaded script will be executed under the given module.
1038 * In no circumstance will any local variables in the loaded file be
1039 * propagated to the loading environment.
1040 */
1041
1042static VALUE
1043rb_f_load(int argc, VALUE *argv, VALUE _)
1044{
1045 VALUE fname, wrap;
1046 rb_scan_args(argc, argv, "11", &fname, &wrap);
1047 return load_entrypoint_internal(fname, wrap);
1048}
1049
1050static char *
1051load_lock(vm_ns_t *vm_ns, const char *ftptr, bool warn)
1052{
1053 st_data_t data;
1054 st_table *loading_tbl = get_loading_table(vm_ns);
1055
1056 if (!st_lookup(loading_tbl, (st_data_t)ftptr, &data)) {
1057 /* partial state */
1058 ftptr = ruby_strdup(ftptr);
1059 data = (st_data_t)rb_thread_shield_new();
1060 st_insert(loading_tbl, (st_data_t)ftptr, data);
1061 return (char *)ftptr;
1062 }
1063
1064 if (warn && rb_thread_shield_owned((VALUE)data)) {
1065 VALUE warning = rb_warning_string("loading in progress, circular require considered harmful - %s", ftptr);
1066 rb_backtrace_each(rb_str_append, warning);
1067 rb_warning("%"PRIsVALUE, warning);
1068 }
1069 switch (rb_thread_shield_wait((VALUE)data)) {
1070 case Qfalse:
1071 case Qnil:
1072 return 0;
1073 }
1074 return (char *)ftptr;
1075}
1076
1077static int
1078release_thread_shield(st_data_t *key, st_data_t *value, st_data_t done, int existing)
1079{
1080 VALUE thread_shield = (VALUE)*value;
1081 if (!existing) return ST_STOP;
1082 if (done) {
1083 rb_thread_shield_destroy(thread_shield);
1084 /* Delete the entry even if there are waiting threads, because they
1085 * won't load the file and won't delete the entry. */
1086 }
1087 else if (rb_thread_shield_release(thread_shield)) {
1088 /* still in-use */
1089 return ST_CONTINUE;
1090 }
1091 xfree((char *)*key);
1092 return ST_DELETE;
1093}
1094
1095static void
1096load_unlock(vm_ns_t *vm_ns, const char *ftptr, int done)
1097{
1098 if (ftptr) {
1099 st_data_t key = (st_data_t)ftptr;
1100 st_table *loading_tbl = get_loading_table(vm_ns);
1101
1102 st_update(loading_tbl, key, release_thread_shield, done);
1103 }
1104}
1105
1106static VALUE rb_require_string_internal(VALUE fname, bool resurrect);
1107
1108/*
1109 * call-seq:
1110 * require(name) -> true or false
1111 *
1112 * Loads the given +name+, returning +true+ if successful and +false+ if the
1113 * feature is already loaded.
1114 *
1115 * If the filename neither resolves to an absolute path nor starts with
1116 * './' or '../', the file will be searched for in the library
1117 * directories listed in <code>$LOAD_PATH</code> (<code>$:</code>).
1118 * If the filename starts with './' or '../', resolution is based on Dir.pwd.
1119 *
1120 * If the filename has the extension ".rb", it is loaded as a source file; if
1121 * the extension is ".so", ".o", or the default shared library extension on
1122 * the current platform, Ruby loads the shared library as a Ruby extension.
1123 * Otherwise, Ruby tries adding ".rb", ".so", and so on to the name until
1124 * found. If the file named cannot be found, a LoadError will be raised.
1125 *
1126 * For Ruby extensions the filename given may use ".so" or ".o". For example,
1127 * on macOS the socket extension is "socket.bundle" and
1128 * <code>require 'socket.so'</code> will load the socket extension.
1129 *
1130 * The absolute path of the loaded file is added to
1131 * <code>$LOADED_FEATURES</code> (<code>$"</code>). A file will not be
1132 * loaded again if its path already appears in <code>$"</code>. For example,
1133 * <code>require 'a'; require './a'</code> will not load <code>a.rb</code>
1134 * again.
1135 *
1136 * require "my-library.rb"
1137 * require "db-driver"
1138 *
1139 * Any constants or globals within the loaded source file will be available
1140 * in the calling program's global namespace. However, local variables will
1141 * not be propagated to the loading environment.
1142 *
1143 */
1144
1145VALUE
1147{
1148 // const rb_namespace_t *ns = rb_loading_namespace();
1149 // printf("F:current loading ns: %ld\n", ns->ns_id);
1150 return rb_require_string(fname);
1151}
1152
1153VALUE
1154rb_require_relative_entrypoint(VALUE fname)
1155{
1156 VALUE base = rb_current_realfilepath();
1157 if (NIL_P(base)) {
1158 rb_loaderror("cannot infer basepath");
1159 }
1160 base = rb_file_dirname(base);
1161 return rb_require_string_internal(rb_file_absolute_path(fname, base), false);
1162}
1163
1164/*
1165 * call-seq:
1166 * require_relative(string) -> true or false
1167 *
1168 * Ruby tries to load the library named _string_ relative to the directory
1169 * containing the requiring file. If the file does not exist a LoadError is
1170 * raised. Returns +true+ if the file was loaded and +false+ if the file was
1171 * already loaded before.
1172 */
1173VALUE
1174rb_f_require_relative(VALUE obj, VALUE fname)
1175{
1176 return rb_require_relative_entrypoint(fname);
1177}
1178
1179typedef int (*feature_func)(vm_ns_t *vm_ns, const char *feature, const char *ext, int rb, int expanded, const char **fn);
1180
1181static int
1182search_required(vm_ns_t *vm_ns, VALUE fname, volatile VALUE *path, feature_func rb_feature_p)
1183{
1184 VALUE tmp;
1185 char *ext, *ftptr;
1186 int ft = 0;
1187 const char *loading;
1188
1189 *path = 0;
1190 ext = strrchr(ftptr = RSTRING_PTR(fname), '.');
1191 if (ext && !strchr(ext, '/')) {
1192 if (IS_RBEXT(ext)) {
1193 if (rb_feature_p(vm_ns, ftptr, ext, TRUE, FALSE, &loading)) {
1194 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1195 return 'r';
1196 }
1197 if ((tmp = rb_find_file(fname)) != 0) {
1198 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
1199 if (!rb_feature_p(vm_ns, ftptr, ext, TRUE, TRUE, &loading) || loading)
1200 *path = tmp;
1201 return 'r';
1202 }
1203 return 0;
1204 }
1205 else if (IS_SOEXT(ext)) {
1206 if (rb_feature_p(vm_ns, ftptr, ext, FALSE, FALSE, &loading)) {
1207 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1208 return 's';
1209 }
1210 tmp = rb_str_subseq(fname, 0, ext - RSTRING_PTR(fname));
1211 rb_str_cat2(tmp, DLEXT);
1212 OBJ_FREEZE(tmp);
1213 if ((tmp = rb_find_file(tmp)) != 0) {
1214 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
1215 if (!rb_feature_p(vm_ns, ftptr, ext, FALSE, TRUE, &loading) || loading)
1216 *path = tmp;
1217 return 's';
1218 }
1219 }
1220 else if (IS_DLEXT(ext)) {
1221 if (rb_feature_p(vm_ns, ftptr, ext, FALSE, FALSE, &loading)) {
1222 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1223 return 's';
1224 }
1225 if ((tmp = rb_find_file(fname)) != 0) {
1226 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
1227 if (!rb_feature_p(vm_ns, ftptr, ext, FALSE, TRUE, &loading) || loading)
1228 *path = tmp;
1229 return 's';
1230 }
1231 }
1232 }
1233 else if ((ft = rb_feature_p(vm_ns, ftptr, 0, FALSE, FALSE, &loading)) == 'r') {
1234 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1235 return 'r';
1236 }
1237 tmp = fname;
1238 const unsigned int type = rb_find_file_ext(&tmp, ft == 's' ? ruby_ext : loadable_ext);
1239
1240 // Check if it's a statically linked extension when
1241 // not already a feature and not found as a dynamic library.
1242 if (!ft && type != loadable_ext_rb && vm_ns->vm->static_ext_inits) {
1243 VALUE lookup_name = tmp;
1244 // Append ".so" if not already present so for example "etc" can find "etc.so".
1245 // We always register statically linked extensions with a ".so" extension.
1246 // See encinit.c and extinit.c (generated at build-time).
1247 if (!ext) {
1248 lookup_name = rb_str_dup(lookup_name);
1249 rb_str_cat_cstr(lookup_name, ".so");
1250 }
1251 ftptr = RSTRING_PTR(lookup_name);
1252 if (st_lookup(vm_ns->vm->static_ext_inits, (st_data_t)ftptr, NULL)) {
1253 *path = rb_filesystem_str_new_cstr(ftptr);
1254 RB_GC_GUARD(lookup_name);
1255 return 's';
1256 }
1257 }
1258
1259 switch (type) {
1260 case 0:
1261 if (ft)
1262 goto feature_present;
1263 ftptr = RSTRING_PTR(tmp);
1264 return rb_feature_p(vm_ns, ftptr, 0, FALSE, TRUE, 0);
1265
1266 default:
1267 if (ft) {
1268 goto feature_present;
1269 }
1270 /* fall through */
1271 case loadable_ext_rb:
1272 ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
1273 if (rb_feature_p(vm_ns, ftptr, ext, type == loadable_ext_rb, TRUE, &loading) && !loading)
1274 break;
1275 *path = tmp;
1276 }
1277 return type > loadable_ext_rb ? 's' : 'r';
1278
1279 feature_present:
1280 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1281 return ft;
1282}
1283
1284static void
1285load_failed(VALUE fname)
1286{
1287 rb_load_fail(fname, "cannot load such file");
1288}
1289
1290static VALUE
1291load_ext(VALUE path, VALUE fname)
1292{
1293 VALUE loaded = path;
1294 GET_loading_vm_ns();
1295 if (NAMESPACE_USER_P(vm_ns->ns)) {
1296 loaded = rb_namespace_local_extension(vm_ns->ns->ns_object, fname, path);
1297 }
1298 rb_scope_visibility_set(METHOD_VISI_PUBLIC);
1299 return (VALUE)dln_load_feature(RSTRING_PTR(loaded), RSTRING_PTR(fname));
1300}
1301
1302static bool
1303run_static_ext_init(rb_vm_t *vm, const char *feature)
1304{
1305 st_data_t key = (st_data_t)feature;
1306 st_data_t init_func;
1307
1308 if (vm->static_ext_inits && st_delete(vm->static_ext_inits, &key, &init_func)) {
1309 ((void (*)(void))init_func)();
1310 return true;
1311 }
1312 return false;
1313}
1314
1315static int
1316no_feature_p(vm_ns_t *vm_ns, const char *feature, const char *ext, int rb, int expanded, const char **fn)
1317{
1318 return 0;
1319}
1320
1321// Documented in doc/globals.rdoc
1322VALUE
1323rb_resolve_feature_path(VALUE klass, VALUE fname)
1324{
1325 VALUE path;
1326 int found;
1327 VALUE sym;
1328 GET_loading_vm_ns();
1329
1330 fname = rb_get_path(fname);
1331 path = rb_str_encode_ospath(fname);
1332 found = search_required(vm_ns, path, &path, no_feature_p);
1333
1334 switch (found) {
1335 case 'r':
1336 sym = ID2SYM(rb_intern("rb"));
1337 break;
1338 case 's':
1339 sym = ID2SYM(rb_intern("so"));
1340 break;
1341 default:
1342 return Qnil;
1343 }
1344
1345 return rb_ary_new_from_args(2, sym, path);
1346}
1347
1348static void
1349ext_config_push(rb_thread_t *th, struct rb_ext_config *prev)
1350{
1351 *prev = th->ext_config;
1352 th->ext_config = (struct rb_ext_config){0};
1353}
1354
1355static void
1356ext_config_pop(rb_thread_t *th, struct rb_ext_config *prev)
1357{
1358 th->ext_config = *prev;
1359}
1360
1361void
1363{
1364 GET_THREAD()->ext_config.ractor_safe = flag;
1365}
1366
1368 VALUE recv;
1369 VALUE arg1;
1370 VALUE arg2;
1371 VALUE block_handler;
1372 VALUE filename;
1373};
1374
1375static VALUE
1376call_load_ext_in_ns(VALUE data)
1377{
1378 struct rb_vm_call_cfunc2_data *arg = (struct rb_vm_call_cfunc2_data *)data;
1379 return rb_vm_call_cfunc2(arg->recv, load_ext, arg->arg1, arg->arg2, arg->block_handler, arg->filename);
1380}
1381
1382/*
1383 * returns
1384 * 0: if already loaded (false)
1385 * 1: successfully loaded (true)
1386 * <0: not found (LoadError)
1387 * >1: exception
1388 */
1389static int
1390require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool warn)
1391{
1392 volatile int result = -1;
1393 rb_thread_t *th = rb_ec_thread_ptr(ec);
1394 volatile const struct {
1395 VALUE wrapper, self, errinfo;
1397 } saved = {
1398 th->top_wrapper, th->top_self, ec->errinfo,
1399 ec,
1400 };
1401 GET_loading_vm_ns();
1402 enum ruby_tag_type state;
1403 char *volatile ftptr = 0;
1404 VALUE path;
1405 volatile VALUE saved_path;
1406 volatile VALUE realpath = 0;
1407 VALUE realpaths = get_loaded_features_realpaths(vm_ns);
1408 VALUE realpath_map = get_loaded_features_realpath_map(vm_ns);
1409 volatile bool reset_ext_config = false;
1410 struct rb_ext_config prev_ext_config;
1411
1412 path = rb_str_encode_ospath(fname);
1413 RUBY_DTRACE_HOOK(REQUIRE_ENTRY, RSTRING_PTR(fname));
1414 saved_path = path;
1415
1416 EC_PUSH_TAG(ec);
1417 ec->errinfo = Qnil; /* ensure */
1418 th->top_wrapper = 0;
1419 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1420 VALUE handle;
1421 int found;
1422
1423 RUBY_DTRACE_HOOK(FIND_REQUIRE_ENTRY, RSTRING_PTR(fname));
1424 found = search_required(vm_ns, path, &saved_path, rb_feature_p);
1425 RUBY_DTRACE_HOOK(FIND_REQUIRE_RETURN, RSTRING_PTR(fname));
1426 path = saved_path;
1427
1428 if (found) {
1429 if (!path || !(ftptr = load_lock(vm_ns, RSTRING_PTR(path), warn))) {
1430 result = 0;
1431 }
1432 else if (!*ftptr) {
1433 result = TAG_RETURN;
1434 }
1435 else if (found == 's' && run_static_ext_init(th->vm, RSTRING_PTR(path))) {
1436 result = TAG_RETURN;
1437 }
1438 else if (RTEST(rb_hash_aref(realpaths,
1439 realpath = realpath_internal_cached(realpath_map, path)))) {
1440 result = 0;
1441 }
1442 else {
1443 switch (found) {
1444 case 'r':
1445 // iseq_eval_in_namespace will be called with the loading namespace eventually
1446 if (NAMESPACE_OPTIONAL_P(vm_ns->ns)) {
1447 // check with NAMESPACE_OPTIONAL_P (not NAMESPACE_USER_P) for NS1::xxx naming
1448 // it is not expected for the main namespace
1449 load_wrapping(saved.ec, path, vm_ns->ns->ns_object);
1450 }
1451 else {
1452 load_iseq_eval(saved.ec, path);
1453 }
1454 break;
1455
1456 case 's':
1457 // the loading namespace must be set to the current namespace before calling load_ext
1458 reset_ext_config = true;
1459 ext_config_push(th, &prev_ext_config);
1460 struct rb_vm_call_cfunc2_data arg = {
1461 .recv = rb_vm_top_self(),
1462 .arg1 = path,
1463 .arg2 = fname,
1464 .block_handler = VM_BLOCK_HANDLER_NONE,
1465 .filename = path,
1466 };
1467 handle = rb_namespace_exec(vm_ns->ns, call_load_ext_in_ns, (VALUE)&arg);
1468 rb_hash_aset(CURRENT_NS_RUBY_DLN_LIBMAP(vm_ns, ruby_dln_libmap), path,
1469 SVALUE2NUM((SIGNED_VALUE)handle));
1470 break;
1471 }
1472 result = TAG_RETURN;
1473 }
1474 }
1475 }
1476 EC_POP_TAG();
1477
1478 ec = saved.ec;
1479 rb_thread_t *th2 = rb_ec_thread_ptr(ec);
1480 th2->top_self = saved.self;
1481 th2->top_wrapper = saved.wrapper;
1482 if (reset_ext_config) ext_config_pop(th2, &prev_ext_config);
1483
1484 path = saved_path;
1485 if (ftptr) load_unlock(vm_ns, RSTRING_PTR(path), !state);
1486
1487 if (state) {
1488 if (state == TAG_FATAL || state == TAG_THROW) {
1489 EC_JUMP_TAG(ec, state);
1490 }
1491 else if (exception) {
1492 /* usually state == TAG_RAISE only, except for
1493 * rb_iseq_load_iseq in load_iseq_eval case */
1494 VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
1495 if (!NIL_P(exc)) ec->errinfo = exc;
1496 return TAG_RAISE;
1497 }
1498 else if (state == TAG_RETURN) {
1499 return TAG_RAISE;
1500 }
1501 RB_GC_GUARD(fname);
1502 /* never TAG_RETURN */
1503 return state;
1504 }
1505 if (!NIL_P(ec->errinfo)) {
1506 if (!exception) return TAG_RAISE;
1507 rb_exc_raise(ec->errinfo);
1508 }
1509
1510 if (result == TAG_RETURN) {
1511 rb_provide_feature(vm_ns, path);
1512 VALUE real = realpath;
1513 if (real) {
1514 real = rb_fstring(real);
1515 rb_hash_aset(realpaths, real, Qtrue);
1516 }
1517 }
1518 ec->errinfo = saved.errinfo;
1519
1520 RUBY_DTRACE_HOOK(REQUIRE_RETURN, RSTRING_PTR(fname));
1521
1522 return result;
1523}
1524
1525int
1526rb_require_internal_silent(VALUE fname)
1527{
1528 if (!rb_ractor_main_p()) {
1529 return NUM2INT(rb_ractor_require(fname, true));
1530 }
1531
1532 rb_execution_context_t *ec = GET_EC();
1533 return require_internal(ec, fname, 1, false);
1534}
1535
1536int
1537rb_require_internal(VALUE fname)
1538{
1539 rb_execution_context_t *ec = GET_EC();
1540 return require_internal(ec, fname, 1, RTEST(ruby_verbose));
1541}
1542
1543int
1544ruby_require_internal(const char *fname, unsigned int len)
1545{
1546 struct RString fake;
1547 VALUE str = rb_setup_fake_str(&fake, fname, len, 0);
1548 rb_execution_context_t *ec = GET_EC();
1549 int result = require_internal(ec, str, 0, RTEST(ruby_verbose));
1550 rb_set_errinfo(Qnil);
1551 return result == TAG_RETURN ? 1 : result ? -1 : 0;
1552}
1553
1554VALUE
1556{
1557 return rb_require_string_internal(FilePathValue(fname), false);
1558}
1559
1560static VALUE
1561rb_require_string_internal(VALUE fname, bool resurrect)
1562{
1563 rb_execution_context_t *ec = GET_EC();
1564
1565 // main ractor check
1566 if (!rb_ractor_main_p()) {
1567 if (resurrect) fname = rb_str_resurrect(fname);
1568 return rb_ractor_require(fname, false);
1569 }
1570 else {
1571 int result = require_internal(ec, fname, 1, RTEST(ruby_verbose));
1572
1573 if (result > TAG_RETURN) {
1574 EC_JUMP_TAG(ec, result);
1575 }
1576 if (result < 0) {
1577 if (resurrect) fname = rb_str_resurrect(fname);
1578 load_failed(fname);
1579 }
1580
1581 return RBOOL(result);
1582 }
1583}
1584
1585VALUE
1586rb_require(const char *fname)
1587{
1588 struct RString fake;
1589 VALUE str = rb_setup_fake_str(&fake, fname, strlen(fname), 0);
1590 return rb_require_string_internal(str, true);
1591}
1592
1593static int
1594register_init_ext(st_data_t *key, st_data_t *value, st_data_t init, int existing)
1595{
1596 const char *name = (char *)*key;
1597 if (existing) {
1598 /* already registered */
1599 rb_warn("%s is already registered", name);
1600 }
1601 else {
1602 *value = (st_data_t)init;
1603 }
1604 return ST_CONTINUE;
1605}
1606
1607// Private API for statically linked extensions.
1608// Used with the ext/Setup file, the --with-setup and
1609// --with-static-linked-ext configuration option, etc.
1610void
1611ruby_init_ext(const char *name, void (*init)(void))
1612{
1613 st_table *inits_table;
1614 GET_loading_vm_ns();
1615
1616 if (feature_provided(vm_ns, name, 0))
1617 return;
1618
1619 inits_table = vm_ns->vm->static_ext_inits;
1620 if (!inits_table) {
1621 inits_table = st_init_strtable();
1622 vm_ns->vm->static_ext_inits = inits_table;
1623 }
1624 st_update(inits_table, (st_data_t)name, register_init_ext, (st_data_t)init);
1625}
1626
1627/*
1628 * call-seq:
1629 * mod.autoload(const, filename) -> nil
1630 *
1631 * Registers _filename_ to be loaded (using Kernel::require)
1632 * the first time that _const_ (which may be a String or
1633 * a symbol) is accessed in the namespace of _mod_.
1634 *
1635 * module A
1636 * end
1637 * A.autoload(:B, "b")
1638 * A::B.doit # autoloads "b"
1639 *
1640 * If _const_ in _mod_ is defined as autoload, the file name to be
1641 * loaded is replaced with _filename_. If _const_ is defined but not
1642 * as autoload, does nothing.
1643 *
1644 * Files that are currently being loaded must not be registered for
1645 * autoload.
1646 */
1647
1648static VALUE
1649rb_mod_autoload(VALUE mod, VALUE sym, VALUE file)
1650{
1651 ID id = rb_to_id(sym);
1652
1653 FilePathValue(file);
1654 rb_autoload_str(mod, id, file);
1655 return Qnil;
1656}
1657
1658/*
1659 * call-seq:
1660 * mod.autoload?(name, inherit=true) -> String or nil
1661 *
1662 * Returns _filename_ to be loaded if _name_ is registered as
1663 * +autoload+ in the namespace of _mod_ or one of its ancestors.
1664 *
1665 * module A
1666 * end
1667 * A.autoload(:B, "b")
1668 * A.autoload?(:B) #=> "b"
1669 *
1670 * If +inherit+ is false, the lookup only checks the autoloads in the receiver:
1671 *
1672 * class A
1673 * autoload :CONST, "const.rb"
1674 * end
1675 *
1676 * class B < A
1677 * end
1678 *
1679 * B.autoload?(:CONST) #=> "const.rb", found in A (ancestor)
1680 * B.autoload?(:CONST, false) #=> nil, not found in B itself
1681 *
1682 */
1683
1684static VALUE
1685rb_mod_autoload_p(int argc, VALUE *argv, VALUE mod)
1686{
1687 int recur = (rb_check_arity(argc, 1, 2) == 1) ? TRUE : RTEST(argv[1]);
1688 VALUE sym = argv[0];
1689
1690 ID id = rb_check_id(&sym);
1691 if (!id) {
1692 return Qnil;
1693 }
1694 return rb_autoload_at_p(mod, id, recur);
1695}
1696
1697/*
1698 * call-seq:
1699 * autoload(const, filename) -> nil
1700 *
1701 * Registers _filename_ to be loaded (using Kernel::require)
1702 * the first time that _const_ (which may be a String or
1703 * a symbol) is accessed.
1704 *
1705 * autoload(:MyModule, "/usr/local/lib/modules/my_module.rb")
1706 *
1707 * If _const_ is defined as autoload, the file name to be loaded is
1708 * replaced with _filename_. If _const_ is defined but not as
1709 * autoload, does nothing.
1710 *
1711 * Files that are currently being loaded must not be registered for
1712 * autoload.
1713 */
1714
1715static VALUE
1716rb_f_autoload(VALUE obj, VALUE sym, VALUE file)
1717{
1718 VALUE klass = rb_class_real(rb_vm_cbase());
1719 if (!klass) {
1720 rb_raise(rb_eTypeError, "Can not set autoload on singleton class");
1721 }
1722 return rb_mod_autoload(klass, sym, file);
1723}
1724
1725/*
1726 * call-seq:
1727 * autoload?(name, inherit=true) -> String or nil
1728 *
1729 * Returns _filename_ to be loaded if _name_ is registered as
1730 * +autoload+ in the current namespace or one of its ancestors.
1731 *
1732 * autoload(:B, "b")
1733 * autoload?(:B) #=> "b"
1734 *
1735 * module C
1736 * autoload(:D, "d")
1737 * autoload?(:D) #=> "d"
1738 * autoload?(:B) #=> nil
1739 * end
1740 *
1741 * class E
1742 * autoload(:F, "f")
1743 * autoload?(:F) #=> "f"
1744 * autoload?(:B) #=> "b"
1745 * end
1746 */
1747
1748static VALUE
1749rb_f_autoload_p(int argc, VALUE *argv, VALUE obj)
1750{
1751 /* use rb_vm_cbase() as same as rb_f_autoload. */
1752 VALUE klass = rb_vm_cbase();
1753 if (NIL_P(klass)) {
1754 return Qnil;
1755 }
1756 return rb_mod_autoload_p(argc, argv, klass);
1757}
1758
1759void *
1760rb_ext_resolve_symbol(const char* fname, const char* symbol)
1761{
1762 VALUE handle;
1763 VALUE resolved;
1764 VALUE path;
1765 char *ext;
1766 VALUE fname_str = rb_str_new_cstr(fname);
1767 GET_loading_vm_ns();
1768
1769 resolved = rb_resolve_feature_path((VALUE)NULL, fname_str);
1770 if (NIL_P(resolved)) {
1771 ext = strrchr(fname, '.');
1772 if (!ext || !IS_SOEXT(ext)) {
1773 rb_str_cat_cstr(fname_str, ".so");
1774 }
1775 if (rb_feature_p(vm_ns, fname, 0, FALSE, FALSE, 0)) {
1776 return dln_symbol(NULL, symbol);
1777 }
1778 return NULL;
1779 }
1780 if (RARRAY_LEN(resolved) != 2 || rb_ary_entry(resolved, 0) != ID2SYM(rb_intern("so"))) {
1781 return NULL;
1782 }
1783 path = rb_ary_entry(resolved, 1);
1784 handle = rb_hash_lookup(CURRENT_NS_RUBY_DLN_LIBMAP(vm_ns, ruby_dln_libmap), path);
1785 if (NIL_P(handle)) {
1786 return NULL;
1787 }
1788 return dln_symbol((void *)NUM2SVALUE(handle), symbol);
1789}
1790
1791void
1792Init_load(void)
1793{
1794 rb_vm_t *vm = GET_VM();
1795 static const char var_load_path[] = "$:";
1796 ID id_load_path = rb_intern2(var_load_path, sizeof(var_load_path)-1);
1797
1798 rb_define_hooked_variable(var_load_path, (VALUE*)vm, load_path_getter, rb_gvar_readonly_setter);
1799 rb_gvar_namespace_ready(var_load_path);
1800 rb_alias_variable(rb_intern_const("$-I"), id_load_path);
1801 rb_alias_variable(rb_intern_const("$LOAD_PATH"), id_load_path);
1802 vm->load_path = rb_ary_new();
1803 vm->expanded_load_path = rb_ary_hidden_new(0);
1804 vm->load_path_snapshot = rb_ary_hidden_new(0);
1805 vm->load_path_check_cache = 0;
1806 rb_define_singleton_method(vm->load_path, "resolve_feature_path", rb_resolve_feature_path, 1);
1807
1808 rb_define_virtual_variable("$\"", get_LOADED_FEATURES, 0);
1809 rb_gvar_namespace_ready("$\"");
1810 rb_define_virtual_variable("$LOADED_FEATURES", get_LOADED_FEATURES, 0); // TODO: rb_alias_variable ?
1811 rb_gvar_namespace_ready("$LOADED_FEATURES");
1812 vm->loaded_features = rb_ary_new();
1813 vm->loaded_features_snapshot = rb_ary_hidden_new(0);
1814 vm->loaded_features_index = st_init_numtable();
1815 vm->loaded_features_realpaths = rb_hash_new();
1816 rb_obj_hide(vm->loaded_features_realpaths);
1817 vm->loaded_features_realpath_map = rb_hash_new();
1818 rb_obj_hide(vm->loaded_features_realpath_map);
1819
1820 rb_define_global_function("load", rb_f_load, -1);
1822 rb_define_global_function("require_relative", rb_f_require_relative, 1);
1823 rb_define_method(rb_cModule, "autoload", rb_mod_autoload, 2);
1824 rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, -1);
1825 rb_define_global_function("autoload", rb_f_autoload, 2);
1826 rb_define_global_function("autoload?", rb_f_autoload_p, -1);
1827
1828 ruby_dln_libmap = rb_hash_new_with_size(0);
1829 rb_vm_register_global_object(ruby_dln_libmap);
1830}
#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:1784
VALUE rb_module_new(void)
Creates a new, anonymous module.
Definition class.c:1573
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 NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define FIX2LONG
Old name of RB_FIX2LONG.
Definition long.h:46
#define NIL_P
Old name of RB_NIL_P.
#define FIXNUM_P
Old name of RB_FIXNUM_P.
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:682
#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:101
VALUE rb_cModule
Module class.
Definition object.c:63
VALUE rb_mNamespaceRefiner
Namespace::Refiner module.
Definition namespace.c:22
VALUE rb_class_real(VALUE klass)
Finds a "real" class.
Definition object.c:233
VALUE rb_obj_clone(VALUE obj)
Produces a shallow copy of the given object.
Definition object.c:498
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:767
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:1146
void rb_ext_ractor_safe(bool flag)
Asserts that the extension library that calls this function is aware of Ractor.
Definition load.c:1362
VALUE rb_require_string(VALUE feature)
Finds and loads the given feature, if absent.
Definition load.c:1555
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:738
void rb_load_protect(VALUE path, int wrap, int *state)
Identical to rb_load(), except it avoids potential global escapes.
Definition load.c:965
int rb_provided(const char *feature)
Queries if the given feature has already been loaded into the execution context.
Definition load.c:705
void rb_load(VALUE path, int wrap)
Loads and executes the Ruby program in the given file.
Definition load.c:957
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:1760
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:3757
VALUE rb_str_tmp_new(long len)
Allocates a "temporary" string.
Definition string.c:1711
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:3113
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition string.c:1956
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:1974
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:1404
VALUE rb_str_equal(VALUE str1, VALUE str2)
Equality of two strings.
Definition string.c:4230
#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:3238
#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:1139
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:1177
ID rb_to_id(VALUE str)
Definition string.c:12558
rb_gvar_setter_t rb_gvar_readonly_setter
This function just raises rb_eNameError.
Definition variable.h:135
int len
Length of the buffer.
Definition io.h:8
char * ruby_strdup(const char *str)
This is our own version of strdup(3) that uses ruby_xmalloc() instead of system malloc (benefits our ...
Definition util.c:515
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:372
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
#define MEMMOVE(p1, p2, type, n)
Handy macro to call memmove.
Definition memory.h:384
void rb_define_hooked_variable(const char *q, VALUE *w, type *e, void_type *r)
Define a function-backended global variable.
VALUE type(ANYARGS)
ANYARGS-ed function type.
void rb_define_virtual_variable(const char *q, type *w, void_type *e)
Define a function-backended global variable.
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
#define RARRAY_AREF(a, i)
Definition rarray.h:403
#define StringValue(v)
Ensures that the parameter object is a String.
Definition rstring.h:66
#define StringValuePtr(v)
Identical to StringValue, except it returns a char*.
Definition rstring.h:76
VALUE rb_require(const char *feature)
Identical to rb_require_string(), except it takes C's string instead of Ruby's.
Definition load.c:1586
#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