Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions ext/json/ext/generator/generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)));
}
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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;
}
15 changes: 11 additions & 4 deletions ext/json/ext/parser/parser.c
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down
13 changes: 10 additions & 3 deletions ext/json/ext/parser/parser.rl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down
Binary file modified json.gemspec
Binary file not shown.
4 changes: 2 additions & 2 deletions lib/json/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
6 changes: 3 additions & 3 deletions tests/json_common_interface_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
43 changes: 43 additions & 0 deletions tests/json_generator_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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
28 changes: 14 additions & 14 deletions tests/json_parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down