/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * * Author: Eric Niebler */ #include namespace folly { template struct exception_wrapper::arg_type_ : public arg_type_ {}; template struct exception_wrapper::arg_type_ { using type = Arg; }; template struct exception_wrapper::arg_type_ { using type = Arg; }; template struct exception_wrapper::arg_type_ { using type = Arg; }; template struct exception_wrapper::arg_type_ { using type = Arg; }; template struct exception_wrapper::arg_type_ { using type = AnyException; }; template struct exception_wrapper::arg_type_ { using type = AnyException; }; template struct exception_wrapper::arg_type_ { using type = AnyException; }; template struct exception_wrapper::arg_type_ { using type = AnyException; }; #ifdef FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE template struct exception_wrapper::arg_type_ { using type = Arg; }; template struct exception_wrapper::arg_type_ { using type = Arg; }; template struct exception_wrapper::arg_type_ { using type = Arg; }; template struct exception_wrapper::arg_type_ { using type = Arg; }; template struct exception_wrapper::arg_type_ { using type = AnyException; }; template struct exception_wrapper::arg_type_ { using type = AnyException; }; template struct exception_wrapper::arg_type_ { using type = AnyException; }; template struct exception_wrapper::arg_type_ { using type = AnyException; }; #endif struct exception_wrapper::with_exception_from_fn_ { template using apply = arg_type; }; struct exception_wrapper::with_exception_from_ex_ { template using apply = Ex; }; template inline Ret exception_wrapper::noop_(Args...) { return Ret(); } inline std::type_info const* exception_wrapper::uninit_type_( exception_wrapper const*) { return &typeid(void); } template inline exception_wrapper::Buffer::Buffer(in_place_type_t, As&&... as_) { ::new (static_cast(&buff_)) Ex(std::forward(as_)...); } template inline Ex& exception_wrapper::Buffer::as() noexcept { return *static_cast(static_cast(&buff_)); } template inline Ex const& exception_wrapper::Buffer::as() const noexcept { return *static_cast(static_cast(&buff_)); } inline std::exception const* exception_wrapper::as_exception_or_null_( std::exception const& ex) { return &ex; } inline std::exception const* exception_wrapper::as_exception_or_null_( AnyException) { return nullptr; } inline void exception_wrapper::ExceptionPtr::copy_( exception_wrapper const* from, exception_wrapper* to) { ::new (static_cast(&to->eptr_)) ExceptionPtr(from->eptr_); } inline void exception_wrapper::ExceptionPtr::move_( exception_wrapper* from, exception_wrapper* to) { ::new (static_cast(&to->eptr_)) ExceptionPtr(std::move(from->eptr_)); delete_(from); } inline void exception_wrapper::ExceptionPtr::delete_(exception_wrapper* that) { that->eptr_.~ExceptionPtr(); that->vptr_ = &uninit_; } [[noreturn]] inline void exception_wrapper::ExceptionPtr::throw_( exception_wrapper const* that) { std::rethrow_exception(that->eptr_.ptr_); } inline std::type_info const* exception_wrapper::ExceptionPtr::type_( exception_wrapper const* that) { return exception_ptr_get_type(that->eptr_.ptr_); } inline std::exception const* exception_wrapper::ExceptionPtr::get_exception_( exception_wrapper const* that) { return exception_ptr_get_object(that->eptr_.ptr_); } inline exception_wrapper exception_wrapper::ExceptionPtr::get_exception_ptr_( exception_wrapper const* that) { return *that; } template inline void exception_wrapper::InPlace::copy_( exception_wrapper const* from, exception_wrapper* to) { ::new (static_cast(std::addressof(to->buff_.as()))) Ex(from->buff_.as()); } template inline void exception_wrapper::InPlace::move_( exception_wrapper* from, exception_wrapper* to) { ::new (static_cast(std::addressof(to->buff_.as()))) Ex(std::move(from->buff_.as())); delete_(from); } template inline void exception_wrapper::InPlace::delete_(exception_wrapper* that) { that->buff_.as().~Ex(); that->vptr_ = &uninit_; } template [[noreturn]] inline void exception_wrapper::InPlace::throw_( exception_wrapper const* that) { throw that->buff_.as(); } template inline std::type_info const* exception_wrapper::InPlace::type_( exception_wrapper const*) { return &typeid(Ex); } template inline std::exception const* exception_wrapper::InPlace::get_exception_( exception_wrapper const* that) { return as_exception_or_null_(that->buff_.as()); } template inline exception_wrapper exception_wrapper::InPlace::get_exception_ptr_( exception_wrapper const* that) { try { throw_(that); } catch (...) { return exception_wrapper{std::current_exception()}; } } template [[noreturn]] inline void exception_wrapper::SharedPtr::Impl::throw_() const { throw ex_; } template inline std::exception const* exception_wrapper::SharedPtr::Impl::get_exception_() const noexcept { return as_exception_or_null_(ex_); } template inline exception_wrapper exception_wrapper::SharedPtr::Impl::get_exception_ptr_() const noexcept { try { throw_(); } catch (...) { return exception_wrapper{std::current_exception()}; } } inline void exception_wrapper::SharedPtr::copy_( exception_wrapper const* from, exception_wrapper* to) { ::new (static_cast(std::addressof(to->sptr_))) SharedPtr(from->sptr_); } inline void exception_wrapper::SharedPtr::move_( exception_wrapper* from, exception_wrapper* to) { ::new (static_cast(std::addressof(to->sptr_))) SharedPtr(std::move(from->sptr_)); delete_(from); } inline void exception_wrapper::SharedPtr::delete_(exception_wrapper* that) { that->sptr_.~SharedPtr(); that->vptr_ = &uninit_; } [[noreturn]] inline void exception_wrapper::SharedPtr::throw_( exception_wrapper const* that) { that->sptr_.ptr_->throw_(); folly::assume_unreachable(); } inline std::type_info const* exception_wrapper::SharedPtr::type_( exception_wrapper const* that) { return that->sptr_.ptr_->info_; } inline std::exception const* exception_wrapper::SharedPtr::get_exception_( exception_wrapper const* that) { return that->sptr_.ptr_->get_exception_(); } inline exception_wrapper exception_wrapper::SharedPtr::get_exception_ptr_( exception_wrapper const* that) { return that->sptr_.ptr_->get_exception_ptr_(); } template inline exception_wrapper::exception_wrapper( ThrownTag, in_place_type_t, As&&... as) : eptr_{std::make_exception_ptr(Ex(std::forward(as)...))}, vptr_(&ExceptionPtr::ops_) {} template inline exception_wrapper::exception_wrapper( OnHeapTag, in_place_type_t, As&&... as) : sptr_{std::make_shared>(std::forward(as)...)}, vptr_(&SharedPtr::ops_) {} template inline exception_wrapper::exception_wrapper( InSituTag, in_place_type_t, As&&... as) : buff_{in_place_type, std::forward(as)...}, vptr_(&InPlace::ops_) {} inline exception_wrapper::exception_wrapper(exception_wrapper&& that) noexcept : exception_wrapper{} { (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw } inline exception_wrapper::exception_wrapper( exception_wrapper const& that) noexcept : exception_wrapper{} { that.vptr_->copy_(&that, this); // Copy into *this, won't throw vptr_ = that.vptr_; } // If `this == &that`, this move assignment operator leaves the object in a // valid but unspecified state. inline exception_wrapper& exception_wrapper::operator=( exception_wrapper&& that) noexcept { vptr_->delete_(this); // Free the current exception (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw return *this; } inline exception_wrapper& exception_wrapper::operator=( exception_wrapper const& that) noexcept { exception_wrapper(that).swap(*this); return *this; } inline exception_wrapper::~exception_wrapper() { reset(); } template inline exception_wrapper::exception_wrapper( std::exception_ptr const& ptr, Ex& ex) noexcept : exception_wrapper{folly::copy(ptr), ex} {} template inline exception_wrapper::exception_wrapper( std::exception_ptr&& ptr, Ex& ex) noexcept : eptr_{std::move(ptr)}, vptr_(&ExceptionPtr::ops_) { assert(eptr_.ptr_); (void)ex; assert(exception_ptr_get_object(eptr_.ptr_)); assert(exception_ptr_get_object(eptr_.ptr_) == &ex || kIsWindows); } namespace exception_wrapper_detail { template Ex&& dont_slice(Ex&& ex) { assert( (typeid(ex) == typeid(std::decay_t)) && "Dynamic and static exception types don't match. Exception would " "be sliced when storing in exception_wrapper."); return std::forward(ex); } } // namespace exception_wrapper_detail template < class Ex, class Ex_, FOLLY_REQUIRES_DEF(Conjunction< exception_wrapper::IsStdException, exception_wrapper::IsRegularExceptionType>::value)> inline exception_wrapper::exception_wrapper(Ex&& ex) : exception_wrapper{ PlacementOf{}, in_place_type, exception_wrapper_detail::dont_slice(std::forward(ex))} {} template < class Ex, class Ex_, FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType::value)> inline exception_wrapper::exception_wrapper(in_place_t, Ex&& ex) : exception_wrapper{ PlacementOf{}, in_place_type, exception_wrapper_detail::dont_slice(std::forward(ex))} {} template < class Ex, typename... As, FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType::value)> inline exception_wrapper::exception_wrapper(in_place_type_t, As&&... as) : exception_wrapper{ PlacementOf{}, in_place_type, std::forward(as)...} {} inline void exception_wrapper::swap(exception_wrapper& that) noexcept { exception_wrapper tmp(std::move(that)); that = std::move(*this); *this = std::move(tmp); } inline exception_wrapper::operator bool() const noexcept { return vptr_ != &uninit_; } inline bool exception_wrapper::operator!() const noexcept { return !static_cast(*this); } inline void exception_wrapper::reset() { vptr_->delete_(this); } inline bool exception_wrapper::has_exception_ptr() const noexcept { return vptr_ == &ExceptionPtr::ops_; } inline std::exception* exception_wrapper::get_exception() noexcept { return const_cast(vptr_->get_exception_(this)); } inline std::exception const* exception_wrapper::get_exception() const noexcept { return vptr_->get_exception_(this); } template inline Ex* exception_wrapper::get_exception() noexcept { constexpr auto stdexcept = std::is_base_of::value; if (vptr_ == &ExceptionPtr::ops_) { return exception_ptr_get_object(eptr_.ptr_); } else if (!stdexcept || vptr_ == &uninit_) { return nullptr; } else { using Target = conditional_t; auto const ptr = dynamic_cast(get_exception()); return reinterpret_cast(ptr); } } template inline Ex const* exception_wrapper::get_exception() const noexcept { constexpr auto stdexcept = std::is_base_of::value; if (vptr_ == &ExceptionPtr::ops_) { return exception_ptr_get_object(eptr_.ptr_); } else if (!stdexcept || vptr_ == &uninit_) { return nullptr; } else { using Target = conditional_t; auto const ptr = dynamic_cast(get_exception()); return reinterpret_cast(ptr); } } inline std::exception_ptr exception_wrapper::to_exception_ptr() noexcept { if (*this) { // Computing an exception_ptr is expensive so cache the result. return (*this = vptr_->get_exception_ptr_(this)).eptr_.ptr_; } return {}; } inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept { return vptr_->get_exception_ptr_(this).eptr_.ptr_; } inline std::type_info const& exception_wrapper::none() noexcept { return typeid(void); } inline std::type_info const& exception_wrapper::type() const noexcept { return *vptr_->type_(this); } inline folly::fbstring exception_wrapper::what() const { if (auto e = get_exception()) { return class_name() + ": " + e->what(); } return class_name(); } inline folly::fbstring exception_wrapper::class_name() const { auto& ti = type(); return ti == none() ? "" : folly::demangle(ti); } template inline bool exception_wrapper::is_compatible_with() const noexcept { return get_exception(); } [[noreturn]] inline void exception_wrapper::throw_exception() const { vptr_->throw_(this); onNoExceptionError(__func__); } template [[noreturn]] inline void exception_wrapper::throw_with_nested(Ex&& ex) const { try { throw_exception(); } catch (...) { std::throw_with_nested(std::forward(ex)); } } template inline bool exception_wrapper::with_exception_( This&, Fn fn_, tag_t) { return void(fn_()), true; } template inline bool exception_wrapper::with_exception_(This& this_, Fn fn_, tag_t) { auto ptr = this_.template get_exception>(); return ptr && (void(fn_(static_cast(*ptr))), true); } template inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) { using from_fn = with_exception_from_fn_; using from_ex = with_exception_from_ex_; using from = conditional_t::value, from_fn, from_ex>; using type = typename from::template apply; return with_exception_(this_, std::move(fn_), tag); } template inline void exception_wrapper::handle_( This& this_, char const* name, CatchFns&... fns) { using _ = bool[]; if (!this_) { onNoExceptionError(name); } bool handled = false; void(_{false, (handled = handled || with_exception_(this_, fns))...}); if (!handled) { this_.throw_exception(); } } template inline bool exception_wrapper::with_exception(Fn fn) { return with_exception_(*this, std::move(fn)); } template inline bool exception_wrapper::with_exception(Fn fn) const { return with_exception_(*this, std::move(fn)); } template inline void exception_wrapper::handle(CatchFns... fns) { handle_(*this, __func__, fns...); } template inline void exception_wrapper::handle(CatchFns... fns) const { handle_(*this, __func__, fns...); } } // namespace folly