From b35b7a1ef25347735a6bb7c28ab7e77afea1d856 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Mon, 18 Oct 2021 08:50:10 -0700 Subject: Allow Kernel#load to load code into a specified module Instead of always using a new anonymous module for Kernel#load if the wrap argument is not false/nil, use the given module if a module is provided. Implements [Feature #6210] --- load.c | 26 +++++++++++++++----------- test/ruby/test_require.rb | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/load.c b/load.c index 58f71b9d22..a2b9da48fb 100644 --- a/load.c +++ b/load.c @@ -657,7 +657,7 @@ load_iseq_eval(rb_execution_context_t *ec, VALUE fname) } static inline enum ruby_tag_type -load_wrapping(rb_execution_context_t *ec, VALUE fname) +load_wrapping(rb_execution_context_t *ec, VALUE fname, VALUE load_wrapper) { enum ruby_tag_type state; rb_thread_t *th = rb_ec_thread_ptr(ec); @@ -669,9 +669,9 @@ load_wrapping(rb_execution_context_t *ec, VALUE fname) ec->errinfo = Qnil; /* ensure */ - /* load in anonymous module as toplevel */ + /* load in module as toplevel */ th->top_self = rb_obj_clone(rb_vm_top_self()); - th->top_wrapper = rb_module_new(); + th->top_wrapper = load_wrapper; rb_extend_object(th->top_self, th->top_wrapper); EC_PUSH_TAG(ec); @@ -703,12 +703,15 @@ raise_load_if_failed(rb_execution_context_t *ec, enum ruby_tag_type state) } static void -rb_load_internal(VALUE fname, int wrap) +rb_load_internal(VALUE fname, VALUE wrap) { rb_execution_context_t *ec = GET_EC(); enum ruby_tag_type state = TAG_NONE; - if (wrap) { - state = load_wrapping(ec, fname); + if (RTEST(wrap)) { + if (!RB_TYPE_P(wrap, T_MODULE)) { + wrap = rb_module_new(); + } + state = load_wrapping(ec, fname, wrap); } else { load_iseq_eval(ec, fname); @@ -721,7 +724,7 @@ rb_load(VALUE fname, int wrap) { VALUE tmp = rb_find_file(FilePathValue(fname)); if (!tmp) load_failed(fname); - rb_load_internal(tmp, wrap); + rb_load_internal(tmp, RBOOL(wrap)); } void @@ -763,9 +766,10 @@ rb_load_protect(VALUE fname, int wrap, int *pstate) * * If the optional _wrap_ parameter is +true+, the loaded script will * be executed under an anonymous module, protecting the calling - * program's global namespace. In no circumstance will any local - * variables in the loaded file be propagated to the loading - * environment. + * program's global namespace. If the optional _wrap_ parameter is a + * module, the loaded script will be executed under the given module. + * In no circumstance will any local variables in the loaded file be + * propagated to the loading environment. */ static VALUE @@ -785,7 +789,7 @@ rb_f_load(int argc, VALUE *argv, VALUE _) load_failed(orig_fname); path = fname; } - rb_load_internal(path, RTEST(wrap)); + rb_load_internal(path, wrap); RUBY_DTRACE_HOOK(LOAD_RETURN, RSTRING_PTR(orig_fname)); diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb index 7a9faf18f9..77cdae64d9 100644 --- a/test/ruby/test_require.rb +++ b/test/ruby/test_require.rb @@ -367,6 +367,38 @@ class TestRequire < Test::Unit::TestCase } end + def test_load_into_module + Tempfile.create(["test_ruby_test_require", ".rb"]) {|t| + t.puts "def b; 1 end" + t.puts "class Foo" + t.puts " def c; 2 end" + t.puts "end" + t.close + + m = Module.new + load(t.path, m) + assert_equal([:b], m.private_instance_methods(false)) + c = Class.new do + include m + public :b + end + assert_equal(1, c.new.b) + assert_equal(2, m::Foo.new.c) + } + end + + def test_load_wrap_nil + Dir.mktmpdir do |tmp| + File.write("#{tmp}/1.rb", "class LoadWrapNil; end\n") + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") + path = ""#{tmp.dump}"/1.rb" + begin; + load path, nil + assert_instance_of(Class, LoadWrapNil) + end; + end + end + def test_load_ospath bug = '[ruby-list:49994] path in ospath' base = "test_load\u{3042 3044 3046 3048 304a}".encode(Encoding::Windows_31J) -- cgit v1.2.3