Project

General

Profile

Bug #3924 » require_performance.diff

xaviershay (Xavier Shay), 05/23/2011 07:56 PM

View differences:

.gitignore
# /win32/
/win32/*.ico
ext/win32ole/.document
array.c
return (VALUE)ary;
}
static VALUE
VALUE // TODO: Removed the static declaration off this, figure out if that is OK
ary_new(VALUE klass, long capa)
{
VALUE ary;
enumerator.c
id_rewind = rb_intern("rewind");
id_each = rb_intern("each");
sym_each = ID2SYM(id_each);
rb_provide("enumerator.so"); /* for backward compatibility */
}
file.c
* Return <code>true</code> if the named file exists.
*/
static VALUE
VALUE // TODO: Mark this as static again
rb_file_exist_p(VALUE obj, VALUE fname)
{
struct stat st;
lib/enumerator.rb
# This class is now defined entirely in enumerator.c and is always available.
# This file needs to remain here for backwards compatibility, so that `require
# "enumerator"` will not raise an exception.
load.c
#endif
VALUE rb_f_require(VALUE, VALUE);
VALUE rb_f_require_relative(VALUE, VALUE);
static VALUE rb_f_load(int, VALUE *);
VALUE rb_require_safe(VALUE, int);
static int rb_file_has_been_required(VALUE);
static int rb_file_is_being_required(VALUE);
static int rb_file_is_ruby(VALUE);
static st_table * get_loaded_features_hash(void);
static void rb_load_internal(VALUE, int);
static char * load_lock(const char *);
static void load_unlock(const char *, int);
static void load_failed(VALUE fname);
void rb_provide(const char *feature);
static void rb_provide_feature(VALUE);
/*
* TODO: These functions are all conceptually related, and should be extracted
* into a separate file.
*/
static VALUE rb_locate_file(VALUE);
static VALUE rb_locate_file_absolute(VALUE);
static VALUE rb_locate_file_relative(VALUE);
static VALUE rb_locate_file_in_load_path(VALUE);
static VALUE rb_locate_file_with_extensions(VALUE);
static int rb_path_is_absolute(VALUE);
static int rb_path_is_relative(VALUE);
VALUE rb_get_expanded_load_path();
/*
* TODO: These functions are all conceptually related, and should be extracted
* into a separate file.
*/
static VALUE rb_cLoadedFeaturesProxy;
static void rb_rehash_loaded_features();
static VALUE rb_loaded_features_hook(int, VALUE*, VALUE);
static void define_loaded_features_proxy();
VALUE ary_new(VALUE, long); // array.c
static const char *const loadable_ext[] = {
".rb", DLEXT,
".rb",
DLEXT,
#ifdef DLEXT2
DLEXT2,
#endif
......
return load_path;
}
static st_table *
get_loaded_features_hash(void)
{
st_table* loaded_features_hash;
loaded_features_hash = GET_VM()->loaded_features_hash;
if (!loaded_features_hash) {
GET_VM()->loaded_features_hash = loaded_features_hash = st_init_strcasetable();
}
return loaded_features_hash;
}
static st_table *
get_filename_expansion_hash(void)
{
st_table* filename_expansion_hash;
filename_expansion_hash = GET_VM()->filename_expansion_hash;
if (!filename_expansion_hash) {
GET_VM()->filename_expansion_hash = filename_expansion_hash = st_init_strcasetable();
}
return filename_expansion_hash;
}
VALUE
rb_get_expanded_load_path(void)
{
......
return GET_VM()->loading_table;
}
static VALUE
loaded_feature_path(const char *name, long vlen, const char *feature, long len,
int type, VALUE load_path)
{
long i;
for (i = 0; i < RARRAY_LEN(load_path); ++i) {
VALUE p = RARRAY_PTR(load_path)[i];
const char *s = StringValuePtr(p);
long n = RSTRING_LEN(p);
if (vlen < n + len + 1) continue;
if (n && (strncmp(name, s, n) || name[n] != '/')) continue;
if (strncmp(name + n + 1, feature, len)) continue;
if (name[n+len+1] && name[n+len+1] != '.') continue;
switch (type) {
case 's':
if (IS_DLEXT(&name[n+len+1])) return p;
break;
case 'r':
if (IS_RBEXT(&name[n+len+1])) return p;
break;
default:
return p;
}
}
return 0;
}
struct loaded_feature_searching {
const char *name;
long len;
int type;
VALUE load_path;
const char *result;
};
static int
loaded_feature_path_i(st_data_t v, st_data_t b, st_data_t f)
{
const char *s = (const char *)v;
struct loaded_feature_searching *fp = (struct loaded_feature_searching *)f;
VALUE p = loaded_feature_path(s, strlen(s), fp->name, fp->len,
fp->type, fp->load_path);
if (!p) return ST_CONTINUE;
fp->result = s;
return ST_STOP;
}
static int
rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const char **fn)
{
VALUE v, features, p, load_path = 0;
const char *f, *e;
long i, len, elen, n;
st_table *loading_tbl;
st_data_t data;
int type;
if (fn) *fn = 0;
if (ext) {
elen = strlen(ext);
len = strlen(feature) - elen;
type = rb ? 'r' : 's';
}
else {
len = strlen(feature);
elen = 0;
type = 0;
}
features = get_loaded_features();
for (i = 0; i < RARRAY_LEN(features); ++i) {
v = RARRAY_PTR(features)[i];
f = StringValuePtr(v);
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 (!(p = loaded_feature_path(f, n, feature, len, type, load_path)))
continue;
expanded = 1;
f += RSTRING_LEN(p) + 1;
}
if (!*(e = f + len)) {
if (ext) continue;
return 'u';
}
if (*e != '.') continue;
if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) {
return 's';
}
if ((rb || !ext) && (IS_RBEXT(e))) {
return 'r';
}
}
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_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;
}
}
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;
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';
}
}
rb_str_resize(bufstr, 0);
}
}
return 0;
}
int
rb_provided(const char *feature)
{
return rb_feature_provided(feature, 0);
}
int
rb_feature_provided(const char *feature, const char **loading)
/* Mark the given feature as loaded, after it has been evaluated. */
static void
rb_provide_feature(VALUE feature)
{
const char *ext = strrchr(feature, '.');
volatile VALUE fullpath = 0;
int frozen = 0;
st_table* loaded_features_hash;
if (*feature == '.' &&
(feature[1] == '/' || strncmp(feature+1, "./", 2) == 0)) {
fullpath = rb_file_expand_path(rb_str_new2(feature), Qnil);
feature = RSTRING_PTR(fullpath);
}
if (ext && !strchr(ext, '/')) {
if (IS_RBEXT(ext)) {
if (rb_feature_p(feature, ext, TRUE, FALSE, loading)) return TRUE;
return FALSE;
if (OBJ_FROZEN(get_loaded_features())) {
rb_raise(rb_eRuntimeError,
"$LOADED_FEATURES is frozen; cannot append feature");
}
else if (IS_SOEXT(ext) || IS_DLEXT(ext)) {
if (rb_feature_p(feature, ext, FALSE, FALSE, loading)) return TRUE;
return FALSE;
}
}
if (rb_feature_p(feature, 0, TRUE, FALSE, loading))
return TRUE;
return FALSE;
}
static void
rb_provide_feature(VALUE feature)
{
if (OBJ_FROZEN(get_loaded_features())) {
rb_raise(rb_eRuntimeError,
"$LOADED_FEATURES is frozen; cannot append feature");
}
rb_ary_push(get_loaded_features(), feature);
loaded_features_hash = get_loaded_features_hash();
st_insert(
loaded_features_hash,
(st_data_t)ruby_strdup(RSTRING_PTR(feature)),
(st_data_t)rb_barrier_new());
rb_ary_push(get_loaded_features(), feature);
}
void
......
/*
* call-seq:
* require(string) -> true or false
*
* Ruby tries to load the library named _string_, returning
* +true+ if successful. If the filename does not resolve to
* an absolute path, it will be searched for in the directories listed
* in <code>$:</code>. If the file has the extension ``.rb'', it is
* loaded as a source file; if the extension is ``.so'', ``.o'', or
* ``.dll'', or whatever the default shared library extension is on
* the current platform, Ruby loads the shared library as a Ruby
* extension. Otherwise, Ruby tries adding ``.rb'', ``.so'', and so on
* to the name. The name of the loaded feature is added to the array in
* <code>$"</code>. A feature will not be loaded if its name already
* appears in <code>$"</code>. The file name is converted to an absolute
* path, so ``<code>require 'a'; require './a'</code>'' will not load
* <code>a.rb</code> twice.
*
* require "my-library.rb"
* require "db-driver"
*/
VALUE
rb_f_require(VALUE obj, VALUE fname)
{
return rb_require_safe(fname, rb_safe_level());
}
/*
* call-seq:
* require_relative(string) -> true or false
*
......
return rb_require_safe(rb_file_absolute_path(fname, base), rb_safe_level());
}
static int
search_required(VALUE fname, volatile VALUE *path, int safe_level)
{
VALUE tmp;
char *ext, *ftptr;
int type, ft = 0;
const char *loading;
*path = 0;
ext = strrchr(ftptr = RSTRING_PTR(fname), '.');
if (ext && !strchr(ext, '/')) {
if (IS_RBEXT(ext)) {
if (rb_feature_p(ftptr, ext, TRUE, FALSE, &loading)) {
if (loading) *path = rb_str_new2(loading);
return 'r';
}
if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) {
ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
if (!rb_feature_p(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 (loading) *path = rb_str_new2(loading);
return 's';
}
tmp = rb_str_new(RSTRING_PTR(fname), 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) {
ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
if (!rb_feature_p(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 (loading) *path = rb_str_new2(loading);
return 's';
}
if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) {
ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
*path = tmp;
return 's';
}
}
}
else if ((ft = rb_feature_p(ftptr, 0, FALSE, FALSE, &loading)) == 'r') {
if (loading) *path = rb_str_new2(loading);
return 'r';
}
tmp = fname;
type = rb_find_file_ext_safe(&tmp, loadable_ext, safe_level);
switch (type) {
case 0:
if (ft)
break;
ftptr = RSTRING_PTR(tmp);
return rb_feature_p(ftptr, 0, FALSE, TRUE, 0);
default:
if (ft)
break;
case 1:
ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
if (rb_feature_p(ftptr, ext, !--type, TRUE, &loading) && !loading)
break;
*path = tmp;
}
return type ? 's' : 'r';
}
static void
load_failed(VALUE fname)
......
}
VALUE
rb_require_safe(VALUE fname, int safe)
{
volatile VALUE result = Qnil;
rb_thread_t *th = GET_THREAD();
volatile VALUE errinfo = th->errinfo;
int state;
struct {
int safe;
} volatile saved;
char *volatile ftptr = 0;
PUSH_TAG();
saved.safe = rb_safe_level();
if ((state = EXEC_TAG()) == 0) {
VALUE path;
long handle;
int found;
rb_set_safe_level_force(safe);
FilePathValue(fname);
rb_set_safe_level_force(0);
found = search_required(fname, &path, safe);
if (found) {
if (!path || !(ftptr = load_lock(RSTRING_PTR(path)))) {
result = Qfalse;
}
else {
switch (found) {
case 'r':
rb_load_internal(path, 0);
break;
case 's':
handle = (long)rb_vm_call_cfunc(rb_vm_top_self(), load_ext,
path, 0, path);
rb_ary_push(ruby_dln_librefs, LONG2NUM(handle));
break;
}
rb_provide_feature(path);
result = Qtrue;
}
}
}
POP_TAG();
load_unlock(ftptr, !state);
rb_set_safe_level_force(saved.safe);
if (state) {
JUMP_TAG(state);
}
if (NIL_P(result)) {
load_failed(fname);
}
th->errinfo = errinfo;
return result;
}
VALUE
rb_require(const char *fname)
{
VALUE fn = rb_str_new2(fname);
......
return rb_mod_autoload_p(klass, sym);
}
VALUE
rb_file_exist_p(VALUE obj, VALUE path);
static int
rb_feature_exists(VALUE expanded_path)
{
return rb_funcall(rb_cFile, rb_intern("file?"), 1, expanded_path) == Qtrue;
}
const char *available_extensions[] = {
".rb",
DLEXT,
#ifdef DLEXT2
DLEXT2,
#endif
""
};
#ifdef DLEXT2
VALUE available_ext_rb_str[4];
#else
VALUE available_ext_rb_str[3];
#endif
const char *alternate_dl_extensions[] = {
DLEXT,
#ifdef DLEXT2
DLEXT2
#endif
};
#define CHAR_ARRAY_LEN(array) sizeof(array) / sizeof(char*)
#define VALUE_ARRAY_LEN(array) sizeof(array) / sizeof(VALUE)
static VALUE
rb_locate_file_with_extensions(VALUE base_file_name) {
unsigned int j;
VALUE file_name_with_extension;
VALUE extension;
VALUE directory, basename;
extension = rb_funcall(rb_cFile, rb_intern("extname"), 1, base_file_name);
if (RSTRING_LEN(extension) == 0) {
for (j = 0; j < VALUE_ARRAY_LEN(available_ext_rb_str); ++j) {
file_name_with_extension = rb_str_plus(
base_file_name,
available_ext_rb_str[j]);
if (rb_feature_exists(file_name_with_extension)) {
return file_name_with_extension;
}
}
} else {
if (rb_feature_exists(base_file_name)) {
return base_file_name;
} else {
for (j = 0; j < CHAR_ARRAY_LEN(alternate_dl_extensions); ++j) {
// Try loading the native DLEXT version of this platform.
// This allows 'pathname.so' to require 'pathname.bundle' on OSX
directory = rb_file_dirname(base_file_name);
basename = rb_funcall(rb_cFile, rb_intern("basename"), 2,
base_file_name, extension);
basename = rb_str_cat2(basename, alternate_dl_extensions[j]);
file_name_with_extension = rb_funcall(rb_cFile, rb_intern("join"), 2,
directory, basename);
if (rb_feature_exists(file_name_with_extension)) {
return file_name_with_extension;
}
// Also try loading 'dot.dot.bundle' for 'dot.dot'
file_name_with_extension = rb_str_plus(
base_file_name,
rb_str_new2(alternate_dl_extensions[j]));
if (rb_feature_exists(file_name_with_extension)) {
return file_name_with_extension;
}
}
}
}
return Qnil;
}
static VALUE
rb_locate_file_absolute(VALUE fname)
{
return rb_locate_file_with_extensions(fname);
}
static VALUE
rb_locate_file_relative(VALUE fname)
{
return rb_locate_file_with_extensions(rb_file_expand_path(fname, Qnil));
}
static VALUE
rb_locate_file_in_load_path(VALUE path)
{
long i, j;
VALUE load_path = rb_get_expanded_load_path();
VALUE expanded_file_name = Qnil;
VALUE base_file_name = Qnil;
VALUE sep = rb_str_new2("/");
for (i = 0; i < RARRAY_LEN(load_path); ++i) {
VALUE directory = RARRAY_PTR(load_path)[i];
base_file_name = rb_str_plus(directory, sep);
base_file_name = rb_str_concat(base_file_name, path);
expanded_file_name = rb_locate_file_with_extensions(base_file_name);
if (expanded_file_name != Qnil) {
return expanded_file_name;
}
}
return Qnil;
}
static int
rb_path_is_relative(VALUE path)
{
const char * path_ptr = RSTRING_PTR(path);
const char * current_directory = "./";
const char * parent_directory = "../";
return (
strncmp(current_directory, path_ptr, 2) == 0 ||
strncmp(parent_directory, path_ptr, 3) == 0
);
}
static int
rb_file_is_ruby(VALUE path)
{
const char * ext;
ext = ruby_find_extname(RSTRING_PTR(path), 0);
return ext && IS_RBEXT(ext);
}
static int
rb_path_is_absolute(VALUE path)
{
// Delegate to file.c
return rb_is_absolute_path(RSTRING_PTR(path));
}
static int
rb_file_has_been_required(VALUE expanded_path)
{
st_data_t data;
st_data_t path_key = (st_data_t)RSTRING_PTR(expanded_path);
st_table *loaded_features_hash = get_loaded_features_hash();
return st_lookup(loaded_features_hash, path_key, &data);
}
static int
rb_file_is_being_required(VALUE full_path) {
const char *ftptr = RSTRING_PTR(full_path);
st_data_t data;
st_table *loading_tbl = get_loading_table();
return (loading_tbl && st_lookup(loading_tbl, (st_data_t)ftptr, &data));
}
static VALUE
rb_get_cached_expansion(VALUE filename)
{
st_data_t data;
st_data_t path_key = (st_data_t)RSTRING_PTR(filename);
st_table *filename_expansion_hash = get_filename_expansion_hash();
if (st_lookup(filename_expansion_hash, path_key, &data)) {
return (VALUE)data;
} else {
return Qnil;
};
}
static void
rb_set_cached_expansion(VALUE filename, VALUE expanded)
{
st_data_t data = (st_data_t)expanded;
st_data_t path_key = (st_data_t)RSTRING_PTR(filename);
st_table *filename_expansion_hash = get_filename_expansion_hash();
st_insert(filename_expansion_hash, path_key, data);
}
static VALUE
rb_locate_file(VALUE filename)
{
VALUE full_path = Qnil;
full_path = rb_get_cached_expansion(filename);
if (full_path != Qnil)
return full_path;
if (rb_path_is_relative(filename)) {
full_path = rb_locate_file_relative(filename);
} else if (rb_path_is_absolute(filename)) {
full_path = rb_locate_file_absolute(filename);
} else {
full_path = rb_locate_file_in_load_path(filename);
}
if (full_path != Qnil)
rb_set_cached_expansion(filename, full_path);
return full_path;
}
/*
* returns the path loaded, or nil if the file was already loaded. Raises
* LoadError if a file cannot be found.
*/
VALUE
rb_require_safe(VALUE fname, int safe)
{
VALUE path = Qnil;
volatile VALUE result = Qnil;
rb_thread_t *th = GET_THREAD();
volatile VALUE errinfo = th->errinfo;
int state;
struct {
int safe;
} volatile saved;
char *volatile ftptr = 0;
PUSH_TAG();
saved.safe = rb_safe_level();
if ((state = EXEC_TAG()) == 0) {
long handle;
int found;
rb_set_safe_level_force(safe);
FilePathValue(fname);
rb_set_safe_level_force(0);
path = rb_locate_file(fname);
if (safe >= 1 && OBJ_TAINTED(path)) {
rb_raise(rb_eSecurityError, "Loading from unsafe file %s", RSTRING_PTR(path));
}
result = Qfalse;
if (path == Qnil) {
load_failed(fname);
} else {
if (ftptr = load_lock(RSTRING_PTR(path))) { // Allows circular requires to work
if (!rb_file_has_been_required(path)) {
if (rb_file_is_ruby(path)) {
rb_load_internal(path, 0);
} else {
handle = (long)rb_vm_call_cfunc(rb_vm_top_self(), load_ext,
path, 0, path);
rb_ary_push(ruby_dln_librefs, LONG2NUM(handle));
}
rb_provide_feature(path);
result = Qtrue;
}
}
}
}
POP_TAG();
load_unlock(ftptr, !state);
rb_set_safe_level_force(saved.safe);
if (state) {
JUMP_TAG(state);
}
if (NIL_P(result)) {
load_failed(fname);
}
th->errinfo = errinfo;
if (result == Qtrue) {
return path;
} else {
return Qnil;
}
}
/*
* call-seq:
* require(string) -> true or false
*
* Ruby tries to load the library named _string_, returning
* +true+ if successful. If the filename does not resolve to
* an absolute path, it will be searched for in the directories listed
* in <code>$:</code>. If the file has the extension ``.rb'', it is
* loaded as a source file; if the extension is ``.so'', ``.o'', or
* ``.dll'', or whatever the default shared library extension is on
* the current platform, Ruby loads the shared library as a Ruby
* extension. Otherwise, Ruby tries adding ``.rb'', ``.so'', and so on
* to the name. The name of the loaded feature is added to the array in
* <code>$"</code>. A feature will not be loaded if its name already
* appears in <code>$"</code>. The file name is converted to an absolute
* path, so ``<code>require 'a'; require './a'</code>'' will not load
* <code>a.rb</code> twice.
*
* require "my-library.rb"
* require "db-driver"
*/
VALUE
rb_f_require(VALUE obj, VALUE fname)
{
return rb_require_safe(fname, rb_safe_level()) == Qnil ? Qfalse : Qtrue;
}
static void
rb_rehash_loaded_features()
{
int i;
VALUE features;
VALUE feature;
st_table* loaded_features_hash = get_loaded_features_hash();
st_clear(loaded_features_hash);
features = get_loaded_features();
for (i = 0; i < RARRAY_LEN(features); ++i) {
feature = RARRAY_PTR(features)[i];
st_insert(
loaded_features_hash,
(st_data_t)ruby_strdup(RSTRING_PTR(feature)),
(st_data_t)rb_barrier_new());
}
}
static void
rb_clear_cached_expansions()
{
st_table* filename_expansion_hash = get_filename_expansion_hash();
st_clear(filename_expansion_hash);
}
static VALUE
rb_loaded_features_hook(int argc, VALUE *argv, VALUE self)
{
VALUE ret;
ret = rb_call_super(argc, argv);
rb_rehash_loaded_features();
rb_clear_cached_expansions();
return ret;
}
/*
* $LOADED_FEATURES is exposed publically as an array, but under the covers
* we also store this data in a hash for fast lookups. So that we can rebuild
* the hash whenever $LOADED_FEATURES is changed, we wrap the Array class
* in a proxy that intercepts all data-modifying methods and rebuilds the
* hash.
*
* Note that the list of intercepted methods is currently non-comprehensive
* --- it only covers modifications made by the ruby and rubyspec test suites.
*/
static void
define_loaded_features_proxy()
{
const char* methods_to_hook[] = {"push", "clear", "replace", "delete"};
unsigned int i;
rb_cLoadedFeaturesProxy = rb_define_class("LoadedFeaturesProxy", rb_cArray);
for (i = 0; i < CHAR_ARRAY_LEN(methods_to_hook); ++i) {
rb_define_method(
rb_cLoadedFeaturesProxy,
methods_to_hook[i],
rb_loaded_features_hook,
-1);
}
}
/* Should return true if the file has or is being loaded, but should
* not actually load the file.
*/
int
rb_feature_provided_2(VALUE fname)
{
VALUE full_path = rb_locate_file(fname);
if (
full_path != Qnil &&
(
rb_file_has_been_required(full_path) ||
rb_file_is_being_required(full_path)
)
) {
return TRUE;
} else {
return FALSE;
}
}
/*
* Deprecated, use rb_feature_provided_2
*/
int
rb_feature_provided(const char *feature, const char **loading)
{
VALUE fname = rb_str_new2(feature);
rb_feature_provided_2(fname);
}
void
Init_load()
{
#undef rb_intern
#define rb_intern(str) rb_intern2((str), strlen(str))
unsigned int j;
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_virtual_variable("$\"", get_loaded_features, 0);
rb_define_virtual_variable("$LOADED_FEATURES", get_loaded_features, 0);
vm->loaded_features = rb_ary_new();
define_loaded_features_proxy();
vm->loaded_features = ary_new(rb_cLoadedFeaturesProxy, RARRAY_EMBED_LEN_MAX);
rb_define_global_function("load", rb_f_load, -1);
rb_define_global_function("require", rb_f_require, 1);
......
ruby_dln_librefs = rb_ary_new();
rb_gc_register_mark_object(ruby_dln_librefs);
for (j = 0; j < CHAR_ARRAY_LEN(available_extensions); ++j) {
available_ext_rb_str[j] = rb_str_new2(available_extensions[j]);
rb_gc_register_mark_object(available_ext_rb_str[j]);
}
}
test/ruby/test_require.rb
[], /\$LOADED_FEATURES is frozen; cannot append feature \(RuntimeError\)$/,
bug3756)
end
def test_case_insensitive
load_path = $:.dup
loaded = $".dup
path = File.expand_path(__FILE__)
$:.unshift(File.dirname(path))
$".push(path) unless $".include?(path)
bug4255 = '[ruby-core:34297]'
assert_equal(false, $bug4255 ||= false, bug4255)
$bug4255 = true
f = File.basename(__FILE__, ".*").upcase
assert_equal(false, require(f))
ensure
$:.replace(load_path)
$".replace(loaded)
end if File.identical?(__FILE__, __FILE__.upcase)
def test_feature_is_reloaded_from_new_load_path_entry
# This is a bit of a weird test, but it is needed to ensure that some
# caching optimizations are working correctly.
load_path = $:.dup
loaded = $".dup
initial_length = loaded.length
Dir.mktmpdir do |tmp|
Dir.chdir(tmp) do
Dir.mkdir "a"
Dir.mkdir "b"
File.open("a/test.rb", "w") {|f| f.puts '' }
File.open("b/test.rb", "w") {|f| f.puts '' }
$".clear
$:.unshift(File.join(tmp, "b"))
require 'test.rb'
assert $"[0].include?('b/test.rb')
$".clear
$:.unshift(File.join(tmp, "a"))
require 'test.rb'
assert $"[0].include?('a/test.rb')
end
end
ensure
$:.replace(load_path)
$".replace(loaded)
end
end
variable.c
}
static VALUE
autoload_provided(VALUE arg)
autoload_provided(VALUE fname)
{
const char **p = (const char **)arg;
return rb_feature_provided(*p, p);
return rb_feature_provided_2(fname);
}
static VALUE
......
loading = RSTRING_PTR(file);
safe = rb_safe_level();
rb_set_safe_level_force(0);
if (!rb_ensure(autoload_provided, (VALUE)&loading, reset_safe, (VALUE)safe)) {
if (!rb_ensure(autoload_provided, (VALUE)file, reset_safe, (VALUE)safe)) {
return load;
}
if (loadingpath && loading) {
vm.c
rb_mark_tbl(vm->loading_table);
}
if (vm->loaded_features_hash) {
rb_mark_tbl(vm->loaded_features_hash);
}
if (vm->filename_expansion_hash) {
rb_mark_tbl(vm->filename_expansion_hash);
}
mark_event_hooks(vm->event_hooks);
for (i = 0; i < RUBY_NSIG; i++) {
vm_core.h
* objects so do *NOT* mark this when you GC.
*/
struct RArray at_exit;
struct st_table *loaded_features_hash;
struct st_table *filename_expansion_hash;
} rb_vm_t;
typedef struct {
(2-2/2)