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