Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
337 lines (261 sloc) 8.75 KB
#include "builtin/exception.hpp"
#include "builtin/array.hpp"
#include "builtin/proc.hpp"
#include "exception_point.hpp"
#include "capi/capi.hpp"
#include "capi/18/include/ruby.h"
using namespace rubinius;
extern "C" {
#define RB_EXC_BUFSIZE 256
void rb_raise(VALUE error_handle, const char* format_string, ...) {
va_list args;
char reason[RB_EXC_BUFSIZE];
va_start(args, format_string);
vsnprintf(reason, RB_EXC_BUFSIZE, format_string, args);
va_end(args);
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
Exception* exc = Exception::make_exception(
env->state(), as<Class>(env->get_object(error_handle)), reason);
capi::capi_raise_backend(exc);
rubinius::bug("rb_raise failed");
exit(1); // compiler snack.
}
void rb_throw(const char* symbol, VALUE result) {
rb_funcall(rb_mKernel, rb_intern("throw"), 2, ID2SYM(rb_intern(symbol)), result);
rubinius::bug("rb_throw failed");
exit(1); // compiler snack.
}
VALUE rb_rescue2(VALUE (*func)(ANYARGS), VALUE arg1,
VALUE (*raise_func)(ANYARGS), VALUE arg2, ...)
{
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
VALUE ret = Qnil;
ExceptionPoint ep(env);
va_list exc_classes;
PLACE_EXCEPTION_POINT(ep);
if(unlikely(ep.jumped_to())) {
ep.pop(env);
if(env->state()->thread_state()->raise_reason() != cException) {
//Something bad happened, we shouldn't be here.
return Qnil;
}
VALUE exc_handle = env->get_handle(env->state()->thread_state()->current_exception());
bool handle_exc = false;
va_start(exc_classes, arg2);
while(VALUE eclass = va_arg(exc_classes, VALUE)) {
if(rb_obj_is_kind_of(exc_handle, eclass) == Qtrue) {
handle_exc = true;
break;
}
}
va_end(exc_classes);
if(handle_exc) {
ret = (*raise_func)(arg2, exc_handle);
env->state()->thread_state()->clear_raise();
} else {
env->current_ep()->return_to(env);
}
} else {
ret = (*func)(arg1);
ep.pop(env);
}
return ret;
}
VALUE rb_rescue(VALUE (*func)(ANYARGS), VALUE arg1,
VALUE (*raise_func)(ANYARGS), VALUE arg2)
{
// Sending 0 as the last argument is an ugly hack, but we have to mimic MRI, so...
return rb_rescue2(func, arg1, raise_func, arg2, rb_eStandardError, 0);
}
VALUE rb_protect(VALUE (*func)(VALUE), VALUE data, int* status) {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
VALUE ret = Qnil;
ExceptionPoint ep(env);
PLACE_EXCEPTION_POINT(ep);
if(unlikely(ep.jumped_to())) {
if(status) *status = 1;
} else {
ret = (*func)(data);
if(status) *status = 0;
}
ep.pop(env);
return ret;
}
void rb_jump_tag(int status) {
if(!status) return;
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
// TODO should check if there is an ep?
env->current_ep()->return_to(env);
}
VALUE rb_ensure(VALUE (*func)(ANYARGS), VALUE arg1,
VALUE (*ensure_func)(ANYARGS), VALUE arg2)
{
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
VALUE ret = Qnil;
bool exc_raised = false;
ExceptionPoint ep(env);
PLACE_EXCEPTION_POINT(ep);
if(unlikely(ep.jumped_to())) {
exc_raised = true;
} else {
ret = (*func)(arg1);
}
ep.pop(env);
(*ensure_func)(arg2);
if(exc_raised) {
env->current_ep()->return_to(env);
}
return ret;
}
void rb_p(VALUE obj) {
rb_funcall(rb_mKernel, rb_intern("p"), 1, obj);
}
/* @note It is of dubious correctness that any C extension uses
* rb_bug, but some do. We basically mimic MRI behavior here.
*/
void rb_bug(const char *fmt, ...) {
va_list args;
fputs("[BUG]", stderr);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
rubinius::abort();
exit(1); // compiler snack.
}
void rb_fatal(const char *fmt, ...) {
va_list args;
char msg[RB_EXC_BUFSIZE];
va_start(args, fmt);
vsnprintf(msg, RB_EXC_BUFSIZE, fmt, args);
va_end(args);
rb_raise(rb_eFatal, msg);
}
void rb_notimplement() {
rb_fatal("not implemented");
}
/* rb_warn and rb_warning don't factor out creating the string for the
* message because it is just as much work preparing the va_list to
* pass to another variadic function.
*/
void rb_warn(const char *fmt, ...) {
va_list args;
char msg[RB_EXC_BUFSIZE];
va_start(args, fmt);
vsnprintf(msg, RB_EXC_BUFSIZE, fmt, args);
va_end(args);
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
String* string = String::create(env->state(), msg);
rb_funcall(rb_mKernel, rb_intern("warn"), 1, env->get_handle(string));
}
void rb_warning(const char* fmt, ...) {
va_list args;
char msg[RB_EXC_BUFSIZE];
va_start(args, fmt);
vsnprintf(msg, RB_EXC_BUFSIZE, fmt, args);
va_end(args);
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
String* string = String::create(env->state(), msg);
rb_funcall(rb_mKernel, rb_intern("warning"), 1, env->get_handle(string));
}
VALUE rb_require(const char* name) {
return rb_funcall(rb_mKernel, rb_intern("require"), 1, rb_str_new2(name));
}
void rb_sys_fail(const char* mesg) {
if(!mesg) mesg = "system failure";
rb_funcall(rb_mErrno, rb_intern("handle"), 1, rb_str_new2(mesg));
rubinius::bug("rb_sys_fail failed");
exit(1); // compiler snack.
}
VALUE rb_eval_string(const char* str) {
return rb_funcall(rb_mKernel, rb_intern("eval"), 1, rb_str_new2(str));
}
VALUE rb_block_proc() {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
return rb_funcall(rb_cProc, rb_intern("__from_block__"), 1, env->get_handle(env->block()));
}
// Hoisted from 1.8.7
static VALUE recursive_check(VALUE hash, VALUE obj) {
if(NIL_P(hash) || TYPE(hash) != T_HASH) {
return Qfalse;
} else {
VALUE list = rb_hash_aref(hash, ID2SYM(rb_frame_last_func()));
if(NIL_P(list) || TYPE(list) != T_HASH) return Qfalse;
if(NIL_P(rb_hash_lookup(list, obj))) return Qfalse;
return Qtrue;
}
}
static VALUE recursive_push(VALUE hash, VALUE obj) {
VALUE list, sym;
sym = ID2SYM(rb_frame_last_func());
if(NIL_P(hash) || TYPE(hash) != T_HASH) {
hash = rb_hash_new();
rb_thread_local_aset(rb_thread_current(),
rb_intern("__recursive_key"), hash);
list = Qnil;
} else {
list = rb_hash_aref(hash, sym);
}
if(NIL_P(list) || TYPE(list) != T_HASH) {
list = rb_hash_new();
rb_hash_aset(hash, sym, list);
}
rb_hash_aset(list, obj, Qtrue);
return hash;
}
static void recursive_pop(VALUE hash, VALUE obj) {
VALUE list, sym;
sym = ID2SYM(rb_frame_last_func());
if(NIL_P(hash) || TYPE(hash) != T_HASH) {
VALUE symname;
VALUE thrname;
symname = rb_inspect(sym);
thrname = rb_inspect(rb_thread_current());
rb_raise(rb_eTypeError, "invalid inspect_tbl hash for %s in %s",
StringValuePtr(symname), StringValuePtr(thrname));
}
list = rb_hash_aref(hash, sym);
if(NIL_P(list) || TYPE(list) != T_HASH) {
VALUE symname = rb_inspect(sym);
VALUE thrname = rb_inspect(rb_thread_current());
rb_raise(rb_eTypeError, "invalid inspect_tbl list for %s in %s",
StringValuePtr(symname), StringValuePtr(thrname));
}
rb_hash_delete(list, obj);
}
VALUE rb_exec_recursive(VALUE (*func)(VALUE, VALUE, int),
VALUE obj, VALUE arg)
{
VALUE hash = rb_thread_local_aref(rb_thread_current(),
rb_intern("__recursive_key"));
VALUE objid = rb_obj_id(obj);
if(recursive_check(hash, objid)) {
return (*func)(obj, arg, 1);
} else {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
VALUE ret = Qnil;
ExceptionPoint ep(env);
PLACE_EXCEPTION_POINT(ep);
bool unwinding = false;
hash = recursive_push(hash, objid);
if(unlikely(ep.jumped_to())) {
unwinding = true;
} else {
ret = (*func)(obj, arg, 0);
}
ep.pop(env);
recursive_pop(hash, objid);
if(unwinding) env->current_ep()->return_to(env);
return ret;
}
}
void rb_set_end_proc(void* cb, VALUE cb_data) {
NativeMethodEnvironment* env = NativeMethodEnvironment::get();
VALUE prc = env->get_handle(
capi::wrap_c_function(cb, cb_data, C_CALLBACK));
rb_funcall(rb_mKernel, rb_intern("at_exit"), 1, prc);
}
VALUE rb_f_sprintf(int argc, const VALUE* argv) {
VALUE ary = rb_ary_new4(argc-1, argv+1);
return rb_funcall(rb_mCAPI, rb_intern("sprintf"), 2, argv[0], ary);
}
}
Something went wrong with that request. Please try again.