summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/json/generator/generator.c34
-rwxr-xr-xtest/json/json_coder_test.rb15
2 files changed, 36 insertions, 13 deletions
diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c
index b2fcd2b294..119b1dfdaa 100644
--- a/ext/json/generator/generator.c
+++ b/ext/json/generator/generator.c
@@ -841,15 +841,19 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
return ST_CONTINUE;
}
-static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
+static inline long increase_depth(JSON_Generator_State *state)
{
- long max_nesting = state->max_nesting;
long depth = ++state->depth;
- int j;
-
- if (max_nesting != 0 && depth > max_nesting) {
+ if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
}
+ return depth;
+}
+
+static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
+{
+ int j;
+ long depth = increase_depth(state);
if (RHASH_SIZE(obj) == 0) {
fbuffer_append(buffer, "{}", 2);
@@ -879,12 +883,8 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
{
- long max_nesting = state->max_nesting;
- long depth = ++state->depth;
int i, j;
- if (max_nesting != 0 && depth > max_nesting) {
- rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
- }
+ long depth = increase_depth(state);
if (RARRAY_LEN(obj) == 0) {
fbuffer_append(buffer, "[]", 2);
@@ -1031,13 +1031,21 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
{
double value = RFLOAT_VALUE(obj);
char allow_nan = state->allow_nan;
- VALUE tmp = rb_funcall(obj, i_to_s, 0);
if (!allow_nan) {
if (isinf(value) || isnan(value)) {
- raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", tmp);
+ if (state->strict && state->as_json) {
+ VALUE casted_obj = rb_proc_call_with_block(state->as_json, 1, &obj, Qnil);
+ if (casted_obj != obj) {
+ increase_depth(state);
+ generate_json(buffer, data, state, casted_obj);
+ state->depth--;
+ return;
+ }
+ }
+ raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", rb_funcall(obj, i_to_s, 0));
}
}
- fbuffer_append_str(buffer, tmp);
+ fbuffer_append_str(buffer, rb_funcall(obj, i_to_s, 0));
}
static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
diff --git a/test/json/json_coder_test.rb b/test/json/json_coder_test.rb
index 37331c4eb8..9861181910 100755
--- a/test/json/json_coder_test.rb
+++ b/test/json/json_coder_test.rb
@@ -35,4 +35,19 @@ class JSONCoderTest < Test::Unit::TestCase
coder = JSON::Coder.new(symbolize_names: true)
assert_equal({a: 1}, coder.load('{"a":1}'))
end
+
+ def test_json_coder_dump_NaN_or_Infinity
+ coder = JSON::Coder.new(&:inspect)
+ assert_equal "NaN", coder.load(coder.dump(Float::NAN))
+ assert_equal "Infinity", coder.load(coder.dump(Float::INFINITY))
+ assert_equal "-Infinity", coder.load(coder.dump(-Float::INFINITY))
+ end
+
+ def test_json_coder_dump_NaN_or_Infinity_loop
+ coder = JSON::Coder.new(&:itself)
+ error = assert_raise JSON::GeneratorError do
+ coder.dump(Float::NAN)
+ end
+ assert_include error.message, "NaN not allowed in JSON"
+ end
end