Skip to content

Commit

Permalink
Add an optional inherit argument to Module#autoload?
Browse files Browse the repository at this point in the history
[Feature #15777]

Closes: ruby#2173
  • Loading branch information
byroot authored and nobu committed Jun 21, 2019
1 parent 887163b commit fb85a42
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 9 deletions.
7 changes: 7 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);
Expand Down
35 changes: 26 additions & 9 deletions load.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/*
Expand All @@ -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+.
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions spec/ruby/core/module/autoload_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@
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
Expand Down
6 changes: 6 additions & 0 deletions spec/ruby/core/module/fixtures/classes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,12 @@ def self.use_ex1
end
end

class Parent
end

class Child < Parent
end

module FromThread
module A
autoload :B, fixture(__FILE__, "autoload_empty.rb")
Expand Down
4 changes: 4 additions & 0 deletions test/ruby/test_autoload.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ def test_autoload_p
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"))
Expand Down
7 changes: 7 additions & 0 deletions variable.c
Original file line number Diff line number Diff line change
Expand Up @@ -2331,11 +2331,18 @@ 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;
}
Expand Down

0 comments on commit fb85a42

Please sign in to comment.