Outline throw statements in dynamic
authorYedidya Feldblum <yfeldblum@fb.com>
Wed, 26 Jul 2017 18:39:53 +0000 (11:39 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Wed, 26 Jul 2017 18:54:15 +0000 (11:54 -0700)
Summary:
[Folly] Outline `throw` statements in `dynamic`.

There is no need for them to be inline - definitionally, they are always cold.

Reviewed By: WillerZ

Differential Revision: D5497457

fbshipit-source-id: 7b649c59b5b2609f838eb10e9329468d1bd1d558

folly/dynamic-inl.h
folly/dynamic.cpp

index 7db61c02f276e55c0e309a4ebe3fc61709197f66..ee6a850eb485db355ff59eb2273281f7ff27f0c8 100644 (file)
@@ -78,11 +78,20 @@ namespace folly {
 
 struct TypeError : std::runtime_error {
   explicit TypeError(const std::string& expected, dynamic::Type actual);
-  explicit TypeError(const std::string& expected,
-    dynamic::Type actual1, dynamic::Type actual2);
+  explicit TypeError(
+      const std::string& expected,
+      dynamic::Type actual1,
+      dynamic::Type actual2);
   ~TypeError() override;
 };
 
+[[noreturn]] void throwTypeError_(
+    std::string const& expected,
+    dynamic::Type actual);
+[[noreturn]] void throwTypeError_(
+    std::string const& expected,
+    dynamic::Type actual1,
+    dynamic::Type actual2);
 
 //////////////////////////////////////////////////////////////////////
 
@@ -102,7 +111,7 @@ namespace detail {
   template<template<class> class Op>
   dynamic numericOp(dynamic const& a, dynamic const& b) {
     if (!a.isNumber() || !b.isNumber()) {
-      throw TypeError("numeric", a.type(), b.type());
+      throwTypeError_("numeric", a.type(), b.type());
     }
     if (a.type() != b.type()) {
       auto& integ  = a.isInt() ? a : b;
@@ -517,13 +526,13 @@ inline dynamic& dynamic::operator/=(dynamic const& o) {
   return *this;
 }
 
-#define FB_DYNAMIC_INTEGER_OP(op)                           \
-  inline dynamic& dynamic::operator op(dynamic const& o) {  \
-    if (!isInt() || !o.isInt()) {                           \
-      throw TypeError("int64", type(), o.type());           \
-    }                                                       \
-    *getAddress<int64_t>() op o.asInt();                    \
-    return *this;                                           \
+#define FB_DYNAMIC_INTEGER_OP(op)                          \
+  inline dynamic& dynamic::operator op(dynamic const& o) { \
+    if (!isInt() || !o.isInt()) {                          \
+      throwTypeError_("int64", type(), o.type());          \
+    }                                                      \
+    *getAddress<int64_t>() op o.asInt();                   \
+    return *this;                                          \
   }
 
 FB_DYNAMIC_INTEGER_OP(%=)
@@ -606,7 +615,7 @@ template<class K, class V> inline void dynamic::insert(K&& key, V&& val) {
 
 inline void dynamic::update(const dynamic& mergeObj) {
   if (!isObject() || !mergeObj.isObject()) {
-    throw TypeError("object", type(), mergeObj.type());
+    throwTypeError_("object", type(), mergeObj.type());
   }
 
   for (const auto& pair : mergeObj.items()) {
@@ -616,7 +625,7 @@ inline void dynamic::update(const dynamic& mergeObj) {
 
 inline void dynamic::update_missing(const dynamic& mergeObj1) {
   if (!isObject() || !mergeObj1.isObject()) {
-    throw TypeError("object", type(), mergeObj1.type());
+    throwTypeError_("object", type(), mergeObj1.type());
   }
 
   // Only add if not already there
@@ -735,7 +744,7 @@ T dynamic::asImpl() const {
   case STRING:
     return to<T>(*get_nothrow<std::string>());
   default:
-    throw TypeError("int/double/bool/string", type());
+    throwTypeError_("int/double/bool/string", type());
   }
 }
 
@@ -804,7 +813,7 @@ T& dynamic::get() {
   if (auto* p = get_nothrow<T>()) {
     return *p;
   }
-  throw TypeError(TypeInfo<T>::name, type());
+  throwTypeError_(TypeInfo<T>::name, type());
 }
 
 template<class T>
index 8ef7d08ee6e4159efc7f484dc8262c86f5d0cf09..08e97472832b4ac4f4cb069e766a9347fa1bca23 100644 (file)
@@ -17,6 +17,7 @@
 #include <folly/dynamic.h>
 
 #include <folly/Assume.h>
+#include <folly/Format.h>
 #include <folly/Hash.h>
 #include <folly/portability/BitsFunctexcept.h>
 
@@ -44,21 +45,36 @@ const char* dynamic::typeName() const {
 }
 
 TypeError::TypeError(const std::string& expected, dynamic::Type actual)
-  : std::runtime_error(to<std::string>("TypeError: expected dynamic "
-      "type `", expected, '\'', ", but had type `",
-      dynamic::typeName(actual), '\''))
-{}
-
-TypeError::TypeError(const std::string& expected,
-    dynamic::Type actual1, dynamic::Type actual2)
-  : std::runtime_error(to<std::string>("TypeError: expected dynamic "
-      "types `", expected, '\'', ", but had types `",
-      dynamic::typeName(actual1), "' and `", dynamic::typeName(actual2),
-      '\''))
-{}
+    : std::runtime_error(sformat(
+          "TypeError: expected dynamic type `{}', but had type `{}'",
+          expected,
+          dynamic::typeName(actual))) {}
+
+TypeError::TypeError(
+    const std::string& expected,
+    dynamic::Type actual1,
+    dynamic::Type actual2)
+    : std::runtime_error(sformat(
+          "TypeError: expected dynamic types `{}, but had types `{}' and `{}'",
+          expected,
+          dynamic::typeName(actual1),
+          dynamic::typeName(actual2))) {}
 
 TypeError::~TypeError() = default;
 
+[[noreturn]] void throwTypeError_(
+    std::string const& expected,
+    dynamic::Type actual) {
+  throw TypeError(expected, actual);
+}
+
+[[noreturn]] void throwTypeError_(
+    std::string const& expected,
+    dynamic::Type actual1,
+    dynamic::Type actual2) {
+  throw TypeError(expected, actual1, actual2);
+}
+
 // This is a higher-order preprocessor macro to aid going from runtime
 // types to the compile time type system.
 #define FB_DYNAMIC_APPLY(type, apply) \
@@ -93,7 +109,7 @@ TypeError::~TypeError() = default;
 
 bool dynamic::operator<(dynamic const& o) const {
   if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
-    throw TypeError("object", type_);
+    throwTypeError_("object", type_);
   }
   if (type_ != o.type_) {
     return type_ < o.type_;
@@ -156,7 +172,7 @@ dynamic& dynamic::operator=(dynamic&& o) noexcept {
 
 dynamic& dynamic::operator[](dynamic const& k) & {
   if (!isObject() && !isArray()) {
-    throw TypeError("object/array", type());
+    throwTypeError_("object/array", type());
   }
   if (isArray()) {
     return at(k);
@@ -203,7 +219,7 @@ dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) && {
 const dynamic* dynamic::get_ptr(dynamic const& idx) const& {
   if (auto* parray = get_nothrow<Array>()) {
     if (!idx.isInt()) {
-      throw TypeError("int64", idx.type());
+      throwTypeError_("int64", idx.type());
     }
     if (idx < 0 || idx >= parray->size()) {
       return nullptr;
@@ -216,14 +232,19 @@ const dynamic* dynamic::get_ptr(dynamic const& idx) const& {
     }
     return &it->second;
   } else {
-    throw TypeError("object/array", type());
+    throwTypeError_("object/array", type());
   }
 }
 
+[[noreturn]] static void throwOutOfRangeAtMissingKey(dynamic const& idx) {
+  auto msg = sformat("couldn't find key {} in dynamic object", idx.asString());
+  std::__throw_out_of_range(msg.c_str());
+}
+
 dynamic const& dynamic::at(dynamic const& idx) const& {
   if (auto* parray = get_nothrow<Array>()) {
     if (!idx.isInt()) {
-      throw TypeError("int64", idx.type());
+      throwTypeError_("int64", idx.type());
     }
     if (idx < 0 || idx >= parray->size()) {
       std::__throw_out_of_range("out of range in dynamic array");
@@ -232,12 +253,11 @@ dynamic const& dynamic::at(dynamic const& idx) const& {
   } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
     auto it = pobject->find(idx);
     if (it == pobject->end()) {
-      throw std::out_of_range(to<std::string>(
-          "couldn't find key ", idx.asString(), " in dynamic object"));
+      throwOutOfRangeAtMissingKey(idx);
     }
     return it->second;
   } else {
-    throw TypeError("object/array", type());
+    throwTypeError_("object/array", type());
   }
 }
 
@@ -251,7 +271,7 @@ std::size_t dynamic::size() const {
   if (auto* str = get_nothrow<std::string>()) {
     return str->size();
   }
-  throw TypeError("array/object", type());
+  throwTypeError_("array/object", type());
 }
 
 dynamic::iterator dynamic::erase(const_iterator first, const_iterator last) {
@@ -266,7 +286,7 @@ std::size_t dynamic::hash() const {
   case OBJECT:
   case ARRAY:
   case NULLT:
-    throw TypeError("not null/object/array", type());
+    throwTypeError_("not null/object/array", type());
   case INT64:
     return std::hash<int64_t>()(getInt());
   case DOUBLE: