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
2 changes: 2 additions & 0 deletions ext/json/ext/parser/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
require 'mkmf'

$defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
$defs << "-DJSON_WORKAROUND_RB_CATCH_BUG" if RUBY_ENGINE == 'truffleruby'

have_func("rb_enc_interned_str", "ruby/encoding.h") # RUBY_VERSION >= 3.0
have_func("rb_str_to_interned_str", "ruby.h") # RUBY_VERSION >= 3.0
have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2
Expand Down
72 changes: 54 additions & 18 deletions ext/json/ext/parser/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
static VALUE mJSON, eNestingError, eParserError, Encoding_UTF_8;
static VALUE CNaN, CInfinity, CMinusInfinity;

static ID i_new, i_try_convert, i_uminus, i_encode, i_at_line, i_at_column, i_at_eos;
static ID i_new, i_try_convert, i_uminus, i_encode, i_at_line, i_at_column;

static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_allow_comments,
sym_allow_control_characters, sym_allow_invalid_escape, sym_symbolize_names,
Expand Down Expand Up @@ -669,18 +669,52 @@ static VALUE parse_error_new(JSON_ParserState *state, VALUE message, long line,
VALUE exc = rb_exc_new_str(eParserError, message);
rb_ivar_set(exc, i_at_line, LONG2NUM(line));
rb_ivar_set(exc, i_at_column, LONG2NUM(column));
if (eos && state->parser) {
rb_ivar_set(exc, i_at_eos, state->parser);
}
return exc;
}

#ifdef JSON_WORKAROUND_RB_CATCH_BUG
#define JSON_CATCH_FUNC_ARGLIST(yielded_arg, func_args) VALUE func_args

NORETURN(static) void parser_throw_eos(VALUE parser)
{
VALUE exc = rb_exc_new_str(eParserError, rb_utf8_str_new_cstr("EOS"));
rb_ivar_set(exc, rb_intern("@resumable_parser_eos"), parser);
rb_exc_raise(exc);
}

static VALUE parser_catch_eos(VALUE parser, VALUE (*func)(VALUE args), VALUE func_args)
{
int status;
VALUE result = rb_protect(func, func_args, &status);
if (status) {
VALUE error_source = rb_ivar_get(rb_errinfo(), rb_intern("@resumable_parser_eos"));
if (error_source == parser) {
rb_set_errinfo(Qnil);
return parser;
}
rb_jump_tag(status);
}
return result;
}
#else
#define JSON_CATCH_FUNC_ARGLIST RB_BLOCK_CALL_FUNC_ARGLIST
#define parser_throw_eos(parser) rb_throw_obj(parser, parser)
#define parser_catch_eos(parser, func, func_args) rb_catch_obj(parser, func, func_args)
#endif

NORETURN(static) void raise_parse_error(const char *format, JSON_ParserState *state, bool eos)
{
VALUE message = build_parse_error_message(format, state);
if (state->parser) { // line and columns can't be accurate in resumable
rb_exc_raise(parse_error_new(state, message, 0, 0, eos));
if (state->parser) {
if (eos) {
// the error will be swallowed by ResumableParser#parse, so no
// point building a message or backtrace.
parser_throw_eos(state->parser);
} else {
// line and columns can't be accurate in resumable
rb_exc_raise(parse_error_new(state, build_parse_error_message(format, state), 0, 0, eos));
}
} else {
VALUE message = build_parse_error_message(format, state);
long line, column;
cursor_position(state, &line, &column);
rb_str_catf(message, " at line %ld column %ld", line, column);
Expand Down Expand Up @@ -2379,14 +2413,22 @@ static VALUE cResumableParser_feed(VALUE self, VALUE str)
struct json_parse_any_args {
JSON_ParserState *state;
JSON_ParserConfig *config;
VALUE parser;
};

static VALUE json_parse_any_resumable_safe(VALUE _args)
static VALUE json_parse_any_resumable_safe0(JSON_CATCH_FUNC_ARGLIST(yielded_arg, _args))
{
struct json_parse_any_args *args = (struct json_parse_any_args *)_args;
return (VALUE)json_parse_any(args->state, args->config, true);
}

static VALUE json_parse_any_resumable_safe(VALUE _args)
{
struct json_parse_any_args *args = (struct json_parse_any_args *)_args;
VALUE result = parser_catch_eos(args->parser, json_parse_any_resumable_safe0, _args);
return result == args->parser ? Qfalse : result;
}

static JSON_ResumableParser *ResumableParser_acquire(VALUE self, bool lock)
{
JSON_ResumableParser *parser = cResumableParser_get(self);
Expand Down Expand Up @@ -2455,25 +2497,20 @@ static VALUE cResumableParser_parse(VALUE self)
struct json_parse_any_args args = {
.state = &parser->state,
.config = &parser->config,
.parser = self,
};
int status;
const char *initial_cursor = parser->state.cursor;
parser->complete = rb_protect(json_parse_any_resumable_safe, (VALUE)&args, &status);

if (status) {
VALUE error_source = rb_ivar_get(rb_errinfo(), i_at_eos);
if (error_source == self) {
parser->complete = false; // is an EOS error raised by ourself
rb_set_errinfo(Qnil);
status = 0;
} else {
parser->complete = true; // a parse error is considered complete
}
parser->complete = true; // a parse error is considered complete
}

parser->parsed_bytes += parser->state.cursor - initial_cursor;
parser->incomplete_bytes = parser->complete ? 0 : parser->state.end - parser->state.cursor;

parser->in_use = false;

if (status) {
rb_jump_tag(status); // reraise
}
Expand Down Expand Up @@ -2765,7 +2802,6 @@ void Init_parser(void)
i_encode = rb_intern("encode");
i_at_line = rb_intern("@line");
i_at_column = rb_intern("@column");
i_at_eos = rb_intern("@eos");

binary_encindex = rb_ascii8bit_encindex();
utf8_encindex = rb_utf8_encindex();
Expand Down
Loading