cache_expanded_load_path.patch

final version, `rb_find_file_safe` included, tests fixed. - Yura Sokolov, 12/19/2011 04:07 AM

Download (14.4 KB)

View differences:

file.c
2821 2821
    buflen = RSTRING_LEN(result),\
2822 2822
    pend = p + buflen)
2823 2823

  
2824
#define EXPAND_PATH()\
2825
    if ( !(abs_mode & FEP_DIR_EXPANDED) ) { \
2826
	file_expand_path(dname, Qnil, abs_mode, result); \
2827
    } \
2828
    else { \
2829
	size_t dlen = RSTRING_LEN(dname); \
2830
	BUFCHECK(dlen > buflen); \
2831
	strncpy(buf, RSTRING_PTR(dname), dlen + 1); \
2832
	rb_str_set_len(result, dlen); \
2833
	rb_enc_associate(result, rb_enc_check(result, dname)); \
2834
	ENC_CODERANGE_CLEAR(result); \
2835
    }
2836

  
2837

  
2824 2838
VALUE
2825 2839
rb_home_dir(const char *user, VALUE result)
2826 2840
{
......
2865 2879
    return result;
2866 2880
}
2867 2881

  
2882
#define FEP_FILE_ABSOLUTE 1
2883
#define FEP_DIR_EXPANDED 2
2868 2884
static VALUE
2869 2885
file_expand_path(VALUE fname, VALUE dname, int abs_mode, VALUE result)
2870 2886
{
......
2877 2893
    BUFINIT();
2878 2894
    tainted = OBJ_TAINTED(fname);
2879 2895

  
2880
    if (s[0] == '~' && abs_mode == 0) {      /* execute only if NOT absolute_path() */
2896
    if (s[0] == '~' && !(abs_mode & FEP_FILE_ABSOLUTE)) {      /* execute only if NOT absolute_path() */
2881 2897
	long userlen = 0;
2882 2898
	tainted = 1;
2883 2899
	if (isdirsep(s[1]) || s[1] == '\0') {
......
2925 2941
	    /* specified drive, but not full path */
2926 2942
	    int same = 0;
2927 2943
	    if (!NIL_P(dname) && !not_same_drive(dname, s[0])) {
2928
		file_expand_path(dname, Qnil, abs_mode, result);
2944
		EXPAND_PATH();
2929 2945
		BUFINIT();
2930 2946
		if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
2931 2947
		    /* ok, same drive */
......
2951 2967
#endif
2952 2968
    else if (!rb_is_absolute_path(s)) {
2953 2969
	if (!NIL_P(dname)) {
2954
	    file_expand_path(dname, Qnil, abs_mode, result);
2970
	    EXPAND_PATH();
2955 2971
	    BUFINIT();
2956 2972
	    rb_enc_associate(result, rb_enc_check(result, fname));
2957 2973
	}
......
3235 3251
rb_file_absolute_path(VALUE fname, VALUE dname)
3236 3252
{
3237 3253
    check_expand_path_args(fname, dname);
3238
    return file_expand_path(fname, dname, 1, EXPAND_PATH_BUFFER());
3254
    return file_expand_path(fname, dname, FEP_FILE_ABSOLUTE, EXPAND_PATH_BUFFER());
3239 3255
}
3240 3256

  
3241 3257
/*
......
5109 5125
    return rb_find_file_ext_safe(filep, ext, rb_safe_level());
5110 5126
}
5111 5127

  
5128
#define GET_LOAD_PATH() \
5129
    if (cached_expanded_load_path) { \
5130
	RB_GC_GUARD(load_path) = rb_get_expanded_load_path(); \
5131
	dirs_mode = FEP_DIR_EXPANDED; \
5132
    } \
5133
    else { \
5134
	RB_GC_GUARD(load_path) = rb_get_load_path(); \
5135
	dirs_mode = 0; \
5136
    }
5137

  
5112 5138
int
5113 5139
rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level)
5114 5140
{
5115 5141
    const char *f = StringValueCStr(*filep);
5116 5142
    VALUE fname = *filep, load_path, tmp;
5117 5143
    long i, j, fnlen;
5118
    int expanded = 0;
5144
    int expanded = 0, dirs_mode;
5119 5145

  
5120 5146
    if (!ext[0]) return 0;
5121 5147

  
......
5150 5176
	rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
5151 5177
    }
5152 5178

  
5153
    RB_GC_GUARD(load_path) = rb_get_load_path();
5179
    GET_LOAD_PATH();
5154 5180
    if (!load_path) return 0;
5155 5181

  
5156 5182
    fname = rb_str_dup(*filep);
......
5164 5190

  
5165 5191
	    RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
5166 5192
	    if (RSTRING_LEN(str) == 0) continue;
5167
	    file_expand_path(fname, str, 0, tmp);
5193
	    file_expand_path(fname, str, dirs_mode, tmp);
5168 5194
	    if (file_load_ok(RSTRING_PTR(tmp))) {
5169 5195
		*filep = copy_path_class(tmp, *filep);
5170 5196
		return (int)(j+1);
......
5188 5214
{
5189 5215
    VALUE tmp, load_path;
5190 5216
    const char *f = StringValueCStr(path);
5191
    int expanded = 0;
5217
    int expanded = 0, dirs_mode;
5192 5218

  
5193 5219
    if (f[0] == '~') {
5194 5220
	tmp = file_expand_path_1(path);
......
5214 5240
	rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
5215 5241
    }
5216 5242

  
5217
    RB_GC_GUARD(load_path) = rb_get_load_path();
5243
    GET_LOAD_PATH();
5218 5244
    if (load_path) {
5219 5245
	long i;
5220 5246

  
......
5223 5249
	    VALUE str = RARRAY_PTR(load_path)[i];
5224 5250
	    RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
5225 5251
	    if (RSTRING_LEN(str) > 0) {
5226
		file_expand_path(path, str, 0, tmp);
5252
		file_expand_path(path, str, dirs_mode, tmp);
5227 5253
		f = RSTRING_PTR(tmp);
5228 5254
		if (file_load_ok(f)) goto found;
5229 5255
	    }
internal.h
109 109

  
110 110
/* load.c */
111 111
VALUE rb_get_load_path(void);
112
VALUE rb_get_expanded_load_path(void);
113
RUBY_EXTERN int cached_expanded_load_path;
112 114

  
113 115
/* math.c */
114 116
VALUE rb_math_atan2(VALUE, VALUE);
load.c
4 4

  
5 5
#include "ruby/ruby.h"
6 6
#include "ruby/util.h"
7
#include "ruby/encoding.h"
7 8
#include "internal.h"
8 9
#include "dln.h"
9 10
#include "eval_intern.h"
......
18 19
#define IS_DLEXT(e) (strcmp((e), DLEXT) == 0)
19 20
#endif
20 21

  
21

  
22 22
static const char *const loadable_ext[] = {
23 23
    ".rb", DLEXT,
24 24
#ifdef DLEXT2
......
34 34
    return load_path;
35 35
}
36 36

  
37
static VALUE rb_checked_expanded_cache();
38
static void rb_set_expanded_cache(VALUE);
39
int cached_expanded_load_path = 1;
40

  
37 41
VALUE
38 42
rb_get_expanded_load_path(void)
39 43
{
40
    VALUE load_path = rb_get_load_path();
41
    VALUE ary;
42
    long i;
44
    VALUE expanded = rb_checked_expanded_cache();
43 45

  
44
    ary = rb_ary_new2(RARRAY_LEN(load_path));
45
    for (i = 0; i < RARRAY_LEN(load_path); ++i) {
46
	VALUE path = rb_file_expand_path(RARRAY_PTR(load_path)[i], Qnil);
47
	rb_str_freeze(path);
48
	rb_ary_push(ary, path);
46
    if ( !RTEST(expanded) ) {
47
	VALUE load_path = rb_get_load_path();
48
	long i;
49

  
50
	if (!load_path) return 0;
51

  
52
	expanded = rb_ary_new2(RARRAY_LEN(load_path));
53
	for (i = 0; i < RARRAY_LEN(load_path); ++i) {
54
	    VALUE path = rb_file_expand_path(RARRAY_PTR(load_path)[i], Qnil);
55
	    rb_str_freeze(path);
56
	    rb_ary_push(expanded, path);
57
	}
58

  
59
	if (cached_expanded_load_path) {
60
	    rb_set_expanded_cache(expanded);
61
	}
62
    } else {
63
	expanded = rb_ary_dup(expanded);
49 64
    }
50
    rb_obj_freeze(ary);
51
    return ary;
65
    return expanded;
52 66
}
53 67

  
54 68
static VALUE
......
772 786
    return rb_mod_autoload_p(klass, sym);
773 787
}
774 788

  
789
// $LOAD_PATH methods which invalidates cache
790
static const char *load_path_reset_cache_methods[] = {
791
    "[]=", "collect!", "compact!", "delete",
792
    "delete_if", "fill", "flatten!", "insert", "keep_if",
793
    "map!", "reject!", "replace", "select!", "shuffle!",
794
    "sort!", "sort_by!", "uniq!", NULL
795
};
796

  
797
// $LOAD_PATH methods which sends also to cache
798
static const char *load_path_apply_to_cache_methods[] = {
799
    "clear", "delete_at", "pop", "reverse!", "rotate!",
800
    "shift", "slice!", NULL
801
};
802

  
803
// $LOAD_PATH methods which sends to cache whith expanded arguments
804
static const char *load_path_apply_expanded_methods[] = {
805
    "<<", "push", "unshift", NULL
806
};
807

  
808
static void
809
rb_reset_expanded_cache()
810
{
811
    GET_VM()->load_path_expanded_cache = 0;
812
}
813

  
814
static VALUE
815
rb_load_path_expanded_cache()
816
{
817
    VALUE cache = GET_VM()->load_path_expanded_cache;
818
    VALUE expanded = Qnil;
819
    if (RTEST(cache)) {
820
	expanded = RARRAY_PTR(cache)[2];
821
    }
822
    return expanded;
823
}
824

  
825
// Return cache only if we still in the same working directory
826
// and filesystem_encoding didn't change
827
// Invalidate cache otherwise
828
static VALUE
829
rb_checked_expanded_cache()
830
{
831
    VALUE cache = GET_VM()->load_path_expanded_cache;
832
    VALUE expanded = Qnil;
833
    if (RTEST(cache)) {
834
	VALUE curwd = RARRAY_PTR(cache)[0];
835
	VALUE encindex = RARRAY_PTR(cache)[1];
836
	int cache_valid = rb_filesystem_encindex() == FIX2INT(encindex);
837

  
838
	if ( cache_valid ) {
839
	    cache_valid = curwd == Qtrue;
840
	    if (!cache_valid ) {
841
		char *cwd = my_getcwd();
842
		cache_valid = !strcmp(RSTRING_PTR(curwd), cwd);
843
		xfree(cwd);
844
	    }
845
	}
846

  
847
	if ( !cache_valid ) {
848
	    rb_reset_expanded_cache();
849
	} else {
850
	    expanded = RARRAY_PTR(cache)[2];
851
	}
852
    }
853
    RB_GC_GUARD(cache);
854
    return expanded;
855
}
856

  
857
static void
858
rb_set_expanded_cache(VALUE expanded)
859
{
860
    VALUE load_path = rb_get_load_path();
861
    VALUE cache = rb_ary_new2(2);
862
    int i, lplen = RARRAY_LEN(load_path), has_relative = 0;
863
    char *d;
864

  
865
    for(i = 0; i < lplen && !has_relative; i++) {
866
	d = RSTRING_PTR(RARRAY_PTR(load_path)[i]);
867
	has_relative = !rb_is_absolute_path(d);
868
    }
869

  
870
    if (has_relative) {
871
	char *cwd = my_getcwd();
872
	rb_ary_push(cache, rb_str_new_cstr(cwd));
873
	xfree(cwd);
874
    } else {
875
	rb_ary_push(cache, Qtrue);
876
    }
877

  
878
    rb_ary_push(cache, INT2FIX(rb_filesystem_encindex()));
879
    rb_ary_push(cache, rb_ary_dup(expanded));
880
    GET_VM()->load_path_expanded_cache = cache;
881
}
882

  
883
// Invalidating $LOAD_PATH methods implementation
884
static VALUE
885
rb_load_path_reset_cache_method(int argc, VALUE *argv, VALUE self)
886
{
887
    rb_reset_expanded_cache();
888
    return rb_call_super(argc, argv);
889
}
890

  
891
// Proxying $LOAD_PATH methods implementation
892
static VALUE
893
rb_load_path_apply_to_cache_method(int argc, VALUE *argv, VALUE self)
894
{
895
    VALUE load_path_expanded = rb_load_path_expanded_cache();
896
    if (RTEST(load_path_expanded)) {
897
	ID func = rb_frame_this_func();
898
	rb_funcall2(load_path_expanded, func, argc, argv);
899
    }
900
    return rb_call_super(argc, argv);
901
}
902

  
903
// Proxying with expansion $LOAD_PATH methods implementation
904
static VALUE
905
rb_load_path_apply_expanded_method(int argc, VALUE *argv, VALUE self)
906
{
907
    // We call methods on cache only if we still in the same working directory
908
    VALUE load_path_expanded = rb_checked_expanded_cache();
909
    if (RTEST(load_path_expanded)) {
910
	int i, has_relative = 0;
911
	ID func = rb_frame_this_func();
912
	VALUE expanded = rb_ary_new2(argc);
913

  
914
	for(i = 0; i < argc; i++) {
915
	    VALUE path = rb_get_path(argv[i]);
916
	    has_relative = has_relative || !rb_is_absolute_path(RSTRING_PTR(path));
917
	    path = rb_file_expand_path(path, Qnil);
918
	    rb_str_freeze(path);
919
	    rb_ary_push(expanded, path);
920
	}
921

  
922
	if (has_relative) {
923
	    VALUE cache = GET_VM()->load_path_expanded_cache;
924
	    char *cwd = my_getcwd();
925
	    rb_ary_store(cache, 0, rb_str_new_cstr(cwd));
926
	    xfree(cwd);
927
	}
928

  
929
	rb_funcall2(load_path_expanded, func, argc, RARRAY_PTR(expanded));
930
	RB_GC_GUARD(expanded);
931
    }
932
    return rb_call_super(argc, argv);
933
}
934
// $LOAD_PATH.concat(ary) - special, we call push(*ary) instead
935
// cause I'm lazy a bit and wish not to rewrite method above second time :)
936
static VALUE
937
rb_load_path_concat(VALUE self, VALUE ary)
938
{
939
    ID push;
940
    CONST_ID(push, "push");
941
    RB_GC_GUARD(ary);
942
    return rb_funcall2(self, push, RARRAY_LEN(ary), RARRAY_PTR(ary));
943
}
944

  
945
static VALUE
946
rb_load_path_init(void)
947
{
948
    const char **name;
949
    VALUE load_path = rb_ary_new();
950
    char *cached_flag;
951

  
952
    cached_flag = getenv("RUBY_CACHED_LOAD_PATH");
953
    if (cached_flag != NULL) {
954
	cached_expanded_load_path = atoi(cached_flag);
955
    }
956

  
957
    // Do all the magick if user did not disable it
958
    // with RUBY_CACHED_LOAD_PATH=0 environment variable
959
    if (cached_expanded_load_path) {
960
	VALUE load_path_c = rb_singleton_class(load_path);
961

  
962
	for(name = load_path_reset_cache_methods; *name; name++ ) {
963
	    rb_define_method(load_path_c, *name, rb_load_path_reset_cache_method, -1);
964
	}
965

  
966
	for(name = load_path_apply_to_cache_methods; *name; name++ ) {
967
	    rb_define_method(load_path_c, *name, rb_load_path_apply_to_cache_method, -1);
968
	}
969

  
970
	for(name = load_path_apply_expanded_methods; *name; name++ ) {
971
	    rb_define_method(load_path_c, *name, rb_load_path_apply_expanded_method, -1);
972
	}
973

  
974
	rb_define_method(load_path_c, "concat", rb_load_path_concat, 1);
975
    }
976

  
977
    rb_reset_expanded_cache();
978

  
979
    return load_path;
980
}
981

  
775 982
void
776 983
Init_load()
777 984
{
......
784 991
    rb_define_hooked_variable(var_load_path, (VALUE*)vm, load_path_getter, rb_gvar_readonly_setter);
785 992
    rb_alias_variable(rb_intern("$-I"), id_load_path);
786 993
    rb_alias_variable(rb_intern("$LOAD_PATH"), id_load_path);
787
    vm->load_path = rb_ary_new();
994
    vm->load_path = rb_load_path_init();
788 995

  
789 996
    rb_define_virtual_variable("$\"", get_loaded_features, 0);
790 997
    rb_define_virtual_variable("$LOADED_FEATURES", get_loaded_features, 0);
ruby.c
210 210
    const char sep = PATH_SEP_CHAR;
211 211
    const char *p, *s;
212 212
    VALUE load_path = GET_VM()->load_path;
213
    ID push;
214
    CONST_ID(push, "<<");
213 215

  
214 216
    p = path;
215 217
    while (*p) {
......
217 219
	    p++;
218 220
	if (!*p) break;
219 221
	for (s = p; *s && *s != sep; s = CharNext(s));
220
	rb_ary_push(load_path, (*filter)(rubylib_mangled_path(p, s - p)));
222
	rb_funcall(load_path, push, 1, (*filter)(rubylib_mangled_path(p, s - p)));
221 223
	p = s;
222 224
    }
223 225
}
......
339 341
ruby_init_loadpath_safe(int safe_level)
340 342
{
341 343
    VALUE load_path;
342
    ID id_initial_load_path_mark;
344
    ID id_initial_load_path_mark, push;
343 345
    extern const char ruby_initial_load_paths[];
344 346
    const char *paths = ruby_initial_load_paths;
345 347
#if defined LOAD_RELATIVE
......
448 450
    }
449 451

  
450 452
    id_initial_load_path_mark = rb_intern_const("@gem_prelude_index");
453
    CONST_ID(push, "<<");
451 454
    while (*paths) {
452 455
	size_t len = strlen(paths);
453 456
	VALUE path = RUBY_RELATIVE(paths, len);
454 457
	rb_ivar_set(path, id_initial_load_path_mark, path);
455
	rb_ary_push(load_path, path);
458
	rb_funcall(load_path, push, 1, path);
456 459
	paths += len + 1;
457 460
    }
458 461

  
vm.c
1570 1570
	RUBY_MARK_UNLESS_NULL(vm->thgroup_default);
1571 1571
	RUBY_MARK_UNLESS_NULL(vm->mark_object_ary);
1572 1572
	RUBY_MARK_UNLESS_NULL(vm->load_path);
1573
	RUBY_MARK_UNLESS_NULL(vm->load_path_expanded_cache);
1573 1574
	RUBY_MARK_UNLESS_NULL(vm->loaded_features);
1574 1575
	RUBY_MARK_UNLESS_NULL(vm->top_self);
1575 1576
	RUBY_MARK_UNLESS_NULL(vm->coverages);
vm_core.h
298 298
    /* load */
299 299
    VALUE top_self;
300 300
    VALUE load_path;
301
    VALUE load_path_expanded_cache;
301 302
    VALUE loaded_features;
302 303
    struct st_table *loading_table;
303 304