2013-06-05 19:19:00 +00:00
|
|
|
#include <fc/exception/exception.hpp>
|
2012-09-08 02:50:37 +00:00
|
|
|
#include <boost/exception/all.hpp>
|
2013-06-05 19:19:00 +00:00
|
|
|
#include <fc/io/sstream.hpp>
|
|
|
|
|
#include <fc/log/logger.hpp>
|
|
|
|
|
#include <fc/io/json.hpp>
|
2012-10-10 01:40:29 +00:00
|
|
|
|
2015-07-06 18:50:59 +00:00
|
|
|
#include <iostream>
|
|
|
|
|
|
2013-06-05 19:19:00 +00:00
|
|
|
namespace fc
|
|
|
|
|
{
|
2019-05-20 19:38:15 +00:00
|
|
|
FC_IMPLEMENT_EXCEPTION( timeout_exception, timeout_exception_code, "Timeout" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( file_not_found_exception, file_not_found_exception_code, "File Not Found" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( parse_error_exception, parse_error_exception_code, "Parse Error" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( invalid_arg_exception, invalid_arg_exception_code, "Invalid Argument" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( key_not_found_exception, key_not_found_exception_code, "Key Not Found" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( bad_cast_exception, bad_cast_exception_code, "Bad Cast" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( out_of_range_exception, out_of_range_exception_code, "Out of Range" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( method_not_found_exception, method_not_found_exception_code, "Method Not Found" );
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( invalid_operation_exception, invalid_operation_exception_code, "Invalid Operation" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( unknown_host_exception, unknown_host_exception_code, "Unknown Host" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( canceled_exception, canceled_exception_code, "Canceled" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( assert_exception, assert_exception_code, "Assert Exception" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( eof_exception, eof_exception_code, "End Of File" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( null_optional, null_optional_code, "null optional" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( aes_exception, aes_error_code, "AES error" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( overflow_exception, overflow_code, "Integer Overflow" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( underflow_exception, underflow_code, "Integer Underflow" )
|
|
|
|
|
FC_IMPLEMENT_EXCEPTION( divide_by_zero_exception, divide_by_zero_code, "Integer Divide By Zero" )
|
|
|
|
|
|
2014-06-06 20:42:42 +00:00
|
|
|
FC_REGISTER_EXCEPTIONS( (timeout_exception)
|
|
|
|
|
(file_not_found_exception)
|
|
|
|
|
(parse_error_exception)
|
|
|
|
|
(invalid_arg_exception)
|
|
|
|
|
(invalid_operation_exception)
|
|
|
|
|
(key_not_found_exception)
|
|
|
|
|
(bad_cast_exception)
|
|
|
|
|
(out_of_range_exception)
|
|
|
|
|
(canceled_exception)
|
|
|
|
|
(assert_exception)
|
|
|
|
|
(eof_exception)
|
2014-06-12 01:46:26 +00:00
|
|
|
(unknown_host_exception)
|
|
|
|
|
(null_optional)
|
2014-07-29 17:54:50 +00:00
|
|
|
(aes_exception)
|
2015-02-19 16:39:17 +00:00
|
|
|
(overflow_exception)
|
|
|
|
|
(underflow_exception)
|
2015-07-13 18:28:44 +00:00
|
|
|
(divide_by_zero_exception)
|
2014-06-06 20:42:42 +00:00
|
|
|
)
|
|
|
|
|
|
2013-06-05 19:19:00 +00:00
|
|
|
namespace detail
|
|
|
|
|
{
|
|
|
|
|
class exception_impl
|
|
|
|
|
{
|
|
|
|
|
public:
|
2014-06-06 20:42:42 +00:00
|
|
|
std::string _name;
|
|
|
|
|
std::string _what;
|
|
|
|
|
int64_t _code;
|
2013-06-05 19:19:00 +00:00
|
|
|
log_messages _elog;
|
|
|
|
|
};
|
|
|
|
|
}
|
2014-06-12 06:23:52 +00:00
|
|
|
exception::exception( log_messages&& msgs, int64_t code,
|
2015-07-13 18:28:44 +00:00
|
|
|
const std::string& name_value,
|
2014-06-12 06:23:52 +00:00
|
|
|
const std::string& what_value )
|
|
|
|
|
:my( new detail::exception_impl() )
|
|
|
|
|
{
|
|
|
|
|
my->_code = code;
|
|
|
|
|
my->_what = what_value;
|
|
|
|
|
my->_name = name_value;
|
2019-04-04 11:46:38 +00:00
|
|
|
my->_elog = std::move(msgs);
|
2014-06-12 06:23:52 +00:00
|
|
|
}
|
2013-06-05 19:19:00 +00:00
|
|
|
|
2015-07-28 20:40:49 +00:00
|
|
|
exception::exception(
|
|
|
|
|
const log_messages& msgs,
|
|
|
|
|
int64_t code,
|
|
|
|
|
const std::string& name_value,
|
|
|
|
|
const std::string& what_value )
|
|
|
|
|
:my( new detail::exception_impl() )
|
|
|
|
|
{
|
|
|
|
|
my->_code = code;
|
|
|
|
|
my->_what = what_value;
|
|
|
|
|
my->_name = name_value;
|
|
|
|
|
my->_elog = msgs;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-13 18:28:44 +00:00
|
|
|
unhandled_exception::unhandled_exception( log_message&& m, std::exception_ptr e )
|
2019-04-04 11:46:38 +00:00
|
|
|
:exception( std::move(m) )
|
2015-07-13 18:28:44 +00:00
|
|
|
{
|
2013-06-05 19:19:00 +00:00
|
|
|
_inner = e;
|
2015-07-13 18:28:44 +00:00
|
|
|
}
|
2013-06-05 19:19:00 +00:00
|
|
|
unhandled_exception::unhandled_exception( const exception& r )
|
|
|
|
|
:exception(r)
|
|
|
|
|
{
|
|
|
|
|
}
|
2015-07-13 18:28:44 +00:00
|
|
|
unhandled_exception::unhandled_exception( log_messages m )
|
|
|
|
|
:exception()
|
2019-04-04 11:46:38 +00:00
|
|
|
{ my->_elog = std::move(m); }
|
2014-06-06 20:42:42 +00:00
|
|
|
|
2013-06-05 19:19:00 +00:00
|
|
|
std::exception_ptr unhandled_exception::get_inner_exception()const { return _inner; }
|
2014-06-06 20:42:42 +00:00
|
|
|
|
2019-06-03 12:13:12 +00:00
|
|
|
[[noreturn]] void unhandled_exception::dynamic_rethrow_exception()const
|
2013-06-05 19:19:00 +00:00
|
|
|
{
|
|
|
|
|
if( !(_inner == std::exception_ptr()) ) std::rethrow_exception( _inner );
|
|
|
|
|
else { fc::exception::dynamic_rethrow_exception(); }
|
|
|
|
|
}
|
2014-06-06 20:42:42 +00:00
|
|
|
|
2013-06-05 19:19:00 +00:00
|
|
|
std::shared_ptr<exception> unhandled_exception::dynamic_copy_exception()const
|
|
|
|
|
{
|
|
|
|
|
auto e = std::make_shared<unhandled_exception>( *this );
|
|
|
|
|
e->_inner = _inner;
|
|
|
|
|
return e;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-13 18:28:44 +00:00
|
|
|
exception::exception( int64_t code,
|
2014-06-06 20:42:42 +00:00
|
|
|
const std::string& name_value,
|
2015-07-13 18:28:44 +00:00
|
|
|
const std::string& what_value )
|
2013-06-05 19:19:00 +00:00
|
|
|
:my( new detail::exception_impl() )
|
|
|
|
|
{
|
2014-06-06 20:42:42 +00:00
|
|
|
my->_code = code;
|
|
|
|
|
my->_what = what_value;
|
|
|
|
|
my->_name = name_value;
|
2013-06-05 19:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
2015-07-13 18:28:44 +00:00
|
|
|
exception::exception( log_message&& msg,
|
|
|
|
|
int64_t code,
|
2014-06-06 20:42:42 +00:00
|
|
|
const std::string& name_value,
|
|
|
|
|
const std::string& what_value )
|
2013-06-05 19:19:00 +00:00
|
|
|
:my( new detail::exception_impl() )
|
|
|
|
|
{
|
2014-06-06 20:42:42 +00:00
|
|
|
my->_code = code;
|
|
|
|
|
my->_what = what_value;
|
|
|
|
|
my->_name = name_value;
|
2019-04-04 11:46:38 +00:00
|
|
|
my->_elog.push_back( std::move( msg ) );
|
2013-06-05 19:19:00 +00:00
|
|
|
}
|
|
|
|
|
exception::exception( const exception& c )
|
|
|
|
|
:my( new detail::exception_impl(*c.my) )
|
2014-06-06 20:42:42 +00:00
|
|
|
{ }
|
2013-06-05 19:19:00 +00:00
|
|
|
exception::exception( exception&& c )
|
2019-04-04 11:46:38 +00:00
|
|
|
:my( std::move(c.my) ){}
|
2013-06-05 19:19:00 +00:00
|
|
|
|
2014-06-06 20:42:42 +00:00
|
|
|
const char* exception::name()const throw() { return my->_name.c_str(); }
|
|
|
|
|
const char* exception::what()const throw() { return my->_what.c_str(); }
|
|
|
|
|
int64_t exception::code()const throw() { return my->_code; }
|
2013-06-05 19:19:00 +00:00
|
|
|
|
2014-06-06 20:42:42 +00:00
|
|
|
exception::~exception(){}
|
2013-06-05 19:19:00 +00:00
|
|
|
|
2018-03-11 09:11:14 +00:00
|
|
|
void to_variant( const exception& e, variant& v, uint32_t max_depth )
|
2013-06-05 19:19:00 +00:00
|
|
|
{
|
2018-03-11 09:11:14 +00:00
|
|
|
FC_ASSERT( max_depth > 0, "Recursion depth exceeded!" );
|
|
|
|
|
variant v_log;
|
|
|
|
|
to_variant( e.get_log(), v_log, max_depth - 1 );
|
|
|
|
|
mutable_variant_object tmp;
|
|
|
|
|
tmp( "code", e.code() )
|
|
|
|
|
( "name", e.name() )
|
|
|
|
|
( "message", e.what() )
|
|
|
|
|
( "stack", v_log );
|
|
|
|
|
v = variant( tmp, max_depth );
|
2015-07-13 18:28:44 +00:00
|
|
|
|
2013-06-05 19:19:00 +00:00
|
|
|
}
|
2018-03-11 09:11:14 +00:00
|
|
|
void from_variant( const variant& v, exception& ll, uint32_t max_depth )
|
2013-06-05 19:19:00 +00:00
|
|
|
{
|
2018-03-11 09:11:14 +00:00
|
|
|
FC_ASSERT( max_depth > 0, "Recursion depth exceeded!" );
|
2013-06-05 19:19:00 +00:00
|
|
|
auto obj = v.get_object();
|
2014-06-06 20:42:42 +00:00
|
|
|
if( obj.contains( "stack" ) )
|
2018-03-11 09:11:14 +00:00
|
|
|
ll.my->_elog = obj["stack"].as<log_messages>( max_depth - 1 );
|
2014-06-06 20:42:42 +00:00
|
|
|
if( obj.contains( "code" ) )
|
|
|
|
|
ll.my->_code = obj["code"].as_int64();
|
|
|
|
|
if( obj.contains( "name" ) )
|
|
|
|
|
ll.my->_name = obj["name"].as_string();
|
|
|
|
|
if( obj.contains( "message" ) )
|
|
|
|
|
ll.my->_what = obj["message"].as_string();
|
2013-06-05 19:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const log_messages& exception::get_log()const { return my->_elog; }
|
2014-06-06 20:42:42 +00:00
|
|
|
void exception::append_log( log_message m )
|
2013-06-05 19:19:00 +00:00
|
|
|
{
|
2019-04-04 11:46:38 +00:00
|
|
|
my->_elog.emplace_back( std::move(m) );
|
2013-06-05 19:19:00 +00:00
|
|
|
}
|
2015-07-13 18:28:44 +00:00
|
|
|
|
2013-06-05 19:19:00 +00:00
|
|
|
/**
|
|
|
|
|
* Generates a detailed string including file, line, method,
|
|
|
|
|
* and other information that is generally only useful for
|
|
|
|
|
* developers.
|
|
|
|
|
*/
|
2017-04-10 15:12:24 +00:00
|
|
|
string exception::to_detail_string( log_level ll )const
|
2013-06-05 19:19:00 +00:00
|
|
|
{
|
2019-09-18 10:51:17 +00:00
|
|
|
std::stringstream ss;
|
2018-05-22 14:59:42 +00:00
|
|
|
try {
|
|
|
|
|
try {
|
|
|
|
|
ss << variant( my->_code ).as_string();
|
|
|
|
|
} catch( std::bad_alloc& ) {
|
|
|
|
|
throw;
|
|
|
|
|
} catch( ... ) {
|
|
|
|
|
ss << "<- exception in to_detail_string.";
|
|
|
|
|
}
|
|
|
|
|
ss << " " << my->_name << ": " << my->_what << "\n";
|
|
|
|
|
for( auto itr = my->_elog.begin(); itr != my->_elog.end(); ) {
|
|
|
|
|
try {
|
2019-09-18 10:51:17 +00:00
|
|
|
ss << itr->get_message() <<"\n"; //fc::format_string( itr->get_format(), itr->get_data() ) <<"\n";
|
2018-03-09 17:49:21 +00:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
ss << " " << json::to_string( itr->get_data() )<<"\n";
|
|
|
|
|
}
|
|
|
|
|
catch( const fc::assert_exception& e )
|
|
|
|
|
{
|
|
|
|
|
ss << "ERROR: Failed to convert log data to string!\n";
|
|
|
|
|
}
|
2013-06-05 19:19:00 +00:00
|
|
|
ss << " " << itr->get_context().to_string();
|
|
|
|
|
++itr;
|
2018-05-22 14:59:42 +00:00
|
|
|
} catch( std::bad_alloc& ) {
|
|
|
|
|
throw;
|
|
|
|
|
} catch( ... ) {
|
|
|
|
|
ss << "<- exception in to_detail_string.";
|
|
|
|
|
}
|
2013-06-05 19:19:00 +00:00
|
|
|
if( itr != my->_elog.end() ) ss<<"\n";
|
|
|
|
|
}
|
2018-05-22 14:59:42 +00:00
|
|
|
} catch( std::bad_alloc& ) {
|
|
|
|
|
throw;
|
|
|
|
|
} catch( ... ) {
|
|
|
|
|
ss << "<- exception in to_detail_string.\n";
|
|
|
|
|
}
|
2013-06-05 19:19:00 +00:00
|
|
|
return ss.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generates a user-friendly error report.
|
|
|
|
|
*/
|
2017-04-10 15:12:24 +00:00
|
|
|
string exception::to_string( log_level ll )const
|
2013-06-05 19:19:00 +00:00
|
|
|
{
|
2019-09-18 10:51:17 +00:00
|
|
|
std::stringstream ss;
|
2018-05-22 14:59:42 +00:00
|
|
|
try {
|
|
|
|
|
ss << my->_what;
|
|
|
|
|
try {
|
|
|
|
|
ss << " (" << variant( my->_code ).as_string() << ")\n";
|
|
|
|
|
} catch( std::bad_alloc& ) {
|
|
|
|
|
throw;
|
|
|
|
|
} catch( ... ) {
|
|
|
|
|
ss << "<- exception in to_string.\n";
|
|
|
|
|
}
|
|
|
|
|
for( auto itr = my->_elog.begin(); itr != my->_elog.end(); ++itr ) {
|
|
|
|
|
try {
|
2019-09-18 10:51:17 +00:00
|
|
|
ss << fc::format_string( itr->get_format(), itr->get_data() ) << "\n";
|
|
|
|
|
// ss << " " << itr->get_context().to_string() <<"\n";
|
2018-05-22 14:59:42 +00:00
|
|
|
} catch( std::bad_alloc& ) {
|
|
|
|
|
throw;
|
|
|
|
|
} catch( ... ) {
|
|
|
|
|
ss << "<- exception in to_string.\n";
|
|
|
|
|
}
|
2013-06-05 19:19:00 +00:00
|
|
|
}
|
|
|
|
|
return ss.str();
|
2018-05-22 14:59:42 +00:00
|
|
|
} catch( std::bad_alloc& ) {
|
|
|
|
|
throw;
|
|
|
|
|
} catch( ... ) {
|
|
|
|
|
ss << "<- exception in to_string.\n";
|
|
|
|
|
}
|
2018-05-30 09:56:20 +00:00
|
|
|
return ss.str();
|
2013-06-05 19:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
2019-06-03 12:13:12 +00:00
|
|
|
[[noreturn]] void exception_factory::rethrow( const exception& e )const
|
2014-06-06 20:42:42 +00:00
|
|
|
{
|
|
|
|
|
auto itr = _registered_exceptions.find( e.code() );
|
|
|
|
|
if( itr != _registered_exceptions.end() )
|
|
|
|
|
itr->second->rethrow( e );
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
2013-06-05 19:19:00 +00:00
|
|
|
/**
|
|
|
|
|
* Rethrows the exception restoring the proper type based upon
|
|
|
|
|
* the error code. This is used to propagate exception types
|
|
|
|
|
* across conversions to/from JSON
|
|
|
|
|
*/
|
2019-06-03 12:13:12 +00:00
|
|
|
[[noreturn]] void exception::dynamic_rethrow_exception()const
|
2013-06-05 19:19:00 +00:00
|
|
|
{
|
2014-06-06 20:42:42 +00:00
|
|
|
exception_factory::instance().rethrow( *this );
|
2013-06-05 19:19:00 +00:00
|
|
|
}
|
2014-06-06 20:42:42 +00:00
|
|
|
|
2015-07-13 18:28:44 +00:00
|
|
|
exception_ptr exception::dynamic_copy_exception()const
|
2013-06-05 19:19:00 +00:00
|
|
|
{
|
2014-06-06 20:42:42 +00:00
|
|
|
return std::make_shared<exception>(*this);
|
2013-06-05 19:19:00 +00:00
|
|
|
}
|
2015-07-13 18:28:44 +00:00
|
|
|
|
2019-04-04 11:46:38 +00:00
|
|
|
std::string except_str()
|
2013-06-05 19:19:00 +00:00
|
|
|
{
|
2015-07-13 18:28:44 +00:00
|
|
|
return boost::current_exception_diagnostic_information();
|
2013-06-05 19:19:00 +00:00
|
|
|
}
|
2014-06-06 20:42:42 +00:00
|
|
|
|
2013-06-05 19:19:00 +00:00
|
|
|
void throw_bad_enum_cast( int64_t i, const char* e )
|
|
|
|
|
{
|
2015-07-13 18:28:44 +00:00
|
|
|
FC_THROW_EXCEPTION( bad_cast_exception,
|
|
|
|
|
"invalid index '${key}' in enum '${enum}'",
|
2014-06-06 20:42:42 +00:00
|
|
|
("key",i)("enum",e) );
|
2013-06-05 19:19:00 +00:00
|
|
|
}
|
|
|
|
|
void throw_bad_enum_cast( const char* k, const char* e )
|
|
|
|
|
{
|
2015-07-13 18:28:44 +00:00
|
|
|
FC_THROW_EXCEPTION( bad_cast_exception,
|
|
|
|
|
"invalid name '${key}' in enum '${enum}'",
|
2014-06-06 20:42:42 +00:00
|
|
|
("key",k)("enum",e) );
|
2013-06-05 19:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
2014-06-12 01:46:26 +00:00
|
|
|
bool assert_optional(bool is_valid )
|
|
|
|
|
{
|
|
|
|
|
if( !is_valid )
|
|
|
|
|
throw null_optional();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2014-06-12 06:23:52 +00:00
|
|
|
exception& exception::operator=( const exception& copy )
|
|
|
|
|
{
|
|
|
|
|
*my = *copy.my;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exception& exception::operator=( exception&& copy )
|
|
|
|
|
{
|
|
|
|
|
my = std::move(copy.my);
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
2014-06-12 01:46:26 +00:00
|
|
|
|
2018-03-11 09:11:14 +00:00
|
|
|
void throw_assertion_failure( const std::string& message )
|
|
|
|
|
{
|
|
|
|
|
FC_THROW_EXCEPTION( fc::assert_exception, message );
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-06 18:50:59 +00:00
|
|
|
void record_assert_trip(
|
|
|
|
|
const char* filename,
|
|
|
|
|
uint32_t lineno,
|
|
|
|
|
const char* expr
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
fc::mutable_variant_object assert_trip_info =
|
|
|
|
|
fc::mutable_variant_object()
|
|
|
|
|
("source_file", filename)
|
|
|
|
|
("source_lineno", lineno)
|
|
|
|
|
("expr", expr)
|
|
|
|
|
;
|
2018-03-09 17:49:21 +00:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
std::cout
|
|
|
|
|
<< "FC_ASSERT triggered: "
|
|
|
|
|
<< fc::json::to_string( assert_trip_info ) << "\n";
|
|
|
|
|
}
|
|
|
|
|
catch( const fc::assert_exception& e )
|
|
|
|
|
{ // this should never happen. assert_trip_info is flat.
|
|
|
|
|
std::cout << "ERROR: Failed to convert info to string?!\n";
|
|
|
|
|
}
|
2015-07-06 18:50:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool enable_record_assert_trip = false;
|
|
|
|
|
|
2013-06-05 19:19:00 +00:00
|
|
|
} // fc
|