summaryrefslogtreecommitdiff
path: root/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'eval.c')
-rw-r--r--eval.c285
1 files changed, 166 insertions, 119 deletions
diff --git a/eval.c b/eval.c
index cf32a82214..e8da342ac6 100644
--- a/eval.c
+++ b/eval.c
@@ -18,11 +18,12 @@
#endif
#include "eval_intern.h"
-#include "gc.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"
@@ -31,7 +32,7 @@
#include "internal/variable.h"
#include "ruby/fiber/scheduler.h"
#include "iseq.h"
-#include "mjit.h"
+#include "rjit.h"
#include "probes.h"
#include "probes_helper.h"
#include "ruby/vm.h"
@@ -42,7 +43,7 @@ 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, int ex);
+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;
@@ -69,8 +70,6 @@ ruby_setup(void)
if (GET_VM())
return 0;
- ruby_init_stack((void *)&state);
-
/*
* Disable THP early before mallocs happen because we want this to
* affect as many future pages as possible for CoW-friendliness
@@ -79,7 +78,6 @@ ruby_setup(void)
prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0);
#endif
Init_BareVM();
- Init_heap();
rb_vm_encoded_insn_data_table_init();
Init_vm_objects();
@@ -99,8 +97,10 @@ ruby_init(void)
{
int state = ruby_setup();
if (state) {
- if (RTEST(ruby_debug))
- error_print(GET_EC());
+ if (RTEST(ruby_debug)) {
+ rb_execution_context_t *ec = GET_EC();
+ rb_ec_error_print(ec, ec->errinfo);
+ }
exit(EXIT_FAILURE);
}
}
@@ -112,15 +112,15 @@ ruby_options(int argc, char **argv)
enum ruby_tag_type state;
void *volatile iseq = 0;
- ruby_init_stack((void *)&iseq);
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- SAVE_ROOT_JMPBUF(GET_THREAD(), iseq = ruby_process_options(argc, argv));
+ iseq = ruby_process_options(argc, argv);
}
else {
rb_ec_clear_current_thread_trace_func(ec);
- state = error_handle(ec, state);
- iseq = (void *)INT2FIX(state);
+ int exitcode = error_handle(ec, ec->errinfo, state);
+ ec->errinfo = Qnil; /* just been handled */
+ iseq = (void *)INT2FIX(exitcode);
}
EC_POP_TAG();
return iseq;
@@ -136,7 +136,7 @@ rb_ec_fiber_scheduler_finalize(rb_execution_context_t *ec)
rb_fiber_scheduler_set(Qnil);
}
else {
- state = error_handle(ec, state);
+ state = error_handle(ec, ec->errinfo, state);
}
EC_POP_TAG();
}
@@ -175,83 +175,82 @@ ruby_finalize(void)
int
ruby_cleanup(int ex)
{
- return rb_ec_cleanup(GET_EC(), ex);
+ return rb_ec_cleanup(GET_EC(), (enum ruby_tag_type)ex);
}
static int
-rb_ec_cleanup(rb_execution_context_t *ec, int ex0)
+rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex)
{
int state;
- volatile VALUE errs[2] = { Qundef, Qundef };
- int nerr;
+ 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 sysex = EXIT_SUCCESS;
volatile int step = 0;
- volatile int ex = ex0;
+ 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) {
- SAVE_ROOT_JMPBUF(th, { RUBY_VM_CHECK_INTS(ec); });
+ RUBY_VM_CHECK_INTS(ec);
step_0: step++;
- errs[1] = ec->errinfo;
+ save_error = ec->errinfo;
if (THROW_DATA_P(ec->errinfo)) ec->errinfo = Qnil;
- ruby_init_stack(&errs[STACK_UPPER(errs, 0, 1)]);
- SAVE_ROOT_JMPBUF(th, rb_ec_teardown(ec));
+ /* 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;
- errs[0] = ec->errinfo;
- SAVE_ROOT_JMPBUF(th, rb_ractor_terminate_all());
+ 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;
}
- if (ex == 0) ex = state;
}
- ec->errinfo = errs[1];
- sysex = error_handle(ec, ex);
-
- state = 0;
- for (nerr = 0; nerr < numberof(errs); ++nerr) {
- VALUE err = ATOMIC_VALUE_EXCHANGE(errs[nerr], Qnil);
- VALUE sig;
-
- if (!RTEST(err)) continue;
-
- /* ec->errinfo contains a NODE while break'ing */
- if (THROW_DATA_P(err)) continue;
-
- if (rb_obj_is_kind_of(err, rb_eSystemExit)) {
- sysex = sysexit_status(err);
- break;
- }
- else if (rb_obj_is_kind_of(err, rb_eSignal)) {
- VALUE sig = rb_ivar_get(err, id_signo);
- state = NUM2INT(sig);
- break;
- }
- else if (rb_obj_is_kind_of(err, rb_eSystemCallError) &&
- FIXNUM_P(sig = rb_attr_get(err, id_signo))) {
- state = NUM2INT(sig);
- break;
- }
- else if (sysex == EXIT_SUCCESS) {
- sysex = EXIT_FAILURE;
- }
- }
-
- mjit_finish(true); // We still need ISeqs here.
rb_ec_finalize(ec);
@@ -262,7 +261,10 @@ rb_ec_cleanup(rb_execution_context_t *ec, int ex0)
th = th0;
rb_thread_stop_timer_thread();
ruby_vm_destruct(th->vm);
- if (state) ruby_default_signal(state);
+ // For YJIT, call this after ruby_vm_destruct() frees jit_cont for the root fiber.
+ rb_jit_cont_finish();
+
+ if (signaled) ruby_default_signal(signaled);
return sysex;
}
@@ -276,10 +278,7 @@ rb_ec_exec_node(rb_execution_context_t *ec, void *n)
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- rb_thread_t *const th = rb_ec_thread_ptr(ec);
- SAVE_ROOT_JMPBUF(th, {
- rb_iseq_eval_main(iseq);
- });
+ rb_iseq_eval_main(iseq);
}
EC_POP_TAG();
return state;
@@ -314,17 +313,15 @@ ruby_run_node(void *n)
rb_execution_context_t *ec = GET_EC();
int status;
if (!ruby_executable_node(n, &status)) {
- rb_ec_cleanup(ec, 0);
+ rb_ec_cleanup(ec, (NIL_P(ec->errinfo) ? TAG_NONE : TAG_RAISE));
return status;
}
- ruby_init_stack((void *)&status);
return rb_ec_cleanup(ec, rb_ec_exec_node(ec, n));
}
int
ruby_exec_node(void *n)
{
- ruby_init_stack((void *)&n);
return rb_ec_exec_node(GET_EC(), n);
}
@@ -412,11 +409,11 @@ rb_mod_s_constants(int argc, VALUE *argv, VALUE mod)
return rb_const_list(data);
}
-/*!
- * Asserts that \a klass is not a frozen class.
- * \param[in] klass a \c Module object
- * \exception RuntimeError if \a klass is not a class or frozen.
- * \ingroup class
+/**
+ * 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_class_modify_check(VALUE klass)
@@ -430,9 +427,9 @@ rb_class_modify_check(VALUE klass)
if (OBJ_FROZEN(klass)) {
const char *desc;
- if (FL_TEST(klass, FL_SINGLETON)) {
+ if (RCLASS_SINGLETON_P(klass)) {
desc = "object";
- klass = rb_ivar_get(klass, id__attached__);
+ klass = RCLASS_ATTACHED_OBJECT(klass);
if (!SPECIAL_CONST_P(klass)) {
switch (BUILTIN_TYPE(klass)) {
case T_MODULE:
@@ -465,7 +462,7 @@ rb_class_modify_check(VALUE klass)
}
}
-NORETURN(static void rb_longjmp(rb_execution_context_t *, int, volatile VALUE, VALUE));
+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)
@@ -511,7 +508,7 @@ exc_setup_message(const rb_execution_context_t *ec, VALUE mesg, VALUE *cause)
nocause = 0;
nocircular = 1;
}
- if (*cause == Qundef) {
+ if (UNDEF_P(*cause)) {
if (nocause) {
*cause = Qnil;
nocircular = 1;
@@ -527,37 +524,41 @@ exc_setup_message(const rb_execution_context_t *ec, VALUE mesg, VALUE *cause)
rb_raise(rb_eTypeError, "exception object expected");
}
- if (!nocircular && !NIL_P(*cause) && *cause != Qundef && *cause != mesg) {
+ 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 = rb_attr_get(c, id_cause))) {
if (c == mesg) {
rb_raise(rb_eArgError, "circular causes");
}
}
+#endif
}
return mesg;
}
static void
-setup_exception(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE cause)
+setup_exception(rb_execution_context_t *ec, enum ruby_tag_type tag, volatile VALUE mesg, VALUE cause)
{
VALUE e;
int line;
const char *file = rb_source_location_cstr(&line);
const char *const volatile file0 = file;
- if ((file && !NIL_P(mesg)) || (cause != Qundef)) {
+ 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) || cause == Qundef) {
+ if (!NIL_P(bt) || UNDEF_P(cause)) {
if (OBJ_FROZEN(mesg)) {
mesg = rb_obj_dup(mesg);
}
}
- if (cause != Qundef && !THROW_DATA_P(cause)) {
+ if (!UNDEF_P(cause) && !THROW_DATA_P(cause)) {
exc_setup_cause(mesg, cause);
}
if (NIL_P(bt)) {
@@ -587,15 +588,15 @@ setup_exception(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE
e = rb_obj_as_string(mesg);
ec->errinfo = mesg;
if (file && line) {
- e = rb_sprintf("Exception `%"PRIsVALUE"' at %s:%d - %"PRIsVALUE"\n",
+ 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",
+ e = rb_sprintf("Exception '%"PRIsVALUE"' at %s - %"PRIsVALUE"\n",
rb_obj_class(mesg), file, e);
}
else {
- e = rb_sprintf("Exception `%"PRIsVALUE"' - %"PRIsVALUE"\n",
+ e = rb_sprintf("Exception '%"PRIsVALUE"' - %"PRIsVALUE"\n",
rb_obj_class(mesg), e);
}
warn_print_str(e);
@@ -630,16 +631,20 @@ setup_exception(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE
void
rb_ec_setup_exception(const rb_execution_context_t *ec, VALUE mesg, VALUE cause)
{
- if (cause == Qundef) {
+ if (UNDEF_P(cause)) {
cause = get_ec_errinfo(ec);
}
if (cause != mesg) {
+ if (THROW_DATA_P(cause)) {
+ cause = Qnil;
+ }
+
rb_ivar_set(mesg, id_cause, cause);
}
}
static void
-rb_longjmp(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE cause)
+rb_longjmp(rb_execution_context_t *ec, enum ruby_tag_type tag, volatile VALUE mesg, VALUE cause)
{
mesg = exc_setup_message(ec, mesg, &cause);
setup_exception(ec, tag, mesg, cause);
@@ -649,10 +654,10 @@ rb_longjmp(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE cause
static VALUE make_exception(int argc, const VALUE *argv, int isstr);
-NORETURN(static void rb_exc_exception(VALUE mesg, int tag, VALUE cause));
+NORETURN(static void rb_exc_exception(VALUE mesg, enum ruby_tag_type tag, VALUE cause));
static void
-rb_exc_exception(VALUE mesg, int tag, VALUE cause)
+rb_exc_exception(VALUE mesg, enum ruby_tag_type tag, VALUE cause)
{
if (!NIL_P(mesg)) {
mesg = make_exception(1, &mesg, FALSE);
@@ -660,12 +665,12 @@ rb_exc_exception(VALUE mesg, int tag, VALUE cause)
rb_longjmp(GET_EC(), tag, mesg, cause);
}
-/*!
+/**
* Raises an exception in the current thread.
- * \param[in] mesg an Exception class or an \c Exception object.
- * \exception always raises an instance of the given exception class or
- * the given \c Exception object.
- * \ingroup exception
+ * @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(VALUE mesg)
@@ -725,7 +730,7 @@ rb_f_raise(int argc, VALUE *argv)
argc = extract_raise_opts(argc, argv, opts);
if (argc == 0) {
- if (*cause != Qundef) {
+ if (!UNDEF_P(*cause)) {
rb_raise(rb_eArgError, "only cause is given with no arguments");
}
err = get_errinfo();
@@ -755,7 +760,8 @@ rb_f_raise(int argc, VALUE *argv)
* object that returns an +Exception+ object when sent an +exception+
* message). The optional second parameter sets the message associated with
* the exception (accessible via Exception#message), and the third parameter
- * is an array of callback information (accessible via Exception#backtrace).
+ * is an array of callback information (accessible via
+ * Exception#backtrace_locations or Exception#backtrace).
* The +cause+ of the generated exception (accessible via Exception#cause)
* is automatically set to the "current" exception (<code>$!</code>), if any.
* An alternative value, either an +Exception+ object or +nil+, can be
@@ -765,7 +771,7 @@ rb_f_raise(int argc, VALUE *argv)
* <code>begin...end</code> blocks.
*
* raise "Failed to create socket"
- * raise ArgumentError, "No parameters", caller
+ * raise ArgumentError, "No parameters", caller_locations
*/
static VALUE
@@ -801,7 +807,7 @@ make_exception(int argc, const VALUE *argv, int isstr)
if (NIL_P(mesg)) {
mesg = rb_check_funcall(argv[0], idException, argc != 1, &argv[1]);
}
- if (mesg == Qundef) {
+ if (UNDEF_P(mesg)) {
rb_raise(rb_eTypeError, "exception class/object expected");
}
if (!rb_obj_is_kind_of(mesg, rb_eException)) {
@@ -965,7 +971,7 @@ rb_protect(VALUE (* proc) (VALUE), VALUE data, int *pstate)
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- SAVE_ROOT_JMPBUF(rb_ec_thread_ptr(ec), result = (*proc) (data));
+ result = (*proc)(data);
}
else {
rb_vm_rewind_cfp(ec, cfp);
@@ -979,7 +985,7 @@ rb_protect(VALUE (* proc) (VALUE), VALUE data, int *pstate)
VALUE
rb_ensure(VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*e_proc)(VALUE), VALUE data2)
{
- int state;
+ enum ruby_tag_type state;
volatile VALUE result = Qnil;
VALUE errinfo;
rb_execution_context_t * volatile ec = GET_EC();
@@ -1270,12 +1276,6 @@ rb_using_refinement(rb_cref_t *cref, VALUE klass, VALUE module)
RCLASS_M_TBL(c) = RCLASS_M_TBL(module);
- module = RCLASS_SUPER(module);
- while (module && module != klass) {
- c = RCLASS_SET_SUPER(c, rb_include_class_new(module, RCLASS_SUPER(c)));
- RB_OBJ_WRITE(c, &RCLASS_REFINED_CLASS(c), klass);
- module = RCLASS_SUPER(module);
- }
rb_hash_aset(CREF_REFINEMENTS(cref), klass, iclass);
}
@@ -1326,14 +1326,21 @@ rb_using_module(const rb_cref_t *cref, VALUE module)
{
Check_Type(module, T_MODULE);
using_module_recursive(cref, module);
- rb_clear_method_cache_all();
+ rb_clear_all_refinement_method_cache();
}
/*
* call-seq:
- * refined_class -> class
+ * target -> class_or_module
*
- * Return the class refined by the receiver.
+ * Return the class or module refined by the receiver.
+ *
+ * module M
+ * refine String do
+ * end
+ * end
+ *
+ * M.refinements[0].target # => String
*/
VALUE
rb_refinement_module_get_refined_class(VALUE module)
@@ -1344,6 +1351,21 @@ rb_refinement_module_get_refined_class(VALUE module)
return rb_attr_get(module, id_refined_class);
}
+/*
+ * call-seq:
+ * refined_class -> class
+ *
+ * Deprecated; prefer #target.
+ *
+ * Return the class refined by the receiver.
+ */
+static VALUE
+rb_refinement_refined_class(VALUE module)
+{
+ rb_warn_deprecated_to_remove("3.4", "Refinement#refined_class", "Refinement#target");
+ return rb_refinement_module_get_refined_class(module);
+}
+
static void
add_activated_refinement(VALUE activated_refinements,
VALUE klass, VALUE refinement)
@@ -1760,6 +1782,16 @@ rb_obj_extend(int argc, VALUE *argv, VALUE 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
@@ -1772,13 +1804,7 @@ rb_obj_extend(int argc, VALUE *argv, VALUE obj)
static VALUE
top_include(int argc, VALUE *argv, VALUE self)
{
- rb_thread_t *th = GET_THREAD();
-
- 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);
- }
- return rb_mod_include(argc, argv, rb_cObject);
+ return rb_mod_include(argc, argv, rb_top_main_class("include"));
}
/*
@@ -1792,10 +1818,12 @@ top_include(int argc, VALUE *argv, VALUE self)
static VALUE
top_using(VALUE self, VALUE module)
{
- const rb_cref_t *cref = rb_vm_cref();
+ 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();
- if (CREF_NEXT(cref) || (prev_cfp && rb_vm_frame_method_entry(prev_cfp))) {
+ 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");
}
if (rb_block_given_p()) {
@@ -1983,7 +2011,7 @@ f_global_variables(VALUE _)
* +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
- * Kernel::untrace_var.
+ * #untrace_var.
*
* trace_var :$_, proc {|v| puts "$_ is now '#{v}'" }
* $_ = "hello"
@@ -2050,7 +2078,8 @@ Init_eval(void)
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, "refined_class", rb_refinement_module_get_refined_class, 0);
+ rb_define_method(rb_cRefinement, "target", rb_refinement_module_get_refined_class, 0);
+ rb_define_method(rb_cRefinement, "refined_class", rb_refinement_refined_class, 0);
rb_undef_method(rb_cRefinement, "append_features");
rb_undef_method(rb_cRefinement, "prepend_features");
rb_undef_method(rb_cRefinement, "extend_object");
@@ -2079,3 +2108,21 @@ Init_eval(void)
id_signo = rb_intern_const("signo");
id_status = rb_intern_const("status");
}
+
+int
+rb_errno(void)
+{
+ return *rb_orig_errno_ptr();
+}
+
+void
+rb_errno_set(int e)
+{
+ *rb_orig_errno_ptr() = e;
+}
+
+int *
+rb_errno_ptr(void)
+{
+ return rb_orig_errno_ptr();
+}