diff options
-rw-r--r-- | NEWS | 7 | ||||
-rw-r--r-- | internal.h | 1 | ||||
-rw-r--r-- | load.c | 35 | ||||
-rw-r--r-- | spec/ruby/core/module/autoload_spec.rb | 12 | ||||
-rw-r--r-- | spec/ruby/core/module/fixtures/classes.rb | 6 | ||||
-rw-r--r-- | test/ruby/test_autoload.rb | 4 | ||||
-rw-r--r-- | variable.c | 7 |
7 files changed, 63 insertions, 9 deletions
@@ -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 *); @@ -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; } |