mirror of
https://github.com/ruby/ruby.git
synced 2026-07-29 22:34:58 +08:00
Audit of every EC_PUSH_TAG user, following the LLVM 19 longjmp rematerialization bug worked around in rb_fiber_start: locals and arguments that are read on the longjmp path but not modified inside the tag region are made volatile. C99 7.13.2.1p3 guarantees such unmodified locals, but LLVM 17 and 19 have been observed to reconstruct their values incorrectly after a longjmp (cfp in rb_iterate0, fiber in rb_fiber_start). volatile on a parameter may not be effective (67d0f4821f, https://bugs.ruby-lang.org/issues/19145), so arguments are copied into volatile locals instead. * vm_eval.c (rb_iterate0): cfp is compared against the escaping frame after a longjmp; clang 17 at -O1 with -fPIC was observed to clobber it during the development of the ifunc invalidation for https://bugs.ruby-lang.org/issues/22019. ec is read next to it to rewind the frame and to rethrow. * eval_error.c (rb_ec_error_print_detailed0): opt and highlight are read on the path reached by a longjmp from rb_get_backtrace while the other locals of the function are already volatile. * scheduler.c (rb_fiber_scheduler_unblock, fiber_interrupt): ec and saved_interrupt_mask are read after a longjmp to restore ec->interrupt_mask; cfp next to them was already volatile. * eval.c (rb_vrescue2): r_proc and data2 are used when the raise is handled. args is left as is; a va_list cannot portably be volatile-qualified and is memory-backed in practice. * eval.c (rb_protect): pstate is written through after the tag is popped. * eval.c (rb_ec_ensure): ec, e_proc and data2 run the ensure proc after a longjmp; a clobbered e_proc would miscall it. * vm_eval.c (vm_catch_protect): stateptr is written through after the pop. tag has its address taken and stays in memory. * vm_trace.c (exec_hooks_protected): ec and list are passed to exec_hooks_postcheck after the pop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
589 lines
18 KiB
C
589 lines
18 KiB
C
/* -*-c-*- */
|
|
/*
|
|
* included by eval.c
|
|
*/
|
|
|
|
#define write_warn(str, x) \
|
|
(NIL_P(str) ? warn_print(x) : (void)rb_str_cat_cstr(str, x))
|
|
#define write_warn2(str, x, l) \
|
|
(NIL_P(str) ? warn_print2(x, l) : (void)rb_str_cat(str, x, l))
|
|
#define write_warn_enc(str, x, l, enc) \
|
|
(NIL_P(str) ? warn_print2(x, l) : (void)rb_enc_str_buf_cat(str, x, l, enc))
|
|
#ifdef HAVE_BUILTIN___BUILTIN_CONSTANT_P
|
|
#define warn_print(x) RB_GNUC_EXTENSION_BLOCK( \
|
|
(__builtin_constant_p(x)) ? \
|
|
rb_write_error2((x), (long)strlen(x)) : \
|
|
rb_write_error(x) \
|
|
)
|
|
#else
|
|
#define warn_print(x) rb_write_error(x)
|
|
#endif
|
|
|
|
#define warn_print2(x,l) rb_write_error2((x),(l))
|
|
|
|
#define write_warn_str(str,x) NIL_P(str) ? rb_write_error_str(x) : (void)rb_str_concat((str), (x))
|
|
#define warn_print_str(x) rb_write_error_str(x)
|
|
|
|
static VALUE error_pos_str(void);
|
|
|
|
static void
|
|
error_pos(const VALUE str)
|
|
{
|
|
VALUE pos = error_pos_str();
|
|
if (!NIL_P(pos)) {
|
|
write_warn_str(str, pos);
|
|
}
|
|
}
|
|
|
|
static VALUE
|
|
error_pos_str(void)
|
|
{
|
|
int sourceline;
|
|
VALUE sourcefile = rb_source_location(&sourceline);
|
|
|
|
if (!NIL_P(sourcefile)) {
|
|
ID caller_name;
|
|
if (sourceline == 0) {
|
|
return rb_sprintf("%"PRIsVALUE": ", sourcefile);
|
|
}
|
|
else if ((caller_name = rb_frame_callee()) != 0) {
|
|
return rb_sprintf("%"PRIsVALUE":%d:in '%"PRIsVALUE"': ",
|
|
sourcefile, sourceline,
|
|
rb_id2str(caller_name));
|
|
}
|
|
else {
|
|
return rb_sprintf("%"PRIsVALUE":%d: ", sourcefile, sourceline);
|
|
}
|
|
}
|
|
return Qnil;
|
|
}
|
|
|
|
static void
|
|
set_backtrace(VALUE info, VALUE bt)
|
|
{
|
|
ID set_backtrace = rb_intern("set_backtrace");
|
|
|
|
if (rb_backtrace_p(bt)) {
|
|
if (rb_method_basic_definition_p(CLASS_OF(info), set_backtrace)) {
|
|
rb_exc_set_backtrace(info, bt);
|
|
return;
|
|
}
|
|
else {
|
|
bt = rb_backtrace_to_str_ary(bt);
|
|
}
|
|
}
|
|
rb_check_funcall(info, set_backtrace, 1, &bt);
|
|
}
|
|
|
|
#define CSI_BEGIN "\033["
|
|
#define CSI_SGR "m"
|
|
|
|
static const char underline[] = CSI_BEGIN"1;4"CSI_SGR;
|
|
static const char bold[] = CSI_BEGIN"1"CSI_SGR;
|
|
static const char reset[] = CSI_BEGIN""CSI_SGR;
|
|
|
|
static void
|
|
print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, const VALUE str, int highlight)
|
|
{
|
|
long elen = 0;
|
|
VALUE mesg;
|
|
|
|
if (NIL_P(errat) || RARRAY_LEN(errat) == 0 ||
|
|
NIL_P(mesg = RARRAY_AREF(errat, 0))) {
|
|
error_pos(str);
|
|
}
|
|
else {
|
|
write_warn_str(str, mesg);
|
|
write_warn(str, ": ");
|
|
}
|
|
|
|
if (!NIL_P(emesg)) {
|
|
elen = RSTRING_LEN(emesg);
|
|
}
|
|
|
|
if (eclass == rb_eRuntimeError && elen == 0) {
|
|
if (highlight) write_warn(str, underline);
|
|
write_warn(str, "unhandled exception");
|
|
if (highlight) write_warn(str, reset);
|
|
write_warn(str, "\n");
|
|
}
|
|
else {
|
|
VALUE epath;
|
|
|
|
epath = rb_class_name(eclass);
|
|
if (elen == 0) {
|
|
if (highlight) write_warn(str, underline);
|
|
write_warn_str(str, epath);
|
|
if (highlight) write_warn(str, reset);
|
|
write_warn(str, "\n");
|
|
}
|
|
else {
|
|
write_warn_str(str, emesg);
|
|
write_warn(str, "\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
VALUE
|
|
rb_decorate_message(const VALUE eclass, VALUE emesg, int highlight)
|
|
{
|
|
const char *einfo = "";
|
|
long elen = 0;
|
|
rb_encoding *eenc;
|
|
|
|
VALUE str = rb_usascii_str_new_cstr("");
|
|
|
|
if (!NIL_P(emesg) && rb_enc_asciicompat(eenc = rb_enc_get(emesg))) {
|
|
einfo = RSTRING_PTR(emesg);
|
|
elen = RSTRING_LEN(emesg);
|
|
}
|
|
else {
|
|
eenc = NULL;
|
|
}
|
|
if (eclass == rb_eRuntimeError && elen == 0) {
|
|
if (highlight) write_warn(str, underline);
|
|
write_warn(str, "unhandled exception");
|
|
if (highlight) write_warn(str, reset);
|
|
}
|
|
else {
|
|
VALUE epath;
|
|
|
|
epath = rb_class_name(eclass);
|
|
if (elen == 0) {
|
|
if (highlight) write_warn(str, underline);
|
|
write_warn_str(str, epath);
|
|
if (highlight) write_warn(str, reset);
|
|
}
|
|
else {
|
|
/* emesg is a String instance */
|
|
const char *tail = 0;
|
|
|
|
if (highlight) write_warn(str, bold);
|
|
if (RSTRING_PTR(epath)[0] == '#')
|
|
epath = 0;
|
|
if ((tail = memchr(einfo, '\n', elen)) != 0) {
|
|
write_warn_enc(str, einfo, tail - einfo, eenc);
|
|
tail++; /* skip newline */
|
|
}
|
|
else {
|
|
write_warn_str(str, emesg);
|
|
}
|
|
if (epath) {
|
|
write_warn(str, " (");
|
|
if (highlight) write_warn(str, underline);
|
|
write_warn_str(str, epath);
|
|
if (highlight) {
|
|
write_warn(str, reset);
|
|
write_warn(str, bold);
|
|
}
|
|
write_warn(str, ")");
|
|
if (highlight) write_warn(str, reset);
|
|
}
|
|
if (tail && einfo+elen > tail) {
|
|
if (!highlight) {
|
|
write_warn(str, "\n");
|
|
write_warn_enc(str, tail, einfo+elen-tail, eenc);
|
|
}
|
|
else {
|
|
elen -= tail - einfo;
|
|
einfo = tail;
|
|
write_warn(str, "\n");
|
|
while (elen > 0) {
|
|
tail = memchr(einfo, '\n', elen);
|
|
if (!tail || tail > einfo) {
|
|
write_warn(str, bold);
|
|
write_warn_enc(str, einfo, tail ? tail-einfo : elen, eenc);
|
|
write_warn(str, reset);
|
|
if (!tail) {
|
|
break;
|
|
}
|
|
}
|
|
elen -= tail - einfo;
|
|
einfo = tail;
|
|
do ++tail; while (tail < einfo+elen && *tail == '\n');
|
|
write_warn_enc(str, einfo, tail-einfo, eenc);
|
|
elen -= tail - einfo;
|
|
einfo = tail;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RB_GC_GUARD(emesg);
|
|
|
|
return str;
|
|
}
|
|
|
|
static void
|
|
print_backtrace(const VALUE eclass, const VALUE errat, const VALUE str, int reverse, long backtrace_limit)
|
|
{
|
|
if (!NIL_P(errat)) {
|
|
long i;
|
|
long len = RARRAY_LEN(errat);
|
|
const int threshold = 1000000000;
|
|
int width = (len <= 1) ? INT_MIN : ((int)log10((double)(len > threshold ?
|
|
((len - 1) / threshold) :
|
|
len - 1)) +
|
|
(len < threshold ? 0 : 9) + 1);
|
|
|
|
long skip_start = -1, skip_len = 0;
|
|
|
|
// skip for stackoverflow
|
|
if (eclass == rb_eSysStackError) {
|
|
long trace_head = 9;
|
|
long trace_tail = 4;
|
|
long trace_max = trace_head + trace_tail + 5;
|
|
if (len > trace_max) {
|
|
skip_start = trace_head;
|
|
skip_len = len - trace_max + 5;
|
|
}
|
|
}
|
|
|
|
// skip for explicit limit
|
|
if (backtrace_limit >= 0 && len > backtrace_limit + 2) {
|
|
skip_start = backtrace_limit + 1;
|
|
skip_len = len - skip_start;
|
|
}
|
|
|
|
for (i = 1; i < len; i++) {
|
|
if (i == skip_start) {
|
|
write_warn_str(str, rb_sprintf("\t ... %ld levels...\n", skip_len));
|
|
i += skip_len;
|
|
if (i >= len) break;
|
|
}
|
|
VALUE line = RARRAY_AREF(errat, reverse ? len - i : i);
|
|
if (RB_TYPE_P(line, T_STRING)) {
|
|
VALUE bt = rb_str_new_cstr("\t");
|
|
if (reverse) rb_str_catf(bt, "%*ld: ", width, len - i);
|
|
write_warn_str(str, rb_str_catf(bt, "from %"PRIsVALUE"\n", line));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VALUE rb_get_detailed_message(VALUE exc, VALUE opt);
|
|
|
|
static int
|
|
shown_cause_p(VALUE cause, VALUE *shown_causes)
|
|
{
|
|
VALUE shown = *shown_causes;
|
|
if (!shown) {
|
|
*shown_causes = shown = rb_obj_hide(rb_ident_hash_new());
|
|
}
|
|
if (rb_hash_has_key(shown, cause)) return TRUE;
|
|
rb_hash_aset(shown, cause, Qtrue);
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
add_shown_cause(VALUE cause, VALUE *shown_causes)
|
|
{
|
|
if (!NIL_OR_UNDEF_P(cause) &&
|
|
!THROW_DATA_P(cause) &&
|
|
rb_obj_is_kind_of(cause, rb_eException)) {
|
|
shown_cause_p(cause, shown_causes);
|
|
}
|
|
}
|
|
|
|
static void
|
|
show_cause(VALUE errinfo, VALUE str, VALUE opt, VALUE highlight, VALUE reverse, long backtrace_limit, VALUE *shown_causes)
|
|
{
|
|
VALUE cause = rb_attr_get(errinfo, id_cause);
|
|
if (!NIL_P(cause) && rb_obj_is_kind_of(cause, rb_eException) &&
|
|
!shown_cause_p(cause, shown_causes)) {
|
|
volatile VALUE eclass = CLASS_OF(cause);
|
|
VALUE errat = rb_get_backtrace(cause);
|
|
VALUE emesg = rb_get_detailed_message(cause, opt);
|
|
if (reverse) {
|
|
show_cause(cause, str, opt, highlight, reverse, backtrace_limit, shown_causes);
|
|
print_backtrace(eclass, errat, str, TRUE, backtrace_limit);
|
|
print_errinfo(eclass, errat, emesg, str, RTEST(highlight));
|
|
}
|
|
else {
|
|
print_errinfo(eclass, errat, emesg, str, RTEST(highlight));
|
|
print_backtrace(eclass, errat, str, FALSE, backtrace_limit);
|
|
show_cause(cause, str, opt, highlight, reverse, backtrace_limit, shown_causes);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
rb_exc_check_circular_cause(VALUE exc)
|
|
{
|
|
VALUE cause = exc, shown_causes = 0;
|
|
do {
|
|
if (shown_cause_p(cause, &shown_causes)) {
|
|
rb_raise(rb_eArgError, "circular causes");
|
|
}
|
|
} while (!NIL_P(cause = rb_attr_get(cause, id_cause)));
|
|
}
|
|
|
|
static void
|
|
rb_error_write0(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, VALUE highlight, VALUE reverse, VALUE *shown_causes)
|
|
{
|
|
volatile VALUE eclass;
|
|
VALUE local_shown_causes = 0;
|
|
long backtrace_limit = rb_backtrace_length_limit;
|
|
|
|
if (NIL_P(errinfo))
|
|
return;
|
|
if (!shown_causes) {
|
|
shown_causes = &local_shown_causes;
|
|
}
|
|
|
|
if (UNDEF_P(errat)) {
|
|
errat = Qnil;
|
|
}
|
|
eclass = CLASS_OF(errinfo);
|
|
if (reverse) {
|
|
static const char traceback[] = "Traceback "
|
|
"(most recent call last):\n";
|
|
const int bold_part = rb_strlen_lit("Traceback");
|
|
char buff[sizeof(traceback)+sizeof(bold)+sizeof(reset)-2], *p = buff;
|
|
const char *msg = traceback;
|
|
long len = sizeof(traceback) - 1;
|
|
if (RTEST(highlight)) {
|
|
#define APPEND(s, l) (memcpy(p, s, l), p += (l))
|
|
APPEND(bold, sizeof(bold)-1);
|
|
APPEND(traceback, bold_part);
|
|
APPEND(reset, sizeof(reset)-1);
|
|
APPEND(traceback + bold_part, sizeof(traceback)-bold_part-1);
|
|
#undef APPEND
|
|
len = p - (msg = buff);
|
|
}
|
|
write_warn2(str, msg, len);
|
|
show_cause(errinfo, str, opt, highlight, reverse, backtrace_limit, shown_causes);
|
|
print_backtrace(eclass, errat, str, TRUE, backtrace_limit);
|
|
print_errinfo(eclass, errat, emesg, str, RTEST(highlight));
|
|
}
|
|
else {
|
|
print_errinfo(eclass, errat, emesg, str, RTEST(highlight));
|
|
print_backtrace(eclass, errat, str, FALSE, backtrace_limit);
|
|
show_cause(errinfo, str, opt, highlight, reverse, backtrace_limit, shown_causes);
|
|
}
|
|
}
|
|
|
|
void
|
|
rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, VALUE highlight, VALUE reverse)
|
|
{
|
|
rb_error_write0(errinfo, emesg, errat, str, opt, highlight, reverse, NULL);
|
|
}
|
|
|
|
static void
|
|
rb_ec_error_print_detailed0(rb_execution_context_t *const ec, const VALUE errinfo, const VALUE str, VALUE emesg0, VALUE *shown_causes)
|
|
{
|
|
volatile uint8_t raised_flag = ec->raised_flag;
|
|
volatile VALUE errat = Qundef;
|
|
volatile bool written = false;
|
|
volatile VALUE emesg = emesg0;
|
|
|
|
volatile VALUE opt = rb_hash_new();
|
|
volatile VALUE highlight = rb_stderr_tty_p() ? Qtrue : Qfalse;
|
|
rb_hash_aset(opt, ID2SYM(rb_intern_const("highlight")), highlight);
|
|
|
|
if (NIL_P(errinfo))
|
|
return;
|
|
rb_ec_raised_clear(ec);
|
|
|
|
EC_PUSH_TAG(ec);
|
|
if (EC_EXEC_TAG() == TAG_NONE) {
|
|
errat = rb_get_backtrace(errinfo);
|
|
}
|
|
if (UNDEF_P(emesg)) {
|
|
emesg = Qnil;
|
|
emesg = rb_get_detailed_message(errinfo, opt);
|
|
}
|
|
|
|
if (!written) {
|
|
written = true;
|
|
rb_error_write0(errinfo, emesg, errat, str, opt, highlight, Qfalse, shown_causes);
|
|
}
|
|
|
|
EC_POP_TAG();
|
|
ec->errinfo = errinfo;
|
|
rb_ec_raised_set(ec, raised_flag);
|
|
}
|
|
|
|
static void
|
|
rb_ec_error_print_detailed(rb_execution_context_t *const ec, const VALUE errinfo, const VALUE str, VALUE emesg0)
|
|
{
|
|
rb_ec_error_print_detailed0(ec, errinfo, str, emesg0, NULL);
|
|
}
|
|
|
|
void
|
|
rb_ec_error_print(rb_execution_context_t *volatile ec, volatile VALUE errinfo)
|
|
{
|
|
rb_ec_error_print_detailed(ec, errinfo, Qnil, Qundef);
|
|
}
|
|
|
|
#define undef_mesg_for(v, k) rb_fstring_lit("undefined"v" method '%1$s' for "k" '%2$s'")
|
|
#define undef_mesg(v) ( \
|
|
is_mod ? \
|
|
undef_mesg_for(v, "module") : \
|
|
undef_mesg_for(v, "class"))
|
|
|
|
void
|
|
rb_print_undef(VALUE klass, ID id, rb_method_visibility_t visi)
|
|
{
|
|
const int is_mod = RB_TYPE_P(klass, T_MODULE);
|
|
VALUE mesg;
|
|
switch (visi & METHOD_VISI_MASK) {
|
|
case METHOD_VISI_UNDEF:
|
|
case METHOD_VISI_PUBLIC: mesg = undef_mesg(""); break;
|
|
case METHOD_VISI_PRIVATE: mesg = undef_mesg(" private"); break;
|
|
case METHOD_VISI_PROTECTED: mesg = undef_mesg(" protected"); break;
|
|
default: UNREACHABLE;
|
|
}
|
|
rb_name_err_raise_str(mesg, klass, ID2SYM(id));
|
|
}
|
|
|
|
void
|
|
rb_print_undef_str(VALUE klass, VALUE name)
|
|
{
|
|
const int is_mod = RB_TYPE_P(klass, T_MODULE);
|
|
rb_name_err_raise_str(undef_mesg(""), klass, name);
|
|
}
|
|
|
|
#define inaccessible_mesg_for(v, k) rb_fstring_lit("method '%1$s' for "k" '%2$s' is "v)
|
|
#define inaccessible_mesg(v) ( \
|
|
is_mod ? \
|
|
inaccessible_mesg_for(v, "module") : \
|
|
inaccessible_mesg_for(v, "class"))
|
|
|
|
void
|
|
rb_print_inaccessible(VALUE klass, ID id, rb_method_visibility_t visi)
|
|
{
|
|
const int is_mod = RB_TYPE_P(klass, T_MODULE);
|
|
VALUE mesg;
|
|
switch (visi & METHOD_VISI_MASK) {
|
|
case METHOD_VISI_UNDEF:
|
|
case METHOD_VISI_PUBLIC: mesg = inaccessible_mesg(""); break;
|
|
case METHOD_VISI_PRIVATE: mesg = inaccessible_mesg("private"); break;
|
|
case METHOD_VISI_PROTECTED: mesg = inaccessible_mesg("protected"); break;
|
|
default: UNREACHABLE;
|
|
}
|
|
rb_name_err_raise_str(mesg, klass, ID2SYM(id));
|
|
}
|
|
|
|
static int
|
|
sysexit_status(VALUE err)
|
|
{
|
|
VALUE st = rb_ivar_get(err, id_status);
|
|
return NUM2INT(st);
|
|
}
|
|
|
|
enum {
|
|
EXITING_WITH_MESSAGE = 1,
|
|
EXITING_WITH_STATUS = 2,
|
|
EXITING_WITH_SIGNAL = 4
|
|
};
|
|
static int
|
|
exiting_split(VALUE errinfo, volatile int *exitcode, volatile int *sigstatus)
|
|
{
|
|
int ex = EXIT_SUCCESS;
|
|
VALUE signo;
|
|
int sig = 0;
|
|
int result = 0;
|
|
|
|
if (NIL_P(errinfo)) return 0;
|
|
|
|
if (THROW_DATA_P(errinfo)) {
|
|
int throw_state = ((const struct vm_throw_data *)errinfo)->throw_state;
|
|
ex = throw_state & VM_THROW_STATE_MASK;
|
|
result |= EXITING_WITH_STATUS;
|
|
}
|
|
else if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
|
|
ex = sysexit_status(errinfo);
|
|
result |= EXITING_WITH_STATUS;
|
|
}
|
|
else if (rb_obj_is_kind_of(errinfo, rb_eSignal)) {
|
|
signo = rb_ivar_get(errinfo, id_signo);
|
|
sig = FIX2INT(signo);
|
|
result |= EXITING_WITH_SIGNAL;
|
|
/* no message when exiting by signal */
|
|
if (signo == INT2FIX(SIGSEGV) || !rb_obj_is_instance_of(errinfo, rb_eSignal))
|
|
/* except for SEGV and subclasses */
|
|
result |= EXITING_WITH_MESSAGE;
|
|
}
|
|
else if (rb_obj_is_kind_of(errinfo, rb_eSystemCallError) &&
|
|
FIXNUM_P(signo = rb_attr_get(errinfo, id_signo))) {
|
|
sig = FIX2INT(signo);
|
|
result |= EXITING_WITH_SIGNAL;
|
|
/* no message when exiting by error to be mapped to signal */
|
|
}
|
|
else {
|
|
ex = EXIT_FAILURE;
|
|
result |= EXITING_WITH_STATUS | EXITING_WITH_MESSAGE;
|
|
}
|
|
|
|
if (exitcode && (result & EXITING_WITH_STATUS))
|
|
*exitcode = ex;
|
|
if (sigstatus && (result & EXITING_WITH_SIGNAL))
|
|
*sigstatus = sig;
|
|
|
|
return result;
|
|
}
|
|
|
|
#define unknown_longjmp_status(status) \
|
|
rb_bug("Unknown longjmp status %d", status)
|
|
|
|
static int
|
|
error_handle_with_shown_causes(rb_execution_context_t *ec, VALUE errinfo, enum ruby_tag_type ex, VALUE *shown_causes)
|
|
{
|
|
int status = EXIT_FAILURE;
|
|
|
|
if (rb_ec_set_raised(ec))
|
|
return EXIT_FAILURE;
|
|
switch (ex & TAG_MASK) {
|
|
case 0:
|
|
status = EXIT_SUCCESS;
|
|
break;
|
|
|
|
case TAG_RETURN:
|
|
error_pos(Qnil);
|
|
warn_print("unexpected return\n");
|
|
break;
|
|
case TAG_NEXT:
|
|
error_pos(Qnil);
|
|
warn_print("unexpected next\n");
|
|
break;
|
|
case TAG_BREAK:
|
|
error_pos(Qnil);
|
|
warn_print("unexpected break\n");
|
|
break;
|
|
case TAG_REDO:
|
|
error_pos(Qnil);
|
|
warn_print("unexpected redo\n");
|
|
break;
|
|
case TAG_RETRY:
|
|
error_pos(Qnil);
|
|
warn_print("retry outside of rescue clause\n");
|
|
break;
|
|
case TAG_THROW:
|
|
/* TODO: fix me */
|
|
error_pos(Qnil);
|
|
warn_print("unexpected throw\n");
|
|
break;
|
|
case TAG_RAISE:
|
|
if (!(exiting_split(errinfo, &status, NULL) & EXITING_WITH_MESSAGE)) {
|
|
break;
|
|
}
|
|
/* fallthrough */
|
|
case TAG_FATAL:
|
|
rb_ec_error_print_detailed0(ec, errinfo, Qnil, Qundef, shown_causes);
|
|
break;
|
|
default:
|
|
unknown_longjmp_status(ex);
|
|
break;
|
|
}
|
|
rb_ec_reset_raised(ec);
|
|
return status;
|
|
}
|
|
|
|
static int
|
|
error_handle(rb_execution_context_t *ec, VALUE errinfo, enum ruby_tag_type ex)
|
|
{
|
|
return error_handle_with_shown_causes(ec, errinfo, ex, NULL);
|
|
}
|