Feature #5767 » cache_expanded_load_path.patch
file.c | ||
---|---|---|
buflen = RSTRING_LEN(result),\
|
||
pend = p + buflen)
|
||
#define EXPAND_PATH()\
|
||
if ( !(abs_mode & FEP_DIR_EXPANDED) ) { \
|
||
file_expand_path(dname, Qnil, abs_mode, result); \
|
||
} \
|
||
else { \
|
||
size_t dlen = RSTRING_LEN(dname); \
|
||
BUFCHECK(dlen > buflen); \
|
||
strncpy(buf, RSTRING_PTR(dname), dlen + 1); \
|
||
rb_str_set_len(result, dlen); \
|
||
rb_enc_associate(result, rb_enc_check(result, dname)); \
|
||
ENC_CODERANGE_CLEAR(result); \
|
||
}
|
||
VALUE
|
||
rb_home_dir(const char *user, VALUE result)
|
||
{
|
||
... | ... | |
return result;
|
||
}
|
||
#define FEP_FILE_ABSOLUTE 1
|
||
#define FEP_DIR_EXPANDED 2
|
||
static VALUE
|
||
file_expand_path(VALUE fname, VALUE dname, int abs_mode, VALUE result)
|
||
{
|
||
... | ... | |
BUFINIT();
|
||
tainted = OBJ_TAINTED(fname);
|
||
if (s[0] == '~' && abs_mode == 0) { /* execute only if NOT absolute_path() */
|
||
if (s[0] == '~' && !(abs_mode & FEP_FILE_ABSOLUTE)) { /* execute only if NOT absolute_path() */
|
||
long userlen = 0;
|
||
tainted = 1;
|
||
if (isdirsep(s[1]) || s[1] == '\0') {
|
||
... | ... | |
/* specified drive, but not full path */
|
||
int same = 0;
|
||
if (!NIL_P(dname) && !not_same_drive(dname, s[0])) {
|
||
file_expand_path(dname, Qnil, abs_mode, result);
|
||
EXPAND_PATH();
|
||
BUFINIT();
|
||
if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
|
||
/* ok, same drive */
|
||
... | ... | |
#endif
|
||
else if (!rb_is_absolute_path(s)) {
|
||
if (!NIL_P(dname)) {
|
||
file_expand_path(dname, Qnil, abs_mode, result);
|
||
EXPAND_PATH();
|
||
BUFINIT();
|
||
rb_enc_associate(result, rb_enc_check(result, fname));
|
||
}
|
||
... | ... | |
rb_file_absolute_path(VALUE fname, VALUE dname)
|
||
{
|
||
check_expand_path_args(fname, dname);
|
||
return file_expand_path(fname, dname, 1, EXPAND_PATH_BUFFER());
|
||
return file_expand_path(fname, dname, FEP_FILE_ABSOLUTE, EXPAND_PATH_BUFFER());
|
||
}
|
||
/*
|
||
... | ... | |
return rb_find_file_ext_safe(filep, ext, rb_safe_level());
|
||
}
|
||
#define GET_LOAD_PATH() \
|
||
if (cached_expanded_load_path) { \
|
||
RB_GC_GUARD(load_path) = rb_get_expanded_load_path(); \
|
||
dirs_mode = FEP_DIR_EXPANDED; \
|
||
} \
|
||
else { \
|
||
RB_GC_GUARD(load_path) = rb_get_load_path(); \
|
||
dirs_mode = 0; \
|
||
}
|
||
int
|
||
rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level)
|
||
{
|
||
const char *f = StringValueCStr(*filep);
|
||
VALUE fname = *filep, load_path, tmp;
|
||
long i, j, fnlen;
|
||
int expanded = 0;
|
||
int expanded = 0, dirs_mode;
|
||
if (!ext[0]) return 0;
|
||
... | ... | |
rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
|
||
}
|
||
RB_GC_GUARD(load_path) = rb_get_load_path();
|
||
GET_LOAD_PATH();
|
||
if (!load_path) return 0;
|
||
fname = rb_str_dup(*filep);
|
||
... | ... | |
RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
|
||
if (RSTRING_LEN(str) == 0) continue;
|
||
file_expand_path(fname, str, 0, tmp);
|
||
file_expand_path(fname, str, dirs_mode, tmp);
|
||
if (file_load_ok(RSTRING_PTR(tmp))) {
|
||
*filep = copy_path_class(tmp, *filep);
|
||
return (int)(j+1);
|
||
... | ... | |
{
|
||
VALUE tmp, load_path;
|
||
const char *f = StringValueCStr(path);
|
||
int expanded = 0;
|
||
int expanded = 0, dirs_mode;
|
||
if (f[0] == '~') {
|
||
tmp = file_expand_path_1(path);
|
||
... | ... | |
rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
|
||
}
|
||
RB_GC_GUARD(load_path) = rb_get_load_path();
|
||
GET_LOAD_PATH();
|
||
if (load_path) {
|
||
long i;
|
||
... | ... | |
VALUE str = RARRAY_PTR(load_path)[i];
|
||
RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
|
||
if (RSTRING_LEN(str) > 0) {
|
||
file_expand_path(path, str, 0, tmp);
|
||
file_expand_path(path, str, dirs_mode, tmp);
|
||
f = RSTRING_PTR(tmp);
|
||
if (file_load_ok(f)) goto found;
|
||
}
|
internal.h | ||
---|---|---|
/* load.c */
|
||
VALUE rb_get_load_path(void);
|
||
VALUE rb_get_expanded_load_path(void);
|
||
RUBY_EXTERN int cached_expanded_load_path;
|
||
/* math.c */
|
||
VALUE rb_math_atan2(VALUE, VALUE);
|
load.c | ||
---|---|---|
#include "ruby/ruby.h"
|
||
#include "ruby/util.h"
|
||
#include "ruby/encoding.h"
|
||
#include "internal.h"
|
||
#include "dln.h"
|
||
#include "eval_intern.h"
|
||
... | ... | |
#define IS_DLEXT(e) (strcmp((e), DLEXT) == 0)
|
||
#endif
|
||
static const char *const loadable_ext[] = {
|
||
".rb", DLEXT,
|
||
#ifdef DLEXT2
|
||
... | ... | |
return load_path;
|
||
}
|
||
static VALUE rb_checked_expanded_cache();
|
||
static void rb_set_expanded_cache(VALUE);
|
||
int cached_expanded_load_path = 1;
|
||
VALUE
|
||
rb_get_expanded_load_path(void)
|
||
{
|
||
VALUE load_path = rb_get_load_path();
|
||
VALUE ary;
|
||
long i;
|
||
VALUE expanded = rb_checked_expanded_cache();
|
||
ary = rb_ary_new2(RARRAY_LEN(load_path));
|
||
for (i = 0; i < RARRAY_LEN(load_path); ++i) {
|
||
VALUE path = rb_file_expand_path(RARRAY_PTR(load_path)[i], Qnil);
|
||
rb_str_freeze(path);
|
||
rb_ary_push(ary, path);
|
||
if ( !RTEST(expanded) ) {
|
||
VALUE load_path = rb_get_load_path();
|
||
long i;
|
||
if (!load_path) return 0;
|
||
expanded = rb_ary_new2(RARRAY_LEN(load_path));
|
||
for (i = 0; i < RARRAY_LEN(load_path); ++i) {
|
||
VALUE path = rb_file_expand_path(RARRAY_PTR(load_path)[i], Qnil);
|
||
rb_str_freeze(path);
|
||
rb_ary_push(expanded, path);
|
||
}
|
||
if (cached_expanded_load_path) {
|
||
rb_set_expanded_cache(expanded);
|
||
}
|
||
} else {
|
||
expanded = rb_ary_dup(expanded);
|
||
}
|
||
rb_obj_freeze(ary);
|
||
return ary;
|
||
return expanded;
|
||
}
|
||
static VALUE
|
||
... | ... | |
return rb_mod_autoload_p(klass, sym);
|
||
}
|
||
// $LOAD_PATH methods which invalidates cache
|
||
static const char *load_path_reset_cache_methods[] = {
|
||
"[]=", "collect!", "compact!", "delete",
|
||
"delete_if", "fill", "flatten!", "insert", "keep_if",
|
||
"map!", "reject!", "replace", "select!", "shuffle!",
|
||
"sort!", "sort_by!", "uniq!", NULL
|
||
};
|
||
// $LOAD_PATH methods which sends also to cache
|
||
static const char *load_path_apply_to_cache_methods[] = {
|
||
"clear", "delete_at", "pop", "reverse!", "rotate!",
|
||
"shift", "slice!", NULL
|
||
};
|
||
// $LOAD_PATH methods which sends to cache whith expanded arguments
|
||
static const char *load_path_apply_expanded_methods[] = {
|
||
"<<", "push", "unshift", NULL
|
||
};
|
||
static void
|
||
rb_reset_expanded_cache()
|
||
{
|
||
GET_VM()->load_path_expanded_cache = 0;
|
||
}
|
||
static VALUE
|
||
rb_load_path_expanded_cache()
|
||
{
|
||
VALUE cache = GET_VM()->load_path_expanded_cache;
|
||
VALUE expanded = Qnil;
|
||
if (RTEST(cache)) {
|
||
expanded = RARRAY_PTR(cache)[2];
|
||
}
|
||
return expanded;
|
||
}
|
||
// Return cache only if we still in the same working directory
|
||
// and filesystem_encoding didn't change
|
||
// Invalidate cache otherwise
|
||
static VALUE
|
||
rb_checked_expanded_cache()
|
||
{
|
||
VALUE cache = GET_VM()->load_path_expanded_cache;
|
||
VALUE expanded = Qnil;
|
||
if (RTEST(cache)) {
|
||
VALUE curwd = RARRAY_PTR(cache)[0];
|
||
VALUE encindex = RARRAY_PTR(cache)[1];
|
||
int cache_valid = rb_filesystem_encindex() == FIX2INT(encindex);
|
||
if ( cache_valid ) {
|
||
cache_valid = curwd == Qtrue;
|
||
if (!cache_valid ) {
|
||
char *cwd = my_getcwd();
|
||
cache_valid = !strcmp(RSTRING_PTR(curwd), cwd);
|
||
xfree(cwd);
|
||
}
|
||
}
|
||
if ( !cache_valid ) {
|
||
rb_reset_expanded_cache();
|
||
} else {
|
||
expanded = RARRAY_PTR(cache)[2];
|
||
}
|
||
}
|
||
RB_GC_GUARD(cache);
|
||
return expanded;
|
||
}
|
||
static void
|
||
rb_set_expanded_cache(VALUE expanded)
|
||
{
|
||
VALUE load_path = rb_get_load_path();
|
||
VALUE cache = rb_ary_new2(2);
|
||
int i, lplen = RARRAY_LEN(load_path), has_relative = 0;
|
||
char *d;
|
||
for(i = 0; i < lplen && !has_relative; i++) {
|
||
d = RSTRING_PTR(RARRAY_PTR(load_path)[i]);
|
||
has_relative = !rb_is_absolute_path(d);
|
||
}
|
||
if (has_relative) {
|
||
char *cwd = my_getcwd();
|
||
rb_ary_push(cache, rb_str_new_cstr(cwd));
|
||
xfree(cwd);
|
||
} else {
|
||
rb_ary_push(cache, Qtrue);
|
||
}
|
||
rb_ary_push(cache, INT2FIX(rb_filesystem_encindex()));
|
||
rb_ary_push(cache, rb_ary_dup(expanded));
|
||
GET_VM()->load_path_expanded_cache = cache;
|
||
}
|
||
// Invalidating $LOAD_PATH methods implementation
|
||
static VALUE
|
||
rb_load_path_reset_cache_method(int argc, VALUE *argv, VALUE self)
|
||
{
|
||
rb_reset_expanded_cache();
|
||
return rb_call_super(argc, argv);
|
||
}
|
||
// Proxying $LOAD_PATH methods implementation
|
||
static VALUE
|
||
rb_load_path_apply_to_cache_method(int argc, VALUE *argv, VALUE self)
|
||
{
|
||
VALUE load_path_expanded = rb_load_path_expanded_cache();
|
||
if (RTEST(load_path_expanded)) {
|
||
ID func = rb_frame_this_func();
|
||
rb_funcall2(load_path_expanded, func, argc, argv);
|
||
}
|
||
return rb_call_super(argc, argv);
|
||
}
|
||
// Proxying with expansion $LOAD_PATH methods implementation
|
||
static VALUE
|
||
rb_load_path_apply_expanded_method(int argc, VALUE *argv, VALUE self)
|
||
{
|
||
// We call methods on cache only if we still in the same working directory
|
||
VALUE load_path_expanded = rb_checked_expanded_cache();
|
||
if (RTEST(load_path_expanded)) {
|
||
int i, has_relative = 0;
|
||
ID func = rb_frame_this_func();
|
||
VALUE expanded = rb_ary_new2(argc);
|
||
for(i = 0; i < argc; i++) {
|
||
VALUE path = rb_get_path(argv[i]);
|
||
has_relative = has_relative || !rb_is_absolute_path(RSTRING_PTR(path));
|
||
path = rb_file_expand_path(path, Qnil);
|
||
rb_str_freeze(path);
|
||
rb_ary_push(expanded, path);
|
||
}
|
||
if (has_relative) {
|
||
VALUE cache = GET_VM()->load_path_expanded_cache;
|
||
char *cwd = my_getcwd();
|
||
rb_ary_store(cache, 0, rb_str_new_cstr(cwd));
|
||
xfree(cwd);
|
||
}
|
||
rb_funcall2(load_path_expanded, func, argc, RARRAY_PTR(expanded));
|
||
RB_GC_GUARD(expanded);
|
||
}
|
||
return rb_call_super(argc, argv);
|
||
}
|
||
// $LOAD_PATH.concat(ary) - special, we call push(*ary) instead
|
||
// cause I'm lazy a bit and wish not to rewrite method above second time :)
|
||
static VALUE
|
||
rb_load_path_concat(VALUE self, VALUE ary)
|
||
{
|
||
ID push;
|
||
CONST_ID(push, "push");
|
||
RB_GC_GUARD(ary);
|
||
return rb_funcall2(self, push, RARRAY_LEN(ary), RARRAY_PTR(ary));
|
||
}
|
||
static VALUE
|
||
rb_load_path_init(void)
|
||
{
|
||
const char **name;
|
||
VALUE load_path = rb_ary_new();
|
||
char *cached_flag;
|
||
cached_flag = getenv("RUBY_CACHED_LOAD_PATH");
|
||
if (cached_flag != NULL) {
|
||
cached_expanded_load_path = atoi(cached_flag);
|
||
}
|
||
// Do all the magick if user did not disable it
|
||
// with RUBY_CACHED_LOAD_PATH=0 environment variable
|
||
if (cached_expanded_load_path) {
|
||
VALUE load_path_c = rb_singleton_class(load_path);
|
||
for(name = load_path_reset_cache_methods; *name; name++ ) {
|
||
rb_define_method(load_path_c, *name, rb_load_path_reset_cache_method, -1);
|
||
}
|
||
for(name = load_path_apply_to_cache_methods; *name; name++ ) {
|
||
rb_define_method(load_path_c, *name, rb_load_path_apply_to_cache_method, -1);
|
||
}
|
||
for(name = load_path_apply_expanded_methods; *name; name++ ) {
|
||
rb_define_method(load_path_c, *name, rb_load_path_apply_expanded_method, -1);
|
||
}
|
||
rb_define_method(load_path_c, "concat", rb_load_path_concat, 1);
|
||
}
|
||
rb_reset_expanded_cache();
|
||
return load_path;
|
||
}
|
||
void
|
||
Init_load()
|
||
{
|
||
... | ... | |
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);
|
||
vm->load_path = rb_ary_new();
|
||
vm->load_path = rb_load_path_init();
|
||
rb_define_virtual_variable("$\"", get_loaded_features, 0);
|
||
rb_define_virtual_variable("$LOADED_FEATURES", get_loaded_features, 0);
|
ruby.c | ||
---|---|---|
const char sep = PATH_SEP_CHAR;
|
||
const char *p, *s;
|
||
VALUE load_path = GET_VM()->load_path;
|
||
ID push;
|
||
CONST_ID(push, "<<");
|
||
p = path;
|
||
while (*p) {
|
||
... | ... | |
p++;
|
||
if (!*p) break;
|
||
for (s = p; *s && *s != sep; s = CharNext(s));
|
||
rb_ary_push(load_path, (*filter)(rubylib_mangled_path(p, s - p)));
|
||
rb_funcall(load_path, push, 1, (*filter)(rubylib_mangled_path(p, s - p)));
|
||
p = s;
|
||
}
|
||
}
|
||
... | ... | |
ruby_init_loadpath_safe(int safe_level)
|
||
{
|
||
VALUE load_path;
|
||
ID id_initial_load_path_mark;
|
||
ID id_initial_load_path_mark, push;
|
||
extern const char ruby_initial_load_paths[];
|
||
const char *paths = ruby_initial_load_paths;
|
||
#if defined LOAD_RELATIVE
|
||
... | ... | |
}
|
||
id_initial_load_path_mark = rb_intern_const("@gem_prelude_index");
|
||
CONST_ID(push, "<<");
|
||
while (*paths) {
|
||
size_t len = strlen(paths);
|
||
VALUE path = RUBY_RELATIVE(paths, len);
|
||
rb_ivar_set(path, id_initial_load_path_mark, path);
|
||
rb_ary_push(load_path, path);
|
||
rb_funcall(load_path, push, 1, path);
|
||
paths += len + 1;
|
||
}
|
||
vm.c | ||
---|---|---|
RUBY_MARK_UNLESS_NULL(vm->thgroup_default);
|
||
RUBY_MARK_UNLESS_NULL(vm->mark_object_ary);
|
||
RUBY_MARK_UNLESS_NULL(vm->load_path);
|
||
RUBY_MARK_UNLESS_NULL(vm->load_path_expanded_cache);
|
||
RUBY_MARK_UNLESS_NULL(vm->loaded_features);
|
||
RUBY_MARK_UNLESS_NULL(vm->top_self);
|
||
RUBY_MARK_UNLESS_NULL(vm->coverages);
|
vm_core.h | ||
---|---|---|
/* load */
|
||
VALUE top_self;
|
||
VALUE load_path;
|
||
VALUE load_path_expanded_cache;
|
||
VALUE loaded_features;
|
||
struct st_table *loading_table;
|
||