From db5265a82c221887faf0dfd0d04e47cf11a08e3a Mon Sep 17 00:00:00 2001 From: nobu Date: Wed, 11 Apr 2012 13:31:23 +0000 Subject: * parse.y (rb_check_id_cstr): new function to check if ID is registered with NUL-terminated C string. * sprintf.c (rb_str_format): avoid inadvertent symbol creation. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@35300 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 7 +++++++ README.EXT | 4 +++- README.EXT.ja | 6 ++++-- include/ruby/encoding.h | 1 + parse.y | 28 ++++++++++++++++++++++++++++ sprintf.c | 7 +++++-- test/ruby/test_sprintf.rb | 10 ++++++++++ 7 files changed, 58 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index ae83e26a75..3130d8554f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Wed Apr 11 22:31:19 2012 Nobuyoshi Nakada + + * parse.y (rb_check_id_cstr): new function to check if ID is + registered with NUL-terminated C string. + + * sprintf.c (rb_str_format): avoid inadvertent symbol creation. + Wed Apr 11 20:28:36 2012 Hiroshi Shirosaki * io.c (rb_io_eof): use eof() instead of io_fillbuf(). It's because diff --git a/README.EXT b/README.EXT index ad45255bca..8368a94708 100644 --- a/README.EXT +++ b/README.EXT @@ -452,12 +452,14 @@ argument by using rb_to_id(VALUE symbol) rb_check_id(volatile VALUE *name) + rb_check_id_cstr(const char *name, long len, rb_encoding *enc) These functions try to convert the argument to a String if it was not -a Symbol nor a String. The latter function stores the converted +a Symbol nor a String. The second function stores the converted result into *name, and returns 0 if the string is not a known symbol. After this function returned a non-zero value, *name is always a Symbol or a String, otherwise it is a String if the result is 0. +The third function takes NUL-terminated C string, not Ruby VALUE. You can convert C ID to Ruby Symbol by using diff --git a/README.EXT.ja b/README.EXT.ja index 6b0cb11757..9ce9c586a1 100644 --- a/README.EXT.ja +++ b/README.EXT.ja @@ -500,12 +500,14 @@ IDとは変数名,メソッド名を表す整数です.Rubyの中では rb_to_id(VALUE symbol) rb_check_id(volatile VALUE *name) + rb_check_id_cstr(const char *name, long len, rb_encoding *enc) もし引数がシンボルでも文字列でもなければ、to_strメソッドで文 -字列に変換しようとします.後者の関数はその変換結果を*nameに保 +字列に変換しようとします.第二の関数はその変換結果を*nameに保 存し,その名前が既知のシンボルでない場合は0を返します.この関 数が0以外を返した場合は*nameは常にシンボルか文字列であり、0を -返した場合は常に文字列です. +返した場合は常に文字列です.第三の関数はRubyの文字列ではなく +NUL終端されたCの文字列を使います. 2.2.3 CからRubyのメソッドを呼び出す diff --git a/include/ruby/encoding.h b/include/ruby/encoding.h index b4e66c1dc4..41640ec7e0 100644 --- a/include/ruby/encoding.h +++ b/include/ruby/encoding.h @@ -220,6 +220,7 @@ char *rb_enc_path_last_separator(const char *,const char *,rb_encoding*); char *rb_enc_path_end(const char *,const char *,rb_encoding*); const char *ruby_enc_find_basename(const char *name, long *baselen, long *alllen, rb_encoding *enc); const char *ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc); +ID rb_check_id_cstr(const char *ptr, long len, rb_encoding *enc); RUBY_EXTERN VALUE rb_cEncoding; #define ENC_DUMMY_FLAG (1<<24) diff --git a/parse.y b/parse.y index eaee57e4ad..5d8cc08a74 100644 --- a/parse.y +++ b/parse.y @@ -10336,6 +10336,34 @@ rb_check_id(volatile VALUE *namep) return (ID)0; } +ID +rb_check_id_cstr(const char *ptr, long len, rb_encoding *enc) +{ + st_data_t id; + struct RString fake_str; + const VALUE name = (VALUE)&fake_str; + fake_str.basic.flags = T_STRING|RSTRING_NOEMBED; + fake_str.basic.klass = rb_cString; + fake_str.as.heap.len = len; + fake_str.as.heap.ptr = (char *)ptr; + fake_str.as.heap.aux.capa = len; + rb_enc_associate(name, enc); + + sym_check_asciionly(name); + + if (st_lookup(global_symbols.sym_id, (st_data_t)name, &id)) + return (ID)id; + + if (rb_is_attrset_name(name)) { + fake_str.as.heap.len = len - 1; + if (st_lookup(global_symbols.sym_id, (st_data_t)name, &id)) { + return rb_id_attrset((ID)id); + } + } + + return (ID)0; +} + int rb_is_const_name(VALUE name) { diff --git a/sprintf.c b/sprintf.c index b9e40f4b87..5927c252dd 100644 --- a/sprintf.c +++ b/sprintf.c @@ -588,8 +588,11 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) rb_enc_raise(enc, rb_eArgError, "named%.*s after <%s>", len, start, rb_id2name(id)); } - id = rb_intern3(start + 1, len - 2 /* without parenthesis */, enc); - nextvalue = GETNAMEARG(ID2SYM(id), start, len, enc); + nextvalue = GETNAMEARG((id = rb_check_id_cstr(start + 1, + len - 2 /* without parenthesis */, + enc), + ID2SYM(id)), + start, len, enc); if (nextvalue == Qundef) { rb_enc_raise(enc, rb_eKeyError, "key%.*s not found", len, start); } diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb index ecbce36942..21329dcd66 100644 --- a/test/ruby/test_sprintf.rb +++ b/test/ruby/test_sprintf.rb @@ -331,6 +331,8 @@ class TestSprintf < Test::Unit::TestCase assert_equal("named after unnumbered(2)", e.message) e = assert_raise(ArgumentError) {sprintf("%s", :key => "value")} assert_equal("named after ", e.message) + e = assert_raise(KeyError) {sprintf("%s", {})} + assert_equal("key not found", e.message) end def test_named_untyped_enc @@ -349,6 +351,9 @@ class TestSprintf < Test::Unit::TestCase e = assert_raise(ArgumentError) {sprintf("%<#{k}>s", k.to_sym => "value")} assert_equal(enc, e.message.encoding) assert_equal("named after <#{k}>", e.message) + e = assert_raise(KeyError) {sprintf("%<#{k}>s", {})} + assert_equal(enc, e.message.encoding) + assert_equal("key<#{k}> not found", e.message) end end @@ -361,6 +366,8 @@ class TestSprintf < Test::Unit::TestCase e = assert_raise(ArgumentError) {sprintf("%{key2}", :key => "value")} assert_equal("named{key2} after ", e.message) assert_equal("value{key2}", sprintf("%{key}{key2}", :key => "value")) + e = assert_raise(KeyError) {sprintf("%{key}", {})} + assert_equal("key{key} not found", e.message) end def test_named_typed_enc @@ -379,6 +386,9 @@ class TestSprintf < Test::Unit::TestCase e = assert_raise(ArgumentError) {sprintf("%<#{k}>{key}s", k.to_sym => "value")} assert_equal(enc, e.message.encoding) assert_equal("named{key} after <#{k}>", e.message) + e = assert_raise(KeyError) {sprintf("%{#{k}}", {})} + assert_equal(enc, e.message.encoding) + assert_equal("key{#{k}} not found", e.message) end end end -- cgit v1.2.3