Ruby 4.1.0dev (2026-08-15 revision d1c079751b80352d347452c8d134ffb177838adb)
load.c (d1c079751b80352d347452c8d134ffb177838adb)
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
734static inline void
735load_iseq_eval(rb_execution_context_t *ec, VALUE fname)
736{
737 const rb_box_t *box = rb_loading_box();
738 const rb_iseq_t *iseq = rb_iseq_load_iseq(fname);
739
740 if (!iseq) {
741 rb_execution_context_t *ec = GET_EC();
742 VALUE v = rb_vm_push_frame_fname(ec, fname);
743
744 VALUE realpath_map = box->loaded_features_realpath_map;
745
746 if (rb_ruby_prism_p()) {
747 pm_parse_result_t result;
748 pm_parse_result_init(&result);
749 result.node.coverage_enabled = 1;
750
751 VALUE error = pm_load_parse_file(&result, fname, NULL);
752
753 if (error == Qnil) {
754 int error_state;
755 iseq = pm_iseq_new_top(&result.node, rb_fstring_lit("<top (required)>"), fname, realpath_internal_cached(realpath_map, fname), NULL, &error_state);
756
757 pm_parse_result_free(&result);
758
759 if (error_state) {
760 RUBY_ASSERT(iseq == NULL);
761 rb_jump_tag(error_state);
762 }
763 }
764 else {
765 rb_vm_pop_frame(ec);
766 RB_GC_GUARD(v);
767 pm_parse_result_free(&result);
768 rb_exc_raise(error);
769 }
770 }
771 else {
772 rb_ast_t *ast;
773 VALUE ast_value;
774 VALUE parser = rb_parser_new();
775 rb_parser_set_context(parser, NULL, FALSE);
776 ast_value = rb_parser_load_file(parser, fname);
777 ast = rb_ruby_ast_data_get(ast_value);
778
779 iseq = rb_iseq_new_top(ast_value, rb_fstring_lit("<top (required)>"),
780 fname, realpath_internal_cached(realpath_map, fname), NULL);
781 rb_ast_dispose(ast);
782 }
783
784 rb_vm_pop_frame(ec);
785 RB_GC_GUARD(v);
786 }
787 rb_exec_event_hook_script_compiled(ec, iseq, Qnil);
788
789 rb_iseq_eval(iseq, box);
790}
791
792static inline enum ruby_tag_type
793load_wrapping(rb_execution_context_t *ec, VALUE fname, VALUE load_wrapper)
794{
795 enum ruby_tag_type state;
796 rb_box_t *box;
797 rb_thread_t *th = rb_ec_thread_ptr(ec);
798 volatile VALUE wrapper = th->top_wrapper;
799 volatile VALUE self = th->top_self;
800#if !defined __GNUC__
801 rb_thread_t *volatile th0 = th;
802#endif
803
804 ec->errinfo = Qnil; /* ensure */
805
806 /* load in module as toplevel */
807 if (BOX_OBJ_P(load_wrapper)) {
808 box = rb_get_box_t(load_wrapper);
809 if (!box->top_self) {
810 box->top_self = rb_obj_clone(rb_vm_top_self());
811 }
812 th->top_self = box->top_self;
813 }
814 else {
815 th->top_self = rb_obj_clone(rb_vm_top_self());
816 }
817 th->top_wrapper = load_wrapper;
818 rb_extend_object(th->top_self, th->top_wrapper);
819
820 EC_PUSH_TAG(ec);
821 state = EC_EXEC_TAG();
822 if (state == TAG_NONE) {
823 load_iseq_eval(ec, fname);
824 }
825 EC_POP_TAG();
826
827#if !defined __GNUC__
828 th = th0;
829 fname = RB_GC_GUARD(fname);
830#endif
831 th->top_self = self;
832 th->top_wrapper = wrapper;
833 return state;
834}
835
836static inline void
837raise_load_if_failed(rb_execution_context_t *ec, enum ruby_tag_type state)
838{
839 if (state) {
840 rb_vm_jump_tag_but_local_jump(state);
841 }
842
843 if (!NIL_P(ec->errinfo)) {
844 rb_exc_raise(ec->errinfo);
845 }
846}
847
848static void
849rb_load_internal(VALUE fname, VALUE wrap)
850{
851 VALUE box_value;
852 rb_execution_context_t *ec = GET_EC();
853 const rb_box_t *box = rb_loading_box();
854 enum ruby_tag_type state = TAG_NONE;
855 if (RTEST(wrap)) {
856 if (!RB_TYPE_P(wrap, T_MODULE)) {
857 wrap = rb_module_new();
858 }
859 state = load_wrapping(ec, fname, wrap);
860 }
861 else if (BOX_OPTIONAL_P(box)) {
862 box_value = box->box_object;
863 state = load_wrapping(ec, fname, box_value);
864 }
865 else {
866 load_iseq_eval(ec, fname);
867 }
868 raise_load_if_failed(ec, state);
869}
870
871void
872rb_load(VALUE fname, int wrap)
873{
874 VALUE tmp = rb_find_file(FilePathValue(fname));
875 if (!tmp) load_failed(fname);
876 rb_load_internal(tmp, RBOOL(wrap));
877}
878
879void
880rb_load_protect(VALUE fname, int wrap, int *pstate)
881{
882 enum ruby_tag_type state;
883
884 EC_PUSH_TAG(GET_EC());
885 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
886 rb_load(fname, wrap);
887 }
888 EC_POP_TAG();
889
890 if (state != TAG_NONE) *pstate = state;
891}
892
893VALUE
894rb_load_entrypoint(VALUE fname, VALUE wrap)
895{
896 VALUE path, orig_fname;
897
898 orig_fname = rb_get_path_check_to_string(fname);
899 fname = rb_str_encode_ospath(orig_fname);
900 RUBY_DTRACE_HOOK(LOAD_ENTRY, RSTRING_PTR(orig_fname));
901
902 path = rb_find_file(fname);
903 if (!path) {
904 if (!rb_file_load_ok(RSTRING_PTR(fname)))
905 load_failed(orig_fname);
906 path = fname;
907 }
908 rb_load_internal(path, wrap);
909
910 RUBY_DTRACE_HOOK(LOAD_RETURN, RSTRING_PTR(orig_fname));
911
912 return Qtrue;
913}
914
915/*
916 * call-seq:
917 * load(filename, wrap=false) -> true
918 *
919 * Loads and executes the Ruby program in the file _filename_.
920 *
921 * If the filename is an absolute path (e.g. starts with '/'), the file
922 * will be loaded directly using the absolute path.
923 *
924 * If the filename is an explicit relative path (e.g. starts with './' or
925 * '../'), the file will be loaded using the relative path from the current
926 * directory.
927 *
928 * Otherwise, the file will be searched for in the library
929 * directories listed in <code>$LOAD_PATH</code> (<code>$:</code>).
930 * If the file is found in a directory, it will attempt to load the file
931 * relative to that directory. If the file is not found in any of the
932 * directories in <code>$LOAD_PATH</code>, the file will be loaded using
933 * the relative path from the current directory.
934 *
935 * If the file doesn't exist when there is an attempt to load it, a
936 * LoadError will be raised.
937 *
938 * If the optional _wrap_ parameter is +true+, the loaded script will
939 * be executed under an anonymous module. If the optional _wrap_ parameter
940 * is a module, the loaded script will be executed under the given module.
941 * In no circumstance will any local variables in the loaded file be
942 * propagated to the loading environment.
943 */
944
945static VALUE
946rb_f_load(int argc, VALUE *argv, VALUE _)
947{
948 VALUE fname, wrap;
949 rb_scan_args(argc, argv, "11", &fname, &wrap);
950 return rb_load_entrypoint(fname, wrap);
951}
952
953static char *
954load_lock(const rb_box_t *box, const char *ftptr, bool warn)
955{
956 st_data_t data;
957 st_table *loading_tbl = box->loading_table;
958
959 if (!st_lookup(loading_tbl, (st_data_t)ftptr, &data)) {
960 /* partial state */
961 ftptr = ruby_strdup(ftptr);
962 data = (st_data_t)rb_thread_shield_new();
963 st_insert(loading_tbl, (st_data_t)ftptr, data);
964 return (char *)ftptr;
965 }
966
967 if (warn && rb_thread_shield_owned((VALUE)data)) {
968 VALUE warning = rb_warning_string("loading in progress, circular require considered harmful - %s", ftptr);
969 rb_backtrace_each(rb_str_append, warning);
970 rb_warning("%"PRIsVALUE, warning);
971 }
972 switch (rb_thread_shield_wait((VALUE)data)) {
973 case Qfalse:
974 case Qnil:
975 return 0;
976 }
977 return (char *)ftptr;
978}
979
980static int
981release_thread_shield(st_data_t *key, st_data_t *value, st_data_t done, int existing)
982{
983 VALUE thread_shield = (VALUE)*value;
984 if (!existing) return ST_STOP;
985 if (done) {
986 rb_thread_shield_destroy(thread_shield);
987 /* Delete the entry even if there are waiting threads, because they
988 * won't load the file and won't delete the entry. */
989 }
990 else if (rb_thread_shield_release(thread_shield)) {
991 /* still in-use */
992 return ST_CONTINUE;
993 }
994 xfree((char *)*key);
995 return ST_DELETE;
996}
997
998static void
999load_unlock(const rb_box_t *box, const char *ftptr, int done)
1000{
1001 if (ftptr) {
1002 st_data_t key = (st_data_t)ftptr;
1003 st_table *loading_tbl = box->loading_table;
1004
1005 st_update(loading_tbl, key, release_thread_shield, done);
1006 }
1007}
1008
1009static VALUE rb_require_string_internal(VALUE fname, bool resurrect);
1010
1011/*
1012 * call-seq:
1013 * require(name) -> true or false
1014 *
1015 * Loads the given +name+, returning +true+ if successful and +false+ if the
1016 * feature is already loaded.
1017 *
1018 * If the filename neither resolves to an absolute path nor starts with
1019 * './' or '../', the file will be searched for in the library
1020 * directories listed in <code>$LOAD_PATH</code> (<code>$:</code>).
1021 * If the filename starts with './' or '../', resolution is based on Dir.pwd.
1022 *
1023 * If the filename has the extension ".rb", it is loaded as a source file; if
1024 * the extension is ".so", ".o", or the default shared library extension on
1025 * the current platform, Ruby loads the shared library as a Ruby extension.
1026 * Otherwise, Ruby tries adding ".rb", ".so", and so on to the name until
1027 * found. If the file named cannot be found, a LoadError will be raised.
1028 *
1029 * For Ruby extensions the filename given may use ".so" or ".o". For example,
1030 * on macOS the socket extension is "socket.bundle" and
1031 * <code>require 'socket.so'</code> will load the socket extension.
1032 *
1033 * The absolute path of the loaded file is added to
1034 * <code>$LOADED_FEATURES</code> (<code>$"</code>). A file will not be
1035 * loaded again if its path already appears in <code>$"</code>. For example,
1036 * <code>require 'a'; require './a'</code> will not load <code>a.rb</code>
1037 * again.
1038 *
1039 * require "my-library.rb"
1040 * require "db-driver"
1041 *
1042 * Any constants or globals within the loaded source file will be available
1043 * in the calling program's global namespace. However, local variables will
1044 * not be propagated to the loading environment.
1045 *
1046 */
1047
1048VALUE
1050{
1051 return rb_require_string(fname);
1052}
1053
1054VALUE
1055rb_require_relative_entrypoint(VALUE fname)
1056{
1057 VALUE base = rb_current_realfilepath();
1058 if (NIL_P(base)) {
1059 rb_loaderror("cannot infer basepath");
1060 }
1061 base = rb_file_dirname(base);
1062 return rb_require_string_internal(rb_file_absolute_path(fname, base), false);
1063}
1064
1065/*
1066 * call-seq:
1067 * require_relative(string) -> true or false
1068 *
1069 * Ruby tries to load the library named _string_ relative to the directory
1070 * containing the requiring file. If the file does not exist a LoadError is
1071 * raised. Returns +true+ if the file was loaded and +false+ if the file was
1072 * already loaded before.
1073 */
1074VALUE
1075rb_f_require_relative(VALUE obj, VALUE fname)
1076{
1077 return rb_require_relative_entrypoint(fname);
1078}
1079
1080static char *
1081find_ext(VALUE str, char **ptr, const char **end)
1082{
1083 long len = RSTRING_LEN(str);
1084 *ptr = RSTRING_PTR(str);
1085 *end = *ptr + len;
1086 return memrchr(*ptr, '.', len);
1087}
1088
1089static bool
1090ext_equal(const char *ext, const char *end, const char *suffix, size_t len)
1091{
1092 return (size_t)(end - ext) == len && memcmp(ext, suffix, len) == 0;
1093}
1094
1095#define EXT_RB_P(ext, end) ext_equal(ext, end, ".rb", rb_strlen_lit(".rb"))
1096#define EXT_SO_P(ext, end) (ext_equal(ext, end, ".so", rb_strlen_lit(".so")) || \
1097 ext_equal(ext, end, ".o", rb_strlen_lit(".o")))
1098#define EXT_DLEXT_P(ext, end) ext_equal(ext, end, DLEXT, rb_strlen_lit(DLEXT))
1099
1100typedef int (*feature_func)(const rb_box_t *box, const char *feature, const char *ext, int rb, int expanded, const char **fn);
1101
1102static int
1103search_required(const rb_box_t *box, VALUE fname, volatile VALUE *path, feature_func rb_feature_p)
1104{
1105 VALUE tmp;
1106 char *ext, *ftptr;
1107 int ft = 0;
1108 const char *ftend, *loading;
1109
1110 *path = 0;
1111 ext = find_ext(fname, &ftptr, &ftend);
1112 if (ext && !memchr(ext, '/', ftend - ext)) {
1113 if (EXT_RB_P(ext, ftend)) {
1114 if (rb_feature_p(box, ftptr, ext, TRUE, FALSE, &loading)) {
1115 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1116 return 'r';
1117 }
1118 if ((tmp = rb_find_file(fname)) != 0) {
1119 ext = find_ext(tmp, &ftptr, &ftend);
1120 if (!rb_feature_p(box, ftptr, ext, TRUE, TRUE, &loading) || loading)
1121 *path = tmp;
1122 return 'r';
1123 }
1124 return 0;
1125 }
1126 else if (EXT_SO_P(ext, ftend)) {
1127 if (rb_feature_p(box, ftptr, ext, FALSE, FALSE, &loading)) {
1128 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1129 return 's';
1130 }
1131 tmp = rb_str_subseq(fname, 0, ext - RSTRING_PTR(fname));
1132 rb_str_cat2(tmp, DLEXT);
1133 OBJ_FREEZE(tmp);
1134 if ((tmp = rb_find_file(tmp)) != 0) {
1135 ext = find_ext(tmp, &ftptr, &ftend);
1136 if (!rb_feature_p(box, ftptr, ext, FALSE, TRUE, &loading) || loading)
1137 *path = tmp;
1138 return 's';
1139 }
1140 }
1141 else if (EXT_DLEXT_P(ext, ftend)) {
1142 if (rb_feature_p(box, ftptr, ext, FALSE, FALSE, &loading)) {
1143 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1144 return 's';
1145 }
1146 if ((tmp = rb_find_file(fname)) != 0) {
1147 ext = find_ext(tmp, &ftptr, &ftend);
1148 if (!rb_feature_p(box, ftptr, ext, FALSE, TRUE, &loading) || loading)
1149 *path = tmp;
1150 return 's';
1151 }
1152 }
1153 }
1154 else if ((ft = rb_feature_p(box, ftptr, 0, FALSE, FALSE, &loading)) == 'r') {
1155 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1156 return 'r';
1157 }
1158 tmp = fname;
1159 const unsigned int type = rb_find_file_ext(&tmp, ft == 's' ? ruby_ext : loadable_ext);
1160
1161 // Check if it's a statically linked extension when
1162 // not already a feature and not found as a dynamic library.
1163 if (!ft && type != loadable_ext_rb) {
1164 rb_vm_t *vm = GET_VM();
1165 if (vm->static_ext_inits.num_entries) {
1166 VALUE lookup_name = tmp;
1167 // Append ".so" if not already present so for example "etc" can find "etc.so".
1168 // We always register statically linked extensions with a ".so" extension.
1169 // See encinit.c and extinit.c (generated at build-time).
1170 if (!ext) {
1171 lookup_name = rb_str_dup(lookup_name);
1172 rb_str_cat_cstr(lookup_name, ".so");
1173 }
1174 ftptr = RSTRING_PTR(lookup_name);
1175 if (st_lookup(&vm->static_ext_inits, (st_data_t)ftptr, NULL)) {
1176 *path = rb_filesystem_str_new_cstr(ftptr);
1177 RB_GC_GUARD(lookup_name);
1178 return 's';
1179 }
1180 }
1181 }
1182
1183 switch (type) {
1184 case 0:
1185 if (ft)
1186 goto feature_present;
1187 ftptr = RSTRING_PTR(tmp);
1188 return rb_feature_p(box, ftptr, 0, FALSE, TRUE, 0);
1189
1190 default:
1191 if (ft) {
1192 goto feature_present;
1193 }
1194 /* fall through */
1195 case loadable_ext_rb:
1196 ext = find_ext(tmp, &ftptr, &ftend);
1197 if (rb_feature_p(box, ftptr, ext, type == loadable_ext_rb, TRUE, &loading) && !loading)
1198 break;
1199 *path = tmp;
1200 }
1201 return type > loadable_ext_rb ? 's' : 'r';
1202
1203 feature_present:
1204 if (loading) *path = rb_filesystem_str_new_cstr(loading);
1205 return ft;
1206}
1207
1208static void
1209load_failed(VALUE fname)
1210{
1211 rb_load_fail(fname, "cannot load such file");
1212}
1213
1214static VALUE
1215load_ext(VALUE path, VALUE fname)
1216{
1217 VALUE loaded = path;
1218 const rb_box_t *box = rb_loading_box();
1219 VALUE cleanup = 0;
1220 if (BOX_USER_P(box)) {
1221 loaded = rb_box_local_extension(box->box_object, fname, path, &cleanup);
1222 }
1223 rb_scope_visibility_set(METHOD_VISI_PUBLIC);
1224 void *handle = dln_load_feature(RSTRING_PTR(loaded), RSTRING_PTR(fname));
1225 if (cleanup) {
1226 rb_box_cleanup_local_extension(cleanup);
1227 }
1228 RB_GC_GUARD(loaded);
1229 RB_GC_GUARD(fname);
1230 return (VALUE)handle;
1231}
1232
1233static VALUE
1234run_static_ext_init(VALUE vm_ptr, VALUE feature_value)
1235{
1236 rb_vm_t *vm = (rb_vm_t *)vm_ptr;
1237 const char *feature = RSTRING_PTR(feature_value);
1238 st_data_t key = (st_data_t)feature;
1239 st_data_t init_func;
1240
1241 if (st_delete(&vm->static_ext_inits, &key, &init_func)) {
1242 ((void (*)(void))init_func)();
1243 return Qtrue;
1244 }
1245 return Qfalse;
1246}
1247
1248static int
1249no_feature_p(const rb_box_t *box, const char *feature, const char *ext, int rb, int expanded, const char **fn)
1250{
1251 return 0;
1252}
1253
1254// Documented in doc/language/globals.md
1255VALUE
1256rb_resolve_feature_path(VALUE klass, VALUE fname)
1257{
1258 VALUE path;
1259 int found;
1260 VALUE sym;
1261 const rb_box_t *box = rb_loading_box();
1262
1263 fname = rb_get_path(fname);
1264 path = rb_str_encode_ospath(fname);
1265 found = search_required(box, path, &path, no_feature_p);
1266
1267 switch (found) {
1268 case 'r':
1269 sym = ID2SYM(rb_intern("rb"));
1270 break;
1271 case 's':
1272 sym = ID2SYM(rb_intern("so"));
1273 break;
1274 default:
1275 return Qnil;
1276 }
1277
1278 return rb_ary_new_from_args(2, sym, path);
1279}
1280
1281static void
1282ext_config_push(rb_thread_t *th, volatile struct rb_ext_config *prev)
1283{
1284 *prev = th->ext_config;
1285 th->ext_config = (struct rb_ext_config){0};
1286}
1287
1288static void
1289ext_config_pop(rb_thread_t *th, volatile struct rb_ext_config *prev)
1290{
1291 th->ext_config = *prev;
1292}
1293
1294void
1296{
1297 GET_THREAD()->ext_config.ractor_safe = flag;
1298}
1299
1300/*
1301 * returns
1302 * 0: if already loaded (false)
1303 * 1: successfully loaded (true)
1304 * <0: not found (LoadError)
1305 * >1: exception
1306 */
1307static int
1308require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool warn)
1309{
1310 volatile int result = -1;
1311 rb_thread_t *th = rb_ec_thread_ptr(ec);
1312 const rb_box_t *box = rb_loading_box();
1313 volatile const struct {
1314 VALUE wrapper, self, errinfo;
1316 const rb_box_t *box;
1317 } saved = {
1318 th->top_wrapper, th->top_self, ec->errinfo,
1319 ec, box,
1320 };
1321 enum ruby_tag_type state;
1322 char *volatile ftptr = 0;
1323 VALUE path;
1324 volatile VALUE saved_path;
1325 volatile VALUE realpath = 0;
1326 VALUE realpaths = box->loaded_features_realpaths;
1327 VALUE realpath_map = box->loaded_features_realpath_map;
1328 volatile bool reset_ext_config = false;
1329 volatile struct rb_ext_config prev_ext_config;
1330
1331 path = rb_str_encode_ospath(fname);
1332 RUBY_DTRACE_HOOK(REQUIRE_ENTRY, RSTRING_PTR(fname));
1333 saved_path = path;
1334
1335 EC_PUSH_TAG(ec);
1336 ec->errinfo = Qnil; /* ensure */
1337 th->top_wrapper = 0;
1338 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1339 VALUE handle;
1340 int found;
1341
1342 RUBY_DTRACE_HOOK(FIND_REQUIRE_ENTRY, RSTRING_PTR(fname));
1343 found = search_required(box, path, &saved_path, rb_feature_p);
1344 RUBY_DTRACE_HOOK(FIND_REQUIRE_RETURN, RSTRING_PTR(fname));
1345 path = saved_path;
1346
1347 if (found) {
1348 if (!path || !(ftptr = load_lock(box, RSTRING_PTR(path), warn))) {
1349 result = 0;
1350 }
1351 else if (!*ftptr) {
1352 result = TAG_RETURN;
1353 }
1354 else if (found == 's' && RTEST(rb_vm_call_cfunc_in_box(Qnil, run_static_ext_init, (VALUE)th->vm, path, path, box))) {
1355 result = TAG_RETURN;
1356 }
1357 else if (RTEST(rb_hash_aref(realpaths,
1358 realpath = realpath_internal_cached(realpath_map, path)))) {
1359 result = 0;
1360 }
1361 else {
1362 switch (found) {
1363 case 'r':
1364 load_iseq_eval(saved.ec, path);
1365 break;
1366
1367 case 's':
1368 reset_ext_config = true;
1369 ext_config_push(th, &prev_ext_config);
1370 handle = rb_vm_call_cfunc_in_box(box->top_self, load_ext, path, fname, path, box);
1371 rb_hash_aset(box->ruby_dln_libmap, path, PTR2NUM(handle));
1372 break;
1373 }
1374 result = TAG_RETURN;
1375 }
1376 }
1377 }
1378 EC_POP_TAG();
1379
1380 ec = saved.ec;
1381 box = saved.box;
1382 rb_thread_t *th2 = rb_ec_thread_ptr(ec);
1383 th2->top_self = saved.self;
1384 th2->top_wrapper = saved.wrapper;
1385 if (reset_ext_config) ext_config_pop(th2, &prev_ext_config);
1386
1387 path = saved_path;
1388 if (ftptr) load_unlock(box, RSTRING_PTR(path), !state);
1389
1390 if (state) {
1391 if (state == TAG_FATAL || state == TAG_THROW) {
1392 EC_JUMP_TAG(ec, state);
1393 }
1394 else if (exception) {
1395 /* usually state == TAG_RAISE only, except for
1396 * rb_iseq_load_iseq in load_iseq_eval case */
1397 VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
1398 if (!NIL_P(exc)) ec->errinfo = exc;
1399 return TAG_RAISE;
1400 }
1401 else if (state == TAG_RETURN) {
1402 return TAG_RAISE;
1403 }
1404 RB_GC_GUARD(fname);
1405 /* never TAG_RETURN */
1406 return state;
1407 }
1408 if (!NIL_P(ec->errinfo)) {
1409 if (!exception) return TAG_RAISE;
1410 rb_exc_raise(ec->errinfo);
1411 }
1412
1413 if (result == TAG_RETURN) {
1414 rb_provide_feature(box, path);
1415 VALUE real = realpath;
1416 if (real) {
1417 real = rb_fstring(real);
1418 rb_hash_aset(realpaths, real, Qtrue);
1419 }
1420 }
1421 ec->errinfo = saved.errinfo;
1422
1423 RUBY_DTRACE_HOOK(REQUIRE_RETURN, RSTRING_PTR(fname));
1424
1425 return result;
1426}
1427
1428int
1429rb_require_internal_silent(VALUE fname)
1430{
1431 if (!rb_ractor_main_p()) {
1432 return NUM2INT(rb_ractor_require(fname, true));
1433 }
1434
1435 rb_execution_context_t *ec = GET_EC();
1436 return require_internal(ec, fname, 1, false);
1437}
1438
1439int
1440rb_require_internal(VALUE fname)
1441{
1442 rb_execution_context_t *ec = GET_EC();
1443 return require_internal(ec, fname, 1, RTEST(ruby_verbose));
1444}
1445
1446int
1447ruby_require_internal(const char *fname, unsigned int len)
1448{
1449 struct RString fake = {RBASIC_INIT};
1450 VALUE str = rb_setup_fake_str(&fake, fname, len, 0);
1451 rb_execution_context_t *ec = GET_EC();
1452 int result = require_internal(ec, str, 0, RTEST(ruby_verbose));
1453 rb_set_errinfo(Qnil);
1454 return result == TAG_RETURN ? 1 : result ? -1 : 0;
1455}
1456
1457VALUE
1459{
1460 return rb_require_string_internal(FilePathValue(fname), false);
1461}
1462
1463static VALUE
1464rb_require_string_internal(VALUE fname, bool resurrect)
1465{
1466 rb_execution_context_t *ec = GET_EC();
1467
1468 // main ractor check
1469 if (!rb_ractor_main_p()) {
1470 if (resurrect) fname = rb_str_resurrect(fname);
1471 return rb_ractor_require(fname, false);
1472 }
1473 else {
1474 int result = require_internal(ec, fname, 1, RTEST(ruby_verbose));
1475
1476 if (result > TAG_RETURN) {
1477 EC_JUMP_TAG(ec, result);
1478 }
1479 if (result < 0) {
1480 if (resurrect) fname = rb_str_resurrect(fname);
1481 load_failed(fname);
1482 }
1483
1484 return RBOOL(result);
1485 }
1486}
1487
1488VALUE
1489rb_require(const char *fname)
1490{
1491 struct RString fake = {RBASIC_INIT};
1492 VALUE str = rb_setup_fake_str(&fake, fname, strlen(fname), 0);
1493 return rb_require_string_internal(str, true);
1494}
1495
1496static int
1497register_init_ext(st_data_t *key, st_data_t *value, st_data_t init, int existing)
1498{
1499 const char *name = (char *)*key;
1500 if (existing) {
1501 /* already registered */
1502 rb_warn("%s is already registered", name);
1503 }
1504 else {
1505 *value = (st_data_t)init;
1506 }
1507 return ST_CONTINUE;
1508}
1509
1510// Private API for statically linked extensions.
1511// Used with the ext/Setup file, the --with-setup and
1512// --with-static-linked-ext configuration option, etc.
1513void
1514ruby_init_ext(const char *name, void (*init)(void))
1515{
1516 rb_vm_t *vm = GET_VM();
1517 const rb_box_t *box = rb_loading_box();
1518
1519 if (feature_provided((rb_box_t *)box, name, 0))
1520 return;
1521
1522 st_update(&vm->static_ext_inits, (st_data_t)name, register_init_ext, (st_data_t)init);
1523}
1524
1525/*
1526 * call-seq:
1527 * mod.autoload(const, filename) -> nil
1528 *
1529 * Registers _filename_ to be loaded (using Kernel::require)
1530 * the first time that _const_ (which may be a String or
1531 * a symbol) is accessed in the namespace of _mod_.
1532 *
1533 * module A
1534 * end
1535 * A.autoload(:B, "b")
1536 * A::B.doit # autoloads "b"
1537 *
1538 * If _const_ in _mod_ is defined as autoload, the file name to be
1539 * loaded is replaced with _filename_. If _const_ is defined but not
1540 * as autoload, does nothing.
1541 *
1542 * Files that are currently being loaded must not be registered for
1543 * autoload.
1544 */
1545
1546static VALUE
1547rb_mod_autoload(VALUE mod, VALUE sym, VALUE file)
1548{
1549 ID id = rb_to_id(sym);
1550
1551 FilePathValue(file);
1552 rb_autoload_str(mod, id, file);
1553 return Qnil;
1554}
1555
1556/*
1557 * call-seq:
1558 * mod.autoload_relative(const, filename) -> nil
1559 *
1560 * Registers _filename_ to be loaded (using Kernel::require)
1561 * the first time that _const_ (which may be a String or
1562 * a symbol) is accessed in the namespace of _mod_. The _filename_
1563 * is interpreted as relative to the directory of the file where
1564 * autoload_relative is called.
1565 *
1566 * module A
1567 * end
1568 * A.autoload_relative(:B, "b.rb")
1569 *
1570 * If _const_ in _mod_ is defined as autoload, the file name to be
1571 * loaded is replaced with _filename_. If _const_ is defined but not
1572 * as autoload, does nothing.
1573 *
1574 * The relative path is converted to an absolute path, which is what
1575 * will be returned by Module#autoload? for the constant.
1576 *
1577 * Raises LoadError if called without file context (e.g., from eval).
1578 */
1579
1580static VALUE
1581rb_mod_autoload_relative(VALUE mod, VALUE sym, VALUE file)
1582{
1583 ID id = rb_to_id(sym);
1584 VALUE base, absolute_path;
1585
1586 FilePathValue(file);
1587
1588 base = rb_current_realfilepath();
1589 if (NIL_P(base)) {
1590 rb_loaderror("cannot infer basepath (autoload_relative called without file context)");
1591 }
1592 base = rb_file_dirname(base);
1593 absolute_path = rb_file_absolute_path(file, base);
1594
1595 rb_autoload_str(mod, id, absolute_path);
1596 return Qnil;
1597}
1598
1599/*
1600 * call-seq:
1601 * mod.autoload?(name, inherit=true) -> String or nil
1602 *
1603 * Returns _filename_ to be loaded if _name_ is registered as
1604 * +autoload+ in the namespace of _mod_ or one of its ancestors.
1605 *
1606 * module A
1607 * end
1608 * A.autoload(:B, "b")
1609 * A.autoload?(:B) #=> "b"
1610 *
1611 * If +inherit+ is false, the lookup only checks the autoloads in the receiver:
1612 *
1613 * class A
1614 * autoload :CONST, "const.rb"
1615 * end
1616 *
1617 * class B < A
1618 * end
1619 *
1620 * B.autoload?(:CONST) #=> "const.rb", found in A (ancestor)
1621 * B.autoload?(:CONST, false) #=> nil, not found in B itself
1622 *
1623 */
1624
1625static VALUE
1626rb_mod_autoload_p(int argc, VALUE *argv, VALUE mod)
1627{
1628 int recur = (rb_check_arity(argc, 1, 2) == 1) ? TRUE : RTEST(argv[1]);
1629 VALUE sym = argv[0];
1630
1631 ID id = rb_check_id(&sym);
1632 if (!id) {
1633 return Qnil;
1634 }
1635 return rb_autoload_at_p(mod, id, recur);
1636}
1637
1638/*
1639 * call-seq:
1640 * autoload(const, filename) -> nil
1641 *
1642 * Registers _filename_ to be loaded (using Kernel::require)
1643 * the first time that _const_ (which may be a String or
1644 * a symbol) is accessed.
1645 *
1646 * autoload(:MyModule, "/usr/local/lib/modules/my_module.rb")
1647 *
1648 * If _const_ is defined as autoload, the file name to be loaded is
1649 * replaced with _filename_. If _const_ is defined but not as
1650 * autoload, does nothing.
1651 *
1652 * Files that are currently being loaded must not be registered for
1653 * autoload.
1654 */
1655
1656static VALUE
1657rb_f_autoload(VALUE obj, VALUE sym, VALUE file)
1658{
1659 VALUE klass = rb_class_real(rb_vm_cbase());
1660 if (!klass) {
1661 rb_raise(rb_eTypeError, "Can not set autoload on singleton class");
1662 }
1663 return rb_mod_autoload(klass, sym, file);
1664}
1665
1666/*
1667 * call-seq:
1668 * autoload_relative(const, filename) -> nil
1669 *
1670 * Registers _filename_ to be loaded (using Kernel::require)
1671 * the first time that _const_ (which may be a String or
1672 * a symbol) is accessed. The _filename_ is interpreted as
1673 * relative to the directory of the file where autoload_relative
1674 * is called.
1675 *
1676 * autoload_relative(:MyModule, "my_module.rb")
1677 *
1678 * If _const_ is defined as autoload, the file name to be loaded is
1679 * replaced with _filename_. If _const_ is defined but not as
1680 * autoload, does nothing.
1681 *
1682 * The relative path is converted to an absolute path, which is what
1683 * will be returned by Kernel#autoload? for the constant.
1684 *
1685 * Raises LoadError if called without file context (e.g., from eval).
1686 */
1687
1688static VALUE
1689rb_f_autoload_relative(VALUE obj, VALUE sym, VALUE file)
1690{
1691 VALUE klass = rb_class_real(rb_vm_cbase());
1692 if (!klass) {
1693 rb_raise(rb_eTypeError, "Can not set autoload on singleton class");
1694 }
1695 return rb_mod_autoload_relative(klass, sym, file);
1696}
1697
1698/*
1699 * call-seq:
1700 * autoload?(name, inherit=true) -> String or nil
1701 *
1702 * Returns _filename_ to be loaded if _name_ is registered as
1703 * +autoload+ in the current namespace or one of its ancestors.
1704 *
1705 * autoload(:B, "b")
1706 * autoload?(:B) #=> "b"
1707 *
1708 * module C
1709 * autoload(:D, "d")
1710 * autoload?(:D) #=> "d"
1711 * autoload?(:B) #=> nil
1712 * end
1713 *
1714 * class E
1715 * autoload(:F, "f")
1716 * autoload?(:F) #=> "f"
1717 * autoload?(:B) #=> "b"
1718 * end
1719 */
1720
1721static VALUE
1722rb_f_autoload_p(int argc, VALUE *argv, VALUE obj)
1723{
1724 /* use rb_vm_cbase() as same as rb_f_autoload. */
1725 VALUE klass = rb_vm_cbase();
1726 if (NIL_P(klass)) {
1727 return Qnil;
1728 }
1729 return rb_mod_autoload_p(argc, argv, klass);
1730}
1731
1732void *
1733rb_ext_resolve_symbol(const char* fname, const char* symbol)
1734{
1735 VALUE handle;
1736 VALUE resolved;
1737 VALUE path;
1738 const char *ext;
1739 VALUE fname_str = rb_str_new_cstr(fname);
1740 const rb_box_t *box = rb_loading_box();
1741
1742 resolved = rb_resolve_feature_path((VALUE)NULL, fname_str);
1743 if (NIL_P(resolved)) {
1744 ext = strrchr(fname, '.');
1745 if (!ext || !IS_SOEXT(ext)) {
1746 rb_str_cat_cstr(fname_str, ".so");
1747 }
1748 if (rb_feature_p(box, fname, 0, FALSE, FALSE, 0)) {
1749 return dln_symbol(NULL, symbol);
1750 }
1751 return NULL;
1752 }
1753 if (RARRAY_LEN(resolved) != 2 || rb_ary_entry(resolved, 0) != ID2SYM(rb_intern("so"))) {
1754 return NULL;
1755 }
1756 path = rb_ary_entry(resolved, 1);
1757 handle = rb_hash_lookup(box->ruby_dln_libmap, path);
1758 if (NIL_P(handle)) {
1759 return NULL;
1760 }
1761 return dln_symbol(NUM2PTR(handle), symbol);
1762}
1763
1764void
1765Init_load(void)
1766{
1767 static const char var_load_path[] = "$:";
1768 ID id_load_path = rb_intern2(var_load_path, sizeof(var_load_path)-1);
1769
1770 rb_define_hooked_variable(var_load_path, 0, load_path_getter, rb_gvar_readonly_setter);
1771 rb_gvar_box_ready(var_load_path);
1772 rb_alias_variable(rb_intern_const("$-I"), id_load_path);
1773 rb_alias_variable(rb_intern_const("$LOAD_PATH"), id_load_path);
1774
1775 rb_define_virtual_variable("$\"", get_LOADED_FEATURES, 0);
1776 rb_gvar_box_ready("$\"");
1777 rb_define_virtual_variable("$LOADED_FEATURES", get_LOADED_FEATURES, 0); // TODO: rb_alias_variable ?
1778 rb_gvar_box_ready("$LOADED_FEATURES");
1779
1780 rb_define_global_function("load", rb_f_load, -1);
1782 rb_define_global_function("require_relative", rb_f_require_relative, 1);
1783 rb_define_method(rb_cModule, "autoload", rb_mod_autoload, 2);
1784 rb_define_method(rb_cModule, "autoload_relative", rb_mod_autoload_relative, 2);
1785 rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, -1);
1786 rb_define_global_function("autoload", rb_f_autoload, 2);
1787 rb_define_global_function("autoload_relative", rb_f_autoload_relative, 2);
1788 rb_define_global_function("autoload?", rb_f_autoload_p, -1);
1789}
#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:1900
VALUE rb_module_new(void)
Creates a new, anonymous module.
Definition class.c:1500
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:3187
#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:672
#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:1431
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1429
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:3949
void rb_warning(const char *fmt,...)
Issues a warning.
Definition error.c:499
VALUE rb_cModule
Module class.
Definition object.c:59
VALUE rb_class_real(VALUE klass)
Finds a "real" class.
Definition object.c:223
VALUE rb_obj_clone(VALUE obj)
Produces a shallow copy of the given object.
Definition object.c:498
Encoding relates APIs.
VALUE rb_ary_shared_with_p(VALUE lhs, VALUE rhs)
Queries if the passed two arrays share the same backend storage.
VALUE rb_ary_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:1049
void rb_ext_ractor_safe(bool flag)
Asserts that the extension library that calls this function is aware of Ractor.
Definition load.c:1295
VALUE rb_require_string(VALUE feature)
Finds and loads the given feature, if absent.
Definition load.c:1458
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:880
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:872
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:1733
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:3880
VALUE rb_str_tmp_new(long len)
Allocates a "temporary" string.
Definition string.c:1765
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:3233
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition string.c:2005
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:2023
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:1448
VALUE rb_str_equal(VALUE str1, VALUE str2)
Equality of two strings.
Definition string.c:4350
#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:3358
#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:1180
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:1287
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:13261
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: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:1489
#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