summaryrefslogtreecommitdiff
path: root/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'eval.c')
-rw-r--r--eval.c3683
1 files changed, 1558 insertions, 2125 deletions
diff --git a/eval.c b/eval.c
index 2f6300bdb7..deadd5dd64 100644
--- a/eval.c
+++ b/eval.c
@@ -3,7 +3,6 @@
eval.c -
$Author$
- $Date$
created at: Thu Jun 10 14:22:17 JST 1993
Copyright (C) 1993-2007 Yukihiro Matsumoto
@@ -12,231 +11,278 @@
**********************************************************************/
-#include "eval_intern.h"
-
-VALUE proc_invoke(VALUE, VALUE, VALUE, VALUE);
-VALUE rb_binding_new();
+#include "ruby/internal/config.h"
-VALUE rb_f_block_given_p(void);
-
-ID rb_frame_callee(void);
-static VALUE rb_frame_self(void);
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
-static ID removed, singleton_removed, undefined, singleton_undefined;
-static ID init, eqq, each, aref, aset, match, missing;
-static ID added, singleton_added;
-static ID object_id, __send__, respond_to;
+#include "eval_intern.h"
+#include "internal.h"
+#include "internal/class.h"
+#include "internal/cont.h"
+#include "internal/error.h"
+#include "internal/eval.h"
+#include "internal/gc.h"
+#include "internal/hash.h"
+#include "internal/inits.h"
+#include "internal/io.h"
+#include "internal/object.h"
+#include "internal/thread.h"
+#include "internal/variable.h"
+#include "ruby/fiber/scheduler.h"
+#include "iseq.h"
+#include "probes.h"
+#include "probes_helper.h"
+#include "ruby/vm.h"
+#include "vm_core.h"
+#include "ractor_core.h"
+
+NORETURN(static void rb_raise_jump(VALUE, VALUE));
+void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec);
+void rb_ec_clear_all_trace_func(const rb_execution_context_t *ec);
+
+static int rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex);
+static int rb_ec_exec_node(rb_execution_context_t *ec, void *n);
VALUE rb_eLocalJumpError;
VALUE rb_eSysStackError;
-VALUE sysstack_error;
-
-static VALUE exception_error;
-static VALUE eval(VALUE, VALUE, VALUE, const char *, int);
+ID ruby_static_id_signo, ruby_static_id_status;
+extern ID ruby_static_id_cause;
+#define id_cause ruby_static_id_cause
-static inline VALUE rb_yield_0(int argc, VALUE *argv);
-static VALUE rb_call(VALUE, VALUE, ID, int, const VALUE *, int);
+#define exception_error GET_VM()->special_exceptions[ruby_error_reenter]
-#include "eval_error.ci"
-#include "eval_method.ci"
-#include "eval_safe.ci"
-#include "eval_jump.ci"
+#include "eval_error.c"
+#include "eval_jump.c"
-/* initialize ruby */
+#define CLASS_OR_MODULE_P(obj) \
+ (!SPECIAL_CONST_P(obj) && \
+ (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE))
-#if defined(__APPLE__)
-#define environ (*_NSGetEnviron())
-#elif !defined(_WIN32) && !defined(__MACOS__) || defined(_WIN32_WCE)
-extern char **environ;
-#endif
-char **rb_origenviron;
-
-jmp_buf function_call_may_return_twice_jmp_buf;
-int function_call_may_return_twice_false = 0;
-
-void rb_clear_trace_func(void);
-void rb_thread_stop_timer_thread(void);
-
-void rb_call_inits _((void));
-void Init_stack _((VALUE *));
-void Init_heap _((void));
-void Init_ext _((void));
-void Init_BareVM(void);
-
-void
-ruby_init(void)
+int
+ruby_setup(void)
{
- static int initialized = 0;
- int state;
+ enum ruby_tag_type state;
- if (initialized)
- return;
- initialized = 1;
+ if (GET_VM())
+ return 0;
-#ifdef __MACOS__
- rb_origenviron = 0;
-#else
- rb_origenviron = environ;
+ /*
+ * Disable THP early before mallocs happen because we want this to
+ * affect as many future pages as possible for CoW-friendliness
+ */
+#if defined(__linux__) && defined(PR_SET_THP_DISABLE)
+ prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0);
#endif
-
- Init_stack((void *)&state);
Init_BareVM();
- Init_heap();
-
- PUSH_TAG();
- if ((state = EXEC_TAG()) == 0) {
- rb_call_inits();
+ rb_vm_encoded_insn_data_table_init();
+ Init_enable_box();
+ Init_vm_objects();
+ Init_root_box();
+ Init_fstring_table();
-#ifdef __MACOS__
- _macruby_init();
-#elif defined(__VMS)
- _vmsruby_init();
-#endif
-
- ruby_prog_init();
- ALLOW_INTS;
+ EC_PUSH_TAG(GET_EC());
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ rb_call_inits();
+ ruby_prog_init();
+ GET_VM()->running = 1;
}
- POP_TAG();
+ EC_POP_TAG();
+
+ return state;
+}
+void
+ruby_init(void)
+{
+ int state = ruby_setup();
if (state) {
- error_print();
- exit(EXIT_FAILURE);
+ if (RTEST(ruby_debug)) {
+ rb_execution_context_t *ec = GET_EC();
+ rb_ec_error_print(ec, ec->errinfo);
+ }
+ exit(EXIT_FAILURE);
}
- ruby_running = 1;
}
-extern void rb_clear_trace_func(void);
-
void *
ruby_options(int argc, char **argv)
{
- int state;
- void *tree = 0;
+ rb_execution_context_t *ec = GET_EC();
+ enum ruby_tag_type state;
+ void *volatile iseq = 0;
- Init_stack((void *)&state);
- PUSH_TAG();
- if ((state = EXEC_TAG()) == 0) {
- SAVE_ROOT_JMPBUF(GET_THREAD(), tree = ruby_process_options(argc, argv));
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ iseq = ruby_process_options(argc, argv);
}
else {
- rb_clear_trace_func();
- state = error_handle(state);
- tree = (void *)INT2FIX(state);
+ rb_ec_clear_current_thread_trace_func(ec);
+ int exitcode = error_handle(ec, ec->errinfo, state);
+ ec->errinfo = Qnil; /* just been handled */
+ iseq = (void *)INT2FIX(exitcode);
}
- POP_TAG();
- return tree;
+ EC_POP_TAG();
+ return iseq;
}
static void
-ruby_finalize_0(void)
+rb_ec_fiber_scheduler_finalize(rb_execution_context_t *ec)
{
- rb_clear_trace_func();
- PUSH_TAG();
- if (EXEC_TAG() == 0) {
- rb_trap_exit();
+ enum ruby_tag_type state;
+
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ rb_fiber_scheduler_set(Qnil);
}
- POP_TAG();
- rb_exec_end_proc();
+ else {
+ state = error_handle(ec, ec->errinfo, state);
+ }
+ EC_POP_TAG();
}
static void
-ruby_finalize_1(void)
+rb_ec_teardown(rb_execution_context_t *ec)
{
- signal(SIGINT, SIG_DFL);
- GET_THREAD()->errinfo = Qnil;
- rb_gc_call_finalizer_at_exit();
+ // If the user code defined a scheduler for the top level thread, run it:
+ rb_ec_fiber_scheduler_finalize(ec);
+
+ EC_PUSH_TAG(ec);
+ if (EC_EXEC_TAG() == TAG_NONE) {
+ rb_vm_trap_exit(rb_ec_vm_ptr(ec));
+ }
+ EC_POP_TAG();
+ rb_ec_exec_end_proc(ec);
+ rb_ec_clear_all_trace_func(ec);
+}
+
+static void
+rb_ec_finalize(rb_execution_context_t *ec)
+{
+ ruby_sig_finalize();
+ ec->errinfo = Qnil;
+ rb_objspace_call_finalizer();
}
void
ruby_finalize(void)
{
- ruby_finalize_0();
- ruby_finalize_1();
+ rb_execution_context_t *ec = GET_EC();
+ rb_ec_teardown(ec);
+ rb_ec_finalize(ec);
}
-void rb_thread_stop_timer_thread(void);
-
int
ruby_cleanup(int ex)
{
- int state;
- volatile VALUE errs[2];
- rb_thread_t *th = GET_THREAD();
- int nerr;
-
- errs[1] = th->errinfo;
- th->safe_level = 0;
- Init_stack((void *)&state);
- ruby_finalize_0();
- errs[0] = th->errinfo;
- PUSH_TAG();
- if ((state = EXEC_TAG()) == 0) {
- SAVE_ROOT_JMPBUF(th, rb_thread_terminate_all());
- }
- else if (ex == 0) {
- ex = state;
- }
- th->errinfo = errs[1];
- ex = error_handle(ex);
- ruby_finalize_1();
- POP_TAG();
- rb_thread_stop_timer_thread();
-
- for (nerr = 0; nerr < sizeof(errs) / sizeof(errs[0]); ++nerr) {
- VALUE err = errs[nerr];
-
- if (!RTEST(err)) continue;
-
- /* th->errinfo contains a NODE while break'ing */
- if (TYPE(err) == T_NODE) continue;
+ return rb_ec_cleanup(GET_EC(), (enum ruby_tag_type)ex);
+}
- if (rb_obj_is_kind_of(err, rb_eSystemExit)) {
- return sysexit_status(err);
- }
- else if (rb_obj_is_kind_of(err, rb_eSignal)) {
- VALUE sig = rb_iv_get(err, "signo");
- ruby_default_signal(NUM2INT(sig));
- }
- else if (ex == 0) {
- ex = 1;
- }
+static int
+rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex)
+{
+ int state;
+ volatile VALUE save_error = Qundef;
+ volatile int sysex = EXIT_SUCCESS;
+ volatile int signaled = 0;
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
+ rb_thread_t *const volatile th0 = th;
+ volatile int step = 0;
+ volatile VALUE message = Qnil;
+ VALUE buf;
+
+ rb_threadptr_interrupt(th);
+ rb_threadptr_check_signal(th);
+
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ RUBY_VM_CHECK_INTS(ec);
+
+ step_0: step++;
+ save_error = ec->errinfo;
+ if (THROW_DATA_P(ec->errinfo)) ec->errinfo = Qnil;
+
+ /* exits with failure but silently when an exception raised
+ * here */
+ rb_ec_teardown(ec);
+
+ step_1: step++;
+ VALUE err = ec->errinfo;
+ volatile int mode0 = 0, mode1 = 0;
+ if (err != save_error && !NIL_P(err)) {
+ mode0 = exiting_split(err, &sysex, &signaled);
+ }
+
+ /* exceptions after here will be ignored */
+
+ /* build error message including causes */
+ err = ATOMIC_VALUE_EXCHANGE(save_error, Qnil);
+
+ if (!NIL_P(err) && !THROW_DATA_P(err)) {
+ mode1 = exiting_split(err, (mode0 & EXITING_WITH_STATUS) ? NULL : &sysex, &signaled);
+ if (mode1 & EXITING_WITH_MESSAGE) {
+ buf = rb_str_new(NULL, 0);
+ rb_ec_error_print_detailed(ec, err, buf, Qundef);
+ message = buf;
+ }
+ }
+
+ step_2: step++;
+ /* protect from Thread#raise */
+ th->status = THREAD_KILLED;
+
+ rb_ractor_terminate_all();
+
+ step_3: step++;
+ if (!NIL_P(buf = message)) {
+ warn_print_str(buf);
+ }
+ else if (!NIL_OR_UNDEF_P(err = save_error) ||
+ (ex != TAG_NONE && !((mode0|mode1) & EXITING_WITH_STATUS))) {
+ sysex = error_handle(ec, err, ex);
+ }
}
+ else {
+ th = th0;
+ switch (step) {
+ case 0: goto step_0;
+ case 1: goto step_1;
+ case 2: goto step_2;
+ case 3: goto step_3;
+ }
+ }
+
+ rb_ec_finalize(ec);
+
+ /* unlock again if finalizer took mutexes. */
+ rb_threadptr_unlock_all_locking_mutexes(th);
+ th = th0;
+ EC_POP_TAG();
+ th = th0;
+ rb_thread_stop_timer_thread();
+ ruby_vm_destruct(th->vm);
+ // For YJIT, call this after ruby_vm_destruct() frees jit_cont for the root fiber.
+ rb_jit_cont_finish();
-#if EXIT_SUCCESS != 0 || EXIT_FAILURE != 1
- switch (ex) {
-#if EXIT_SUCCESS != 0
- case 0: return EXIT_SUCCESS;
-#endif
-#if EXIT_FAILURE != 1
- case 1: return EXIT_FAILURE;
-#endif
- }
-#endif
+ if (signaled) ruby_default_signal(signaled);
- return ex;
+ return sysex;
}
-int
-ruby_exec_node(void *n, char *file)
+static int
+rb_ec_exec_node(rb_execution_context_t *ec, void *n)
{
- int state;
- VALUE val;
- NODE *node = n;
- rb_thread_t *th = GET_THREAD();
-
- if (!node) return 0;
+ volatile int state;
+ rb_iseq_t *iseq = (rb_iseq_t *)n;
+ if (!n) return 0;
- PUSH_TAG();
- if ((state = EXEC_TAG()) == 0) {
- SAVE_ROOT_JMPBUF(th, {
- VALUE iseq = rb_iseq_new(n, rb_str_new2("<main>"),
- rb_str_new2(file), Qfalse, ISEQ_TYPE_TOP);
- th->base_block = 0;
- val = rb_iseq_eval(iseq);
- });
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ rb_iseq_eval_main(iseq);
}
- POP_TAG();
+ EC_POP_TAG();
return state;
}
@@ -247,101 +293,43 @@ ruby_stop(int ex)
}
int
-ruby_run_node(void *n)
+ruby_executable_node(void *n, int *status)
{
- NODE *node = (NODE *)n;
+ VALUE v = (VALUE)n;
+ int s;
- switch ((VALUE)n) {
- case Qtrue: return EXIT_SUCCESS;
- case Qfalse: return EXIT_FAILURE;
- }
- if (FIXNUM_P((VALUE)n)) {
- return FIX2INT((VALUE)n);
+ switch (v) {
+ case Qtrue: s = EXIT_SUCCESS; break;
+ case Qfalse: s = EXIT_FAILURE; break;
+ default:
+ if (!FIXNUM_P(v)) return TRUE;
+ s = FIX2INT(v);
}
- Init_stack((void *)&n);
- return ruby_cleanup(ruby_exec_node(node, node->nd_file));
-}
-
-VALUE
-rb_eval_string(const char *str)
-{
- return eval(rb_vm_top_self(), rb_str_new2(str), Qnil, "(eval)", 1);
+ if (status) *status = s;
+ return FALSE;
}
-VALUE
-rb_eval_string_protect(const char *str, int *state)
-{
- return rb_protect((VALUE (*)_((VALUE)))rb_eval_string, (VALUE)str, state);
-}
-
-VALUE
-rb_eval_string_wrap(const char *str, int *state)
+int
+ruby_run_node(void *n)
{
+ rb_execution_context_t *ec = GET_EC();
int status;
- rb_thread_t *th = GET_THREAD();
- VALUE self = th->top_self;
- VALUE wrapper = th->top_wrapper;
- VALUE val;
-
- th->top_wrapper = rb_module_new();
- th->top_self = rb_obj_clone(rb_vm_top_self());
- rb_extend_object(th->top_self, th->top_wrapper);
-
- val = rb_eval_string_protect(str, &status);
-
- th->top_self = self;
- th->top_wrapper = wrapper;
-
- if (state) {
- *state = status;
+ if (!ruby_executable_node(n, &status)) {
+ rb_ec_cleanup(ec, (NIL_P(ec->errinfo) ? TAG_NONE : TAG_RAISE));
+ return status;
}
- else if (status) {
- JUMP_TAG(status);
- }
- return val;
+ return rb_ec_cleanup(ec, rb_ec_exec_node(ec, n));
}
-VALUE
-rb_eval_cmd(VALUE cmd, VALUE arg, int level)
+int
+ruby_exec_node(void *n)
{
- int state;
- VALUE val = Qnil; /* OK */
- volatile int safe = rb_safe_level();
-
- if (OBJ_TAINTED(cmd)) {
- level = 4;
- }
-
- if (TYPE(cmd) != T_STRING) {
- PUSH_TAG();
- rb_set_safe_level_force(level);
- if ((state = EXEC_TAG()) == 0) {
- val = rb_funcall2(cmd, rb_intern("call"), RARRAY_LEN(arg),
- RARRAY_PTR(arg));
- }
- POP_TAG();
-
- rb_set_safe_level_force(safe);
-
- if (state)
- JUMP_TAG(state);
- return val;
- }
-
- PUSH_TAG();
- if ((state = EXEC_TAG()) == 0) {
- val = eval(rb_vm_top_self(), cmd, Qnil, 0, 0);
- }
- POP_TAG();
-
- rb_set_safe_level_force(safe);
- if (state) vm_jump_tag_but_local_jump(state, val);
- return val;
+ return rb_ec_exec_node(GET_EC(), n);
}
/*
* call-seq:
- * Module.nesting => array
+ * Module.nesting -> array
*
* Returns the list of +Modules+ nested at the point of call.
*
@@ -355,2030 +343,1513 @@ rb_eval_cmd(VALUE cmd, VALUE arg, int level)
*/
static VALUE
-rb_mod_nesting(void)
+rb_mod_nesting(VALUE _)
{
VALUE ary = rb_ary_new();
- NODE *cref = ruby_cref();
+ const rb_cref_t *cref = rb_vm_cref();
- while (cref && cref->nd_next) {
- VALUE klass = cref->nd_clss;
- if (!NIL_P(klass)) {
- rb_ary_push(ary, klass);
- }
- cref = cref->nd_next;
+ while (cref && CREF_NEXT(cref)) {
+ VALUE klass = CREF_CLASS(cref);
+ if (!CREF_PUSHED_BY_EVAL(cref) &&
+ !NIL_P(klass)) {
+ rb_ary_push(ary, klass);
+ }
+ cref = CREF_NEXT(cref);
}
return ary;
}
/*
* call-seq:
- * Module.constants => array
+ * Module.constants -> array
+ * Module.constants(inherited) -> array
*
- * Returns an array of the names of all constants defined in the
- * system. This list includes the names of all modules and classes.
+ * In the first form, returns an array of the names of all
+ * constants accessible from the point of call.
+ * This list includes the names of all modules and classes
+ * defined in the global scope.
*
- * p Module.constants.sort[1..5]
+ * Module.constants.first(4)
+ * # => [:ARGF, :ARGV, :ArgumentError, :Array]
*
- * <em>produces:</em>
+ * Module.constants.include?(:SEEK_SET) # => false
+ *
+ * class IO
+ * Module.constants.include?(:SEEK_SET) # => true
+ * end
*
- * ["ARGV", "ArgumentError", "Array", "Bignum", "Binding"]
+ * The second form calls the instance method +constants+.
*/
static VALUE
rb_mod_s_constants(int argc, VALUE *argv, VALUE mod)
{
- NODE *cref = ruby_cref();
+ const rb_cref_t *cref = rb_vm_cref();
VALUE klass;
VALUE cbase = 0;
void *data = 0;
- if (argc > 0) {
- return rb_mod_constants(argc, argv, rb_cModule);
+ if (argc > 0 || mod != rb_cModule) {
+ return rb_mod_constants(argc, argv, mod);
}
while (cref) {
- klass = cref->nd_clss;
- if (!NIL_P(klass)) {
- data = rb_mod_const_at(cref->nd_clss, data);
- if (!cbase) {
- cbase = klass;
- }
- }
- cref = cref->nd_next;
+ klass = CREF_CLASS(cref);
+ if (!CREF_PUSHED_BY_EVAL(cref) &&
+ !NIL_P(klass)) {
+ data = rb_mod_const_at(CREF_CLASS(cref), data);
+ if (!cbase) {
+ cbase = klass;
+ }
+ }
+ cref = CREF_NEXT(cref);
}
if (cbase) {
- data = rb_mod_const_of(cbase, data);
+ data = rb_mod_const_of(cbase, data);
}
return rb_const_list(data);
}
+/**
+ * Asserts that `klass` is not a frozen class.
+ * @param[in] klass a `Module` object
+ * @exception RuntimeError if `klass` is not a class or frozen.
+ * @ingroup class
+ */
void
-rb_frozen_class_p(VALUE klass)
+rb_class_modify_check(VALUE klass)
{
- char *desc = "something(?!)";
-
+ if (SPECIAL_CONST_P(klass)) {
+ Check_Type(klass, T_CLASS);
+ }
+ if (RB_TYPE_P(klass, T_MODULE)) {
+ // TODO: shouldn't this only happen in a few places?
+ rb_class_set_initialized(klass);
+ }
if (OBJ_FROZEN(klass)) {
- if (FL_TEST(klass, FL_SINGLETON))
- desc = "object";
- else {
- switch (TYPE(klass)) {
- case T_MODULE:
- case T_ICLASS:
- desc = "module";
- break;
- case T_CLASS:
- desc = "class";
- break;
- }
- }
- rb_error_frozen(desc);
+ if (RCLASS_SINGLETON_P(klass)) {
+ klass = RCLASS_ATTACHED_OBJECT(klass);
+ }
+ rb_error_frozen_object(klass);
}
}
-/*
- * call-seq:
- * obj.respond_to?(symbol, include_private=false) => true or false
- *
- * Returns +true+> if _obj_ responds to the given
- * method. Private methods are included in the search only if the
- * optional second parameter evaluates to +true+.
- */
-
-static NODE *basic_respond_to = 0;
+NORETURN(static void rb_longjmp(rb_execution_context_t *, enum ruby_tag_type, volatile VALUE, VALUE));
+static VALUE get_errinfo(void);
+#define get_ec_errinfo(ec) rb_ec_get_errinfo(ec)
-int
-rb_obj_respond_to(VALUE obj, ID id, int priv)
+static VALUE
+exc_setup_cause(VALUE exc, VALUE cause)
{
- VALUE klass = CLASS_OF(obj);
+#if OPT_SUPPORT_JOKE
+ if (NIL_P(cause)) {
+ ID id_true_cause;
+ CONST_ID(id_true_cause, "true_cause");
- if (rb_method_node(klass, respond_to) == basic_respond_to) {
- return rb_method_boundp(klass, id, !priv);
+ cause = rb_attr_get(rb_eFatal, id_true_cause);
+ if (NIL_P(cause)) {
+ cause = rb_exc_new_cstr(rb_eFatal, "because using such Ruby");
+ rb_ivar_set(cause, id_cause, INT2FIX(42)); /* the answer */
+ OBJ_FREEZE(cause);
+ rb_ivar_set(rb_eFatal, id_true_cause, cause);
+ }
}
- else {
- VALUE args[2];
- int n = 0;
- args[n++] = ID2SYM(id);
- if (priv)
- args[n++] = Qtrue;
- return rb_funcall2(obj, respond_to, n, args);
+#endif
+ if (!NIL_P(cause) && cause != exc) {
+ rb_ivar_set(exc, id_cause, cause);
+ if (!rb_ivar_defined(cause, id_cause)) {
+ rb_ivar_set(cause, id_cause, Qnil);
+ }
}
+ return exc;
}
-int
-rb_respond_to(VALUE obj, ID id)
-{
- return rb_obj_respond_to(obj, id, Qfalse);
-}
-
-/*
- * call-seq:
- * obj.respond_to?(symbol, include_private=false) => true or false
- *
- * Returns +true+> if _obj_ responds to the given
- * method. Private methods are included in the search only if the
- * optional second parameter evaluates to +true+.
- */
-
-static VALUE
-obj_respond_to(int argc, VALUE *argv, VALUE obj)
+static inline VALUE
+exc_setup_message(const rb_execution_context_t *ec, VALUE mesg, VALUE *cause)
{
- VALUE mid, priv;
- ID id;
+ int nocause = 0;
+ int nocircular = 0;
- rb_scan_args(argc, argv, "11", &mid, &priv);
- id = rb_to_id(mid);
- if (rb_method_boundp(CLASS_OF(obj), id, !RTEST(priv))) {
- return Qtrue;
+ if (NIL_P(mesg)) {
+ mesg = ec->errinfo;
+ if (INTERNAL_EXCEPTION_P(mesg)) EC_JUMP_TAG(ec, TAG_FATAL);
+ nocause = 1;
}
- return Qfalse;
-}
-
-/*
- * call-seq:
- * mod.method_defined?(symbol) => true or false
- *
- * Returns +true+ if the named method is defined by
- * _mod_ (or its included modules and, if _mod_ is a class,
- * its ancestors). Public and protected methods are matched.
- *
- * module A
- * def method1() end
- * end
- * class B
- * def method2() end
- * end
- * class C < B
- * include A
- * def method3() end
- * end
- *
- * A.method_defined? :method1 #=> true
- * C.method_defined? "method1" #=> true
- * C.method_defined? "method2" #=> true
- * C.method_defined? "method3" #=> true
- * C.method_defined? "method4" #=> false
- */
-
-static VALUE
-rb_mod_method_defined(mod, mid)
- VALUE mod, mid;
-{
- return rb_method_boundp(mod, rb_to_id(mid), 1);
+ if (NIL_P(mesg)) {
+ mesg = rb_exc_new(rb_eRuntimeError, 0, 0);
+ nocause = 0;
+ nocircular = 1;
+ }
+ if (UNDEF_P(*cause)) {
+ if (nocause) {
+ *cause = Qnil;
+ nocircular = 1;
+ }
+ else if (!rb_ivar_defined(mesg, id_cause)) {
+ *cause = get_ec_errinfo(ec);
+ }
+ else {
+ nocircular = 1;
+ }
+ }
+ else if (!NIL_P(*cause) && !rb_obj_is_kind_of(*cause, rb_eException)) {
+ rb_raise(rb_eTypeError, "exception object expected");
+ }
+
+ if (!nocircular && !NIL_P(*cause) && !UNDEF_P(*cause) && *cause != mesg) {
+#if 0 /* maybe critical for some cases */
+ rb_exc_check_circular_cause(*cause);
+#else
+ VALUE c = *cause;
+ while (!NIL_P(c)) {
+ if (c == mesg) {
+ rb_raise(rb_eArgError, "circular causes");
+ }
+ if (THROW_DATA_P(c)) {
+ break;
+ }
+ c = rb_attr_get(c, id_cause);
+ }
+#endif
+ }
+ return mesg;
}
-#define VISI_CHECK(x,f) (((x)&NOEX_MASK) == (f))
-
-/*
- * call-seq:
- * mod.public_method_defined?(symbol) => true or false
- *
- * Returns +true+ if the named public method is defined by
- * _mod_ (or its included modules and, if _mod_ is a class,
- * its ancestors).
- *
- * module A
- * def method1() end
- * end
- * class B
- * protected
- * def method2() end
- * end
- * class C < B
- * include A
- * def method3() end
- * end
- *
- * A.method_defined? :method1 #=> true
- * C.public_method_defined? "method1" #=> true
- * C.public_method_defined? "method2" #=> false
- * C.method_defined? "method2" #=> true
- */
-
-static VALUE
-rb_mod_public_method_defined(VALUE mod, VALUE mid)
+static void
+setup_exception(rb_execution_context_t *ec, enum ruby_tag_type tag, volatile VALUE mesg, VALUE cause)
{
- ID id = rb_to_id(mid);
- NODE *method;
-
- method = rb_method_node(mod, id);
- if (method) {
- if (VISI_CHECK(method->nd_noex, NOEX_PUBLIC))
- return Qtrue;
+ VALUE e;
+ int line;
+ const char *file = rb_source_location_cstr(&line);
+ const char *const volatile file0 = file;
+
+ if ((file && !NIL_P(mesg)) || !UNDEF_P(cause)) {
+ volatile int state = 0;
+
+ EC_PUSH_TAG(ec);
+ if (EC_EXEC_TAG() == TAG_NONE && !(state = rb_ec_set_raised(ec))) {
+ VALUE bt = rb_get_backtrace(mesg);
+ if (!NIL_P(bt) || UNDEF_P(cause)) {
+ if (OBJ_FROZEN(mesg)) {
+ mesg = rb_obj_dup(mesg);
+ }
+ }
+ if (!UNDEF_P(cause) && !THROW_DATA_P(cause)) {
+ exc_setup_cause(mesg, cause);
+ }
+ if (NIL_P(bt)) {
+ VALUE at = rb_ec_backtrace_object(ec);
+ rb_ivar_set(mesg, idBt_locations, at);
+ set_backtrace(mesg, at);
+ }
+ rb_ec_reset_raised(ec);
+ }
+ EC_POP_TAG();
+ file = file0;
+ if (state) goto fatal;
}
- return Qfalse;
-}
-
-/*
- * call-seq:
- * mod.private_method_defined?(symbol) => true or false
- *
- * Returns +true+ if the named private method is defined by
- * _ mod_ (or its included modules and, if _mod_ is a class,
- * its ancestors).
- *
- * module A
- * def method1() end
- * end
- * class B
- * private
- * def method2() end
- * end
- * class C < B
- * include A
- * def method3() end
- * end
- *
- * A.method_defined? :method1 #=> true
- * C.private_method_defined? "method1" #=> false
- * C.private_method_defined? "method2" #=> true
- * C.method_defined? "method2" #=> false
- */
-static VALUE
-rb_mod_private_method_defined(VALUE mod, VALUE mid)
-{
- ID id = rb_to_id(mid);
- NODE *method;
+ if (!NIL_P(mesg)) {
+ ec->errinfo = mesg;
+ }
+
+ if (RTEST(ruby_debug) && !NIL_P(e = ec->errinfo) &&
+ !rb_obj_is_kind_of(e, rb_eSystemExit)) {
+ enum ruby_tag_type state;
+
+ mesg = e;
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ ec->errinfo = Qnil;
+ e = rb_obj_as_string(mesg);
+ ec->errinfo = mesg;
+ if (file && line) {
+ e = rb_sprintf("Exception '%"PRIsVALUE"' at %s:%d - %"PRIsVALUE"\n",
+ rb_obj_class(mesg), file, line, e);
+ }
+ else if (file) {
+ e = rb_sprintf("Exception '%"PRIsVALUE"' at %s - %"PRIsVALUE"\n",
+ rb_obj_class(mesg), file, e);
+ }
+ else {
+ e = rb_sprintf("Exception '%"PRIsVALUE"' - %"PRIsVALUE"\n",
+ rb_obj_class(mesg), e);
+ }
+ warn_print_str(e);
+ }
+ EC_POP_TAG();
+ if (state == TAG_FATAL && ec->errinfo == exception_error) {
+ ec->errinfo = mesg;
+ }
+ else if (state) {
+ rb_ec_reset_raised(ec);
+ EC_JUMP_TAG(ec, state);
+ }
+ }
+
+ if (rb_ec_set_raised(ec)) {
+ goto fatal;
+ }
- method = rb_method_node(mod, id);
- if (method) {
- if (VISI_CHECK(method->nd_noex, NOEX_PRIVATE))
- return Qtrue;
+ if (tag != TAG_FATAL) {
+ RUBY_DTRACE_HOOK(RAISE, rb_obj_classname(ec->errinfo));
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_RAISE, ec->cfp->self, 0, 0, 0, mesg);
}
- return Qfalse;
-}
+ return;
-/*
- * call-seq:
- * mod.protected_method_defined?(symbol) => true or false
- *
- * Returns +true+ if the named protected method is defined
- * by _mod_ (or its included modules and, if _mod_ is a
- * class, its ancestors).
- *
- * module A
- * def method1() end
- * end
- * class B
- * protected
- * def method2() end
- * end
- * class C < B
- * include A
- * def method3() end
- * end
- *
- * A.method_defined? :method1 #=> true
- * C.protected_method_defined? "method1" #=> false
- * C.protected_method_defined? "method2" #=> true
- * C.method_defined? "method2" #=> true
- */
+ fatal:
+ ec->errinfo = exception_error;
+ rb_ec_reset_raised(ec);
+ EC_JUMP_TAG(ec, TAG_FATAL);
+}
-static VALUE
-rb_mod_protected_method_defined(VALUE mod, VALUE mid)
+/*! \private */
+void
+rb_ec_setup_exception(const rb_execution_context_t *ec, VALUE mesg, VALUE cause)
{
- ID id = rb_to_id(mid);
- NODE *method;
+ if (UNDEF_P(cause)) {
+ cause = get_ec_errinfo(ec);
+ }
+ if (cause != mesg) {
+ if (THROW_DATA_P(cause)) {
+ cause = Qnil;
+ }
- method = rb_method_node(mod, id);
- if (method) {
- if (VISI_CHECK(method->nd_noex, NOEX_PROTECTED))
- return Qtrue;
+ rb_ivar_set(mesg, id_cause, cause);
}
- return Qfalse;
}
-NORETURN(static void rb_longjmp(int, VALUE));
-static VALUE make_backtrace(void);
-
static void
-rb_longjmp(int tag, VALUE mesg)
+rb_longjmp(rb_execution_context_t *ec, enum ruby_tag_type tag, volatile VALUE mesg, VALUE cause)
{
- VALUE at;
- VALUE e;
- rb_thread_t *th = GET_THREAD();
- const char *file;
- int line = 0;
+ mesg = exc_setup_message(ec, mesg, &cause);
+ setup_exception(ec, tag, mesg, cause);
+ rb_ec_raised_clear(ec);
+ EC_JUMP_TAG(ec, tag);
+}
- if (thread_set_raised(th)) {
- th->errinfo = exception_error;
- JUMP_TAG(TAG_FATAL);
- }
+static VALUE make_exception(int argc, const VALUE *argv, int isstr);
- if (NIL_P(mesg))
- mesg = th->errinfo;
- if (NIL_P(mesg)) {
- mesg = rb_exc_new(rb_eRuntimeError, 0, 0);
- }
+NORETURN(static void rb_exc_exception(VALUE mesg, enum ruby_tag_type tag, VALUE cause));
- file = rb_sourcefile();
- if (file) line = rb_sourceline();
- if (file && !NIL_P(mesg)) {
- at = get_backtrace(mesg);
- if (NIL_P(at)) {
- at = make_backtrace();
- set_backtrace(mesg, at);
- }
- }
+static void
+rb_exc_exception(VALUE mesg, enum ruby_tag_type tag, VALUE cause)
+{
if (!NIL_P(mesg)) {
- th->errinfo = mesg;
- }
-
- if (RTEST(ruby_debug) && !NIL_P(e = th->errinfo) &&
- !rb_obj_is_kind_of(e, rb_eSystemExit)) {
- int status;
-
- PUSH_TAG();
- if ((status = EXEC_TAG()) == 0) {
- RB_GC_GUARD(e) = rb_obj_as_string(e);
- if (file) {
- warn_printf("Exception `%s' at %s:%d - %s\n",
- rb_obj_classname(th->errinfo),
- file, line, RSTRING_PTR(e));
- }
- else {
- warn_printf("Exception `%s' - %s\n",
- rb_obj_classname(th->errinfo),
- RSTRING_PTR(e));
- }
- }
- POP_TAG();
- if (status == TAG_FATAL && th->errinfo == exception_error) {
- th->errinfo = mesg;
- }
- else if (status) {
- thread_reset_raised(th);
- JUMP_TAG(status);
- }
- }
-
- rb_trap_restore_mask();
-
- if (tag != TAG_FATAL) {
- EXEC_EVENT_HOOK(th, RUBY_EVENT_RAISE, th->cfp->self,
- 0 /* TODO: id */, 0 /* TODO: klass */);
+ mesg = make_exception(1, &mesg, FALSE);
}
-
- thread_reset_raised(th);
- JUMP_TAG(tag);
+ rb_longjmp(GET_EC(), tag, mesg, cause);
}
+/**
+ * Raises an exception in the current thread.
+ * @param[in] mesg an Exception class or an `Exception` object.
+ * @exception always raises an instance of the given exception class or
+ * the given `Exception` object.
+ * @ingroup exception
+ */
void
-rb_exc_raise(mesg)
- VALUE mesg;
+rb_exc_raise(VALUE mesg)
{
- rb_longjmp(TAG_RAISE, mesg);
+ rb_exc_exception(mesg, TAG_RAISE, Qundef);
}
+/*!
+ * Raises a fatal error in the current thread.
+ *
+ * Same as rb_exc_raise() but raises a fatal error, which Ruby codes
+ * cannot rescue.
+ * \ingroup exception
+ */
void
-rb_exc_fatal(mesg)
- VALUE mesg;
+rb_exc_fatal(VALUE mesg)
{
- rb_longjmp(TAG_FATAL, mesg);
+ rb_exc_exception(mesg, TAG_FATAL, Qnil);
}
void
rb_interrupt(void)
{
- static const char fmt[1] = {'\0'};
- rb_raise(rb_eInterrupt, fmt);
+ rb_exc_raise(rb_exc_new(rb_eInterrupt, 0, 0));
}
-/*
- * call-seq:
- * raise
- * raise(string)
- * raise(exception [, string [, array]])
- * fail
- * fail(string)
- * fail(exception [, string [, array]])
- *
- * With no arguments, raises the exception in <code>$!</code> or raises
- * a <code>RuntimeError</code> if <code>$!</code> is +nil+.
- * With a single +String+ argument, raises a
- * +RuntimeError+ with the string as a message. Otherwise,
- * the first parameter should be the name of an +Exception+
- * class (or an object that returns an +Exception+ object when sent
- * an +exception+ message). The optional second parameter sets the
- * message associated with the exception, and the third parameter is an
- * array of callback information. Exceptions are caught by the
- * +rescue+ clause of <code>begin...end</code> blocks.
- *
- * raise "Failed to create socket"
- * raise ArgumentError, "No parameters", caller
- */
+static int
+extract_raise_options(int argc, VALUE *argv, VALUE *cause)
+{
+ // Keyword arguments:
+ static ID keywords[1] = {0};
+ if (!keywords[0]) {
+ CONST_ID(keywords[0], "cause");
+ }
-static VALUE get_errinfo(void);
+ if (argc > 0) {
+ VALUE options;
+ argc = rb_scan_args(argc, argv, "*:", NULL, &options);
-static VALUE
-rb_f_raise(int argc, VALUE *argv)
-{
- VALUE err;
- if (argc == 0) {
- err = get_errinfo();
- if (!NIL_P(err)) {
- argc = 1;
- argv = &err;
- }
+ if (!NIL_P(options)) {
+ if (!RHASH_EMPTY_P(options)) {
+ // Extract optional cause keyword argument, leaving any other options alone:
+ rb_get_kwargs(options, keywords, 0, -2, cause);
+
+ // If there were any other options, add them back to the arguments:
+ if (!RHASH_EMPTY_P(options)) argv[argc++] = options;
+ }
+ }
}
- rb_raise_jump(rb_make_exception(argc, argv));
- return Qnil; /* not reached */
+
+ return argc;
}
+/**
+ * Complete exception setup for cross-context raises (Thread#raise, Fiber#raise).
+ * Handles keyword extraction, validation, exception creation, and cause assignment.
+ *
+ * @param[in] argc Number of arguments
+ * @param[in] argv Argument array (will be modified for keyword extraction)
+ * @return Prepared exception object with cause applied
+ */
VALUE
-rb_make_exception(int argc, VALUE *argv)
+rb_exception_setup(int argc, VALUE *argv)
{
- VALUE mesg;
- ID exception;
- int n;
+ rb_execution_context_t *ec = GET_EC();
- mesg = Qnil;
- switch (argc) {
- case 0:
- mesg = Qnil;
- break;
- case 1:
- if (NIL_P(argv[0]))
- break;
- if (TYPE(argv[0]) == T_STRING) {
- mesg = rb_exc_new3(rb_eRuntimeError, argv[0]);
- break;
- }
- n = 0;
- goto exception_call;
+ // Extract cause keyword argument:
+ VALUE cause = Qundef;
+ argc = extract_raise_options(argc, argv, &cause);
- case 2:
- case 3:
- n = 1;
- exception_call:
- exception = rb_intern("exception");
- if (!rb_respond_to(argv[0], exception)) {
- rb_raise(rb_eTypeError, "exception class/object expected");
- }
- mesg = rb_funcall(argv[0], exception, n, argv[1]);
- break;
- default:
- rb_raise(rb_eArgError, "wrong number of arguments");
- break;
- }
- if (argc > 0) {
- if (!rb_obj_is_kind_of(mesg, rb_eException))
- rb_raise(rb_eTypeError, "exception object expected");
- if (argc > 2)
- set_backtrace(mesg, argv[2]);
+ // Validate cause-only case:
+ if (argc == 0 && !UNDEF_P(cause)) {
+ rb_raise(rb_eArgError, "only cause is given with no arguments");
}
- return mesg;
-}
+ // Create exception:
+ VALUE exception;
+ if (argc == 0) {
+ exception = rb_exc_new(rb_eRuntimeError, 0, 0);
+ }
+ else {
+ exception = rb_make_exception(argc, argv);
+ }
-void
-rb_raise_jump(mesg)
- VALUE mesg;
-{
- /* TODO: fix me */
- rb_longjmp(TAG_RAISE, mesg);
-}
+ VALUE resolved_cause = Qnil;
-void
-rb_jump_tag(tag)
- int tag;
-{
- JUMP_TAG(tag);
-}
+ // Resolve cause with validation:
+ if (UNDEF_P(cause)) {
+ // No explicit cause - use automatic cause chaining from calling context:
+ resolved_cause = rb_ec_get_errinfo(ec);
-int
-rb_block_given_p()
-{
- rb_thread_t *th = GET_THREAD();
- if (GC_GUARDED_PTR_REF(th->cfp->lfp[0])) {
- return Qtrue;
+ // Prevent self-referential cause (e.g. `raise $!`):
+ if (resolved_cause == exception) {
+ resolved_cause = Qnil;
+ }
}
- else {
- return Qfalse;
+ else if (NIL_P(cause)) {
+ // Explicit nil cause - prevent chaining:
+ resolved_cause = Qnil;
}
+ else {
+ // Explicit cause - validate and assign:
+ if (!rb_obj_is_kind_of(cause, rb_eException)) {
+ rb_raise(rb_eTypeError, "exception object expected");
+ }
+
+ if (cause == exception) {
+ // Prevent self-referential cause (e.g. `raise error, cause: error`) - although I'm not sure this is good behaviour, it's inherited from `Kernel#raise`.
+ resolved_cause = Qnil;
+ }
+ else {
+ // Check for circular causes:
+ VALUE current_cause = cause;
+ while (!NIL_P(current_cause)) {
+ // We guarantee that the cause chain is always terminated. Then, creating an exception with an existing cause is not circular as long as exception is not an existing cause of any other exception.
+ if (current_cause == exception) {
+ rb_raise(rb_eArgError, "circular causes");
+ }
+ if (THROW_DATA_P(current_cause)) {
+ break;
+ }
+ current_cause = rb_attr_get(current_cause, id_cause);
+ }
+ resolved_cause = cause;
+ }
+ }
+
+ // Apply cause to exception object (duplicate if frozen):
+ if (!UNDEF_P(resolved_cause)) {
+ if (OBJ_FROZEN(exception)) {
+ exception = rb_obj_dup(exception);
+ }
+ rb_ivar_set(exception, id_cause, resolved_cause);
+ }
+
+ return exception;
}
-int
-rb_iterator_p()
+VALUE
+rb_f_raise(int argc, VALUE *argv)
{
- return rb_block_given_p();
-}
+ VALUE cause = Qundef;
+ argc = extract_raise_options(argc, argv, &cause);
-/*
- * call-seq:
- * block_given? => true or false
- * iterator? => true or false
- *
- * Returns <code>true</code> if <code>yield</code> would execute a
- * block in the current context. The <code>iterator?</code> form
- * is mildly deprecated.
- *
- * def try
- * if block_given?
- * yield
- * else
- * "no block"
- * end
- * end
- * try #=> "no block"
- * try { "hello" } #=> "hello"
- * try do "hello" end #=> "hello"
- */
+ VALUE exception;
+ // Bare re-raise case:
+ if (argc == 0) {
+ // Cause was extracted, but no arguments were provided:
+ if (!UNDEF_P(cause)) {
+ rb_raise(rb_eArgError, "only cause is given with no arguments");
+ }
-VALUE
-rb_f_block_given_p()
-{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
- cfp = vm_get_ruby_level_cfp(th, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp));
- if (GC_GUARDED_PTR_REF(cfp->lfp[0])) {
- return Qtrue;
+ // Otherwise, re-raise the current exception:
+ exception = get_errinfo();
+ if (!NIL_P(exception)) {
+ argc = 1;
+ argv = &exception;
+ }
}
- else {
- return Qfalse;
- }
-}
-VALUE rb_eThreadError;
+ rb_raise_jump(rb_make_exception(argc, argv), cause);
-void
-rb_need_block()
-{
- if (!rb_block_given_p()) {
- vm_localjump_error("no block given", Qnil, 0);
- }
+ UNREACHABLE_RETURN(Qnil);
}
-static inline VALUE
-rb_yield_0(int argc, VALUE *argv)
-{
- return vm_yield(GET_THREAD(), argc, argv);
-}
+/*
+ * call-seq:
+ * raise(exception, message = exception.to_s, backtrace = nil, cause: $!)
+ * raise(message = nil, cause: $!)
+ *
+ * Raises an exception;
+ * see {Exceptions}[rdoc-ref:exceptions.md].
+ *
+ * Argument +exception+ sets the class of the new exception;
+ * it should be class Exception or one of its subclasses
+ * (most commonly, RuntimeError or StandardError),
+ * or an instance of one of those classes:
+ *
+ * begin
+ * raise(StandardError)
+ * rescue => x
+ * p x.class
+ * end
+ * # => StandardError
+ *
+ * Argument +message+ sets the stored message in the new exception,
+ * which may be retrieved by method Exception#message;
+ * the message must be
+ * a {string-convertible object}[rdoc-ref:implicit_conversion.rdoc@String-Convertible+Objects]
+ * or +nil+:
+ *
+ * begin
+ * raise(StandardError, 'Boom')
+ * rescue => x
+ * p x.message
+ * end
+ * # => "Boom"
+ *
+ * If argument +message+ is not given,
+ * the message is the exception class name.
+ *
+ * See {Messages}[rdoc-ref:exceptions.md@Messages].
+ *
+ * Argument +backtrace+ might be used to modify the backtrace of the new exception,
+ * as reported by Exception#backtrace and Exception#backtrace_locations;
+ * the backtrace must be an array of Thread::Backtrace::Location, an array of
+ * strings, a single string, or +nil+.
+ *
+ * Using the array of Thread::Backtrace::Location instances is the most consistent option
+ * and should be preferred when possible. The necessary value might be obtained
+ * from #caller_locations, or copied from Exception#backtrace_locations of another
+ * error:
+ *
+ * begin
+ * do_some_work()
+ * rescue ZeroDivisionError => ex
+ * raise(LogicalError, "You have an error in your math", ex.backtrace_locations)
+ * end
+ *
+ * The ways, both Exception#backtrace and Exception#backtrace_locations of the
+ * raised error are set to the same backtrace.
+ *
+ * When the desired stack of locations is not available and should
+ * be constructed from scratch, an array of strings or a singular
+ * string can be used. In this case, only Exception#backtrace is set:
+ *
+ * begin
+ * raise(StandardError, 'Boom', %w[dsl.rb:3 framework.rb:1])
+ * rescue => ex
+ * p ex.backtrace
+ * # => ["dsl.rb:3", "framework.rb:1"]
+ * p ex.backtrace_locations
+ * # => nil
+ * end
+ *
+ * If argument +backtrace+ is not given,
+ * the backtrace is set according to an array of Thread::Backtrace::Location objects,
+ * as derived from the call stack.
+ *
+ * See {Backtraces}[rdoc-ref:exceptions.md@Backtraces].
+ *
+ * Keyword argument +cause+ sets the stored cause in the new exception,
+ * which may be retrieved by method Exception#cause;
+ * the cause must be an exception object (Exception or one of its subclasses),
+ * or +nil+:
+ *
+ * begin
+ * raise(StandardError, cause: RuntimeError.new)
+ * rescue => x
+ * p x.cause
+ * end
+ * # => #<RuntimeError: RuntimeError>
+ *
+ * If keyword argument +cause+ is not given,
+ * the cause is the value of <tt>$!</tt>.
+ *
+ * See {Cause}[rdoc-ref:exceptions.md@Cause].
+ *
+ * In the alternate calling sequence,
+ * where argument +exception+ _not_ given,
+ * raises a new exception of the class given by <tt>$!</tt>,
+ * or of class RuntimeError if <tt>$!</tt> is +nil+:
+ *
+ * begin
+ * raise
+ * rescue => x
+ * p x
+ * end
+ * # => RuntimeError
+ *
+ * With argument +exception+ not given,
+ * argument +message+ and keyword argument +cause+ may be given,
+ * but argument +backtrace+ may not be given.
+ *
+ * +cause+ can not be given as an only argument.
+ *
+ */
-VALUE
-rb_yield(VALUE val)
+static VALUE
+f_raise(int c, VALUE *v, VALUE _)
{
- volatile VALUE tmp = val;
- if (val == Qundef) {
- tmp = rb_yield_0(0, 0);
- }
- else {
- tmp = rb_yield_0(1, &val);
- }
- return tmp;
+ return rb_f_raise(c, v);
}
-VALUE
-rb_yield_values(int n, ...)
+static VALUE
+make_exception(int argc, const VALUE *argv, int isstr)
{
- int i;
- VALUE *argv;
- va_list args;
+ VALUE mesg, exc;
- if (n == 0) {
- return rb_yield_0(0, 0);
- }
-
- argv = ALLOCA_N(VALUE, n);
+ mesg = Qnil;
+ switch (argc) {
+ case 0:
+ return Qnil;
+ case 1:
+ exc = argv[0];
+ if (isstr &&! NIL_P(exc)) {
+ mesg = rb_check_string_type(exc);
+ if (!NIL_P(mesg)) {
+ return rb_exc_new3(rb_eRuntimeError, mesg);
+ }
+ }
- va_init_list(args, n);
- for (i=0; i<n; i++) {
- argv[i] = va_arg(args, VALUE);
+ case 2:
+ case 3:
+ break;
+ default:
+ rb_error_arity(argc, 0, 3);
+ }
+ if (NIL_P(mesg)) {
+ mesg = rb_check_funcall(argv[0], idException, argc != 1, &argv[1]);
+ }
+ if (UNDEF_P(mesg)) {
+ rb_raise(rb_eTypeError, "exception class/object expected");
+ }
+ if (!rb_obj_is_kind_of(mesg, rb_eException)) {
+ rb_raise(rb_eTypeError, "exception object expected");
+ }
+ if (argc == 3) {
+ set_backtrace(mesg, argv[2]);
}
- va_end(args);
- return rb_yield_0(n, argv);
+ return mesg;
}
VALUE
-rb_yield_splat(VALUE values)
+rb_make_exception(int argc, const VALUE *argv)
{
- VALUE tmp = rb_check_array_type(values);
- volatile VALUE v;
- if (NIL_P(tmp)) {
- rb_raise(rb_eArgError, "not an array");
- }
- v = rb_yield_0(RARRAY_LEN(tmp), RARRAY_PTR(tmp));
- return v;
+ return make_exception(argc, argv, TRUE);
}
-static VALUE
-loop_i()
+/*! \private
+ */
+static void
+rb_raise_jump(VALUE mesg, VALUE cause)
{
- for (;;) {
- rb_yield_0(0, 0);
- }
- return Qnil;
-}
+ rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = ec->cfp;
+ const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
+ VALUE klass = me->owner;
+ VALUE self = cfp->self;
+ ID mid = me->called_id;
-/*
- * call-seq:
- * loop {|| block }
- *
- * Repeatedly executes the block.
- *
- * loop do
- * print "Input: "
- * line = gets
- * break if !line or line =~ /^qQ/
- * # ...
- * end
- */
+ rb_vm_pop_frame(ec);
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, self, me->def->original_id, mid, klass, Qnil);
-static VALUE
-rb_f_loop(void)
+ rb_longjmp(ec, TAG_RAISE, mesg, cause);
+}
+
+void
+rb_jump_tag(int tag)
{
- rb_rescue2(loop_i, (VALUE)0, 0, 0, rb_eStopIteration, (VALUE)0);
- return Qnil; /* dummy */
+ if (UNLIKELY(tag < TAG_RETURN || tag > TAG_FATAL)) {
+ unknown_longjmp_status(tag);
+ }
+ EC_JUMP_TAG(GET_EC(), tag);
}
-VALUE
-rb_iterate(VALUE (*it_proc) (VALUE), VALUE data1,
- VALUE (*bl_proc) (ANYARGS), VALUE data2)
+int
+rb_block_given_p(void)
{
- int state;
- volatile VALUE retval = Qnil;
- NODE *node = NEW_IFUNC(bl_proc, data2);
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
-
- TH_PUSH_TAG(th);
- state = TH_EXEC_TAG();
- if (state == 0) {
- iter_retry:
- {
- rb_block_t *blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP(th->cfp);
- blockptr->iseq = (void *)node;
- blockptr->proc = 0;
- th->passed_block = blockptr;
- }
- retval = (*it_proc) (data1);
+ if (rb_vm_frame_block_handler(GET_EC()->cfp) == VM_BLOCK_HANDLER_NONE) {
+ return FALSE;
}
else {
- VALUE err = th->errinfo;
- if (state == TAG_BREAK) {
- VALUE *escape_dfp = GET_THROWOBJ_CATCH_POINT(err);
- VALUE *cdfp = cfp->dfp;
-
- if (cdfp == escape_dfp) {
- state = 0;
- th->state = 0;
- th->errinfo = Qnil;
- th->cfp = cfp;
- }
- else{
- /* SDR(); printf("%p, %p\n", cdfp, escape_dfp); */
- }
- }
- else if (state == TAG_RETRY) {
- VALUE *escape_dfp = GET_THROWOBJ_CATCH_POINT(err);
- VALUE *cdfp = cfp->dfp;
-
- if (cdfp == escape_dfp) {
- state = 0;
- th->state = 0;
- th->errinfo = Qnil;
- th->cfp = cfp;
- goto iter_retry;
- }
- }
- }
- TH_POP_TAG();
-
- switch (state) {
- case 0:
- break;
- default:
- TH_JUMP_TAG(th, state);
+ return TRUE;
}
- return retval;
}
-struct iter_method_arg {
- VALUE obj;
- ID mid;
- int argc;
- VALUE *argv;
-};
+int rb_vm_cframe_keyword_p(const rb_control_frame_t *cfp);
-static VALUE
-iterate_method(VALUE obj)
+int
+rb_keyword_given_p(void)
{
- struct iter_method_arg *arg;
-
- arg = (struct iter_method_arg *)obj;
- return rb_call(CLASS_OF(arg->obj), arg->obj, arg->mid,
- arg->argc, arg->argv, NOEX_PRIVATE);
+ return rb_vm_cframe_keyword_p(GET_EC()->cfp);
}
-VALUE
-rb_block_call(VALUE obj, ID mid, int argc, VALUE *argv,
- VALUE (*bl_proc) (ANYARGS), VALUE data2)
-{
- struct iter_method_arg arg;
+VALUE rb_eThreadError;
- arg.obj = obj;
- arg.mid = mid;
- arg.argc = argc;
- arg.argv = argv;
- return rb_iterate(iterate_method, (VALUE)&arg, bl_proc, data2);
+void
+rb_need_block(void)
+{
+ if (!rb_block_given_p()) {
+ rb_vm_localjump_error("no block given", Qnil, 0);
+ }
}
VALUE
-rb_each(VALUE obj)
+rb_rescue2(VALUE (* b_proc) (VALUE), VALUE data1,
+ VALUE (* r_proc) (VALUE, VALUE), VALUE data2, ...)
{
- return rb_call(CLASS_OF(obj), obj, rb_intern("each"), 0, 0,
- NOEX_PRIVATE);
+ va_list ap;
+ va_start(ap, data2);
+ VALUE ret = rb_vrescue2(b_proc, data1, r_proc, data2, ap);
+ va_end(ap);
+ return ret;
}
VALUE
-rb_rescue2(VALUE (*b_proc) (ANYARGS), VALUE data1, VALUE (*r_proc) (ANYARGS),
- VALUE data2, ...)
-{
- int state;
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
- volatile VALUE result;
- volatile VALUE e_info = th->errinfo;
- va_list args;
-
- PUSH_TAG();
- if ((state = EXEC_TAG()) == 0) {
+rb_vrescue2(VALUE (* b_proc) (VALUE), VALUE data1,
+ VALUE (* r_proc) (VALUE, VALUE), VALUE data2,
+ va_list args)
+{
+ enum ruby_tag_type state;
+ rb_execution_context_t * volatile ec = GET_EC();
+ rb_control_frame_t *volatile cfp = ec->cfp;
+ volatile VALUE result = Qfalse;
+ volatile VALUE e_info = ec->errinfo;
+
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
retry_entry:
- result = (*b_proc) (data1);
+ result = (*b_proc) (data1);
+ }
+ else if (result) {
+ /* escape from r_proc */
+ if (state == TAG_RETRY) {
+ state = TAG_NONE;
+ ec->errinfo = Qnil;
+ result = Qfalse;
+ goto retry_entry;
+ }
}
else {
- th->cfp = cfp; /* restore */
-
- if (state == TAG_RAISE) {
- int handle = Qfalse;
- VALUE eclass;
-
- va_init_list(args, data2);
- while ((eclass = va_arg(args, VALUE)) != 0) {
- if (rb_obj_is_kind_of(th->errinfo, eclass)) {
- handle = Qtrue;
- break;
- }
- }
- va_end(args);
-
- if (handle) {
- if (r_proc) {
- PUSH_TAG();
- if ((state = EXEC_TAG()) == 0) {
- result = (*r_proc) (data2, th->errinfo);
- }
- POP_TAG();
- if (state == TAG_RETRY) {
- state = 0;
- th->errinfo = Qnil;
- goto retry_entry;
- }
- }
- else {
- result = Qnil;
- state = 0;
- }
- if (state == 0) {
- th->errinfo = e_info;
- }
- }
- }
- }
- POP_TAG();
+ rb_vm_rewind_cfp(ec, cfp);
+
+ if (state == TAG_RAISE) {
+ int handle = FALSE;
+ VALUE eclass;
+ va_list ap;
+
+ result = Qnil;
+ /* reuses args when raised again after retrying in r_proc */
+ va_copy(ap, args);
+ while ((eclass = va_arg(ap, VALUE)) != 0) {
+ if (rb_obj_is_kind_of(ec->errinfo, eclass)) {
+ handle = TRUE;
+ break;
+ }
+ }
+ va_end(ap);
+
+ if (handle) {
+ state = TAG_NONE;
+ if (r_proc) {
+ result = (*r_proc) (data2, ec->errinfo);
+ }
+ ec->errinfo = e_info;
+ }
+ }
+ }
+ EC_POP_TAG();
if (state)
- JUMP_TAG(state);
+ EC_JUMP_TAG(ec, state);
return result;
}
VALUE
-rb_rescue(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE (*r_proc)(ANYARGS), VALUE data2)
+rb_rescue(VALUE (* b_proc)(VALUE), VALUE data1,
+ VALUE (* r_proc)(VALUE, VALUE), VALUE data2)
{
return rb_rescue2(b_proc, data1, r_proc, data2, rb_eStandardError,
- (VALUE)0);
+ (VALUE)0);
}
VALUE
-rb_protect(VALUE (*proc) (VALUE), VALUE data, int *state)
+rb_protect(VALUE (* proc) (VALUE), VALUE data, int *pstate)
{
- VALUE result = Qnil; /* OK */
- int status;
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
- struct rb_vm_trap_tag trap_tag;
-
- trap_tag.prev = th->trap_tag;
+ volatile VALUE result = Qnil;
+ volatile enum ruby_tag_type state;
+ rb_execution_context_t * volatile ec = GET_EC();
+ rb_control_frame_t *volatile cfp = ec->cfp;
- PUSH_TAG();
- th->trap_tag = &trap_tag;
- if ((status = EXEC_TAG()) == 0) {
- SAVE_ROOT_JMPBUF(th, result = (*proc) (data));
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ result = (*proc)(data);
}
- th->trap_tag = trap_tag.prev;
- POP_TAG();
-
- if (state) {
- *state = status;
- }
- if (status != 0) {
- th->cfp = cfp;
- return Qnil;
+ else {
+ rb_vm_rewind_cfp(ec, cfp);
}
+ EC_POP_TAG();
+ if (pstate != NULL) *pstate = state;
return result;
}
VALUE
-rb_ensure(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE (*e_proc)(ANYARGS), VALUE data2)
+rb_ec_ensure(rb_execution_context_t *ec, VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*e_proc)(VALUE), VALUE data2)
{
- int state;
+ enum ruby_tag_type state;
volatile VALUE result = Qnil;
-
- PUSH_TAG();
- if ((state = EXEC_TAG()) == 0) {
- result = (*b_proc) (data1);
- }
- POP_TAG();
- /* TODO: fix me */
- /* retval = prot_tag ? prot_tag->retval : Qnil; */ /* save retval */
- (*e_proc) (data2);
+ VALUE errinfo;
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ result = (*b_proc) (data1);
+ }
+ EC_POP_TAG();
+ errinfo = ec->errinfo;
+ if (!NIL_P(errinfo) && !RB_TYPE_P(errinfo, T_OBJECT)) {
+ ec->errinfo = Qnil;
+ }
+ (*e_proc)(data2);
+ ec->errinfo = errinfo;
if (state)
- JUMP_TAG(state);
+ EC_JUMP_TAG(ec, state);
return result;
}
VALUE
-rb_with_disable_interrupt(VALUE (*proc)(ANYARGS), VALUE data)
+rb_ensure(VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*e_proc)(VALUE), VALUE data2)
{
- VALUE result = Qnil; /* OK */
- int status;
-
- DEFER_INTS;
- {
- int thr_critical = rb_thread_critical;
-
- rb_thread_critical = Qtrue;
- PUSH_TAG();
- if ((status = EXEC_TAG()) == 0) {
- result = (*proc) (data);
- }
- POP_TAG();
- rb_thread_critical = thr_critical;
- }
- ENABLE_INTS;
- if (status)
- JUMP_TAG(status);
-
- return result;
+ return rb_ec_ensure(GET_EC(), b_proc, data1, e_proc, data2);
}
-static inline void
-stack_check(void)
+static ID
+frame_func_id(const rb_control_frame_t *cfp)
{
- static int overflowing = 0;
+ const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
- if (!overflowing && ruby_stack_check()) {
- int state;
- overflowing = 1;
- PUSH_TAG();
- if ((state = EXEC_TAG()) == 0) {
- rb_exc_raise(sysstack_error);
- }
- POP_TAG();
- overflowing = 0;
- JUMP_TAG(state);
+ if (me) {
+ return me->def->original_id;
+ }
+ else {
+ return 0;
}
}
-/*
- * call-seq:
- * obj.method_missing(symbol [, *args] ) => result
- *
- * Invoked by Ruby when <i>obj</i> is sent a message it cannot handle.
- * <i>symbol</i> is the symbol for the method called, and <i>args</i>
- * are any arguments that were passed to it. By default, the interpreter
- * raises an error when this method is called. However, it is possible
- * to override the method to provide more dynamic behavior.
- * The example below creates
- * a class <code>Roman</code>, which responds to methods with names
- * consisting of roman numerals, returning the corresponding integer
- * values.
- *
- * class Roman
- * def romanToInt(str)
- * # ...
- * end
- * def method_missing(methId)
- * str = methId.id2name
- * romanToInt(str)
- * end
- * end
- *
- * r = Roman.new
- * r.iv #=> 4
- * r.xxiii #=> 23
- * r.mm #=> 2000
- */
-
-static VALUE
-rb_method_missing(int argc, const VALUE *argv, VALUE obj)
+static ID
+frame_called_id(rb_control_frame_t *cfp)
{
- ID id;
- VALUE exc = rb_eNoMethodError;
- char *format = 0;
- rb_thread_t *th = GET_THREAD();
- int last_call_status = th->method_missing_reason;
- if (argc == 0 || !SYMBOL_P(argv[0])) {
- rb_raise(rb_eArgError, "no id given");
- }
-
- stack_check();
-
- id = SYM2ID(argv[0]);
+ const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
- if (last_call_status & NOEX_PRIVATE) {
- format = "private method `%s' called for %s";
- }
- else if (last_call_status & NOEX_PROTECTED) {
- format = "protected method `%s' called for %s";
- }
- else if (last_call_status & NOEX_VCALL) {
- format = "undefined local variable or method `%s' for %s";
- exc = rb_eNameError;
- }
- else if (last_call_status & NOEX_SUPER) {
- format = "super: no superclass method `%s' for %s";
+ if (me) {
+ return me->called_id;
}
- if (!format) {
- format = "undefined method `%s' for %s";
- }
-
- {
- int n = 0;
- VALUE args[3];
- args[n++] = rb_funcall(rb_const_get(exc, rb_intern("message")), '!',
- 3, rb_str_new2(format), obj, argv[0]);
- args[n++] = argv[0];
- if (exc == rb_eNoMethodError) {
- args[n++] = rb_ary_new4(argc - 1, argv + 1);
- }
- exc = rb_class_new_instance(n, args, exc);
- rb_exc_raise(exc);
+ else {
+ return 0;
}
-
- return Qnil; /* not reached */
}
-static VALUE
-method_missing(VALUE obj, ID id, int argc, const VALUE *argv, int call_status)
+ID
+rb_frame_this_func(void)
{
- VALUE *nargv;
- GET_THREAD()->method_missing_reason = call_status;
-
- if (id == missing) {
- rb_method_missing(argc, argv, obj);
- }
- else if (id == ID_ALLOCATOR) {
- rb_raise(rb_eTypeError, "allocator undefined for %s",
- rb_class2name(obj));
- }
-
- nargv = ALLOCA_N(VALUE, argc + 1);
- nargv[0] = ID2SYM(id);
- MEMCPY(nargv + 1, argv, VALUE, argc);
-
- return rb_funcall2(obj, missing, argc + 1, nargv);
+ return frame_func_id(GET_EC()->cfp);
}
-static VALUE
-rb_call0(VALUE klass, VALUE recv, ID mid, int argc, const VALUE *argv, int scope, VALUE self)
+ID
+rb_frame_callee(void)
{
- NODE *body, *method;
- int noex;
- ID id = mid;
- struct cache_entry *ent;
- rb_thread_t *th = GET_THREAD();
-
- if (!klass) {
- rb_raise(rb_eNotImpError,
- "method `%s' called on terminated object (%p)",
- rb_id2name(mid), (void *)recv);
- }
- /* is it in the method cache? */
- ent = cache + EXPR1(klass, mid);
+ return frame_called_id(GET_EC()->cfp);
+}
- if (ent->mid == mid && ent->klass == klass) {
- if (!ent->method)
- return method_missing(recv, mid, argc, argv,
- scope == 2 ? NOEX_VCALL : 0);
- id = ent->mid0;
- noex = ent->method->nd_noex;
- klass = ent->method->nd_clss;
- body = ent->method->nd_body;
- }
- else if ((method = rb_get_method_body(klass, id, &id)) != 0) {
- noex = method->nd_noex;
- klass = method->nd_clss;
- body = method->nd_body;
- }
- else {
- if (scope == 3) {
- return method_missing(recv, mid, argc, argv, NOEX_SUPER);
- }
- return method_missing(recv, mid, argc, argv,
- scope == 2 ? NOEX_VCALL : 0);
- }
-
-
- if (mid != missing) {
- /* receiver specified form for private method */
- if (UNLIKELY(noex)) {
- if (((noex & NOEX_MASK) & NOEX_PRIVATE) && scope == 0) {
- return method_missing(recv, mid, argc, argv, NOEX_PRIVATE);
- }
-
- /* self must be kind of a specified form for protected method */
- if (((noex & NOEX_MASK) & NOEX_PROTECTED) && scope == 0) {
- VALUE defined_class = klass;
-
- if (TYPE(defined_class) == T_ICLASS) {
- defined_class = RBASIC(defined_class)->klass;
- }
-
- if (!rb_obj_is_kind_of(self, rb_class_real(defined_class))) {
- return method_missing(recv, mid, argc, argv, NOEX_PROTECTED);
- }
- }
-
- if (NOEX_SAFE(noex) > th->safe_level) {
- rb_raise(rb_eSecurityError, "calling insecure method: %s", rb_id2name(mid));
- }
- }
- }
-
- {
- VALUE val;
- /*
- //static int level;
- //int i;
- //for(i=0; i<level; i++){printf(" ");}
- //printf("invoke %s (%s)\n", rb_id2name(mid), ruby_node_name(nd_type(body)));
- //level++;
- //printf("%s with %d args\n", rb_id2name(mid), argc);
- */
- val = vm_call0(th, klass, recv, mid, id, argc, argv, body,
- noex & NOEX_NOSUPER);
- /*
- //level--;
- //for(i=0; i<level; i++){printf(" ");}
- //printf("done %s (%s)\n", rb_id2name(mid), ruby_node_name(nd_type(body)));
- */
- return val;
+static rb_control_frame_t *
+previous_frame(const rb_execution_context_t *ec)
+{
+ rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(ec->cfp);
+ /* check if prev_cfp can be accessible */
+ if ((void *)(ec->vm_stack + ec->vm_stack_size) == (void *)(prev_cfp)) {
+ return 0;
}
+ return prev_cfp;
}
-static VALUE
-rb_call(VALUE klass, VALUE recv, ID mid, int argc, const VALUE *argv, int scope)
+static ID
+prev_frame_callee(void)
{
- return rb_call0(klass, recv, mid, argc, argv, scope, rb_frame_self());
+ rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
+ if (!prev_cfp) return 0;
+ return frame_called_id(prev_cfp);
}
-VALUE
-rb_apply(VALUE recv, ID mid, VALUE args)
+static ID
+prev_frame_func(void)
{
- int argc;
- VALUE *argv;
-
- argc = RARRAY_LEN(args); /* Assigns LONG, but argc is INT */
- argv = ALLOCA_N(VALUE, argc);
- MEMCPY(argv, RARRAY_PTR(args), VALUE, argc);
- return rb_call(CLASS_OF(recv), recv, mid, argc, argv, NOEX_NOSUPER);
+ rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
+ if (!prev_cfp) return 0;
+ return frame_func_id(prev_cfp);
}
-static VALUE
-send_internal(int argc, VALUE *argv, VALUE recv, int scope)
+/*!
+ * \private
+ * Returns the ID of the last method in the call stack.
+ * \sa rb_frame_this_func
+ * \ingroup defmethod
+ */
+ID
+rb_frame_last_func(void)
{
- VALUE vid;
- VALUE self = RUBY_VM_PREVIOUS_CONTROL_FRAME(GET_THREAD()->cfp)->self;
-
- if (argc == 0) {
- rb_raise(rb_eArgError, "no method name given");
- }
+ const rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = ec->cfp;
+ ID mid;
- vid = *argv++; argc--;
- PASS_PASSED_BLOCK();
- return rb_call0(CLASS_OF(recv), recv, rb_to_id(vid), argc, argv, scope, self);
+ while (!(mid = frame_func_id(cfp)) &&
+ (cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp),
+ !RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)));
+ return mid;
}
/*
* call-seq:
- * obj.send(symbol [, args...]) => obj
- * obj.__send__(symbol [, args...]) => obj
- *
- * Invokes the method identified by _symbol_, passing it any
- * arguments specified. You can use <code>__send__</code> if the name
- * +send+ clashes with an existing method in _obj_.
+ * append_features(mod) -> mod
*
- * class Klass
- * def hello(*args)
- * "Hello " + args.join(' ')
- * end
- * end
- * k = Klass.new
- * k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
+ * When this module is included in another, Ruby calls
+ * #append_features in this module, passing it the receiving module
+ * in _mod_. Ruby's default implementation is to add the constants,
+ * methods, and module variables of this module to _mod_ if this
+ * module has not already been added to _mod_ or one of its
+ * ancestors. See also Module#include.
*/
-VALUE
-rb_f_send(int argc, VALUE *argv, VALUE recv)
+static VALUE
+rb_mod_append_features(VALUE module, VALUE include)
{
- return send_internal(argc, argv, recv, NOEX_NOSUPER | NOEX_PRIVATE);
+ if (!CLASS_OR_MODULE_P(include)) {
+ Check_Type(include, T_CLASS);
+ }
+ rb_include_module(include, module);
+
+ return module;
}
+static VALUE refinement_import_methods(int argc, VALUE *argv, VALUE refinement);
/*
* call-seq:
- * obj.invoke_method(symbol [, args...]) => obj
- *
- * Invokes the method identified by _symbol_, passing it any
- * arguments specified. Unlike send, invoke_method calls public
- * methods only, unless it's invoked in a function call style.
+ * include(module, ...) -> self
*
- * 1.invoke_method(:puts, "hello") # causes NoMethodError
+ * Invokes Module.append_features on each parameter in reverse order.
*/
-VALUE
-rb_invoke_method(int argc, VALUE *argv, VALUE recv)
-{
- int rb_vm_cfunc_funcall_p(rb_control_frame_t *);
- int scope = NOEX_PUBLIC;
- rb_thread_t *th = GET_THREAD();
-
- if (rb_vm_cfunc_funcall_p(th->cfp)) {
- scope = NOEX_NOSUPER | NOEX_PRIVATE;
- }
-
- return send_internal(argc, argv, recv, scope);
-}
-
-VALUE
-rb_funcall(VALUE recv, ID mid, int n, ...)
+static VALUE
+rb_mod_include(int argc, VALUE *argv, VALUE module)
{
- VALUE *argv;
- va_list ar;
- va_init_list(ar, n);
+ int i;
+ ID id_append_features, id_included;
- if (n > 0) {
- long i;
+ CONST_ID(id_append_features, "append_features");
+ CONST_ID(id_included, "included");
- argv = ALLOCA_N(VALUE, n);
+ if (BUILTIN_TYPE(module) == T_MODULE && FL_TEST(module, RMODULE_IS_REFINEMENT)) {
+ rb_raise(rb_eTypeError, "Refinement#include has been removed");
+ }
- for (i = 0; i < n; i++) {
- argv[i] = va_arg(ar, VALUE);
- }
- va_end(ar);
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
+ for (i = 0; i < argc; i++) {
+ Check_Type(argv[i], T_MODULE);
+ if (FL_TEST(argv[i], RMODULE_IS_REFINEMENT)) {
+ rb_raise(rb_eTypeError, "Cannot include refinement");
+ }
}
- else {
- argv = 0;
+ while (argc--) {
+ rb_funcall(argv[argc], id_append_features, 1, module);
+ rb_funcall(argv[argc], id_included, 1, module);
}
- return rb_call(CLASS_OF(recv), recv, mid, n, argv,
- NOEX_NOSUPER | NOEX_PRIVATE);
-}
-
-VALUE
-rb_funcall2(VALUE recv, ID mid, int argc, const VALUE *argv)
-{
- return rb_call(CLASS_OF(recv), recv, mid, argc, argv,
- NOEX_NOSUPER | NOEX_PRIVATE);
+ return module;
}
-VALUE
-rb_funcall3(VALUE recv, ID mid, int argc, const VALUE *argv)
-{
- return rb_call(CLASS_OF(recv), recv, mid, argc, argv, NOEX_PUBLIC);
-}
+/*
+ * call-seq:
+ * prepend_features(mod) -> mod
+ *
+ * When this module is prepended in another, Ruby calls
+ * #prepend_features in this module, passing it the receiving module
+ * in _mod_. Ruby's default implementation is to overlay the
+ * constants, methods, and module variables of this module to _mod_
+ * if this module has not already been added to _mod_ or one of its
+ * ancestors. See also Module#prepend.
+ */
static VALUE
-backtrace(int lev)
+rb_mod_prepend_features(VALUE module, VALUE prepend)
{
- return vm_backtrace(GET_THREAD(), lev);
+ if (!CLASS_OR_MODULE_P(prepend)) {
+ Check_Type(prepend, T_CLASS);
+ }
+ rb_prepend_module(prepend, module);
+
+ return module;
}
/*
* call-seq:
- * caller(start=1) => array
- *
- * Returns the current execution stack---an array containing strings in
- * the form ``<em>file:line</em>'' or ``<em>file:line: in
- * `method'</em>''. The optional _start_ parameter
- * determines the number of initial stack entries to omit from the
- * result.
+ * prepend(module, ...) -> self
*
- * def a(skip)
- * caller(skip)
- * end
- * def b(skip)
- * a(skip)
- * end
- * def c(skip)
- * b(skip)
- * end
- * c(0) #=> ["prog:2:in `a'", "prog:5:in `b'", "prog:8:in `c'", "prog:10"]
- * c(1) #=> ["prog:5:in `b'", "prog:8:in `c'", "prog:11"]
- * c(2) #=> ["prog:8:in `c'", "prog:12"]
- * c(3) #=> ["prog:13"]
+ * Invokes Module.prepend_features on each parameter in reverse order.
*/
static VALUE
-rb_f_caller(int argc, VALUE *argv)
+rb_mod_prepend(int argc, VALUE *argv, VALUE module)
{
- VALUE level;
- int lev;
-
- rb_scan_args(argc, argv, "01", &level);
-
- if (NIL_P(level))
- lev = 1;
- else
- lev = NUM2INT(level);
- if (lev < 0)
- rb_raise(rb_eArgError, "negative level (%d)", lev);
-
- return backtrace(lev);
-}
-
-void
-rb_backtrace(void)
-{
- long i;
- VALUE ary;
+ int i;
+ ID id_prepend_features, id_prepended;
- ary = backtrace(-1);
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- printf("\tfrom %s\n", RSTRING_PTR(RARRAY_PTR(ary)[i]));
+ if (BUILTIN_TYPE(module) == T_MODULE && FL_TEST(module, RMODULE_IS_REFINEMENT)) {
+ rb_raise(rb_eTypeError, "Refinement#prepend has been removed");
}
-}
-static VALUE
-make_backtrace(void)
-{
- return backtrace(-1);
-}
+ CONST_ID(id_prepend_features, "prepend_features");
+ CONST_ID(id_prepended, "prepended");
-static ID
-frame_func_id(rb_control_frame_t *cfp)
-{
- rb_iseq_t *iseq = cfp->iseq;
- if (!iseq) {
- return cfp->method_id;
- }
- while (iseq) {
- if (RUBY_VM_IFUNC_P(iseq)) {
- return rb_intern("<ifunc>");
- }
- if (iseq->defined_method_id) {
- return iseq->defined_method_id;
- }
- if (iseq->local_iseq == iseq) {
- break;
- }
- iseq = iseq->parent_iseq;
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
+ for (i = 0; i < argc; i++) {
+ Check_Type(argv[i], T_MODULE);
+ if (FL_TEST(argv[i], RMODULE_IS_REFINEMENT)) {
+ rb_raise(rb_eTypeError, "Cannot prepend refinement");
+ }
}
- return 0;
-}
-
-ID
-rb_frame_this_func(void)
-{
- return frame_func_id(GET_THREAD()->cfp);
-}
-
-ID
-rb_frame_callee(void)
-{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
- /* check if prev_cfp can be accessible */
- if ((void *)(th->stack + th->stack_size) == (void *)(prev_cfp)) {
- return 0;
+ while (argc--) {
+ rb_funcall(argv[argc], id_prepend_features, 1, module);
+ rb_funcall(argv[argc], id_prepended, 1, module);
}
- return frame_func_id(prev_cfp);
+ return module;
}
-void
-rb_frame_pop(void)
+static void
+ensure_class_or_module(VALUE obj)
{
- rb_thread_t *th = GET_THREAD();
- th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
+ if (!RB_TYPE_P(obj, T_CLASS) && !RB_TYPE_P(obj, T_MODULE)) {
+ rb_raise(rb_eTypeError,
+ "wrong argument type %"PRIsVALUE" (expected Class or Module)",
+ rb_obj_class(obj));
+ }
}
static VALUE
-rb_frame_self(void)
+hidden_identity_hash_new(void)
{
- return GET_THREAD()->cfp->self;
+ VALUE hash = rb_ident_hash_new();
+
+ RBASIC_CLEAR_CLASS(hash); /* hide from ObjectSpace */
+ return hash;
}
static VALUE
-eval(VALUE self, VALUE src, VALUE scope, const char *file, int line)
+refinement_superclass(VALUE superclass)
{
- int state;
- VALUE result = Qundef;
- VALUE envval;
- rb_binding_t *bind = 0;
- rb_thread_t *th = GET_THREAD();
- rb_env_t *env = NULL;
- NODE *stored_cref_stack = 0;
-
- if (file == 0) {
- file = rb_sourcefile();
- line = rb_sourceline();
- }
-
- PUSH_TAG();
- if ((state = EXEC_TAG()) == 0) {
- rb_iseq_t *iseq;
- volatile VALUE iseqval;
-
- if (scope != Qnil) {
- if (CLASS_OF(scope) == rb_cBinding) {
- GetBindingPtr(scope, bind);
- envval = bind->env;
- stored_cref_stack = bind->cref_stack;
- }
- else {
- rb_raise(rb_eTypeError,
- "wrong argument type %s (expected Binding)",
- rb_obj_classname(scope));
- }
- GetEnvPtr(envval, env);
- th->base_block = &env->block;
- }
- else {
- rb_control_frame_t *cfp = vm_get_ruby_level_cfp(th, th->cfp);
- th->base_block = RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp);
- th->base_block->iseq = cfp->iseq; /* TODO */
- }
-
- /* make eval iseq */
- th->parse_in_eval++;
- iseqval = rb_iseq_compile(src, rb_str_new2(file), INT2FIX(line));
- th->parse_in_eval--;
- rb_vm_set_eval_stack(th, iseqval);
- th->base_block = 0;
-
- if (0) { /* for debug */
- extern VALUE ruby_iseq_disasm(VALUE);
- printf("%s\n", RSTRING_PTR(ruby_iseq_disasm(iseqval)));
- }
-
- /* save new env */
- GetISeqPtr(iseqval, iseq);
- if (bind && iseq->local_size > 0) {
- bind->env = vm_make_env_object(th, th->cfp);
- }
-
- /* push tag */
- if (stored_cref_stack) {
- stored_cref_stack =
- vm_set_special_cref(th, env->block.lfp, stored_cref_stack);
- }
-
- /* kick */
- CHECK_STACK_OVERFLOW(th->cfp, iseq->stack_max);
- result = vm_eval_body(th);
- }
- POP_TAG();
-
- if (stored_cref_stack) {
- vm_set_special_cref(th, env->block.lfp, stored_cref_stack);
+ if (RB_TYPE_P(superclass, T_MODULE)) {
+ /* FIXME: Should ancestors of superclass be used here? */
+ return rb_include_class_new(RCLASS_ORIGIN(superclass), rb_cBasicObject);
}
-
- if (state) {
- if (state == TAG_RAISE) {
- VALUE errinfo = th->errinfo;
- if (strcmp(file, "(eval)") == 0) {
- VALUE mesg, errat;
-
- errat = get_backtrace(errinfo);
- mesg = rb_attr_get(errinfo, rb_intern("mesg"));
- if (!NIL_P(errat) && TYPE(errat) == T_ARRAY) {
- if (!NIL_P(mesg) && TYPE(mesg) == T_STRING) {
- rb_str_update(mesg, 0, 0, rb_str_new2(": "));
- rb_str_update(mesg, 0, 0, RARRAY_PTR(errat)[0]);
- }
- RARRAY_PTR(errat)[0] = RARRAY_PTR(backtrace(-2))[0];
- }
- }
- rb_exc_raise(errinfo);
- }
- JUMP_TAG(state);
+ else {
+ return superclass;
}
- return result;
}
-/*
- * call-seq:
- * eval(string [, binding [, filename [,lineno]]]) => obj
- *
- * Evaluates the Ruby expression(s) in <em>string</em>. If
- * <em>binding</em> is given, the evaluation is performed in its
- * context. The binding may be a <code>Binding</code> object or a
- * <code>Proc</code> object. If the optional <em>filename</em> and
- * <em>lineno</em> parameters are present, they will be used when
- * reporting syntax errors.
- *
- * def getBinding(str)
- * return binding
- * end
- * str = "hello"
- * eval "str + ' Fred'" #=> "hello Fred"
- * eval "str + ' Fred'", getBinding("bye") #=> "bye Fred"
+/*!
+ * \private
*/
-
-VALUE
-rb_f_eval(int argc, VALUE *argv, VALUE self)
+static void
+rb_using_refinement(rb_cref_t *cref, VALUE klass, VALUE module)
{
- VALUE src, scope, vfile, vline;
- char *file = "(eval)";
- int line = 1;
+ VALUE iclass, c, superclass = klass;
- rb_scan_args(argc, argv, "13", &src, &scope, &vfile, &vline);
- if (rb_safe_level() >= 4) {
- StringValue(src);
- if (!NIL_P(scope) && !OBJ_TAINTED(scope)) {
- rb_raise(rb_eSecurityError,
- "Insecure: can't modify trusted binding");
- }
+ ensure_class_or_module(klass);
+ Check_Type(module, T_MODULE);
+ if (NIL_P(CREF_REFINEMENTS(cref))) {
+ CREF_REFINEMENTS_SET(cref, hidden_identity_hash_new());
}
else {
- SafeStringValue(src);
- }
- if (argc >= 3) {
- StringValue(vfile);
- }
- if (argc >= 4) {
- line = NUM2INT(vline);
+ if (CREF_OMOD_SHARED(cref)) {
+ CREF_REFINEMENTS_SET(cref, rb_hash_dup(CREF_REFINEMENTS(cref)));
+ CREF_OMOD_SHARED_UNSET(cref);
+ }
+ if (!NIL_P(c = rb_hash_lookup(CREF_REFINEMENTS(cref), klass))) {
+ superclass = c;
+ while (c && RB_TYPE_P(c, T_ICLASS)) {
+ if (RBASIC(c)->klass == module) {
+ /* already used refinement */
+ return;
+ }
+ c = RCLASS_SUPER(c);
+ }
+ }
}
+ superclass = refinement_superclass(superclass);
+ c = iclass = rb_include_class_new(module, superclass);
+ RCLASS_SET_REFINED_CLASS(c, klass);
- if (!NIL_P(vfile))
- file = RSTRING_PTR(vfile);
- return eval(self, src, scope, file, line);
-}
+ RCLASS_WRITE_M_TBL(c, RCLASS_M_TBL(module));
-VALUE vm_cfp_svar_get(rb_thread_t *th, rb_control_frame_t *cfp, VALUE key);
-void vm_cfp_svar_set(rb_thread_t *th, rb_control_frame_t *cfp, VALUE key, VALUE val);
+ rb_hash_aset(CREF_REFINEMENTS(cref), klass, iclass);
+}
-/* function to call func under the specified class/module context */
-static VALUE
-exec_under(VALUE (*func) (VALUE), VALUE under, VALUE self, VALUE args)
+static int
+using_refinement(VALUE klass, VALUE module, VALUE arg)
{
- VALUE val = Qnil; /* OK */
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
- rb_control_frame_t *pcfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
- VALUE stored_self = pcfp->self;
- NODE *stored_cref = 0;
-
- rb_block_t block;
- rb_block_t *blockptr;
- int state;
+ rb_cref_t *cref = (rb_cref_t *) arg;
- /* replace environment */
- pcfp->self = self;
- if ((blockptr = GC_GUARDED_PTR_REF(*th->cfp->lfp)) != 0) {
- /* copy block info */
- /* TODO: why? */
- block = *blockptr;
- block.self = self;
- *th->cfp->lfp = GC_GUARDED_PTR(&block);
- }
-
- while (!RUBY_VM_NORMAL_ISEQ_P(cfp->iseq)) {
- cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
- }
-
- stored_cref = (NODE *)vm_cfp_svar_get(th, cfp, 2);
- vm_cfp_svar_set(th, cfp, 2, (VALUE)vm_cref_push(th, under, NOEX_PUBLIC));
-
- PUSH_TAG();
- if ((state = EXEC_TAG()) == 0) {
- val = (*func) (args);
- }
- POP_TAG();
-
- /* restore environment */
- vm_cfp_svar_set(th, cfp, 2, (VALUE)stored_cref);
- pcfp->self = stored_self;
-
- if (state) {
- JUMP_TAG(state);
- }
- return val;
+ rb_using_refinement(cref, klass, module);
+ return ST_CONTINUE;
}
-static VALUE
-yield_under_i(VALUE arg)
+static void
+using_module_recursive(const rb_cref_t *cref, VALUE klass)
{
- if (arg == Qundef) {
- return rb_yield_0(0, 0);
- }
- else {
- return rb_yield_0(RARRAY_LEN(arg), RARRAY_PTR(arg));
+ ID id_refinements;
+ VALUE super, module, refinements;
+
+ super = RCLASS_SUPER(klass);
+ if (super) {
+ using_module_recursive(cref, super);
}
-}
+ switch (BUILTIN_TYPE(klass)) {
+ case T_MODULE:
+ module = klass;
+ break;
-/* block eval under the class/module context */
-static VALUE
-yield_under(VALUE under, VALUE self, VALUE values)
-{
- return exec_under(yield_under_i, under, self, values);
-}
+ case T_ICLASS:
+ module = RBASIC(klass)->klass;
+ break;
-static VALUE
-eval_under_i(VALUE arg)
-{
- VALUE *args = (VALUE *)arg;
- return eval(args[0], args[1], Qnil, (char *)args[2], (int)args[3]);
+ default:
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Module)",
+ rb_obj_classname(klass));
+ break;
+ }
+ CONST_ID(id_refinements, "__refinements__");
+ refinements = rb_attr_get(module, id_refinements);
+ if (NIL_P(refinements)) return;
+ rb_hash_foreach(refinements, using_refinement, (VALUE) cref);
}
-/* string eval under the class/module context */
-static VALUE
-eval_under(VALUE under, VALUE self, VALUE src, const char *file, int line)
+/*!
+ * \private
+ */
+static void
+rb_using_module(const rb_cref_t *cref, VALUE module)
{
- VALUE args[4];
-
- if (rb_safe_level() >= 4) {
- StringValue(src);
- }
- else {
- SafeStringValue(src);
- }
- args[0] = self;
- args[1] = src;
- args[2] = (VALUE)file;
- args[3] = (VALUE)line;
- return exec_under(eval_under_i, under, self, (VALUE)args);
+ Check_Type(module, T_MODULE);
+ using_module_recursive(cref, module);
+ rb_clear_all_refinement_method_cache();
}
-static VALUE
-specific_eval(int argc, VALUE *argv, VALUE klass, VALUE self)
+void
+rb_vm_using_module(VALUE module)
{
- if (rb_block_given_p()) {
- if (argc > 0) {
- rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)",
- argc);
- }
- return yield_under(klass, self, Qundef);
- }
- else {
- char *file = "(eval)";
- int line = 1;
-
- if (argc == 0) {
- rb_raise(rb_eArgError, "block not supplied");
- }
- else {
- if (rb_safe_level() >= 4) {
- StringValue(argv[0]);
- }
- else {
- SafeStringValue(argv[0]);
- }
- if (argc > 3) {
- const char *name = rb_id2name(rb_frame_callee());
- rb_raise(rb_eArgError,
- "wrong number of arguments: %s(src) or %s{..}",
- name, name);
- }
- if (argc > 2)
- line = NUM2INT(argv[2]);
- if (argc > 1) {
- file = StringValuePtr(argv[1]);
- }
- }
- return eval_under(klass, self, argv[0], file, line);
- }
+ rb_using_module(rb_vm_cref_replace_with_duplicated_cref(), module);
}
/*
* call-seq:
- * obj.instance_eval(string [, filename [, lineno]] ) => obj
- * obj.instance_eval {| | block } => obj
- *
- * Evaluates a string containing Ruby source code, or the given block,
- * within the context of the receiver (_obj_). In order to set the
- * context, the variable +self+ is set to _obj_ while
- * the code is executing, giving the code access to _obj_'s
- * instance variables. In the version of <code>instance_eval</code>
- * that takes a +String+, the optional second and third
- * parameters supply a filename and starting line number that are used
- * when reporting compilation errors.
+ * target -> class_or_module
*
- * class Klass
- * def initialize
- * @secret = 99
+ * Return the class or module refined by the receiver.
+ *
+ * module M
+ * refine String do
* end
* end
- * k = Klass.new
- * k.instance_eval { @secret } #=> 99
+ *
+ * M.refinements[0].target # => String
*/
-
VALUE
-rb_obj_instance_eval(int argc, VALUE *argv, VALUE self)
+rb_refinement_module_get_refined_class(VALUE module)
{
- VALUE klass;
+ ID id_refined_class;
- if (SPECIAL_CONST_P(self)) {
- klass = Qnil;
- }
- else {
- klass = rb_singleton_class(self);
- }
- return specific_eval(argc, argv, klass, self);
+ CONST_ID(id_refined_class, "__refined_class__");
+ return rb_attr_get(module, id_refined_class);
}
-/*
- * call-seq:
- * obj.instance_exec(arg...) {|var...| block } => obj
- *
- * Executes the given block within the context of the receiver
- * (_obj_). In order to set the context, the variable +self+ is set
- * to _obj_ while the code is executing, giving the code access to
- * _obj_'s instance variables. Arguments are passed as block parameters.
- *
- * class Klass
- * def initialize
- * @secret = 99
- * end
- * end
- * k = Klass.new
- * k.instance_exec(5) {|x| @secret+x } #=> 104
- */
-
-VALUE
-rb_obj_instance_exec(int argc, VALUE *argv, VALUE self)
+static void
+add_activated_refinement(VALUE activated_refinements,
+ VALUE klass, VALUE refinement)
{
- VALUE klass;
+ VALUE iclass, c, superclass = klass;
- if (SPECIAL_CONST_P(self)) {
- klass = Qnil;
+ if (!NIL_P(c = rb_hash_lookup(activated_refinements, klass))) {
+ superclass = c;
+ while (c && RB_TYPE_P(c, T_ICLASS)) {
+ if (RBASIC(c)->klass == refinement) {
+ /* already used refinement */
+ return;
+ }
+ c = RCLASS_SUPER(c);
+ }
}
- else {
- klass = rb_singleton_class(self);
+ superclass = refinement_superclass(superclass);
+ c = iclass = rb_include_class_new(refinement, superclass);
+ RCLASS_SET_REFINED_CLASS(c, klass);
+ refinement = RCLASS_SUPER(refinement);
+ while (refinement && refinement != klass) {
+ c = rb_class_set_super(c, rb_include_class_new(refinement, RCLASS_SUPER(c)));
+ RCLASS_SET_REFINED_CLASS(c, klass);
+ refinement = RCLASS_SUPER(refinement);
}
- return yield_under(klass, self, rb_ary_new4(argc, argv));
+ rb_hash_aset(activated_refinements, klass, iclass);
}
-/*
- * call-seq:
- * mod.class_eval(string [, filename [, lineno]]) => obj
- * mod.module_eval {|| block } => obj
- *
- * Evaluates the string or block in the context of _mod_. This can
- * be used to add methods to a class. <code>module_eval</code> returns
- * the result of evaluating its argument. The optional _filename_
- * and _lineno_ parameters set the text for error messages.
- *
- * class Thing
- * end
- * a = %q{def hello() "Hello there!" end}
- * Thing.module_eval(a)
- * puts Thing.new.hello()
- * Thing.module_eval("invalid code", "dummy", 123)
- *
- * <em>produces:</em>
- *
- * Hello there!
- * dummy:123:in `module_eval': undefined local variable
- * or method `code' for Thing:Class
- */
-
-VALUE
-rb_mod_module_eval(int argc, VALUE *argv, VALUE mod)
-{
- return specific_eval(argc, argv, mod, mod);
+void
+rb_refinement_setup(struct rb_refinements_data *data, VALUE module, VALUE klass)
+{
+ VALUE refinement;
+ ID id_refinements, id_activated_refinements,
+ id_refined_class, id_defined_at;
+ VALUE refinements, activated_refinements;
+
+ CONST_ID(id_refinements, "__refinements__");
+ refinements = rb_attr_get(module, id_refinements);
+ if (NIL_P(refinements)) {
+ refinements = hidden_identity_hash_new();
+ rb_ivar_set(module, id_refinements, refinements);
+ }
+ CONST_ID(id_activated_refinements, "__activated_refinements__");
+ activated_refinements = rb_attr_get(module, id_activated_refinements);
+ if (NIL_P(activated_refinements)) {
+ activated_refinements = hidden_identity_hash_new();
+ rb_ivar_set(module, id_activated_refinements,
+ activated_refinements);
+ }
+ refinement = rb_hash_lookup(refinements, klass);
+ if (NIL_P(refinement)) {
+ VALUE superclass = refinement_superclass(klass);
+ refinement = rb_refinement_new();
+ rb_class_set_super(refinement, superclass);
+ RUBY_ASSERT(BUILTIN_TYPE(refinement) == T_MODULE);
+ FL_SET(refinement, RMODULE_IS_REFINEMENT);
+ CONST_ID(id_refined_class, "__refined_class__");
+ rb_ivar_set(refinement, id_refined_class, klass);
+ CONST_ID(id_defined_at, "__defined_at__");
+ rb_ivar_set(refinement, id_defined_at, module);
+ rb_hash_aset(refinements, klass, refinement);
+ add_activated_refinement(activated_refinements, klass, refinement);
+ }
+
+ data->refinement = refinement;
+ data->refinements = activated_refinements;
}
/*
* call-seq:
- * mod.module_exec(arg...) {|var...| block } => obj
- * mod.class_exec(arg...) {|var...| block } => obj
+ * refine(mod) { block } -> module
*
- * Evaluates the given block in the context of the class/module.
- * The method defined in the block will belong to the receiver.
+ * Refine <i>mod</i> in the receiver.
*
- * class Thing
- * end
- * Thing.class_exec{
- * def hello() "Hello there!" end
- * }
- * puts Thing.new.hello()
- *
- * <em>produces:</em>
- *
- * Hello there!
+ * Returns a module, where refined methods are defined.
*/
-VALUE
-rb_mod_module_exec(int argc, VALUE *argv, VALUE mod)
+static VALUE
+rb_mod_refine(VALUE module, VALUE klass)
{
- return yield_under(mod, mod, rb_ary_new4(argc, argv));
-}
+ /* module is the receiver of #refine, klass is a module to be refined (`mod` in the doc) */
+ rb_thread_t *th = GET_THREAD();
+ VALUE block_handler = rb_vm_frame_block_handler(th->ec->cfp);
+ struct rb_refinements_data data;
-static void
-secure_visibility(VALUE self)
-{
- if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
- rb_raise(rb_eSecurityError,
- "Insecure: can't change method visibility");
+ if (block_handler == VM_BLOCK_HANDLER_NONE) {
+ rb_raise(rb_eArgError, "no block given");
+ }
+ if (vm_block_handler_type(block_handler) != block_handler_type_iseq) {
+ rb_raise(rb_eArgError, "can't pass a Proc as a block to Module#refine");
}
+
+ ensure_class_or_module(klass);
+
+ rb_refinement_setup(&data, module, klass);
+
+ rb_yield_refine_block(data.refinement, data.refinements);
+ return data.refinement;
}
static void
-set_method_visibility(VALUE self, int argc, VALUE *argv, ID ex)
+ignored_block(VALUE module, const char *klass)
{
- int i;
- secure_visibility(self);
- for (i = 0; i < argc; i++) {
- rb_export_method(self, rb_to_id(argv[i]), ex);
+ const char *anon = "";
+ Check_Type(module, T_MODULE);
+ if (!RTEST(rb_search_class_path(module))) {
+ anon = ", maybe for Module.new";
}
- rb_clear_cache_by_class(self);
+ rb_warn("%s""using doesn't call the given block""%s.", klass, anon);
}
/*
* call-seq:
- * public => self
- * public(symbol, ...) => self
+ * using(module) -> self
*
- * With no arguments, sets the default visibility for subsequently
- * defined methods to public. With arguments, sets the named methods to
- * have public visibility.
+ * Import class refinements from <i>module</i> into the current class or
+ * module definition.
*/
static VALUE
-rb_mod_public(int argc, VALUE *argv, VALUE module)
+mod_using(VALUE self, VALUE module)
{
- secure_visibility(module);
- if (argc == 0) {
- SCOPE_SET(NOEX_PUBLIC);
- }
- else {
- set_method_visibility(module, argc, argv, NOEX_PUBLIC);
- }
- return module;
-}
-
-/*
- * call-seq:
- * protected => self
- * protected(symbol, ...) => self
- *
- * With no arguments, sets the default visibility for subsequently
- * defined methods to protected. With arguments, sets the named methods
- * to have protected visibility.
- */
+ rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
-static VALUE
-rb_mod_protected(int argc, VALUE *argv, VALUE module)
-{
- secure_visibility(module);
- if (argc == 0) {
- SCOPE_SET(NOEX_PROTECTED);
+ if (prev_frame_func()) {
+ rb_raise(rb_eRuntimeError,
+ "Module#using is not permitted in methods");
}
- else {
- set_method_visibility(module, argc, argv, NOEX_PROTECTED);
+ if (prev_cfp && prev_cfp->self != self) {
+ rb_raise(rb_eRuntimeError, "Module#using is not called on self");
}
- return module;
+ if (rb_block_given_p()) {
+ ignored_block(module, "Module#");
+ }
+ rb_using_module(rb_vm_cref_replace_with_duplicated_cref(), module);
+ return self;
}
+
/*
* call-seq:
- * private => self
- * private(symbol, ...) => self
+ * refinements -> array
*
- * With no arguments, sets the default visibility for subsequently
- * defined methods to private. With arguments, sets the named methods
- * to have private visibility.
+ * Returns an array of +Refinement+ defined within the receiver.
*
- * module Mod
- * def a() end
- * def b() end
- * private
- * def c() end
- * private :a
+ * module A
+ * refine Integer do
+ * end
+ *
+ * refine String do
+ * end
* end
- * Mod.private_instance_methods #=> ["a", "c"]
+ *
+ * p A.refinements
+ *
+ * <em>produces:</em>
+ *
+ * [#<refinement:Integer@A>, #<refinement:String@A>]
*/
-
static VALUE
-rb_mod_private(int argc, VALUE *argv, VALUE module)
+mod_refinements(VALUE self)
{
- secure_visibility(module);
- if (argc == 0) {
- SCOPE_SET(NOEX_PRIVATE);
- }
- else {
- set_method_visibility(module, argc, argv, NOEX_PRIVATE);
+ ID id_refinements;
+ VALUE refinements;
+
+ CONST_ID(id_refinements, "__refinements__");
+ refinements = rb_attr_get(self, id_refinements);
+ if (NIL_P(refinements)) {
+ return rb_ary_new();
}
- return module;
+ return rb_hash_values(refinements);
}
-/*
- * call-seq:
- * mod.public_class_method(symbol, ...) => mod
- *
- * Makes a list of existing class methods public.
- */
-
-static VALUE
-rb_mod_public_method(int argc, VALUE *argv, VALUE obj)
+static int
+used_modules_i(VALUE _, VALUE mod, VALUE ary)
{
- set_method_visibility(CLASS_OF(obj), argc, argv, NOEX_PUBLIC);
- return obj;
+ ID id_defined_at;
+ CONST_ID(id_defined_at, "__defined_at__");
+ while (BUILTIN_TYPE(rb_class_of(mod)) == T_MODULE && FL_TEST(rb_class_of(mod), RMODULE_IS_REFINEMENT)) {
+ rb_ary_push(ary, rb_attr_get(rb_class_of(mod), id_defined_at));
+ mod = RCLASS_SUPER(mod);
+ }
+ return ST_CONTINUE;
}
/*
* call-seq:
- * mod.private_class_method(symbol, ...) => mod
+ * used_modules -> array
*
- * Makes existing class methods private. Often used to hide the default
- * constructor <code>new</code>.
+ * Returns an array of all modules used in the current scope. The ordering
+ * of modules in the resulting array is not defined.
*
- * class SimpleSingleton # Not thread safe
- * private_class_method :new
- * def SimpleSingleton.create(*args, &block)
- * @me = new(*args, &block) if ! @me
- * @me
+ * module A
+ * refine Object do
* end
* end
+ *
+ * module B
+ * refine Object do
+ * end
+ * end
+ *
+ * using A
+ * using B
+ * p Module.used_modules
+ *
+ * <em>produces:</em>
+ *
+ * [B, A]
*/
-
static VALUE
-rb_mod_private_method(int argc, VALUE *argv, VALUE obj)
+rb_mod_s_used_modules(VALUE _)
{
- set_method_visibility(CLASS_OF(obj), argc, argv, NOEX_PRIVATE);
- return obj;
-}
+ const rb_cref_t *cref = rb_vm_cref();
+ VALUE ary = rb_ary_new();
-/*
- * call-seq:
- * public
- * public(symbol, ...)
- *
- * With no arguments, sets the default visibility for subsequently
- * defined methods to public. With arguments, sets the named methods to
- * have public visibility.
- */
+ while (cref) {
+ if (!NIL_P(CREF_REFINEMENTS(cref))) {
+ rb_hash_foreach(CREF_REFINEMENTS(cref), used_modules_i, ary);
+ }
+ cref = CREF_NEXT(cref);
+ }
-static VALUE
-top_public(int argc, VALUE *argv)
-{
- return rb_mod_public(argc, argv, rb_cObject);
+ return rb_funcall(ary, rb_intern("uniq"), 0);
}
-static VALUE
-top_private(int argc, VALUE *argv)
+static int
+used_refinements_i(VALUE _, VALUE mod, VALUE ary)
{
- return rb_mod_private(argc, argv, rb_cObject);
+ while (BUILTIN_TYPE(rb_class_of(mod)) == T_MODULE && FL_TEST(rb_class_of(mod), RMODULE_IS_REFINEMENT)) {
+ rb_ary_push(ary, rb_class_of(mod));
+ mod = RCLASS_SUPER(mod);
+ }
+ return ST_CONTINUE;
}
/*
* call-seq:
- * module_function(symbol, ...) => self
+ * used_refinements -> array
*
- * Creates module functions for the named methods. These functions may
- * be called with the module as a receiver, and also become available
- * as instance methods to classes that mix in the module. Module
- * functions are copies of the original, and so may be changed
- * independently. The instance-method versions are made private. If
- * used with no arguments, subsequently defined methods become module
- * functions.
+ * Returns an array of all modules used in the current scope. The ordering
+ * of modules in the resulting array is not defined.
*
- * module Mod
- * def one
- * "This is one"
- * end
- * module_function :one
- * end
- * class Cls
- * include Mod
- * def callOne
- * one
+ * module A
+ * refine Object do
* end
* end
- * Mod.one #=> "This is one"
- * c = Cls.new
- * c.callOne #=> "This is one"
- * module Mod
- * def one
- * "This is the new one"
+ *
+ * module B
+ * refine Object do
* end
* end
- * Mod.one #=> "This is one"
- * c.callOne #=> "This is the new one"
+ *
+ * using A
+ * using B
+ * p Module.used_refinements
+ *
+ * <em>produces:</em>
+ *
+ * [#<refinement:Object@B>, #<refinement:Object@A>]
*/
-
static VALUE
-rb_mod_modfunc(int argc, VALUE *argv, VALUE module)
+rb_mod_s_used_refinements(VALUE _)
{
- int i;
- ID id;
- NODE *fbody;
-
- if (TYPE(module) != T_MODULE) {
- rb_raise(rb_eTypeError, "module_function must be called for modules");
- }
+ const rb_cref_t *cref = rb_vm_cref();
+ VALUE ary = rb_ary_new();
- secure_visibility(module);
- if (argc == 0) {
- SCOPE_SET(NOEX_MODFUNC);
- return module;
+ while (cref) {
+ if (!NIL_P(CREF_REFINEMENTS(cref))) {
+ rb_hash_foreach(CREF_REFINEMENTS(cref), used_refinements_i, ary);
+ }
+ cref = CREF_NEXT(cref);
}
- set_method_visibility(module, argc, argv, NOEX_PRIVATE);
-
- for (i = 0; i < argc; i++) {
- VALUE m = module;
-
- id = rb_to_id(argv[i]);
- for (;;) {
- fbody = search_method(m, id, &m);
- if (fbody == 0) {
- fbody = search_method(rb_cObject, id, &m);
- }
- if (fbody == 0 || fbody->nd_body == 0) {
- rb_bug("undefined method `%s'; can't happen", rb_id2name(id));
- }
- if (nd_type(fbody->nd_body->nd_body) != NODE_ZSUPER) {
- break; /* normal case: need not to follow 'super' link */
- }
- m = RCLASS_SUPER(m);
- if (!m)
- break;
- }
- rb_add_method(rb_singleton_class(module), id, fbody->nd_body->nd_body,
- NOEX_PUBLIC);
- }
- return module;
+ return ary;
}
-/*
- * call-seq:
- * append_features(mod) => mod
- *
- * When this module is included in another, Ruby calls
- * <code>append_features</code> in this module, passing it the
- * receiving module in _mod_. Ruby's default implementation is
- * to add the constants, methods, and module variables of this module
- * to _mod_ if this module has not already been added to
- * _mod_ or one of its ancestors. See also <code>Module#include</code>.
- */
+struct refinement_import_methods_arg {
+ rb_cref_t *cref;
+ VALUE refinement;
+ VALUE module;
+};
-static VALUE
-rb_mod_append_features(VALUE module, VALUE include)
+/* vm.c */
+rb_cref_t *rb_vm_cref_dup_without_refinements(const rb_cref_t *cref);
+
+static enum rb_id_table_iterator_result
+refinement_import_methods_i(ID key, VALUE value, void *data)
{
- switch (TYPE(include)) {
- case T_CLASS:
- case T_MODULE:
- break;
- default:
- Check_Type(include, T_CLASS);
- break;
- }
- rb_include_module(include, module);
+ const rb_method_entry_t *me = (const rb_method_entry_t *)value;
+ struct refinement_import_methods_arg *arg = (struct refinement_import_methods_arg *)data;
- return module;
+ if (me->def->type != VM_METHOD_TYPE_ISEQ) {
+ rb_raise(rb_eArgError, "Can't import method which is not defined with Ruby code: %"PRIsVALUE"#%"PRIsVALUE, rb_class_path(arg->module), rb_id2str(key));
+ }
+ rb_cref_t *new_cref = rb_vm_cref_dup_without_refinements(me->def->body.iseq.cref);
+ CREF_REFINEMENTS_SET(new_cref, CREF_REFINEMENTS(arg->cref));
+ rb_add_method_iseq(arg->refinement, key, me->def->body.iseq.iseqptr, new_cref, METHOD_ENTRY_VISI(me));
+ return ID_TABLE_CONTINUE;
}
/*
- * call-seq:
- * include(module, ...) => self
- *
- * Invokes <code>Module.append_features</code> on each parameter in turn.
+ * Note: docs for the method are in class.c
*/
static VALUE
-rb_mod_include(int argc, VALUE *argv, VALUE module)
+refinement_import_methods(int argc, VALUE *argv, VALUE refinement)
{
int i;
+ struct refinement_import_methods_arg arg;
- for (i = 0; i < argc; i++)
- Check_Type(argv[i], T_MODULE);
- while (argc--) {
- rb_funcall(argv[argc], rb_intern("append_features"), 1, module);
- rb_funcall(argv[argc], rb_intern("included"), 1, module);
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
+ for (i = 0; i < argc; i++) {
+ Check_Type(argv[i], T_MODULE);
+ if (RCLASS_SUPER(argv[i])) {
+ rb_warn("%"PRIsVALUE" has ancestors, but Refinement#import_methods doesn't import their methods", rb_class_path(argv[i]));
+ }
}
- return module;
+ arg.cref = rb_vm_cref_replace_with_duplicated_cref();
+ arg.refinement = refinement;
+ for (i = 0; i < argc; i++) {
+ arg.module = argv[i];
+ struct rb_id_table *m_tbl = RCLASS_M_TBL(argv[i]);
+ if (!m_tbl) continue;
+ rb_id_table_foreach(m_tbl, refinement_import_methods_i, &arg);
+ }
+ return refinement;
+}
+
+void
+rb_obj_call_init(VALUE obj, int argc, const VALUE *argv)
+{
+ rb_obj_call_init_kw(obj, argc, argv, RB_NO_KEYWORDS);
}
void
-rb_obj_call_init(VALUE obj, int argc, VALUE *argv)
+rb_obj_call_init_kw(VALUE obj, int argc, const VALUE *argv, int kw_splat)
{
- PASS_PASSED_BLOCK();
- rb_funcall2(obj, init, argc, argv);
+ PASS_PASSED_BLOCK_HANDLER();
+ rb_funcallv_kw(obj, idInitialize, argc, argv, kw_splat);
}
void
@@ -2389,11 +1860,11 @@ rb_extend_object(VALUE obj, VALUE module)
/*
* call-seq:
- * extend_object(obj) => obj
+ * extend_object(obj) -> obj
*
* Extends the specified object by adding this module's constants and
* methods (which are added as singleton methods). This is the callback
- * method used by <code>Object#extend</code>.
+ * method used by Object#extend.
*
* module Picky
* def Picky.extend_object(o)
@@ -2423,7 +1894,7 @@ rb_mod_extend_object(VALUE mod, VALUE obj)
/*
* call-seq:
- * obj.extend(module, ...) => obj
+ * obj.extend(module, ...) -> obj
*
* Adds to _obj_ the instance methods from each module given as a
* parameter.
@@ -2450,135 +1921,146 @@ static VALUE
rb_obj_extend(int argc, VALUE *argv, VALUE obj)
{
int i;
+ ID id_extend_object, id_extended;
- if (argc == 0) {
- rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)");
+ CONST_ID(id_extend_object, "extend_object");
+ CONST_ID(id_extended, "extended");
+
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
+ for (i = 0; i < argc; i++) {
+ Check_Type(argv[i], T_MODULE);
+ if (FL_TEST(argv[i], RMODULE_IS_REFINEMENT)) {
+ rb_raise(rb_eTypeError, "Cannot extend object with refinement");
+ }
}
- for (i = 0; i < argc; i++)
- Check_Type(argv[i], T_MODULE);
while (argc--) {
- rb_funcall(argv[argc], rb_intern("extend_object"), 1, obj);
- rb_funcall(argv[argc], rb_intern("extended"), 1, obj);
+ rb_funcall(argv[argc], id_extend_object, 1, obj);
+ rb_funcall(argv[argc], id_extended, 1, obj);
}
return obj;
}
+VALUE
+rb_top_main_class(const char *method)
+{
+ VALUE klass = GET_THREAD()->top_wrapper;
+
+ if (!klass) return rb_cObject;
+ rb_warning("main.%s in the wrapped load is effective only in wrapper module", method);
+ return klass;
+}
+
/*
* call-seq:
- * include(module, ...) => self
+ * include(module, ...) -> self
*
- * Invokes <code>Module.append_features</code>
- * on each parameter in turn. Effectively adds the methods and constants
- * in each module to the receiver.
+ * Invokes Module.append_features on each parameter in turn.
+ * Effectively adds the methods and constants in each module to the
+ * receiver.
*/
static VALUE
top_include(int argc, VALUE *argv, VALUE self)
{
+ return rb_mod_include(argc, argv, rb_top_main_class("include"));
+}
+
+/*
+ * call-seq:
+ * using(module) -> self
+ *
+ * Import class refinements from <i>module</i> into the scope where
+ * #using is called.
+ */
+
+static VALUE
+top_using(VALUE self, VALUE module)
+{
+ const rb_cref_t *cref = CREF_NEXT(rb_vm_cref());
+ rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
rb_thread_t *th = GET_THREAD();
- rb_secure(4);
- if (th->top_wrapper) {
- rb_warning
- ("main#include in the wrapped load is effective only in wrapper module");
- return rb_mod_include(argc, argv, th->top_wrapper);
+ if ((th->top_wrapper ? CREF_NEXT(cref) : cref) ||
+ (prev_cfp && rb_vm_frame_method_entry(prev_cfp))) {
+ rb_raise(rb_eRuntimeError, "main.using is permitted only at toplevel");
}
- return rb_mod_include(argc, argv, rb_cObject);
+ if (rb_block_given_p()) {
+ ignored_block(module, "main.");
+ }
+ rb_using_module(rb_vm_cref_replace_with_duplicated_cref(), module);
+ return self;
}
-VALUE rb_f_trace_var();
-VALUE rb_f_untrace_var();
-
-static VALUE *
-errinfo_place(void)
+static const VALUE *
+errinfo_place(const rb_execution_context_t *ec)
{
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp = th->cfp;
- rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(th);
+ const rb_control_frame_t *cfp = ec->cfp;
+ const rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) {
- if (RUBY_VM_NORMAL_ISEQ_P(cfp->iseq)) {
- if (cfp->iseq->type == ISEQ_TYPE_RESCUE) {
- return &cfp->dfp[-1];
- }
- else if (cfp->iseq->type == ISEQ_TYPE_ENSURE &&
- TYPE(cfp->dfp[-1]) != T_NODE) {
- return &cfp->dfp[-1];
- }
- }
- cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
+ if (VM_FRAME_RUBYFRAME_P(cfp)) {
+ if (ISEQ_BODY(cfp->iseq)->type == ISEQ_TYPE_RESCUE) {
+ return &cfp->ep[VM_ENV_INDEX_LAST_LVAR];
+ }
+ else if (ISEQ_BODY(cfp->iseq)->type == ISEQ_TYPE_ENSURE &&
+ !THROW_DATA_P(cfp->ep[VM_ENV_INDEX_LAST_LVAR]) &&
+ !FIXNUM_P(cfp->ep[VM_ENV_INDEX_LAST_LVAR])) {
+ return &cfp->ep[VM_ENV_INDEX_LAST_LVAR];
+ }
+ }
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
return 0;
}
-static VALUE
-get_errinfo(void)
+VALUE
+rb_ec_get_errinfo(const rb_execution_context_t *ec)
{
- VALUE *ptr = errinfo_place();
+ const VALUE *ptr = errinfo_place(ec);
if (ptr) {
- return *ptr;
+ return *ptr;
}
else {
- return Qnil;
+ return ec->errinfo;
}
}
static VALUE
-errinfo_getter(ID id)
+get_errinfo(void)
{
- return get_errinfo();
+ return get_ec_errinfo(GET_EC());
}
-#if 0
-static void
-errinfo_setter(VALUE val, ID id, VALUE *var)
+static VALUE
+errinfo_getter(ID id, VALUE *_)
{
- if (!NIL_P(val) && !rb_obj_is_kind_of(val, rb_eException)) {
- rb_raise(rb_eTypeError, "assigning non-exception to $!");
- }
- else {
- VALUE *ptr = errinfo_place();
- if (ptr) {
- *ptr = val;
- }
- else {
- rb_raise(rb_eRuntimeError, "errinfo_setter: not in rescue clause.");
- }
- }
+ return get_errinfo();
}
-#endif
VALUE
rb_errinfo(void)
{
- rb_thread_t *th = GET_THREAD();
- return th->errinfo;
+ return GET_EC()->errinfo;
}
void
rb_set_errinfo(VALUE err)
{
if (!NIL_P(err) && !rb_obj_is_kind_of(err, rb_eException)) {
- rb_raise(rb_eTypeError, "assigning non-exception to $!");
+ rb_raise(rb_eTypeError, "assigning non-exception to $!");
}
- GET_THREAD()->errinfo = err;
-}
-
-VALUE
-rb_rubylevel_errinfo(void)
-{
- return get_errinfo();
+ GET_EC()->errinfo = err;
}
static VALUE
-errat_getter(ID id)
+errat_getter(ID id, VALUE *_)
{
VALUE err = get_errinfo();
if (!NIL_P(err)) {
- return get_backtrace(err);
+ return rb_get_backtrace(err);
}
else {
- return Qnil;
+ return Qnil;
}
}
@@ -2587,266 +2069,217 @@ errat_setter(VALUE val, ID id, VALUE *var)
{
VALUE err = get_errinfo();
if (NIL_P(err)) {
- rb_raise(rb_eArgError, "$! not set");
+ rb_raise(rb_eArgError, "$! not set");
}
set_backtrace(err, val);
}
/*
* call-seq:
- * local_variables => array
+ * __method__ -> symbol
*
- * Returns the names of the current local variables.
+ * Returns the name at the definition of the current method as a
+ * Symbol.
+ * If called outside of a method, it returns <code>nil</code>.
*
- * fred = 1
- * for i in 1..10
- * # ...
- * end
- * local_variables #=> ["fred", "i"]
*/
-int vm_collect_local_variables_in_heap(rb_thread_t *th, VALUE *dfp, VALUE ary);
-
static VALUE
-rb_f_local_variables(void)
+rb_f_method_name(VALUE _)
{
- VALUE ary = rb_ary_new();
- rb_thread_t *th = GET_THREAD();
- rb_control_frame_t *cfp =
- vm_get_ruby_level_cfp(th, RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp));
- int i;
+ ID fname = prev_frame_func(); /* need *method* ID */
- while (1) {
- if (cfp->iseq) {
- for (i = 0; i < cfp->iseq->local_table_size; i++) {
- ID lid = cfp->iseq->local_table[i];
- if (lid) {
- const char *vname = rb_id2name(lid);
- /* should skip temporary variable */
- if (vname) {
- rb_ary_push(ary, rb_str_new2(vname));
- }
- }
- }
- }
- if (cfp->lfp != cfp->dfp) {
- /* block */
- VALUE *dfp = GC_GUARDED_PTR_REF(cfp->dfp[0]);
-
- if (vm_collect_local_variables_in_heap(th, dfp, ary)) {
- break;
- }
- else {
- while (cfp->dfp != dfp) {
- cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
- }
- }
- }
- else {
- break;
- }
+ if (fname) {
+ return ID2SYM(fname);
+ }
+ else {
+ return Qnil;
}
- return ary;
}
-
/*
* call-seq:
- * __method__ => symbol
+ * __callee__ -> symbol
*
- * Returns the name of the current method as a Symbol.
- * If called from inside of an aliased method it will return the original
- * nonaliased name.
+ * Returns the called name of the current method as a Symbol.
* If called outside of a method, it returns <code>nil</code>.
- * See also <code>\_\_callee__</code>.
*
*/
static VALUE
-rb_f_method_name(void)
+rb_f_callee_name(VALUE _)
{
- ID fname = rb_frame_callee();
+ ID fname = prev_frame_callee(); /* need *callee* ID */
if (fname) {
- return ID2SYM(fname);
+ return ID2SYM(fname);
}
else {
- return Qnil;
+ return Qnil;
}
}
/*
* call-seq:
- * __callee__ => symbol
+ * __dir__ -> string
*
- * Returns the name of the current method as Symbol.
- * If called from inside of an aliased method it will return the aliased
- * name.
- * If called outside of a method, it returns <code>nil</code>.
- * See also <code>\_\_method__</code>.
+ * Returns the canonicalized absolute path of the directory of the file from
+ * which this method is called. It means symlinks in the path is resolved.
+ * If <code>__FILE__</code> is <code>nil</code>, it returns <code>nil</code>.
+ * The return value equals to <code>File.dirname(File.realpath(__FILE__))</code>.
*
*/
-
static VALUE
-rb_f_callee_name(void)
+f_current_dirname(VALUE _)
{
- /* xxx need to get callee name */
- ID callee = rb_frame_callee();
-
- if (callee) {
- return ID2SYM(callee);
- }
- else {
- return Qnil;
+ VALUE base = rb_current_realfilepath();
+ if (NIL_P(base)) {
+ return Qnil;
}
+ base = rb_file_dirname(base);
+ return base;
}
-void
-Init_eval(void)
+/*
+ * call-seq:
+ * global_variables -> array
+ *
+ * Returns an array of the names of global variables. This includes
+ * special regexp global variables such as <tt>$~</tt> and <tt>$+</tt>,
+ * but does not include the numbered regexp global variables (<tt>$1</tt>,
+ * <tt>$2</tt>, etc.).
+ *
+ * global_variables.grep /std/ #=> [:$stdin, :$stdout, :$stderr]
+ */
+
+static VALUE
+f_global_variables(VALUE _)
{
- /* TODO: fix position */
- GET_THREAD()->vm->mark_object_ary = rb_ary_new();
+ return rb_f_global_variables();
+}
- init = rb_intern("initialize");
- eqq = rb_intern("===");
- each = rb_intern("each");
+/*
+ * call-seq:
+ * trace_var(symbol, cmd ) -> nil
+ * trace_var(symbol) {|val| block } -> nil
+ *
+ * Controls tracing of assignments to global variables. The parameter
+ * +symbol+ identifies the variable (as either a string name or a
+ * symbol identifier). _cmd_ (which may be a string or a
+ * +Proc+ object) or block is executed whenever the variable
+ * is assigned. The block or +Proc+ object receives the
+ * variable's new value as a parameter. Also see
+ * #untrace_var.
+ *
+ * trace_var :$_, proc {|v| puts "$_ is now '#{v}'" }
+ * $_ = "hello"
+ * $_ = ' there'
+ *
+ * <em>produces:</em>
+ *
+ * $_ is now 'hello'
+ * $_ is now ' there'
+ */
- aref = rb_intern("[]");
- aset = rb_intern("[]=");
- match = rb_intern("=~");
- missing = rb_intern("method_missing");
- added = rb_intern("method_added");
- singleton_added = rb_intern("singleton_method_added");
- removed = rb_intern("method_removed");
- singleton_removed = rb_intern("singleton_method_removed");
- undefined = rb_intern("method_undefined");
- singleton_undefined = rb_intern("singleton_method_undefined");
+static VALUE
+f_trace_var(int c, const VALUE *a, VALUE _)
+{
+ return rb_f_trace_var(c, a);
+}
- object_id = rb_intern("object_id");
- __send__ = rb_intern("__send__");
+/*
+ * call-seq:
+ * untrace_var(symbol [, cmd] ) -> array or nil
+ *
+ * Removes tracing for the specified command on the given global
+ * variable and returns +nil+. If no command is specified,
+ * removes all tracing for that variable and returns an array
+ * containing the commands actually removed.
+ */
+static VALUE
+f_untrace_var(int c, const VALUE *a, VALUE _)
+{
+ return rb_f_untrace_var(c, a);
+}
+
+void
+Init_eval(void)
+{
rb_define_virtual_variable("$@", errat_getter, errat_setter);
rb_define_virtual_variable("$!", errinfo_getter, 0);
- rb_define_global_function("eval", rb_f_eval, -1);
- rb_define_global_function("iterator?", rb_f_block_given_p, 0);
- rb_define_global_function("block_given?", rb_f_block_given_p, 0);
- rb_define_global_function("method_missing", rb_method_missing, -1);
- rb_define_global_function("loop", rb_f_loop, 0);
-
- rb_define_method(rb_mKernel, "respond_to?", obj_respond_to, -1);
- respond_to = rb_intern("respond_to?");
- basic_respond_to = rb_method_node(rb_cObject, respond_to);
- rb_register_mark_object((VALUE)basic_respond_to);
+ rb_gvar_ractor_local("$@");
+ rb_gvar_ractor_local("$!");
- rb_define_global_function("raise", rb_f_raise, -1);
- rb_define_global_function("fail", rb_f_raise, -1);
+ rb_define_global_function("raise", f_raise, -1);
+ rb_define_global_function("fail", f_raise, -1);
- rb_define_global_function("caller", rb_f_caller, -1);
-
- rb_define_global_function("global_variables", rb_f_global_variables, 0); /* in variable.c */
- rb_define_global_function("local_variables", rb_f_local_variables, 0);
+ rb_define_global_function("global_variables", f_global_variables, 0);
rb_define_global_function("__method__", rb_f_method_name, 0);
rb_define_global_function("__callee__", rb_f_callee_name, 0);
+ rb_define_global_function("__dir__", f_current_dirname, 0);
- rb_define_method(rb_cBasicObject, "__send__", rb_f_send, -1);
- rb_define_method(rb_mKernel, "send", rb_f_send, -1);
- rb_define_method(rb_mKernel, "invoke_method", rb_invoke_method, -1);
-
- rb_define_method(rb_mKernel, "instance_eval", rb_obj_instance_eval, -1);
- rb_define_method(rb_mKernel, "instance_exec", rb_obj_instance_exec, -1);
+ rb_define_method(rb_cModule, "include", rb_mod_include, -1);
+ rb_define_method(rb_cModule, "prepend", rb_mod_prepend, -1);
rb_define_private_method(rb_cModule, "append_features", rb_mod_append_features, 1);
rb_define_private_method(rb_cModule, "extend_object", rb_mod_extend_object, 1);
- rb_define_private_method(rb_cModule, "include", rb_mod_include, -1);
- rb_define_private_method(rb_cModule, "public", rb_mod_public, -1);
- rb_define_private_method(rb_cModule, "protected", rb_mod_protected, -1);
- rb_define_private_method(rb_cModule, "private", rb_mod_private, -1);
- rb_define_private_method(rb_cModule, "module_function", rb_mod_modfunc, -1);
- rb_define_method(rb_cModule, "method_defined?", rb_mod_method_defined, 1);
- rb_define_method(rb_cModule, "public_method_defined?", rb_mod_public_method_defined, 1);
- rb_define_method(rb_cModule, "private_method_defined?", rb_mod_private_method_defined, 1);
- rb_define_method(rb_cModule, "protected_method_defined?", rb_mod_protected_method_defined, 1);
- rb_define_method(rb_cModule, "public_class_method", rb_mod_public_method, -1);
- rb_define_method(rb_cModule, "private_class_method", rb_mod_private_method, -1);
- rb_define_method(rb_cModule, "module_eval", rb_mod_module_eval, -1);
- rb_define_method(rb_cModule, "class_eval", rb_mod_module_eval, -1);
- rb_define_method(rb_cModule, "module_exec", rb_mod_module_exec, -1);
- rb_define_method(rb_cModule, "class_exec", rb_mod_module_exec, -1);
+ rb_define_private_method(rb_cModule, "prepend_features", rb_mod_prepend_features, 1);
+ rb_define_private_method(rb_cModule, "refine", rb_mod_refine, 1);
+ rb_define_private_method(rb_cModule, "using", mod_using, 1);
+ rb_define_method(rb_cModule, "refinements", mod_refinements, 0);
+ rb_define_singleton_method(rb_cModule, "used_modules",
+ rb_mod_s_used_modules, 0);
+ rb_define_singleton_method(rb_cModule, "used_refinements",
+ rb_mod_s_used_refinements, 0);
+ rb_undef_method(rb_cClass, "refine");
+ rb_define_private_method(rb_cRefinement, "import_methods", refinement_import_methods, -1);
+ rb_define_method(rb_cRefinement, "target", rb_refinement_module_get_refined_class, 0);
+ rb_undef_method(rb_cRefinement, "append_features");
+ rb_undef_method(rb_cRefinement, "prepend_features");
+ rb_undef_method(rb_cRefinement, "extend_object");
rb_undef_method(rb_cClass, "module_function");
- rb_define_private_method(rb_cModule, "remove_method", rb_mod_remove_method, -1);
- rb_define_private_method(rb_cModule, "undef_method", rb_mod_undef_method, -1);
- rb_define_private_method(rb_cModule, "alias_method", rb_mod_alias_method, 2);
+ Init_vm_eval();
+ Init_eval_method();
rb_define_singleton_method(rb_cModule, "nesting", rb_mod_nesting, 0);
rb_define_singleton_method(rb_cModule, "constants", rb_mod_s_constants, -1);
- rb_define_singleton_method(rb_vm_top_self(), "include", top_include, -1);
- rb_define_singleton_method(rb_vm_top_self(), "public", top_public, -1);
- rb_define_singleton_method(rb_vm_top_self(), "private", top_private, -1);
+ rb_define_private_method(rb_singleton_class(rb_vm_top_self()),
+ "include", top_include, -1);
+ rb_define_private_method(rb_singleton_class(rb_vm_top_self()),
+ "using", top_using, 1);
rb_define_method(rb_mKernel, "extend", rb_obj_extend, -1);
- rb_define_global_function("trace_var", rb_f_trace_var, -1); /* in variable.c */
- rb_define_global_function("untrace_var", rb_f_untrace_var, -1); /* in variable.c */
+ rb_define_global_function("trace_var", f_trace_var, -1);
+ rb_define_global_function("untrace_var", f_untrace_var, -1);
- rb_define_virtual_variable("$SAFE", safe_getter, safe_setter);
+ rb_vm_register_special_exception(ruby_error_reenter, rb_eFatal, "exception reentered");
+ rb_vm_register_special_exception(ruby_error_stackfatal, rb_eFatal, "machine stack overflow in critical region");
- exception_error = rb_exc_new2(rb_eFatal, "exception reentered");
- rb_ivar_set(exception_error, idThrowState, INT2FIX(TAG_FATAL));
- rb_register_mark_object(exception_error);
+ id_signo = rb_intern_const("signo");
+ id_status = rb_intern_const("status");
}
-
-/* for parser */
-
int
-rb_dvar_defined(ID id)
+rb_errno(void)
{
- rb_thread_t *th = GET_THREAD();
- rb_iseq_t *iseq;
- if (th->base_block && (iseq = th->base_block->iseq)) {
- while (iseq->type == ISEQ_TYPE_BLOCK ||
- iseq->type == ISEQ_TYPE_RESCUE ||
- iseq->type == ISEQ_TYPE_ENSURE ||
- iseq->type == ISEQ_TYPE_EVAL) {
- int i;
-
- for (i = 0; i < iseq->local_table_size; i++) {
- if (iseq->local_table[i] == id) {
- return 1;
- }
- }
- iseq = iseq->parent_iseq;
- }
- }
- return 0;
+ return *rb_orig_errno_ptr();
}
-int
-rb_local_defined(ID id)
+void
+rb_errno_set(int e)
{
- rb_thread_t *th = GET_THREAD();
- rb_iseq_t *iseq;
-
- if (th->base_block && th->base_block->iseq) {
- int i;
- iseq = th->base_block->iseq->local_iseq;
-
- for (i=0; i<iseq->local_table_size; i++) {
- if (iseq->local_table[i] == id) {
- return 1;
- }
- }
- }
- return 0;
+ *rb_orig_errno_ptr() = e;
}
-int
-rb_parse_in_eval(void)
+int *
+rb_errno_ptr(void)
{
- return GET_THREAD()->parse_in_eval != 0;
+ return rb_orig_errno_ptr();
}