# HG changeset patch # User John W. Eaton # Date 1559654125 0 # Node ID 7883e6e71b0aed76814d4e1ac20245dbd6af7087 # Parent 6b0c61a5a0f0c4406d32d64e5951db63c1396475 additional refactoring of error handling functions * error.h, error.cc. Move most functions inside error_system class. Global non-member functions now get interpreter error_system object and call class member fuctions through that object. * graphics.cc (base_graphics_object::remove_all_listeners): Use interpreter_try instead of directly saving and restoring error systsem state here. * interpreter.cc (interpreter::interpreter): Don't call initialize_default_warning_state. That is now handled by constructing the error_system object. diff -r 6b0c61a5a0f0 -r 7883e6e71b0a libinterp/corefcn/error.cc --- a/libinterp/corefcn/error.cc Fri May 31 15:49:38 2019 +0000 +++ b/libinterp/corefcn/error.cc Tue Jun 04 13:15:25 2019 +0000 @@ -52,16 +52,330 @@ #include "utils.h" #include "variables.h" -// This variable is obsolete and always has the value 0. -int error_state = 0; - -void -reset_error_handler (void) +static void verror (bool save_last_error, std::ostream& os, const char *name, + const char *id, const char *fmt, va_list args, + bool with_cfn = false) +{ + octave::error_system& es = octave::__get_error_system__ ("verror"); + + es.verror (save_last_error, os, name, id, fmt, args, with_cfn); +} + +static void +vpr_where (std::ostream& os, const char *fmt, va_list args) +{ + if (fmt) + { + if (*fmt) + { + size_t len = strlen (fmt); + + if (len > 0) + { + if (fmt[len - 1] == '\n') + { + if (len > 1) + { + std::string tmp_fmt (fmt, len - 1); + verror (false, os, nullptr, "", tmp_fmt.c_str (), args); + } + } + else + verror (false, os, nullptr, "", fmt, args); + } + } + } + else + panic ("vpr_where: invalid format"); +} + +static void +pr_where_internal (std::ostream& os, const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + vpr_where (os, fmt, args); + va_end (args); +} + +struct +error_stack_frame +{ + std::string name; + int line; + int column; +}; + +static void +pr_where (std::ostream& os, const char *who, + const std::list& frames) +{ + size_t nframes = frames.size (); + + if (nframes > 0) + pr_where_internal (os, "%s: called from\n", who); + + for (const auto& frm : frames) + { + std::string fcn_name = frm.name; + int line = frm.line; + int column = frm.column; + + if (line > 0) + { + if (column > 0) + pr_where_internal (os, " %s at line %d column %d\n", + fcn_name.c_str (), line, column); + else + pr_where_internal (os, " %s at line %d\n", + fcn_name.c_str (), line); + } + else + pr_where_internal (os, " %s\n", fcn_name.c_str ()); + } +} + +static void +pr_where (std::ostream& os, const char *who) +{ + octave::call_stack& cs = octave::__get_call_stack__ ("pr_where"); + + std::list call_stack_frames = cs.backtrace_frames (); + + // Print the error message only if it is different from the previous one; + // Makes the output more concise and readable. + call_stack_frames.unique (); + + std::list frames; + for (const auto *frm : call_stack_frames) + { + error_stack_frame frame; + + frame.name = frm->fcn_name (); + frame.line = frm->line (); + frame.column = frm->column (); + + frames.push_back (frame); + } + + pr_where (os, who, frames); +} + +static void +maybe_enter_debugger (octave::execution_exception& e, + bool show_stack_trace = false) { octave::error_system& es - = octave::__get_error_system__ ("reset_error_handler"); - - es.reset (); + = octave::__get_error_system__ ("maybe_enter_debugger"); + + es.maybe_enter_debugger (e, show_stack_trace); +} + +OCTAVE_NORETURN +static void +vusage (octave::execution_exception& e, const char *id, + const char *fmt, va_list args) +{ + verror (true, std::cerr, "usage", id, fmt, args); + + maybe_enter_debugger (e); + + throw e; +} + +OCTAVE_NORETURN +static void +error_1 (octave::execution_exception& e, std::ostream& os, + const char *name, const char *id, const char *fmt, + va_list args, bool with_cfn = false) +{ + octave::error_system& es = octave::__get_error_system__ ("error_1"); + + es.error_1 (e, os, name, id, fmt, args, with_cfn); +} + +OCTAVE_NORETURN +static void +error_1 (std::ostream& os, const char *name, const char *id, + const char *fmt, va_list args, bool with_cfn = false) +{ + octave::error_system& es = octave::__get_error_system__ ("error_1"); + + es.error_1 (os, name, id, fmt, args, with_cfn); +} + +static int +check_state (const std::string& state) +{ + // -1: not found + // 0: found, "off" + // 1: found, "on" + // 2: found, "error" + + if (state == "off") + return 0; + else if (state == "on") + return 1; + else if (state == "error") + return 2; + else + return -1; +} + +static void +vwarning (const char *id, const char *fmt, va_list args) +{ + octave::error_system& es = octave::__get_error_system__ ("warning"); + + es.vwarning (id, fmt, args); +} + +static std::list +make_stack_frame_list (const octave_map& stack) +{ + std::list frames; + + Cell name = stack.contents ("name"); + Cell line = stack.contents ("line"); + Cell column; + bool have_column = false; + if (stack.contains ("column")) + { + have_column = true; + column = stack.contents ("column"); + } + + octave_idx_type nel = name.numel (); + + for (octave_idx_type i = 0; i < nel; i++) + { + error_stack_frame frame; + + frame.name = name(i).string_value (); + frame.line = line(i).int_value (); + frame.column = (have_column ? column(i).int_value () : -1); + + frames.push_back (frame); + } + + return frames; +} + +static void +defun_usage_message (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + error_1 (octave_stdout, nullptr, "", fmt, args); + va_end (args); +} + +typedef void (*error_fun)(const char *, const char *, ...); + +static std::string +handle_message (error_fun f, const char *id, const char *msg, + const octave_value_list& args, bool have_fmt) +{ + std::string retval; + + std::string tmpstr; + + if (args.length () > 0) + { + octave_value arg; + + if (have_fmt) + { + octave_value_list tmp = Fsprintf (args, 1); + arg = tmp(0); + } + else + arg = args(0); + + if (arg.is_defined ()) + { + if (arg.isempty ()) + return retval; + else if (arg.is_string ()) + { + tmpstr = arg.string_value (); // 2-stage assignment required + msg = tmpstr.c_str (); // in order to generate pointer + // to valid memory. + } + } + } + + // Ugh. + + size_t len = strlen (msg); + + if (len > 0) + { + if (msg[len - 1] == '\n') + { + if (len > 1) + { + std::string tmp_msg (msg, len - 1); + f (id, "%s\n", tmp_msg.c_str ()); + retval = tmp_msg; + } + } + else + { + f (id, "%s", msg); + retval = msg; + } + } + + return retval; +} + +// Determine whether the first argument to error or warning function +// should be handled as the message identifier or as the format string. + +static bool +maybe_extract_message_id (const std::string& caller, + const octave_value_list& args, + octave_value_list& nargs, + std::string& id) +{ + nargs = args; + id = ""; + + int nargin = args.length (); + + bool have_fmt = nargin > 1; + + if (nargin > 0) + { + std::string arg1 = args(0).string_value (); + + // For compatibility with Matlab, an identifier must contain ':', + // but not at the beginning or the end, and it must not contain '%' + // (even if it is not a valid conversion operator) or whitespace. + + if (arg1.find_first_of ("% \f\n\r\t\v") == std::string::npos + && arg1.find (':') != std::string::npos + && arg1[0] != ':' + && arg1.back () != ':') + { + if (nargin > 1) + { + id = arg1; + + nargs.resize (nargin-1); + + for (int i = 1; i < nargin; i++) + nargs(i-1) = args(i); + } + else + nargs(0) = "call to " + caller + + " with message identifier '" + arg1 + + "' requires message"; + } + } + + return have_fmt; } namespace octave @@ -80,7 +394,7 @@ static octave_map init_error_stack (interpreter& interp) { - octave::call_stack& cs = interp.get_call_stack (); + call_stack& cs = interp.get_call_stack (); return cs.empty_backtrace (); } @@ -104,7 +418,9 @@ m_last_warning_id (), m_last_error_id (), m_last_error_stack (init_error_stack (interp)) - { } + { + initialize_default_warning_state (); + } octave_value error_system::debug_on_error (const octave_value_list& args, int nargout) @@ -215,193 +531,641 @@ return set_internal_variable (m_last_error_id, args, nargout, "last_error_id"); } -} - -static void -verror (bool save_last_error, std::ostream& os, - const char *name, const char *id, const char *fmt, va_list args, - bool with_cfn = false) -{ - octave::interpreter& interp = octave::__get_interpreter__ ("verror"); - - octave::error_system& es = interp.get_error_system (); - - if (es.discard_error_messages () && ! es.debug_on_caught ()) - return; - - if (! es.buffer_error_messages () || es.debug_on_caught ()) - octave::flush_stdout (); - - // FIXME: we really want to capture the message before it has all the - // formatting goop attached to it. We probably also want just the - // message, not the traceback information. - - std::ostringstream output_buf; - - octave::vformat (output_buf, fmt, args); - - std::string base_msg = output_buf.str (); - - bool to_beep_or_not_to_beep_p = es.beep_on_error (); - - std::string msg_string; - - if (to_beep_or_not_to_beep_p) - msg_string = "\a"; - - if (name) - { - if (es.in_try_catch () && ! strcmp (name, "error")) - msg_string += "caught error: "; - else - msg_string += std::string (name) + ": "; - } - - octave::call_stack& cs = interp.get_call_stack (); - - // If with_fcn is specified, we'll attempt to prefix the message with the name - // of the current executing function. But we'll do so only if: - // 1. the name is not empty (anonymous function) - // 2. it is not already there (including the following colon) - if (with_cfn) - { - octave_function *curfcn = cs.current (); - if (curfcn) - { - std::string cfn = curfcn->name (); - if (! cfn.empty ()) - { - cfn += ':'; - if (cfn.length () > base_msg.length () - || base_msg.compare (0, cfn.length (), cfn) != 0) - { - msg_string += cfn + ' '; - } - } - } - } - - msg_string += base_msg + '\n'; - - if (save_last_error) - { - // This is the first error in a possible series. - - es.last_error_id (id); - es.last_error_message (base_msg); - - octave_user_code *fcn = cs.current_user_code (); - - if (fcn) - es.last_error_stack (cs.backtrace ()); - else - es.last_error_stack (octave::init_error_stack (interp)); - } - - if (! es.buffer_error_messages () || es.debug_on_caught ()) - { - octave_diary << msg_string; - os << msg_string; - } -} - -static void -pr_where_2 (std::ostream& os, const char *fmt, va_list args) -{ - if (fmt) - { - if (*fmt) - { - size_t len = strlen (fmt); - - if (len > 0) - { - if (fmt[len - 1] == '\n') - { - if (len > 1) - { - std::string tmp_fmt (fmt, len - 1); - verror (false, os, nullptr, "", tmp_fmt.c_str (), args); - } - } - else - verror (false, os, nullptr, "", fmt, args); - } - } - } - else - panic ("pr_where_2: invalid format"); -} - -static void -pr_where_1 (std::ostream& os, const char *fmt, ...) -{ - va_list args; - va_start (args, fmt); - pr_where_2 (os, fmt, args); - va_end (args); -} - -struct -error_stack_frame -{ - std::string name; - int line; - int column; -}; - -static void -pr_where (std::ostream& os, const char *who, - const std::list& frames) -{ - size_t nframes = frames.size (); - - if (nframes > 0) - pr_where_1 (os, "%s: called from\n", who); - - for (const auto& frm : frames) - { - std::string fcn_name = frm.name; - int line = frm.line; - int column = frm.column; - - if (line > 0) - { - if (column > 0) - pr_where_1 (os, " %s at line %d column %d\n", - fcn_name.c_str (), line, column); - else - pr_where_1 (os, " %s at line %d\n", fcn_name.c_str (), line); - } - else - pr_where_1 (os, " %s\n", fcn_name.c_str ()); - } -} - -static void -pr_where (std::ostream& os, const char *who) -{ - octave::call_stack& cs = octave::__get_call_stack__ ("pr_where"); - - std::list call_stack_frames = cs.backtrace_frames (); - - // Print the error message only if it is different from the previous one; - // Makes the output more concise and readable. - call_stack_frames.unique (); - - std::list frames; - for (const auto *frm : call_stack_frames) - { - error_stack_frame frame; - - frame.name = frm->fcn_name (); - frame.line = frm->line (); - frame.column = frm->column (); - - frames.push_back (frame); - } - - pr_where (os, who, frames); + + // For given warning ID, return 0 if warnings are disabled, 1 if + // enabled, and 2 if the given ID should be an error instead of a + // warning. + + int error_system::warning_enabled (const std::string& id) + { + int retval = 0; + + int all_state = -1; + int id_state = -1; + + octave_map opts = warning_options (); + + octave_idx_type nel = opts.numel (); + + if (nel > 0) + { + Cell identifier = opts.contents ("identifier"); + Cell state = opts.contents ("state"); + + bool all_found = false; + bool id_found = false; + + for (octave_idx_type i = 0; i < nel; i++) + { + octave_value ov = identifier(i); + std::string ovs = ov.string_value (); + + if (! all_found && ovs == "all") + { + all_state = check_state (state(i).string_value ()); + + if (all_state >= 0) + all_found = true; + } + + if (! id_found && ovs == id) + { + id_state = check_state (state(i).string_value ()); + + if (id_state >= 0) + id_found = true; + } + + if (all_found && id_found) + break; + } + } + + // If "all" is not present, assume warnings are enabled. + if (all_state == -1) + all_state = 1; + + if (all_state == 0) + { + if (id_state >= 0) + retval = id_state; + } + else if (all_state == 1) + { + if (id_state == 0 || id_state == 2) + retval = id_state; + else + retval = all_state; + } + else if (all_state == 2) + { + if (id_state == 0) + retval= id_state; + else + retval = all_state; + } + + return retval; + } + + void error_system::verror (bool save_last_error, std::ostream& os, + const char *name, const char *id, + const char *fmt, va_list args, bool with_cfn) + { + if (discard_error_messages () && ! debug_on_caught ()) + return; + + if (! buffer_error_messages () || debug_on_caught ()) + flush_stdout (); + + // FIXME: we really want to capture the message before it has all the + // formatting goop attached to it. We probably also want just the + // message, not the traceback information. + + std::ostringstream output_buf; + + vformat (output_buf, fmt, args); + + std::string base_msg = output_buf.str (); + + bool to_beep_or_not_to_beep_p = beep_on_error (); + + std::string msg_string; + + if (to_beep_or_not_to_beep_p) + msg_string = "\a"; + + if (name) + { + if (in_try_catch () && ! strcmp (name, "error")) + msg_string += "caught error: "; + else + msg_string += std::string (name) + ": "; + } + + call_stack& cs = m_interpreter.get_call_stack (); + + // If with_fcn is specified, we'll attempt to prefix the message with the name + // of the current executing function. But we'll do so only if: + // 1. the name is not empty (anonymous function) + // 2. it is not already there (including the following colon) + if (with_cfn) + { + octave_function *curfcn = cs.current (); + if (curfcn) + { + std::string cfn = curfcn->name (); + if (! cfn.empty ()) + { + cfn += ':'; + if (cfn.length () > base_msg.length () + || base_msg.compare (0, cfn.length (), cfn) != 0) + { + msg_string += cfn + ' '; + } + } + } + } + + msg_string += base_msg + '\n'; + + if (save_last_error) + { + // This is the first error in a possible series. + + last_error_id (id); + last_error_message (base_msg); + + octave_user_code *fcn = cs.current_user_code (); + + if (fcn) + last_error_stack (cs.backtrace ()); + else + last_error_stack (init_error_stack (m_interpreter)); + } + + if (! buffer_error_messages () || debug_on_caught ()) + { + octave_diary << msg_string; + os << msg_string; + } + } + + void error_system::maybe_enter_debugger (execution_exception& e, + bool show_stack_trace) + { + call_stack& cs = m_interpreter.get_call_stack (); + tree_evaluator& tw = m_interpreter.get_evaluator (); + bp_table& bptab = tw.get_bp_table (); + + if ((application::interactive () + || application::forced_interactive ()) + && ((debug_on_error () + && bptab.debug_on_err (last_error_id ())) + || (debug_on_caught () + && bptab.debug_on_caught (last_error_id ()))) + && cs.current_user_code ()) + { + unwind_protect frame; + + frame.protect_var (m_debug_on_error); + m_debug_on_error = false; + + if (show_stack_trace) + { + std::string stack_trace = e.info (); + + if (! stack_trace.empty ()) + { + std::cerr << stack_trace; + + e.set_stack_trace (); + } + } + + tw.enter_debugger (); + } + } + + void error_system::vwarning (const char *name, const char *id, + const char *fmt, va_list args) + { + if (discard_warning_messages ()) + return; + + flush_stdout (); + + std::ostringstream output_buf; + + vformat (output_buf, fmt, args); + + // FIXME: we really want to capture the message before it has all the + // formatting goop attached to it. We probably also want just the + // message, not the traceback information. + + std::string base_msg = output_buf.str (); + std::string msg_string; + + if (name) + msg_string = std::string (name) + ": "; + + msg_string += base_msg + '\n'; + + last_warning_id (id); + last_warning_message (base_msg); + + if (! quiet_warning ()) + { + octave_diary << msg_string; + + std::cerr << msg_string; + } + } + + void error_system::error_1 (execution_exception& e, std::ostream& os, + const char *name, const char *id, + const char *fmt, va_list args, bool with_cfn) + { + bool show_stack_trace = false; + + if (fmt) + { + if (*fmt) + { + size_t len = strlen (fmt); + + if (len > 0) + { + if (fmt[len - 1] == '\n') + { + if (len > 1) + { + std::string tmp_fmt (fmt, len - 1); + verror (true, os, name, id, tmp_fmt.c_str (), + args, with_cfn); + } + + // If format ends with newline, suppress stack trace. + e.set_stack_trace (); + } + else + { + verror (true, os, name, id, fmt, args, with_cfn); + + call_stack& cs = m_interpreter.get_call_stack (); + + bool in_user_code = cs.current_user_code () != nullptr; + + if (in_user_code && ! discard_error_messages ()) + show_stack_trace = true; + } + } + } + } + else + panic ("error_1: invalid format"); + + maybe_enter_debugger (e, show_stack_trace); + + throw e; + } + + void error_system::error_1 (std::ostream& os, const char *name, + const char *id, const char *fmt, + va_list args, bool with_cfn = false) + { + execution_exception e = make_execution_exception ("error"); + + error_1 (e, os, name, id, fmt, args, with_cfn); + } + + void error_system::vwarning (const char *id, const char *fmt, va_list args) + { + int warn_opt = warning_enabled (id); + + if (warn_opt == 2) + { + // Handle this warning as an error. + + error_1 (std::cerr, "error", id, fmt, args); + } + else if (warn_opt == 1) + { + bool fmt_suppresses_backtrace = false; + size_t fmt_len = (fmt ? strlen (fmt) : 0); + fmt_suppresses_backtrace = (fmt_len > 0 && fmt[fmt_len-1] == '\n'); + + if (fmt_suppresses_backtrace && fmt_len > 1) + { + // Strip newline before issuing warning + std::string tmp_fmt (fmt, fmt_len - 1); + vwarning ("warning", id, tmp_fmt.c_str (), args); + } + else + vwarning ("warning", id, fmt, args); + + call_stack& cs = m_interpreter.get_call_stack (); + + bool in_user_code = cs.current_user_code () != nullptr; + + if (! fmt_suppresses_backtrace && in_user_code + && backtrace_on_warning () + && ! discard_warning_messages ()) + pr_where (std::cerr, "warning"); + + tree_evaluator& tw = m_interpreter.get_evaluator (); + bp_table& bptab = tw.get_bp_table (); + + if ((application::interactive () + || application::forced_interactive ()) + && debug_on_warning () && in_user_code && bptab.debug_on_warn (id)) + { + unwind_protect frame; + + frame.protect_var (m_debug_on_warning); + m_debug_on_warning = false; + + tw.enter_debugger (); + } + } + } + + void error_system::rethrow_error (const char *id, const char *fmt, ...) + { + va_list args; + va_start (args, fmt); + verror (false, std::cerr, nullptr, id, fmt, args); + va_end (args); + } + + void error_system::rethrow_error (const std::string& id, + const std::string& msg, + const octave_map& stack) + { + execution_exception e = make_execution_exception ("error"); + + if (! stack.isempty () + && ! (stack.contains ("file") && stack.contains ("name") + && stack.contains ("line"))) + error ("rethrow: STACK struct must contain the fields 'file', 'name', and 'line'"); + + last_error_id (id); + last_error_message (msg); + last_error_stack (stack); + + size_t len = msg.length (); + + std::string tmp_msg (msg); + if (len > 1 && tmp_msg[len-1] == '\n') + { + tmp_msg.erase (len - 1); + + rethrow_error (id.c_str (), "%s\n", tmp_msg.c_str ()); + } + else + rethrow_error (id.c_str (), "%s", tmp_msg.c_str ()); + + if (! stack.isempty ()) + { + std::ostringstream buf; + + pr_where (buf, "error", make_stack_frame_list (stack)); + + e.set_stack_trace (buf.str ()); + } + + throw e; + } + + void error_system::vpanic (const char *fmt, va_list args) + { + buffer_error_messages (0); + discard_error_messages (false); + + verror (false, std::cerr, "panic", "", fmt, args); + + abort (); + } + + void error_system::panic (const char *fmt, ...) + { + va_list args; + va_start (args, fmt); + vpanic (fmt, args); + va_end (args); + } + + octave_scalar_map error_system::warning_query (const std::string& id_arg) + { + octave_scalar_map retval; + + std::string id = id_arg; + + if (id == "last") + id = last_warning_id (); + + octave_map opts = warning_options (); + + Cell ident = opts.contents ("identifier"); + Cell state = opts.contents ("state"); + + octave_idx_type nel = ident.numel (); + + assert (nel != 0); + + bool found = false; + + std::string val; + + for (octave_idx_type i = 0; i < nel; i++) + { + if (ident(i).string_value () == id) + { + val = state(i).string_value (); + found = true; + break; + } + } + + if (! found) + { + for (octave_idx_type i = 0; i < nel; i++) + { + if (ident(i).string_value () == "all") + { + val = state(i).string_value (); + found = true; + break; + } + } + } + + // The warning state "all" is always supposed to remain in the list, + // so we should always find a state, either explicitly or by using the + // state for "all". + + assert (found); + + retval.assign ("identifier", id); + retval.assign ("state", val); + + return retval; + } + + std::string error_system::default_warning_state (void) + { + std::string retval = "on"; + + octave_map opts = warning_options (); + + Cell ident = opts.contents ("identifier"); + Cell state = opts.contents ("state"); + + octave_idx_type nel = ident.numel (); + + for (octave_idx_type i = 0; i < nel; i++) + { + if (ident(i).string_value () == "all") + { + retval = state(i).string_value (); + break; + } + } + + return retval; + } + + void error_system::display_warning_options (std::ostream& os) + { + octave_map opts = warning_options (); + + Cell ident = opts.contents ("identifier"); + Cell state = opts.contents ("state"); + + octave_idx_type nel = ident.numel (); + + std::string all_state = default_warning_state (); + + if (all_state == "on") + os << "By default, warnings are enabled."; + else if (all_state == "off") + os << "By default, warnings are disabled."; + else if (all_state == "error") + os << "By default, warnings are treated as errors."; + else + panic_impossible (); + + if (nel > 1) + os << "\n\n"; + + // The state for all is always supposed to be first in the list. + + for (octave_idx_type i = 1; i < nel; i++) + { + std::string tid = ident(i).string_value (); + std::string tst = state(i).string_value (); + + os << std::setw (7) << tst << " " << tid << "\n"; + } + + os << std::endl; + } + + void error_system::set_warning_option (const std::string& state, + const std::string& ident) + { + std::string all_state = default_warning_state (); + + if (state != "on" && state != "off" && state != "error") + error ("invalid warning state: %s", state.c_str ()); + + octave_map opts = warning_options (); + + Cell tid = opts.contents ("identifier"); + Cell tst = opts.contents ("state"); + + octave_idx_type nel = tid.numel (); + + for (octave_idx_type i = 0; i < nel; i++) + { + if (tid(i).string_value () == ident) + { + // We found it in the current list of options. If the state + // for "all" is same as arg1, we can simply remove the item + // from the list. + + if (state == all_state && ident != "all") + { + for (i = i + 1; i < nel; i++) + { + tid(i-1) = tid(i); + tst(i-1) = tst(i); + } + + tid.resize (dim_vector (1, nel-1)); + tst.resize (dim_vector (1, nel-1)); + } + else + tst(i) = state; + + opts.clear (); + + opts.assign ("identifier", tid); + opts.assign ("state", tst); + + warning_options (opts); + + return; + } + } + + // The option wasn't already in the list. Append it. + + tid.resize (dim_vector (1, nel+1)); + tst.resize (dim_vector (1, nel+1)); + + tid(nel) = ident; + tst(nel) = state; + + opts.clear (); + + opts.assign ("identifier", tid); + opts.assign ("state", tst); + + warning_options (opts); + } + + void error_system::disable_warning (const std::string& id) + { + set_warning_option ("off", id); + } + + void error_system::initialize_default_warning_state (void) + { + warning_options (octave::init_warning_options ("on")); + + // Most people will want to have the following disabled. + + disable_warning ("Octave:array-as-logical"); + disable_warning ("Octave:array-to-scalar"); + disable_warning ("Octave:array-to-vector"); + disable_warning ("Octave:imag-to-real"); + disable_warning ("Octave:language-extension"); + disable_warning ("Octave:missing-semicolon"); + disable_warning ("Octave:neg-dim-as-zero"); + disable_warning ("Octave:resize-on-range-error"); + disable_warning ("Octave:separator-insert"); + disable_warning ("Octave:single-quote-string"); + disable_warning ("Octave:str-to-num"); + disable_warning ("Octave:mixed-string-concat"); + disable_warning ("Octave:variable-switch-label"); + } + + void error_system::interpreter_try (octave::unwind_protect& frame, + try_option opt) + { + switch (opt) + { + case buffer: + frame.protect_var (m_buffer_error_messages); + m_buffer_error_messages++; + break; + + case discard: + frame.protect_var (m_discard_error_messages); + m_discard_error_messages = true; + break; + } + + frame.protect_var (m_debug_on_error); + m_debug_on_error = false; + + frame.protect_var (m_debug_on_warning); + m_debug_on_warning = false; + + // Leave debug_on_caught as it was, so errors in try/catch are still + // caught. + } } octave::execution_exception @@ -418,89 +1182,6 @@ return retval; } -static void -maybe_enter_debugger (octave::execution_exception& e, - bool show_stack_trace = false) -{ - octave::interpreter& interp - = octave::__get_interpreter__ ("maybe_enter_debugger"); - - octave::call_stack& cs = interp.get_call_stack (); - octave::tree_evaluator& tw = interp.get_evaluator (); - octave::bp_table& bptab = tw.get_bp_table (); - octave::error_system& es = interp.get_error_system (); - - if ((octave::application::interactive () - || octave::application::forced_interactive ()) - && ((es.debug_on_error () - && bptab.debug_on_err (es.last_error_id ())) - || (es.debug_on_caught () - && bptab.debug_on_caught (es.last_error_id ()))) - && cs.current_user_code ()) - { - octave::unwind_protect frame; - - frame.add_method (es, &octave::error_system::set_debug_on_error, - es.debug_on_error ()); - es.debug_on_error (false); - - if (show_stack_trace) - { - std::string stack_trace = e.info (); - - if (! stack_trace.empty ()) - { - std::cerr << stack_trace; - - e.set_stack_trace (); - } - } - - tw.enter_debugger (); - } -} - -// Warning messages are never buffered. - -static void -vwarning (const char *name, const char *id, const char *fmt, va_list args) -{ - octave::interpreter& interp = octave::__get_interpreter__ ("vwarning"); - - octave::error_system& es = interp.get_error_system (); - - if (es.discard_warning_messages ()) - return; - - octave::flush_stdout (); - - std::ostringstream output_buf; - - octave::vformat (output_buf, fmt, args); - - // FIXME: we really want to capture the message before it has all the - // formatting goop attached to it. We probably also want just the - // message, not the traceback information. - - std::string base_msg = output_buf.str (); - std::string msg_string; - - if (name) - msg_string = std::string (name) + ": "; - - msg_string += base_msg + '\n'; - - es.last_warning_id (id); - es.last_warning_message (base_msg); - - if (! es.quiet_warning ()) - { - octave_diary << msg_string; - - std::cerr << msg_string; - } -} - void vmessage (const char *name, const char *fmt, va_list args) { @@ -532,31 +1213,12 @@ va_end (args); } -OCTAVE_NORETURN static -void -usage_1 (octave::execution_exception& e, const char *id, - const char *fmt, va_list args) -{ - verror (true, std::cerr, "usage", id, fmt, args); - - maybe_enter_debugger (e); - - throw e; -} - -OCTAVE_NORETURN static -void -usage_1 (const char *id, const char *fmt, va_list args) -{ - octave::execution_exception e = make_execution_exception ("usage"); - - usage_1 (e, id, fmt, args); -} - void vusage_with_id (const char *id, const char *fmt, va_list args) { - usage_1 (id, fmt, args); + octave::execution_exception e = make_execution_exception ("usage"); + + vusage (e, id, fmt, args); } void @@ -568,70 +1230,6 @@ va_end (args); } -OCTAVE_NORETURN static -void -error_1 (octave::execution_exception& e, std::ostream& os, - const char *name, const char *id, const char *fmt, - va_list args, bool with_cfn = false) -{ - bool show_stack_trace = false; - - if (fmt) - { - if (*fmt) - { - size_t len = strlen (fmt); - - if (len > 0) - { - if (fmt[len - 1] == '\n') - { - if (len > 1) - { - std::string tmp_fmt (fmt, len - 1); - verror (true, os, name, id, tmp_fmt.c_str (), - args, with_cfn); - } - - // If format ends with newline, suppress stack trace. - e.set_stack_trace (); - } - else - { - verror (true, os, name, id, fmt, args, with_cfn); - - octave::interpreter& interp - = octave::__get_interpreter__ ("error_1"); - - octave::call_stack& cs = interp.get_call_stack (); - octave::error_system& es = interp.get_error_system (); - - bool in_user_code = cs.current_user_code () != nullptr; - - if (in_user_code && ! es.discard_error_messages ()) - show_stack_trace = true; - } - } - } - } - else - panic ("error_1: invalid format"); - - maybe_enter_debugger (e, show_stack_trace); - - throw e; -} - -OCTAVE_NORETURN static -void -error_1 (std::ostream& os, const char *name, const char *id, - const char *fmt, va_list args, bool with_cfn = false) -{ - octave::execution_exception e = make_execution_exception ("error"); - - error_1 (e, os, name, id, fmt, args, with_cfn); -} - void verror (const char *fmt, va_list args) { @@ -707,164 +1305,17 @@ va_end (args); } -static int -check_state (const std::string& state) -{ - // -1: not found - // 0: found, "off" - // 1: found, "on" - // 2: found, "error" - - if (state == "off") - return 0; - else if (state == "on") - return 1; - else if (state == "error") - return 2; - else - return -1; -} - -// For given warning ID, return 0 if warnings are disabled, 1 if -// enabled, and 2 if the given ID should be an error instead of a -// warning. - -int -warning_enabled (const std::string& id) +int warning_enabled (const std::string& id) { - int retval = 0; - - int all_state = -1; - int id_state = -1; - - octave::error_system& es - = octave::__get_error_system__ ("warning_enabled"); - - octave_map opts = es.warning_options (); - - octave_idx_type nel = opts.numel (); - - if (nel > 0) - { - Cell identifier = opts.contents ("identifier"); - Cell state = opts.contents ("state"); - - bool all_found = false; - bool id_found = false; - - for (octave_idx_type i = 0; i < nel; i++) - { - octave_value ov = identifier(i); - std::string ovs = ov.string_value (); - - if (! all_found && ovs == "all") - { - all_state = check_state (state(i).string_value ()); - - if (all_state >= 0) - all_found = true; - } - - if (! id_found && ovs == id) - { - id_state = check_state (state(i).string_value ()); - - if (id_state >= 0) - id_found = true; - } - - if (all_found && id_found) - break; - } - } - - // If "all" is not present, assume warnings are enabled. - if (all_state == -1) - all_state = 1; - - if (all_state == 0) - { - if (id_state >= 0) - retval = id_state; - } - else if (all_state == 1) - { - if (id_state == 0 || id_state == 2) - retval = id_state; - else - retval = all_state; - } - else if (all_state == 2) - { - if (id_state == 0) - retval= id_state; - else - retval = all_state; - } - - return retval; -} - -static void -warning_1 (const char *id, const char *fmt, va_list args) -{ - int warn_opt = warning_enabled (id); - - if (warn_opt == 2) - { - // Handle this warning as an error. - - error_1 (std::cerr, "error", id, fmt, args); - } - else if (warn_opt == 1) - { - bool fmt_suppresses_backtrace = false; - size_t fmt_len = (fmt ? strlen (fmt) : 0); - fmt_suppresses_backtrace = (fmt_len > 0 && fmt[fmt_len-1] == '\n'); - - if (fmt_suppresses_backtrace && fmt_len > 1) - { - // Strip newline before issuing warning - std::string tmp_fmt (fmt, fmt_len - 1); - vwarning ("warning", id, tmp_fmt.c_str (), args); - } - else - vwarning ("warning", id, fmt, args); - - octave::interpreter& interp = octave::__get_interpreter__ ("warning_1"); - - octave::call_stack& cs = interp.get_call_stack (); - octave::error_system& es = interp.get_error_system (); - - bool in_user_code = cs.current_user_code () != nullptr; - - if (! fmt_suppresses_backtrace && in_user_code - && es.backtrace_on_warning () - && ! es.discard_warning_messages ()) - pr_where (std::cerr, "warning"); - - octave::tree_evaluator& tw = interp.get_evaluator (); - octave::bp_table& bptab = tw.get_bp_table (); - - if ((octave::application::interactive () - || octave::application::forced_interactive ()) - && es.debug_on_warning () && in_user_code && bptab.debug_on_warn (id)) - { - octave::unwind_protect frame; - - frame.add_method (es, &octave::error_system::set_debug_on_warning, - es.debug_on_warning ()); - es.debug_on_warning (false); - - tw.enter_debugger (); - } - } + octave::error_system& es = octave::__get_error_system__ ("warning_enabled"); + + return es.warning_enabled (id); } void vwarning (const char *fmt, va_list args) { - warning_1 ("", fmt, args); + vwarning ("", fmt, args); } void @@ -879,7 +1330,7 @@ void vwarning_with_id (const char *id, const char *fmt, va_list args) { - warning_1 (id, fmt, args); + vwarning (id, fmt, args); } void @@ -887,7 +1338,7 @@ { va_list args; va_start (args, fmt); - vwarning_with_id (id, fmt, args); + vwarning (id, fmt, args); va_end (args); } @@ -930,182 +1381,29 @@ va_end (args); } -static std::list -make_stack_frame_list (const octave_map& stack) +OCTAVE_NORETURN +void +vpanic (const char *fmt, va_list args) { - std::list frames; - - Cell name = stack.contents ("name"); - Cell line = stack.contents ("line"); - Cell column; - bool have_column = false; - if (stack.contains ("column")) - { - have_column = true; - column = stack.contents ("column"); - } - - octave_idx_type nel = name.numel (); - - for (octave_idx_type i = 0; i < nel; i++) - { - error_stack_frame frame; - - frame.name = name(i).string_value (); - frame.line = line(i).int_value (); - frame.column = (have_column ? column(i).int_value () : -1); - - frames.push_back (frame); - } - - return frames; -} - -static void -rethrow_error_1 (const char *id, const char *fmt, ...) -{ - va_list args; - va_start (args, fmt); - verror (false, std::cerr, nullptr, id, fmt, args); - va_end (args); + octave::error_system& es = octave::__get_error_system__ ("vpanic"); + + es.vpanic (fmt, args); } -OCTAVE_NORETURN static -void -rethrow_error (const std::string& id, const std::string& msg, - const octave_map& stack) -{ - octave::execution_exception e = make_execution_exception ("error"); - - if (! stack.isempty () - && ! (stack.contains ("file") && stack.contains ("name") - && stack.contains ("line"))) - error ("rethrow: STACK struct must contain the fields 'file', 'name', and 'line'"); - - octave::error_system& es = octave::__get_error_system__ ("rethrow_error"); - - es.last_error_id (id); - es.last_error_message (msg); - es.last_error_stack (stack); - - size_t len = msg.length (); - - std::string tmp_msg (msg); - if (len > 1 && tmp_msg[len-1] == '\n') - { - tmp_msg.erase (len - 1); - - rethrow_error_1 (id.c_str (), "%s\n", tmp_msg.c_str ()); - } - else - rethrow_error_1 (id.c_str (), "%s", tmp_msg.c_str ()); - - if (! stack.isempty ()) - { - std::ostringstream buf; - - pr_where (buf, "error", make_stack_frame_list (stack)); - - e.set_stack_trace (buf.str ()); - } - - throw e; -} - +OCTAVE_NORETURN void panic (const char *fmt, ...) { va_list args; - va_start (args, fmt); - - octave::error_system& es = octave::__get_error_system__ ("panic"); - - es.buffer_error_messages (0); - es.discard_error_messages (false); - - verror (false, std::cerr, "panic", "", fmt, args); - - va_end (args); - - abort (); -} - -static void -defun_usage_message_1 (const char *fmt, ...) -{ - va_list args; - va_start (args, fmt); - error_1 (octave_stdout, nullptr, "", fmt, args); + vpanic (fmt, args); va_end (args); } void defun_usage_message (const std::string& msg) { - defun_usage_message_1 ("%s", msg.c_str ()); -} - -typedef void (*error_fun)(const char *, const char *, ...); - -extern octave_value_list Fsprintf (const octave_value_list&, int); - -static std::string -handle_message (error_fun f, const char *id, const char *msg, - const octave_value_list& args, bool have_fmt) -{ - std::string retval; - - std::string tmpstr; - - if (args.length () > 0) - { - octave_value arg; - - if (have_fmt) - { - octave_value_list tmp = Fsprintf (args, 1); - arg = tmp(0); - } - else - arg = args(0); - - if (arg.is_defined ()) - { - if (arg.isempty ()) - return retval; - else if (arg.is_string ()) - { - tmpstr = arg.string_value (); // 2-stage assignment required - msg = tmpstr.c_str (); // in order to generate pointer - // to valid memory. - } - } - } - - // Ugh. - - size_t len = strlen (msg); - - if (len > 0) - { - if (msg[len - 1] == '\n') - { - if (len > 1) - { - std::string tmp_msg (msg, len - 1); - f (id, "%s\n", tmp_msg.c_str ()); - retval = tmp_msg; - } - } - else - { - f (id, "%s", msg); - retval = msg; - } - } - - return retval; + defun_usage_message ("%s", msg.c_str ()); } DEFMETHOD (rethrow, interp, args, , @@ -1136,59 +1434,13 @@ if (err.contains ("stack")) err_stack = err.contents ("stack").xmap_value ("ERR.STACK must be a struct"); - rethrow_error (id, msg, err_stack); + octave::error_system& es = interp.get_error_system (); + + es.rethrow_error (id, msg, err_stack); return ovl (); } -// Determine whether the first argument to error or warning function -// should be handled as the message identifier or as the format string. - -static bool -maybe_extract_message_id (const std::string& caller, - const octave_value_list& args, - octave_value_list& nargs, - std::string& id) -{ - nargs = args; - id = ""; - - int nargin = args.length (); - - bool have_fmt = nargin > 1; - - if (nargin > 0) - { - std::string arg1 = args(0).string_value (); - - // For compatibility with Matlab, an identifier must contain ':', - // but not at the beginning or the end, and it must not contain '%' - // (even if it is not a valid conversion operator) or whitespace. - - if (arg1.find_first_of ("% \f\n\r\t\v") == std::string::npos - && arg1.find (':') != std::string::npos - && arg1[0] != ':' - && arg1.back () != ':') - { - if (nargin > 1) - { - id = arg1; - - nargs.resize (nargin-1); - - for (int i = 1; i < nargin; i++) - nargs(i-1) = args(i); - } - else - nargs(0) = "call to " + caller - + " with message identifier '" + arg1 - + "' requires message"; - } - } - - return have_fmt; -} - DEFUN (error, args, , doc: /* -*- texinfo -*- @deftypefn {} {} error (@var{template}, @dots{}) @@ -1332,200 +1584,6 @@ return retval; } -static octave_scalar_map -warning_query (const std::string& id_arg) -{ - octave_scalar_map retval; - - std::string id = id_arg; - - octave::error_system& es = octave::__get_error_system__ ("warning_query"); - - if (id == "last") - id = es.last_warning_id (); - - octave_map opts = es.warning_options (); - - Cell ident = opts.contents ("identifier"); - Cell state = opts.contents ("state"); - - octave_idx_type nel = ident.numel (); - - assert (nel != 0); - - bool found = false; - - std::string val; - - for (octave_idx_type i = 0; i < nel; i++) - { - if (ident(i).string_value () == id) - { - val = state(i).string_value (); - found = true; - break; - } - } - - if (! found) - { - for (octave_idx_type i = 0; i < nel; i++) - { - if (ident(i).string_value () == "all") - { - val = state(i).string_value (); - found = true; - break; - } - } - } - - // The warning state "all" is always supposed to remain in the list, - // so we should always find a state, either explicitly or by using the - // state for "all". - - assert (found); - - retval.assign ("identifier", id); - retval.assign ("state", val); - - return retval; -} - -static std::string -default_warning_state (void) -{ - std::string retval = "on"; - - octave::error_system& es - = octave::__get_error_system__ ("default_warning_state"); - - octave_map opts = es.warning_options (); - - Cell ident = opts.contents ("identifier"); - Cell state = opts.contents ("state"); - - octave_idx_type nel = ident.numel (); - - for (octave_idx_type i = 0; i < nel; i++) - { - if (ident(i).string_value () == "all") - { - retval = state(i).string_value (); - break; - } - } - - return retval; -} - -static void -display_warning_options (std::ostream& os) -{ - octave::error_system& es - = octave::__get_error_system__ ("display_warning_options"); - - octave_map opts = es.warning_options (); - - Cell ident = opts.contents ("identifier"); - Cell state = opts.contents ("state"); - - octave_idx_type nel = ident.numel (); - - std::string all_state = default_warning_state (); - - if (all_state == "on") - os << "By default, warnings are enabled."; - else if (all_state == "off") - os << "By default, warnings are disabled."; - else if (all_state == "error") - os << "By default, warnings are treated as errors."; - else - panic_impossible (); - - if (nel > 1) - os << "\n\n"; - - // The state for all is always supposed to be first in the list. - - for (octave_idx_type i = 1; i < nel; i++) - { - std::string tid = ident(i).string_value (); - std::string tst = state(i).string_value (); - - os << std::setw (7) << tst << " " << tid << "\n"; - } - - os << std::endl; -} - -static void -set_warning_option (const std::string& state, const std::string& ident) -{ - std::string all_state = default_warning_state (); - - if (state != "on" && state != "off" && state != "error") - error ("invalid warning state: %s", state.c_str ()); - - octave::error_system& es - = octave::__get_error_system__ ("set_warning_option"); - - octave_map opts = es.warning_options (); - - Cell tid = opts.contents ("identifier"); - Cell tst = opts.contents ("state"); - - octave_idx_type nel = tid.numel (); - - for (octave_idx_type i = 0; i < nel; i++) - { - if (tid(i).string_value () == ident) - { - // We found it in the current list of options. If the state - // for "all" is same as arg1, we can simply remove the item - // from the list. - - if (state == all_state && ident != "all") - { - for (i = i + 1; i < nel; i++) - { - tid(i-1) = tid(i); - tst(i-1) = tst(i); - } - - tid.resize (dim_vector (1, nel-1)); - tst.resize (dim_vector (1, nel-1)); - } - else - tst(i) = state; - - opts.clear (); - - opts.assign ("identifier", tid); - opts.assign ("state", tst); - - es.warning_options (opts); - - return; - } - } - - // The option wasn't already in the list. Append it. - - tid.resize (dim_vector (1, nel+1)); - tst.resize (dim_vector (1, nel+1)); - - tid(nel) = ident; - tst(nel) = state; - - opts.clear (); - - opts.assign ("identifier", tid); - opts.assign ("state", tst); - - es.warning_options (opts); -} - DEFMETHOD (warning, interp, args, nargout, doc: /* -*- texinfo -*- @deftypefn {} {} warning (@var{template}, @dots{}) @@ -1672,11 +1730,11 @@ if (arg2 == "all") old_warning_options = es.warning_options (); else - old_warning_options = octave_map (warning_query (arg2)); + old_warning_options = octave_map (es.warning_query (arg2)); if (nargin == 3 && argv[3] == "local" && ! interp.at_top_level ()) { - octave_scalar_map val = warning_query (arg2); + octave_scalar_map val = es.warning_query (arg2); octave_value curr_state = val.contents ("state"); @@ -1811,7 +1869,7 @@ if (arg2 == "last") arg2 = es.last_warning_id (); - set_warning_option (arg1, arg2); + es.set_warning_option (arg1, arg2); done = true; } @@ -1840,7 +1898,7 @@ retval = tmp; } else - retval = warning_query (arg2); + retval = es.warning_query (arg2); done = true; } @@ -1850,7 +1908,7 @@ if (nargout > 0) retval = es.warning_options (); else - display_warning_options (octave_stdout); + es.display_warning_options (octave_stdout); done = true; } @@ -1881,7 +1939,7 @@ for (octave_idx_type i = 0; i < nel; i++) { std::string tid = ident(i).string_value (); - oldstate(i) = warning_query (tid).getfield ("state"); + oldstate(i) = es.warning_query (tid).getfield ("state"); } old_warning_options.setfield ("state", oldstate); @@ -1891,7 +1949,7 @@ std::string tst = state(i).string_value (); std::string tid = ident(i).string_value (); - set_warning_option (tst, tid); + es.set_warning_option (tst, tid); } done = true; @@ -1967,32 +2025,9 @@ void disable_warning (const std::string& id) { - set_warning_option ("off", id); -} - -void -initialize_default_warning_state (void) -{ - octave::error_system& es - = octave::__get_error_system__ ("initialize_default_warning_state"); - - es.warning_options (octave::init_warning_options ("on")); - - // Most people will want to have the following disabled. - - disable_warning ("Octave:array-as-logical"); - disable_warning ("Octave:array-to-scalar"); - disable_warning ("Octave:array-to-vector"); - disable_warning ("Octave:imag-to-real"); - disable_warning ("Octave:language-extension"); - disable_warning ("Octave:missing-semicolon"); - disable_warning ("Octave:neg-dim-as-zero"); - disable_warning ("Octave:resize-on-range-error"); - disable_warning ("Octave:separator-insert"); - disable_warning ("Octave:single-quote-string"); - disable_warning ("Octave:str-to-num"); - disable_warning ("Octave:mixed-string-concat"); - disable_warning ("Octave:variable-switch-label"); + octave::error_system& es = octave::__get_error_system__ ("disable_warning"); + + es.disable_warning (id); } DEFMETHOD (lasterror, interp, args, , @@ -2367,22 +2402,25 @@ } void -interpreter_try (octave::unwind_protect& frame) +interpreter_try (octave::unwind_protect& frame, + octave::error_system::try_option opt) { octave::error_system& es = octave::__get_error_system__ ("interpreter_try"); - int bem = es.buffer_error_messages (); - frame.add_method (es, &octave::error_system::set_buffer_error_messages, bem); - frame.add_method (es, &octave::error_system::set_debug_on_error, - es.debug_on_error ()); - frame.add_method (es, &octave::error_system::set_debug_on_warning, - es.debug_on_warning ()); - - es.buffer_error_messages (bem + 1); - es.debug_on_error (false); - es.debug_on_warning (false); - - // Leave debug_on_caught as it was, so errors in try/catch are still - // caught. + es.interpreter_try (frame, opt); } + +// Deprecated variables and functions. + +// This variable is obsolete and always has the value 0. +int error_state = 0; + +void +reset_error_handler (void) +{ + octave::error_system& es + = octave::__get_error_system__ ("reset_error_handler"); + + es.reset (); +} diff -r 6b0c61a5a0f0 -r 7883e6e71b0a libinterp/corefcn/error.h --- a/libinterp/corefcn/error.h Fri May 31 15:49:38 2019 +0000 +++ b/libinterp/corefcn/error.h Tue Jun 04 13:15:25 2019 +0000 @@ -39,126 +39,18 @@ class execution_exception; } -#define panic_impossible() \ - panic ("impossible state reached in file '%s' at line %d", __FILE__, __LINE__) - -OCTAVE_DEPRECATED (6, "use 'error_system::reset' instead") -extern OCTINTERP_API void reset_error_handler (void); - -extern OCTINTERP_API int warning_enabled (const std::string& id); - -extern OCTINTERP_API octave::execution_exception -make_execution_exception (const char *who); - -extern OCTINTERP_API void -vmessage (const char *name, const char *fmt, va_list args); - -OCTAVE_FORMAT_PRINTF (2, 3) -extern OCTINTERP_API void message (const char *name, const char *fmt, ...); - -extern OCTINTERP_API void vwarning (const char *fmt, va_list args); -OCTAVE_FORMAT_PRINTF (1, 2) -extern OCTINTERP_API void warning (const char *fmt, ...); - -OCTAVE_NORETURN OCTINTERP_API extern -void verror (const char *fmt, va_list args); - -OCTAVE_FORMAT_PRINTF (1, 2) -OCTAVE_NORETURN OCTINTERP_API extern -void error (const char *fmt, ...); - -OCTAVE_NORETURN OCTINTERP_API extern -void verror (octave::execution_exception&, const char *fmt, va_list args); - -OCTAVE_FORMAT_PRINTF (2, 3) -OCTAVE_NORETURN OCTINTERP_API extern -void error (octave::execution_exception&, const char *fmt, ...); - -OCTAVE_NORETURN OCTINTERP_API extern -void verror_with_cfn (const char *fmt, va_list args); - -OCTAVE_FORMAT_PRINTF (1, 2) -OCTAVE_NORETURN OCTINTERP_API extern -void error_with_cfn (const char *fmt, ...); - -OCTAVE_NORETURN OCTINTERP_API extern -void vparse_error (const char *fmt, va_list args); - -OCTAVE_FORMAT_PRINTF (1, 2) -OCTAVE_NORETURN OCTINTERP_API extern -void parse_error (const char *fmt, ...); - -extern OCTINTERP_API void -vmessage_with_id (const char *id, const char *name, - const char *fmt, va_list args); - -OCTAVE_FORMAT_PRINTF (3, 4) -extern OCTINTERP_API void -message_with_id (const char *id, const char *name, const char *fmt, ...); - -OCTAVE_NORETURN OCTINTERP_API extern -void vusage_with_id (const char *id, const char *fmt, va_list args); - -OCTAVE_FORMAT_PRINTF (2, 3) -OCTAVE_NORETURN OCTINTERP_API extern -void usage_with_id (const char *id, const char *fmt, ...); - -extern OCTINTERP_API void -vwarning_with_id (const char *id, const char *fmt, va_list args); - -OCTAVE_FORMAT_PRINTF (2, 3) -extern OCTINTERP_API void -warning_with_id (const char *id, const char *fmt, ...); - -OCTAVE_NORETURN OCTINTERP_API extern -void verror_with_id (const char *id, const char *fmt, va_list args); - -OCTAVE_FORMAT_PRINTF (2, 3) -OCTAVE_NORETURN OCTINTERP_API extern -void error_with_id (const char *id, const char *fmt, ...); - -OCTAVE_NORETURN OCTINTERP_API extern -void verror_with_id_cfn (const char *id, const char *fmt, va_list args); - -OCTAVE_FORMAT_PRINTF (2, 3) -OCTAVE_NORETURN OCTINTERP_API extern -void error_with_id_cfn (const char *id, const char *fmt, ...); - -OCTAVE_NORETURN OCTINTERP_API extern -void vparse_error_with_id (const char *id, const char *fmt, va_list args); - -OCTAVE_FORMAT_PRINTF (2, 3) -OCTAVE_NORETURN OCTINTERP_API extern -void parse_error_with_id (const char *id, const char *fmt, ...); - -OCTAVE_FORMAT_PRINTF (1, 2) -OCTAVE_NORETURN OCTINTERP_API extern -void panic (const char *fmt, ...); - -//! Helper function for print_usage defined in defun.cc. - -extern OCTINTERP_API void defun_usage_message (const std::string& msg); - -extern OCTINTERP_API octave_value_list -set_warning_state (const std::string& id, const std::string& state); - -extern OCTINTERP_API octave_value_list -set_warning_state (const octave_value_list& args); - -extern OCTINTERP_API void disable_warning (const std::string& id); -extern OCTINTERP_API void initialize_default_warning_state (void); - -OCTAVE_DEPRECATED (6, "this variable is obsolete and always has the value 0") -extern OCTINTERP_API int error_state; - -extern OCTINTERP_API void interpreter_try (octave::unwind_protect&); - namespace octave { class error_system { public: + enum try_option + { + buffer = 1, + discard = 2, + }; + error_system (interpreter& interp); error_system (const error_system&) = delete; @@ -394,6 +286,59 @@ return val; } + //! For given warning ID, return 0 if warnings are disabled, 1 if + //! enabled, and 2 if the given ID should be an error instead of a + //! warning. + + int warning_enabled (const std::string& id); + + void verror (bool save_last_error, std::ostream& os, const char *name, + const char *id, const char *fmt, va_list args, + bool with_cfn = false); + + void maybe_enter_debugger (execution_exception& e, + bool show_stack_trace = false); + + void vwarning (const char *name, const char *id, const char *fmt, + va_list args); + + OCTAVE_NORETURN + void error_1 (execution_exception& e, std::ostream& os, + const char *name, const char *id, const char *fmt, + va_list args, bool with_cfn = false); + + OCTAVE_NORETURN + void error_1 (std::ostream& os, const char *name, const char *id, + const char *fmt, va_list args, bool with_cfn); + + void vwarning (const char *id, const char *fmt, va_list args); + + void rethrow_error (const char *id, const char *fmt, ...); + + void rethrow_error (const std::string& id, const std::string& msg, + const octave_map& stack); + + OCTAVE_NORETURN + void vpanic (const char *fmt, va_list args); + + OCTAVE_NORETURN + void panic (const char *fmt, ...); + + octave_scalar_map warning_query (const std::string& id_arg); + + std::string default_warning_state (void); + + void display_warning_options (std::ostream& os); + + void set_warning_option (const std::string& state, const std::string& id); + + void disable_warning (const std::string& id); + + void initialize_default_warning_state (void); + + void interpreter_try (octave::unwind_protect& frame, + try_option = buffer); + private: interpreter& m_interpreter; @@ -469,4 +414,143 @@ }; } +// FIXME: should we move the following functions inside the octave +// namespace? If so, should the functions outside of the namespace be +// deprecated? Doing that might cause a lot of trouble... If they are +// not deprecated and eventually removed, does it make sense to also +// define them inside the octave namespace? + +#define panic_impossible() \ + panic ("impossible state reached in file '%s' at line %d", __FILE__, __LINE__) + +extern OCTINTERP_API int warning_enabled (const std::string& id); + +extern OCTINTERP_API octave::execution_exception +make_execution_exception (const char *who); + +extern OCTINTERP_API void +vmessage (const char *name, const char *fmt, va_list args); + +OCTAVE_FORMAT_PRINTF (2, 3) +extern OCTINTERP_API void message (const char *name, const char *fmt, ...); + +extern OCTINTERP_API void vwarning (const char *fmt, va_list args); + +OCTAVE_FORMAT_PRINTF (1, 2) +extern OCTINTERP_API void warning (const char *fmt, ...); + +OCTAVE_NORETURN +extern OCTINTERP_API void verror (const char *fmt, va_list args); + +OCTAVE_FORMAT_PRINTF (1, 2) +OCTAVE_NORETURN +extern OCTINTERP_API void error (const char *fmt, ...); + +OCTAVE_NORETURN +extern OCTINTERP_API void +verror (octave::execution_exception&, const char *fmt, va_list args); + +OCTAVE_FORMAT_PRINTF (2, 3) +OCTAVE_NORETURN +extern OCTINTERP_API void +error (octave::execution_exception&, const char *fmt, ...); + +OCTAVE_NORETURN +extern OCTINTERP_API void +verror_with_cfn (const char *fmt, va_list args); + +OCTAVE_FORMAT_PRINTF (1, 2) +OCTAVE_NORETURN +extern OCTINTERP_API void +error_with_cfn (const char *fmt, ...); + +OCTAVE_NORETURN +extern OCTINTERP_API void +vparse_error (const char *fmt, va_list args); + +OCTAVE_FORMAT_PRINTF (1, 2) +OCTAVE_NORETURN +extern OCTINTERP_API void +parse_error (const char *fmt, ...); + +extern OCTINTERP_API void +vmessage_with_id (const char *id, const char *name, const char *fmt, + va_list args); + +OCTAVE_FORMAT_PRINTF (3, 4) +extern OCTINTERP_API void +message_with_id (const char *id, const char *name, const char *fmt, ...); + +OCTAVE_NORETURN +extern OCTINTERP_API void +vusage_with_id (const char *id, const char *fmt, va_list args); + +OCTAVE_FORMAT_PRINTF (2, 3) +OCTAVE_NORETURN +extern OCTINTERP_API void +usage_with_id (const char *id, const char *fmt, ...); + +extern OCTINTERP_API void +vwarning_with_id (const char *id, const char *fmt, va_list args); + +OCTAVE_FORMAT_PRINTF (2, 3) +extern OCTINTERP_API void +warning_with_id (const char *id, const char *fmt, ...); + +OCTAVE_NORETURN +extern OCTINTERP_API void +verror_with_id (const char *id, const char *fmt, va_list args); + +OCTAVE_FORMAT_PRINTF (2, 3) +OCTAVE_NORETURN +extern OCTINTERP_API void +error_with_id (const char *id, const char *fmt, ...); + +OCTAVE_NORETURN +extern OCTINTERP_API void +verror_with_id_cfn (const char *id, const char *fmt, va_list args); + +OCTAVE_FORMAT_PRINTF (2, 3) +OCTAVE_NORETURN +extern OCTINTERP_API void +error_with_id_cfn (const char *id, const char *fmt, ...); + +OCTAVE_NORETURN +extern OCTINTERP_API void +vparse_error_with_id (const char *id, const char *fmt, va_list args); + +OCTAVE_FORMAT_PRINTF (2, 3) +OCTAVE_NORETURN +extern OCTINTERP_API void +parse_error_with_id (const char *id, const char *fmt, ...); + +OCTAVE_NORETURN +extern OCTINTERP_API void vpanic (const char *fmt, va_list args); + +OCTAVE_FORMAT_PRINTF (1, 2) +OCTAVE_NORETURN +extern OCTINTERP_API void panic (const char *fmt, ...); + +//! Helper function for print_usage defined in defun.cc. + +extern OCTINTERP_API void defun_usage_message (const std::string& msg); + +extern OCTINTERP_API octave_value_list +set_warning_state (const std::string& id, const std::string& state); + +extern OCTINTERP_API octave_value_list +set_warning_state (const octave_value_list& args); + +extern OCTINTERP_API void disable_warning (const std::string& id); + +extern OCTINTERP_API void +interpreter_try (octave::unwind_protect&, + octave::error_system::try_option = octave::error_system::buffer); + +OCTAVE_DEPRECATED (6, "this variable is obsolete and always has the value 0") +extern OCTINTERP_API int error_state; + +OCTAVE_DEPRECATED (6, "use 'error_system::reset' instead") +extern OCTINTERP_API void reset_error_handler (void); + #endif diff -r 6b0c61a5a0f0 -r 7883e6e71b0a libinterp/corefcn/graphics.cc --- a/libinterp/corefcn/graphics.cc Fri May 31 15:49:38 2019 +0000 +++ b/libinterp/corefcn/graphics.cc Tue Jun 04 13:15:25 2019 +0000 @@ -3570,22 +3570,7 @@ octave::unwind_protect frame; - octave::error_system& es - = octave::__get_error_system__ ("base_graphics_object::remove_all_listeners"); - - // Almost the same as interpreter_try but discard instead of - // buffer error messages. - - frame.add_method (es, &octave::error_system::set_discard_error_messages, - es.discard_error_messages ()); - frame.add_method (es, &octave::error_system::set_debug_on_error, - es.debug_on_error ()); - frame.add_method (es, &octave::error_system::set_debug_on_warning, - es.debug_on_warning ()); - - es.discard_error_messages (true); - es.debug_on_error (false); - es.debug_on_warning (false); + interpreter_try (frame, octave::error_system::discard); try { diff -r 6b0c61a5a0f0 -r 7883e6e71b0a libinterp/corefcn/interpreter.cc --- a/libinterp/corefcn/interpreter.cc Fri May 31 15:49:38 2019 +0000 +++ b/libinterp/corefcn/interpreter.cc Tue Jun 04 13:15:25 2019 +0000 @@ -409,11 +409,6 @@ thread::init (); - // Initialize default warning state before --traditional option - // that may reset them. - - initialize_default_warning_state (); - octave_ieee_init (); initialize_xerbla_error_handler ();