summaryrefslogtreecommitdiff
path: root/load.c
diff options
context:
space:
mode:
Diffstat (limited to 'load.c')
-rw-r--r--load.c1015
1 files changed, 624 insertions, 391 deletions
diff --git a/load.c b/load.c
index bf5d9cf648..0a79f8fa36 100644
--- a/load.c
+++ b/load.c
@@ -2,37 +2,36 @@
* load methods from eval.c
*/
-#include "internal.h"
-#include "ruby/util.h"
#include "dln.h"
#include "eval_intern.h"
+#include "internal.h"
+#include "internal/dir.h"
+#include "internal/error.h"
+#include "internal/file.h"
+#include "internal/load.h"
+#include "internal/parse.h"
+#include "internal/thread.h"
+#include "internal/variable.h"
+#include "iseq.h"
#include "probes.h"
-#include "node.h"
+#include "ruby/encoding.h"
+#include "ruby/util.h"
-VALUE ruby_dln_librefs;
+static VALUE ruby_dln_librefs;
#define IS_RBEXT(e) (strcmp((e), ".rb") == 0)
#define IS_SOEXT(e) (strcmp((e), ".so") == 0 || strcmp((e), ".o") == 0)
-#ifdef DLEXT2
-#define IS_DLEXT(e) (strcmp((e), DLEXT) == 0 || strcmp((e), DLEXT2) == 0)
-#else
#define IS_DLEXT(e) (strcmp((e), DLEXT) == 0)
-#endif
static const char *const loadable_ext[] = {
".rb", DLEXT,
-#ifdef DLEXT2
- DLEXT2,
-#endif
0
};
-VALUE
-rb_get_load_path(void)
-{
- VALUE load_path = GET_VM()->load_path;
- return load_path;
-}
+static const char *const ruby_ext[] = {
+ ".rb",
+ 0
+};
enum expand_type {
EXPAND_ALL,
@@ -47,14 +46,12 @@ enum expand_type {
string objects in $LOAD_PATH are frozen.
*/
static void
-rb_construct_expanded_load_path(int type, int *has_relative, int *has_non_cache)
+rb_construct_expanded_load_path(rb_vm_t *vm, enum expand_type type, int *has_relative, int *has_non_cache)
{
- rb_vm_t *vm = GET_VM();
VALUE load_path = vm->load_path;
VALUE expanded_load_path = vm->expanded_load_path;
VALUE ary;
long i;
- int level = rb_safe_level();
ary = rb_ary_tmp_new(RARRAY_LEN(load_path));
for (i = 0; i < RARRAY_LEN(load_path); ++i) {
@@ -64,7 +61,7 @@ rb_construct_expanded_load_path(int type, int *has_relative, int *has_non_cache)
as_str = path = RARRAY_AREF(load_path, i);
is_string = RB_TYPE_P(path, T_STRING) ? 1 : 0;
non_cache = !is_string ? 1 : 0;
- as_str = rb_get_path_check_to_string(path, level);
+ as_str = rb_get_path_check_to_string(path);
as_cstr = RSTRING_PTR(as_str);
if (!non_cache) {
@@ -85,10 +82,10 @@ rb_construct_expanded_load_path(int type, int *has_relative, int *has_non_cache)
/* Freeze only string object. We expand other objects every time. */
if (is_string)
rb_str_freeze(path);
- as_str = rb_get_path_check_convert(path, as_str, level);
- expanded_path = rb_file_expand_path_fast(as_str, Qnil);
- rb_str_freeze(expanded_path);
- rb_ary_push(ary, expanded_path);
+ as_str = rb_get_path_check_convert(as_str);
+ expanded_path = rb_check_realpath(Qnil, as_str, NULL);
+ if (NIL_P(expanded_path)) expanded_path = as_str;
+ rb_ary_push(ary, rb_fstring(expanded_path));
}
rb_obj_freeze(ary);
vm->expanded_load_path = ary;
@@ -96,26 +93,16 @@ rb_construct_expanded_load_path(int type, int *has_relative, int *has_non_cache)
}
static VALUE
-load_path_getcwd(void)
+get_expanded_load_path(rb_vm_t *vm)
{
- char *cwd = my_getcwd();
- VALUE cwd_str = rb_filesystem_str_new_cstr(cwd);
- xfree(cwd);
- return cwd_str;
-}
-
-VALUE
-rb_get_expanded_load_path(void)
-{
- rb_vm_t *vm = GET_VM();
const VALUE non_cache = Qtrue;
if (!rb_ary_shared_with_p(vm->load_path_snapshot, vm->load_path)) {
/* The load path was modified. Rebuild the expanded load path. */
int has_relative = 0, has_non_cache = 0;
- rb_construct_expanded_load_path(EXPAND_ALL, &has_relative, &has_non_cache);
+ rb_construct_expanded_load_path(vm, EXPAND_ALL, &has_relative, &has_non_cache);
if (has_relative) {
- vm->load_path_check_cache = load_path_getcwd();
+ vm->load_path_check_cache = rb_dir_getwd_ospath();
}
else if (has_non_cache) {
/* Non string object. */
@@ -128,113 +115,179 @@ rb_get_expanded_load_path(void)
else if (vm->load_path_check_cache == non_cache) {
int has_relative = 1, has_non_cache = 1;
/* Expand only non-cacheable objects. */
- rb_construct_expanded_load_path(EXPAND_NON_CACHE,
+ rb_construct_expanded_load_path(vm, EXPAND_NON_CACHE,
&has_relative, &has_non_cache);
}
else if (vm->load_path_check_cache) {
int has_relative = 1, has_non_cache = 1;
- VALUE cwd = load_path_getcwd();
+ VALUE cwd = rb_dir_getwd_ospath();
if (!rb_str_equal(vm->load_path_check_cache, cwd)) {
/* Current working directory or filesystem encoding was changed.
Expand relative load path and non-cacheable objects again. */
vm->load_path_check_cache = cwd;
- rb_construct_expanded_load_path(EXPAND_RELATIVE,
+ rb_construct_expanded_load_path(vm, EXPAND_RELATIVE,
&has_relative, &has_non_cache);
}
else {
/* Expand only tilde (User HOME) and non-cacheable objects. */
- rb_construct_expanded_load_path(EXPAND_HOME,
+ rb_construct_expanded_load_path(vm, EXPAND_HOME,
&has_relative, &has_non_cache);
}
}
return vm->expanded_load_path;
}
+VALUE
+rb_get_expanded_load_path(void)
+{
+ return get_expanded_load_path(GET_VM());
+}
+
static VALUE
-load_path_getter(ID id, rb_vm_t *vm)
+load_path_getter(ID id, VALUE * p)
{
+ rb_vm_t *vm = (void *)p;
return vm->load_path;
}
static VALUE
-get_loaded_features(void)
+get_loaded_features(rb_vm_t *vm)
+{
+ return vm->loaded_features;
+}
+
+static VALUE
+get_loaded_features_realpaths(rb_vm_t *vm)
+{
+ return vm->loaded_features_realpaths;
+}
+
+static VALUE
+get_loaded_features_realpath_map(rb_vm_t *vm)
+{
+ return vm->loaded_features_realpath_map;
+}
+
+static VALUE
+get_LOADED_FEATURES(ID _x, VALUE *_y)
{
- return GET_VM()->loaded_features;
+ return get_loaded_features(GET_VM());
}
static void
-reset_loaded_features_snapshot(void)
+reset_loaded_features_snapshot(rb_vm_t *vm)
{
- rb_vm_t *vm = GET_VM();
rb_ary_replace(vm->loaded_features_snapshot, vm->loaded_features);
}
static struct st_table *
-get_loaded_features_index_raw(void)
+get_loaded_features_index_raw(rb_vm_t *vm)
{
- return GET_VM()->loaded_features_index;
+ return vm->loaded_features_index;
}
static st_table *
-get_loading_table(void)
+get_loading_table(rb_vm_t *vm)
+{
+ return vm->loading_table;
+}
+
+static st_data_t
+feature_key(const char *str, size_t len)
+{
+ return st_hash(str, len, 0xfea7009e);
+}
+
+static bool
+is_rbext_path(VALUE feature_path)
{
- return GET_VM()->loading_table;
+ long len = RSTRING_LEN(feature_path);
+ long rbext_len = rb_strlen_lit(".rb");
+ if (len <= rbext_len) return false;
+ return IS_RBEXT(RSTRING_PTR(feature_path) + len - rbext_len);
}
static void
-features_index_add_single(VALUE short_feature, VALUE offset)
+features_index_add_single(rb_vm_t *vm, const char* str, size_t len, VALUE offset, bool rb)
{
struct st_table *features_index;
VALUE this_feature_index = Qnil;
- char *short_feature_cstr;
+ st_data_t short_feature_key;
+ st_data_t data;
Check_Type(offset, T_FIXNUM);
- Check_Type(short_feature, T_STRING);
- short_feature_cstr = StringValueCStr(short_feature);
-
- features_index = get_loaded_features_index_raw();
- st_lookup(features_index, (st_data_t)short_feature_cstr, (st_data_t *)&this_feature_index);
+ short_feature_key = feature_key(str, len);
- if (NIL_P(this_feature_index)) {
- st_insert(features_index, (st_data_t)ruby_strdup(short_feature_cstr), (st_data_t)offset);
+ features_index = get_loaded_features_index_raw(vm);
+ if (!st_lookup(features_index, short_feature_key, &data) ||
+ NIL_P(this_feature_index = (VALUE)data)) {
+ st_insert(features_index, short_feature_key, (st_data_t)offset);
}
- else if (RB_TYPE_P(this_feature_index, T_FIXNUM)) {
+ else if (FIXNUM_P(this_feature_index)) {
+ VALUE loaded_features = get_loaded_features(vm);
+ VALUE this_feature_path = RARRAY_AREF(loaded_features, FIX2LONG(this_feature_index));
VALUE feature_indexes[2];
- feature_indexes[0] = this_feature_index;
- feature_indexes[1] = offset;
+ int top = (rb && !is_rbext_path(this_feature_path)) ? 1 : 0;
+ feature_indexes[top^0] = this_feature_index;
+ feature_indexes[top^1] = offset;
this_feature_index = (VALUE)xcalloc(1, sizeof(struct RArray));
RBASIC(this_feature_index)->flags = T_ARRAY; /* fake VALUE, do not mark/sweep */
rb_ary_cat(this_feature_index, feature_indexes, numberof(feature_indexes));
- st_insert(features_index, (st_data_t)short_feature_cstr, (st_data_t)this_feature_index);
+ st_insert(features_index, short_feature_key, (st_data_t)this_feature_index);
}
else {
+ long pos = -1;
+
Check_Type(this_feature_index, T_ARRAY);
+ if (rb) {
+ VALUE loaded_features = get_loaded_features(vm);
+ for (long i = 0; i < RARRAY_LEN(this_feature_index); ++i) {
+ VALUE idx = RARRAY_AREF(this_feature_index, i);
+ VALUE this_feature_path = RARRAY_AREF(loaded_features, FIX2LONG(idx));
+ Check_Type(this_feature_path, T_STRING);
+ if (!is_rbext_path(this_feature_path)) {
+ /* as this_feature_index is a fake VALUE, `push` (which
+ * doesn't wb_unprotect like as rb_ary_splice) first,
+ * then rotate partially. */
+ pos = i;
+ break;
+ }
+ }
+ }
rb_ary_push(this_feature_index, offset);
+ if (pos >= 0) {
+ VALUE *ptr = (VALUE *)RARRAY_CONST_PTR_TRANSIENT(this_feature_index);
+ long len = RARRAY_LEN(this_feature_index);
+ MEMMOVE(ptr + pos, ptr + pos + 1, VALUE, len - pos - 1);
+ ptr[pos] = offset;
+ }
}
}
/* Add to the loaded-features index all the required entries for
`feature`, located at `offset` in $LOADED_FEATURES. We add an
index entry at each string `short_feature` for which
- feature == "#{prefix}#{short_feature}#{e}"
- where `e` is empty or matches %r{^\.[^./]*$}, and `prefix` is empty
+ feature == "#{prefix}#{short_feature}#{ext}"
+ where `ext` is empty or matches %r{^\.[^./]*$}, and `prefix` is empty
or ends in '/'. This maintains the invariant that `rb_feature_p()`
relies on for its fast lookup.
*/
static void
-features_index_add(VALUE feature, VALUE offset)
+features_index_add(rb_vm_t *vm, VALUE feature, VALUE offset)
{
- VALUE short_feature;
const char *feature_str, *feature_end, *ext, *p;
+ bool rb = false;
feature_str = StringValuePtr(feature);
feature_end = feature_str + RSTRING_LEN(feature);
for (ext = feature_end; ext > feature_str; ext--)
- if (*ext == '.' || *ext == '/')
- break;
+ if (*ext == '.' || *ext == '/')
+ break;
if (*ext != '.')
- ext = NULL;
+ ext = NULL;
+ else
+ rb = IS_RBEXT(ext);
/* Now `ext` points to the only string matching %r{^\.[^./]*$} that is
at the end of `feature`, or is NULL if there is no such string. */
@@ -246,17 +299,14 @@ features_index_add(VALUE feature, VALUE offset)
if (p < feature_str)
break;
/* Now *p == '/'. We reach this point for every '/' in `feature`. */
- short_feature = rb_str_subseq(feature, p + 1 - feature_str, feature_end - p - 1);
- features_index_add_single(short_feature, offset);
+ features_index_add_single(vm, p + 1, feature_end - p - 1, offset, false);
if (ext) {
- short_feature = rb_str_subseq(feature, p + 1 - feature_str, ext - p - 1);
- features_index_add_single(short_feature, offset);
+ features_index_add_single(vm, p + 1, ext - p - 1, offset, rb);
}
}
- features_index_add_single(feature, offset);
+ features_index_add_single(vm, feature_str, feature_end - feature_str, offset, false);
if (ext) {
- short_feature = rb_str_subseq(feature, 0, ext - feature_str);
- features_index_add_single(short_feature, offset);
+ features_index_add_single(vm, feature_str, ext - feature_str, offset, rb);
}
}
@@ -266,34 +316,52 @@ loaded_features_index_clear_i(st_data_t key, st_data_t val, st_data_t arg)
VALUE obj = (VALUE)val;
if (!SPECIAL_CONST_P(obj)) {
rb_ary_free(obj);
- xfree((void *)obj);
+ ruby_sized_xfree((void *)obj, sizeof(struct RArray));
}
- xfree((char *)key);
return ST_DELETE;
}
static st_table *
-get_loaded_features_index(void)
+get_loaded_features_index(rb_vm_t *vm)
{
VALUE features;
int i;
- rb_vm_t *vm = GET_VM();
if (!rb_ary_shared_with_p(vm->loaded_features_snapshot, vm->loaded_features)) {
/* The sharing was broken; something (other than us in rb_provide_feature())
modified loaded_features. Rebuild the index. */
st_foreach(vm->loaded_features_index, loaded_features_index_clear_i, 0);
- features = vm->loaded_features;
- for (i = 0; i < RARRAY_LEN(features); i++) {
- VALUE entry, as_str;
- as_str = entry = rb_ary_entry(features, i);
- StringValue(as_str);
- if (as_str != entry)
- rb_ary_store(features, i, as_str);
- rb_str_freeze(as_str);
- features_index_add(as_str, INT2FIX(i));
- }
- reset_loaded_features_snapshot();
+
+ VALUE realpaths = vm->loaded_features_realpaths;
+ VALUE realpath_map = vm->loaded_features_realpath_map;
+ VALUE previous_realpath_map = rb_hash_dup(realpath_map);
+ rb_hash_clear(realpaths);
+ rb_hash_clear(realpath_map);
+ features = vm->loaded_features;
+ for (i = 0; i < RARRAY_LEN(features); i++) {
+ VALUE entry, as_str;
+ as_str = entry = rb_ary_entry(features, i);
+ StringValue(as_str);
+ as_str = rb_fstring(as_str);
+ if (as_str != entry)
+ rb_ary_store(features, i, as_str);
+ features_index_add(vm, as_str, INT2FIX(i));
+ }
+ reset_loaded_features_snapshot(vm);
+
+ features = rb_ary_dup(vm->loaded_features_snapshot);
+ long j = RARRAY_LEN(features);
+ for (i = 0; i < j; i++) {
+ VALUE as_str = rb_ary_entry(features, i);
+ VALUE realpath = rb_hash_aref(previous_realpath_map, as_str);
+ if (NIL_P(realpath)) {
+ realpath = rb_check_realpath(Qnil, as_str, NULL);
+ if (NIL_P(realpath)) realpath = as_str;
+ realpath = rb_fstring(realpath);
+ }
+ rb_hash_aset(realpaths, realpath, Qtrue);
+ rb_hash_aset(realpath_map, as_str, realpath);
+ }
}
return vm->loaded_features_index;
}
@@ -374,13 +442,14 @@ loaded_feature_path_i(st_data_t v, st_data_t b, st_data_t f)
}
static int
-rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const char **fn)
+rb_feature_p(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expanded, const char **fn)
{
VALUE features, this_feature_index = Qnil, v, p, load_path = 0;
const char *f, *e;
long i, len, elen, n;
st_table *loading_tbl, *features_index;
st_data_t data;
+ st_data_t key;
int type;
if (fn) *fn = 0;
@@ -394,10 +463,10 @@ rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const c
elen = 0;
type = 0;
}
- features = get_loaded_features();
- features_index = get_loaded_features_index();
+ features = get_loaded_features(vm);
+ features_index = get_loaded_features_index(vm);
- st_lookup(features_index, (st_data_t)feature, (st_data_t *)&this_feature_index);
+ key = feature_key(feature, strlen(feature));
/* We search `features` for an entry such that either
"#{features[i]}" == "#{load_path[j]}/#{feature}#{e}"
for some j, or
@@ -424,7 +493,7 @@ rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const c
or ends in '/'. This includes both match forms above, as well
as any distractors, so we may ignore all other entries in `features`.
*/
- if (!NIL_P(this_feature_index)) {
+ if (st_lookup(features_index, key, &data) && !NIL_P(this_feature_index = (VALUE)data)) {
for (i = 0; ; i++) {
VALUE entry;
long index;
@@ -443,7 +512,7 @@ rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const c
if ((n = RSTRING_LEN(v)) < len) continue;
if (strncmp(f, feature, len) != 0) {
if (expanded) continue;
- if (!load_path) load_path = rb_get_expanded_load_path();
+ if (!load_path) load_path = get_expanded_load_path(vm);
if (!(p = loaded_feature_path(f, n, feature, len, type, load_path)))
continue;
expanded = 1;
@@ -463,59 +532,59 @@ rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const c
}
}
- loading_tbl = get_loading_table();
- if (loading_tbl) {
- f = 0;
- if (!expanded) {
- struct loaded_feature_searching fs;
- fs.name = feature;
- fs.len = len;
- fs.type = type;
- fs.load_path = load_path ? load_path : rb_get_expanded_load_path();
- fs.result = 0;
- st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs);
- if ((f = fs.result) != 0) {
- if (fn) *fn = f;
- goto loading;
- }
+ loading_tbl = get_loading_table(vm);
+ f = 0;
+ if (!expanded) {
+ struct loaded_feature_searching fs;
+ fs.name = feature;
+ fs.len = len;
+ fs.type = type;
+ fs.load_path = load_path ? load_path : get_expanded_load_path(vm);
+ fs.result = 0;
+ st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs);
+ if ((f = fs.result) != 0) {
+ if (fn) *fn = f;
+ goto loading;
}
- if (st_get_key(loading_tbl, (st_data_t)feature, &data)) {
- if (fn) *fn = (const char*)data;
- loading:
- if (!ext) return 'u';
- return !IS_RBEXT(ext) ? 's' : 'r';
- }
- else {
- VALUE bufstr;
- char *buf;
- static const char so_ext[][4] = {
- ".so", ".o",
- };
-
- if (ext && *ext) return 0;
- bufstr = rb_str_tmp_new(len + DLEXT_MAXLEN);
- buf = RSTRING_PTR(bufstr);
- MEMCPY(buf, feature, char, len);
- for (i = 0; (e = loadable_ext[i]) != 0; i++) {
- strlcpy(buf + len, e, DLEXT_MAXLEN + 1);
- if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
- rb_str_resize(bufstr, 0);
- if (fn) *fn = (const char*)data;
- return i ? 's' : 'r';
- }
+ }
+ if (st_get_key(loading_tbl, (st_data_t)feature, &data)) {
+ if (fn) *fn = (const char*)data;
+ goto loading;
+ }
+ else {
+ VALUE bufstr;
+ char *buf;
+ static const char so_ext[][4] = {
+ ".so", ".o",
+ };
+
+ if (ext && *ext) return 0;
+ bufstr = rb_str_tmp_new(len + DLEXT_MAXLEN);
+ buf = RSTRING_PTR(bufstr);
+ MEMCPY(buf, feature, char, len);
+ for (i = 0; (e = loadable_ext[i]) != 0; i++) {
+ strlcpy(buf + len, e, DLEXT_MAXLEN + 1);
+ if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
+ rb_str_resize(bufstr, 0);
+ if (fn) *fn = (const char*)data;
+ return i ? 's' : 'r';
}
- for (i = 0; i < numberof(so_ext); i++) {
- strlcpy(buf + len, so_ext[i], DLEXT_MAXLEN + 1);
- if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
- rb_str_resize(bufstr, 0);
- if (fn) *fn = (const char*)data;
- return 's';
- }
+ }
+ for (i = 0; i < numberof(so_ext); i++) {
+ strlcpy(buf + len, so_ext[i], DLEXT_MAXLEN + 1);
+ if (st_get_key(loading_tbl, (st_data_t)buf, &data)) {
+ rb_str_resize(bufstr, 0);
+ if (fn) *fn = (const char*)data;
+ return 's';
}
- rb_str_resize(bufstr, 0);
}
+ rb_str_resize(bufstr, 0);
}
return 0;
+
+ loading:
+ if (!ext) return 'u';
+ return !IS_RBEXT(ext) ? 's' : 'r';
}
int
@@ -524,11 +593,11 @@ rb_provided(const char *feature)
return rb_feature_provided(feature, 0);
}
-int
-rb_feature_provided(const char *feature, const char **loading)
+static int
+feature_provided(rb_vm_t *vm, const char *feature, const char **loading)
{
const char *ext = strrchr(feature, '.');
- volatile VALUE fullpath = 0;
+ VALUE fullpath = 0;
if (*feature == '.' &&
(feature[1] == '/' || strncmp(feature+1, "./", 2) == 0)) {
@@ -537,225 +606,234 @@ rb_feature_provided(const char *feature, const char **loading)
}
if (ext && !strchr(ext, '/')) {
if (IS_RBEXT(ext)) {
- if (rb_feature_p(feature, ext, TRUE, FALSE, loading)) return TRUE;
+ if (rb_feature_p(vm, feature, ext, TRUE, FALSE, loading)) return TRUE;
return FALSE;
}
else if (IS_SOEXT(ext) || IS_DLEXT(ext)) {
- if (rb_feature_p(feature, ext, FALSE, FALSE, loading)) return TRUE;
+ if (rb_feature_p(vm, feature, ext, FALSE, FALSE, loading)) return TRUE;
return FALSE;
}
}
- if (rb_feature_p(feature, 0, TRUE, FALSE, loading))
+ if (rb_feature_p(vm, feature, 0, TRUE, FALSE, loading))
return TRUE;
+ RB_GC_GUARD(fullpath);
return FALSE;
}
+int
+rb_feature_provided(const char *feature, const char **loading)
+{
+ return feature_provided(GET_VM(), feature, loading);
+}
+
static void
-rb_provide_feature(VALUE feature)
+rb_provide_feature(rb_vm_t *vm, VALUE feature)
{
VALUE features;
- features = get_loaded_features();
+ features = get_loaded_features(vm);
if (OBJ_FROZEN(features)) {
rb_raise(rb_eRuntimeError,
"$LOADED_FEATURES is frozen; cannot append feature");
}
rb_str_freeze(feature);
- rb_ary_push(features, feature);
- features_index_add(feature, INT2FIX(RARRAY_LEN(features)-1));
- reset_loaded_features_snapshot();
+ get_loaded_features_index(vm);
+ // If loaded_features and loaded_features_snapshot share the same backing
+ // array, pushing into it would cause the whole array to be copied.
+ // To avoid this we first clear loaded_features_snapshot.
+ rb_ary_clear(vm->loaded_features_snapshot);
+ rb_ary_push(features, rb_fstring(feature));
+ features_index_add(vm, feature, INT2FIX(RARRAY_LEN(features)-1));
+ reset_loaded_features_snapshot(vm);
}
void
rb_provide(const char *feature)
{
- rb_provide_feature(rb_usascii_str_new2(feature));
+ rb_provide_feature(GET_VM(), rb_fstring_cstr(feature));
}
NORETURN(static void load_failed(VALUE));
-static int
-rb_load_internal0(rb_thread_t *th, VALUE fname, int wrap)
+static inline void
+load_iseq_eval(rb_execution_context_t *ec, VALUE fname)
{
- int state;
+ const rb_iseq_t *iseq = rb_iseq_load_iseq(fname);
+
+ if (!iseq) {
+ rb_ast_t *ast;
+ VALUE parser = rb_parser_new();
+ rb_parser_set_context(parser, NULL, FALSE);
+ ast = (rb_ast_t *)rb_parser_load_file(parser, fname);
+ iseq = rb_iseq_new_top(&ast->body, rb_fstring_lit("<top (required)>"),
+ fname, rb_realpath_internal(Qnil, fname, 1), NULL);
+ rb_ast_dispose(ast);
+ }
+ rb_exec_event_hook_script_compiled(ec, iseq, Qnil);
+ rb_iseq_eval(iseq);
+}
+
+static inline enum ruby_tag_type
+load_wrapping(rb_execution_context_t *ec, VALUE fname, VALUE load_wrapper)
+{
+ enum ruby_tag_type state;
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
volatile VALUE wrapper = th->top_wrapper;
volatile VALUE self = th->top_self;
- volatile int mild_compile_error;
#if !defined __GNUC__
rb_thread_t *volatile th0 = th;
#endif
- th->errinfo = Qnil; /* ensure */
+ ec->errinfo = Qnil; /* ensure */
- if (!wrap) {
- th->top_wrapper = 0;
- }
- else {
- /* load in anonymous module as toplevel */
- th->top_self = rb_obj_clone(rb_vm_top_self());
- th->top_wrapper = rb_module_new();
- rb_extend_object(th->top_self, th->top_wrapper);
- }
+ /* load in module as toplevel */
+ th->top_self = rb_obj_clone(rb_vm_top_self());
+ th->top_wrapper = load_wrapper;
+ rb_extend_object(th->top_self, th->top_wrapper);
- mild_compile_error = th->mild_compile_error;
- PUSH_TAG();
- state = EXEC_TAG();
- if (state == 0) {
- NODE *node;
- VALUE iseq;
-
- th->mild_compile_error++;
- node = (NODE *)rb_load_file_str(fname);
- iseq = rb_iseq_new_top(node, rb_str_new2("<top (required)>"), fname, rb_realpath_internal(Qnil, fname, 1), Qfalse);
- th->mild_compile_error--;
- rb_iseq_eval(iseq);
+ EC_PUSH_TAG(ec);
+ state = EC_EXEC_TAG();
+ if (state == TAG_NONE) {
+ load_iseq_eval(ec, fname);
}
- POP_TAG();
+ EC_POP_TAG();
#if !defined __GNUC__
th = th0;
fname = RB_GC_GUARD(fname);
#endif
- th->mild_compile_error = mild_compile_error;
th->top_self = self;
th->top_wrapper = wrapper;
+ return state;
+}
+static inline void
+raise_load_if_failed(rb_execution_context_t *ec, enum ruby_tag_type state)
+{
if (state) {
- VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
- if (NIL_P(exc)) return state;
- th->errinfo = exc;
- return TAG_RAISE;
+ rb_vm_jump_tag_but_local_jump(state);
}
- if (!NIL_P(th->errinfo)) {
- /* exception during load */
- return TAG_RAISE;
+ if (!NIL_P(ec->errinfo)) {
+ rb_exc_raise(ec->errinfo);
}
- return state;
}
static void
-rb_load_internal(VALUE fname, int wrap)
+rb_load_internal(VALUE fname, VALUE wrap)
{
- rb_thread_t *curr_th = GET_THREAD();
- int state = rb_load_internal0(curr_th, fname, wrap);
- if (state) {
- if (state == TAG_RAISE) rb_exc_raise(curr_th->errinfo);
- JUMP_TAG(state);
+ rb_execution_context_t *ec = GET_EC();
+ enum ruby_tag_type state = TAG_NONE;
+ if (RTEST(wrap)) {
+ if (!RB_TYPE_P(wrap, T_MODULE)) {
+ wrap = rb_module_new();
+ }
+ state = load_wrapping(ec, fname, wrap);
}
-}
-
-static VALUE
-file_to_load(VALUE fname)
-{
- VALUE tmp = rb_find_file(FilePathValue(fname));
- if (!tmp) load_failed(fname);
- return tmp;
+ else {
+ load_iseq_eval(ec, fname);
+ }
+ raise_load_if_failed(ec, state);
}
void
rb_load(VALUE fname, int wrap)
{
- rb_load_internal(file_to_load(fname), wrap);
+ VALUE tmp = rb_find_file(FilePathValue(fname));
+ if (!tmp) load_failed(fname);
+ rb_load_internal(tmp, RBOOL(wrap));
}
void
-rb_load_protect(VALUE fname, int wrap, int *state)
+rb_load_protect(VALUE fname, int wrap, int *pstate)
{
- int status;
- volatile VALUE path = 0;
+ enum ruby_tag_type state;
- PUSH_TAG();
- if ((status = EXEC_TAG()) == 0) {
- path = file_to_load(fname);
+ EC_PUSH_TAG(GET_EC());
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ rb_load(fname, wrap);
}
- POP_TAG();
- if (!status) status = rb_load_internal0(GET_THREAD(), path, wrap);
- if (state)
- *state = status;
+ EC_POP_TAG();
+
+ if (state != TAG_NONE) *pstate = state;
}
/*
* call-seq:
* load(filename, wrap=false) -> true
*
- * Loads and executes the Ruby
- * program in the file _filename_. If the filename does not
- * resolve to an absolute path, the file is searched for in the library
- * directories listed in <code>$:</code>. If the optional _wrap_
- * parameter is +true+, the loaded script will be executed
- * under an anonymous module, protecting the calling program's global
- * namespace. In no circumstance will any local variables in the loaded
- * file be propagated to the loading environment.
+ * Loads and executes the Ruby program in the file _filename_.
+ *
+ * If the filename is an absolute path (e.g. starts with '/'), the file
+ * will be loaded directly using the absolute path.
+ *
+ * If the filename is an explicit relative path (e.g. starts with './' or
+ * '../'), the file will be loaded using the relative path from the current
+ * directory.
+ *
+ * Otherwise, the file will be searched for in the library
+ * directories listed in <code>$LOAD_PATH</code> (<code>$:</code>).
+ * If the file is found in a directory, it will attempt to load the file
+ * relative to that directory. If the file is not found in any of the
+ * directories in <code>$LOAD_PATH</code>, the file will be loaded using
+ * the relative path from the current directory.
+ *
+ * If the file doesn't exist when there is an attempt to load it, a
+ * LoadError will be raised.
+ *
+ * If the optional _wrap_ parameter is +true+, the loaded script will
+ * be executed under an anonymous module, protecting the calling
+ * program's global namespace. If the optional _wrap_ parameter is a
+ * module, the loaded script will be executed under the given module.
+ * In no circumstance will any local variables in the loaded file be
+ * propagated to the loading environment.
*/
static VALUE
-rb_f_load(int argc, VALUE *argv)
+rb_f_load(int argc, VALUE *argv, VALUE _)
{
VALUE fname, wrap, path, orig_fname;
rb_scan_args(argc, argv, "11", &fname, &wrap);
- if (RUBY_DTRACE_LOAD_ENTRY_ENABLED()) {
- RUBY_DTRACE_LOAD_ENTRY(StringValuePtr(fname),
- rb_sourcefile(),
- rb_sourceline());
- }
-
- orig_fname = FilePathValue(fname);
+ orig_fname = rb_get_path_check_to_string(fname);
fname = rb_str_encode_ospath(orig_fname);
+ RUBY_DTRACE_HOOK(LOAD_ENTRY, RSTRING_PTR(orig_fname));
+
path = rb_find_file(fname);
if (!path) {
if (!rb_file_load_ok(RSTRING_PTR(fname)))
load_failed(orig_fname);
path = fname;
}
- rb_load_internal(path, RTEST(wrap));
+ rb_load_internal(path, wrap);
- if (RUBY_DTRACE_LOAD_RETURN_ENABLED()) {
- RUBY_DTRACE_LOAD_RETURN(StringValuePtr(fname),
- rb_sourcefile(),
- rb_sourceline());
- }
+ RUBY_DTRACE_HOOK(LOAD_RETURN, RSTRING_PTR(orig_fname));
return Qtrue;
}
static char *
-load_lock(const char *ftptr)
+load_lock(rb_vm_t *vm, const char *ftptr, bool warn)
{
st_data_t data;
- st_table *loading_tbl = get_loading_table();
+ st_table *loading_tbl = get_loading_table(vm);
- if (!loading_tbl || !st_lookup(loading_tbl, (st_data_t)ftptr, &data)) {
- /* loading ruby library should be serialized. */
- if (!loading_tbl) {
- GET_VM()->loading_table = loading_tbl = st_init_strtable();
- }
+ if (!st_lookup(loading_tbl, (st_data_t)ftptr, &data)) {
/* partial state */
ftptr = ruby_strdup(ftptr);
data = (st_data_t)rb_thread_shield_new();
st_insert(loading_tbl, (st_data_t)ftptr, data);
return (char *)ftptr;
}
- else if (RB_TYPE_P((VALUE)data, T_NODE) && nd_type((VALUE)data) == NODE_MEMO) {
- NODE *memo = RNODE(data);
- void (*init)(void) = (void (*)(void))memo->nd_cfnc;
- data = (st_data_t)rb_thread_shield_new();
- st_insert(loading_tbl, (st_data_t)ftptr, data);
- (*init)();
- return (char *)"";
- }
- if (RTEST(ruby_verbose)) {
- rb_warning("loading in progress, circular require considered harmful - %s", ftptr);
- rb_backtrace_print_to(rb_stderr);
+ if (warn && rb_thread_shield_owned((VALUE)data)) {
+ VALUE warning = rb_warning_string("loading in progress, circular require considered harmful - %s", ftptr);
+ rb_backtrace_each(rb_str_append, warning);
+ rb_warning("%"PRIsVALUE, warning);
}
switch (rb_thread_shield_wait((VALUE)data)) {
case Qfalse:
- data = (st_data_t)ftptr;
- st_insert(loading_tbl, data, (st_data_t)rb_thread_shield_new());
- return 0;
case Qnil:
return 0;
}
@@ -767,7 +845,12 @@ release_thread_shield(st_data_t *key, st_data_t *value, st_data_t done, int exis
{
VALUE thread_shield = (VALUE)*value;
if (!existing) return ST_STOP;
- if (done ? rb_thread_shield_destroy(thread_shield) : rb_thread_shield_release(thread_shield)) {
+ if (done) {
+ rb_thread_shield_destroy(thread_shield);
+ /* Delete the entry even if there are waiting threads, because they
+ * won't load the file and won't delete the entry. */
+ }
+ else if (rb_thread_shield_release(thread_shield)) {
/* still in-use */
return ST_CONTINUE;
}
@@ -776,16 +859,17 @@ release_thread_shield(st_data_t *key, st_data_t *value, st_data_t done, int exis
}
static void
-load_unlock(const char *ftptr, int done)
+load_unlock(rb_vm_t *vm, const char *ftptr, int done)
{
if (ftptr) {
st_data_t key = (st_data_t)ftptr;
- st_table *loading_tbl = get_loading_table();
+ st_table *loading_tbl = get_loading_table(vm);
st_update(loading_tbl, key, release_thread_shield, done);
}
}
+static VALUE rb_require_string_internal(VALUE fname);
/*
* call-seq:
@@ -794,8 +878,10 @@ load_unlock(const char *ftptr, int done)
* Loads the given +name+, returning +true+ if successful and +false+ if the
* feature is already loaded.
*
- * If the filename does not resolve to an absolute path, it will be searched
- * for in the directories listed in <code>$LOAD_PATH</code> (<code>$:</code>).
+ * If the filename neither resolves to an absolute path nor starts with
+ * './' or '../', the file will be searched for in the library
+ * directories listed in <code>$LOAD_PATH</code> (<code>$:</code>).
+ * If the filename starts with './' or '../', resolution is based on Dir.pwd.
*
* If the filename has the extension ".rb", it is loaded as a source file; if
* the extension is ".so", ".o", or ".dll", or the default shared library
@@ -826,7 +912,7 @@ load_unlock(const char *ftptr, int done)
VALUE
rb_f_require(VALUE obj, VALUE fname)
{
- return rb_require_safe(fname, rb_safe_level());
+ return rb_require_string(fname);
}
/*
@@ -845,11 +931,13 @@ rb_f_require_relative(VALUE obj, VALUE fname)
rb_loaderror("cannot infer basepath");
}
base = rb_file_dirname(base);
- return rb_require_safe(rb_file_absolute_path(fname, base), rb_safe_level());
+ return rb_require_string_internal(rb_file_absolute_path(fname, base));
}
+typedef int (*feature_func)(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expanded, const char **fn);
+
static int
-search_required(VALUE fname, volatile VALUE *path, int safe_level)
+search_required(rb_vm_t *vm, VALUE fname, volatile VALUE *path, feature_func rb_feature_p)
{
VALUE tmp;
char *ext, *ftptr;
@@ -860,82 +948,92 @@ search_required(VALUE fname, volatile VALUE *path, int safe_level)
ext = strrchr(ftptr = RSTRING_PTR(fname), '.');
if (ext && !strchr(ext, '/')) {
if (IS_RBEXT(ext)) {
- if (rb_feature_p(ftptr, ext, TRUE, FALSE, &loading)) {
+ if (rb_feature_p(vm, ftptr, ext, TRUE, FALSE, &loading)) {
if (loading) *path = rb_filesystem_str_new_cstr(loading);
return 'r';
}
- if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) {
+ if ((tmp = rb_find_file(fname)) != 0) {
ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
- if (!rb_feature_p(ftptr, ext, TRUE, TRUE, &loading) || loading)
+ if (!rb_feature_p(vm, ftptr, ext, TRUE, TRUE, &loading) || loading)
*path = tmp;
return 'r';
}
return 0;
}
else if (IS_SOEXT(ext)) {
- if (rb_feature_p(ftptr, ext, FALSE, FALSE, &loading)) {
+ if (rb_feature_p(vm, ftptr, ext, FALSE, FALSE, &loading)) {
if (loading) *path = rb_filesystem_str_new_cstr(loading);
return 's';
}
tmp = rb_str_subseq(fname, 0, ext - RSTRING_PTR(fname));
-#ifdef DLEXT2
- OBJ_FREEZE(tmp);
- if (rb_find_file_ext_safe(&tmp, loadable_ext + 1, safe_level)) {
- ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
- if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
- *path = tmp;
- return 's';
- }
-#else
rb_str_cat2(tmp, DLEXT);
OBJ_FREEZE(tmp);
- if ((tmp = rb_find_file_safe(tmp, safe_level)) != 0) {
+ if ((tmp = rb_find_file(tmp)) != 0) {
ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
- if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
+ if (!rb_feature_p(vm, ftptr, ext, FALSE, TRUE, &loading) || loading)
*path = tmp;
return 's';
}
-#endif
}
else if (IS_DLEXT(ext)) {
- if (rb_feature_p(ftptr, ext, FALSE, FALSE, &loading)) {
+ if (rb_feature_p(vm, ftptr, ext, FALSE, FALSE, &loading)) {
if (loading) *path = rb_filesystem_str_new_cstr(loading);
return 's';
}
- if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) {
+ if ((tmp = rb_find_file(fname)) != 0) {
ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
- if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
+ if (!rb_feature_p(vm, ftptr, ext, FALSE, TRUE, &loading) || loading)
*path = tmp;
return 's';
}
}
}
- else if ((ft = rb_feature_p(ftptr, 0, FALSE, FALSE, &loading)) == 'r') {
+ else if ((ft = rb_feature_p(vm, ftptr, 0, FALSE, FALSE, &loading)) == 'r') {
if (loading) *path = rb_filesystem_str_new_cstr(loading);
return 'r';
}
tmp = fname;
- type = rb_find_file_ext_safe(&tmp, loadable_ext, safe_level);
+ type = rb_find_file_ext(&tmp, ft == 's' ? ruby_ext : loadable_ext);
+#if EXTSTATIC
+ if (!ft && type != 1) { // not already a feature and not found as a dynamic library
+ VALUE lookup_name = tmp;
+ // Append ".so" if not already present so for example "etc" can find "etc.so".
+ // We always register statically linked extensions with a ".so" extension.
+ // See encinit.c and extinit.c (generated at build-time).
+ if (!ext) {
+ lookup_name = rb_str_dup(lookup_name);
+ rb_str_cat_cstr(lookup_name, ".so");
+ }
+ ftptr = RSTRING_PTR(lookup_name);
+ if (st_lookup(vm->static_ext_inits, (st_data_t)ftptr, NULL)) {
+ *path = rb_filesystem_str_new_cstr(ftptr);
+ return 's';
+ }
+ }
+#endif
switch (type) {
case 0:
if (ft)
goto statically_linked;
ftptr = RSTRING_PTR(tmp);
- return rb_feature_p(ftptr, 0, FALSE, TRUE, 0);
+ return rb_feature_p(vm, ftptr, 0, FALSE, TRUE, 0);
default:
if (ft) {
- statically_linked:
- if (loading) *path = rb_filesystem_str_new_cstr(loading);
- return ft;
+ goto statically_linked;
}
+ /* fall through */
case 1:
ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
- if (rb_feature_p(ftptr, ext, !--type, TRUE, &loading) && !loading)
+ if (rb_feature_p(vm, ftptr, ext, !--type, TRUE, &loading) && !loading)
break;
*path = tmp;
}
return type ? 's' : 'r';
+
+ statically_linked:
+ if (loading) *path = rb_filesystem_str_new_cstr(loading);
+ return ft;
}
static void
@@ -947,10 +1045,75 @@ load_failed(VALUE fname)
static VALUE
load_ext(VALUE path)
{
- SCOPE_SET(NOEX_PUBLIC);
+ rb_scope_visibility_set(METHOD_VISI_PUBLIC);
return (VALUE)dln_load(RSTRING_PTR(path));
}
+#if EXTSTATIC
+static bool
+run_static_ext_init(rb_vm_t *vm, const char *feature)
+{
+ st_data_t key = (st_data_t)feature;
+ st_data_t init_func;
+ if (st_delete(vm->static_ext_inits, &key, &init_func)) {
+ ((void (*)(void))init_func)();
+ return true;
+ }
+ return false;
+}
+#endif
+
+static int
+no_feature_p(rb_vm_t *vm, const char *feature, const char *ext, int rb, int expanded, const char **fn)
+{
+ return 0;
+}
+
+// Documented in doc/globals.rdoc
+VALUE
+rb_resolve_feature_path(VALUE klass, VALUE fname)
+{
+ VALUE path;
+ int found;
+ VALUE sym;
+
+ fname = rb_get_path(fname);
+ path = rb_str_encode_ospath(fname);
+ found = search_required(GET_VM(), path, &path, no_feature_p);
+
+ switch (found) {
+ case 'r':
+ sym = ID2SYM(rb_intern("rb"));
+ break;
+ case 's':
+ sym = ID2SYM(rb_intern("so"));
+ break;
+ default:
+ return Qnil;
+ }
+
+ return rb_ary_new_from_args(2, sym, path);
+}
+
+static void
+ext_config_push(rb_thread_t *th, struct rb_ext_config *prev)
+{
+ *prev = th->ext_config;
+ th->ext_config = (struct rb_ext_config){0};
+}
+
+static void
+ext_config_pop(rb_thread_t *th, struct rb_ext_config *prev)
+{
+ th->ext_config = *prev;
+}
+
+void
+rb_ext_ractor_safe(bool flag)
+{
+ GET_THREAD()->ext_config.ractor_safe = flag;
+}
+
/*
* returns
* 0: if already loaded (false)
@@ -958,128 +1121,181 @@ load_ext(VALUE path)
* <0: not found (LoadError)
* >1: exception
*/
-int
-rb_require_internal(VALUE fname, int safe)
+static int
+require_internal(rb_execution_context_t *ec, VALUE fname, int exception, bool warn)
{
volatile int result = -1;
- rb_thread_t *th = GET_THREAD();
- volatile VALUE errinfo = th->errinfo;
- int state;
- struct {
- int safe;
- } volatile saved;
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
+ volatile const struct {
+ VALUE wrapper, self, errinfo;
+ } saved = {
+ th->top_wrapper, th->top_self, ec->errinfo,
+ };
+ enum ruby_tag_type state;
char *volatile ftptr = 0;
-
- if (RUBY_DTRACE_REQUIRE_ENTRY_ENABLED()) {
- RUBY_DTRACE_REQUIRE_ENTRY(StringValuePtr(fname),
- rb_sourcefile(),
- rb_sourceline());
- }
-
- PUSH_TAG();
- saved.safe = rb_safe_level();
- if ((state = EXEC_TAG()) == 0) {
- VALUE path;
+ VALUE path;
+ volatile VALUE saved_path;
+ volatile VALUE realpath = 0;
+ VALUE realpaths = get_loaded_features_realpaths(th->vm);
+ VALUE realpath_map = get_loaded_features_realpath_map(th->vm);
+ volatile bool reset_ext_config = false;
+ struct rb_ext_config prev_ext_config;
+
+ path = rb_str_encode_ospath(fname);
+ RUBY_DTRACE_HOOK(REQUIRE_ENTRY, RSTRING_PTR(fname));
+ saved_path = path;
+
+ EC_PUSH_TAG(ec);
+ ec->errinfo = Qnil; /* ensure */
+ th->top_wrapper = 0;
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
long handle;
int found;
- rb_set_safe_level_force(safe);
- FilePathValue(fname);
- rb_set_safe_level_force(0);
-
- if (RUBY_DTRACE_FIND_REQUIRE_ENTRY_ENABLED()) {
- RUBY_DTRACE_FIND_REQUIRE_ENTRY(StringValuePtr(fname),
- rb_sourcefile(),
- rb_sourceline());
- }
-
- path = rb_str_encode_ospath(fname);
- found = search_required(path, &path, safe);
+ RUBY_DTRACE_HOOK(FIND_REQUIRE_ENTRY, RSTRING_PTR(fname));
+ found = search_required(th->vm, path, &saved_path, rb_feature_p);
+ RUBY_DTRACE_HOOK(FIND_REQUIRE_RETURN, RSTRING_PTR(fname));
+ path = saved_path;
- if (RUBY_DTRACE_FIND_REQUIRE_RETURN_ENABLED()) {
- RUBY_DTRACE_FIND_REQUIRE_RETURN(StringValuePtr(fname),
- rb_sourcefile(),
- rb_sourceline());
- }
if (found) {
- if (!path || !(ftptr = load_lock(RSTRING_PTR(path)))) {
+ if (!path || !(ftptr = load_lock(th->vm, RSTRING_PTR(path), warn))) {
result = 0;
}
else if (!*ftptr) {
- rb_provide_feature(path);
- result = 1;
+ result = TAG_RETURN;
}
+#if EXTSTATIC
+ else if (found == 's' && run_static_ext_init(th->vm, RSTRING_PTR(path))) {
+ result = TAG_RETURN;
+ }
+#endif
+ else if (RTEST(rb_hash_aref(realpaths,
+ realpath = rb_realpath_internal(Qnil, path, 1)))) {
+ result = 0;
+ }
else {
switch (found) {
case 'r':
- rb_load_internal(path, 0);
+ load_iseq_eval(ec, path);
break;
case 's':
+ reset_ext_config = true;
+ ext_config_push(th, &prev_ext_config);
handle = (long)rb_vm_call_cfunc(rb_vm_top_self(), load_ext,
- path, 0, path);
+ path, VM_BLOCK_HANDLER_NONE, path);
rb_ary_push(ruby_dln_librefs, LONG2NUM(handle));
break;
}
- rb_provide_feature(path);
- result = 1;
+ result = TAG_RETURN;
}
}
}
- POP_TAG();
- load_unlock(ftptr, !state);
+ EC_POP_TAG();
+
+ rb_thread_t *th2 = rb_ec_thread_ptr(ec);
+ th2->top_self = saved.self;
+ th2->top_wrapper = saved.wrapper;
+ if (reset_ext_config) ext_config_pop(th2, &prev_ext_config);
+
+ path = saved_path;
+ if (ftptr) load_unlock(th2->vm, RSTRING_PTR(path), !state);
- rb_set_safe_level_force(saved.safe);
if (state) {
+ if (state == TAG_FATAL || state == TAG_THROW) {
+ EC_JUMP_TAG(ec, state);
+ }
+ else if (exception) {
+ /* usually state == TAG_RAISE only, except for
+ * rb_iseq_load_iseq in load_iseq_eval case */
+ VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
+ if (!NIL_P(exc)) ec->errinfo = exc;
+ return TAG_RAISE;
+ }
+ else if (state == TAG_RETURN) {
+ return TAG_RAISE;
+ }
+ RB_GC_GUARD(fname);
/* never TAG_RETURN */
return state;
}
+ if (!NIL_P(ec->errinfo)) {
+ if (!exception) return TAG_RAISE;
+ rb_exc_raise(ec->errinfo);
+ }
- th->errinfo = errinfo;
-
- if (RUBY_DTRACE_REQUIRE_RETURN_ENABLED()) {
- RUBY_DTRACE_REQUIRE_RETURN(StringValuePtr(fname),
- rb_sourcefile(),
- rb_sourceline());
+ if (result == TAG_RETURN) {
+ rb_provide_feature(th2->vm, path);
+ VALUE real = realpath;
+ if (real) {
+ real = rb_fstring(real);
+ rb_hash_aset(realpaths, real, Qtrue);
+ rb_hash_aset(realpath_map, path, real);
+ }
}
+ ec->errinfo = saved.errinfo;
+
+ RUBY_DTRACE_HOOK(REQUIRE_RETURN, RSTRING_PTR(fname));
return result;
}
int
+rb_require_internal_silent(VALUE fname)
+{
+ rb_execution_context_t *ec = GET_EC();
+ return require_internal(ec, fname, 1, false);
+}
+
+int
+rb_require_internal(VALUE fname)
+{
+ rb_execution_context_t *ec = GET_EC();
+ return require_internal(ec, fname, 1, RTEST(ruby_verbose));
+}
+
+int
ruby_require_internal(const char *fname, unsigned int len)
{
struct RString fake;
VALUE str = rb_setup_fake_str(&fake, fname, len, 0);
- int result = rb_require_internal(str, 0);
- if (result > 1) result = -1;
+ rb_execution_context_t *ec = GET_EC();
+ int result = require_internal(ec, str, 0, RTEST(ruby_verbose));
rb_set_errinfo(Qnil);
- return result;
+ return result == TAG_RETURN ? 1 : result ? -1 : 0;
}
VALUE
-rb_require_safe(VALUE fname, int safe)
+rb_require_string(VALUE fname)
+{
+ return rb_require_string_internal(FilePathValue(fname));
+}
+
+static VALUE
+rb_require_string_internal(VALUE fname)
{
- int result = rb_require_internal(fname, safe);
+ rb_execution_context_t *ec = GET_EC();
+ int result = require_internal(ec, fname, 1, RTEST(ruby_verbose));
- if (result > 1) {
- JUMP_TAG(result);
+ if (result > TAG_RETURN) {
+ EC_JUMP_TAG(ec, result);
}
if (result < 0) {
load_failed(fname);
}
- return result ? Qtrue : Qfalse;
+ return RBOOL(result);
}
VALUE
rb_require(const char *fname)
{
- VALUE fn = rb_str_new2(fname);
- OBJ_FREEZE(fn);
- return rb_require_safe(fn, rb_safe_level());
+ struct RString fake;
+ VALUE str = rb_setup_fake_str(&fake, fname, strlen(fname), 0);
+ return rb_require_string_internal(str);
}
+#if EXTSTATIC
static int
register_init_ext(st_data_t *key, st_data_t *value, st_data_t init, int existing)
{
@@ -1089,31 +1305,29 @@ register_init_ext(st_data_t *key, st_data_t *value, st_data_t init, int existing
rb_warn("%s is already registered", name);
}
else {
- *value = (st_data_t)NEW_MEMO(init, 0, 0);
- *key = (st_data_t)ruby_strdup(name);
+ *value = (st_data_t)init;
}
return ST_CONTINUE;
}
-RUBY_FUNC_EXPORTED void
+void
ruby_init_ext(const char *name, void (*init)(void))
{
- st_table *loading_tbl = get_loading_table();
+ rb_vm_t *vm = GET_VM();
+ st_table *inits_table = vm->static_ext_inits;
- if (rb_provided(name))
+ if (feature_provided(vm, name, 0))
return;
- if (!loading_tbl) {
- GET_VM()->loading_table = loading_tbl = st_init_strtable();
- }
- st_update(loading_tbl, (st_data_t)name, register_init_ext, (st_data_t)init);
+ st_update(inits_table, (st_data_t)name, register_init_ext, (st_data_t)init);
}
+#endif
/*
* call-seq:
* mod.autoload(module, filename) -> nil
*
- * Registers _filename_ to be loaded (using <code>Kernel::require</code>)
- * the first time that _module_ (which may be a <code>String</code> or
+ * Registers _filename_ to be loaded (using Kernel::require)
+ * the first time that _module_ (which may be a String or
* a symbol) is accessed in the namespace of _mod_.
*
* module A
@@ -1128,39 +1342,55 @@ rb_mod_autoload(VALUE mod, VALUE sym, VALUE file)
ID id = rb_to_id(sym);
FilePathValue(file);
- rb_autoload(mod, id, RSTRING_PTR(file));
+ rb_autoload_str(mod, id, file);
return Qnil;
}
/*
* call-seq:
- * mod.autoload?(name) -> String or nil
+ * mod.autoload?(name, inherit=true) -> String or nil
*
* Returns _filename_ to be loaded if _name_ is registered as
- * +autoload+ in the namespace of _mod_.
+ * +autoload+ in the namespace of _mod_ or one of its ancestors.
*
* module A
* end
* A.autoload(:B, "b")
* A.autoload?(:B) #=> "b"
+ *
+ * If +inherit+ is false, the lookup only checks the autoloads in the receiver:
+ *
+ * class A
+ * autoload :CONST, "const.rb"
+ * end
+ *
+ * class B < A
+ * end
+ *
+ * B.autoload?(:CONST) #=> "const.rb", found in A (ancestor)
+ * B.autoload?(:CONST, false) #=> nil, not found in B itself
+ *
*/
static VALUE
-rb_mod_autoload_p(VALUE mod, VALUE sym)
+rb_mod_autoload_p(int argc, VALUE *argv, VALUE mod)
{
+ int recur = (rb_check_arity(argc, 1, 2) == 1) ? TRUE : RTEST(argv[1]);
+ VALUE sym = argv[0];
+
ID id = rb_check_id(&sym);
if (!id) {
return Qnil;
}
- return rb_autoload_p(mod, id);
+ return rb_autoload_at_p(mod, id, recur);
}
/*
* call-seq:
* autoload(module, filename) -> nil
*
- * Registers _filename_ to be loaded (using <code>Kernel::require</code>)
- * the first time that _module_ (which may be a <code>String</code> or
+ * Registers _filename_ to be loaded (using Kernel::require)
+ * the first time that _module_ (which may be a String or
* a symbol) is accessed.
*
* autoload(:MyModule, "/usr/local/lib/modules/my_module.rb")
@@ -1170,7 +1400,7 @@ static VALUE
rb_f_autoload(VALUE obj, VALUE sym, VALUE file)
{
VALUE klass = rb_class_real(rb_vm_cbase());
- if (NIL_P(klass)) {
+ if (!klass) {
rb_raise(rb_eTypeError, "Can not set autoload on singleton class");
}
return rb_mod_autoload(klass, sym, file);
@@ -1178,7 +1408,7 @@ rb_f_autoload(VALUE obj, VALUE sym, VALUE file)
/*
* call-seq:
- * autoload?(name) -> String or nil
+ * autoload?(name, inherit=true) -> String or nil
*
* Returns _filename_ to be loaded if _name_ is registered as
* +autoload+.
@@ -1188,46 +1418,49 @@ rb_f_autoload(VALUE obj, VALUE sym, VALUE file)
*/
static VALUE
-rb_f_autoload_p(VALUE obj, VALUE sym)
+rb_f_autoload_p(int argc, VALUE *argv, VALUE obj)
{
/* use rb_vm_cbase() as same as rb_f_autoload. */
VALUE klass = rb_vm_cbase();
if (NIL_P(klass)) {
return Qnil;
}
- return rb_mod_autoload_p(klass, sym);
+ return rb_mod_autoload_p(argc, argv, klass);
}
void
Init_load(void)
{
-#undef rb_intern
-#define rb_intern(str) rb_intern2((str), strlen(str))
rb_vm_t *vm = GET_VM();
static const char var_load_path[] = "$:";
ID id_load_path = rb_intern2(var_load_path, sizeof(var_load_path)-1);
rb_define_hooked_variable(var_load_path, (VALUE*)vm, load_path_getter, rb_gvar_readonly_setter);
- rb_alias_variable(rb_intern("$-I"), id_load_path);
- rb_alias_variable(rb_intern("$LOAD_PATH"), id_load_path);
+ rb_alias_variable(rb_intern_const("$-I"), id_load_path);
+ rb_alias_variable(rb_intern_const("$LOAD_PATH"), id_load_path);
vm->load_path = rb_ary_new();
vm->expanded_load_path = rb_ary_tmp_new(0);
vm->load_path_snapshot = rb_ary_tmp_new(0);
vm->load_path_check_cache = 0;
+ rb_define_singleton_method(vm->load_path, "resolve_feature_path", rb_resolve_feature_path, 1);
- rb_define_virtual_variable("$\"", get_loaded_features, 0);
- rb_define_virtual_variable("$LOADED_FEATURES", get_loaded_features, 0);
+ rb_define_virtual_variable("$\"", get_LOADED_FEATURES, 0);
+ rb_define_virtual_variable("$LOADED_FEATURES", get_LOADED_FEATURES, 0);
vm->loaded_features = rb_ary_new();
vm->loaded_features_snapshot = rb_ary_tmp_new(0);
- vm->loaded_features_index = st_init_strtable();
+ vm->loaded_features_index = st_init_numtable();
+ vm->loaded_features_realpaths = rb_hash_new();
+ rb_obj_hide(vm->loaded_features_realpaths);
+ vm->loaded_features_realpath_map = rb_hash_new();
+ rb_obj_hide(vm->loaded_features_realpath_map);
rb_define_global_function("load", rb_f_load, -1);
rb_define_global_function("require", rb_f_require, 1);
rb_define_global_function("require_relative", rb_f_require_relative, 1);
rb_define_method(rb_cModule, "autoload", rb_mod_autoload, 2);
- rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, 1);
+ rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, -1);
rb_define_global_function("autoload", rb_f_autoload, 2);
- rb_define_global_function("autoload?", rb_f_autoload_p, 1);
+ rb_define_global_function("autoload?", rb_f_autoload_p, -1);
ruby_dln_librefs = rb_ary_tmp_new(0);
rb_gc_register_mark_object(ruby_dln_librefs);