Permalink
Browse files

Support to_path and to_str method of load path

* file.c (rb_get_path_check_to_string): extract from rb_get_path_check().
  Add an argument to check to have to_path method.

* file.c (rb_get_path_check_convert): extract from rb_get_path_check().

* file.c (rb_get_path_check_to_string): move from rb_get_path_check().

* file.c (rb_file_expand_path_fast): remove check_expand_path_args().
  Instead we call it in load.c.

* internal.h (rb_get_path_check_to_string, rb_get_path_check_convert):
  add declarations.

* load.c (rb_construct_expanded_load_path): add checks in
  rb_get_path_check(). We don't freeze non string object and expand it
  every times. We expand string object which has to_path method every times.

* load.c (rb_get_expanded_load_path): expand non-cacheable objects every
  times.

* load.c (rb_feature_provided): call rb_get_path() since we changed
  rb_file_expand_path_fast() not to call it.

* load.c (Init_load): rename to load_path_check_cache for changed usage.
* vm.c (rb_vm_mark): ditto.
* vm_core.h (rb_vm_struct): ditto.

* test/ruby/test_require.rb (TestRequire): add tests for compatibility
  of to_path and to_str in $LOAD_PATH.
  • Loading branch information...
1 parent 988df8c commit 480fcd43ce844fef456b958352c9b8e605a6ff0e @shirosaki committed Nov 1, 2012
Showing with 168 additions and 40 deletions.
  1. +18 −3 file.c
  2. +2 −0 internal.h
  3. +67 −24 load.c
  4. +79 −11 test/ruby/test_require.rb
  5. +1 −1 vm.c
  6. +1 −1 vm_core.h
View
21 file.c
@@ -167,8 +167,8 @@ check_path_encoding(VALUE str)
return enc;
}
-static VALUE
-rb_get_path_check(VALUE obj, int level)
+VALUE
+rb_get_path_check_to_string(VALUE obj, int level, int *has_to_path)
{
VALUE tmp;
ID to_path;
@@ -182,8 +182,16 @@ rb_get_path_check(VALUE obj, int level)
if (tmp == Qundef) {
tmp = obj;
}
+ else {
+ *has_to_path = 1;
+ }
StringValue(tmp);
+ return tmp;
+}
+VALUE
+rb_get_path_check_convert(VALUE obj, VALUE tmp, int level)
+{
tmp = file_path_convert(tmp);
if (obj != tmp && insecure_obj_p(tmp, level)) {
rb_insecure_operation();
@@ -195,6 +203,14 @@ rb_get_path_check(VALUE obj, int level)
return rb_str_new4(tmp);
}
+static VALUE
+rb_get_path_check(VALUE obj, int level)
+{
+ int has_to_path = 0;
+ VALUE tmp = rb_get_path_check_to_string(obj, level, &has_to_path);
+ return rb_get_path_check_convert(obj, tmp, level);
+}
+
VALUE
rb_get_path_no_checksafe(VALUE obj)
{
@@ -3255,7 +3271,6 @@ rb_file_expand_path(VALUE fname, VALUE dname)
VALUE
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());
}
View
@@ -108,6 +108,8 @@ void rb_file_const(const char*, VALUE);
int rb_file_load_ok(const char *);
VALUE rb_file_expand_path_fast(VALUE, VALUE);
VALUE rb_file_expand_path_internal(VALUE, VALUE, int, int, VALUE);
+VALUE rb_get_path_check_to_string(VALUE, int, int *);
+VALUE rb_get_path_check_convert(VALUE, VALUE, int);
void Init_File(void);
#ifdef _WIN32
View
91 load.c
@@ -33,33 +33,58 @@ rb_get_load_path(void)
return load_path;
}
+enum expand_type {
+ 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 object which has to_path method and
+ expand it every times. We ensure $LOAD_PATH string objects are frozen.
+ */
static void
-rb_construct_expanded_load_path(int only_tilde, int only_relative, int *has_relative)
+rb_construct_expanded_load_path(int 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_new2(RARRAY_LEN(load_path));
for (i = 0; i < RARRAY_LEN(load_path); ++i) {
VALUE path, as_str, expanded_path;
+ int is_string, non_cache, has_to_path = 0;
char *as_cstr;
as_str = path = RARRAY_PTR(load_path)[i];
- StringValue(as_str);
- as_cstr = StringValuePtr(as_str);
- if (only_relative && rb_is_absolute_path(as_cstr)) {
- rb_ary_push(ary, RARRAY_PTR(expanded_load_path)[i]);
- continue;
- }
- else if (only_tilde && (!as_cstr[0] || as_cstr[0] != '~')) {
- rb_ary_push(ary, RARRAY_PTR(expanded_load_path)[i]);
- continue;
+ is_string = (TYPE(path) == T_STRING) ? 1 : 0;
+ as_str = rb_get_path_check_to_string(path, level, &has_to_path);
+ non_cache = (!is_string || has_to_path) ? 1 : 0;
+ 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;
- rb_obj_freeze(path);
+ if (!*has_non_cache && non_cache)
+ *has_non_cache = 1;
+ /* Freeze only string object. We expand other objects every times. */
+ 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);
@@ -82,25 +107,43 @@ 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;
- rb_construct_expanded_load_path(0, 0, &has_relative);
- vm->load_path_cwd = has_relative ? load_path_getcwd() : 0;
+ 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 and object which has to_path method. */
+ 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_cwd) {
+ else if (vm->load_path_check_cache) {
+ int has_relative = 1, has_non_cache = 1;
VALUE cwd = load_path_getcwd();
- int has_relative = 1;
- if (!rb_str_equal(vm->load_path_cwd, cwd)) {
+ if (!rb_str_equal(vm->load_path_check_cache, cwd)) {
/* Current working directory or filesystem encoding was changed.
- Expand relative load path again. */
- vm->load_path_cwd = cwd;
- rb_construct_expanded_load_path(0, 1, &has_relative);
+ 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) */
- rb_construct_expanded_load_path(1, 0, &has_relative);
+ /* 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;
@@ -429,7 +472,7 @@ rb_feature_provided(const char *feature, const char **loading)
if (*feature == '.' &&
(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);
}
if (ext && !strchr(ext, '/')) {
@@ -1000,7 +1043,7 @@ Init_load()
vm->load_path = rb_ary_new();
vm->expanded_load_path = rb_ary_new();
vm->load_path_snapshot = rb_ary_new();
- vm->load_path_cwd = 0;
+ vm->load_path_check_cache = 0;
rb_define_virtual_variable("$\"", get_loaded_features, 0);
rb_define_virtual_variable("$LOADED_FEATURES", get_loaded_features, 0);
View
@@ -458,26 +458,25 @@ def test_require_changed_current_dir
}
end
- def test_require_unmodified_lood_path
+ def test_require_unmodified_load_path
Dir.mktmpdir {|tmp|
Dir.chdir(tmp) {
open("foo.rb", "w") {}
assert_in_out_err([], <<-INPUT, %w(:ok), [])
- $: << "#{tmp}"
- a = 123
- $: << a
- begin
- require "foo"
- rescue TypeError
+ a = Object.new
+ def a.to_str
+ "#{tmp}"
end
- p :ok if $:.pop == a
+ $: << a
+ require "foo"
+ last_path = $:.pop
+ p :ok if last_path == a && last_path.class == Object
INPUT
}
}
end
def test_require_changed_home
- home = ENV['HOME']
Dir.mktmpdir {|tmp|
Dir.chdir(tmp) {
open("foo.rb", "w") {}
@@ -492,7 +491,76 @@ def test_require_changed_home
INPUT
}
}
- ensure
- ENV['HOME'] = home
+ end
+
+ def test_require_to_path_redifined_in_load_path
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("foo.rb", "w") {}
+ assert_in_out_err([], <<-INPUT, %w(:ok), [])
+ a = Object.new
+ def a.to_path
+ "bar"
+ end
+ $: << a
+ begin
+ require "foo"
+ p :ng
+ rescue LoadError
+ end
+ def a.to_path
+ "#{tmp}"
+ end
+ p :ok if require "foo"
+ INPUT
+ }
+ }
+ end
+
+ def test_require_string_to_path_in_load_path
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("foo.rb", "w") {}
+ assert_in_out_err([], <<-INPUT, %w(:ok), [])
+ a = "foo"
+ ENV['FOO'] = "bar"
+ def a.to_path
+ ENV['FOO']
+ end
+ $: << a
+ begin
+ require "foo"
+ p :ng
+ rescue LoadError
+ end
+ ENV['FOO'] = "#{tmp}"
+ p :ok if require "foo"
+ INPUT
+ }
+ }
+ end
+
+ def test_require_to_str_redefined_in_load_path
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("foo.rb", "w") {}
+ assert_in_out_err([], <<-INPUT, %w(:ok), [])
+ a = Object.new
+ def a.to_str
+ "foo"
+ end
+ $: << a
+ begin
+ require "foo"
+ p :ng
+ rescue LoadError
+ end
+ def a.to_str
+ "#{tmp}"
+ end
+ p :ok if require "foo"
+ INPUT
+ }
+ }
end
end
View
2 vm.c
@@ -1508,7 +1508,7 @@ rb_vm_mark(void *ptr)
RUBY_MARK_UNLESS_NULL(vm->mark_object_ary);
RUBY_MARK_UNLESS_NULL(vm->load_path);
RUBY_MARK_UNLESS_NULL(vm->load_path_snapshot);
- RUBY_MARK_UNLESS_NULL(vm->load_path_cwd);
+ RUBY_MARK_UNLESS_NULL(vm->load_path_check_cache);
RUBY_MARK_UNLESS_NULL(vm->expanded_load_path);
RUBY_MARK_UNLESS_NULL(vm->loaded_features);
RUBY_MARK_UNLESS_NULL(vm->loaded_features_snapshot);
View
@@ -355,7 +355,7 @@ typedef struct rb_vm_struct {
VALUE top_self;
VALUE load_path;
VALUE load_path_snapshot;
- VALUE load_path_cwd;
+ VALUE load_path_check_cache;
VALUE expanded_load_path;
VALUE loaded_features;
VALUE loaded_features_snapshot;

0 comments on commit 480fcd4

Please sign in to comment.