Skip to content

Commit

Permalink
Optimize Symbol generation in strict mode
Browse files Browse the repository at this point in the history
Co-authored-by: Jean Boussier <[email protected]>
  • Loading branch information
etiennebarrie and byroot committed Feb 5, 2025
1 parent c472d72 commit 1bb591e
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 8 deletions.
38 changes: 30 additions & 8 deletions ext/json/ext/generator/generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,29 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
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)
{
fbuffer_append(buffer, "null", 4);
Expand Down Expand Up @@ -1049,7 +1072,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)
{
VALUE tmp;
bool as_json_called = false;
start:
if (obj == Qnil) {
Expand All @@ -1063,6 +1085,8 @@ static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON
generate_json_fixnum(buffer, data, state, obj);
} else if (RB_FLONUM_P(obj)) {
generate_json_float(buffer, data, state, obj);
} else if (RB_STATIC_SYM_P(obj)) {
generate_json_symbol(buffer, data, state, obj);
} else {
goto general;
}
Expand All @@ -1084,6 +1108,10 @@ static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON
if (klass != rb_cString) goto general;
generate_json_string(buffer, data, state, obj);
break;
case T_SYMBOL:
if (klass != rb_cSymbol) goto general;
generate_json_string(buffer, data, state, rb_sym2str(obj));
break;
case T_FLOAT:
if (klass != rb_cFloat) goto general;
generate_json_float(buffer, data, state, obj);
Expand All @@ -1102,14 +1130,8 @@ static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON
} else {
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 {
tmp = rb_funcall(obj, i_to_s, 0);
Check_Type(tmp, T_STRING);
generate_json_string(buffer, data, state, tmp);
generate_json_fallback(buffer, data, state, obj);
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions test/json/json_generator_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ def test_dump_strict

assert_equal '42', dump(42, strict: true)
assert_equal 'true', dump(true, strict: true)

assert_equal '"hello"', dump(:hello, strict: true)

Check failure on line 90 in test/json/json_generator_test.rb

View workflow job for this annotation

GitHub Actions / ubuntu-latest jruby

Error

JSON::GeneratorError: hello not allowed in JSON json/ext/GeneratorState.java:237:in `generate' json/ext/GeneratorState.java:141:in `generate' /home/runner/work/json/json/lib/json/common.rb:846:in `dump' /home/runner/work/json/json/test/json/json_generator_test.rb:90:in `test_dump_strict' org/jruby/RubyKernel.java:1426:in `catch' org/jruby/RubyKernel.java:1421:in `catch' org/jruby/RubyArray.java:2009:in `each' org/jruby/RubyArray.java:2009:in `each' org/jruby/RubyKernel.java:1426:in `catch' org/jruby/RubyKernel.java:1421:in `catch'

Check failure on line 90 in test/json/json_generator_test.rb

View workflow job for this annotation

GitHub Actions / ubuntu-latest jruby-head

Error

JSON::GeneratorError: hello not allowed in JSON json/ext/GeneratorState.java:237:in `generate' json/ext/GeneratorState.java:141:in `generate' /home/runner/work/json/json/lib/json/common.rb:846:in `dump' /home/runner/work/json/json/test/json/json_generator_test.rb:90:in `test_dump_strict' org/jruby/RubyKernel.java:1426:in `catch' org/jruby/RubyKernel.java:1421:in `catch' org/jruby/RubyArray.java:2009:in `each' org/jruby/RubyArray.java:2009:in `each' org/jruby/RubyKernel.java:1426:in `catch' org/jruby/RubyKernel.java:1421:in `catch'

Check failure on line 90 in test/json/json_generator_test.rb

View workflow job for this annotation

GitHub Actions / macos-14 jruby

Error

JSON::GeneratorError: hello not allowed in JSON json/ext/GeneratorState.java:237:in `generate' json/ext/GeneratorState.java:141:in `generate' /Users/runner/work/json/json/lib/json/common.rb:846:in `dump' /Users/runner/work/json/json/test/json/json_generator_test.rb:90:in `test_dump_strict' org/jruby/RubyKernel.java:1426:in `catch' org/jruby/RubyKernel.java:1421:in `catch' org/jruby/RubyArray.java:2009:in `each' org/jruby/RubyArray.java:2009:in `each' org/jruby/RubyKernel.java:1426:in `catch' org/jruby/RubyKernel.java:1421:in `catch'

Check failure on line 90 in test/json/json_generator_test.rb

View workflow job for this annotation

GitHub Actions / macos-14 jruby-head

Error

JSON::GeneratorError: hello not allowed in JSON json/ext/GeneratorState.java:237:in `generate' json/ext/GeneratorState.java:141:in `generate' /Users/runner/work/json/json/lib/json/common.rb:846:in `dump' /Users/runner/work/json/json/test/json/json_generator_test.rb:90:in `test_dump_strict' org/jruby/RubyKernel.java:1426:in `catch' org/jruby/RubyKernel.java:1421:in `catch' org/jruby/RubyArray.java:2009:in `each' org/jruby/RubyArray.java:2009:in `each' org/jruby/RubyKernel.java:1426:in `catch' org/jruby/RubyKernel.java:1421:in `catch'

Check failure on line 90 in test/json/json_generator_test.rb

View workflow job for this annotation

GitHub Actions / ubuntu-latest jruby-9.4

Error

JSON::GeneratorError: hello not allowed in JSON json/ext/GeneratorState.java:237:in `generate' json/ext/GeneratorState.java:141:in `generate' /home/runner/work/json/json/lib/json/common.rb:846:in `dump' /home/runner/work/json/json/test/json/json_generator_test.rb:90:in `test_dump_strict' org/jruby/RubyKernel.java:1426:in `catch' org/jruby/RubyKernel.java:1421:in `catch' org/jruby/RubyArray.java:2009:in `each' org/jruby/RubyArray.java:2009:in `each' org/jruby/RubyKernel.java:1426:in `catch' org/jruby/RubyKernel.java:1421:in `catch'

Check failure on line 90 in test/json/json_generator_test.rb

View workflow job for this annotation

GitHub Actions / macos-latest truffleruby-head

Failure

<"\"hello\""> expected but was <"{\"json_class\":\"Symbol\",\"s\":\"hello\"}">.

Check failure on line 90 in test/json/json_generator_test.rb

View workflow job for this annotation

GitHub Actions / ubuntu-latest truffleruby-head

Failure

<"\"hello\""> expected but was <"{\"json_class\":\"Symbol\",\"s\":\"hello\"}">.
assert_equal '"World"', dump("World".to_sym, strict: true)
end

def test_generate_pretty
Expand Down

0 comments on commit 1bb591e

Please sign in to comment.