peerplays-fc/src/exception.cpp

267 lines
8 KiB
C++

#include <fc/exception/exception.hpp>
#include <boost/exception/all.hpp>
#include <fc/io/sstream.hpp>
#include <fc/log/logger.hpp>
#include <fc/io/json.hpp>
#include <iostream>
namespace fc
{
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)
(unknown_host_exception)
(null_optional)
(aes_exception)
(overflow_exception)
(underflow_exception)
(divide_by_zero_exception)
)
namespace detail
{
class exception_impl
{
public:
std::string _name;
std::string _what;
int64_t _code;
log_messages _elog;
};
}
exception::exception( 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 = fc::move(msgs);
}
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;
}
unhandled_exception::unhandled_exception( log_message&& m, std::exception_ptr e )
:exception( fc::move(m) )
{
_inner = e;
}
unhandled_exception::unhandled_exception( const exception& r )
:exception(r)
{
}
unhandled_exception::unhandled_exception( log_messages m )
:exception()
{ my->_elog = fc::move(m); }
std::exception_ptr unhandled_exception::get_inner_exception()const { return _inner; }
NO_RETURN void unhandled_exception::dynamic_rethrow_exception()const
{
if( !(_inner == std::exception_ptr()) ) std::rethrow_exception( _inner );
else { fc::exception::dynamic_rethrow_exception(); }
}
std::shared_ptr<exception> unhandled_exception::dynamic_copy_exception()const
{
auto e = std::make_shared<unhandled_exception>( *this );
e->_inner = _inner;
return e;
}
exception::exception( 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;
}
exception::exception( log_message&& msg,
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.push_back( fc::move( msg ) );
}
exception::exception( const exception& c )
:my( new detail::exception_impl(*c.my) )
{ }
exception::exception( exception&& c )
:my( fc::move(c.my) ){}
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; }
exception::~exception(){}
void to_variant( const exception& e, variant& v )
{
v = mutable_variant_object( "code", e.code() )
( "name", e.name() )
( "message", e.what() )
( "stack", e.get_log() );
}
void from_variant( const variant& v, exception& ll )
{
auto obj = v.get_object();
if( obj.contains( "stack" ) )
ll.my->_elog = obj["stack"].as<log_messages>();
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();
}
const log_messages& exception::get_log()const { return my->_elog; }
void exception::append_log( log_message m )
{
my->_elog.emplace_back( fc::move(m) );
}
/**
* Generates a detailed string including file, line, method,
* and other information that is generally only useful for
* developers.
*/
string exception::to_detail_string( log_level ll )const
{
fc::stringstream ss;
ss << variant(my->_code).as_string() <<" " << my->_name << ": " <<my->_what<<"\n";
for( auto itr = my->_elog.begin(); itr != my->_elog.end(); )
{
ss << itr->get_message() <<"\n"; //fc::format_string( itr->get_format(), itr->get_data() ) <<"\n";
ss << " " << json::to_string( itr->get_data() )<<"\n";
ss << " " << itr->get_context().to_string();
++itr;
if( itr != my->_elog.end() ) ss<<"\n";
}
return ss.str();
}
/**
* Generates a user-friendly error report.
*/
string exception::to_string( log_level ll )const
{
fc::stringstream ss;
ss << what() << ":";
for( auto itr = my->_elog.begin(); itr != my->_elog.end(); ++itr )
{
if( itr->get_format().size() )
ss << " " << fc::format_string( itr->get_format(), itr->get_data() );
// ss << " " << itr->get_context().to_string() <<"\n";
}
return ss.str();
}
void NO_RETURN exception_factory::rethrow( const exception& e )const
{
auto itr = _registered_exceptions.find( e.code() );
if( itr != _registered_exceptions.end() )
itr->second->rethrow( e );
throw e;
}
/**
* Rethrows the exception restoring the proper type based upon
* the error code. This is used to propagate exception types
* across conversions to/from JSON
*/
NO_RETURN void exception::dynamic_rethrow_exception()const
{
exception_factory::instance().rethrow( *this );
}
exception_ptr exception::dynamic_copy_exception()const
{
return std::make_shared<exception>(*this);
}
fc::string except_str()
{
return boost::current_exception_diagnostic_information();
}
void throw_bad_enum_cast( int64_t i, const char* e )
{
FC_THROW_EXCEPTION( bad_cast_exception,
"invalid index '${key}' in enum '${enum}'",
("key",i)("enum",e) );
}
void throw_bad_enum_cast( const char* k, const char* e )
{
FC_THROW_EXCEPTION( bad_cast_exception,
"invalid name '${key}' in enum '${enum}'",
("key",k)("enum",e) );
}
bool assert_optional(bool is_valid )
{
if( !is_valid )
throw null_optional();
return true;
}
exception& exception::operator=( const exception& copy )
{
*my = *copy.my;
return *this;
}
exception& exception::operator=( exception&& copy )
{
my = std::move(copy.my);
return *this;
}
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)
;
std::cout
<< "FC_ASSERT triggered: "
<< fc::json::to_string( assert_trip_info ) << "\n";
return;
}
bool enable_record_assert_trip = false;
} // fc