1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-08 05:27:14 +09:00

AK+Everywhere: Convert JSON value serialization to String

This removes the use of StringBuilder::OutputType (which was ByteString,
and only used by the JSON classes). And it removes the StringBuilder
template parameter from the serialization methods; this was only ever
used with StringBuilder, so a template is pretty overkill here.
This commit is contained in:
Timothy Flynn 2025-02-17 15:08:17 -05:00 committed by Tim Flynn
parent 2c03de60da
commit fe2dff4944
Notes: github-actions[bot] 2025-02-21 00:28:53 +00:00
15 changed files with 98 additions and 95 deletions

View file

@ -13,6 +13,7 @@ set(SOURCES
Format.cpp Format.cpp
GenericLexer.cpp GenericLexer.cpp
Hex.cpp Hex.cpp
JsonArray.cpp
JsonObject.cpp JsonObject.cpp
JsonParser.cpp JsonParser.cpp
JsonValue.cpp JsonValue.cpp

30
AK/JsonArray.cpp Normal file
View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/JsonArray.h>
#include <AK/JsonArraySerializer.h>
#include <AK/StringBuilder.h>
namespace AK {
String JsonArray::serialized() const
{
StringBuilder builder;
serialize(builder);
return MUST(builder.to_string());
}
void JsonArray::serialize(StringBuilder& builder) const
{
auto serializer = MUST(JsonArraySerializer<>::try_create(builder));
for_each([&](auto const& value) {
MUST(serializer.add(value));
});
MUST(serializer.finish());
}
}

View file

@ -8,8 +8,8 @@
#include <AK/Concepts.h> #include <AK/Concepts.h>
#include <AK/Error.h> #include <AK/Error.h>
#include <AK/JsonArraySerializer.h>
#include <AK/JsonValue.h> #include <AK/JsonValue.h>
#include <AK/String.h>
#include <AK/Vector.h> #include <AK/Vector.h>
namespace AK { namespace AK {
@ -72,11 +72,8 @@ public:
ErrorOr<void> append(JsonValue value) { return m_values.try_append(move(value)); } ErrorOr<void> append(JsonValue value) { return m_values.try_append(move(value)); }
void set(size_t index, JsonValue value) { m_values.at(index) = move(value); } void set(size_t index, JsonValue value) { m_values.at(index) = move(value); }
template<typename Builder> String serialized() const;
typename Builder::OutputType serialized() const; void serialize(StringBuilder&) const;
template<typename Builder>
void serialize(Builder&) const;
template<typename Callback> template<typename Callback>
void for_each(Callback callback) void for_each(Callback callback)
@ -118,22 +115,6 @@ private:
Vector<JsonValue> m_values; Vector<JsonValue> m_values;
}; };
template<typename Builder>
inline void JsonArray::serialize(Builder& builder) const
{
auto serializer = MUST(JsonArraySerializer<>::try_create(builder));
for_each([&](auto& value) { MUST(serializer.add(value)); });
MUST(serializer.finish());
}
template<typename Builder>
inline typename Builder::OutputType JsonArray::serialized() const
{
Builder builder;
serialize(builder);
return builder.to_byte_string();
}
} }
#if USING_AK_GLOBALLY #if USING_AK_GLOBALLY

View file

@ -6,7 +6,9 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include "JsonObject.h" #include <AK/JsonObject.h>
#include <AK/JsonObjectSerializer.h>
#include <AK/StringBuilder.h>
namespace AK { namespace AK {
@ -275,4 +277,21 @@ bool JsonObject::remove(StringView key)
return m_members.remove(key); return m_members.remove(key);
} }
String JsonObject::serialized() const
{
StringBuilder builder;
serialize(builder);
return MUST(builder.to_string());
}
void JsonObject::serialize(StringBuilder& builder) const
{
auto serializer = MUST(JsonObjectSerializer<>::try_create(builder));
for_each_member([&](auto& key, auto& value) {
MUST(serializer.add(key, value));
});
MUST(serializer.finish());
}
} }

View file

@ -8,12 +8,10 @@
#pragma once #pragma once
#include <AK/ByteString.h>
#include <AK/Concepts.h> #include <AK/Concepts.h>
#include <AK/Error.h> #include <AK/Error.h>
#include <AK/HashMap.h> #include <AK/HashMap.h>
#include <AK/JsonArray.h> #include <AK/JsonArray.h>
#include <AK/JsonObjectSerializer.h>
#include <AK/JsonValue.h> #include <AK/JsonValue.h>
#include <AK/String.h> #include <AK/String.h>
@ -107,59 +105,13 @@ public:
bool remove(StringView key); bool remove(StringView key);
template<typename Builder> String serialized() const;
typename Builder::OutputType serialized() const; void serialize(StringBuilder&) const;
template<typename Builder>
void serialize(Builder&) const;
private: private:
OrderedHashMap<String, JsonValue> m_members; OrderedHashMap<String, JsonValue> m_members;
}; };
template<typename Builder>
inline void JsonObject::serialize(Builder& builder) const
{
auto serializer = MUST(JsonObjectSerializer<>::try_create(builder));
for_each_member([&](auto& key, auto& value) {
MUST(serializer.add(key, value));
});
MUST(serializer.finish());
}
template<typename Builder>
inline typename Builder::OutputType JsonObject::serialized() const
{
Builder builder;
serialize(builder);
return builder.to_byte_string();
}
template<typename Builder>
inline void JsonValue::serialize(Builder& builder) const
{
m_value.visit(
[&](Empty const&) { builder.append("null"sv); },
[&](bool const& value) { builder.append(value ? "true"sv : "false"sv); },
[&](Arithmetic auto const& value) { builder.appendff("{}", value); },
[&](String const& value) {
builder.append('\"');
builder.append_escaped_for_json(value.bytes());
builder.append('\"');
},
[&](auto const& array_or_object) {
array_or_object->serialize(builder);
});
}
template<typename Builder>
inline typename Builder::OutputType JsonValue::serialized() const
{
Builder builder;
serialize(builder);
return builder.to_byte_string();
}
} }
#if USING_AK_GLOBALLY #if USING_AK_GLOBALLY

View file

@ -9,6 +9,7 @@
#include <AK/JsonObject.h> #include <AK/JsonObject.h>
#include <AK/JsonParser.h> #include <AK/JsonParser.h>
#include <AK/JsonValue.h> #include <AK/JsonValue.h>
#include <AK/StringBuilder.h>
#include <AK/StringView.h> #include <AK/StringView.h>
namespace AK { namespace AK {
@ -192,4 +193,28 @@ ErrorOr<JsonValue> JsonValue::from_string(StringView input)
return JsonParser(input).parse(); return JsonParser(input).parse();
} }
String JsonValue::serialized() const
{
StringBuilder builder;
serialize(builder);
return MUST(builder.to_string());
}
void JsonValue::serialize(StringBuilder& builder) const
{
m_value.visit(
[&](Empty const&) { builder.append("null"sv); },
[&](bool const& value) { builder.append(value ? "true"sv : "false"sv); },
[&](Arithmetic auto const& value) { builder.appendff("{}", value); },
[&](String const& value) {
builder.append('\"');
builder.append_escaped_for_json(value.bytes());
builder.append('\"');
},
[&](auto const& array_or_object) {
array_or_object->serialize(builder);
});
}
} }

View file

@ -11,7 +11,6 @@
#include <AK/NonnullOwnPtr.h> #include <AK/NonnullOwnPtr.h>
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/String.h> #include <AK/String.h>
#include <AK/StringBuilder.h>
namespace AK { namespace AK {
@ -77,10 +76,8 @@ public:
JsonValue& operator=(JsonArray&&); JsonValue& operator=(JsonArray&&);
JsonValue& operator=(JsonObject&&); JsonValue& operator=(JsonObject&&);
template<typename Builder> String serialized() const;
typename Builder::OutputType serialized() const; void serialize(StringBuilder&) const;
template<typename Builder>
void serialize(Builder&) const;
Optional<int> get_int() const { return get_integer<int>(); } Optional<int> get_int() const { return get_integer<int>(); }
Optional<i32> get_i32() const { return get_integer<i32>(); } Optional<i32> get_i32() const { return get_integer<i32>(); }
@ -224,7 +221,7 @@ template<>
struct Formatter<JsonValue> : Formatter<StringView> { struct Formatter<JsonValue> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, JsonValue const& value) ErrorOr<void> format(FormatBuilder& builder, JsonValue const& value)
{ {
return Formatter<StringView>::format(builder, value.serialized<StringBuilder>()); return Formatter<StringView>::format(builder, value.serialized());
} }
}; };

View file

@ -19,7 +19,6 @@ public:
static constexpr size_t inline_capacity = 256; static constexpr size_t inline_capacity = 256;
using Buffer = Detail::ByteBuffer<inline_capacity>; using Buffer = Detail::ByteBuffer<inline_capacity>;
using OutputType = ByteString;
static ErrorOr<StringBuilder> create(size_t initial_capacity = inline_capacity); static ErrorOr<StringBuilder> create(size_t initial_capacity = inline_capacity);

View file

@ -754,7 +754,7 @@ void ArgsParser::autocomplete(FILE* file, StringView program_name, ReadonlySpan<
object.set("invariant_offset"sv, has_invariant ? option_to_complete.length() : 0u); object.set("invariant_offset"sv, has_invariant ? option_to_complete.length() : 0u);
object.set("display_trivia"sv, StringView { option.help_string, strlen(option.help_string) }); object.set("display_trivia"sv, StringView { option.help_string, strlen(option.help_string) });
object.set("trailing_trivia"sv, option.argument_mode == OptionArgumentMode::Required ? " "sv : ""sv); object.set("trailing_trivia"sv, option.argument_mode == OptionArgumentMode::Required ? " "sv : ""sv);
outln(file, "{}", object.serialized<StringBuilder>()); outln(file, "{}", object.serialized());
}; };
if (option_to_complete.starts_with("--"sv)) { if (option_to_complete.starts_with("--"sv)) {

View file

@ -7,7 +7,6 @@
#include <AK/Debug.h> #include <AK/Debug.h>
#include <AK/JsonObject.h> #include <AK/JsonObject.h>
#include <AK/JsonValue.h> #include <AK/JsonValue.h>
#include <AK/StringBuilder.h>
#include <LibCore/EventLoop.h> #include <LibCore/EventLoop.h>
#include <LibDevTools/Connection.h> #include <LibDevTools/Connection.h>
@ -34,7 +33,7 @@ Connection::~Connection() = default;
// https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#packets // https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#packets
void Connection::send_message(JsonValue const& message) void Connection::send_message(JsonValue const& message)
{ {
auto serialized = message.serialized<StringBuilder>(); auto serialized = message.serialized();
if constexpr (DEVTOOLS_DEBUG) { if constexpr (DEVTOOLS_DEBUG) {
if (message.is_object() && message.as_object().get("error"sv).has_value()) if (message.is_object() && message.as_object().get("error"sv).has_value())
@ -43,7 +42,7 @@ void Connection::send_message(JsonValue const& message)
dbgln("\x1b[1;32m<<\x1b[0m {}", serialized); dbgln("\x1b[1;32m<<\x1b[0m {}", serialized);
} }
if (m_socket->write_formatted("{}:{}", serialized.length(), serialized).is_error()) { if (m_socket->write_formatted("{}:{}", serialized.byte_count(), serialized).is_error()) {
if (on_connection_closed) if (on_connection_closed)
on_connection_closed(); on_connection_closed();
} }

View file

@ -82,7 +82,7 @@ ErrorOr<void> encode(Encoder& encoder, ByteBuffer const& value)
template<> template<>
ErrorOr<void> encode(Encoder& encoder, JsonValue const& value) ErrorOr<void> encode(Encoder& encoder, JsonValue const& value)
{ {
return encoder.encode(value.serialized<StringBuilder>()); return encoder.encode(value.serialized());
} }
template<> template<>

View file

@ -274,7 +274,7 @@ inline void TestRunner::print_test_results_as_json() const
root.set("files_total"sv, m_counts.files_total); root.set("files_total"sv, m_counts.files_total);
root.set("duration"sv, m_total_elapsed_time_in_ms / 1000.0); root.set("duration"sv, m_total_elapsed_time_in_ms / 1000.0);
} }
outln("{}", root.serialized<StringBuilder>()); outln("{}", root.serialized());
} }
} }

View file

@ -298,7 +298,7 @@ ErrorOr<void, Client::WrappedError> Client::send_success_response(HTTP::HttpRequ
keep_alive = it->value.trim_whitespace().equals_ignoring_ascii_case("keep-alive"sv); keep_alive = it->value.trim_whitespace().equals_ignoring_ascii_case("keep-alive"sv);
result = make_success_response(move(result)); result = make_success_response(move(result));
auto content = result.serialized<StringBuilder>(); auto content = result.serialized();
StringBuilder builder; StringBuilder builder;
builder.append("HTTP/1.1 200 OK\r\n"sv); builder.append("HTTP/1.1 200 OK\r\n"sv);
@ -309,7 +309,7 @@ ErrorOr<void, Client::WrappedError> Client::send_success_response(HTTP::HttpRequ
builder.append("Connection: keep-alive\r\n"sv); builder.append("Connection: keep-alive\r\n"sv);
builder.append("Cache-Control: no-cache\r\n"sv); builder.append("Cache-Control: no-cache\r\n"sv);
builder.append("Content-Type: application/json; charset=utf-8\r\n"sv); builder.append("Content-Type: application/json; charset=utf-8\r\n"sv);
builder.appendff("Content-Length: {}\r\n", content.length()); builder.appendff("Content-Length: {}\r\n", content.byte_count());
builder.append("\r\n"sv); builder.append("\r\n"sv);
builder.append(content); builder.append(content);
@ -338,13 +338,13 @@ ErrorOr<void, Client::WrappedError> Client::send_error_response(HTTP::HttpReques
JsonObject result; JsonObject result;
result.set("value"sv, move(error_response)); result.set("value"sv, move(error_response));
auto content = result.serialized<StringBuilder>(); auto content = result.serialized();
StringBuilder builder; StringBuilder builder;
builder.appendff("HTTP/1.1 {} {}\r\n", error.http_status, reason); builder.appendff("HTTP/1.1 {} {}\r\n", error.http_status, reason);
builder.append("Cache-Control: no-cache\r\n"sv); builder.append("Cache-Control: no-cache\r\n"sv);
builder.append("Content-Type: application/json; charset=utf-8\r\n"sv); builder.append("Content-Type: application/json; charset=utf-8\r\n"sv);
builder.appendff("Content-Length: {}\r\n", content.length()); builder.appendff("Content-Length: {}\r\n", content.byte_count());
builder.append("\r\n"sv); builder.append("\r\n"sv);
builder.append(content); builder.append(content);

View file

@ -160,13 +160,13 @@ TEST_CASE(json_duplicate_keys)
json.set("test"sv, "foo"sv); json.set("test"sv, "foo"sv);
json.set("test"sv, "bar"sv); json.set("test"sv, "bar"sv);
json.set("test"sv, "baz"sv); json.set("test"sv, "baz"sv);
EXPECT_EQ(json.serialized<StringBuilder>(), "{\"test\":\"baz\"}"); EXPECT_EQ(json.serialized(), "{\"test\":\"baz\"}");
} }
TEST_CASE(json_u64_roundtrip) TEST_CASE(json_u64_roundtrip)
{ {
auto big_value = 0xffffffffffffffffull; auto big_value = 0xffffffffffffffffull;
auto json = JsonValue(big_value).serialized<StringBuilder>(); auto json = JsonValue(big_value).serialized();
auto value = JsonValue::from_string(json); auto value = JsonValue::from_string(json);
EXPECT_EQ_FORCE(value.is_error(), false); EXPECT_EQ_FORCE(value.is_error(), false);
EXPECT_EQ(value.value().as_integer<u64>(), big_value); EXPECT_EQ(value.value().as_integer<u64>(), big_value);
@ -531,7 +531,7 @@ TEST_CASE(json_array_serialized)
auto raw_json = R"(["Hello",2,3.14,4,"World"])"sv; auto raw_json = R"(["Hello",2,3.14,4,"World"])"sv;
auto json_value = MUST(JsonValue::from_string(raw_json)); auto json_value = MUST(JsonValue::from_string(raw_json));
auto array = json_value.as_array(); auto array = json_value.as_array();
auto const& serialized_json = array.serialized<StringBuilder>(); auto const& serialized_json = array.serialized();
EXPECT_EQ(serialized_json, raw_json); EXPECT_EQ(serialized_json, raw_json);
} }

View file

@ -532,7 +532,7 @@ static bool g_in_assert = false;
assert_fail_result.set("assert_fail"sv, true); assert_fail_result.set("assert_fail"sv, true);
assert_fail_result.set("result"sv, "assert_fail"sv); assert_fail_result.set("result"sv, "assert_fail"sv);
assert_fail_result.set("output"sv, StringView { assert_failed_message, strlen(assert_failed_message) }); assert_fail_result.set("output"sv, StringView { assert_failed_message, strlen(assert_failed_message) });
outln(saved_stdout_fd, "RESULT {}{}", assert_fail_result.serialized<StringBuilder>(), '\0'); outln(saved_stdout_fd, "RESULT {}{}", assert_fail_result.serialized(), '\0');
// (Attempt to) Ensure that messages are written before quitting. // (Attempt to) Ensure that messages are written before quitting.
fflush(saved_stdout_fd); fflush(saved_stdout_fd);
fflush(stderr); fflush(stderr);
@ -733,7 +733,7 @@ int main(int argc, char** argv)
result_object.set("test"sv, path); result_object.set("test"sv, path);
ScopeGuard output_guard = [&] { ScopeGuard output_guard = [&] {
outln(saved_stdout_fd, "RESULT {}{}", result_object.serialized<StringBuilder>(), '\0'); outln(saved_stdout_fd, "RESULT {}{}", result_object.serialized(), '\0');
fflush(saved_stdout_fd); fflush(saved_stdout_fd);
}; };