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