Skip to content

Commit 9d8efcb

Browse files
committed
Workaround TruffleRuby buggy rb_catch_obj implementation
Somehow on TruffleRuby `rb_catch_obj` straight out doesn't call the passed function, acting as a noop.
1 parent 4bd1e9b commit 9d8efcb

2 files changed

Lines changed: 37 additions & 12 deletions

File tree

ext/json/ext/parser/extconf.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
require 'mkmf'
33

44
$defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
5+
$defs << "-DJSON_WORKAROUND_RB_CATCH_BUG" if RUBY_ENGINE == 'truffleruby'
6+
57
have_func("rb_enc_interned_str", "ruby/encoding.h") # RUBY_VERSION >= 3.0
68
have_func("rb_str_to_interned_str", "ruby.h") # RUBY_VERSION >= 3.0
79
have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2

ext/json/ext/parser/parser.c

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
static VALUE mJSON, eNestingError, eParserError, Encoding_UTF_8;
66
static VALUE CNaN, CInfinity, CMinusInfinity;
77

8-
static ID i_new, i_try_convert, i_uminus, i_encode, i_at_line, i_at_column, i_at_eos;
8+
static ID i_new, i_try_convert, i_uminus, i_encode, i_at_line, i_at_column;
99

1010
static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_allow_comments,
1111
sym_allow_control_characters, sym_allow_invalid_escape, sym_symbolize_names,
@@ -669,19 +669,46 @@ static VALUE parse_error_new(JSON_ParserState *state, VALUE message, long line,
669669
VALUE exc = rb_exc_new_str(eParserError, message);
670670
rb_ivar_set(exc, i_at_line, LONG2NUM(line));
671671
rb_ivar_set(exc, i_at_column, LONG2NUM(column));
672-
if (eos && state->parser) {
673-
rb_ivar_set(exc, i_at_eos, state->parser);
674-
}
675672
return exc;
676673
}
677674

675+
#ifdef JSON_WORKAROUND_RB_CATCH_BUG
676+
#define JSON_CATCH_FUNC_ARGLIST(yielded_arg, func_args) VALUE func_args
677+
678+
NORETURN(static) void parser_throw_eos(VALUE parser)
679+
{
680+
VALUE exc = rb_exc_new_str(eParserError, rb_utf8_str_new_cstr("EOS"));
681+
rb_ivar_set(exc, rb_intern("@resumable_parser_eos"), parser);
682+
rb_exc_raise(exc);
683+
}
684+
685+
static VALUE parser_catch_eos(VALUE parser, VALUE (*func)(VALUE args), VALUE func_args)
686+
{
687+
int status;
688+
VALUE result = rb_protect(func, func_args, &status);
689+
if (status) {
690+
VALUE error_source = rb_ivar_get(rb_errinfo(), rb_intern("@resumable_parser_eos"));
691+
if (error_source == parser) {
692+
rb_set_errinfo(Qnil);
693+
return parser;
694+
}
695+
rb_jump_tag(status);
696+
}
697+
return result;
698+
}
699+
#else
700+
#define JSON_CATCH_FUNC_ARGLIST RB_BLOCK_CALL_FUNC_ARGLIST
701+
#define parser_throw_eos(parser) rb_throw_obj(parser, parser)
702+
#define parser_catch_eos(parser, func, func_args) rb_catch_obj(parser, func, func_args)
703+
#endif
704+
678705
NORETURN(static) void raise_parse_error(const char *format, JSON_ParserState *state, bool eos)
679706
{
680707
if (state->parser) {
681708
if (eos) {
682709
// the error will be swallowed by ResumableParser#parse, so no
683710
// point building a message or backtrace.
684-
rb_throw_obj(state->parser, state->parser);
711+
parser_throw_eos(state->parser);
685712
} else {
686713
// line and columns can't be accurate in resumable
687714
rb_exc_raise(parse_error_new(state, build_parse_error_message(format, state), 0, 0, eos));
@@ -2389,7 +2416,7 @@ struct json_parse_any_args {
23892416
VALUE parser;
23902417
};
23912418

2392-
static VALUE json_parse_any_resumable_safe0(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, _args))
2419+
static VALUE json_parse_any_resumable_safe0(JSON_CATCH_FUNC_ARGLIST(yielded_arg, _args))
23932420
{
23942421
struct json_parse_any_args *args = (struct json_parse_any_args *)_args;
23952422
return (VALUE)json_parse_any(args->state, args->config, true);
@@ -2398,11 +2425,8 @@ static VALUE json_parse_any_resumable_safe0(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_a
23982425
static VALUE json_parse_any_resumable_safe(VALUE _args)
23992426
{
24002427
struct json_parse_any_args *args = (struct json_parse_any_args *)_args;
2401-
VALUE result = rb_catch_obj(args->parser, json_parse_any_resumable_safe0, _args);
2402-
if (result == args->parser) {
2403-
return (VALUE)false;
2404-
}
2405-
return result;
2428+
VALUE result = parser_catch_eos(args->parser, json_parse_any_resumable_safe0, _args);
2429+
return result == args->parser ? Qfalse : result;
24062430
}
24072431

24082432
static JSON_ResumableParser *ResumableParser_acquire(VALUE self, bool lock)
@@ -2778,7 +2802,6 @@ void Init_parser(void)
27782802
i_encode = rb_intern("encode");
27792803
i_at_line = rb_intern("@line");
27802804
i_at_column = rb_intern("@column");
2781-
i_at_eos = rb_intern("@eos");
27822805

27832806
binary_encindex = rb_ascii8bit_encindex();
27842807
utf8_encindex = rb_utf8_encindex();

0 commit comments

Comments
 (0)