diff options
Diffstat (limited to 'eval.c')
-rw-r--r-- | eval.c | 285 |
1 files changed, 166 insertions, 119 deletions
@@ -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(); +} |