Optimize Symbol generation in strict mode

Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
This commit is contained in:
Étienne Barrié 2025-02-05 12:35:30 +01:00 committed by Hiroshi SHIBATA
parent f865148e19
commit b4bfbcaddc
No known key found for this signature in database
GPG Key ID: F9CF13417264FAC2
3 changed files with 40 additions and 10 deletions

View File

@ -991,6 +991,29 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
fbuffer_append_char(buffer, '"'); fbuffer_append_char(buffer, '"');
} }
static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
{
VALUE tmp;
if (rb_respond_to(obj, i_to_json)) {
tmp = rb_funcall(obj, i_to_json, 1, vstate_get(data));
Check_Type(tmp, T_STRING);
fbuffer_append_str(buffer, tmp);
} else {
tmp = rb_funcall(obj, i_to_s, 0);
Check_Type(tmp, T_STRING);
generate_json_string(buffer, data, state, tmp);
}
}
static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
{
if (state->strict) {
generate_json_string(buffer, data, state, rb_sym2str(obj));
} else {
generate_json_fallback(buffer, data, state, obj);
}
}
static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
{ {
fbuffer_append(buffer, "null", 4); fbuffer_append(buffer, "null", 4);
@ -1057,7 +1080,6 @@ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *d
static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj) static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
{ {
VALUE tmp;
bool as_json_called = false; bool as_json_called = false;
start: start:
if (obj == Qnil) { if (obj == Qnil) {
@ -1071,6 +1093,8 @@ start:
generate_json_fixnum(buffer, data, state, obj); generate_json_fixnum(buffer, data, state, obj);
} else if (RB_FLONUM_P(obj)) { } else if (RB_FLONUM_P(obj)) {
generate_json_float(buffer, data, state, obj); generate_json_float(buffer, data, state, obj);
} else if (RB_STATIC_SYM_P(obj)) {
generate_json_symbol(buffer, data, state, obj);
} else { } else {
goto general; goto general;
} }
@ -1092,6 +1116,9 @@ start:
if (klass != rb_cString) goto general; if (klass != rb_cString) goto general;
generate_json_string(buffer, data, state, obj); generate_json_string(buffer, data, state, obj);
break; break;
case T_SYMBOL:
generate_json_symbol(buffer, data, state, obj);
break;
case T_FLOAT: case T_FLOAT:
if (klass != rb_cFloat) goto general; if (klass != rb_cFloat) goto general;
generate_json_float(buffer, data, state, obj); generate_json_float(buffer, data, state, obj);
@ -1110,14 +1137,8 @@ start:
} else { } else {
raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj)); raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj));
} }
} else if (rb_respond_to(obj, i_to_json)) {
tmp = rb_funcall(obj, i_to_json, 1, vstate_get(data));
Check_Type(tmp, T_STRING);
fbuffer_append_str(buffer, tmp);
} else { } else {
tmp = rb_funcall(obj, i_to_s, 0); generate_json_fallback(buffer, data, state, obj);
Check_Type(tmp, T_STRING);
generate_json_string(buffer, data, state, tmp);
} }
} }
} }

View File

@ -36,8 +36,13 @@ class Symbol
# #
# # {"json_class":"Symbol","s":"foo"} # # {"json_class":"Symbol","s":"foo"}
# #
def to_json(*a) def to_json(state = nil, *a)
as_json.to_json(*a) state = ::JSON::State.from_state(state)
if state.strict?
super
else
as_json.to_json(state, *a)
end
end end
# See #as_json. # See #as_json.

View File

@ -86,6 +86,10 @@ class JSONGeneratorTest < Test::Unit::TestCase
assert_equal '42', dump(42, strict: true) assert_equal '42', dump(42, strict: true)
assert_equal 'true', dump(true, strict: true) assert_equal 'true', dump(true, strict: true)
assert_equal '"hello"', dump(:hello, strict: true)
assert_equal '"hello"', :hello.to_json(strict: true)
assert_equal '"World"', "World".to_json(strict: true)
end end
def test_generate_pretty def test_generate_pretty