From 4eb51dfc9e67683a1a03fdf302d5ddd95cad716a Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 5 Sep 2024 00:47:06 +0900 Subject: [PATCH] Merge JSON 2.7.2 for Ruby 3.3 (#11541) Merge JSON 2.7.2 --- ext/json/generator/generator.c | 42 +++++++++++++--- ext/json/lib/json/add/ostruct.rb | 7 ++- ext/json/lib/json/common.rb | 3 +- ext/json/lib/json/generic_object.rb | 8 ++- ext/json/lib/json/version.rb | 2 +- test/json/json_addition_test.rb | 2 +- test/json/json_generic_object_test.rb | 2 +- test/json/json_parser_test.rb | 71 ++++++++++++++------------- 8 files changed, 90 insertions(+), 47 deletions(-) diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c index a71acfbb76..6d78284bc4 100644 --- a/ext/json/generator/generator.c +++ b/ext/json/generator/generator.c @@ -867,7 +867,7 @@ json_object_i(VALUE key, VALUE val, VALUE _arg) if (klass == rb_cString) { key_to_s = key; } else if (klass == rb_cSymbol) { - key_to_s = rb_id2str(SYM2ID(key)); + key_to_s = rb_sym2str(key); } else { key_to_s = rb_funcall(key, i_to_s, 0); } @@ -892,7 +892,6 @@ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_S struct hash_foreach_arg arg; if (max_nesting != 0 && depth > max_nesting) { - fbuffer_free(buffer); rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth); } fbuffer_append_char(buffer, '{'); @@ -927,7 +926,6 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St long depth = ++state->depth; int i, j; if (max_nesting != 0 && depth > max_nesting) { - fbuffer_free(buffer); rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth); } fbuffer_append_char(buffer, '['); @@ -1020,10 +1018,8 @@ static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_St VALUE tmp = rb_funcall(obj, i_to_s, 0); if (!allow_nan) { if (isinf(value)) { - fbuffer_free(buffer); rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp)); } else if (isnan(value)) { - fbuffer_free(buffer); rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp)); } } @@ -1096,11 +1092,45 @@ static FBuffer *cState_prepare_buffer(VALUE self) return buffer; } +struct generate_json_data { + FBuffer *buffer; + VALUE vstate; + JSON_Generator_State *state; + VALUE obj; +}; + +static VALUE generate_json_try(VALUE d) +{ + struct generate_json_data *data = (struct generate_json_data *)d; + + generate_json(data->buffer, data->vstate, data->state, data->obj); + + return Qnil; +} + +static VALUE generate_json_rescue(VALUE d, VALUE exc) +{ + struct generate_json_data *data = (struct generate_json_data *)d; + fbuffer_free(data->buffer); + + rb_exc_raise(exc); + + return Qundef; +} + static VALUE cState_partial_generate(VALUE self, VALUE obj) { FBuffer *buffer = cState_prepare_buffer(self); GET_STATE(self); - generate_json(buffer, self, state, obj); + + struct generate_json_data data = { + .buffer = buffer, + .vstate = self, + .state = state, + .obj = obj + }; + rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data); + return fbuffer_to_s(buffer); } diff --git a/ext/json/lib/json/add/ostruct.rb b/ext/json/lib/json/add/ostruct.rb index 498de17178..1e6f408248 100644 --- a/ext/json/lib/json/add/ostruct.rb +++ b/ext/json/lib/json/add/ostruct.rb @@ -2,7 +2,10 @@ unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED require 'json' end -require 'ostruct' +begin + require 'ostruct' +rescue LoadError +end class OpenStruct @@ -48,4 +51,4 @@ class OpenStruct def to_json(*args) as_json.to_json(*args) end -end +end if defined?(::OpenStruct) diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb index 090066012d..95098d3bb4 100644 --- a/ext/json/lib/json/common.rb +++ b/ext/json/lib/json/common.rb @@ -1,8 +1,9 @@ #frozen_string_literal: false require 'json/version' -require 'json/generic_object' module JSON + autoload :GenericObject, 'json/generic_object' + NOT_SET = Object.new.freeze private_constant :NOT_SET diff --git a/ext/json/lib/json/generic_object.rb b/ext/json/lib/json/generic_object.rb index 108309db26..56efda6495 100644 --- a/ext/json/lib/json/generic_object.rb +++ b/ext/json/lib/json/generic_object.rb @@ -1,5 +1,9 @@ #frozen_string_literal: false -require 'ostruct' +begin + require 'ostruct' +rescue LoadError + warn "JSON::GenericObject requires 'ostruct'. Please install it with `gem install ostruct`." +end module JSON class GenericObject < OpenStruct @@ -67,5 +71,5 @@ module JSON def to_json(*a) as_json.to_json(*a) end - end + end if defined?(::OpenStruct) end diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb index b43ceecdcd..836f47edf4 100644 --- a/ext/json/lib/json/version.rb +++ b/ext/json/lib/json/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module JSON # JSON version - VERSION = '2.7.1' + VERSION = '2.7.2' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: diff --git a/test/json/json_addition_test.rb b/test/json/json_addition_test.rb index 2877bef7d8..3a7a58176a 100644 --- a/test/json/json_addition_test.rb +++ b/test/json/json_addition_test.rb @@ -190,7 +190,7 @@ class JSONAdditionTest < Test::Unit::TestCase # XXX this won't work; o.foo = { :bar => true } o.foo = { 'bar' => true } assert_equal o, parse(JSON(o), :create_additions => true) - end + end if defined?(::OpenStruct) def test_set s = Set.new([:a, :b, :c, :a]) diff --git a/test/json/json_generic_object_test.rb b/test/json/json_generic_object_test.rb index c4d391208c..d6d7e30816 100644 --- a/test/json/json_generic_object_test.rb +++ b/test/json/json_generic_object_test.rb @@ -79,4 +79,4 @@ class JSONGenericObjectTest < Test::Unit::TestCase ensure JSON::GenericObject.json_creatable = false end -end +end if defined?(JSON::GenericObject) diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index cc9a74a95b..49c7c8565d 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -3,7 +3,10 @@ require_relative 'test_helper' require 'stringio' require 'tempfile' -require 'ostruct' +begin + require 'ostruct' +rescue LoadError +end begin require 'bigdecimal' rescue LoadError @@ -412,21 +415,6 @@ EOT end end - class SubOpenStruct < OpenStruct - def [](k) - __send__(k) - end - - def []=(k, v) - @item_set = true - __send__("#{k}=", v) - end - - def item_set? - @item_set - end - end - def test_parse_object_custom_hash_derived_class res = parse('{"foo":"bar"}', :object_class => SubHash) assert_equal({"foo" => "bar"}, res) @@ -434,24 +422,41 @@ EOT assert res.item_set? end - def test_parse_object_custom_non_hash_derived_class - res = parse('{"foo":"bar"}', :object_class => SubOpenStruct) - assert_equal "bar", res.foo - assert_equal(SubOpenStruct, res.class) - assert res.item_set? - end + if defined?(::OpenStruct) + class SubOpenStruct < OpenStruct + def [](k) + __send__(k) + end - def test_parse_generic_object - res = parse( - '{"foo":"bar", "baz":{}}', - :object_class => JSON::GenericObject - ) - assert_equal(JSON::GenericObject, res.class) - assert_equal "bar", res.foo - assert_equal "bar", res["foo"] - assert_equal "bar", res[:foo] - assert_equal "bar", res.to_hash[:foo] - assert_equal(JSON::GenericObject, res.baz.class) + def []=(k, v) + @item_set = true + __send__("#{k}=", v) + end + + def item_set? + @item_set + end + end + + def test_parse_object_custom_non_hash_derived_class + res = parse('{"foo":"bar"}', :object_class => SubOpenStruct) + assert_equal "bar", res.foo + assert_equal(SubOpenStruct, res.class) + assert res.item_set? + end + + def test_parse_generic_object + res = parse( + '{"foo":"bar", "baz":{}}', + :object_class => JSON::GenericObject + ) + assert_equal(JSON::GenericObject, res.class) + assert_equal "bar", res.foo + assert_equal "bar", res["foo"] + assert_equal "bar", res[:foo] + assert_equal "bar", res.to_hash[:foo] + assert_equal(JSON::GenericObject, res.baz.class) + end end def test_generate_core_subclasses_with_new_to_json