d-unsed / ruru

Native Ruby extensions written in Rust
MIT License
832 stars 40 forks source link

Feature: raise! macro #92

Closed danielpclark closed 6 years ago

danielpclark commented 6 years ago

Looking at the source code for raise in Ruby the rb_raise is an alias for sprintf, or format! for us, and then pass the exception object on to be raised.

/* rb_raise is a sprintf formatter to exception raiser */
void
rb_raise(VALUE exc, const char *fmt, ...)
{
    va_list args;
    VALUE mesg;

    va_start(args, fmt);
    mesg = rb_vsprintf(fmt, args);
    va_end(args);
    rb_exc_raise(rb_exc_new3(exc, mesg));
}

/*
#define rb_exc_new3 rb_exc_new_str
*/

VALUE
rb_exc_new_str(VALUE etype, VALUE str)
{
    StringValue(str);
    return rb_class_new_instance(1, &str, etype);
}

/*!
 * Raises an exception in the current thread.
 * \param[in] mesg an Exception class or an \c Exception object.
 * \exception always raises an instance of the given exception class or
 *   the given \c Exception object.
 * \ingroup exception
 */
void
rb_exc_raise(VALUE mesg)
{
    if (!NIL_P(mesg)) {
    mesg = make_exception(1, &mesg, FALSE);
    }
    rb_longjmp(GET_EC(), TAG_RAISE, mesg, Qundef);
}

NORETURN(static void rb_longjmp(rb_execution_context_t *, int, volatile VALUE, VALUE));

static void
rb_longjmp(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE cause)
{
    mesg = exc_setup_message(ec, mesg, &cause);
    setup_exception(ec, tag, mesg, cause);
    rb_ec_raised_clear(ec);
    EC_JUMP_TAG(ec, tag);
}

/*
#define EC_JUMP_TAG(ec, st) rb_ec_tag_jump(ec, st)
*/

NORETURN(static inline void rb_ec_tag_jump(const rb_execution_context_t *ec, enum ruby_tag_type st));
static inline void
rb_ec_tag_jump(const rb_execution_context_t *ec, enum ruby_tag_type st)
{
    ec->tag->state = st;
    ruby_longjmp(ec->tag->buf, 1);
}

/*
#define ruby_longjmp(env,val) RUBY_LONGJMP((env),(val))
#define RUBY_LONGJMP(env,val) longjmp(env,val)
*/

Looking into longjmp that seems to be defined by the C language itself: “longjump(jmp_buf buf, i) : Go back to place buf is pointing to and return i”

My proposal for the raise! macro is to re-implement the sprintf behavior and the appropriate C code references to raise the exception back in Ruby.

I think this could work. It needs to be tested out first to verify we can do this.