From: nobu@... Date: 2017-04-28T07:46:23+00:00 Subject: [ruby-core:80917] [Ruby trunk Bug#13397] #object_id should not be signed Issue #13397 has been updated by nobu (Nobuyoshi Nakada). Such feature would be nice, but can't help libraries which support older versions. ```diff commit 3eb27e2637f506a71d8e86d7318e96a6cbcfff1b Author: Nobuyoshi Nakada Date: Fri Apr 28 16:35:48 2017 %I for object ID diff --git a/sprintf.c b/sprintf.c index f2d51f1c76..758e32fed4 100644 --- a/sprintf.c +++ b/sprintf.c @@ -25,6 +25,7 @@ static char *fmt_setup(char*,size_t,int,int,int,int); static char *ruby_ultoa(unsigned long val, char *endp, int base, int octzero); +static char *ruby_ptoa(VALUE val, char *endp, int base, int octzero); static char sign_bits(int base, const char *p) @@ -781,6 +782,18 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) } break; + case 'I': + { + VALUE arg = GETARG(); + prec = (SIZEOF_VOIDP * CHAR_BIT + 3) / 4; + CHECK(prec + 2); + PUSH_("0x", 2); + t = ruby_ptoa(arg, &buf[blen + prec], 16, 0); + if (t > &buf[blen]) memset(&buf[blen], '0', t - &buf[blen]); + blen += prec; + } + break; + case 'd': case 'i': case 'o': @@ -1255,6 +1268,18 @@ ruby_ultoa(unsigned long val, char *endp, int base, int flags) return BSD__ultoa(val, endp, base, octzero, xdigs); } +static char * +ruby_ptoa(VALUE val, char *endp, int base, int flags) +{ + const char *xdigs = lower_hexdigits; + int octzero = flags & FSHARP; +#ifdef _HAVE_SANE_QUAD_ + return BSD__uqtoa(val, endp, base, octzero, xdigs); +#else + return BSD__ultoa(val, endp, base, octzero, xdigs); +#endif +} + int ruby_vsnprintf(char *str, size_t n, const char *fmt, va_list ap) { diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb index 1bf65f1eab..0b7a11a33b 100644 --- a/test/ruby/test_sprintf.rb +++ b/test/ruby/test_sprintf.rb @@ -513,4 +513,9 @@ assert_equal before + 1, after, 'only new string is the created one' assert_equal '1970-01-01', val end + + def test_object_id + x = Object.new + assert_equal(x.to_s, sprintf("#<%s:%I>", x.class, x)) + end end ``` ---------------------------------------- Bug #13397: #object_id should not be signed https://bugs.ruby-lang.org/issues/13397#change-64526 * Author: vo.x (Vit Ondruch) * Status: Open * Priority: Normal * Assignee: * Target version: * ruby -v: ruby 2.4.0p0 (2016-12-24 revision 57164) [i386-linux] * Backport: 2.2: UNKNOWN, 2.3: UNKNOWN, 2.4: UNKNOWN ---------------------------------------- It is surprising that #object_id returns signed value. Let me explain show two examples. Working with 32b Ruby (ruby 2.4.0p0 (2016-12-24 revision 57164) [i386-linux]) to make this issue more apparent. ~~~ $ ruby << \EOR GC.disable 3_000_000.times { p Object.new.inspect } EOR "#" "#" "#" "#" ... snip ... "#" "#" "#" "#" "#" ^C-:2:in `p': Interrupt from -:2:in `block in
' from -:2:in `times' from -:2:in `
' "#" ~~~ In this example, the "object_id", which is part of the inspect object is unsigned, since it is printed using C sprintf with %p format. There are other libraries, which tries to mimic the output [ [1] ]. The implementation is approximately following: ~~~ $ ruby << \EOR GC.disable class A DEFAULT_OBJ_ID_STR_WIDTH = 0.size == 4 ? 7 : 14 def inspect id_str = (object_id << 1).to_s(16).rjust(DEFAULT_OBJ_ID_STR_WIDTH, '0') "#<#{self.class.name}:0x#{id_str}>" end end 3_000_000.times { p A.new.inspect } EOR "#" "#" "#" "#" "#" "#" "#" "#" ... snip ... "#" "#" "#" "#" "#" "#" "#" "#" "#" "#" "#" "#" "#" "#" "#" "#" "#" "#" "#" ^C-:10:in `p': Interrupt from -:10:in `block in
' from -:10:in `times' from -:10:in `
' ~~~ And the output is quite surprising to me. Why the object_id should be signed value? It doesn't make any sense to me. Is this implementation wrong or is Ruby wrong? [1]: https://github.com/ruby-concurrency/concurrent-ruby/issues/547 -- https://bugs.ruby-lang.org/ Unsubscribe: