diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index cc8c3a7c..881435e3 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -15,7 +15,7 @@ static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, #endif mFloat, mString, mString_Extend, mTrueClass, mFalseClass, mNilClass, eGeneratorError, - eNestingError, CRegexp_MULTILINE, CJSON_SAFE_STATE_PROTOTYPE, + eNestingError, i_SAFE_STATE_PROTOTYPE; static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, @@ -696,7 +696,7 @@ static VALUE cState_aref(VALUE self, VALUE name) if (RTEST(rb_funcall(self, i_respond_to_p, 1, name))) { return rb_funcall(self, i_send, 1, name); } else { - return rb_ivar_get(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name))); + return rb_attr_get(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name))); } } @@ -846,11 +846,20 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St fbuffer_append_char(buffer, ']'); } +#ifdef HAVE_RUBY_ENCODING_H +static int enc_utf8_compatible_p(rb_encoding *enc) +{ + if (enc == rb_usascii_encoding()) return 1; + if (enc == rb_utf8_encoding()) return 1; + return 0; +} +#endif + static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { fbuffer_append_char(buffer, '"'); #ifdef HAVE_RUBY_ENCODING_H - if (!rb_enc_str_asciicompat_p(obj)) { + if (!enc_utf8_compatible_p(rb_enc_get(obj))) { obj = rb_str_encode(obj, CEncoding_UTF_8, 0, Qnil); } #endif @@ -1073,10 +1082,8 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts) } else if (rb_obj_is_kind_of(opts, rb_cHash)) { return rb_funcall(self, i_new, 1, opts); } else { - if (NIL_P(CJSON_SAFE_STATE_PROTOTYPE)) { - CJSON_SAFE_STATE_PROTOTYPE = rb_const_get(mJSON, i_SAFE_STATE_PROTOTYPE); - } - return rb_funcall(CJSON_SAFE_STATE_PROTOTYPE, i_dup, 0); + VALUE prototype = rb_const_get(mJSON, i_SAFE_STATE_PROTOTYPE); + return rb_funcall(prototype, i_dup, 0); } } @@ -1392,6 +1399,8 @@ void Init_generator(void) eGeneratorError = rb_path2class("JSON::GeneratorError"); eNestingError = rb_path2class("JSON::NestingError"); + rb_gc_register_mark_object(eGeneratorError); + rb_gc_register_mark_object(eNestingError); cState = rb_define_class_under(mGenerator, "State", rb_cObject); rb_define_alloc_func(cState, cState_s_allocate); @@ -1457,7 +1466,6 @@ void Init_generator(void) mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass"); rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1); - CRegexp_MULTILINE = rb_const_get(rb_cRegexp, rb_intern("MULTILINE")); i_to_s = rb_intern("to_s"); i_to_json = rb_intern("to_json"); i_new = rb_intern("new"); @@ -1488,5 +1496,4 @@ void Init_generator(void) i_encode = rb_intern("encode"); #endif i_SAFE_STATE_PROTOTYPE = rb_intern("SAFE_STATE_PROTOTYPE"); - CJSON_SAFE_STATE_PROTOTYPE = Qnil; } diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 63b895eb..0f98cf98 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -1,4 +1,4 @@ - +/* This file is automatically generated from parser.rl by using ragel */ #line 1 "parser.rl" #include "../fbuffer/fbuffer.h" #include "parser.h" @@ -27,7 +27,7 @@ enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...) /* unicode */ -static const char digit_values[256] = { +static const signed char digit_values[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, @@ -46,7 +46,7 @@ static const char digit_values[256] = { static UTF32 unescape_unicode(const unsigned char *p) { - char b; + signed char b; UTF32 result = 0; b = digit_values[p[0]]; if (b < 0) return UNI_REPLACEMENT_CHAR; @@ -1833,7 +1833,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } else { json->max_nesting = 100; json->allow_nan = 0; - json->create_additions = 1; + json->create_additions = 0; json->create_id = rb_funcall(mJSON, i_create_id, 0); json->object_class = Qnil; json->array_class = Qnil; @@ -2089,14 +2089,21 @@ void Init_parser(void) cParser = rb_define_class_under(mExt, "Parser", rb_cObject); eParserError = rb_path2class("JSON::ParserError"); eNestingError = rb_path2class("JSON::NestingError"); + rb_gc_register_mark_object(eParserError); + rb_gc_register_mark_object(eNestingError); rb_define_alloc_func(cParser, cJSON_parser_s_allocate); rb_define_method(cParser, "initialize", cParser_initialize, -1); rb_define_method(cParser, "parse", cParser_parse, 0); rb_define_method(cParser, "source", cParser_source, 0); CNaN = rb_const_get(mJSON, rb_intern("NaN")); + rb_gc_register_mark_object(CNaN); + CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); + rb_gc_register_mark_object(CInfinity); + CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity")); + rb_gc_register_mark_object(CMinusInfinity); i_json_creatable_p = rb_intern("json_creatable?"); i_json_create = rb_intern("json_create"); diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index e7738d65..6b38bb28 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -25,7 +25,7 @@ enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...) /* unicode */ -static const char digit_values[256] = { +static const signed char digit_values[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, @@ -44,7 +44,7 @@ static const char digit_values[256] = { static UTF32 unescape_unicode(const unsigned char *p) { - char b; + signed char b; UTF32 result = 0; b = digit_values[p[0]]; if (b < 0) return UNI_REPLACEMENT_CHAR; @@ -728,7 +728,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } else { json->max_nesting = 100; json->allow_nan = 0; - json->create_additions = 1; + json->create_additions = 0; json->create_id = rb_funcall(mJSON, i_create_id, 0); json->object_class = Qnil; json->array_class = Qnil; @@ -849,14 +849,21 @@ void Init_parser(void) cParser = rb_define_class_under(mExt, "Parser", rb_cObject); eParserError = rb_path2class("JSON::ParserError"); eNestingError = rb_path2class("JSON::NestingError"); + rb_gc_register_mark_object(eParserError); + rb_gc_register_mark_object(eNestingError); rb_define_alloc_func(cParser, cJSON_parser_s_allocate); rb_define_method(cParser, "initialize", cParser_initialize, -1); rb_define_method(cParser, "parse", cParser_parse, 0); rb_define_method(cParser, "source", cParser_source, 0); CNaN = rb_const_get(mJSON, rb_intern("NaN")); + rb_gc_register_mark_object(CNaN); + CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); + rb_gc_register_mark_object(CInfinity); + CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity")); + rb_gc_register_mark_object(CMinusInfinity); i_json_creatable_p = rb_intern("json_creatable?"); i_json_create = rb_intern("json_create"); diff --git a/json.gemspec b/json.gemspec index 6e7873fd..51dd8f02 100644 Binary files a/json.gemspec and b/json.gemspec differ diff --git a/lib/json/common.rb b/lib/json/common.rb index 7cc85291..3be9fd8d 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -153,7 +153,7 @@ class MissingUnicodeSupport < JSONError; end # * *object_class*: Defaults to Hash # * *array_class*: Defaults to Array def parse(source, opts = {}) - Parser.new(source, opts).parse + Parser.new(source, **(opts||{})).parse end # Parse the JSON document _source_ into a Ruby data structure and return it. @@ -176,7 +176,7 @@ def parse!(source, opts = {}) :max_nesting => false, :allow_nan => true }.merge(opts) - Parser.new(source, opts).parse + Parser.new(source, **(opts||{})).parse end # Generate a JSON document from the Ruby data structure _obj_ and return diff --git a/tests/json_common_interface_test.rb b/tests/json_common_interface_test.rb index de88c6e8..53f335ed 100644 --- a/tests/json_common_interface_test.rb +++ b/tests/json_common_interface_test.rb @@ -27,15 +27,15 @@ def test_index end def test_parser - assert_match /::Parser\z/, JSON.parser.name + assert_match(/::Parser\z/, JSON.parser.name) end def test_generator - assert_match /::Generator\z/, JSON.generator.name + assert_match(/::Generator\z/, JSON.generator.name) end def test_state - assert_match /::Generator::State\z/, JSON.state.name + assert_match(/::Generator::State\z/, JSON.state.name) end def test_create_id diff --git a/tests/json_generator_test.rb b/tests/json_generator_test.rb index 86be398f..10cbcd17 100644 --- a/tests/json_generator_test.rb +++ b/tests/json_generator_test.rb @@ -40,6 +40,43 @@ def setup EOT end + def silence + v = $VERBOSE + $VERBOSE = nil + yield + ensure + $VERBOSE = v + end + + def test_remove_const_segv + stress = GC.stress + const = JSON::SAFE_STATE_PROTOTYPE.dup + + bignum_too_long_to_embed_as_string = 1234567890123456789012345 + expect = bignum_too_long_to_embed_as_string.to_s + GC.stress = true + + 10.times do |i| + tmp = bignum_too_long_to_embed_as_string.to_json + raise "'\#{expect}' is expected, but '\#{tmp}'" unless tmp == expect + end + + silence do + JSON.const_set :SAFE_STATE_PROTOTYPE, nil + end + + 10.times do |i| + assert_raise TypeError do + bignum_too_long_to_embed_as_string.to_json + end + end + ensure + GC.stress = stress + silence do + JSON.const_set :SAFE_STATE_PROTOTYPE, const + end + end if JSON.const_defined?("Ext") + def test_generate json = generate(@hash) assert_equal(parse(@json2), parse(json)) @@ -374,4 +411,10 @@ def to_s; self; end assert_equal '["foo"]', JSON.generate([s.new('foo')]) end end + + if defined?(Encoding) + def test_nonutf8_encoding + assert_equal("\"5\u{b0}\"", "5\xb0".force_encoding("iso-8859-1").to_json) + end + end end diff --git a/tests/json_parser_test.rb b/tests/json_parser_test.rb index 5f454eb1..9946dd93 100644 --- a/tests/json_parser_test.rb +++ b/tests/json_parser_test.rb @@ -91,27 +91,27 @@ def test_parse_numbers assert_raise(JSON::ParserError) { parse('+23') } assert_raise(JSON::ParserError) { parse('.23') } assert_raise(JSON::ParserError) { parse('023') } - assert_equal 23, parse('23') - assert_equal -23, parse('-23') - assert_equal_float 3.141, parse('3.141') - assert_equal_float -3.141, parse('-3.141') - assert_equal_float 3.141, parse('3141e-3') - assert_equal_float 3.141, parse('3141.1e-3') - assert_equal_float 3.141, parse('3141E-3') - assert_equal_float 3.141, parse('3141.0E-3') - assert_equal_float -3.141, parse('-3141.0e-3') - assert_equal_float -3.141, parse('-3141e-3') + assert_equal(23, parse('23')) + assert_equal(-23, parse('-23')) + assert_equal_float(3.141, parse('3.141')) + assert_equal_float(-3.141, parse('-3.141')) + assert_equal_float(3.141, parse('3141e-3')) + assert_equal_float(3.141, parse('3141.1e-3')) + assert_equal_float(3.141, parse('3141E-3')) + assert_equal_float(3.141, parse('3141.0E-3')) + assert_equal_float(-3.141, parse('-3141.0e-3')) + assert_equal_float(-3.141, parse('-3141e-3')) assert_raise(ParserError) { parse('NaN') } assert parse('NaN', :allow_nan => true).nan? assert_raise(ParserError) { parse('Infinity') } - assert_equal 1.0/0, parse('Infinity', :allow_nan => true) + assert_equal(1.0/0, parse('Infinity', :allow_nan => true)) assert_raise(ParserError) { parse('-Infinity') } - assert_equal -1.0/0, parse('-Infinity', :allow_nan => true) + assert_equal(-1.0/0, parse('-Infinity', :allow_nan => true)) end def test_parse_bigdecimals - assert_equal(BigDecimal, JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"].class) - assert_equal(BigDecimal.new("0.901234567890123456789E1"),JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"] ) + assert_equal(BigDecimal, JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"].class) + assert_equal(BigDecimal("0.901234567890123456789E1"),JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"] ) end if Array.method_defined?(:permutation)