summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS7
-rw-r--r--internal.h1
-rw-r--r--load.c35
-rw-r--r--spec/ruby/core/module/autoload_spec.rb12
-rw-r--r--spec/ruby/core/module/fixtures/classes.rb6
-rw-r--r--test/ruby/test_autoload.rb4
-rw-r--r--variable.c7
7 files changed, 63 insertions, 9 deletions
diff --git a/NEWS b/NEWS
index 80ec1975d2..d4e943d9e1 100644
--- a/NEWS
+++ b/NEWS
@@ -123,6 +123,13 @@ Integer::
0b01001100[2...6] #=> 0b0011
^^^^
+Module::
+
+ Modified method::
+
+ * Module#autoload? now takes an +inherit+ optional argument, like as
+ Module#const_defined?. [Feature #15777]
+
Regexp / String::
* Update Unicode version and Emoji version from 11.0.0 to
diff --git a/internal.h b/internal.h
index b1e6aec0dd..7443278a0f 100644
--- a/internal.h
+++ b/internal.h
@@ -2200,6 +2200,7 @@ VALUE rb_search_class_path(VALUE);
VALUE rb_attr_delete(VALUE, ID);
VALUE rb_ivar_lookup(VALUE obj, ID id, VALUE undef);
void rb_autoload_str(VALUE mod, ID id, VALUE file);
+VALUE rb_autoload_at_p(VALUE, ID, VALUE);
void rb_deprecate_constant(VALUE mod, const char *name);
NORETURN(VALUE rb_mod_const_missing(VALUE,VALUE));
rb_gvar_getter_t *rb_gvar_getter_function_of(const struct rb_global_entry *);
diff --git a/load.c b/load.c
index 576464fb68..a58498555c 100644
--- a/load.c
+++ b/load.c
@@ -1142,25 +1142,42 @@ rb_mod_autoload(VALUE mod, VALUE sym, VALUE file)
/*
* call-seq:
- * mod.autoload?(name) -> String or nil
+ * mod.autoload?(name, inherit=true) -> String or nil
*
* Returns _filename_ to be loaded if _name_ is registered as
- * +autoload+ in the namespace of _mod_.
+ * +autoload+ in the namespace of _mod_ or one of its ancestors.
*
* module A
* end
* A.autoload(:B, "b")
* A.autoload?(:B) #=> "b"
+ *
+ * If +inherit+ is false, the lookup only checks the autoloads in the receiver:
+ *
+ * class A
+ * autoload :CONST, "const.rb"
+ * end
+ *
+ * class B < A
+ * end
+ *
+ * B.autoload?(:CONST) #=> "const.rb", found in A (ancestor)
+ * B.autoload?(:CONST, false) #=> nil, not found in B itself
+ *
*/
static VALUE
-rb_mod_autoload_p(VALUE mod, VALUE sym)
+rb_mod_autoload_p(int argc, VALUE *argv, VALUE mod)
{
+ rb_check_arity(argc, 1, 2);
+ VALUE sym = argv[0];
+ VALUE recur = (argc == 1) ? Qtrue : argv[1];
+
ID id = rb_check_id(&sym);
if (!id) {
return Qnil;
}
- return rb_autoload_p(mod, id);
+ return rb_autoload_at_p(mod, id, recur);
}
/*
@@ -1186,7 +1203,7 @@ rb_f_autoload(VALUE obj, VALUE sym, VALUE file)
/*
* call-seq:
- * autoload?(name) -> String or nil
+ * autoload?(name, inherit=true) -> String or nil
*
* Returns _filename_ to be loaded if _name_ is registered as
* +autoload+.
@@ -1196,14 +1213,14 @@ rb_f_autoload(VALUE obj, VALUE sym, VALUE file)
*/
static VALUE
-rb_f_autoload_p(VALUE obj, VALUE sym)
+rb_f_autoload_p(int argc, VALUE *argv, VALUE obj)
{
/* use rb_vm_cbase() as same as rb_f_autoload. */
VALUE klass = rb_vm_cbase();
if (NIL_P(klass)) {
return Qnil;
}
- return rb_mod_autoload_p(klass, sym);
+ return rb_mod_autoload_p(argc, argv, klass);
}
void
@@ -1233,9 +1250,9 @@ Init_load(void)
rb_define_global_function("require", rb_f_require, 1);
rb_define_global_function("require_relative", rb_f_require_relative, 1);
rb_define_method(rb_cModule, "autoload", rb_mod_autoload, 2);
- rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, 1);
+ rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, -1);
rb_define_global_function("autoload", rb_f_autoload, 2);
- rb_define_global_function("autoload?", rb_f_autoload_p, 1);
+ rb_define_global_function("autoload?", rb_f_autoload_p, -1);
ruby_dln_librefs = rb_ary_tmp_new(0);
rb_gc_register_mark_object(ruby_dln_librefs);
diff --git a/spec/ruby/core/module/autoload_spec.rb b/spec/ruby/core/module/autoload_spec.rb
index eaad4b8df6..09fbafd51c 100644
--- a/spec/ruby/core/module/autoload_spec.rb
+++ b/spec/ruby/core/module/autoload_spec.rb
@@ -11,6 +11,18 @@ describe "Module#autoload?" do
it "returns nil if no file has been registered for a constant" do
ModuleSpecs::Autoload.autoload?(:Manualload).should be_nil
end
+
+ it "returns the name of the file that will be autoloaded if an ancestor defined that autoload" do
+ ModuleSpecs::Autoload::Parent.autoload :AnotherAutoload, "another_autoload.rb"
+ ModuleSpecs::Autoload::Child.autoload?(:AnotherAutoload).should == "another_autoload.rb"
+ end
+
+ ruby_version_is "2.7" do
+ it "returns nil if an ancestor defined that autoload but recursion is disabled" do
+ ModuleSpecs::Autoload::Parent.autoload :AnotherAutoload, "another_autoload.rb"
+ ModuleSpecs::Autoload::Child.autoload?(:AnotherAutoload, false).should be_nil
+ end
+ end
end
describe "Module#autoload" do
diff --git a/spec/ruby/core/module/fixtures/classes.rb b/spec/ruby/core/module/fixtures/classes.rb
index f93c39683e..ef70eaf9cf 100644
--- a/spec/ruby/core/module/fixtures/classes.rb
+++ b/spec/ruby/core/module/fixtures/classes.rb
@@ -386,6 +386,12 @@ module ModuleSpecs
end
end
+ class Parent
+ end
+
+ class Child < Parent
+ end
+
module FromThread
module A
autoload :B, fixture(__FILE__, "autoload_empty.rb")
diff --git a/test/ruby/test_autoload.rb b/test/ruby/test_autoload.rb
index d4e8f20899..0428e10525 100644
--- a/test/ruby/test_autoload.rb
+++ b/test/ruby/test_autoload.rb
@@ -54,6 +54,10 @@ p Foo::Bar
assert_equal(true, b.const_defined?(:X))
assert_equal(tmpfile, a.autoload?(:X), bug4565)
assert_equal(tmpfile, b.autoload?(:X), bug4565)
+ assert_equal(tmpfile, a.autoload?(:X, false))
+ assert_equal(tmpfile, a.autoload?(:X, nil))
+ assert_nil(b.autoload?(:X, false))
+ assert_nil(b.autoload?(:X, nil))
assert_equal(true, a.const_defined?("Y"))
assert_equal(true, b.const_defined?("Y"))
assert_equal(tmpfile2, a.autoload?("Y"))
diff --git a/variable.c b/variable.c
index be0eee1df3..616c4852c0 100644
--- a/variable.c
+++ b/variable.c
@@ -2332,10 +2332,17 @@ rb_autoload_load(VALUE mod, ID id)
VALUE
rb_autoload_p(VALUE mod, ID id)
{
+ return rb_autoload_at_p(mod, id, Qtrue);
+}
+
+VALUE
+rb_autoload_at_p(VALUE mod, ID id, VALUE recur)
+{
VALUE load;
struct autoload_data_i *ele;
while (!autoload_defined_p(mod, id)) {
+ if (!RTEST(recur)) return Qnil;
mod = RCLASS_SUPER(mod);
if (!mod) return Qnil;
}