diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index c21a5fda5f..776eb916f0 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -454,15 +454,24 @@ RBIMPL_ATTR_NORETURN() #endif static void raise_parse_error(const char *format, const char *start) { - char buffer[PARSE_ERROR_FRAGMENT_LEN + 1]; + unsigned char buffer[PARSE_ERROR_FRAGMENT_LEN + 1]; size_t len = start ? strnlen(start, PARSE_ERROR_FRAGMENT_LEN) : 0; const char *ptr = start; if (len == PARSE_ERROR_FRAGMENT_LEN) { MEMCPY(buffer, start, char, PARSE_ERROR_FRAGMENT_LEN); - buffer[PARSE_ERROR_FRAGMENT_LEN] = '\0'; - ptr = buffer; + + while (buffer[len - 1] >= 0x80 && buffer[len - 1] < 0xC0) { // Is continuation byte + len--; + } + + if (buffer[len - 1] >= 0xC0) { // multibyte character start + len--; + } + + buffer[len] = '\0'; + ptr = (const char *)buffer; } rb_enc_raise(enc_utf8, rb_path2class("JSON::ParserError"), format, ptr); diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index d1f084bb63..ae0f285d32 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -645,6 +645,22 @@ class JSONParserTest < Test::Unit::TestCase end end + def test_parse_error_snippet + omit "C ext only test" unless RUBY_ENGINE == "ruby" + + error = assert_raise(JSON::ParserError) { JSON.parse("あああああああああああああああああああああああ") } + assert_equal "unexpected character: 'ああああああああああ'", error.message + + error = assert_raise(JSON::ParserError) { JSON.parse("aあああああああああああああああああああああああ") } + assert_equal "unexpected character: 'aああああああああああ'", error.message + + error = assert_raise(JSON::ParserError) { JSON.parse("abあああああああああああああああああああああああ") } + assert_equal "unexpected character: 'abあああああああああ'", error.message + + error = assert_raise(JSON::ParserError) { JSON.parse("abcあああああああああああああああああああああああ") } + assert_equal "unexpected character: 'abcあああああああああ'", error.message + end + def test_parse_leading_slash # ref: https://github.com/ruby/ruby/pull/12598 assert_raise(JSON::ParserError) do