Skip to content

Commit 5795ffe

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 58ac19c commit 5795ffe

2 files changed

Lines changed: 38 additions & 6 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: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -675,13 +675,47 @@ static VALUE parse_error_new(JSON_ParserState *state, VALUE message, long line,
675675
return exc;
676676
}
677677

678+
NORETURN(static) void parser_throw_eos(VALUE parser)
679+
{
680+
#ifdef JSON_WORKAROUND_RB_CATCH_BUG
681+
VALUE exc = rb_exc_new_str(eParserError, rb_utf8_str_new_cstr("EOS"));
682+
rb_ivar_set(exc, i_at_eos, parser);
683+
rb_exc_raise(exc);
684+
#else
685+
rb_throw_obj(parser, parser);
686+
#endif
687+
}
688+
689+
static VALUE parser_catch_eos(VALUE parser, VALUE (*func)(VALUE args), VALUE func_args)
690+
{
691+
#ifdef JSON_WORKAROUND_RB_CATCH_BUG
692+
int status;
693+
VALUE result = rb_protect(func, func_args, &status);
694+
if (status) {
695+
VALUE error_source = rb_ivar_get(rb_errinfo(), i_at_eos);
696+
if (error_source == parser) {
697+
rb_set_errinfo(Qnil);
698+
return Qfalse;
699+
}
700+
rb_jump_tag(status);
701+
}
702+
return result;
703+
#else
704+
VALUE result = rb_catch_obj(parser, func, func_args);
705+
if (result == parser) {
706+
return Qfalse;
707+
}
708+
return result;
709+
#endif
710+
}
711+
678712
NORETURN(static) void raise_parse_error(const char *format, JSON_ParserState *state, bool eos)
679713
{
680714
if (state->parser) {
681715
if (eos) {
682716
// the error will be swallowed by ResumableParser#parse, so no
683717
// point building a message or backtrace.
684-
rb_throw_obj(state->parser, state->parser);
718+
parser_throw_eos(state->parser);
685719
} else {
686720
// line and columns can't be accurate in resumable
687721
rb_exc_raise(parse_error_new(state, build_parse_error_message(format, state), 0, 0, eos));
@@ -2398,11 +2432,7 @@ static VALUE json_parse_any_resumable_safe0(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_a
23982432
static VALUE json_parse_any_resumable_safe(VALUE _args)
23992433
{
24002434
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;
2435+
return parser_catch_eos(args->parser, json_parse_any_resumable_safe0, _args);
24062436
}
24072437

24082438
static JSON_ResumableParser *ResumableParser_acquire(VALUE self, bool lock)

0 commit comments

Comments
 (0)