mirror of
https://github.com/nlohmann/json.git
synced 2026-10-01 04:00:31 +00:00
Deduplicate the null-to-container conversion into convert_null_to()
Nine sites wrote out the same "turn a null value into an empty array or object" logic with two different idioms: operator[](size_type), operator[](key_type), operator[](KeyType&&) and update() set m_type then assigned m_value.array/object directly via create<T>(), while the three push_back() overloads, emplace_back() and emplace() set m_type then assigned m_value = value_t::array/object (going through json_value's converting constructor and a temporary). Both idioms end up calling create<T>() and produce the same state, just via a different path; both also share a latent exception-safety bug, since m_type is written before the (possibly throwing) allocation, so a throwing allocator leaves m_type == array/object with a null pointer behind it, violating the class invariant and crashing on the next access to, or destruction of, the value. Add a private convert_null_to(value_t) helper and call it from all nine sites. Unlike the idioms it replaces, it allocates the container first and only then writes m_type, so a throwing allocation leaves the value as a valid null instead of a mistyped, half-constructed one; verified with a throwing allocator (see unit-allocator.cpp's bad_allocator) that j["x"] = ... on a null j now stays null, and no longer trips assert_invariant()/crashes, when create<object_t>() throws. Same allocator usage and assert_invariant() call as before, otherwise. Overlaps #5585, which reorders these same nine blocks for exception safety; whichever of this commit and that PR lands second will need a small rebase. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
+29
-27
@@ -2741,6 +2741,26 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return (*j.m_data.m_value.array)[idx];
|
||||
}
|
||||
|
||||
/// @brief convert a null value to an empty container of type @a t (array or object)
|
||||
void convert_null_to(value_t t)
|
||||
{
|
||||
JSON_ASSERT(is_null());
|
||||
JSON_ASSERT(t == value_t::array || t == value_t::object);
|
||||
// create the container before touching the type, so a throwing
|
||||
// allocation leaves this value as a valid null rather than a type
|
||||
// tag with a dangling/null pointer behind it
|
||||
if (t == value_t::array)
|
||||
{
|
||||
m_data.m_value.array = create<array_t>();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_data.m_value.object = create<object_t>();
|
||||
}
|
||||
m_data.m_type = t;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
public:
|
||||
////////////////////
|
||||
// element access //
|
||||
@@ -2803,9 +2823,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value.array = create<array_t>();
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::array);
|
||||
}
|
||||
|
||||
// operator[] only works for arrays
|
||||
@@ -2863,9 +2881,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::object);
|
||||
}
|
||||
|
||||
// operator[] only works for objects
|
||||
@@ -2916,9 +2932,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::object);
|
||||
}
|
||||
|
||||
// operator[] only works for objects
|
||||
@@ -3790,9 +3804,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::array);
|
||||
}
|
||||
|
||||
// add the element to the array (move semantics)
|
||||
@@ -3823,9 +3835,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::array);
|
||||
}
|
||||
|
||||
// add the element to the array
|
||||
@@ -3855,9 +3865,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value = value_t::object;
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::object);
|
||||
}
|
||||
|
||||
// add the element to the object
|
||||
@@ -3911,9 +3919,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::array);
|
||||
}
|
||||
|
||||
// add the element to the array (perfect forwarding)
|
||||
@@ -3936,9 +3942,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value = value_t::object;
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::object);
|
||||
}
|
||||
|
||||
// add the element to the array (perfect forwarding)
|
||||
@@ -4133,9 +4137,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::object);
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
||||
|
||||
@@ -28821,6 +28821,26 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return (*j.m_data.m_value.array)[idx];
|
||||
}
|
||||
|
||||
/// @brief convert a null value to an empty container of type @a t (array or object)
|
||||
void convert_null_to(value_t t)
|
||||
{
|
||||
JSON_ASSERT(is_null());
|
||||
JSON_ASSERT(t == value_t::array || t == value_t::object);
|
||||
// create the container before touching the type, so a throwing
|
||||
// allocation leaves this value as a valid null rather than a type
|
||||
// tag with a dangling/null pointer behind it
|
||||
if (t == value_t::array)
|
||||
{
|
||||
m_data.m_value.array = create<array_t>();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_data.m_value.object = create<object_t>();
|
||||
}
|
||||
m_data.m_type = t;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
public:
|
||||
////////////////////
|
||||
// element access //
|
||||
@@ -28883,9 +28903,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value.array = create<array_t>();
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::array);
|
||||
}
|
||||
|
||||
// operator[] only works for arrays
|
||||
@@ -28943,9 +28961,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::object);
|
||||
}
|
||||
|
||||
// operator[] only works for objects
|
||||
@@ -28996,9 +29012,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::object);
|
||||
}
|
||||
|
||||
// operator[] only works for objects
|
||||
@@ -29870,9 +29884,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::array);
|
||||
}
|
||||
|
||||
// add the element to the array (move semantics)
|
||||
@@ -29903,9 +29915,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::array);
|
||||
}
|
||||
|
||||
// add the element to the array
|
||||
@@ -29935,9 +29945,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value = value_t::object;
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::object);
|
||||
}
|
||||
|
||||
// add the element to the object
|
||||
@@ -29991,9 +29999,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::array);
|
||||
}
|
||||
|
||||
// add the element to the array (perfect forwarding)
|
||||
@@ -30016,9 +30022,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value = value_t::object;
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::object);
|
||||
}
|
||||
|
||||
// add the element to the array (perfect forwarding)
|
||||
@@ -30213,9 +30217,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
assert_invariant();
|
||||
convert_null_to(value_t::object);
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
||||
|
||||
Reference in New Issue
Block a user