LibJS: Use Utf16StringBuilder for JSON and Temporal strings

Build JSON.stringify, Date ISO strings, and Temporal string results with
Utf16StringBuilder when the result is consumed as a JavaScript string.
Keep UTF-8 conversion only at callers that explicitly need bytes outside
LibJS.
This commit is contained in:
Andreas Kling 2026-06-22 13:06:30 +02:00 committed by Andreas Kling
parent 7c86352f34
commit bcf7f27a4f
12 changed files with 98 additions and 90 deletions

View file

@ -7,8 +7,8 @@
#include <AK/NeverDestroyed.h>
#include <AK/NumericLimits.h>
#include <AK/StringBuilder.h>
#include <AK/Time.h>
#include <AK/Utf16StringBuilder.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/GlobalObject.h>
@ -36,30 +36,30 @@ Date::Date(double date_value, Object& prototype)
Date::~Date() = default;
ErrorOr<String> Date::iso_date_string() const
ErrorOr<Utf16String> Date::iso_date_string() const
{
int year = year_from_time(m_date_value);
StringBuilder builder;
Utf16StringBuilder builder;
if (year < 0)
builder.appendff("-{:06}", -year);
else if (year > 9999)
builder.appendff("+{:06}", year);
else
builder.appendff("{:04}", year);
builder.append('-');
builder.append_ascii('-');
builder.appendff("{:02}", month_from_time(m_date_value) + 1);
builder.append('-');
builder.append_ascii('-');
builder.appendff("{:02}", date_from_time(m_date_value));
builder.append('T');
builder.append_ascii('T');
builder.appendff("{:02}", hour_from_time(m_date_value));
builder.append(':');
builder.append_ascii(':');
builder.appendff("{:02}", min_from_time(m_date_value));
builder.append(':');
builder.append_ascii(':');
builder.appendff("{:02}", sec_from_time(m_date_value));
builder.append('.');
builder.append_ascii('.');
builder.appendff("{:03}", ms_from_time(m_date_value));
builder.append('Z');
builder.append_ascii('Z');
return builder.to_string();
}

View file

@ -27,7 +27,7 @@ public:
double date_value() const { return m_date_value; }
void set_date_value(double value) { m_date_value = value; }
ErrorOr<String> iso_date_string() const;
ErrorOr<Utf16String> iso_date_string() const;
private:
Date(double date_value, Object& prototype);

View file

@ -6,7 +6,6 @@
#include <AK/Function.h>
#include <AK/GenericLexer.h>
#include <AK/StringBuilder.h>
#include <AK/StringConversions.h>
#include <AK/TypeCasts.h>
#include <AK/Utf16StringBuilder.h>
@ -52,7 +51,7 @@ void JSONObject::initialize(Realm& realm)
}
// 25.5.2 JSON.stringify ( value [ , replacer [ , space ] ] ), https://tc39.es/ecma262/#sec-json.stringify
ThrowCompletionOr<Optional<String>> JSONObject::stringify_impl(VM& vm, Value value, Value replacer, Value space)
ThrowCompletionOr<Optional<Utf16String>> JSONObject::stringify_impl(VM& vm, Value value, Value replacer, Value space)
{
auto& realm = *vm.current_realm();
@ -99,15 +98,15 @@ ThrowCompletionOr<Optional<String>> JSONObject::stringify_impl(VM& vm, Value val
if (space.is_number()) {
auto space_mv = MUST(space.to_integer_or_infinity(vm));
space_mv = min(10, space_mv);
state.gap = space_mv < 1 ? String {} : MUST(String::repeated(' ', space_mv));
state.gap = space_mv < 1 ? Utf16String {} : Utf16String::repeated(' ', space_mv);
} else if (space.is_string()) {
auto string = space.as_string().utf8_string();
if (string.bytes().size() <= 10)
auto string = space.as_string().utf16_string();
if (string.length_in_code_units() <= 10)
state.gap = string;
else
state.gap = MUST(string.substring_from_byte_offset(0, 10));
state.gap = Utf16String::from_utf16(string.utf16_view().substring_view(0, 10));
} else {
state.gap = String {};
state.gap = Utf16String {};
}
auto wrapper = Object::create(realm, realm.intrinsics().object_prototype());
@ -115,9 +114,9 @@ ThrowCompletionOr<Optional<String>> JSONObject::stringify_impl(VM& vm, Value val
bool wrote_value = TRY(serialize_json_property(vm, state, Utf16String {}, wrapper));
if (!wrote_value)
return Optional<String> {};
return Optional<Utf16String> {};
return state.builder.to_string_without_validation();
return state.builder.to_string();
}
// 25.5.2 JSON.stringify ( value [ , replacer [ , space ] ] ), https://tc39.es/ecma262/#sec-json.stringify
@ -172,7 +171,7 @@ ThrowCompletionOr<bool> JSONObject::serialize_json_property(VM& vm, StringifySta
// a. If value has an [[IsRawJSON]] internal slot, then
if (is<RawJSONObject>(value_object)) {
// i. Return ! Get(value, "rawJSON").
builder.append(MUST(value_object.get(vm.names.rawJSON)).as_string().utf8_string());
builder.append(MUST(value_object.get(vm.names.rawJSON)).as_string().utf16_string_view());
return true;
}
// b. If value has a [[NumberData]] internal slot, then
@ -199,14 +198,17 @@ ThrowCompletionOr<bool> JSONObject::serialize_json_property(VM& vm, StringifySta
// 5. If value is null, return "null".
if (value.is_null()) {
builder.append("null"sv);
builder.append_ascii("null"sv);
return true;
}
// 6. If value is true, return "true".
// 7. If value is false, return "false".
if (value.is_boolean()) {
builder.append(value.as_bool() ? "true"sv : "false"sv);
if (value.as_bool())
builder.append_ascii("true"sv);
else
builder.append_ascii("false"sv);
return true;
}
@ -220,12 +222,12 @@ ThrowCompletionOr<bool> JSONObject::serialize_json_property(VM& vm, StringifySta
if (value.is_number()) {
// a. If value is finite, return ! ToString(value).
if (value.is_finite_number()) {
number_to_string(builder, value.as_double());
builder.append(number_to_utf16_string(value.as_double()));
return true;
}
// b. Return "null".
builder.append("null"sv);
builder.append_ascii("null"sv);
return true;
}
@ -253,10 +255,10 @@ ThrowCompletionOr<bool> JSONObject::serialize_json_property(VM& vm, StringifySta
return false;
}
static void write_indent(StringBuilder& builder, StringView gap, size_t depth)
static void write_indent(Utf16StringBuilder& builder, Utf16String const& gap, size_t depth)
{
for (size_t i = 0; i < depth; ++i)
builder.append(gap);
builder.append(gap.utf16_view());
}
// 25.5.2.4 SerializeJSONObject ( state, value ), https://tc39.es/ecma262/#sec-serializejsonobject
@ -272,8 +274,8 @@ ThrowCompletionOr<void> JSONObject::serialize_json_object(VM& vm, StringifyState
++state.indent_depth;
auto& builder = state.builder;
builder.append('{');
size_t position_after_open_brace = builder.length();
builder.append_ascii('{');
size_t position_after_open_brace = builder.length_in_code_units();
bool first = true;
auto process_property = [&](PropertyKey const& key) -> ThrowCompletionOr<void> {
@ -281,25 +283,25 @@ ThrowCompletionOr<void> JSONObject::serialize_json_object(VM& vm, StringifyState
return {};
// Mark position before writing anything for this property
size_t mark = builder.length();
size_t mark = builder.length_in_code_units();
// Write separator (comma and possibly newline/indent)
if (!first) {
builder.append(',');
builder.append_ascii(',');
if (!state.gap.is_empty()) {
builder.append('\n');
builder.append_ascii('\n');
write_indent(builder, state.gap, state.indent_depth);
}
} else if (!state.gap.is_empty()) {
builder.append('\n');
builder.append_ascii('\n');
write_indent(builder, state.gap, state.indent_depth);
}
// Write key and colon
quote_json_string(builder, key.to_string());
builder.append(':');
builder.append_ascii(':');
if (!state.gap.is_empty())
builder.append(' ');
builder.append_ascii(' ');
// Serialize value
bool wrote_value = TRY(serialize_json_property(vm, state, key, &object));
@ -308,7 +310,7 @@ ThrowCompletionOr<void> JSONObject::serialize_json_object(VM& vm, StringifyState
first = false;
} else {
// Rollback - value was undefined, remove everything we wrote for this property
builder.trim(builder.length() - mark);
builder.trim(builder.length_in_code_units() - mark);
}
return {};
};
@ -325,11 +327,11 @@ ThrowCompletionOr<void> JSONObject::serialize_json_object(VM& vm, StringifyState
// Close the object
--state.indent_depth;
if (builder.length() > position_after_open_brace && !state.gap.is_empty()) {
builder.append('\n');
if (builder.length_in_code_units() > position_after_open_brace && !state.gap.is_empty()) {
builder.append_ascii('\n');
write_indent(builder, state.gap, state.indent_depth);
}
builder.append('}');
builder.append_ascii('}');
state.seen_objects.remove(&object);
return {};
@ -350,44 +352,44 @@ ThrowCompletionOr<void> JSONObject::serialize_json_array(VM& vm, StringifyState&
auto& builder = state.builder;
auto length = TRY(length_of_array_like(vm, object));
builder.append('[');
builder.append_ascii('[');
for (size_t i = 0; i < length; ++i) {
// Write separator
if (i > 0) {
builder.append(',');
builder.append_ascii(',');
if (!state.gap.is_empty()) {
builder.append('\n');
builder.append_ascii('\n');
write_indent(builder, state.gap, state.indent_depth);
}
} else if (!state.gap.is_empty()) {
builder.append('\n');
builder.append_ascii('\n');
write_indent(builder, state.gap, state.indent_depth);
}
// Serialize value (undefined becomes null for arrays)
bool wrote_value = TRY(serialize_json_property(vm, state, i, &object));
if (!wrote_value)
builder.append("null"sv);
builder.append_ascii("null"sv);
}
// Close the array
--state.indent_depth;
if (length > 0 && !state.gap.is_empty()) {
builder.append('\n');
builder.append_ascii('\n');
write_indent(builder, state.gap, state.indent_depth);
}
builder.append(']');
builder.append_ascii(']');
state.seen_objects.remove(&object);
return {};
}
// 25.5.2.2 QuoteJSONString ( value ), https://tc39.es/ecma262/#sec-quotejsonstring
void JSONObject::quote_json_string(StringBuilder& builder, Utf16View const& string)
void JSONObject::quote_json_string(Utf16StringBuilder& builder, Utf16View const& string)
{
// 1. Let product be the String value consisting solely of the code unit 0x0022 (QUOTATION MARK).
builder.append('"');
builder.append_ascii('"');
// 2. For each code point C of StringToCodePoints(value), do
for (auto code_point : string) {
@ -395,25 +397,25 @@ void JSONObject::quote_json_string(StringBuilder& builder, Utf16View const& stri
// i. Set product to the string-concatenation of product and the escape sequence for C as specified in the "Escape Sequence" column of the corresponding row.
switch (code_point) {
case '\b':
builder.append("\\b"sv);
builder.append_ascii("\\b"sv);
break;
case '\t':
builder.append("\\t"sv);
builder.append_ascii("\\t"sv);
break;
case '\n':
builder.append("\\n"sv);
builder.append_ascii("\\n"sv);
break;
case '\f':
builder.append("\\f"sv);
builder.append_ascii("\\f"sv);
break;
case '\r':
builder.append("\\r"sv);
builder.append_ascii("\\r"sv);
break;
case '"':
builder.append("\\\""sv);
builder.append_ascii("\\\""sv);
break;
case '\\':
builder.append("\\\\"sv);
builder.append_ascii("\\\\"sv);
break;
default:
// b. Else if C has a numeric value less than 0x0020 (SPACE), or if C has the same numeric value as a leading surrogate or trailing surrogate, then
@ -431,7 +433,7 @@ void JSONObject::quote_json_string(StringBuilder& builder, Utf16View const& stri
}
// 3. Set product to the string-concatenation of product and the code unit 0x0022 (QUOTATION MARK).
builder.append('"');
builder.append_ascii('"');
}
// 25.5.1 JSON.parse ( text [ , reviver ] ), https://tc39.es/ecma262/#sec-json.parse

View file

@ -6,6 +6,7 @@
#pragma once
#include <AK/Utf16StringBuilder.h>
#include <LibJS/Export.h>
#include <LibJS/Runtime/Object.h>
@ -38,7 +39,7 @@ public:
// The base implementation of stringify is exposed because it is used by
// test-js to communicate between the JS tests and the C++ test runner.
static ThrowCompletionOr<Optional<String>> stringify_impl(VM&, Value value, Value replacer, Value space);
static ThrowCompletionOr<Optional<Utf16String>> stringify_impl(VM&, Value value, Value replacer, Value space);
static ThrowCompletionOr<Value> parse_json(VM&, StringView text, JSONParseRecord* root_record = nullptr);
@ -49,16 +50,16 @@ private:
GC::Ptr<FunctionObject> replacer_function;
HashTable<GC::Ptr<Object>> seen_objects;
size_t indent_depth { 0 };
String gap;
Utf16String gap;
Optional<Vector<Utf16String>> property_list;
StringBuilder builder;
Utf16StringBuilder builder;
};
// Stringify helpers
static ThrowCompletionOr<bool> serialize_json_property(VM&, StringifyState&, PropertyKey const& key, Object* holder);
static ThrowCompletionOr<void> serialize_json_object(VM&, StringifyState&, Object&);
static ThrowCompletionOr<void> serialize_json_array(VM&, StringifyState&, Object&);
static void quote_json_string(StringBuilder&, Utf16View const&);
static void quote_json_string(Utf16StringBuilder&, Utf16View const&);
// Parse helpers
static ThrowCompletionOr<Value> internalize_json_property(VM&, Object* holder, PropertyKey const& name, FunctionObject& reviver, JSONParseRecord const* parse_record);

View file

@ -8,8 +8,8 @@
*/
#include <AK/NeverDestroyed.h>
#include <AK/StringBuilder.h>
#include <AK/Utf16String.h>
#include <AK/Utf16StringBuilder.h>
#include <LibCrypto/BigFraction/BigFraction.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/PropertyKey.h>
@ -757,44 +757,44 @@ ThrowCompletionOr<bool> is_partial_temporal_object(VM& vm, Value value)
}
// 13.25 FormatFractionalSeconds ( subSecondNanoseconds, precision ), https://tc39.es/proposal-temporal/#sec-temporal-formatfractionalseconds
String format_fractional_seconds(u64 sub_second_nanoseconds, Precision precision)
Utf16String format_fractional_seconds(u64 sub_second_nanoseconds, Precision precision)
{
String fraction_string;
Utf16String fraction_string;
// 1. If precision is auto, then
if (precision.has<Auto>()) {
// a. If subSecondNanoseconds = 0, return the empty String.
if (sub_second_nanoseconds == 0)
return String {};
return Utf16String {};
// b. Let fractionString be ToZeroPaddedDecimalString(subSecondNanoseconds, 9).
fraction_string = MUST(String::formatted("{:09}", sub_second_nanoseconds));
fraction_string = Utf16String::formatted("{:09}", sub_second_nanoseconds);
// c. Set fractionString to the longest prefix of fractionString ending with a code unit other than 0x0030 (DIGIT ZERO).
fraction_string = MUST(fraction_string.trim("0"sv, TrimMode::Right));
fraction_string = Utf16String::from_utf16(fraction_string.utf16_view().trim("0"sv, TrimMode::Right));
}
// 2. Else,
else {
// a. If precision = 0, return the empty String.
if (precision.get<u8>() == 0)
return String {};
return Utf16String {};
// b. Let fractionString be ToZeroPaddedDecimalString(subSecondNanoseconds, 9).
fraction_string = MUST(String::formatted("{:09}", sub_second_nanoseconds));
fraction_string = Utf16String::formatted("{:09}", sub_second_nanoseconds);
// c. Set fractionString to the substring of fractionString from 0 to precision.
fraction_string = MUST(fraction_string.substring_from_byte_offset(0, precision.get<u8>()));
fraction_string = Utf16String::from_utf16(fraction_string.utf16_view().substring_view(0, precision.get<u8>()));
}
// 3. Return the string-concatenation of the code unit 0x002E (FULL STOP) and fractionString.
StringBuilder builder;
builder.append('.');
builder.append(fraction_string);
return MUST(builder.to_string());
Utf16StringBuilder builder;
builder.append_ascii('.');
builder.append(fraction_string.utf16_view());
return builder.to_string();
}
// 13.26 FormatTimeString ( hour, minute, second, subSecondNanoseconds, precision [ , style ] ), https://tc39.es/proposal-temporal/#sec-temporal-formattimestring
String format_time_string(u8 hour, u8 minute, u8 second, u64 sub_second_nanoseconds, SecondsStringPrecision::Precision precision, Optional<TimeStyle> style)
Utf16String format_time_string(u8 hour, u8 minute, u8 second, u64 sub_second_nanoseconds, SecondsStringPrecision::Precision precision, Optional<TimeStyle> style)
{
// 1. If style is present and style is UNSEPARATED, let separator be the empty String; else, let separator be ":".
auto separator = style == TimeStyle::Unseparated ? ""sv : ":"sv;
@ -804,14 +804,14 @@ String format_time_string(u8 hour, u8 minute, u8 second, u64 sub_second_nanoseco
// 4. If precision is minute, return the string-concatenation of hh, separator, and mm.
if (precision.has<SecondsStringPrecision::Minute>())
return MUST(String::formatted("{:02}{}{:02}", hour, separator, minute));
return Utf16String::formatted("{:02}{}{:02}", hour, separator, minute);
// 5. Let ss be ToZeroPaddedDecimalString(second, 2).
// 6. Let subSecondsPart be FormatFractionalSeconds(subSecondNanoseconds, precision).
auto sub_seconds_part = format_fractional_seconds(sub_second_nanoseconds, precision.downcast<Auto, u8>());
// 7. Return the string-concatenation of hh, separator, mm, separator, ss, and subSecondsPart.
return MUST(String::formatted("{:02}{}{:02}{}{:02}{}", hour, separator, minute, separator, second, sub_seconds_part));
return Utf16String::formatted("{:02}{}{:02}{}{:02}{}", hour, separator, minute, separator, second, sub_seconds_part);
}
// 13.27 GetUnsignedRoundingMode ( roundingMode, sign ), https://tc39.es/proposal-temporal/#sec-getunsignedroundingmode

View file

@ -183,8 +183,8 @@ UnitCategory temporal_unit_category(Unit);
RoundingIncrement maximum_temporal_duration_rounding_increment(Unit);
Crypto::UnsignedBigInteger const& temporal_unit_length_in_nanoseconds(Unit);
ThrowCompletionOr<bool> is_partial_temporal_object(VM&, Value);
String format_fractional_seconds(u64, Precision);
String format_time_string(u8 hour, u8 minute, u8 second, u64 sub_second_nanoseconds, SecondsStringPrecision::Precision, Optional<TimeStyle> = {});
Utf16String format_fractional_seconds(u64, Precision);
Utf16String format_time_string(u8 hour, u8 minute, u8 second, u64 sub_second_nanoseconds, SecondsStringPrecision::Precision, Optional<TimeStyle> = {});
UnsignedRoundingMode get_unsigned_rounding_mode(RoundingMode, Sign);
double apply_unsigned_rounding_mode(double, double r1, double r2, UnsignedRoundingMode);
Crypto::SignedBigInteger apply_unsigned_rounding_mode(Crypto::SignedDivisionResult const&, Crypto::SignedBigInteger r1, Crypto::SignedBigInteger r2, UnsignedRoundingMode, Crypto::UnsignedBigInteger const& increment);

View file

@ -10,6 +10,7 @@
#include <AK/GenericShorthands.h>
#include <AK/Math.h>
#include <AK/NumericLimits.h>
#include <AK/Utf16StringBuilder.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/Intrinsics.h>
@ -1474,13 +1475,13 @@ ThrowCompletionOr<Crypto::BigFraction> total_relative_duration(VM& vm, InternalD
}
// 7.5.40 TemporalDurationToString ( duration, precision ), https://tc39.es/proposal-temporal/#sec-temporal-temporaldurationtostring
String temporal_duration_to_string(Duration const& duration, Precision precision)
Utf16String temporal_duration_to_string(Duration const& duration, Precision precision)
{
// 1. Let sign be DurationSign(duration).
auto sign = duration_sign(duration);
// 2. Let datePart be the empty String.
StringBuilder date_part;
Utf16StringBuilder date_part;
// 3. If duration.[[Years]] ≠ 0, then
if (duration.years() != 0) {
@ -1508,7 +1509,7 @@ String temporal_duration_to_string(Duration const& duration, Precision precision
}
// 7. Let timePart be the empty String.
StringBuilder time_part;
Utf16StringBuilder time_part;
// 8. If duration.[[Hours]] ≠ 0, then
if (duration.hours() != 0) {
@ -1552,17 +1553,20 @@ String temporal_duration_to_string(Duration const& duration, Precision precision
auto sign_part = sign < 0 ? "-"sv : ""sv;
// 15. Let result be the string concatenation of signPart, the code unit 0x0050 (LATIN CAPITAL LETTER P) and datePart.
StringBuilder result;
result.appendff("{}P{}", sign_part, date_part.string_view());
Utf16StringBuilder result;
result.append_ascii(sign_part);
result.append_ascii('P');
result.append(date_part.view());
// 16. If timePart is not the empty String, then
if (!time_part.is_empty()) {
// a. Set result to the string concatenation of result, the code unit 0x0054 (LATIN CAPITAL LETTER T), and timePart.
result.appendff("T{}", time_part.string_view());
result.append_ascii('T');
result.append(time_part.view());
}
// 17. Return result.
return MUST(result.to_string());
return result.to_string();
}
// 7.5.41 AddDurations ( operation, duration, other ), https://tc39.es/proposal-temporal/#sec-temporal-adddurations

View file

@ -153,7 +153,7 @@ ThrowCompletionOr<DurationNudgeResult> nudge_to_day_or_time(VM&, InternalDuratio
ThrowCompletionOr<InternalDuration> bubble_relative_duration(VM&, i8 sign, InternalDuration, Crypto::SignedBigInteger const& nudged_epoch_ns, ISODateTime const&, Optional<String const&> time_zone, String const& calendar, Unit largest_unit, Unit smallest_unit);
ThrowCompletionOr<InternalDuration> round_relative_duration(VM&, InternalDuration, Crypto::SignedBigInteger const& origin_epoch_ns, Crypto::SignedBigInteger const& dest_epoch_ns, ISODateTime const&, Optional<String const&> time_zone, String const& calendar, Unit largest_unit, u64 increment, Unit smallest_unit, RoundingMode);
ThrowCompletionOr<Crypto::BigFraction> total_relative_duration(VM&, InternalDuration const&, Crypto::SignedBigInteger const& origin_epoch_ns, Crypto::SignedBigInteger const& dest_epoch_ns, ISODateTime const&, Optional<String const&> time_zone, String const& calendar, Unit);
String temporal_duration_to_string(Duration const&, Precision);
Utf16String temporal_duration_to_string(Duration const&, Precision);
ThrowCompletionOr<GC::Ref<Duration>> add_durations(VM&, ArithmeticOperation, Duration const&, Value);
}

View file

@ -445,7 +445,7 @@ ThrowCompletionOr<TemporalTimeLike> to_temporal_time_record(VM& vm, Object const
}
// 4.5.13 TimeRecordToString ( time, precision ), https://tc39.es/proposal-temporal/#sec-temporal-timerecordtostring
String time_record_to_string(Time const& time, SecondsStringPrecision::Precision precision)
Utf16String time_record_to_string(Time const& time, SecondsStringPrecision::Precision precision)
{
// 1. Let subSecondNanoseconds be time.[[Millisecond]] × 10**6 + time.[[Microsecond]] × 10**3 + time.[[Nanosecond]].
auto sub_second_nanoseconds = (static_cast<u64>(time.millisecond) * 1'000'000) + (static_cast<u64>(time.microsecond) * 1000) + static_cast<u64>(time.nanosecond);

View file

@ -60,7 +60,7 @@ Time balance_time(double hour, double minute, double second, double millisecond,
Time balance_time(double hour, double minute, double second, double millisecond, double microsecond, Crypto::SignedBigInteger const& nanosecond);
ThrowCompletionOr<GC::Ref<PlainTime>> create_temporal_time(VM&, Time const&, GC::Ptr<FunctionObject> new_target = {});
ThrowCompletionOr<TemporalTimeLike> to_temporal_time_record(VM&, Object const& temporal_time_like, Completeness = Completeness::Complete);
String time_record_to_string(Time const&, SecondsStringPrecision::Precision);
Utf16String time_record_to_string(Time const&, SecondsStringPrecision::Precision);
i8 compare_time_record(Time const&, Time const&);
Time add_time(Time const&, TimeDuration const& time_duration);
Time round_time(Time const&, u64 increment, Unit, RoundingMode);

View file

@ -263,7 +263,7 @@ inline ErrorOr<JsonValue> get_test_results(JS::Realm& realm)
auto results = MUST(realm.global_object().get("__TestResults__"_utf16_fly_string));
auto maybe_json_string = MUST(JS::JSONObject::stringify_impl(*g_vm, results, JS::js_undefined(), JS::js_undefined()));
if (maybe_json_string.has_value())
return JsonValue::from_string(*maybe_json_string);
return JsonValue::from_string(MUST(maybe_json_string->utf16_view().to_utf8()));
return JsonValue();
}

View file

@ -977,7 +977,8 @@ GC::Ref<WebIDL::Promise> SubtleCrypto::wrap_key(Bindings::KeyFormat format, GC::
}
// 2. Let bytes be the result of UTF-8 encoding json.
bytes = MUST(ByteBuffer::copy(maybe_json.value()->bytes()));
auto json = MUST(maybe_json.value()->utf16_view().to_utf8());
bytes = MUST(ByteBuffer::copy(json.bytes()));
}
// Otherwise: