AK+Everywhere: Remove now-unecessary use of ByteString with JSON types

This removes JsonObject::get_byte_string and JsonObject::to_byte_string.
This commit is contained in:
Timothy Flynn 2025-02-17 15:04:56 -05:00 committed by Tim Flynn
parent 4791ec35bf
commit 2c03de60da
Notes: github-actions[bot] 2025-02-21 00:28:59 +00:00
17 changed files with 67 additions and 86 deletions

View file

@ -123,13 +123,6 @@ Optional<String const&> JsonObject::get_string(StringView key) const
return {};
}
Optional<ByteString> JsonObject::get_byte_string(StringView key) const
{
if (auto value = get_string(key); value.has_value())
return value->to_byte_string();
return {};
}
Optional<JsonObject&> JsonObject::get_object(StringView key)
{
auto maybe_value = get(key);
@ -282,9 +275,4 @@ bool JsonObject::remove(StringView key)
return m_members.remove(key);
}
ByteString JsonObject::to_byte_string() const
{
return serialized<StringBuilder>();
}
}

View file

@ -77,7 +77,6 @@ public:
Optional<bool> get_bool(StringView key) const;
Optional<String const&> get_string(StringView key) const;
Optional<ByteString> get_byte_string(StringView key) const;
Optional<JsonObject&> get_object(StringView key);
Optional<JsonObject const&> get_object(StringView key) const;
@ -114,8 +113,6 @@ public:
template<typename Builder>
void serialize(Builder&) const;
[[nodiscard]] ByteString to_byte_string() const;
private:
OrderedHashMap<String, JsonValue> m_members;
};

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("display_trivia"sv, StringView { option.help_string, strlen(option.help_string) });
object.set("trailing_trivia"sv, option.argument_mode == OptionArgumentMode::Required ? " "sv : ""sv);
outln(file, "{}", object.to_byte_string());
outln(file, "{}", object.serialized<StringBuilder>());
};
if (option_to_complete.starts_with("--"sv)) {

View file

@ -376,12 +376,12 @@ inline JSFileResult TestRunner::run_file_test(ByteString const& test_path)
VERIFY(suite_value.is_object());
suite_value.as_object().for_each_member([&](String const& test_name, JsonValue const& test_value) {
Test::Case test { test_name, Test::Result::Fail, "", 0 };
Test::Case test { test_name, Test::Result::Fail, {}, 0 };
VERIFY(test_value.is_object());
VERIFY(test_value.as_object().has("result"sv));
auto result = test_value.as_object().get_byte_string("result"sv);
auto result = test_value.as_object().get_string("result"sv);
VERIFY(result.has_value());
auto result_string = result.value();
if (result_string == "pass") {
@ -392,9 +392,9 @@ inline JSFileResult TestRunner::run_file_test(ByteString const& test_path)
m_counts.tests_failed++;
suite.most_severe_test_result = Test::Result::Fail;
VERIFY(test_value.as_object().has("details"sv));
auto details = test_value.as_object().get_byte_string("details"sv);
auto details = test_value.as_object().get_string("details"sv);
VERIFY(result.has_value());
test.details = details.value();
test.details = details.release_value();
} else if (result_string == "xfail") {
test.result = Test::Result::ExpectedFail;
m_counts.tests_expected_failed++;
@ -430,7 +430,7 @@ inline JSFileResult TestRunner::run_file_test(ByteString const& test_path)
Test::Suite suite { test_path, "<top-level>"_string };
suite.most_severe_test_result = Result::Crashed;
Test::Case test_case { "<top-level>"_string, Test::Result::Fail, "", 0 };
Test::Case test_case { "<top-level>"_string, Test::Result::Fail, {}, 0 };
auto error = top_level_result.release_error().release_value().release_value();
if (error.is_object()) {
StringBuilder detail_builder;
@ -453,9 +453,9 @@ inline JSFileResult TestRunner::run_file_test(ByteString const& test_path)
detail_builder.append(error_as_error.stack_string());
}
test_case.details = detail_builder.to_byte_string();
test_case.details = MUST(detail_builder.to_string());
} else {
test_case.details = error.to_string_without_side_effects().to_byte_string();
test_case.details = error.to_string_without_side_effects();
}
suite.tests.append(move(test_case));

View file

@ -26,7 +26,7 @@ enum class Result {
struct Case {
String name;
Result result;
ByteString details;
String details;
u64 duration_us;
};

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("duration"sv, m_total_elapsed_time_in_ms / 1000.0);
}
outln("{}", root.to_byte_string());
outln("{}", root.serialized<StringBuilder>());
}
}

View file

@ -15,9 +15,9 @@
namespace Web::Bindings {
#define JWK_PARSE_STRING_PROPERTY(name) \
if (json_object.has_string(#name##sv)) { \
key.name = MUST(String::from_byte_string(*json_object.get_byte_string(#name##sv))); \
#define JWK_PARSE_STRING_PROPERTY(name) \
if (auto value = json_object.get_string(#name##sv); value.has_value()) { \
key.name = value.release_value(); \
}
JS::ThrowCompletionOr<JsonWebKey> JsonWebKey::parse(JS::Realm& realm, ReadonlyBytes data)

View file

@ -546,7 +546,7 @@ template<typename Generator>
static void generate_tree(StringBuilder& builder, JsonObject const& node, Generator&& generator)
{
if (auto children = node.get_array("children"sv); children.has_value() && !children->is_empty()) {
auto name = node.get_byte_string("name"sv).value_or({});
auto name = node.get_string("name"sv).value_or({});
builder.append("<details>"sv);
builder.append("<summary>"sv);
@ -570,8 +570,8 @@ String InspectorClient::generate_dom_tree(JsonObject const& dom_tree)
StringBuilder builder;
generate_tree(builder, dom_tree, [&](JsonObject const& node) {
auto type = node.get_byte_string("type"sv).value_or("unknown"sv);
auto name = node.get_byte_string("name"sv).value_or({});
auto type = node.get_string("type"sv).value_or("unknown"_string);
auto name = node.get_string("name"sv).value_or({});
StringBuilder data_attributes;
auto append_data_attribute = [&](auto name, auto value) {
@ -592,10 +592,9 @@ String InspectorClient::generate_dom_tree(JsonObject const& dom_tree)
append_data_attribute("id"sv, node_id);
if (type == "text"sv) {
auto deprecated_text = node.get_byte_string("text"sv).release_value();
deprecated_text = escape_html_entities(deprecated_text);
auto deprecated_text = escape_html_entities(*node.get_string("text"sv));
auto text = MUST(Web::Infra::strip_and_collapse_whitespace(deprecated_text));
builder.appendff("<span data-node-type=\"text\" class=\"hoverable editable\" {}>", data_attributes.string_view());
if (text.is_empty())
@ -608,8 +607,7 @@ String InspectorClient::generate_dom_tree(JsonObject const& dom_tree)
}
if (type == "comment"sv) {
auto comment = node.get_byte_string("data"sv).release_value();
comment = escape_html_entities(comment);
auto comment = escape_html_entities(*node.get_string("data"sv));
builder.appendff("<span class=\"hoverable comment\" {}>", data_attributes.string_view());
builder.append("<span>&lt;!--</span>"sv);
@ -620,7 +618,7 @@ String InspectorClient::generate_dom_tree(JsonObject const& dom_tree)
}
if (type == "shadow-root"sv) {
auto mode = node.get_byte_string("mode"sv).release_value();
auto mode = node.get_string("mode"sv).release_value();
builder.appendff("<span class=\"hoverable internal\" {}>", data_attributes.string_view());
builder.appendff("{} ({})", name, mode);
@ -636,8 +634,8 @@ String InspectorClient::generate_dom_tree(JsonObject const& dom_tree)
m_body_or_frameset_node_id = node_id;
auto tag = name;
if (node.get_byte_string("namespace"sv) == Web::Namespace::HTML.bytes_as_string_view())
tag = tag.to_lowercase();
if (node.get_string("namespace"sv) == Web::Namespace::HTML.bytes_as_string_view())
tag = MUST(tag.to_lowercase());
builder.appendff("<span class=\"hoverable\" {}>", data_attributes.string_view());
builder.append("<span>&lt;</span>"sv);
@ -693,12 +691,11 @@ String InspectorClient::generate_accessibility_tree(JsonObject const& accessibil
StringBuilder builder;
generate_tree(builder, accessibility_tree, [&](JsonObject const& node) {
auto type = node.get_byte_string("type"sv).value_or("unknown"sv);
auto role = node.get_byte_string("role"sv).value_or({});
auto type = node.get_string("type"sv).value_or("unknown"_string);
auto role = node.get_string("role"sv).value_or({});
if (type == "text"sv) {
auto text = node.get_byte_string("text"sv).release_value();
text = escape_html_entities(text);
auto text = escape_html_entities(*node.get_string("text"sv));
builder.appendff("<span class=\"hoverable\">");
builder.append(MUST(Web::Infra::strip_and_collapse_whitespace(text)));
@ -708,16 +705,16 @@ String InspectorClient::generate_accessibility_tree(JsonObject const& accessibil
if (type != "element"sv) {
builder.appendff("<span class=\"hoverable internal\">");
builder.appendff(role.to_lowercase());
builder.appendff(MUST(role.to_lowercase()));
builder.append("</span>"sv);
return;
}
auto name = node.get_byte_string("name"sv).value_or({});
auto description = node.get_byte_string("description"sv).value_or({});
auto name = node.get_string("name"sv).value_or({});
auto description = node.get_string("description"sv).value_or({});
builder.appendff("<span class=\"hoverable\">");
builder.append(role.to_lowercase());
builder.append(MUST(role.to_lowercase()));
builder.appendff(" name: \"{}\", description: \"{}\"", name, description);
builder.append("</span>"sv);
});

View file

@ -29,8 +29,8 @@ namespace Web::ARIA {
JsonObject const& value_object = value.as_object();
auto class_definition_generator = generator.fork();
class_definition_generator.set("spec_link"sv, value_object.get_byte_string("specLink"sv).value());
class_definition_generator.set("description"sv, value_object.get_byte_string("description"sv).value());
class_definition_generator.set("spec_link"sv, value_object.get_string("specLink"sv).release_value());
class_definition_generator.set("description"sv, value_object.get_string("description"sv).release_value());
class_definition_generator.set("name"sv, name);
class_definition_generator.append(R"~~~(
// @spec_link@

View file

@ -126,7 +126,7 @@ RefPtr<CalculationNode> Parser::parse_math_function(Function const& function, Ca
functions_data.for_each_member([&](auto& name, JsonValue const& value) -> void {
auto& function_data = value.as_object();
auto& parameters = function_data.get_array("parameters"sv).value();
auto parameter_validation_rule = function_data.get_byte_string("parameter-validation"sv);
auto parameter_validation_rule = function_data.get_string("parameter-validation"sv);
bool requires_same_parameters = parameter_validation_rule.has_value() ? (parameter_validation_rule == "same"sv) : true;
auto function_generator = generator.fork();
@ -158,7 +158,7 @@ RefPtr<CalculationNode> Parser::parse_math_function(Function const& function, Ca
// Generate some type checks
VERIFY(parameters.size() == 1);
auto& parameter_data = parameters[0].as_object();
auto parameter_type_string = parameter_data.get_byte_string("type"sv).value();
auto parameter_type_string = parameter_data.get_string("type"sv).value();
function_generator.set("type_check", generate_calculation_type_check("argument_type"sv, parameter_type_string));
function_generator.append(R"~~~(
if (!(@type_check@)) {
@ -221,11 +221,11 @@ RefPtr<CalculationNode> Parser::parse_math_function(Function const& function, Ca
size_t parameter_index = 0;
parameters.for_each([&](JsonValue const& parameter_value) {
auto& parameter = parameter_value.as_object();
auto parameter_type_string = parameter.get_byte_string("type"sv).value();
auto parameter_type_string = parameter.get_string("type"sv).value();
auto parameter_required = parameter.get_bool("required"sv).value();
auto parameter_generator = function_generator.fork();
parameter_generator.set("parameter_name", parameter.get_byte_string("name"sv).value());
parameter_generator.set("parameter_name", parameter.get_string("name"sv).value());
parameter_generator.set("parameter_index", String::number(parameter_index));
bool parameter_is_calculation;
@ -235,7 +235,7 @@ RefPtr<CalculationNode> Parser::parse_math_function(Function const& function, Ca
parameter_generator.set("parse_function", "parse_rounding_strategy(arguments[argument_index])"_string);
parameter_generator.set("check_function", ".has_value()"_string);
parameter_generator.set("release_function", ".release_value()"_string);
if (auto default_value = parameter.get_byte_string("default"sv); default_value.has_value()) {
if (auto default_value = parameter.get_string("default"sv); default_value.has_value()) {
parameter_generator.set("parameter_default", MUST(String::formatted(" = RoundingStrategy::{}", title_casify(default_value.value()))));
} else {
parameter_generator.set("parameter_default", ""_string);
@ -250,7 +250,7 @@ RefPtr<CalculationNode> Parser::parse_math_function(Function const& function, Ca
// NOTE: We have exactly one default value in the data right now, and it's a `<calc-constant>`,
// so that's all we handle.
if (auto default_value = parameter.get_byte_string("default"sv); default_value.has_value()) {
if (auto default_value = parameter.get_string("default"sv); default_value.has_value()) {
parameter_generator.set("parameter_default", MUST(String::formatted(" = ConstantCalculationNode::create(CalculationNode::constant_type_from_string(\"{}\"sv).value())", default_value.value())));
} else {
parameter_generator.set("parameter_default", ""_string);
@ -343,7 +343,7 @@ RefPtr<CalculationNode> Parser::parse_math_function(Function const& function, Ca
parameter_index = 0;
parameters.for_each([&](JsonValue const& parameter_value) {
auto& parameter = parameter_value.as_object();
auto parameter_type_string = parameter.get_byte_string("type"sv).value();
auto parameter_type_string = parameter.get_string("type"sv).value();
auto parameter_generator = function_generator.fork();
parameter_generator.set("parameter_index"sv, String::number(parameter_index));

View file

@ -140,9 +140,9 @@ bool media_feature_type_is_range(MediaFeatureID media_feature_id)
auto member_generator = generator.fork();
member_generator.set("name:titlecase", title_casify(name));
VERIFY(feature.has("type"sv));
auto feature_type = feature.get_byte_string("type"sv);
auto feature_type = feature.get_string("type"sv);
VERIFY(feature_type.has_value());
member_generator.set("is_range", feature_type.value() == "range" ? "true"_string : "false"_string);
member_generator.set("is_range", feature_type.value() == "range"sv ? "true"_string : "false"_string);
member_generator.append(R"~~~(
case MediaFeatureID::@name:titlecase@:
return @is_range@;)~~~");

View file

@ -420,7 +420,7 @@ Optional<PropertyID> property_id_from_camel_case_string(StringView string)
auto member_generator = generator.fork();
member_generator.set("name", name);
member_generator.set("name:camelcase", camel_casify(name));
if (auto legacy_alias_for = value.as_object().get_byte_string("legacy-alias-for"sv); legacy_alias_for.has_value()) {
if (auto legacy_alias_for = value.as_object().get_string("legacy-alias-for"sv); legacy_alias_for.has_value()) {
member_generator.set("name:titlecase", title_casify(legacy_alias_for.value()));
} else {
member_generator.set("name:titlecase", title_casify(name));
@ -449,7 +449,7 @@ Optional<PropertyID> property_id_from_string(StringView string)
auto member_generator = generator.fork();
member_generator.set("name", name);
if (auto legacy_alias_for = value.as_object().get_byte_string("legacy-alias-for"sv); legacy_alias_for.has_value()) {
if (auto legacy_alias_for = value.as_object().get_string("legacy-alias-for"sv); legacy_alias_for.has_value()) {
member_generator.set("name:titlecase", title_casify(legacy_alias_for.value()));
} else {
member_generator.set("name:titlecase", title_casify(name));
@ -552,7 +552,7 @@ AnimationType animation_type_from_longhand_property(PropertyID property_id)
VERIFY_NOT_REACHED();
}
auto animation_type = value.as_object().get_byte_string("animation-type"sv).value();
auto animation_type = value.as_object().get_string("animation-type"sv).value();
member_generator.set("value", title_casify(animation_type));
member_generator.append(R"~~~(
case PropertyID::@name:titlecase@:
@ -681,7 +681,7 @@ NonnullRefPtr<CSSStyleValue> property_initial_value(PropertyID property_id)
dbgln("No initial value specified for property '{}'", name);
VERIFY_NOT_REACHED();
}
auto initial_value = object.get_byte_string("initial"sv);
auto initial_value = object.get_string("initial"sv);
VERIFY(initial_value.has_value());
auto& initial_value_string = initial_value.value();
@ -921,7 +921,7 @@ Optional<ValueType> property_resolves_percentages_relative_to(PropertyID propert
if (is_legacy_alias(value.as_object()))
return;
if (auto resolved_type = value.as_object().get_byte_string("percentages-resolve-to"sv); resolved_type.has_value()) {
if (auto resolved_type = value.as_object().get_string("percentages-resolve-to"sv); resolved_type.has_value()) {
auto property_generator = generator.fork();
property_generator.set("name:titlecase", title_casify(name));
property_generator.set("resolved_type:titlecase", title_casify(resolved_type.value()));
@ -1057,7 +1057,7 @@ bool is_animatable_property(JsonObject& properties, StringView property_name)
auto property = properties.get_object(property_name);
VERIFY(property.has_value());
if (auto animation_type = property.value().get_byte_string("animation-type"sv); animation_type.has_value()) {
if (auto animation_type = property.value().get_string("animation-type"sv); animation_type.has_value()) {
return animation_type != "none";
}

View file

@ -149,14 +149,14 @@ PseudoClassMetadata pseudo_class_metadata(PseudoClass pseudo_class)
pseudo_classes_data.for_each_member([&](auto& name, JsonValue const& value) {
auto member_generator = generator.fork();
auto& pseudo_class = value.as_object();
auto argument_string = pseudo_class.get_byte_string("argument"sv).value();
auto argument_string = pseudo_class.get_string("argument"sv).value();
bool is_valid_as_identifier = argument_string.is_empty();
bool is_valid_as_function = !argument_string.is_empty();
if (argument_string.ends_with('?')) {
is_valid_as_identifier = true;
argument_string = argument_string.substring(0, argument_string.length() - 1);
argument_string = MUST(argument_string.substring_from_byte_offset(0, argument_string.byte_count() - 1));
}
String parameter_type = "None"_string;

View file

@ -6,14 +6,13 @@
#include <LibTest/TestCase.h>
#include <AK/ByteString.h>
#include <AK/JsonObject.h>
#include <AK/JsonValue.h>
#include <AK/StringBuilder.h>
TEST_CASE(load_form)
{
ByteString raw_form_json = R"(
static constexpr auto raw_form_json = R"(
{
"name": "Form1",
"widgets": [
@ -33,13 +32,13 @@ TEST_CASE(load_form)
"visible":true
}
]
})";
})"sv;
JsonValue form_json = JsonValue::from_string(raw_form_json).value();
EXPECT(form_json.is_object());
auto name = form_json.as_object().get_byte_string("name"sv);
auto name = form_json.as_object().get_string("name"sv);
EXPECT(name.has_value());
EXPECT_EQ(name.value(), "Form1");
@ -49,7 +48,7 @@ TEST_CASE(load_form)
widgets->for_each([&](JsonValue const& widget_value) {
auto& widget_object = widget_value.as_object();
auto widget_class = widget_object.get_byte_string("class"sv).value();
auto widget_class = widget_object.get_string("class"sv).value();
widget_object.for_each_member([&]([[maybe_unused]] auto& property_name, [[maybe_unused]] JsonValue const& property_value) {
});
});
@ -161,7 +160,7 @@ TEST_CASE(json_duplicate_keys)
json.set("test"sv, "foo"sv);
json.set("test"sv, "bar"sv);
json.set("test"sv, "baz"sv);
EXPECT_EQ(json.to_byte_string(), "{\"test\":\"baz\"}");
EXPECT_EQ(json.serialized<StringBuilder>(), "{\"test\":\"baz\"}");
}
TEST_CASE(json_u64_roundtrip)
@ -346,12 +345,12 @@ private:
TEST_CASE(fallible_json_object_for_each)
{
ByteString raw_json = R"(
static constexpr auto raw_json = R"(
{
"name": "anon",
"home": "/home/anon",
"default_browser": "Ladybird"
})";
})"sv;
auto json = JsonValue::from_string(raw_json).value();
auto const& object = json.as_object();
@ -386,12 +385,12 @@ TEST_CASE(fallible_json_object_for_each)
TEST_CASE(fallible_json_array_for_each)
{
ByteString raw_json = R"(
static constexpr auto raw_json = R"(
[
"anon",
"/home/anon",
"Ladybird"
])";
])"sv;
auto json = JsonValue::from_string(raw_json).value();
auto const& array = json.as_array();
@ -543,7 +542,7 @@ TEST_CASE(json_array_serialize)
auto array = json_value.as_array();
StringBuilder builder {};
array.serialize(builder);
EXPECT_EQ(builder.to_byte_string(), raw_json);
EXPECT_EQ(builder.string_view(), raw_json);
}
TEST_CASE(json_array_values)

View file

@ -165,7 +165,7 @@ TESTJS_RUN_FILE_FUNCTION(ByteString const& test_file, JS::Realm& realm, JS::Exec
parse_succeeded = !Test::JS::parse_script(test_file, realm).is_error();
bool test_passed = true;
ByteString message;
String message;
String expectation_string;
switch (expectation) {
@ -174,14 +174,14 @@ TESTJS_RUN_FILE_FUNCTION(ByteString const& test_file, JS::Realm& realm, JS::Exec
expectation_string = "File should not parse"_string;
test_passed = !parse_succeeded;
if (!test_passed)
message = "Expected the file to fail parsing, but it did not";
message = "Expected the file to fail parsing, but it did not"_string;
break;
case Pass:
case ExplicitPass:
expectation_string = "File should parse"_string;
test_passed = parse_succeeded;
if (!test_passed)
message = "Expected the file to parse, but it did not";
message = "Expected the file to parse, but it did not"_string;
break;
}
@ -193,6 +193,6 @@ TESTJS_RUN_FILE_FUNCTION(ByteString const& test_file, JS::Realm& realm, JS::Exec
{},
duration_ms,
test_result,
{ Test::Suite { test_path, "Parse file"_string, test_result, { { move(expectation_string), test_result, message, static_cast<u64>(duration_ms) * 1000u } } } }
{ Test::Suite { test_path, "Parse file"_string, test_result, { { move(expectation_string), test_result, move(message), static_cast<u64>(duration_ms) * 1000u } } } }
};
}

View file

@ -162,7 +162,7 @@ static ErrorOr<HashMap<size_t, TestResult>> run_test_files(Span<ByteString> file
auto result_object_or_error = parser.parse();
if (!result_object_or_error.is_error() && result_object_or_error.value().is_object()) {
auto& result_object = result_object_or_error.value().as_object();
if (auto result_string = result_object.get_byte_string("result"sv); result_string.has_value()) {
if (auto result_string = result_object.get_string("result"sv); result_string.has_value()) {
auto const& view = result_string.value();
// Timeout and assert fail already are the result of the stopping test
if (view == "timeout"sv || view == "assert_fail"sv) {
@ -320,7 +320,7 @@ void write_per_file(HashMap<size_t, TestResult> const& result_map, Vector<ByteSt
complete_results.set("duration"sv, time_taken_in_ms / 1000.);
complete_results.set("results"sv, result_object);
if (file->write_until_depleted(complete_results.to_byte_string()).is_error())
if (file->write_until_depleted(complete_results.to_string()).is_error())
warnln("Failed to write per-file");
file->close();
}

View file

@ -416,7 +416,7 @@ static bool verify_test(ErrorOr<void, TestError>& result, TestMetadata const& me
}
if (metadata.is_async && output.has("output"sv)) {
auto output_messages = output.get_byte_string("output"sv);
auto output_messages = output.get_string("output"sv);
VERIFY(output_messages.has_value());
if (output_messages->contains("AsyncTestFailure:InternalError: TODO("sv)) {
output.set("todo_error"sv, true);
@ -532,7 +532,7 @@ static bool g_in_assert = false;
assert_fail_result.set("assert_fail"sv, true);
assert_fail_result.set("result"sv, "assert_fail"sv);
assert_fail_result.set("output"sv, StringView { assert_failed_message, strlen(assert_failed_message) });
outln(saved_stdout_fd, "RESULT {}{}", assert_fail_result.to_byte_string(), '\0');
outln(saved_stdout_fd, "RESULT {}{}", assert_fail_result.serialized<StringBuilder>(), '\0');
// (Attempt to) Ensure that messages are written before quitting.
fflush(saved_stdout_fd);
fflush(stderr);
@ -733,7 +733,7 @@ int main(int argc, char** argv)
result_object.set("test"sv, path);
ScopeGuard output_guard = [&] {
outln(saved_stdout_fd, "RESULT {}{}", result_object.to_byte_string(), '\0');
outln(saved_stdout_fd, "RESULT {}{}", result_object.serialized<StringBuilder>(), '\0');
fflush(saved_stdout_fd);
};