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:
parent
7c86352f34
commit
bcf7f27a4f
12 changed files with 98 additions and 90 deletions
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Reference in a new issue