diff --git a/NEWS.md b/NEWS.md index dafce164998b18..6c9de939704722 100644 --- a/NEWS.md +++ b/NEWS.md @@ -197,7 +197,7 @@ The following default gems are updated. * resolv 0.6.2 * stringio 3.1.8.dev * strscan 3.1.6.dev -* uri 1.0.3 +* uri 1.0.4 * weakref 0.1.4 The following bundled gems are added. diff --git a/internal/class.h b/internal/class.h index 29b3526cf55cfd..f5c5142b452d78 100644 --- a/internal/class.h +++ b/internal/class.h @@ -336,10 +336,14 @@ RCLASS_SET_NAMESPACE_CLASSEXT(VALUE obj, const rb_namespace_t *ns, rb_classext_t return first_set; } +#define VM_ASSERT_NAMESPACEABLE_TYPE(klass) \ + VM_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE) || RB_TYPE_P(klass, T_ICLASS), "%s is not namespaceable type", rb_type_str(BUILTIN_TYPE(klass))) + static inline bool RCLASS_PRIME_CLASSEXT_READABLE_P(VALUE klass) { - VM_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE) || RB_TYPE_P(klass, T_ICLASS)); + VM_ASSERT(klass != 0, "klass should be a valid object"); + VM_ASSERT_NAMESPACEABLE_TYPE(klass); // if the lookup table exists, then it means the prime classext is NOT directly readable. return !FL_TEST_RAW(klass, RCLASS_NAMESPACEABLE) || RCLASS_CLASSEXT_TBL(klass) == NULL; } @@ -347,15 +351,16 @@ RCLASS_PRIME_CLASSEXT_READABLE_P(VALUE klass) static inline bool RCLASS_PRIME_CLASSEXT_WRITABLE_P(VALUE klass) { - VM_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE) || RB_TYPE_P(klass, T_ICLASS)); + VM_ASSERT(klass != 0, "klass should be a valid object"); + VM_ASSERT_NAMESPACEABLE_TYPE(klass); return FL_TEST(klass, RCLASS_PRIME_CLASSEXT_WRITABLE); } static inline void RCLASS_SET_PRIME_CLASSEXT_WRITABLE(VALUE klass, bool writable) { - VM_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE) || RB_TYPE_P(klass, T_ICLASS)); - + VM_ASSERT(klass != 0, "klass should be a valid object"); + VM_ASSERT_NAMESPACEABLE_TYPE(klass); if (writable) { FL_SET(klass, RCLASS_PRIME_CLASSEXT_WRITABLE); } @@ -429,6 +434,7 @@ RCLASS_EXT_WRITABLE_LOOKUP(VALUE obj, const rb_namespace_t *ns) ext = rb_class_duplicate_classext(RCLASS_EXT_PRIME(obj), obj, ns); first_set = RCLASS_SET_NAMESPACE_CLASSEXT(obj, ns, ext); if (first_set) { + // TODO: are there any case that a class/module become non-writable after its birthtime? RCLASS_SET_PRIME_CLASSEXT_WRITABLE(obj, false); } } diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb index d811c5b9440bf1..634da49fe98f90 100644 --- a/lib/uri/generic.rb +++ b/lib/uri/generic.rb @@ -186,18 +186,18 @@ def initialize(scheme, if arg_check self.scheme = scheme - self.userinfo = userinfo self.hostname = host self.port = port + self.userinfo = userinfo self.path = path self.query = query self.opaque = opaque self.fragment = fragment else self.set_scheme(scheme) - self.set_userinfo(userinfo) self.set_host(host) self.set_port(port) + self.set_userinfo(userinfo) self.set_path(path) self.query = query self.set_opaque(opaque) @@ -511,7 +511,7 @@ def set_userinfo(user, password = nil) user, password = split_userinfo(user) end @user = user - @password = password if password + @password = password [@user, @password] end @@ -522,7 +522,7 @@ def set_userinfo(user, password = nil) # See also URI::Generic.user=. # def set_user(v) - set_userinfo(v, @password) + set_userinfo(v, nil) v end protected :set_user @@ -574,6 +574,12 @@ def password @password end + # Returns the authority info (array of user, password, host and + # port), if any is set. Or returns +nil+. + def authority + return @user, @password, @host, @port if @user || @password || @host || @port + end + # Returns the user component after URI decoding. def decoded_user URI.decode_uri_component(@user) if @user @@ -615,6 +621,13 @@ def set_host(v) end protected :set_host + # Protected setter for the authority info (+user+, +password+, +host+ + # and +port+). If +port+ is +nil+, +default_port+ will be set. + # + protected def set_authority(user, password, host, port = nil) + @user, @password, @host, @port = user, password, host, port || self.default_port + end + # # == Args # @@ -639,6 +652,7 @@ def set_host(v) def host=(v) check_host(v) set_host(v) + set_userinfo(nil) v end @@ -729,6 +743,7 @@ def set_port(v) def port=(v) check_port(v) set_port(v) + set_userinfo(nil) port end @@ -1121,7 +1136,7 @@ def merge(oth) base = self.dup - authority = rel.userinfo || rel.host || rel.port + authority = rel.authority # RFC2396, Section 5.2, 2) if (rel.path.nil? || rel.path.empty?) && !authority && !rel.query @@ -1134,9 +1149,7 @@ def merge(oth) # RFC2396, Section 5.2, 4) if authority - base.set_userinfo(rel.userinfo) - base.set_host(rel.host) - base.set_port(rel.port || base.default_port) + base.set_authority(*authority) base.set_path(rel.path) elsif base.path && rel.path base.set_path(merge_path(base.path, rel.path)) diff --git a/lib/uri/version.rb b/lib/uri/version.rb index b6a8ce15435601..60ada985f91ca8 100644 --- a/lib/uri/version.rb +++ b/lib/uri/version.rb @@ -1,6 +1,6 @@ module URI # :stopdoc: - VERSION_CODE = '010003'.freeze + VERSION_CODE = '010004'.freeze VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze # :startdoc: end diff --git a/namespace.c b/namespace.c index b48914ac0ab004..32249d00bc161b 100644 --- a/namespace.c +++ b/namespace.c @@ -755,13 +755,15 @@ rb_initialize_main_namespace(void) VM_ASSERT(NAMESPACE_OBJ_P(main_ns)); ns = rb_get_namespace_t(main_ns); ns->ns_object = main_ns; - ns->ns_id = namespace_generate_id(); ns->is_user = true; ns->is_optional = false; rb_const_set(rb_cNamespace, rb_intern("MAIN"), main_ns); vm->main_namespace = main_namespace = ns; + + // create the writable classext of ::Object explicitly to finalize the set of visible top-level constants + RCLASS_EXT_WRITABLE_IN_NS(rb_cObject, ns); } static VALUE @@ -843,11 +845,15 @@ rb_namespace_s_main(VALUE recv) static const char * classname(VALUE klass) { - VALUE p = RCLASS_CLASSPATH(klass); + VALUE p; + if (!klass) { + return "Qfalse"; + } + p = RCLASSEXT_CLASSPATH(RCLASS_EXT_PRIME(klass)); if (RTEST(p)) return RSTRING_PTR(p); if (RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE) || RB_TYPE_P(klass, T_ICLASS)) - return RSTRING_PTR(rb_inspect(klass)); + return "AnyClassValue"; return "NonClassValue"; } @@ -971,6 +977,27 @@ rb_f_dump_classext(VALUE recv, VALUE klass) return res; } +static VALUE +rb_namespace_root_p(VALUE namespace) +{ + const rb_namespace_t *ns = (const rb_namespace_t *)rb_get_namespace_t(namespace); + return RBOOL(NAMESPACE_ROOT_P(ns)); +} + +static VALUE +rb_namespace_main_p(VALUE namespace) +{ + const rb_namespace_t *ns = (const rb_namespace_t *)rb_get_namespace_t(namespace); + return RBOOL(NAMESPACE_MAIN_P(ns)); +} + +static VALUE +rb_namespace_user_p(VALUE namespace) +{ + const rb_namespace_t *ns = (const rb_namespace_t *)rb_get_namespace_t(namespace); + return RBOOL(NAMESPACE_USER_P(ns)); +} + #endif /* RUBY_DEBUG */ /* @@ -1008,6 +1035,10 @@ Init_Namespace(void) rb_define_singleton_method(rb_cNamespace, "root", rb_namespace_s_root, 0); rb_define_singleton_method(rb_cNamespace, "main", rb_namespace_s_main, 0); rb_define_global_function("dump_classext", rb_f_dump_classext, 1); + + rb_define_method(rb_cNamespace, "root?", rb_namespace_root_p, 0); + rb_define_method(rb_cNamespace, "main?", rb_namespace_main_p, 0); + rb_define_method(rb_cNamespace, "user?", rb_namespace_user_p, 0); #endif } diff --git a/ruby.c b/ruby.c index 8c1fb5719bc02e..05a9fd4191d8bb 100644 --- a/ruby.c +++ b/ruby.c @@ -1826,10 +1826,13 @@ ruby_opt_init(ruby_cmdline_options_t *opt) GET_VM()->running = 1; memset(ruby_vm_redefined_flag, 0, sizeof(ruby_vm_redefined_flag)); + ruby_init_prelude(); + + /* Initialize the main namespace after loading libraries (including rubygems) + * to enable those in both root and main */ if (rb_namespace_available()) rb_initialize_main_namespace(); rb_namespace_init_done(); - ruby_init_prelude(); // Initialize JITs after ruby_init_prelude() because JITing prelude is typically not optimal. #if USE_YJIT diff --git a/spec/ruby/library/uri/set_component_spec.rb b/spec/ruby/library/uri/set_component_spec.rb index 642a5d6fcf0543..1d4165c5351b13 100644 --- a/spec/ruby/library/uri/set_component_spec.rb +++ b/spec/ruby/library/uri/set_component_spec.rb @@ -6,25 +6,27 @@ it "conforms to the MatzRuby tests" do uri = URI.parse('http://foo:bar@baz') (uri.user = 'oof').should == 'oof' - uri.to_s.should == 'http://oof:bar@baz' - (uri.password = 'rab').should == 'rab' - uri.to_s.should == 'http://oof:rab@baz' - (uri.userinfo = 'foo').should == 'foo' - uri.to_s.should == 'http://foo:rab@baz' - (uri.userinfo = ['foo', 'bar']).should == ['foo', 'bar'] - uri.to_s.should == 'http://foo:bar@baz' - (uri.userinfo = ['foo']).should == ['foo'] - uri.to_s.should == 'http://foo:bar@baz' - (uri.host = 'zab').should == 'zab' - uri.to_s.should == 'http://foo:bar@zab' - (uri.port = 8080).should == 8080 - uri.to_s.should == 'http://foo:bar@zab:8080' - (uri.path = '/').should == '/' - uri.to_s.should == 'http://foo:bar@zab:8080/' - (uri.query = 'a=1').should == 'a=1' - uri.to_s.should == 'http://foo:bar@zab:8080/?a=1' - (uri.fragment = 'b123').should == 'b123' - uri.to_s.should == 'http://foo:bar@zab:8080/?a=1#b123' + version_is(URI::VERSION, "1.0.4") do + uri.to_s.should == 'http://oof@baz' + (uri.password = 'rab').should == 'rab' + uri.to_s.should == 'http://oof:rab@baz' + (uri.userinfo = 'foo').should == 'foo' + uri.to_s.should == 'http://foo@baz' + (uri.userinfo = ['foo', 'bar']).should == ['foo', 'bar'] + uri.to_s.should == 'http://foo:bar@baz' + (uri.userinfo = ['foo']).should == ['foo'] + uri.to_s.should == 'http://foo@baz' + (uri.host = 'zab').should == 'zab' + uri.to_s.should == 'http://zab' + (uri.port = 8080).should == 8080 + uri.to_s.should == 'http://zab:8080' + (uri.path = '/').should == '/' + uri.to_s.should == 'http://zab:8080/' + (uri.query = 'a=1').should == 'a=1' + uri.to_s.should == 'http://zab:8080/?a=1' + (uri.fragment = 'b123').should == 'b123' + uri.to_s.should == 'http://zab:8080/?a=1#b123' + end uri = URI.parse('http://example.com') -> { uri.password = 'bar' }.should raise_error(URI::InvalidURIError) diff --git a/template/fake.rb.in b/template/fake.rb.in index a02582a9dc012f..fed640aee7e02b 100644 --- a/template/fake.rb.in +++ b/template/fake.rb.in @@ -49,6 +49,7 @@ class Object else%><%=v.inspect%><%end%> % } end +v=$VERBOSE;$VERBOSE=nil;module Ruby; end;$VERBOSE=v module Ruby constants.each {|n| remove_const n} % arg['versions'].each {|n, v| diff --git a/test/ruby/test_namespace.rb b/test/ruby/test_namespace.rb index cd593068675de6..af308ab15c25e3 100644 --- a/test/ruby/test_namespace.rb +++ b/test/ruby/test_namespace.rb @@ -3,6 +3,10 @@ require 'test/unit' class TestNamespace < Test::Unit::TestCase + EXPERIMENTAL_WARNINGS = [ + "warning: Namespace is experimental, and the behavior may change in the future!", + "See doc/namespace.md for known issues, etc." + ].join("\n") ENV_ENABLE_NAMESPACE = {'RUBY_NAMESPACE' => '1'} def setup @@ -534,6 +538,51 @@ def test_load_path_and_loaded_features assert !$LOADED_FEATURES.include?(File.join(namespace_dir, 'blank2.rb')) end + def test_prelude_gems_and_loaded_features + assert_in_out_err([ENV_ENABLE_NAMESPACE, "--enable=gems"], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error| + begin; + puts ["before:", $LOADED_FEATURES.select{ it.end_with?("/bundled_gems.rb") }&.first].join + puts ["before:", $LOADED_FEATURES.select{ it.end_with?("/error_highlight.rb") }&.first].join + + require "error_highlight" + + puts ["after:", $LOADED_FEATURES.select{ it.end_with?("/bundled_gems.rb") }&.first].join + puts ["after:", $LOADED_FEATURES.select{ it.end_with?("/error_highlight.rb") }&.first].join + end; + + # No additional warnings except for experimental warnings + assert_includes error.join("\n"), EXPERIMENTAL_WARNINGS + assert_equal error.size, 2 + + assert_includes output.grep(/^before:/).join("\n"), '/bundled_gems.rb' + assert_includes output.grep(/^before:/).join("\n"), '/error_highlight.rb' + assert_includes output.grep(/^after:/).join("\n"), '/bundled_gems.rb' + assert_includes output.grep(/^after:/).join("\n"), '/error_highlight.rb' + end + end + + def test_prelude_gems_and_loaded_features_with_disable_gems + assert_in_out_err([ENV_ENABLE_NAMESPACE, "--disable=gems"], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error| + begin; + puts ["before:", $LOADED_FEATURES.select{ it.end_with?("/bundled_gems.rb") }&.first].join + puts ["before:", $LOADED_FEATURES.select{ it.end_with?("/error_highlight.rb") }&.first].join + + require "error_highlight" + + puts ["after:", $LOADED_FEATURES.select{ it.end_with?("/bundled_gems.rb") }&.first].join + puts ["after:", $LOADED_FEATURES.select{ it.end_with?("/error_highlight.rb") }&.first].join + end; + + assert_includes error.join("\n"), EXPERIMENTAL_WARNINGS + assert_equal error.size, 2 + + refute_includes output.grep(/^before:/).join("\n"), '/bundled_gems.rb' + refute_includes output.grep(/^before:/).join("\n"), '/error_highlight.rb' + refute_includes output.grep(/^after:/).join("\n"), '/bundled_gems.rb' + assert_includes output.grep(/^after:/).join("\n"), '/error_highlight.rb' + end + end + def test_eval_basic pend unless Namespace.enabled? diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb index 4ee6633c2b6560..8126cb3c268b37 100644 --- a/test/ruby/test_rubyoptions.rb +++ b/test/ruby/test_rubyoptions.rb @@ -210,6 +210,8 @@ def test_enable assert_in_out_err(%w(--enable foobarbazqux -e) + [""], "", [], /unknown argument for --enable: 'foobarbazqux'/) assert_in_out_err(%w(--enable), "", [], /missing argument for --enable/) + assert_in_out_err(%w(-e) + ['p defined? Gem'], "", %w["constant"], [], gems: true) + assert_in_out_err(%w(-e) + ['p defined? Gem'], "", %w["constant"], [], gems: nil) end def test_disable @@ -219,7 +221,7 @@ def test_disable assert_in_out_err(%w(--disable foobarbazqux -e) + [""], "", [], /unknown argument for --disable: 'foobarbazqux'/) assert_in_out_err(%w(--disable), "", [], /missing argument for --disable/) - assert_in_out_err(%w(-e) + ['p defined? Gem'], "", ["nil"], []) + assert_in_out_err(%w(-e) + ['p defined? Gem'], "", ["nil"], [], gems: false) assert_in_out_err(%w(--disable-did_you_mean -e) + ['p defined? DidYouMean'], "", ["nil"], []) assert_in_out_err(%w(-e) + ['p defined? DidYouMean'], "", ["nil"], []) end diff --git a/test/uri/test_generic.rb b/test/uri/test_generic.rb index c725116e964e50..94eea71b511161 100644 --- a/test/uri/test_generic.rb +++ b/test/uri/test_generic.rb @@ -283,6 +283,9 @@ def test_merge_authority u0 = URI.parse('http://new.example.org/path') u1 = u.merge('//new.example.org/path') assert_equal(u0, u1) + u0 = URI.parse('http://other@example.net') + u1 = u.merge('//other@example.net') + assert_equal(u0, u1) end def test_route @@ -748,17 +751,18 @@ def test_join def test_set_component uri = URI.parse('http://foo:bar@baz') assert_equal('oof', uri.user = 'oof') - assert_equal('http://oof:bar@baz', uri.to_s) + assert_equal('http://oof@baz', uri.to_s) assert_equal('rab', uri.password = 'rab') assert_equal('http://oof:rab@baz', uri.to_s) assert_equal('foo', uri.userinfo = 'foo') - assert_equal('http://foo:rab@baz', uri.to_s) + assert_equal('http://foo@baz', uri.to_s) assert_equal(['foo', 'bar'], uri.userinfo = ['foo', 'bar']) assert_equal('http://foo:bar@baz', uri.to_s) assert_equal(['foo'], uri.userinfo = ['foo']) - assert_equal('http://foo:bar@baz', uri.to_s) + assert_equal('http://foo@baz', uri.to_s) assert_equal('zab', uri.host = 'zab') - assert_equal('http://foo:bar@zab', uri.to_s) + assert_equal('http://zab', uri.to_s) + uri.userinfo = ['foo', 'bar'] uri.port = "" assert_nil(uri.port) uri.port = "80" @@ -768,7 +772,8 @@ def test_set_component uri.port = " 080 " assert_equal(80, uri.port) assert_equal(8080, uri.port = 8080) - assert_equal('http://foo:bar@zab:8080', uri.to_s) + assert_equal('http://zab:8080', uri.to_s) + uri = URI.parse('http://foo:bar@zab:8080') assert_equal('/', uri.path = '/') assert_equal('http://foo:bar@zab:8080/', uri.to_s) assert_equal('a=1', uri.query = 'a=1') diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index 47cc6574c878d1..934ab080372d67 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -97,9 +97,11 @@ def assert_file end def assert_in_out_err(args, test_stdin = "", test_stdout = [], test_stderr = [], message = nil, - success: nil, failed: nil, **opt) + success: nil, failed: nil, gems: false, **opt) args = Array(args).dup - args.insert((Hash === args[0] ? 1 : 0), '--disable=gems') + unless gems.nil? + args.insert((Hash === args[0] ? 1 : 0), "--#{gems ? 'enable' : 'disable'}=gems") + end stdout, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, true, true, **opt) desc = failed[status, message, stderr] if failed desc ||= FailDesc[status, message, stderr]