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