1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  *
19  * Author: Eric Niebler <eniebler@fb.com>
20  */
21 
22 #include <folly/Portability.h>
23 
24 namespace folly {
25 
26 template <class Fn>
27 struct exception_wrapper::arg_type_
28     : public arg_type_<decltype(&Fn::operator())> {};
29 template <class Ret, class Class, class Arg>
30 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg)> {
31   using type = Arg;
32 };
33 template <class Ret, class Class, class Arg>
34 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg) const> {
35   using type = Arg;
36 };
37 template <class Ret, class Arg>
38 struct exception_wrapper::arg_type_<Ret(Arg)> {
39   using type = Arg;
40 };
41 template <class Ret, class Arg>
42 struct exception_wrapper::arg_type_<Ret (*)(Arg)> {
43   using type = Arg;
44 };
45 template <class Ret, class Class>
46 struct exception_wrapper::arg_type_<Ret (Class::*)(...)> {
47   using type = AnyException;
48 };
49 template <class Ret, class Class>
50 struct exception_wrapper::arg_type_<Ret (Class::*)(...) const> {
51   using type = AnyException;
52 };
53 template <class Ret>
54 struct exception_wrapper::arg_type_<Ret(...)> {
55   using type = AnyException;
56 };
57 template <class Ret>
58 struct exception_wrapper::arg_type_<Ret (*)(...)> {
59   using type = AnyException;
60 };
61 
62 #ifdef FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE
63 template <class Ret, class Class, class Arg>
64 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg) noexcept> {
65   using type = Arg;
66 };
67 template <class Ret, class Class, class Arg>
68 struct exception_wrapper::arg_type_<Ret (Class::*)(Arg) const noexcept> {
69   using type = Arg;
70 };
71 template <class Ret, class Arg>
72 struct exception_wrapper::arg_type_<Ret(Arg) noexcept> {
73   using type = Arg;
74 };
75 template <class Ret, class Arg>
76 struct exception_wrapper::arg_type_<Ret (*)(Arg) noexcept> {
77   using type = Arg;
78 };
79 template <class Ret, class Class>
80 struct exception_wrapper::arg_type_<Ret (Class::*)(...) noexcept> {
81   using type = AnyException;
82 };
83 template <class Ret, class Class>
84 struct exception_wrapper::arg_type_<Ret (Class::*)(...) const noexcept> {
85   using type = AnyException;
86 };
87 template <class Ret>
88 struct exception_wrapper::arg_type_<Ret(...) noexcept> {
89   using type = AnyException;
90 };
91 template <class Ret>
92 struct exception_wrapper::arg_type_<Ret (*)(...) noexcept> {
93   using type = AnyException;
94 };
95 #endif
96 
97 struct exception_wrapper::with_exception_from_fn_ {
98   template <typename, typename Fn>
99   using apply = arg_type<Fn>;
100 };
101 
102 struct exception_wrapper::with_exception_from_ex_ {
103   template <typename Ex, typename>
104   using apply = Ex;
105 };
106 
107 template <class Ret, class... Args>
108 inline Ret exception_wrapper::noop_(Args...) {
109   return Ret();
110 }
111 
112 inline std::type_info const* exception_wrapper::uninit_type_(
113     exception_wrapper const*) {
114   return &typeid(void);
115 }
116 
117 template <class Ex, typename... As>
118 inline exception_wrapper::Buffer::Buffer(in_place_type_t<Ex>, As&&... as_) {
119   ::new (static_cast<void*>(&buff_)) Ex(std::forward<As>(as_)...);
120 }
121 
122 template <class Ex>
123 inline Ex& exception_wrapper::Buffer::as() noexcept {
124   return *static_cast<Ex*>(static_cast<void*>(&buff_));
125 }
126 template <class Ex>
127 inline Ex const& exception_wrapper::Buffer::as() const noexcept {
128   return *static_cast<Ex const*>(static_cast<void const*>(&buff_));
129 }
130 
131 inline std::exception const* exception_wrapper::as_exception_or_null_(
132     std::exception const& ex) {
133   return &ex;
134 }
135 inline std::exception const* exception_wrapper::as_exception_or_null_(
136     AnyException) {
137   return nullptr;
138 }
139 
140 inline void exception_wrapper::ExceptionPtr::copy_(
141     exception_wrapper const* from, exception_wrapper* to) {
142   ::new (static_cast<void*>(&to->eptr_)) ExceptionPtr(from->eptr_);
143 }
144 inline void exception_wrapper::ExceptionPtr::move_(
145     exception_wrapper* from, exception_wrapper* to) {
146   ::new (static_cast<void*>(&to->eptr_)) ExceptionPtr(std::move(from->eptr_));
147   delete_(from);
148 }
149 inline void exception_wrapper::ExceptionPtr::delete_(exception_wrapper* that) {
150   that->eptr_.~ExceptionPtr();
151   that->vptr_ = &uninit_;
152 }
153 [[noreturn]] inline void exception_wrapper::ExceptionPtr::throw_(
154     exception_wrapper const* that) {
155   std::rethrow_exception(that->eptr_.ptr_);
156 }
157 inline std::type_info const* exception_wrapper::ExceptionPtr::type_(
158     exception_wrapper const* that) {
159   return exception_ptr_get_type(that->eptr_.ptr_);
160 }
161 inline std::exception const* exception_wrapper::ExceptionPtr::get_exception_(
162     exception_wrapper const* that) {
163   return exception_ptr_get_object<std::exception>(that->eptr_.ptr_);
164 }
165 inline exception_wrapper exception_wrapper::ExceptionPtr::get_exception_ptr_(
166     exception_wrapper const* that) {
167   return *that;
168 }
169 
170 template <class Ex>
171 inline void exception_wrapper::InPlace<Ex>::copy_(
172     exception_wrapper const* from, exception_wrapper* to) {
173   ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>())))
174       Ex(from->buff_.as<Ex>());
175 }
176 template <class Ex>
177 inline void exception_wrapper::InPlace<Ex>::move_(
178     exception_wrapper* from, exception_wrapper* to) {
179   ::new (static_cast<void*>(std::addressof(to->buff_.as<Ex>())))
180       Ex(std::move(from->buff_.as<Ex>()));
181   delete_(from);
182 }
183 template <class Ex>
184 inline void exception_wrapper::InPlace<Ex>::delete_(exception_wrapper* that) {
185   that->buff_.as<Ex>().~Ex();
186   that->vptr_ = &uninit_;
187 }
188 template <class Ex>
189 [[noreturn]] inline void exception_wrapper::InPlace<Ex>::throw_(
190     exception_wrapper const* that) {
191   throw that->buff_.as<Ex>();
192 }
193 template <class Ex>
194 inline std::type_info const* exception_wrapper::InPlace<Ex>::type_(
195     exception_wrapper const*) {
196   return &typeid(Ex);
197 }
198 template <class Ex>
199 inline std::exception const* exception_wrapper::InPlace<Ex>::get_exception_(
200     exception_wrapper const* that) {
201   return as_exception_or_null_(that->buff_.as<Ex>());
202 }
203 template <class Ex>
204 inline exception_wrapper exception_wrapper::InPlace<Ex>::get_exception_ptr_(
205     exception_wrapper const* that) {
206   try {
207     throw_(that);
208   } catch (...) {
209     return exception_wrapper{std::current_exception()};
210   }
211 }
212 
213 template <class Ex>
214 [[noreturn]] inline void exception_wrapper::SharedPtr::Impl<Ex>::throw_()
215     const {
216   throw ex_;
217 }
218 template <class Ex>
219 inline std::exception const*
220 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_() const noexcept {
221   return as_exception_or_null_(ex_);
222 }
223 template <class Ex>
224 inline exception_wrapper
225 exception_wrapper::SharedPtr::Impl<Ex>::get_exception_ptr_() const noexcept {
226   try {
227     throw_();
228   } catch (...) {
229     return exception_wrapper{std::current_exception()};
230   }
231 }
232 inline void exception_wrapper::SharedPtr::copy_(
233     exception_wrapper const* from, exception_wrapper* to) {
234   ::new (static_cast<void*>(std::addressof(to->sptr_))) SharedPtr(from->sptr_);
235 }
236 inline void exception_wrapper::SharedPtr::move_(
237     exception_wrapper* from, exception_wrapper* to) {
238   ::new (static_cast<void*>(std::addressof(to->sptr_)))
239       SharedPtr(std::move(from->sptr_));
240   delete_(from);
241 }
242 inline void exception_wrapper::SharedPtr::delete_(exception_wrapper* that) {
243   that->sptr_.~SharedPtr();
244   that->vptr_ = &uninit_;
245 }
246 [[noreturn]] inline void exception_wrapper::SharedPtr::throw_(
247     exception_wrapper const* that) {
248   that->sptr_.ptr_->throw_();
249   folly::assume_unreachable();
250 }
251 inline std::type_info const* exception_wrapper::SharedPtr::type_(
252     exception_wrapper const* that) {
253   return that->sptr_.ptr_->info_;
254 }
255 inline std::exception const* exception_wrapper::SharedPtr::get_exception_(
256     exception_wrapper const* that) {
257   return that->sptr_.ptr_->get_exception_();
258 }
259 inline exception_wrapper exception_wrapper::SharedPtr::get_exception_ptr_(
260     exception_wrapper const* that) {
261   return that->sptr_.ptr_->get_exception_ptr_();
262 }
263 
264 template <class Ex, typename... As>
265 inline exception_wrapper::exception_wrapper(
266     ThrownTag, in_place_type_t<Ex>, As&&... as)
267     : eptr_{std::make_exception_ptr(Ex(std::forward<As>(as)...))},
268       vptr_(&ExceptionPtr::ops_) {}
269 
270 template <class Ex, typename... As>
271 inline exception_wrapper::exception_wrapper(
272     OnHeapTag, in_place_type_t<Ex>, As&&... as)
273     : sptr_{std::make_shared<SharedPtr::Impl<Ex>>(std::forward<As>(as)...)},
274       vptr_(&SharedPtr::ops_) {}
275 
276 template <class Ex, typename... As>
277 inline exception_wrapper::exception_wrapper(
278     InSituTag, in_place_type_t<Ex>, As&&... as)
279     : buff_{in_place_type<Ex>, std::forward<As>(as)...},
280       vptr_(&InPlace<Ex>::ops_) {}
281 
282 inline exception_wrapper::exception_wrapper(exception_wrapper&& that) noexcept
283     : exception_wrapper{} {
284   (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw
285 }
286 
287 inline exception_wrapper::exception_wrapper(
288     exception_wrapper const& that) noexcept
289     : exception_wrapper{} {
290   that.vptr_->copy_(&that, this); // Copy into *this, won't throw
291   vptr_ = that.vptr_;
292 }
293 
294 // If `this == &that`, this move assignment operator leaves the object in a
295 // valid but unspecified state.
296 inline exception_wrapper& exception_wrapper::operator=(
297     exception_wrapper&& that) noexcept {
298   vptr_->delete_(this); // Free the current exception
299   (vptr_ = that.vptr_)->move_(&that, this); // Move into *this, won't throw
300   return *this;
301 }
302 
303 inline exception_wrapper& exception_wrapper::operator=(
304     exception_wrapper const& that) noexcept {
305   exception_wrapper(that).swap(*this);
306   return *this;
307 }
308 
309 inline exception_wrapper::~exception_wrapper() {
310   reset();
311 }
312 
313 template <class Ex>
314 inline exception_wrapper::exception_wrapper(
315     std::exception_ptr const& ptr, Ex& ex) noexcept
316     : exception_wrapper{folly::copy(ptr), ex} {}
317 
318 template <class Ex>
319 inline exception_wrapper::exception_wrapper(
320     std::exception_ptr&& ptr, Ex& ex) noexcept
321     : eptr_{std::move(ptr)}, vptr_(&ExceptionPtr::ops_) {
322   assert(eptr_.ptr_);
323   (void)ex;
324   assert(exception_ptr_get_object<Ex>(eptr_.ptr_));
325   assert(exception_ptr_get_object<Ex>(eptr_.ptr_) == &ex || kIsWindows);
326 }
327 
328 namespace exception_wrapper_detail {
329 template <class Ex>
330 Ex&& dont_slice(Ex&& ex) {
331   assert(typeid(ex) == typeid(std::decay_t<Ex>) ||
332        !"Dynamic and static exception types don't match. Exception would "
333         "be sliced when storing in exception_wrapper.");
334   return std::forward<Ex>(ex);
335 }
336 } // namespace exception_wrapper_detail
337 
338 template <
339     class Ex,
340     class Ex_,
341     FOLLY_REQUIRES_DEF(Conjunction<
342                        exception_wrapper::IsStdException<Ex_>,
343                        exception_wrapper::IsRegularExceptionType<Ex_>>::value)>
344 inline exception_wrapper::exception_wrapper(Ex&& ex)
345     : exception_wrapper{
346           PlacementOf<Ex_>{},
347           in_place_type<Ex_>,
348           exception_wrapper_detail::dont_slice(std::forward<Ex>(ex))} {}
349 
350 template <
351     class Ex,
352     class Ex_,
353     FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType<Ex_>::value)>
354 inline exception_wrapper::exception_wrapper(in_place_t, Ex&& ex)
355     : exception_wrapper{
356           PlacementOf<Ex_>{},
357           in_place_type<Ex_>,
358           exception_wrapper_detail::dont_slice(std::forward<Ex>(ex))} {}
359 
360 template <
361     class Ex,
362     typename... As,
363     FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType<Ex>::value)>
364 inline exception_wrapper::exception_wrapper(in_place_type_t<Ex>, As&&... as)
365     : exception_wrapper{
366           PlacementOf<Ex>{}, in_place_type<Ex>, std::forward<As>(as)...} {}
367 
368 inline void exception_wrapper::swap(exception_wrapper& that) noexcept {
369   exception_wrapper tmp(std::move(that));
370   that = std::move(*this);
371   *this = std::move(tmp);
372 }
373 
374 inline exception_wrapper::operator bool() const noexcept {
375   return vptr_ != &uninit_;
376 }
377 
378 inline bool exception_wrapper::operator!() const noexcept {
379   return !static_cast<bool>(*this);
380 }
381 
382 inline void exception_wrapper::reset() {
383   vptr_->delete_(this);
384 }
385 
386 inline bool exception_wrapper::has_exception_ptr() const noexcept {
387   return vptr_ == &ExceptionPtr::ops_;
388 }
389 
390 inline std::exception* exception_wrapper::get_exception() noexcept {
391   return const_cast<std::exception*>(vptr_->get_exception_(this));
392 }
393 inline std::exception const* exception_wrapper::get_exception() const noexcept {
394   return vptr_->get_exception_(this);
395 }
396 
397 template <typename Ex>
398 inline Ex* exception_wrapper::get_exception() noexcept {
399   constexpr auto stdexcept = std::is_base_of<std::exception, Ex>::value;
400   if (vptr_ == &ExceptionPtr::ops_) {
401     return exception_ptr_get_object<Ex>(eptr_.ptr_);
402   } else if (!stdexcept || vptr_ == &uninit_) {
403     return nullptr;
404   } else {
405     using Target = conditional_t<stdexcept, Ex, std::exception>;
406     auto const ptr = dynamic_cast<Target*>(get_exception());
407     return reinterpret_cast<Ex*>(ptr);
408   }
409 }
410 
411 template <typename Ex>
412 inline Ex const* exception_wrapper::get_exception() const noexcept {
413   constexpr auto stdexcept = std::is_base_of<std::exception, Ex>::value;
414   if (vptr_ == &ExceptionPtr::ops_) {
415     return exception_ptr_get_object<Ex>(eptr_.ptr_);
416   } else if (!stdexcept || vptr_ == &uninit_) {
417     return nullptr;
418   } else {
419     using Target = conditional_t<stdexcept, Ex, std::exception>;
420     auto const ptr = dynamic_cast<Target const*>(get_exception());
421     return reinterpret_cast<Ex const*>(ptr);
422   }
423 }
424 
425 inline std::exception_ptr exception_wrapper::to_exception_ptr() noexcept {
426   if (*this) {
427     // Computing an exception_ptr is expensive so cache the result.
428     return (*this = vptr_->get_exception_ptr_(this)).eptr_.ptr_;
429   }
430   return {};
431 }
432 inline std::exception_ptr exception_wrapper::to_exception_ptr() const noexcept {
433   return vptr_->get_exception_ptr_(this).eptr_.ptr_;
434 }
435 
436 inline std::type_info const& exception_wrapper::none() noexcept {
437   return typeid(void);
438 }
439 
440 inline std::type_info const& exception_wrapper::type() const noexcept {
441   return *vptr_->type_(this);
442 }
443 
444 inline folly::fbstring exception_wrapper::what() const {
445   if (auto e = get_exception()) {
446     return class_name() + ": " + e->what();
447   }
448   return class_name();
449 }
450 
451 inline folly::fbstring exception_wrapper::class_name() const {
452   auto& ti = type();
453   return ti == none() ? "" : folly::demangle(ti);
454 }
455 
456 template <class Ex>
457 inline bool exception_wrapper::is_compatible_with() const noexcept {
458   return get_exception<Ex>();
459 }
460 
461 [[noreturn]] inline void exception_wrapper::throw_exception() const {
462   vptr_->throw_(this);
463   onNoExceptionError(__func__);
464 }
465 
466 template <class Ex>
467 [[noreturn]] inline void exception_wrapper::throw_with_nested(Ex&& ex) const {
468   try {
469     throw_exception();
470   } catch (...) {
471     std::throw_with_nested(std::forward<Ex>(ex));
472   }
473 }
474 
475 template <class This, class Fn>
476 inline bool exception_wrapper::with_exception_(
477     This&, Fn fn_, tag_t<AnyException>) {
478   return void(fn_()), true;
479 }
480 
481 template <class This, class Fn, typename Ex>
482 inline bool exception_wrapper::with_exception_(This& this_, Fn fn_, tag_t<Ex>) {
483   auto ptr = this_.template get_exception<remove_cvref_t<Ex>>();
484   return ptr && (void(fn_(static_cast<Ex&>(*ptr))), true);
485 }
486 
487 template <class Ex, class This, class Fn>
488 inline bool exception_wrapper::with_exception_(This& this_, Fn fn_) {
489   using from_fn = with_exception_from_fn_;
490   using from_ex = with_exception_from_ex_;
491   using from = conditional_t<std::is_void<Ex>::value, from_fn, from_ex>;
492   using type = typename from::template apply<Ex, Fn>;
493   return with_exception_(this_, fn_, tag<type>);
494 }
495 
496 template <class This, class... CatchFns>
497 inline void exception_wrapper::handle_(
498     This& this_, char const* name, CatchFns&... fns) {
499   using _ = bool[];
500   if (!this_) {
501     onNoExceptionError(name);
502   }
503   bool handled = false;
504   void(_{false, (handled = handled || with_exception_<void>(this_, fns))...});
505   if (!handled) {
506     this_.throw_exception();
507   }
508 }
509 
510 template <class Ex, class Fn>
511 inline bool exception_wrapper::with_exception(Fn fn) {
512   return with_exception_<Ex>(*this, std::move(fn));
513 }
514 template <class Ex, class Fn>
515 inline bool exception_wrapper::with_exception(Fn fn) const {
516   return with_exception_<Ex const>(*this, std::move(fn));
517 }
518 
519 template <class... CatchFns>
520 inline void exception_wrapper::handle(CatchFns... fns) {
521   handle_(*this, __func__, fns...);
522 }
523 template <class... CatchFns>
524 inline void exception_wrapper::handle(CatchFns... fns) const {
525   handle_(*this, __func__, fns...);
526 }
527 
528 } // namespace folly
529