[前][次][番号順一覧][スレッド一覧]

ruby-changes:54538

From: nobu <ko1@a...>
Date: Tue, 8 Jan 2019 18:08:37 +0900 (JST)
Subject: [ruby-changes:54538] nobu:r66753 (trunk): Defer escaping control char in error messages

nobu	2019-01-08 18:08:31 +0900 (Tue, 08 Jan 2019)

  New Revision: 66753

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=66753

  Log:
    Defer escaping control char in error messages
    
    * eval_error.c (print_errinfo): defer escaping control char in
      error messages until writing to stderr, instead of quoting at
      building the message.  [ruby-core:90853] [Bug #15497]

  Modified files:
    trunk/error.c
    trunk/eval_error.c
    trunk/internal.h
    trunk/ruby.c
    trunk/string.c
    trunk/test/ruby/test_exception.rb
    trunk/test/ruby/test_module.rb
    trunk/test/ruby/test_rubyoptions.rb
Index: string.c
===================================================================
--- string.c	(revision 66752)
+++ string.c	(revision 66753)
@@ -5818,6 +5818,24 @@ rb_str_buf_cat_escaped_char(VALUE result https://github.com/ruby/ruby/blob/trunk/string.c#L5818
     return l;
 }
 
+const char *
+ruby_escaped_char(int c)
+{
+    switch (c) {
+      case '\0': return "\\0";
+      case '\n': return "\\n";
+      case '\r': return "\\r";
+      case '\t': return "\\t";
+      case '\f': return "\\f";
+      case '\013': return "\\v";
+      case '\010': return "\\b";
+      case '\007': return "\\a";
+      case '\033': return "\\e";
+      case '\x7f': return "\\c?";
+    }
+    return NULL;
+}
+
 VALUE
 rb_str_escape(VALUE str)
 {
@@ -5832,7 +5850,8 @@ rb_str_escape(VALUE str) https://github.com/ruby/ruby/blob/trunk/string.c#L5850
     int asciicompat = rb_enc_asciicompat(enc);
 
     while (p < pend) {
-	unsigned int c, cc;
+        unsigned int c;
+        const char *cc;
 	int n = rb_enc_precise_mbclen(p, pend, enc);
         if (!MBCLEN_CHARFOUND_P(n)) {
 	    if (p > prev) str_buf_cat(result, prev, p - prev);
@@ -5849,22 +5868,10 @@ rb_str_escape(VALUE str) https://github.com/ruby/ruby/blob/trunk/string.c#L5868
         n = MBCLEN_CHARFOUND_LEN(n);
 	c = rb_enc_mbc_to_codepoint(p, pend, enc);
 	p += n;
-	switch (c) {
-	  case '\n': cc = 'n'; break;
-	  case '\r': cc = 'r'; break;
-	  case '\t': cc = 't'; break;
-	  case '\f': cc = 'f'; break;
-	  case '\013': cc = 'v'; break;
-	  case '\010': cc = 'b'; break;
-	  case '\007': cc = 'a'; break;
-	  case 033: cc = 'e'; break;
-	  default: cc = 0; break;
-	}
+        cc = ruby_escaped_char(c);
 	if (cc) {
 	    if (p - n > prev) str_buf_cat(result, prev, p - n - prev);
-	    buf[0] = '\\';
-	    buf[1] = (char)cc;
-	    str_buf_cat(result, buf, 2);
+            str_buf_cat(result, cc, strlen(cc));
 	    prev = p;
 	}
 	else if (asciicompat && rb_enc_isascii(c, enc) && ISPRINT(c)) {
Index: test/ruby/test_rubyoptions.rb
===================================================================
--- test/ruby/test_rubyoptions.rb	(revision 66752)
+++ test/ruby/test_rubyoptions.rb	(revision 66753)
@@ -310,7 +310,7 @@ class TestRubyOptions < Test::Unit::Test https://github.com/ruby/ruby/blob/trunk/test/ruby/test_rubyoptions.rb#L310
 
     assert_in_out_err(%W(-\r -e) + [""], "", [], [])
 
-    assert_in_out_err(%W(-\rx), "", [], /invalid option -\\x0D  \(-h will show valid options\) \(RuntimeError\)/)
+    assert_in_out_err(%W(-\rx), "", [], /invalid option -\\r  \(-h will show valid options\) \(RuntimeError\)/)
 
     assert_in_out_err(%W(-\x01), "", [], /invalid option -\\x01  \(-h will show valid options\) \(RuntimeError\)/)
 
Index: test/ruby/test_exception.rb
===================================================================
--- test/ruby/test_exception.rb	(revision 66752)
+++ test/ruby/test_exception.rb	(revision 66753)
@@ -1071,6 +1071,43 @@ $stderr = $stdout; raise "\x82\xa0"') do https://github.com/ruby/ruby/blob/trunk/test/ruby/test_exception.rb#L1071
     end;
   end
 
+  def assert_null_char(src, *args, **opts)
+    begin
+      eval(src)
+    rescue => e
+    end
+    assert_not_nil(e)
+    assert_include(e.message, "\0")
+    assert_in_out_err([], src, [], [], *args, **opts) do |_, err,|
+      err.each do |e|
+        assert_not_include(e, "\0")
+      end
+    end
+    e
+  end
+
+  def test_control_in_message
+    bug7574 = '[ruby-dev:46749]'
+    assert_null_char("#{<<~"begin;"}\n#{<<~'end;'}", bug7574)
+    begin;
+      Object.const_defined?("String\0")
+    end;
+    assert_null_char("#{<<~"begin;"}\n#{<<~'end;'}", bug7574)
+    begin;
+      Object.const_get("String\0")
+    end;
+  end
+
+  def test_encoding_in_message
+    name = "\u{e9}t\u{e9}"
+    e = EnvUtil.with_default_external("US-ASCII") do
+      assert_raise(NameError) do
+        Object.const_get(name)
+      end
+    end
+    assert_include(e.message, name)
+  end
+
   def test_method_missing_reason_clear
     bug10969 = '[ruby-core:68515] [Bug #10969]'
     a = Class.new {def method_missing(*) super end}.new
Index: test/ruby/test_module.rb
===================================================================
--- test/ruby/test_module.rb	(revision 66752)
+++ test/ruby/test_module.rb	(revision 66753)
@@ -745,10 +745,6 @@ class TestModule < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_module.rb#L745
     assert_raise(NameError) { c1.const_get(:foo) }
     bug5084 = '[ruby-dev:44200]'
     assert_raise(TypeError, bug5084) { c1.const_get(1) }
-    bug7574 = '[ruby-dev:46749]'
-    assert_raise_with_message(NameError, "wrong constant name \"String\\u0000\"", bug7574) {
-      Object.const_get("String\0")
-    }
   end
 
   def test_const_defined_invalid_name
@@ -756,10 +752,6 @@ class TestModule < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_module.rb#L752
     assert_raise(NameError) { c1.const_defined?(:foo) }
     bug5084 = '[ruby-dev:44200]'
     assert_raise(TypeError, bug5084) { c1.const_defined?(1) }
-    bug7574 = '[ruby-dev:46749]'
-    assert_raise_with_message(NameError, "wrong constant name \"String\\u0000\"", bug7574) {
-      Object.const_defined?("String\0")
-    }
   end
 
   def test_const_get_no_inherited
Index: internal.h
===================================================================
--- internal.h	(revision 66752)
+++ internal.h	(revision 66753)
@@ -2021,6 +2021,7 @@ VALUE rb_sym_to_proc(VALUE sym); https://github.com/ruby/ruby/blob/trunk/internal.h#L2021
 char *rb_str_to_cstr(VALUE str);
 VALUE rb_str_eql(VALUE str1, VALUE str2);
 VALUE rb_obj_as_string_result(VALUE str, VALUE obj);
+const char *ruby_escaped_char(int c);
 
 /* symbol.c */
 #ifdef RUBY_ENCODING_H
Index: eval_error.c
===================================================================
--- eval_error.c	(revision 66752)
+++ eval_error.c	(revision 66753)
@@ -79,6 +79,44 @@ error_print(rb_execution_context_t *ec) https://github.com/ruby/ruby/blob/trunk/eval_error.c#L79
     rb_ec_error_print(ec, ec->errinfo);
 }
 
+static void
+write_warnq(VALUE out, VALUE str, const char *ptr, long len)
+{
+    if (NIL_P(out)) {
+        const char *beg = ptr;
+        const long olen = len;
+        for (; len > 0; --len, ++ptr) {
+            unsigned char c = *ptr;
+            if (rb_iscntrl(c)) {
+                char buf[5];
+                const char *cc = 0;
+                if (ptr > beg) rb_write_error2(beg, ptr - beg);
+                beg = ptr + 1;
+                cc = ruby_escaped_char(c);
+                if (cc) {
+                    rb_write_error2(cc, strlen(cc));
+                }
+                else {
+                    rb_write_error2(buf, snprintf(buf, sizeof(buf), "\\x%02X", c));
+                }
+            }
+            else if (c == '\\') {
+                rb_write_error2(beg, ptr - beg + 1);
+                beg = ptr;
+            }
+        }
+        if (ptr > beg) {
+            if (beg == RSTRING_PTR(str) && olen == RSTRING_LEN(str))
+                rb_write_error_str(str);
+            else
+                rb_write_error2(beg, ptr - beg);
+        }
+    }
+    else {
+        rb_str_cat(out, ptr, len);
+    }
+}
+
 #define CSI_BEGIN "\033["
 #define CSI_SGR "m"
 
@@ -134,11 +172,11 @@ print_errinfo(const VALUE eclass, const https://github.com/ruby/ruby/blob/trunk/eval_error.c#L172
 	    if (RSTRING_PTR(epath)[0] == '#')
 		epath = 0;
 	    if ((tail = memchr(einfo, '\n', elen)) != 0) {
-		write_warn2(str, einfo, tail - einfo);
+		write_warnq(str, emesg, einfo, tail - einfo);
 		tail++;		/* skip newline */
 	    }
 	    else {
-		write_warn_str(str, emesg);
+		write_warnq(str, emesg, einfo, elen);
 	    }
 	    if (epath) {
 		write_warn(str, " (");
@@ -154,7 +192,7 @@ print_errinfo(const VALUE eclass, const https://github.com/ruby/ruby/blob/trunk/eval_error.c#L192
 	    }
 	    if (tail && einfo+elen > tail) {
 		if (!highlight) {
-		    write_warn2(str, tail, einfo+elen-tail);
+		    write_warnq(str, emesg, tail, einfo+elen-tail);
 		    if (einfo[elen-1] != '\n') write_warn2(str, "\n", 1);
 		}
 		else {
@@ -164,7 +202,7 @@ print_errinfo(const VALUE eclass, const https://github.com/ruby/ruby/blob/trunk/eval_error.c#L202
 			tail = memchr(einfo, '\n', elen);
 			if (!tail || tail > einfo) {
 			    write_warn(str, bold);
-			    write_warn2(str, einfo, tail ? tail-einfo : elen);
+			    write_warnq(str, emesg, einfo, tail ? tail-einfo : elen);
 			    write_warn(str, reset);
 			    if (!tail) {
 				write_warn2(str, "\n", 1);
@@ -174,7 +212,7 @@ print_errinfo(const VALUE eclass, const https://github.com/ruby/ruby/blob/trunk/eval_error.c#L212
 			elen -= tail - einfo;
 			einfo = tail;
 			do ++tail; while (tail < einfo+elen && *tail == '\n');
-			write_warn2(str, einfo, tail-einfo);
+			write_warnq(str, emesg, einfo, tail-einfo);
 			elen -= tail - einfo;
 			einfo = tail;
 		    }
Index: ruby.c
===================================================================
--- ruby.c	(revision 66752)
+++ ruby.c	(revision 66753)
@@ -1367,16 +1367,9 @@ proc_options(long argc, char **argv, rub https://github.com/ruby/ruby/blob/trunk/ruby.c#L1367
 
 	  default:
 	    {
-		if (ISPRINT(*s)) {
-                    rb_raise(rb_eRuntimeError,
+                rb_raise(rb_eRuntimeError,
 			"invalid option -%c  (-h will show valid options)",
                         (int)(unsigned char)*s);
-		}
-		else {
-                    rb_raise(rb_eRuntimeError,
-			"invalid option -\\x%02X  (-h will show valid options)",
-                        (int)(unsigned char)*s);
-		}
 	    }
 	    goto switch_end;
 
Index: error.c
===================================================================
--- error.c	(revision 66752)
+++ error.c	(revision 66753)
@@ -1675,7 +1675,6 @@ name_err_mesg_to_str(VALUE obj) https://github.com/ruby/ruby/blob/trunk/error.c#L1675
 		d = rb_any_to_s(obj);
 	    }
 	    singleton = (RSTRING_LEN(d) > 0 && RSTRING_PTR(d)[0] == '#');
-	    d = QUOTE(d);
 	    break;
 	}
 	if (!singleton) {
@@ -1685,7 +1684,7 @@ name_err_mesg_to_str(VALUE obj) https://github.com/ruby/ruby/blob/trunk/error.c#L1684
 	else {
 	    c = s = FAKE_CSTR(&s_str, "");
 	}
-	args[0] = QUOTE(rb_obj_as_string(ptr[NAME_ERR_MESG__NAME]));
+	args[0] = rb_obj_as_string(ptr[NAME_ERR_MESG__NAME]);
 	args[1] = d;
 	args[2] = s;
 	args[3] = c;

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]