Skip to content

Commit

Permalink
backport load_path caching
Browse files Browse the repository at this point in the history
  • Loading branch information
funny-falcon committed Mar 5, 2013
1 parent e52527a commit e14838c
Show file tree
Hide file tree
Showing 9 changed files with 307 additions and 34 deletions.
27 changes: 27 additions & 0 deletions array.c
Expand Up @@ -295,6 +295,33 @@ rb_ary_frozen_p(VALUE ary)
return Qfalse; return Qfalse;
} }


/* This can be used to take a snapshot of an array (with
e.g. rb_ary_replace) and check later whether the array has been
modified from the snapshot. The snapshot is cheap, though if
something does modify the array it will pay the cost of copying
it. */
VALUE
rb_ary_dup_of_p(VALUE ary1, VALUE ary2)
{
VALUE *p1, *p2;
long len = RARRAY_LEN(ary1);

if (len != RARRAY_LEN(ary2)) return Qfalse;

p1 = RARRAY_PTR(ary1);
p2 = RARRAY_PTR(ary2);

if (ARY_EMBED_P(ary1) && ARY_EMBED_P(ary2)) {
for (; len; len--, p1++, p2++) {
if (*p1 != *p2) return Qfalse;
}
return Qtrue;
}

if (p1 == p2) return Qtrue;
return Qfalse;
}

static VALUE static VALUE
ary_alloc(VALUE klass) ary_alloc(VALUE klass)
{ {
Expand Down
43 changes: 31 additions & 12 deletions file.c
Expand Up @@ -149,40 +149,60 @@ file_path_convert(VALUE name)
return name; return name;
} }


static VALUE static rb_encoding *
rb_get_path_check(VALUE obj, int level) check_path_encoding(VALUE str)
{
rb_encoding *enc = rb_enc_get(str);
if (!rb_enc_asciicompat(enc)) {
rb_raise(rb_eEncCompatError, "path name must be ASCII-compatible (%s): %s",
rb_enc_name(enc), RSTRING_PTR(rb_str_inspect(str)));
}
return enc;
}

VALUE
rb_get_path_check_to_string(VALUE obj, int level)
{ {
VALUE tmp; VALUE tmp;
ID to_path; ID to_path;
rb_encoding *enc;


if (insecure_obj_p(obj, level)) { if (insecure_obj_p(obj, level)) {
rb_insecure_operation(); rb_insecure_operation();
} }


if (RB_TYPE_P(obj, T_STRING)) {
return obj;
}
CONST_ID(to_path, "to_path"); CONST_ID(to_path, "to_path");
tmp = rb_check_funcall(obj, to_path, 0, 0); tmp = rb_check_funcall(obj, to_path, 0, 0);
if (tmp == Qundef) { if (tmp == Qundef) {
tmp = obj; tmp = obj;
} }
StringValue(tmp); StringValue(tmp);
return tmp;
}


VALUE
rb_get_path_check_convert(VALUE obj, VALUE tmp, int level)
{
tmp = file_path_convert(tmp); tmp = file_path_convert(tmp);
if (obj != tmp && insecure_obj_p(tmp, level)) { if (obj != tmp && insecure_obj_p(tmp, level)) {
rb_insecure_operation(); rb_insecure_operation();
} }
enc = rb_enc_get(tmp);
if (!rb_enc_asciicompat(enc)) {
tmp = rb_str_inspect(tmp);
rb_raise(rb_eEncCompatError, "path name must be ASCII-compatible (%s): %s",
rb_enc_name(enc), RSTRING_PTR(tmp));
}


check_path_encoding(tmp);
StringValueCStr(tmp); StringValueCStr(tmp);


return rb_str_new4(tmp); return rb_str_new4(tmp);
} }


static VALUE
rb_get_path_check(VALUE obj, int level)
{
VALUE tmp = rb_get_path_check_to_string(obj, level);
return rb_get_path_check_convert(obj, tmp, level);
}

VALUE VALUE
rb_get_path_no_checksafe(VALUE obj) rb_get_path_no_checksafe(VALUE obj)
{ {
Expand Down Expand Up @@ -3250,7 +3270,6 @@ rb_file_expand_path(VALUE fname, VALUE dname)
VALUE VALUE
rb_file_expand_path_fast(VALUE fname, VALUE dname) rb_file_expand_path_fast(VALUE fname, VALUE dname)
{ {
check_expand_path_args(fname, dname);
return rb_file_expand_path_internal(fname, dname, 0, 0, EXPAND_PATH_BUFFER()); return rb_file_expand_path_internal(fname, dname, 0, 0, EXPAND_PATH_BUFFER());
} }


Expand Down Expand Up @@ -5241,7 +5260,7 @@ rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level)
rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f); rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
} }


RB_GC_GUARD(load_path) = rb_get_load_path(); RB_GC_GUARD(load_path) = rb_get_expanded_load_path();
if (!load_path) return 0; if (!load_path) return 0;


fname = rb_str_dup(*filep); fname = rb_str_dup(*filep);
Expand Down Expand Up @@ -5306,7 +5325,7 @@ rb_find_file_safe(VALUE path, int safe_level)
rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f); rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
} }


RB_GC_GUARD(load_path) = rb_get_load_path(); RB_GC_GUARD(load_path) = rb_get_expanded_load_path();
if (load_path) { if (load_path) {
long i; long i;


Expand Down
1 change: 1 addition & 0 deletions include/ruby/intern.h
Expand Up @@ -56,6 +56,7 @@ VALUE rb_ary_tmp_new(long);
void rb_ary_free(VALUE); void rb_ary_free(VALUE);
void rb_ary_modify(VALUE); void rb_ary_modify(VALUE);
VALUE rb_ary_freeze(VALUE); VALUE rb_ary_freeze(VALUE);
VALUE rb_ary_dup_of_p(VALUE, VALUE);
VALUE rb_ary_aref(int, VALUE*, VALUE); VALUE rb_ary_aref(int, VALUE*, VALUE);
VALUE rb_ary_subseq(VALUE, long, long); VALUE rb_ary_subseq(VALUE, long, long);
void rb_ary_store(VALUE, long, VALUE); void rb_ary_store(VALUE, long, VALUE);
Expand Down
3 changes: 3 additions & 0 deletions internal.h
Expand Up @@ -94,6 +94,8 @@ VALUE rb_home_dir(const char *user, VALUE result);
VALUE rb_realpath_internal(VALUE basedir, VALUE path, int strict); VALUE rb_realpath_internal(VALUE basedir, VALUE path, int strict);
VALUE rb_file_expand_path_fast(VALUE, VALUE); VALUE rb_file_expand_path_fast(VALUE, VALUE);
VALUE rb_file_expand_path_internal(VALUE, VALUE, int, int, VALUE); VALUE rb_file_expand_path_internal(VALUE, VALUE, int, int, VALUE);
VALUE rb_get_path_check_to_string(VALUE, int);
VALUE rb_get_path_check_convert(VALUE, VALUE, int);
void Init_File(void); void Init_File(void);


#ifdef _WIN32 #ifdef _WIN32
Expand All @@ -119,6 +121,7 @@ VALUE rb_iseq_clone(VALUE iseqval, VALUE newcbase);


/* load.c */ /* load.c */
VALUE rb_get_load_path(void); VALUE rb_get_load_path(void);
VALUE rb_get_expanded_load_path(void);


/* math.c */ /* math.c */
VALUE rb_math_atan2(VALUE, VALUE); VALUE rb_math_atan2(VALUE, VALUE);
Expand Down
144 changes: 123 additions & 21 deletions load.c
Expand Up @@ -34,21 +34,120 @@ rb_get_load_path(void)
return load_path; return load_path;
} }


VALUE enum expand_type {
rb_get_expanded_load_path(void) EXPAND_ALL,
EXPAND_RELATIVE,
EXPAND_HOME,
EXPAND_NON_CACHE
};

/* Construct expanded load path and store it to cache.
We rebuild load path partially if the cache is invalid.
We don't cache non string object and expand it every time. We ensure that
string objects in $LOAD_PATH are frozen.
*/
static void
rb_construct_expanded_load_path(int type, int *has_relative, int *has_non_cache)
{ {
VALUE load_path = rb_get_load_path(); rb_vm_t *vm = GET_VM();
VALUE load_path = vm->load_path;
VALUE expanded_load_path = vm->expanded_load_path;
VALUE ary; VALUE ary;
long i; long i;
int level = rb_safe_level();


ary = rb_ary_new2(RARRAY_LEN(load_path)); ary = rb_ary_new2(RARRAY_LEN(load_path));
for (i = 0; i < RARRAY_LEN(load_path); ++i) { for (i = 0; i < RARRAY_LEN(load_path); ++i) {
VALUE path = rb_file_expand_path_fast(RARRAY_PTR(load_path)[i], Qnil); VALUE path, as_str, expanded_path;
rb_str_freeze(path); int is_string, non_cache;
rb_ary_push(ary, path); char *as_cstr;
as_str = path = RARRAY_PTR(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_cstr = RSTRING_PTR(as_str);

if (!non_cache) {
if ((type == EXPAND_RELATIVE &&
rb_is_absolute_path(as_cstr)) ||
(type == EXPAND_HOME &&
(!as_cstr[0] || as_cstr[0] != '~')) ||
(type == EXPAND_NON_CACHE)) {
/* Use cached expanded path. */
rb_ary_push(ary, RARRAY_PTR(expanded_load_path)[i]);
continue;
}
}
if (!*has_relative && !rb_is_absolute_path(as_cstr))
*has_relative = 1;
if (!*has_non_cache && non_cache)
*has_non_cache = 1;
/* 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);
} }
rb_obj_freeze(ary); rb_obj_freeze(ary);
return ary; vm->expanded_load_path = ary;
rb_ary_replace(vm->load_path_snapshot, vm->load_path);
}

static VALUE
load_path_getcwd(void)
{
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_dup_of_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);
if (has_relative) {
vm->load_path_check_cache = load_path_getcwd();
}
else if (has_non_cache) {
/* Non string object. */
vm->load_path_check_cache = non_cache;
}
else {
vm->load_path_check_cache = 0;
}
}
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,
&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();
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,
&has_relative, &has_non_cache);
}
else {
/* Expand only tilde (User HOME) and non-cacheable objects. */
rb_construct_expanded_load_path(EXPAND_HOME,
&has_relative, &has_non_cache);
}
}
return vm->expanded_load_path;
} }


static VALUE static VALUE
Expand Down Expand Up @@ -88,23 +187,22 @@ loaded_feature_path(const char *name, long vlen, const char *feature, long len,
return 0; return 0;
plen = e - name - len - 1; plen = e - name - len - 1;
} }
if (type == 's' && !IS_DLEXT(&name[plen+len+1])
|| type == 'r' && !IS_RBEXT(&name[plen+len+1])
|| name[plen] != '/') {
return 0;
}
/* Now name == "#{prefix}/#{feature}#{ext}" where ext is acceptable
(possibly empty) and prefix is some string of length plen. */

for (i = 0; i < RARRAY_LEN(load_path); ++i) { for (i = 0; i < RARRAY_LEN(load_path); ++i) {
VALUE p = RARRAY_PTR(load_path)[i]; VALUE p = RARRAY_PTR(load_path)[i];
const char *s = StringValuePtr(p); const char *s = StringValuePtr(p);
long n = RSTRING_LEN(p); long n = RSTRING_LEN(p);


if (n != plen ) continue; if (n != plen) continue;
if (n && (strncmp(name, s, n) || name[n] != '/')) continue; if (n && strncmp(name, s, n)) continue;
switch (type) { return p;
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; return 0;
} }
Expand Down Expand Up @@ -175,6 +273,7 @@ rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const c
return 'r'; return 'r';
} }
} }

loading_tbl = get_loading_table(); loading_tbl = get_loading_table();
if (loading_tbl) { if (loading_tbl) {
f = 0; f = 0;
Expand All @@ -183,7 +282,7 @@ rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const c
fs.name = feature; fs.name = feature;
fs.len = len; fs.len = len;
fs.type = type; fs.type = type;
fs.load_path = load_path ? load_path : rb_get_load_path(); fs.load_path = load_path ? load_path : rb_get_expanded_load_path();
fs.result = 0; fs.result = 0;
st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs); st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs);
if ((f = fs.result) != 0) { if ((f = fs.result) != 0) {
Expand Down Expand Up @@ -233,7 +332,7 @@ rb_feature_provided(const char *feature, const char **loading)


if (*feature == '.' && if (*feature == '.' &&
(feature[1] == '/' || strncmp(feature+1, "./", 2) == 0)) { (feature[1] == '/' || strncmp(feature+1, "./", 2) == 0)) {
fullpath = rb_file_expand_path_fast(rb_str_new2(feature), Qnil); fullpath = rb_file_expand_path_fast(rb_get_path(rb_str_new2(feature)), Qnil);
feature = RSTRING_PTR(fullpath); feature = RSTRING_PTR(fullpath);
} }
if (ext && !strchr(ext, '/')) { if (ext && !strchr(ext, '/')) {
Expand Down Expand Up @@ -774,6 +873,9 @@ Init_load()
rb_alias_variable(rb_intern("$-I"), id_load_path); rb_alias_variable(rb_intern("$-I"), id_load_path);
rb_alias_variable(rb_intern("$LOAD_PATH"), id_load_path); rb_alias_variable(rb_intern("$LOAD_PATH"), id_load_path);
vm->load_path = rb_ary_new(); vm->load_path = rb_ary_new();
vm->expanded_load_path = rb_ary_new();
vm->load_path_snapshot = rb_ary_new();
vm->load_path_check_cache = 0;


rb_define_virtual_variable("$\"", get_loaded_features, 0); rb_define_virtual_variable("$\"", get_loaded_features, 0);
rb_define_virtual_variable("$LOADED_FEATURES", get_loaded_features, 0); rb_define_virtual_variable("$LOADED_FEATURES", get_loaded_features, 0);
Expand Down
3 changes: 2 additions & 1 deletion ruby.c
Expand Up @@ -1366,7 +1366,8 @@ process_options(int argc, char **argv, struct cmdline_options *opt)
long i; long i;
VALUE load_path = GET_VM()->load_path; VALUE load_path = GET_VM()->load_path;
for (i = 0; i < RARRAY_LEN(load_path); ++i) { for (i = 0; i < RARRAY_LEN(load_path); ++i) {
rb_enc_associate(RARRAY_PTR(load_path)[i], lenc); RARRAY_PTR(load_path)[i] =
rb_enc_associate(rb_str_dup(RARRAY_PTR(load_path)[i]), lenc);
} }
} }
if (!(opt->disable & DISABLE_BIT(gems))) { if (!(opt->disable & DISABLE_BIT(gems))) {
Expand Down

0 comments on commit e14838c

Please sign in to comment.