Skip to content

Commit 7592a00

Browse files
committed
[ruby/json] Handle non-string keys returning immediate values via to_s
We can't directly call `RBASIC_CLASS` as the return value of `to_s` may be an immediate. ruby/json@12dc394d11
1 parent 34f6bea commit 7592a00

File tree

2 files changed

+32
-1
lines changed

2 files changed

+32
-1
lines changed

ext/json/generator/generator.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,21 @@ struct hash_foreach_arg {
789789
int iter;
790790
};
791791

792+
static VALUE
793+
convert_string_subclass(VALUE key)
794+
{
795+
VALUE key_to_s = rb_funcall(key, i_to_s, 0);
796+
797+
if (RB_UNLIKELY(rb_type(key_to_s) != T_STRING)) {
798+
VALUE cname = rb_obj_class(key);
799+
rb_raise(rb_eTypeError,
800+
"can't convert %"PRIsVALUE" to %s (%"PRIsVALUE"#%s gives %"PRIsVALUE")",
801+
cname, "String", cname, "to_s", rb_obj_class(key_to_s));
802+
}
803+
804+
return key_to_s;
805+
}
806+
792807
static int
793808
json_object_i(VALUE key, VALUE val, VALUE _arg)
794809
{
@@ -817,7 +832,7 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
817832
if (RB_LIKELY(RBASIC_CLASS(key) == rb_cString)) {
818833
key_to_s = key;
819834
} else {
820-
key_to_s = rb_funcall(key, i_to_s, 0);
835+
key_to_s = convert_string_subclass(key);
821836
}
822837
break;
823838
case T_SYMBOL:

test/json/json_generator_test.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,22 @@ def test_string_subclass_with_to_s
605605
assert_equal '{"JSONGeneratorTest::StringWithToS#to_s":1}', JSON.generate(StringWithToS.new => 1)
606606
end
607607

608+
def test_string_subclass_with_broken_to_s
609+
klass = Class.new(String) do
610+
def to_s
611+
false
612+
end
613+
end
614+
s = klass.new("test")
615+
assert_equal '["test"]', JSON.generate([s])
616+
617+
omit("Can't figure out how to match behavior in java code") if RUBY_PLATFORM == "java"
618+
619+
assert_raise TypeError do
620+
JSON.generate(s => 1)
621+
end
622+
end
623+
608624
if defined?(JSON::Ext::Generator) and RUBY_PLATFORM != "java"
609625
def test_valid_utf8_in_different_encoding
610626
utf8_string = "€™"

0 commit comments

Comments
 (0)