diff options
Diffstat (limited to 'load.c')
| -rw-r--r-- | load.c | 1015 |
1 files changed, 624 insertions, 391 deletions
@@ -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); |
