diff --git a/AK/Base64.cpp b/AK/Base64.cpp index f4a21a57a8..cd85868926 100644 --- a/AK/Base64.cpp +++ b/AK/Base64.cpp @@ -138,6 +138,22 @@ static ErrorOr encode_base64_impl(ReadonlyBytes input, simdutf::base64_o return String { move(output) }; } +static Utf16String encode_base64_to_utf16_impl(ReadonlyBytes input, simdutf::base64_options options) +{ + if (input.is_empty()) + return {}; + + return Utf16String::create_uninitialized_ascii( + simdutf::base64_length_from_binary(input.size(), options), + [&](Bytes buffer) { + simdutf::binary_to_base64( + reinterpret_cast(input.data()), + input.size(), + reinterpret_cast(buffer.data()), + options); + }); +} + ErrorOr decode_base64(StringView input, LastChunkHandling last_chunk_handling) { return decode_base64_impl(input, last_chunk_handling, simdutf::base64_default); @@ -186,4 +202,22 @@ ErrorOr encode_base64url(ReadonlyBytes input, OmitPadding omit_padding) return encode_base64_impl(input, options); } +ErrorOr encode_base64_to_utf16(ReadonlyBytes input, OmitPadding omit_padding) +{ + auto options = omit_padding == OmitPadding::Yes + ? simdutf::base64_default_no_padding + : simdutf::base64_default; + + return encode_base64_to_utf16_impl(input, options); +} + +ErrorOr encode_base64url_to_utf16(ReadonlyBytes input, OmitPadding omit_padding) +{ + auto options = omit_padding == OmitPadding::Yes + ? simdutf::base64_url + : simdutf::base64_url_with_padding; + + return encode_base64_to_utf16_impl(input, options); +} + } diff --git a/AK/Base64.h b/AK/Base64.h index 63ef72a901..8aab8146ca 100644 --- a/AK/Base64.h +++ b/AK/Base64.h @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace AK { @@ -45,6 +46,8 @@ enum class OmitPadding { ErrorOr encode_base64(ReadonlyBytes, OmitPadding = OmitPadding::No); ErrorOr encode_base64url(ReadonlyBytes, OmitPadding = OmitPadding::No); +ErrorOr encode_base64_to_utf16(ReadonlyBytes, OmitPadding = OmitPadding::No); +ErrorOr encode_base64url_to_utf16(ReadonlyBytes, OmitPadding = OmitPadding::No); } @@ -52,5 +55,7 @@ ErrorOr encode_base64url(ReadonlyBytes, OmitPadding = OmitPadding::No); using AK::decode_base64; using AK::decode_base64url; using AK::encode_base64; +using AK::encode_base64_to_utf16; using AK::encode_base64url; +using AK::encode_base64url_to_utf16; #endif diff --git a/AK/Utf16String.h b/AK/Utf16String.h index 26fed6dfaf..982c529670 100644 --- a/AK/Utf16String.h +++ b/AK/Utf16String.h @@ -239,6 +239,23 @@ public: static Utf16String from_string_builder(Badge, Utf16StringBuilder& builder); static ErrorOr from_ipc_stream(Stream&, size_t length_in_code_units, bool is_ascii); + template + static Utf16String create_uninitialized_ascii(size_t length_in_code_units, Callback callback) + { + if (length_in_code_units <= Detail::MAX_SHORT_STRING_BYTE_COUNT) { + Utf16String string; + string.m_value.short_ascii_string = Detail::ShortString::create_with_byte_count(length_in_code_units); + + callback({ string.m_value.short_ascii_string.storage, length_in_code_units }); + return string; + } + + Bytes buffer; + Utf16String string { Detail::Utf16StringData::create_uninitialized_ascii(length_in_code_units, buffer) }; + callback(buffer); + return string; + } + constexpr Utf16String(Badge>, nullptr_t) : Detail::Utf16StringBase(Badge {}, nullptr) { diff --git a/AK/Utf16StringData.cpp b/AK/Utf16StringData.cpp index 70bce6e81a..c8733f8314 100644 --- a/AK/Utf16StringData.cpp +++ b/AK/Utf16StringData.cpp @@ -63,6 +63,15 @@ NonnullRefPtr Utf16StringData::from_ascii(ReadonlyBytes ascii_s return string; } +NonnullRefPtr Utf16StringData::create_uninitialized_ascii(size_t length_in_code_units, Bytes& buffer) +{ + VERIFY_UTF16_LENGTH(length_in_code_units); + + auto string = create_uninitialized(StorageType::ASCII, length_in_code_units); + buffer = { string->m_ascii_data, length_in_code_units }; + return string; +} + NonnullRefPtr Utf16StringData::from_utf8(StringView utf8_string, AllowASCIIStorage allow_ascii_storage) { RefPtr string; diff --git a/AK/Utf16StringData.h b/AK/Utf16StringData.h index a84c83a197..dd54e2992d 100644 --- a/AK/Utf16StringData.h +++ b/AK/Utf16StringData.h @@ -15,6 +15,12 @@ #include #include +namespace AK { + +class Utf16String; + +} + namespace AK::Detail { void did_destroy_utf16_fly_string_data(Badge, Detail::Utf16StringData const&); @@ -118,6 +124,8 @@ public: [[nodiscard]] ALWAYS_INLINE bool is_fly_string() const { return m_is_fly_string; } private: + friend class AK::Utf16String; + ALWAYS_INLINE Utf16StringData(StorageType storage_type, size_t code_unit_length) : m_length_in_code_units(code_unit_length) { @@ -126,6 +134,7 @@ private: } static NonnullRefPtr create_uninitialized(StorageType storage_type, size_t code_unit_length); + static NonnullRefPtr create_uninitialized_ascii(size_t length_in_code_units, Bytes& buffer); template static NonnullRefPtr create_from_code_point_iterable(ViewType const&); diff --git a/Libraries/LibCore/TimeZone.cpp b/Libraries/LibCore/TimeZone.cpp index 536e0ec589..faf2cf4199 100644 --- a/Libraries/LibCore/TimeZone.cpp +++ b/Libraries/LibCore/TimeZone.cpp @@ -13,7 +13,8 @@ namespace Core::TimeZone { ErrorOr set_current_time_zone(StringView time_zone) { - TRY(Unicode::set_current_time_zone(time_zone)); + auto time_zone_utf16 = Utf16String::from_utf8(time_zone); + TRY(Unicode::set_current_time_zone(time_zone_utf16)); TRY(Core::Environment::set("TZ"sv, time_zone, Core::Environment::Overwrite::Yes)); tzset(); return {}; @@ -21,7 +22,8 @@ ErrorOr set_current_time_zone(StringView time_zone) String current_time_zone() { - return Unicode::current_time_zone(); + auto time_zone = Unicode::current_time_zone(); + return time_zone.utf16_view().to_utf8_but_should_be_ported_to_utf16(); } } diff --git a/Libraries/LibCrypto/BigFraction/BigFraction.cpp b/Libraries/LibCrypto/BigFraction/BigFraction.cpp index 4e7cb05269..85fd1c0fc2 100644 --- a/Libraries/LibCrypto/BigFraction/BigFraction.cpp +++ b/Libraries/LibCrypto/BigFraction/BigFraction.cpp @@ -5,9 +5,9 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include #include +#include #include #include @@ -233,11 +233,11 @@ void BigFraction::reduce() m_denominator = denominator_divide.quotient; } -String BigFraction::to_string(unsigned rounding_threshold) const +Utf16String BigFraction::to_utf16_string(unsigned rounding_threshold) const { - StringBuilder builder; + Utf16StringBuilder builder; if (m_numerator.is_negative() && m_numerator != "0"_bigint) - builder.append('-'); + builder.append_ascii('-'); auto const number_of_digits = [](auto integer) { unsigned size = 1; @@ -256,43 +256,48 @@ String BigFraction::to_string(unsigned rounding_threshold) const auto const rounded_fraction = rounded(rounding_threshold); // We take the unsigned value as we already manage the '-' - auto const full_value = MUST(rounded_fraction.m_numerator.unsigned_value().to_base(10)).to_byte_string(); - int split = full_value.length() - (number_of_digits(rounded_fraction.m_denominator) - 1); + auto const full_value = MUST(rounded_fraction.m_numerator.unsigned_value().to_base_utf16(10)); + int split = full_value.length_in_code_units() - (number_of_digits(rounded_fraction.m_denominator) - 1); if (split < 0) split = 0; - auto const remove_trailing_zeros = [](StringView value) -> StringView { - auto n = value.length(); + auto const remove_trailing_zeros = [](Utf16View value) -> Utf16View { + auto n = value.length_in_code_units(); VERIFY(n > 0); - while (n > 0 && value.characters_without_null_termination()[n - 1] == '0') + while (n > 0 && value.code_unit_at(n - 1) == '0') --n; - return { value.characters_without_null_termination(), n }; + return value.substring_view(0, n); }; - auto const raw_fractional_value = full_value.substring(split, full_value.length() - split); + auto const raw_fractional_value = full_value.substring_view(split, full_value.length_in_code_units() - split); - auto const integer_value = split == 0 ? "0"sv : full_value.substring_view(0, split); - auto const fractional_value = rounding_threshold == 0 ? "0"sv : remove_trailing_zeros(raw_fractional_value); + Utf16View integer_value = "0"sv; + if (split != 0) + integer_value = full_value.substring_view(0, split); + + Utf16View fractional_value = "0"sv; + if (rounding_threshold != 0) + fractional_value = remove_trailing_zeros(raw_fractional_value); builder.append(integer_value); - bool const has_decimal_part = fractional_value.length() > 0 && fractional_value != "0"; + bool const has_decimal_part = fractional_value.length_in_code_units() > 0 && fractional_value != "0"sv; if (has_decimal_part) { - builder.append('.'); + builder.append_ascii('.'); - auto number_pre_zeros = number_of_digits(rounded_fraction.m_denominator) - full_value.length() - 1; - if (number_pre_zeros > rounding_threshold || fractional_value == "0") + auto number_pre_zeros = number_of_digits(rounded_fraction.m_denominator) - full_value.length_in_code_units() - 1; + if (number_pre_zeros > rounding_threshold || fractional_value == "0"sv) number_pre_zeros = 0; - builder.append_repeated('0', number_pre_zeros); + builder.append_repeated_ascii('0', number_pre_zeros); - if (fractional_value != "0") + if (fractional_value != "0"sv) builder.append(fractional_value); } - return MUST(builder.to_string()); + return builder.to_string(); } BigFraction BigFraction::sqrt() const diff --git a/Libraries/LibCrypto/BigFraction/BigFraction.h b/Libraries/LibCrypto/BigFraction/BigFraction.h index e148cb90d3..eb800c058f 100644 --- a/Libraries/LibCrypto/BigFraction/BigFraction.h +++ b/Libraries/LibCrypto/BigFraction/BigFraction.h @@ -6,6 +6,7 @@ #pragma once +#include #include namespace Crypto { @@ -55,7 +56,7 @@ public: // - m_denominator = 10000 BigFraction rounded(unsigned rounding_threshold) const; - String to_string(unsigned rounding_threshold) const; + Utf16String to_utf16_string(unsigned rounding_threshold) const; double to_double() const; Crypto::SignedBigInteger const& numerator() const& { return m_numerator; } diff --git a/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp b/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp index 9da6344245..e7444895e8 100644 --- a/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp +++ b/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include @@ -170,6 +172,28 @@ ErrorOr SignedBigInteger::to_base(u16 N) const return StringView(buffer.bytes().slice(0, written - 1)).to_ascii_lowercase_string(); } +ErrorOr SignedBigInteger::to_base_utf16(u16 N) const +{ + VERIFY(N <= 36); + if (is_zero()) + return "0"_utf16; + + int size = 0; + MP_MUST(mp_radix_size(&m_mp, N, &size)); + auto buffer = TRY(ByteBuffer::create_zeroed(size)); + + size_t written = 0; + MP_MUST(mp_to_radix(&m_mp, reinterpret_cast(buffer.data()), size, &written, N)); + + Utf16StringBuilder builder(written - 1); + for (auto character : buffer.bytes().slice(0, written - 1)) { + if (character >= 'A' && character <= 'Z') + character += 'a' - 'A'; + builder.append_code_unit(character); + } + return builder.to_string(); +} + i64 SignedBigInteger::to_i64() const { return mp_get_i64(&m_mp); diff --git a/Libraries/LibCrypto/BigInt/SignedBigInteger.h b/Libraries/LibCrypto/BigInt/SignedBigInteger.h index bf30198c65..1f94097140 100644 --- a/Libraries/LibCrypto/BigInt/SignedBigInteger.h +++ b/Libraries/LibCrypto/BigInt/SignedBigInteger.h @@ -8,6 +8,7 @@ #pragma once +#include #include namespace Crypto { @@ -44,6 +45,7 @@ public: [[nodiscard]] static ErrorOr from_base(u16 N, StringView str); [[nodiscard]] static ErrorOr from_base(u16 N, Utf16View str); [[nodiscard]] ErrorOr to_base(u16 N) const; + [[nodiscard]] ErrorOr to_base_utf16(u16 N) const; [[nodiscard]] i64 to_i64() const; [[nodiscard]] u64 to_u64() const; diff --git a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp index 2ad5676f0e..b9af8bd175 100644 --- a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp +++ b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include @@ -172,6 +174,28 @@ ErrorOr UnsignedBigInteger::to_base(u16 N) const return StringView(buffer.bytes().slice(0, written - 1)).to_ascii_lowercase_string(); } +ErrorOr UnsignedBigInteger::to_base_utf16(u16 N) const +{ + VERIFY(N <= 36); + if (is_zero()) + return "0"_utf16; + + int size = 0; + MP_MUST(mp_radix_size(&m_mp, N, &size)); + auto buffer = TRY(ByteBuffer::create_zeroed(size)); + + size_t written = 0; + MP_MUST(mp_to_radix(&m_mp, reinterpret_cast(buffer.data()), size, &written, N)); + + Utf16StringBuilder builder(written - 1); + for (auto character : buffer.bytes().slice(0, written - 1)) { + if (character >= 'A' && character <= 'Z') + character += 'a' - 'A'; + builder.append_code_unit(character); + } + return builder.to_string(); +} + size_t UnsignedBigInteger::count_digits_in_base(u16 base) const { VERIFY(base <= 36); diff --git a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h index 1d6ca5fb7d..023cb276fb 100644 --- a/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h +++ b/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h @@ -10,6 +10,7 @@ #pragma once #include +#include #include #include @@ -47,6 +48,7 @@ public: [[nodiscard]] static ErrorOr from_base(u16 N, StringView str); [[nodiscard]] static ErrorOr from_base(u16 N, Utf16View str); [[nodiscard]] ErrorOr to_base(u16 N) const; + [[nodiscard]] ErrorOr to_base_utf16(u16 N) const; [[nodiscard]] size_t count_digits_in_base(u16 base) const; diff --git a/Libraries/LibJS/Bytecode/AsmInterpreter/AsmSlowPaths.cpp b/Libraries/LibJS/Bytecode/AsmInterpreter/AsmSlowPaths.cpp index 7cb03aa32d..000fa221de 100644 --- a/Libraries/LibJS/Bytecode/AsmInterpreter/AsmSlowPaths.cpp +++ b/Libraries/LibJS/Bytecode/AsmInterpreter/AsmSlowPaths.cpp @@ -245,7 +245,7 @@ static ThrowCompletionOr asm_create_variable(VM& vm, Utf16FlyString const& // Note: This is papering over an issue where "FunctionDeclarationInstantiation" creates these bindings for us. // Instead of crashing in there, we'll just raise an exception here. if (TRY(vm.lexical_environment()->has_binding(name))) [[unlikely]] - return vm.throw_completion(TRY_OR_THROW_OOM(vm, String::formatted("Lexical environment already has binding '{}'", name))); + return vm.throw_completion(Utf16String::formatted("Lexical environment already has binding '{}'", name)); if (is_immutable) return vm.lexical_environment()->create_immutable_binding(vm, name, is_strict); @@ -2344,7 +2344,7 @@ i64 asm_slow_path_dynamic_typeof_binding(VM* vm, u32 pc, Op::DynamicTypeofBindin auto reference = ASM_TRY(*vm, pc, vm->resolve_binding(vm->get_identifier(instruction->identifier()), instruction->strict())); if (reference.is_unresolvable()) { - vm->set(instruction->dst(), PrimitiveString::create(*vm, "undefined"_string)); + vm->set(instruction->dst(), PrimitiveString::create(*vm, "undefined"_utf16_fly_string)); return static_cast(pc + sizeof(Op::DynamicTypeofBinding)); } diff --git a/Libraries/LibJS/Bytecode/Executable.cpp b/Libraries/LibJS/Bytecode/Executable.cpp index fa509acc4a..12a64e606d 100644 --- a/Libraries/LibJS/Bytecode/Executable.cpp +++ b/Libraries/LibJS/Bytecode/Executable.cpp @@ -409,7 +409,7 @@ static void dump_metadata(StringBuilder& output, Executable const& executable) else if (value.is_double()) output.appendff("Double({})", value.as_double()); else if (value.is_bigint()) - output.appendff("BigInt({})", MUST(value.as_bigint().to_string())); + output.appendff("BigInt({})", value.as_bigint().to_utf16_string()); else if (value.is_string()) output.appendff("String(\"{}\")", value.as_string().utf16_string_view()); else if (value.is_undefined()) diff --git a/Libraries/LibJS/Console.cpp b/Libraries/LibJS/Console.cpp index c601b958fd..790b63a065 100644 --- a/Libraries/LibJS/Console.cpp +++ b/Libraries/LibJS/Console.cpp @@ -52,7 +52,7 @@ ThrowCompletionOr Console::assert_() return js_undefined(); // 2. Let message be a string without any formatting specifiers indicating generically an assertion failure (such as "Assertion failed"). - auto message = PrimitiveString::create(vm, "Assertion failed"_string); + auto message = PrimitiveString::create(vm, "Assertion failed"_utf16_fly_string); // NOTE: Assemble `data` from the function arguments. GC::RootVector data; @@ -362,7 +362,7 @@ ThrowCompletionOr Console::trace() Console::TraceFrame frame; auto function_name = (context && context->function) ? context->function->name_for_call_stack() : ""_utf16; - frame.function_name = function_name.is_empty() ? ""_string : function_name.to_utf8(); + frame.function_name = function_name.is_empty() ? ""_utf16 : function_name; if (element.source_range.has_value()) { auto const& source_range = *element.source_range; @@ -963,19 +963,17 @@ ThrowCompletionOr> ConsoleClient::formatter(GC::RootVector ThrowCompletionOr ConsoleClient::generically_format_values(GC::RootVector const& values) { - AllocatingMemoryStream stream; auto& vm = m_console->realm().vm(); - PrintContext ctx { vm, stream, true }; + Utf16StringBuilder builder; + PrintContext ctx { .vm = vm, .builder = &builder, .strip_ansi = true }; bool first = true; for (auto const& value : values) { if (!first) - TRY_OR_THROW_OOM(vm, stream.write_until_depleted(" "sv.bytes())); + builder.append_ascii(' '); TRY_OR_THROW_OOM(vm, JS::print(value, ctx)); first = false; } - // FIXME: Is it possible we could end up serializing objects to invalid UTF-8? - auto output = TRY_OR_THROW_OOM(vm, String::from_stream(stream, stream.used_buffer_size())); - return Utf16String::from_utf8(output); + return builder.to_string(); } } diff --git a/Libraries/LibJS/Console.h b/Libraries/LibJS/Console.h index 4e9dec68c0..bfbacb0012 100644 --- a/Libraries/LibJS/Console.h +++ b/Libraries/LibJS/Console.h @@ -58,7 +58,7 @@ public: }; struct TraceFrame { - String function_name; + Utf16String function_name; Optional source_file; Optional line; Optional column; diff --git a/Libraries/LibJS/Contrib/Test262/262Object.cpp b/Libraries/LibJS/Contrib/Test262/262Object.cpp index c7cf4e87cf..37cf535b8a 100644 --- a/Libraries/LibJS/Contrib/Test262/262Object.cpp +++ b/Libraries/LibJS/Contrib/Test262/262Object.cpp @@ -108,7 +108,7 @@ JS_DEFINE_NATIVE_FUNCTION($262Object::eval_script) auto& error = script_or_error.error()[0]; // b. Return Completion { [[Type]]: throw, [[Value]]: error, [[Target]]: empty }. - return vm.throw_completion(error.to_string()); + return vm.throw_completion(error.to_utf16_string()); } // 5. Let status be ScriptEvaluation(s). diff --git a/Libraries/LibJS/ParserError.cpp b/Libraries/LibJS/ParserError.cpp index cdc3f0816d..d8ea8b9797 100644 --- a/Libraries/LibJS/ParserError.cpp +++ b/Libraries/LibJS/ParserError.cpp @@ -13,18 +13,18 @@ namespace JS { -String ParserError::to_string() const +Utf16String ParserError::to_utf16_string() const { if (!position.has_value()) return message; - return MUST(String::formatted("{} (line: {}, column: {})", message, position.value().line, position.value().column)); + return Utf16String::formatted("{} (line: {}, column: {})", message, position.value().line, position.value().column); } ByteString ParserError::to_byte_string() const { if (!position.has_value()) return message.to_byte_string(); - return ByteString::formatted("{} (line: {}, column: {})", message, position.value().line, position.value().column); + return Utf16String::formatted("{} (line: {}, column: {})", message, position.value().line, position.value().column).to_byte_string(); } ByteString ParserError::source_location_hint(Utf16View const& source, char spacer, char indicator) const diff --git a/Libraries/LibJS/ParserError.h b/Libraries/LibJS/ParserError.h index d7b2c87e05..8b08933245 100644 --- a/Libraries/LibJS/ParserError.h +++ b/Libraries/LibJS/ParserError.h @@ -10,17 +10,17 @@ #include #include #include -#include +#include #include #include namespace JS { struct JS_API ParserError { - String message; + Utf16String message; Optional position; - String to_string() const; + Utf16String to_utf16_string() const; ByteString to_byte_string() const; ByteString source_location_hint(Utf16View const& source, char spacer = ' ', char indicator = '^') const; }; diff --git a/Libraries/LibJS/Print.cpp b/Libraries/LibJS/Print.cpp index e5e5c317a5..1b2e1df4f3 100644 --- a/Libraries/LibJS/Print.cpp +++ b/Libraries/LibJS/Print.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -140,9 +141,21 @@ ErrorOr js_out(JS::PrintContext& print_context, CheckedFormatString variadic_format_parameters { args... }; + TRY(vformat(*print_context.builder, format_string_without_ansi.bytes_as_string_view(), variadic_format_parameters)); + } else { + VERIFY(print_context.stream); + TRY(print_context.stream->write_formatted(format_string_without_ansi, args...)); + } } else { - TRY(print_context.stream.write_formatted(format_string.view(), args...)); + if (print_context.builder) { + AK::VariadicFormatParams variadic_format_parameters { args... }; + TRY(vformat(*print_context.builder, format_string.view(), variadic_format_parameters)); + } else { + VERIFY(print_context.stream); + TRY(print_context.stream->write_formatted(format_string.view(), args...)); + } } return {}; @@ -684,7 +697,7 @@ ErrorOr print_intl_date_time_format(JS::PrintContext& print_context, JS::I return JS::throw_completion(JS::js_null()); } else { auto name = Unicode::calendar_pattern_style_to_string(*option); - if (print_value(print_context, JS::PrimitiveString::create(date_time_format.vm(), name), seen_objects).is_error()) + if (print_value(print_context, JS::PrimitiveString::create(date_time_format.vm(), move(name)), seen_objects).is_error()) return JS::throw_completion(JS::js_null()); } @@ -788,10 +801,10 @@ ErrorOr print_intl_duration_format(JS::PrintContext& print_context, JS::In auto display = JS::Intl::DurationFormat::display_to_string(options.display); TRY(js_out(print_context, "\n {}: ", style_name)); - TRY(print_value(print_context, JS::PrimitiveString::create(duration_format.vm(), style), seen_objects)); + TRY(print_value(print_context, JS::PrimitiveString::create(duration_format.vm(), move(style)), seen_objects)); TRY(js_out(print_context, "\n {}: ", display_name)); - TRY(print_value(print_context, JS::PrimitiveString::create(duration_format.vm(), display), seen_objects)); + TRY(print_value(print_context, JS::PrimitiveString::create(duration_format.vm(), move(display)), seen_objects)); return {}; }; diff --git a/Libraries/LibJS/Print.h b/Libraries/LibJS/Print.h index f384d05c94..6de15dcce3 100644 --- a/Libraries/LibJS/Print.h +++ b/Libraries/LibJS/Print.h @@ -12,11 +12,19 @@ #include #include +namespace AK { + +class Stream; +class Utf16StringBuilder; + +} + namespace JS { struct PrintContext { JS::VM& vm; - Stream& stream; + AK::Stream* stream { nullptr }; + AK::Utf16StringBuilder* builder { nullptr }; bool strip_ansi { false }; bool raw_strings { false }; }; diff --git a/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Libraries/LibJS/Runtime/AbstractOperations.cpp index b6186fde88..405b8c87ec 100644 --- a/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -702,7 +702,7 @@ ThrowCompletionOr perform_eval(VM& vm, Value x, CallerMode strict_caller, auto rust_compilation = RustIntegration::compile_eval(*code_string, vm, strict_caller, in_function, in_method, in_derived_constructor, in_class_field_initializer); if (!rust_compilation.has_value()) - return vm.throw_completion("Failed to compile eval code"_string); + return vm.throw_completion("Failed to compile eval code"_utf16); if (rust_compilation->is_error()) return vm.throw_completion(rust_compilation->release_error()); auto& eval_result = rust_compilation->value(); @@ -1277,7 +1277,7 @@ CanonicalIndex canonical_numeric_index_string(PropertyKey const& property_key, C // FIXME: We return 0 instead of n but it might not observable? // 3. If SameValue(! ToString(n), argument) is true, return n. - if (number_to_string(*maybe_double) == argument) + if (number_to_utf16_string(*maybe_double) == argument) return CanonicalIndex(CanonicalIndex::Type::Numeric, 0); // 4. Return undefined. @@ -1934,7 +1934,7 @@ ThrowCompletionOr get_option(VM& vm, Object const& options, PropertyKey c [](Empty) -> Value { return js_undefined(); }, [](bool default_) -> Value { return Value { default_ }; }, [](double default_) -> Value { return Value { default_ }; }, - [&](StringView default_) -> Value { return PrimitiveString::create(vm, default_); }); + [&](Utf16View default_) -> Value { return PrimitiveString::create(vm, default_); }); } // 3. If type is BOOLEAN, then @@ -1960,8 +1960,6 @@ ThrowCompletionOr get_option(VM& vm, Object const& options, PropertyKey c auto it = find_if(values.begin(), values.end(), [&](auto allowed_value) { return value_string == allowed_value; }); if (it == values.end()) return vm.throw_completion(ErrorType::OptionIsNotValidValue, value_string, property.as_string()); - - value = PrimitiveString::create(vm, *it); } // 6. Return value. @@ -1975,7 +1973,8 @@ ThrowCompletionOr get_rounding_mode_option(VM& vm, Object const& o static constexpr auto allowed_strings = to_array({ "ceil"sv, "floor"sv, "expand"sv, "trunc"sv, "halfCeil"sv, "halfFloor"sv, "halfExpand"sv, "halfTrunc"sv, "halfEven"sv }); // 2. Let stringFallback be the value from the "String Identifier" column of the row with fallback in its "Rounding Mode" column. - auto string_fallback = allowed_strings[to_underlying(fallback)]; + static constexpr auto utf16_allowed_strings = to_array({ u"ceil"sv, u"floor"sv, u"expand"sv, u"trunc"sv, u"halfCeil"sv, u"halfFloor"sv, u"halfExpand"sv, u"halfTrunc"sv, u"halfEven"sv }); + auto string_fallback = utf16_allowed_strings[to_underlying(fallback)]; // 3. Let stringValue be ? GetOption(options, "roundingMode", STRING, allowedStrings, stringFallback). auto string_value = TRY(get_option(vm, options, vm.names.roundingMode, OptionType::String, allowed_strings, string_fallback)); diff --git a/Libraries/LibJS/Runtime/AbstractOperations.h b/Libraries/LibJS/Runtime/AbstractOperations.h index 07dd041e18..3647f82cc7 100644 --- a/Libraries/LibJS/Runtime/AbstractOperations.h +++ b/Libraries/LibJS/Runtime/AbstractOperations.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -375,7 +376,7 @@ enum class OptionType { }; struct Required { }; -using OptionDefault = Variant; +using OptionDefault = Variant; ThrowCompletionOr> get_options_object(VM&, Value options); ThrowCompletionOr get_option(VM&, Object const& options, PropertyKey const& property, OptionType type, ReadonlySpan values, OptionDefault const&); diff --git a/Libraries/LibJS/Runtime/AggregateErrorPrototype.cpp b/Libraries/LibJS/Runtime/AggregateErrorPrototype.cpp index 3a7451a61f..895e8b4c0c 100644 --- a/Libraries/LibJS/Runtime/AggregateErrorPrototype.cpp +++ b/Libraries/LibJS/Runtime/AggregateErrorPrototype.cpp @@ -22,8 +22,8 @@ void AggregateErrorPrototype::initialize(Realm& realm) auto& vm = this->vm(); Base::initialize(realm); u8 attr = Attribute::Writable | Attribute::Configurable; - define_direct_property(vm.names.name, PrimitiveString::create(vm, "AggregateError"_string), attr); - define_direct_property(vm.names.message, PrimitiveString::create(vm, String {}), attr); + define_direct_property(vm.names.name, PrimitiveString::create(vm, "AggregateError"_utf16_fly_string), attr); + define_direct_property(vm.names.message, PrimitiveString::create(vm, Utf16String {}), attr); } } diff --git a/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp b/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp index b974d51e01..c02ef2a5e7 100644 --- a/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp +++ b/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp @@ -29,7 +29,7 @@ void ArrayIteratorPrototype::initialize(Realm& realm) define_native_function(realm, vm.names.next, next, 0, Attribute::Configurable | Attribute::Writable, Bytecode::Builtin::ArrayIteratorPrototypeNext); // 23.1.5.2.2 %ArrayIteratorPrototype% [ @@toStringTag ], https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-@@tostringtag - define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Array Iterator"_string), Attribute::Configurable); + define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Array Iterator"_utf16_fly_string), Attribute::Configurable); } // 23.1.5.2.1 %ArrayIteratorPrototype%.next ( ), https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next diff --git a/Libraries/LibJS/Runtime/ArrayPrototype.cpp b/Libraries/LibJS/Runtime/ArrayPrototype.cpp index 3135f044a3..31266f4b0b 100644 --- a/Libraries/LibJS/Runtime/ArrayPrototype.cpp +++ b/Libraries/LibJS/Runtime/ArrayPrototype.cpp @@ -924,7 +924,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::join) // FWIW: engine262, a "100% spec compliant" ECMA-262 impl, aborts with "too much recursion". // Same applies to Array.prototype.toLocaleString(). if (array_join_seen_objects().contains(this_object)) - return PrimitiveString::create(vm, String {}); + return PrimitiveString::create(vm, Utf16String {}); array_join_seen_objects().set(this_object); ArmedScopeGuard unsee_object_guard = [&] { array_join_seen_objects().remove(this_object); @@ -1809,7 +1809,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_locale_string) auto this_object = TRY(vm.this_value().to_object(vm)); if (array_join_seen_objects().contains(this_object)) - return PrimitiveString::create(vm, String {}); + return PrimitiveString::create(vm, Utf16String {}); array_join_seen_objects().set(this_object); ArmedScopeGuard unsee_object_guard = [&] { array_join_seen_objects().remove(this_object); diff --git a/Libraries/LibJS/Runtime/AsyncGeneratorPrototype.cpp b/Libraries/LibJS/Runtime/AsyncGeneratorPrototype.cpp index f914f0ed59..0fa2cf7698 100644 --- a/Libraries/LibJS/Runtime/AsyncGeneratorPrototype.cpp +++ b/Libraries/LibJS/Runtime/AsyncGeneratorPrototype.cpp @@ -30,7 +30,7 @@ void AsyncGeneratorPrototype::initialize(Realm& realm) define_native_function(realm, vm.names.throw_, throw_, 1, attr); // 27.6.1.5 AsyncGenerator.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-asyncgenerator-prototype-tostringtag - define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "AsyncGenerator"_string), Attribute::Configurable); + define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "AsyncGenerator"_utf16_fly_string), Attribute::Configurable); } // 27.6.3.3 AsyncGeneratorValidate ( generator, generatorBrand ), https://tc39.es/ecma262/#sec-asyncgeneratorvalidate diff --git a/Libraries/LibJS/Runtime/AtomicsObject.cpp b/Libraries/LibJS/Runtime/AtomicsObject.cpp index a08e135805..9d86282834 100644 --- a/Libraries/LibJS/Runtime/AtomicsObject.cpp +++ b/Libraries/LibJS/Runtime/AtomicsObject.cpp @@ -248,7 +248,7 @@ void AtomicsObject::initialize(Realm& realm) define_native_function(realm, vm.names.xor_, xor_, 3, attr); // 25.4.17 Atomics [ @@toStringTag ], https://tc39.es/ecma262/#sec-atomics-@@tostringtag - define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Atomics"_string), Attribute::Configurable); + define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Atomics"_utf16_fly_string), Attribute::Configurable); } // 25.4.4 Atomics.add ( typedArray, index, value ), https://tc39.es/ecma262/#sec-atomics.add diff --git a/Libraries/LibJS/Runtime/BigInt.cpp b/Libraries/LibJS/Runtime/BigInt.cpp index 9467c6f574..83d1d1dbe9 100644 --- a/Libraries/LibJS/Runtime/BigInt.cpp +++ b/Libraries/LibJS/Runtime/BigInt.cpp @@ -23,14 +23,9 @@ BigInt::BigInt(Crypto::SignedBigInteger big_integer) { } -ErrorOr BigInt::to_string() const -{ - return String::formatted("{}n", TRY(m_big_integer.to_base(10))); -} - Utf16String BigInt::to_utf16_string() const { - return Utf16String::formatted("{}n", MUST(m_big_integer.to_base(10))); + return Utf16String::formatted("{}n", MUST(m_big_integer.to_base_utf16(10))); } size_t BigInt::external_memory_size() const diff --git a/Libraries/LibJS/Runtime/BigInt.h b/Libraries/LibJS/Runtime/BigInt.h index 47181b2b27..3db850bb75 100644 --- a/Libraries/LibJS/Runtime/BigInt.h +++ b/Libraries/LibJS/Runtime/BigInt.h @@ -27,7 +27,6 @@ public: Crypto::SignedBigInteger const& big_integer() const { return m_big_integer; } - ErrorOr to_string() const; Utf16String to_utf16_string() const; private: diff --git a/Libraries/LibJS/Runtime/BigIntPrototype.cpp b/Libraries/LibJS/Runtime/BigIntPrototype.cpp index 88595db5cc..8e40c2de25 100644 --- a/Libraries/LibJS/Runtime/BigIntPrototype.cpp +++ b/Libraries/LibJS/Runtime/BigIntPrototype.cpp @@ -74,7 +74,7 @@ JS_DEFINE_NATIVE_FUNCTION(BigIntPrototype::to_string) } // 5. Return BigInt::toString(x, radixMV). - return PrimitiveString::create(vm, TRY_OR_THROW_OOM(vm, bigint->big_integer().to_base(radix))); + return PrimitiveString::create(vm, TRY_OR_THROW_OOM(vm, bigint->big_integer().to_base_utf16(radix))); } // 21.2.3.2 BigInt.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ), https://tc39.es/ecma262/#sec-bigint.prototype.tolocalestring diff --git a/Libraries/LibJS/Runtime/BooleanPrototype.cpp b/Libraries/LibJS/Runtime/BooleanPrototype.cpp index 8d8a5713d5..68e84dc242 100644 --- a/Libraries/LibJS/Runtime/BooleanPrototype.cpp +++ b/Libraries/LibJS/Runtime/BooleanPrototype.cpp @@ -55,7 +55,7 @@ JS_DEFINE_NATIVE_FUNCTION(BooleanPrototype::to_string) auto b = TRY(this_boolean_value(vm, vm.this_value())); // 2. If b is true, return "true"; else return "false". - return PrimitiveString::create(vm, TRY_OR_THROW_OOM(vm, String::from_utf8(b ? "true"sv : "false"sv))); + return PrimitiveString::create(vm, b ? "true"_utf16 : "false"_utf16); } // 20.3.3.3 Boolean.prototype.valueOf ( ), https://tc39.es/ecma262/#sec-boolean.prototype.valueof diff --git a/Libraries/LibJS/Runtime/ConsoleObject.cpp b/Libraries/LibJS/Runtime/ConsoleObject.cpp index e3aff8f65e..1190bdb60e 100644 --- a/Libraries/LibJS/Runtime/ConsoleObject.cpp +++ b/Libraries/LibJS/Runtime/ConsoleObject.cpp @@ -59,7 +59,7 @@ void ConsoleObject::initialize(Realm& realm) define_native_function(realm, vm.names.timeLog, time_log, 0, attr); define_native_function(realm, vm.names.timeEnd, time_end, 0, attr); - define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "console"_string), Attribute::Configurable); + define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "console"_utf16_fly_string), Attribute::Configurable); } // 1.1.1. assert(condition, ...data), https://console.spec.whatwg.org/#assert diff --git a/Libraries/LibJS/Runtime/Date.cpp b/Libraries/LibJS/Runtime/Date.cpp index a462c53e7e..51819aef15 100644 --- a/Libraries/LibJS/Runtime/Date.cpp +++ b/Libraries/LibJS/Runtime/Date.cpp @@ -37,7 +37,7 @@ Date::Date(double date_value, Object& prototype) Date::~Date() = default; -ErrorOr Date::iso_date_string() const +Utf16String Date::iso_date_string() const { int year = year_from_time(m_date_value); @@ -442,14 +442,18 @@ Unicode::TimeZoneOffset get_named_time_zone_offset_milliseconds(StringView time_ return offset.release_value(); } -static Optional cached_system_time_zone_identifier; +static auto& cached_system_time_zone_identifier() +{ + static NeverDestroyed> cached_system_time_zone_identifier; + return *cached_system_time_zone_identifier; +} // 21.4.1.24 SystemTimeZoneIdentifier ( ), https://tc39.es/ecma262/#sec-systemtimezoneidentifier -String system_time_zone_identifier() +Utf16String system_time_zone_identifier() { // OPTIMIZATION: We cache the system time zone to avoid the expensive lookups below. - if (cached_system_time_zone_identifier.has_value()) - return *cached_system_time_zone_identifier; + if (cached_system_time_zone_identifier().has_value()) + return *cached_system_time_zone_identifier(); // 1. If the implementation only supports the UTC time zone, return "UTC". @@ -457,23 +461,22 @@ String system_time_zone_identifier() // time zone identifier or an offset time zone identifier. auto system_time_zone_string = Unicode::current_time_zone(); - auto utf16_system_time_zone_string = Utf16String::from_utf8(system_time_zone_string); - if (!is_offset_time_zone_identifier(utf16_system_time_zone_string)) { + if (!is_offset_time_zone_identifier(system_time_zone_string)) { auto time_zone_identifier = Intl::get_available_named_time_zone_identifier(system_time_zone_string); if (!time_zone_identifier.has_value()) - return "UTC"_string; + return "UTC"_utf16; system_time_zone_string = time_zone_identifier->primary_identifier; } // 3. Return systemTimeZoneString. - cached_system_time_zone_identifier = move(system_time_zone_string); - return *cached_system_time_zone_identifier; + cached_system_time_zone_identifier() = move(system_time_zone_string); + return *cached_system_time_zone_identifier(); } void clear_system_time_zone_cache() { - cached_system_time_zone_identifier.clear(); + cached_system_time_zone_identifier().clear(); } // 21.4.1.25 LocalTime ( t ), https://tc39.es/ecma262/#sec-localtime @@ -484,7 +487,7 @@ double local_time(double time) auto system_time_zone_identifier = JS::system_time_zone_identifier(); // 2. Let parseResult be ! ParseTimeZoneIdentifier(systemTimeZoneIdentifier). - auto parse_result = Temporal::parse_time_zone_identifier(system_time_zone_identifier); + auto parse_result = Temporal::parse_time_zone_identifier(system_time_zone_identifier.utf16_view()); double offset_nanoseconds { 0 }; @@ -496,7 +499,7 @@ double local_time(double time) // 4. Else, else { // a. Let offsetNs be GetNamedTimeZoneOffsetNanoseconds(systemTimeZoneIdentifier, ℤ(ℝ(t) × 10^6)). - auto offset = get_named_time_zone_offset_milliseconds(system_time_zone_identifier, time); + auto offset = get_named_time_zone_offset_milliseconds(system_time_zone_identifier.utf16_view().bytes(), time); offset_nanoseconds = static_cast(offset.offset.to_nanoseconds()); } @@ -515,7 +518,7 @@ double utc_time(double time) auto system_time_zone_identifier = JS::system_time_zone_identifier(); // 2. Let parseResult be ! ParseTimeZoneIdentifier(systemTimeZoneIdentifier). - auto parse_result = Temporal::parse_time_zone_identifier(system_time_zone_identifier); + auto parse_result = Temporal::parse_time_zone_identifier(system_time_zone_identifier.utf16_view()); double offset_nanoseconds { 0 }; @@ -530,7 +533,7 @@ double utc_time(double time) auto iso_date_time = Temporal::time_value_to_iso_date_time_record(time); // b. Let possibleInstants be GetNamedTimeZoneEpochNanoseconds(systemTimeZoneIdentifier, isoDateTime). - auto possible_instants = get_named_time_zone_epoch_nanoseconds(system_time_zone_identifier, iso_date_time); + auto possible_instants = get_named_time_zone_epoch_nanoseconds(system_time_zone_identifier.utf16_view().bytes(), iso_date_time); // c. NOTE: The following steps ensure that when t represents local time repeating multiple times at a negative // time zone transition (e.g. when the daylight saving time ends or the time zone offset is decreased due to @@ -565,7 +568,7 @@ double utc_time(double time) } // f. Let offsetNs be GetNamedTimeZoneOffsetNanoseconds(systemTimeZoneIdentifier, disambiguatedInstant). - auto offset = get_named_time_zone_offset_nanoseconds(system_time_zone_identifier, disambiguated_instant); + auto offset = get_named_time_zone_offset_nanoseconds(system_time_zone_identifier.utf16_view().bytes(), disambiguated_instant); offset_nanoseconds = static_cast(offset.offset.to_nanoseconds()); } diff --git a/Libraries/LibJS/Runtime/Date.h b/Libraries/LibJS/Runtime/Date.h index 4c92ce1895..3c02743a1c 100644 --- a/Libraries/LibJS/Runtime/Date.h +++ b/Libraries/LibJS/Runtime/Date.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -28,7 +29,7 @@ public: double date_value() const { return m_date_value; } void set_date_value(double value) { m_date_value = value; } - ErrorOr iso_date_string() const; + Utf16String iso_date_string() const; private: Date(double date_value, Object& prototype); @@ -43,8 +44,8 @@ inline bool Object::fast_is() const { return is_date(); } // 21.4.1.22 Time Zone Identifier Record, https://tc39.es/ecma262/#sec-time-zone-identifier-record struct TimeZoneIdentifier { - String identifier; // [[Identifier]] - String primary_identifier; // [[PrimaryIdentifier]] + Utf16String identifier; // [[Identifier]] + Utf16String primary_identifier; // [[PrimaryIdentifier]] }; // https://tc39.es/ecma262/#eqn-HoursPerDay @@ -90,7 +91,7 @@ i64 clip_double_to_sane_time(double value); Vector get_named_time_zone_epoch_nanoseconds(StringView time_zone_identifier, Temporal::ISODateTime const&); Unicode::TimeZoneOffset get_named_time_zone_offset_nanoseconds(StringView time_zone_identifier, Crypto::SignedBigInteger const& epoch_nanoseconds); Unicode::TimeZoneOffset get_named_time_zone_offset_milliseconds(StringView time_zone_identifier, double epoch_milliseconds); -String system_time_zone_identifier(); +Utf16String system_time_zone_identifier(); JS_API void clear_system_time_zone_cache(); double local_time(double time); double utc_time(double time); diff --git a/Libraries/LibJS/Runtime/DatePrototype.cpp b/Libraries/LibJS/Runtime/DatePrototype.cpp index 77c4630b28..365e7712a6 100644 --- a/Libraries/LibJS/Runtime/DatePrototype.cpp +++ b/Libraries/LibJS/Runtime/DatePrototype.cpp @@ -7,7 +7,6 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include #include #include @@ -955,11 +954,12 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_date_string) // 3. If tv is NaN, return "Invalid Date". if (isnan(time)) - return PrimitiveString::create(vm, "Invalid Date"_string); + return PrimitiveString::create(vm, "Invalid Date"_utf16_fly_string); // 4. Let t be LocalTime(tv). // 5. Return DateString(t). - return PrimitiveString::create(vm, date_string(local_time(time))); + auto string = date_string(local_time(time)); + return PrimitiveString::create(vm, move(string)); } // 21.4.4.36 Date.prototype.toISOString ( ), https://tc39.es/ecma262/#sec-date.prototype.toisostring @@ -970,7 +970,7 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_iso_string) if (!Value(this_object->date_value()).is_finite_number()) return vm.throw_completion(ErrorType::InvalidTimeValue); - auto string = TRY_OR_THROW_OOM(vm, this_object->iso_date_string()); + auto string = this_object->iso_date_string(); return PrimitiveString::create(vm, move(string)); } @@ -1001,7 +1001,7 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_locale_date_string) // 2. If x is NaN, return "Invalid Date". if (isnan(time)) - return PrimitiveString::create(vm, "Invalid Date"_string); + return PrimitiveString::create(vm, "Invalid Date"_utf16_fly_string); // 3. Let dateFormat be ? CreateDateTimeFormat(%DateTimeFormat%, locales, options, "date", "date"). auto date_format = TRY(Intl::create_date_time_format(vm, realm.intrinsics().intl_date_time_format_constructor(), locales, options, Intl::OptionRequired::Date, Intl::OptionDefaults::Date)); @@ -1025,7 +1025,7 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_locale_string) // 2. If x is NaN, return "Invalid Date". if (isnan(time)) - return PrimitiveString::create(vm, "Invalid Date"_string); + return PrimitiveString::create(vm, "Invalid Date"_utf16_fly_string); // 3. Let dateFormat be ? CreateDateTimeFormat(%DateTimeFormat%, locales, options, "any", "all"). auto date_format = TRY(Intl::create_date_time_format(vm, realm.intrinsics().intl_date_time_format_constructor(), locales, options, Intl::OptionRequired::Any, Intl::OptionDefaults::All)); @@ -1049,7 +1049,7 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_locale_time_string) // 2. If x is NaN, return "Invalid Date". if (isnan(time)) - return PrimitiveString::create(vm, "Invalid Date"_string); + return PrimitiveString::create(vm, "Invalid Date"_utf16_fly_string); // 3. Let timeFormat be ? CreateDateTimeFormat(%DateTimeFormat%, locales, options, "time", "time"). auto time_format = TRY(Intl::create_date_time_format(vm, realm.intrinsics().intl_date_time_format_constructor(), locales, options, Intl::OptionRequired::Time, Intl::OptionDefaults::Time)); @@ -1071,17 +1071,17 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_string) // 21.4.4.41.1 TimeString ( tv ), https://tc39.es/ecma262/#sec-timestring // 14.5.8 TimeString ( tv ), https://tc39.es/proposal-temporal/#sec-timestring -ByteString time_string(double time) +Utf16String time_string(double time) { // 1. Let timeString be FormatTimeString(ℝ(HourFromTime(tv)), ℝ(MinFromTime(tv)), ℝ(SecFromTime(tv)), 0, 0). auto time_string = Temporal::format_time_string(hour_from_time(time), min_from_time(time), sec_from_time(time), 0, 0); // 4. Return the string-concatenation of timeString, the code unit 0x0020 (SPACE), and "GMT". - return ByteString::formatted("{} GMT", time_string); + return Utf16String::formatted("{} GMT", time_string); } // 21.4.4.41.2 DateString ( tv ), https://tc39.es/ecma262/#sec-datestring -ByteString date_string(double time) +Utf16String date_string(double time) { // 1. Let weekday be the Name of the entry in Table 62 with the Number WeekDay(tv). auto weekday = short_day_names[week_day(time)]; @@ -1100,7 +1100,7 @@ ByteString date_string(double time) // 6. Let paddedYear be ToZeroPaddedDecimalString(abs(ℝ(yv)), 4). // 7. Return the string-concatenation of weekday, the code unit 0x0020 (SPACE), month, the code unit 0x0020 (SPACE), day, the code unit 0x0020 (SPACE), yearSign, and paddedYear. - return ByteString::formatted("{} {} {:02} {}{:04}", weekday, month, day, year_sign, abs(year)); + return Utf16String::formatted("{} {} {:02} {}{:04}", weekday, month, day, year_sign, abs(year)); } // 21.4.4.41.3 TimeZoneString ( tv ), https://tc39.es/ecma262/#sec-timezoneestring @@ -1111,13 +1111,13 @@ Utf16String time_zone_string(double time) auto system_time_zone_identifier = JS::system_time_zone_identifier(); // 2. Let offsetMinutes be ! ParseTimeZoneIdentifier(systemTimeZoneIdentifier).[[OffsetMinutes]]. - auto offset_minutes = Temporal::parse_time_zone_identifier(system_time_zone_identifier).offset_minutes; + auto offset_minutes = Temporal::parse_time_zone_identifier(system_time_zone_identifier.utf16_view()).offset_minutes; auto in_dst = Unicode::TimeZoneOffset::InDST::No; // 2. If offsetMinutes is EMPTY, then if (!offset_minutes.has_value()) { // a. Let offsetNs be GetNamedTimeZoneOffsetNanoseconds(systemTimeZoneIdentifier, ℤ(ℝ(tv) × 10^6)). - auto offset = get_named_time_zone_offset_milliseconds(system_time_zone_identifier, time); + auto offset = get_named_time_zone_offset_milliseconds(system_time_zone_identifier.utf16_view().bytes(), time); in_dst = offset.in_dst; // b. Set offsetMinutes to truncate(offsetNs / (60 × 10**9)). @@ -1130,11 +1130,10 @@ Utf16String time_zone_string(double time) // 5. Let tzName be an implementation-defined string that is either the empty String or the string-concatenation of // the code unit 0x0020 (SPACE), the code unit 0x0028 (LEFT PARENTHESIS), an implementation-defined timezone name, // and the code unit 0x0029 (RIGHT PARENTHESIS). - auto time_zone_identifier = Unicode::current_time_zone(); - auto tz_name = Utf16String::from_utf8(time_zone_identifier); + auto tz_name = Unicode::current_time_zone(); // Most implementations seem to prefer the long-form display name of the time zone. Not super important, but we may as well match that behavior. - if (auto name = Unicode::time_zone_display_name(Unicode::default_locale(), time_zone_identifier, in_dst, time); name.has_value()) + if (auto name = Unicode::time_zone_display_name(Unicode::default_locale().bytes(), tz_name.utf16_view().bytes(), in_dst, time); name.has_value()) tz_name = name.release_value(); // 10. Return the string-concatenation of offsetString and tzName. @@ -1164,7 +1163,7 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_time_string) // 3. If tv is NaN, return "Invalid Date". if (isnan(time)) - return PrimitiveString::create(vm, "Invalid Date"_string); + return PrimitiveString::create(vm, "Invalid Date"_utf16_fly_string); // 4. Let t be LocalTime(tv). // 5. Return the string-concatenation of TimeString(t) and TimeZoneString(tv). @@ -1181,7 +1180,7 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_utc_string) // 3. If tv is NaN, return "Invalid Date". if (isnan(time)) - return PrimitiveString::create(vm, "Invalid Date"_string); + return PrimitiveString::create(vm, "Invalid Date"_utf16_fly_string); // 4. Let weekday be the Name of the entry in Table 62 with the Number WeekDay(tv). auto weekday = short_day_names[week_day(time)]; @@ -1200,7 +1199,7 @@ JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_utc_string) // 9. Let paddedYear be ToZeroPaddedDecimalString(abs(ℝ(yv)), 4). // 10. Return the string-concatenation of weekday, ",", the code unit 0x0020 (SPACE), day, the code unit 0x0020 (SPACE), month, the code unit 0x0020 (SPACE), yearSign, paddedYear, the code unit 0x0020 (SPACE), and TimeString(tv). - auto string = ByteString::formatted("{}, {:02} {} {}{:04} {}", weekday, day, month, year_sign, abs(year), time_string(time)); + auto string = Utf16String::formatted("{}, {:02} {} {}{:04} {}", weekday, day, month, year_sign, abs(year), time_string(time)); return PrimitiveString::create(vm, move(string)); } diff --git a/Libraries/LibJS/Runtime/DatePrototype.h b/Libraries/LibJS/Runtime/DatePrototype.h index 90d1495fe6..6fe55e1d70 100644 --- a/Libraries/LibJS/Runtime/DatePrototype.h +++ b/Libraries/LibJS/Runtime/DatePrototype.h @@ -76,8 +76,8 @@ private: }; ThrowCompletionOr this_time_value(VM&, Value value); -ByteString time_string(double time); -ByteString date_string(double time); +Utf16String time_string(double time); +Utf16String date_string(double time); Utf16String time_zone_string(double time); Utf16String to_date_string(double time); diff --git a/Libraries/LibJS/Runtime/Error.cpp b/Libraries/LibJS/Runtime/Error.cpp index 9721ea189d..42d1a0616a 100644 --- a/Libraries/LibJS/Runtime/Error.cpp +++ b/Libraries/LibJS/Runtime/Error.cpp @@ -25,6 +25,13 @@ GC::Ref Error::create(Realm& realm, Utf16String message) return error; } +GC::Ref Error::create(Realm& realm, Utf16View message) +{ + auto error = Error::create(realm); + error->set_message(message); + return error; +} + GC::Ref Error::create(Realm& realm, StringView message) { return create(realm, Utf16String::from_utf8(message)); @@ -78,6 +85,14 @@ void Error::set_message(Utf16String message) define_direct_property(vm.names.message, PrimitiveString::create(vm, move(message)), attr); } +void Error::set_message(Utf16View message) +{ + auto& vm = this->vm(); + + u8 attr = Attribute::Writable | Attribute::Configurable; + define_direct_property(vm.names.message, PrimitiveString::create(vm, message), attr); +} + #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \ GC_DEFINE_ALLOCATOR(ClassName); \ GC::Ref ClassName::create(Realm& realm) \ @@ -92,6 +107,13 @@ void Error::set_message(Utf16String message) return error; \ } \ \ + GC::Ref ClassName::create(Realm& realm, Utf16View message) \ + { \ + auto error = ClassName::create(realm); \ + error->set_message(message); \ + return error; \ + } \ + \ GC::Ref ClassName::create(Realm& realm, StringView message) \ { \ return create(realm, Utf16String::from_utf8(message)); \ diff --git a/Libraries/LibJS/Runtime/Error.h b/Libraries/LibJS/Runtime/Error.h index 7fe6aff4d2..464518a93e 100644 --- a/Libraries/LibJS/Runtime/Error.h +++ b/Libraries/LibJS/Runtime/Error.h @@ -26,6 +26,7 @@ class JS_API Error public: static GC::Ref create(Realm&); static GC::Ref create(Realm&, Utf16String message); + static GC::Ref create(Realm&, Utf16View message); static GC::Ref create(Realm&, StringView message); virtual ~Error() override = default; @@ -35,6 +36,7 @@ public: ThrowCompletionOr install_error_cause(Value options); void set_message(Utf16String); + void set_message(Utf16View); protected: explicit Error(Object& prototype); @@ -62,6 +64,7 @@ inline bool Object::fast_is() const { return is_error_object(); } public: \ static GC::Ref create(Realm&); \ static GC::Ref create(Realm&, Utf16String message); \ + static GC::Ref create(Realm&, Utf16View message); \ static GC::Ref create(Realm&, StringView message); \ \ explicit ClassName(Object& prototype); \ diff --git a/Libraries/LibJS/Runtime/ErrorPrototype.cpp b/Libraries/LibJS/Runtime/ErrorPrototype.cpp index 040a4580a9..5647867d1d 100644 --- a/Libraries/LibJS/Runtime/ErrorPrototype.cpp +++ b/Libraries/LibJS/Runtime/ErrorPrototype.cpp @@ -26,7 +26,7 @@ void ErrorPrototype::initialize(Realm& realm) auto& vm = this->vm(); Base::initialize(realm); u8 attr = Attribute::Writable | Attribute::Configurable; - define_direct_property(vm.names.name, PrimitiveString::create(vm, "Error"_string), attr); + define_direct_property(vm.names.name, PrimitiveString::create(vm, "Error"_utf16_fly_string), attr); define_direct_property(vm.names.message, PrimitiveString::create(vm, Utf16String {}), attr); define_native_function(realm, vm.names.toString, to_string, 0, attr); // Non standard property "stack" @@ -131,21 +131,21 @@ JS_DEFINE_NATIVE_FUNCTION(ErrorPrototype::stack_setter) return TRY(this_object.create_data_property_or_throw(vm.names.stack, vm.argument(0))); } -#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \ - GC_DEFINE_ALLOCATOR(PrototypeName); \ - \ - PrototypeName::PrototypeName(Realm& realm) \ - : PrototypeObject(realm.intrinsics().error_prototype()) \ - { \ - } \ - \ - void PrototypeName::initialize(Realm& realm) \ - { \ - auto& vm = this->vm(); \ - Base::initialize(realm); \ - u8 attr = Attribute::Writable | Attribute::Configurable; \ - define_direct_property(vm.names.name, PrimitiveString::create(vm, #ClassName##_string), attr); \ - define_direct_property(vm.names.message, PrimitiveString::create(vm, Utf16String {}), attr); \ +#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \ + GC_DEFINE_ALLOCATOR(PrototypeName); \ + \ + PrototypeName::PrototypeName(Realm& realm) \ + : PrototypeObject(realm.intrinsics().error_prototype()) \ + { \ + } \ + \ + void PrototypeName::initialize(Realm& realm) \ + { \ + auto& vm = this->vm(); \ + Base::initialize(realm); \ + u8 attr = Attribute::Writable | Attribute::Configurable; \ + define_direct_property(vm.names.name, PrimitiveString::create(vm, #ClassName##_utf16_fly_string), attr); \ + define_direct_property(vm.names.message, PrimitiveString::create(vm, Utf16String {}), attr); \ } JS_ENUMERATE_NATIVE_ERRORS diff --git a/Libraries/LibJS/Runtime/ErrorTypes.cpp b/Libraries/LibJS/Runtime/ErrorTypes.cpp index 0ad1bb7b42..2a3ed2294a 100644 --- a/Libraries/LibJS/Runtime/ErrorTypes.cpp +++ b/Libraries/LibJS/Runtime/ErrorTypes.cpp @@ -9,15 +9,8 @@ namespace JS { #define __ENUMERATE_JS_ERROR(name, message) \ - ErrorType const& ErrorType::name = *new ErrorType(message##sv); + ErrorType const& ErrorType::name = *new ErrorType(message##sv, Utf16View { message##sv }); JS_ENUMERATE_ERROR_TYPES(__ENUMERATE_JS_ERROR) #undef __ENUMERATE_JS_ERROR -Utf16String const& ErrorType::message() const -{ - if (m_message.is_empty()) - m_message = Utf16String::from_utf8_without_validation(m_format); - return m_message; -} - } diff --git a/Libraries/LibJS/Runtime/ErrorTypes.h b/Libraries/LibJS/Runtime/ErrorTypes.h index 01f979f8b9..1784ccbda8 100644 --- a/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Libraries/LibJS/Runtime/ErrorTypes.h @@ -8,6 +8,7 @@ #include #include +#include #include #define JS_ENUMERATE_ERROR_TYPES(M) \ @@ -320,16 +321,17 @@ public: #undef __ENUMERATE_JS_ERROR StringView format() const { return m_format; } - Utf16String const& message() const; + Utf16View message() const { return m_message; } private: - explicit ErrorType(StringView format) + explicit ErrorType(StringView format, Utf16View message) : m_format(format) + , m_message(message) { } StringView m_format; - mutable Utf16String m_message; + Utf16View m_message; }; } diff --git a/Libraries/LibJS/Runtime/FunctionConstructor.cpp b/Libraries/LibJS/Runtime/FunctionConstructor.cpp index 6425f1252a..7c06d7ed09 100644 --- a/Libraries/LibJS/Runtime/FunctionConstructor.cpp +++ b/Libraries/LibJS/Runtime/FunctionConstructor.cpp @@ -152,7 +152,7 @@ ThrowCompletionOr> FunctionConstructor::create auto rust_compilation = RustIntegration::compile_dynamic_function(vm, source_text, parameters_string, body_parse_string, kind); if (!rust_compilation.has_value()) - return vm.throw_completion("Failed to compile dynamic function"_string); + return vm.throw_completion("Failed to compile dynamic function"_utf16); if (rust_compilation->is_error()) return vm.throw_completion(rust_compilation->release_error()); function_data = rust_compilation->value(); diff --git a/Libraries/LibJS/Runtime/FunctionPrototype.cpp b/Libraries/LibJS/Runtime/FunctionPrototype.cpp index 23a749603c..8484d56071 100644 --- a/Libraries/LibJS/Runtime/FunctionPrototype.cpp +++ b/Libraries/LibJS/Runtime/FunctionPrototype.cpp @@ -35,7 +35,7 @@ void FunctionPrototype::initialize(Realm& realm) define_native_function(realm, vm.names.toString, to_string, 0, attr); define_native_function(realm, vm.well_known_symbol_has_instance(), symbol_has_instance, 1, 0, Bytecode::Builtin::OrdinaryHasInstance); define_direct_property(vm.names.length, Value(0), Attribute::Configurable); - define_direct_property(vm.names.name, PrimitiveString::create(vm, String {}), Attribute::Configurable); + define_direct_property(vm.names.name, PrimitiveString::create(vm, Utf16String {}), Attribute::Configurable); } ThrowCompletionOr FunctionPrototype::internal_call(ExecutionContext&, Value) @@ -214,12 +214,12 @@ JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::to_string) if (auto const* native_function = as_if(function)) { // NOTE: once we remove name(), the fallback here can simply be an empty string. auto const name = native_function->initial_name().value_or(native_function->name()); - return PrimitiveString::create(vm, ByteString::formatted("function {}() {{ [native code] }}", name)); + return PrimitiveString::create(vm, Utf16String::formatted("function {}() {{ [native code] }}", name)); } // 4. If Type(func) is Object and IsCallable(func) is true, return an implementation-defined String source code representation of func. The representation must have the syntax of a NativeFunction. // NOTE: ProxyObject, BoundFunction, WrappedFunction - return PrimitiveString::create(vm, "function () { [native code] }"_string); + return PrimitiveString::create(vm, "function () { [native code] }"_utf16_fly_string); } // 20.2.3.6 Function.prototype [ @@hasInstance ] ( V ), https://tc39.es/ecma262/#sec-function.prototype-@@hasinstance diff --git a/Libraries/LibJS/Runtime/GeneratorFunctionPrototype.cpp b/Libraries/LibJS/Runtime/GeneratorFunctionPrototype.cpp index d53c7eb2fa..971124ca7d 100644 --- a/Libraries/LibJS/Runtime/GeneratorFunctionPrototype.cpp +++ b/Libraries/LibJS/Runtime/GeneratorFunctionPrototype.cpp @@ -25,7 +25,7 @@ void GeneratorFunctionPrototype::initialize(Realm& realm) // 27.3.3.2 GeneratorFunction.prototype.prototype, https://tc39.es/ecma262/#sec-generatorfunction.prototype.prototype define_direct_property(vm.names.prototype, realm.intrinsics().generator_prototype(), Attribute::Configurable); // 27.3.3.3 GeneratorFunction.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-generatorfunction.prototype-@@tostringtag - define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "GeneratorFunction"_string), Attribute::Configurable); + define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "GeneratorFunction"_utf16_fly_string), Attribute::Configurable); } } diff --git a/Libraries/LibJS/Runtime/GeneratorPrototype.cpp b/Libraries/LibJS/Runtime/GeneratorPrototype.cpp index 7cedd52a84..f54751dd6f 100644 --- a/Libraries/LibJS/Runtime/GeneratorPrototype.cpp +++ b/Libraries/LibJS/Runtime/GeneratorPrototype.cpp @@ -27,7 +27,7 @@ void GeneratorPrototype::initialize(Realm& realm) define_native_function(realm, vm.names.throw_, throw_, 1, attr); // 27.5.1.5 Generator.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-generator.prototype-@@tostringtag - define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Generator"_string), Attribute::Configurable); + define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Generator"_utf16_fly_string), Attribute::Configurable); } static Value generator_resume_result_to_value(VM& vm, GeneratorObject::IterationResult const& iteration_result) diff --git a/Libraries/LibJS/Runtime/GlobalObject.cpp b/Libraries/LibJS/Runtime/GlobalObject.cpp index 85766eac4f..ca4daad9da 100644 --- a/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -366,15 +367,13 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_int) } // 19.2.6.5 Encode ( string, extraUnescaped ), https://tc39.es/ecma262/#sec-encode -static ThrowCompletionOr encode(VM& vm, ByteString const& string, StringView unescaped_set) +static ThrowCompletionOr encode(VM& vm, Utf16View const& string, StringView unescaped_set) { - auto utf16_string = Utf16String::from_utf8(string); - // 1. Let strLen be the length of string. - auto string_length = utf16_string.length_in_code_units(); + auto string_length = string.length_in_code_units(); // 2. Let R be the empty String. - StringBuilder encoded_builder; + StringBuilder encoded_builder(StringBuilder::Mode::UTF16); // 3. Let alwaysUnescaped be the string-concatenation of the ASCII word characters and "-.!~*'()". // 4. Let unescapedSet be the string-concatenation of alwaysUnescaped and extraUnescaped. @@ -389,7 +388,7 @@ static ThrowCompletionOr encode(VM& vm, ByteString const& string, St // Handled below // b. Let C be the code unit at index k within string. - auto code_unit = utf16_string.code_unit_at(k); + auto code_unit = string.code_unit_at(k); // c. If C is in unescapedSet, then // NOTE: We assume the unescaped set only contains ascii characters as unescaped_set is a StringView. if (code_unit < 0x80 && unescaped_set.contains(static_cast(code_unit))) { @@ -402,7 +401,7 @@ static ThrowCompletionOr encode(VM& vm, ByteString const& string, St // d. Else, else { // i. Let cp be CodePointAt(string, k). - auto code_point = code_point_at(utf16_string, k); + auto code_point = code_point_at(string, k); // ii. If cp.[[IsUnpairedSurrogate]] is true, throw a URIError exception. if (code_point.is_unpaired_surrogate) return vm.throw_completion(ErrorType::URIMalformed); @@ -420,78 +419,79 @@ static ThrowCompletionOr encode(VM& vm, ByteString const& string, St VERIFY(nwritten > 0); } } - return encoded_builder.to_byte_string(); + return encoded_builder.to_utf16_string(); +} + +static ThrowCompletionOr decode_percent_encoded_byte(VM& vm, Utf16View const& string, size_t percent_index) +{ + if (percent_index + 2 >= string.length_in_code_units()) + return vm.throw_completion(ErrorType::URIMalformed); + + auto first_digit = string.code_unit_at(percent_index + 1); + if (!is_ascii_hex_digit(first_digit)) + return vm.throw_completion(ErrorType::URIMalformed); + + auto second_digit = string.code_unit_at(percent_index + 2); + if (!is_ascii_hex_digit(second_digit)) + return vm.throw_completion(ErrorType::URIMalformed); + + return (parse_ascii_hex_digit(first_digit) << 4) | parse_ascii_hex_digit(second_digit); } // 19.2.6.6 Decode ( string, preserveEscapeSet ), https://tc39.es/ecma262/#sec-decode // FIXME: Add spec comments to this implementation. It deviates a lot, so that's a bit tricky. -static ThrowCompletionOr decode(VM& vm, ByteString const& string, StringView reserved_set) +static ThrowCompletionOr decode(VM& vm, Utf16View const& string, StringView reserved_set) { - StringBuilder decoded_builder; - auto code_point_start_offset = 0u; - auto expected_continuation_bytes = 0; - for (size_t k = 0; k < string.length(); k++) { - auto code_unit = string[k]; + Utf16StringBuilder decoded_builder; + for (size_t k = 0; k < string.length_in_code_units(); ++k) { + auto code_unit = string.code_unit_at(k); if (code_unit != '%') { - if (expected_continuation_bytes > 0) - return vm.throw_completion(ErrorType::URIMalformed); - - decoded_builder.append(code_unit); + decoded_builder.append_code_unit(code_unit); continue; } - if (k + 2 >= string.length()) - return vm.throw_completion(ErrorType::URIMalformed); - - auto first_digit = decode_hex_digit(string[k + 1]); - if (first_digit >= 16) - return vm.throw_completion(ErrorType::URIMalformed); - - auto second_digit = decode_hex_digit(string[k + 2]); - if (second_digit >= 16) - return vm.throw_completion(ErrorType::URIMalformed); - - u8 decoded_code_unit = (first_digit << 4) | second_digit; + auto decoded_code_unit = TRY(decode_percent_encoded_byte(vm, string, k)); k += 2; - if (expected_continuation_bytes > 0) { - decoded_builder.append(decoded_code_unit); - expected_continuation_bytes--; - if (expected_continuation_bytes == 0 && !Utf8View(decoded_builder.string_view().substring_view(code_point_start_offset)).validate(AllowLonelySurrogates::No)) - return vm.throw_completion(ErrorType::URIMalformed); - continue; - } if (decoded_code_unit < 0x80) { if (reserved_set.contains(static_cast(decoded_code_unit))) decoded_builder.append(string.substring_view(k - 2, 3)); else - decoded_builder.append(decoded_code_unit); + decoded_builder.append_code_unit(decoded_code_unit); continue; } - auto leading_ones = count_leading_zeroes_safe(static_cast(~decoded_code_unit)); + auto leading_ones = static_cast(count_leading_zeroes_safe(static_cast(~decoded_code_unit))); if (leading_ones == 1 || leading_ones > 4) return vm.throw_completion(ErrorType::URIMalformed); - code_point_start_offset = decoded_builder.length(); - decoded_builder.append(decoded_code_unit); - expected_continuation_bytes = leading_ones - 1; + u8 utf8_bytes[4] { decoded_code_unit }; + for (auto byte_index = 1u; byte_index < leading_ones; ++byte_index) { + if (k + 3 >= string.length_in_code_units() || string.code_unit_at(k + 1) != '%') + return vm.throw_completion(ErrorType::URIMalformed); + utf8_bytes[byte_index] = TRY(decode_percent_encoded_byte(vm, string, k + 1)); + k += 3; + } + + auto utf8_view = Utf8View { StringView { reinterpret_cast(utf8_bytes), leading_ones } }; + if (!utf8_view.validate(AllowLonelySurrogates::No)) + return vm.throw_completion(ErrorType::URIMalformed); + for (auto decoded_code_point : utf8_view) + decoded_builder.append_code_point(decoded_code_point); } - if (expected_continuation_bytes > 0) - return vm.throw_completion(ErrorType::URIMalformed); - return decoded_builder.to_byte_string(); + return decoded_builder.to_string(); } // 19.2.6.1 decodeURI ( encodedURI ), https://tc39.es/ecma262/#sec-decodeuri-encodeduri JS_DEFINE_NATIVE_FUNCTION(GlobalObject::decode_uri) { // 1. Let uriString be ? ToString(encodedURI). - auto uri_string = TRY(vm.argument(0).to_byte_string(vm)); + auto uri_string = TRY(vm.argument(0).to_utf16_string(vm)); // 2. Let preserveEscapeSet be ";/?:@&=+$,#". // 3. Return ? Decode(uriString, preserveEscapeSet). - auto decoded = TRY(decode(vm, uri_string, ";/?:@&=+$,#"sv)); - return PrimitiveString::create(vm, move(decoded)); + auto decoded = TRY(decode(vm, uri_string.utf16_view(), ";/?:@&=+$,#"sv)); + return PrimitiveString::create(vm, decoded); } // 19.2.6.2 decodeURIComponent ( encodedURIComponent ), https://tc39.es/ecma262/#sec-decodeuricomponent-encodeduricomponent @@ -500,12 +500,12 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::decode_uri_component) auto encoded_uri_component = vm.argument(0); // 1. Let componentString be ? ToString(encodedURIComponent). - auto uri_string = TRY(encoded_uri_component.to_byte_string(vm)); + auto uri_string = TRY(encoded_uri_component.to_utf16_string(vm)); // 2. Let preserveEscapeSet be the empty String. // 3. Return ? Decode(componentString, preserveEscapeSet). - auto decoded = TRY(decode(vm, uri_string, ""sv)); - return PrimitiveString::create(vm, move(decoded)); + auto decoded = TRY(decode(vm, uri_string.utf16_view(), ""sv)); + return PrimitiveString::create(vm, decoded); } // 19.2.6.3 encodeURI ( uri ), https://tc39.es/ecma262/#sec-encodeuri-uri @@ -514,11 +514,11 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::encode_uri) auto uri = vm.argument(0); // 1. Let uriString be ? ToString(uri). - auto uri_string = TRY(uri.to_byte_string(vm)); + auto uri_string = TRY(uri.to_utf16_string(vm)); // 2. Let extraUnescaped be ";/?:@&=+$,#". // 3. Return ? Encode(uriString, extraUnescaped). - auto encoded = TRY(encode(vm, uri_string, ";/?:@&=+$,abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()#"sv)); + auto encoded = TRY(encode(vm, uri_string.utf16_view(), ";/?:@&=+$,abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()#"sv)); return PrimitiveString::create(vm, move(encoded)); } @@ -528,11 +528,11 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::encode_uri_component) auto uri_component = vm.argument(0); // 1. Let componentString be ? ToString(uriComponent). - auto uri_string = TRY(uri_component.to_byte_string(vm)); + auto uri_string = TRY(uri_component.to_utf16_string(vm)); // 2. Let extraUnescaped be the empty String. // 3. Return ? Encode(componentString, extraUnescaped). - auto encoded = TRY(encode(vm, uri_string, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()"sv)); + auto encoded = TRY(encode(vm, uri_string.utf16_view(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()"sv)); return PrimitiveString::create(vm, move(encoded)); } @@ -543,7 +543,7 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::escape) auto string = TRY(vm.argument(0).to_utf16_string(vm)); // 3. Let R be the empty String. - StringBuilder escaped; + StringBuilder escaped(StringBuilder::Mode::UTF16); // 4. Let unescapedSet be the string-concatenation of the ASCII word characters and "@*+-./". auto unescaped_set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./"sv; @@ -581,7 +581,7 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::escape) } // 7. Return R. - return PrimitiveString::create(vm, escaped.to_byte_string()); + return PrimitiveString::create(vm, escaped.to_utf16_string()); } // B.2.1.2 unescape ( string ), https://tc39.es/ecma262/#sec-unescape-string @@ -598,7 +598,7 @@ JS_DEFINE_NATIVE_FUNCTION(GlobalObject::unescape) // 4. Let k be 0. // 5. Repeat, while k ≠ length, - for (auto k = 0; k < length; ++k) { + for (size_t k = 0; k < length; ++k) { // a. Let c be the code unit at index k within string. u16 code_unit = string.code_unit_at(k); diff --git a/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp b/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp index 81cf582036..ddf706c906 100644 --- a/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp +++ b/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp @@ -34,7 +34,7 @@ static bool is_well_formed_language_tag_impl(ViewType locale) quick_sort(variants); for (size_t i = 0; i < variants.size() - 1; ++i) { - if (variants[i].equals_ignoring_case(variants[i + 1])) + if (variants[i].equals_ignoring_ascii_case(variants[i + 1])) return true; } @@ -121,12 +121,12 @@ bool is_well_formed_language_tag(Utf16View locale) } // 6.2.2 CanonicalizeUnicodeLocaleId ( locale ), https://tc39.es/ecma402/#sec-canonicalizeunicodelocaleid -String canonicalize_unicode_locale_id(StringView locale) +Utf16String canonicalize_unicode_locale_id(StringView locale) { return Unicode::canonicalize_unicode_locale_id(locale); } -String canonicalize_unicode_locale_id(Utf16View locale) +Utf16String canonicalize_unicode_locale_id(Utf16View locale) { return Unicode::canonicalize_unicode_locale_id(locale); } @@ -189,8 +189,8 @@ Vector const& available_named_time_zone_identifiers() auto primary = identifier; // b. If identifier is a Link name and identifier is not "UTC", then - if (identifier != "UTC"sv) { - if (auto resolved = Unicode::resolve_primary_time_zone(identifier); resolved.has_value() && identifier != resolved) { + if (identifier.utf16_view() != "UTC"sv) { + if (auto resolved = Unicode::resolve_primary_time_zone(identifier.utf16_view().bytes()); resolved.has_value() && identifier != *resolved) { // i. Set primary to the Zone name that identifier resolves to, according to the rules for resolving Link // names in the IANA Time Zone Database. primary = resolved.release_value(); @@ -200,16 +200,19 @@ Vector const& available_named_time_zone_identifiers() } // c. If primary is one of "Etc/UTC", "Etc/GMT", or "GMT", set primary to "UTC". - if (primary.is_one_of("Etc/UTC"sv, "Etc/GMT"sv, "GMT"sv)) - primary = "UTC"_string; + if (primary.utf16_view().is_one_of("Etc/UTC"sv, "Etc/GMT"sv, "GMT"sv)) + primary = "UTC"_utf16; // d. Let record be the Time Zone Identifier Record { [[Identifier]]: identifier, [[PrimaryIdentifier]]: primary }. - TimeZoneIdentifier record { .identifier = identifier, .primary_identifier = primary }; + TimeZoneIdentifier record { + .identifier = identifier, + .primary_identifier = primary, + }; // e. Append record to result. result.unchecked_append(move(record)); - if (!found_utc && identifier == "UTC"sv && primary == "UTC"sv) + if (!found_utc && identifier.utf16_view() == "UTC"sv && primary.utf16_view() == "UTC"sv) found_utc = true; } @@ -224,7 +227,7 @@ Vector const& available_named_time_zone_identifiers() } // 6.5.2 GetAvailableNamedTimeZoneIdentifier ( timeZoneIdentifier ), https://tc39.es/ecma402/#sec-getavailablenamedtimezoneidentifier -Optional get_available_named_time_zone_identifier(StringView time_zone_identifier) +Optional get_available_named_time_zone_identifier(Utf16View time_zone_identifier) { // 1. For each element record of AvailableNamedTimeZoneIdentifiers(), do for (auto const& record : available_named_time_zone_identifiers()) { @@ -287,18 +290,18 @@ bool is_well_formed_unit_identifier(Utf16View unit_identifier) } // 9.2.1 CanonicalizeLocaleList ( locales ), https://tc39.es/ecma402/#sec-canonicalizelocalelist -ThrowCompletionOr> canonicalize_locale_list(VM& vm, Value locales) +ThrowCompletionOr> canonicalize_locale_list(VM& vm, Value locales) { auto& realm = *vm.current_realm(); // 1. If locales is undefined, then if (locales.is_undefined()) { // a. Return a new empty List. - return Vector {}; + return Vector {}; } // 2. Let seen be a new empty List. - Vector seen; + Vector seen; Object* object = nullptr; // 3. If Type(locales) is String or Type(locales) is Object and locales has an [[InitializedLocale]] internal slot, then @@ -334,12 +337,12 @@ ThrowCompletionOr> canonicalize_locale_list(VM& vm, Value locales if (!key_value.is_string() && !key_value.is_object()) return vm.throw_completion(ErrorType::NotAnObjectOrString, key_value); - String canonicalized_tag; + Utf16String canonicalized_tag; // iii. If Type(kValue) is Object and kValue has an [[InitializedLocale]] internal slot, then if (auto locale = key_value.as_if()) { // 1. Let tag be kValue.[[Locale]]. - auto tag = locale->locale(); + auto tag = locale->locale().utf16_view(); // v. If IsWellFormedLanguageTag(tag) is false, throw a RangeError exception. if (!is_well_formed_language_tag(tag)) @@ -374,17 +377,15 @@ ThrowCompletionOr> canonicalize_locale_list(VM& vm, Value locales } // 9.2.3 LookupMatchingLocaleByPrefix ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-lookupmatchinglocalebyprefix -Optional lookup_matching_locale_by_prefix(ReadonlySpan requested_locales) +Optional lookup_matching_locale_by_prefix(ReadonlySpan requested_locales) { // 1. For each element locale of requestedLocales, do for (auto locale : requested_locales) { - auto locale_id = Unicode::parse_unicode_locale_id(locale); + auto locale_id = Unicode::parse_unicode_locale_id(locale.utf16_view()); VERIFY(locale_id.has_value()); // a. Let extension be empty. Optional extension; - String locale_without_extension; - // b. If locale contains a Unicode locale extension sequence, then if (auto extensions = locale_id->remove_extension_type(); !extensions.is_empty()) { VERIFY(extensions.size() == 1); @@ -393,24 +394,24 @@ Optional lookup_matching_locale_by_prefix(ReadonlySpan re extension = extensions.take_first(); // ii. Set locale to the String value that is locale with any Unicode locale extension sequences removed. - locale = locale_id->to_string(); + locale = locale_id->to_utf16_string(); } // c. Let prefix be locale. - StringView prefix { locale }; + auto prefix = locale.utf16_view(); // d. Repeat, while prefix is not the empty String, while (!prefix.is_empty()) { // i. If availableLocales contains prefix, return the Record { [[locale]]: prefix, [[extension]]: extension }. - if (Unicode::is_locale_available(prefix)) - return MatchedLocale { MUST(String::from_utf8(prefix)), move(extension) }; + if (Unicode::is_locale_available(prefix.bytes())) + return MatchedLocale { Utf16String::from_utf16(prefix), move(extension) }; // ii. If prefix contains "-" (code unit 0x002D HYPHEN-MINUS), let pos be the index into prefix of the last // occurrence of "-"; else let pos be 0. - auto position = prefix.find_last('-').value_or(0); + auto position = prefix.find_last_code_point_offset('-').value_or(0); // iii. Repeat, while pos ≥ 2 and the substring of prefix from pos - 2 to pos - 1 is "-", - while (position >= 2 && prefix.substring_view(position - 2, 1) == '-') { + while (position >= 2 && prefix.substring_view(position - 2, 1) == "-"sv) { // 1. Set pos to pos - 2. position -= 2; } @@ -425,7 +426,7 @@ Optional lookup_matching_locale_by_prefix(ReadonlySpan re } // 9.2.4 LookupMatchingLocaleByBestFit ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-lookupmatchinglocalebybestfit -Optional lookup_matching_locale_by_best_fit(ReadonlySpan requested_locales) +Optional lookup_matching_locale_by_best_fit(ReadonlySpan requested_locales) { // The algorithm is implementation dependent, but should produce results that a typical user of the requested locales // would consider at least as good as those produced by the LookupMatchingLocaleByPrefix algorithm. @@ -433,7 +434,7 @@ Optional lookup_matching_locale_by_best_fit(ReadonlySpan } // 9.2.6 InsertUnicodeExtensionAndCanonicalize ( locale, attributes, keywords ), https://tc39.es/ecma402/#sec-insert-unicode-extension-and-canonicalize -String insert_unicode_extension_and_canonicalize(Unicode::LocaleID locale, Vector attributes, Vector keywords) +Utf16String insert_unicode_extension_and_canonicalize(Unicode::LocaleID locale, Vector attributes, Vector keywords) { // Note: This implementation differs from the spec in how the extension is inserted. The spec assumes // the input to this method is a string, and is written such that operations are performed on parts @@ -442,11 +443,11 @@ String insert_unicode_extension_and_canonicalize(Unicode::LocaleID locale, Vecto locale.extensions.append(Unicode::LocaleExtension { move(attributes), move(keywords) }); // 10. Return CanonicalizeUnicodeLocaleId(newLocale). - return JS::Intl::canonicalize_unicode_locale_id(locale.to_string()); + return JS::Intl::canonicalize_unicode_locale_id(locale.to_utf16_string()); } template -static auto& find_key_in_value(T& value, StringView key) +static auto& find_key_in_value(T& value, Utf16View key) { if (key == "ca"sv) return value.ca; @@ -465,7 +466,7 @@ static auto& find_key_in_value(T& value, StringView key) VERIFY_NOT_REACHED(); } -static Vector available_keyword_values(StringView locale, StringView key) +static Vector available_keyword_values(Utf16View locale, Utf16View key) { auto key_locale_data = Unicode::available_keyword_values(locale, key); @@ -485,9 +486,9 @@ static Vector available_keyword_values(StringView locale, StringView } // 9.2.7 ResolveLocale ( availableLocales, requestedLocales, options, relevantExtensionKeys, localeData ), https://tc39.es/ecma402/#sec-resolvelocale -ResolvedLocale resolve_locale(ReadonlySpan requested_locales, LocaleOptions const& options, ReadonlySpan relevant_extension_keys) +ResolvedLocale resolve_locale(ReadonlySpan requested_locales, LocaleOptions const& options, ReadonlySpan relevant_extension_keys) { - static auto true_string = "true"_string; + auto true_string = "true"_utf16; // 1. Let matcher be options.[[localeMatcher]]. auto const& matcher = options.locale_matcher; @@ -507,7 +508,7 @@ ResolvedLocale resolve_locale(ReadonlySpan requested_locales, LocaleOpti // 4. If r is undefined, set r to the Record { [[locale]]: DefaultLocale(), [[extension]]: empty }. if (!matcher_result.has_value()) - matcher_result = MatchedLocale { MUST(String::from_utf8(Unicode::default_locale())), {} }; + matcher_result = MatchedLocale { Utf16String::from_utf16(Unicode::default_locale()), {} }; // 5. Let foundLocale be r.[[locale]]. auto found_locale = move(matcher_result->locale); @@ -541,7 +542,7 @@ ResolvedLocale resolve_locale(ReadonlySpan requested_locales, LocaleOpti for (auto const& key : relevant_extension_keys) { // a. Let keyLocaleData be foundLocaleData.[[]]. // b. Assert: keyLocaleData is a List. - auto key_locale_data = available_keyword_values(found_locale, key); + auto key_locale_data = available_keyword_values(found_locale.utf16_view(), key); // c. Let value be keyLocaleData[0]. // d. Assert: value is a String or value is null. @@ -551,7 +552,7 @@ ResolvedLocale resolve_locale(ReadonlySpan requested_locales, LocaleOpti Optional supported_keyword; // f. If keywords contains an element whose [[Key]] is key, then - if (auto entry = keywords.find_if([&](auto const& entry) { return entry.key == key; }); entry != keywords.end()) { + if (auto entry = keywords.find_if([&](auto const& entry) { return entry.key.utf16_view() == key; }); entry != keywords.end()) { // i. Let entry be the element of keywords whose [[Key]] is key. // ii. Let requestedValue be entry.[[Value]]. auto requested_value = entry->value; @@ -564,7 +565,7 @@ ResolvedLocale resolve_locale(ReadonlySpan requested_locales, LocaleOpti value = move(requested_value); // b. Set supportedKeyword to the Record { [[Key]]: key, [[Value]]: value }. - supported_keyword = Unicode::Keyword { MUST(String::from_utf8(key)), move(entry->value) }; + supported_keyword = Unicode::Keyword { Utf16String::from_utf16(key), move(entry->value) }; } } // iv. Else if keyLocaleData contains "true", then @@ -573,7 +574,7 @@ ResolvedLocale resolve_locale(ReadonlySpan requested_locales, LocaleOpti value = true_string; // 2. Set supportedKeyword to the Record { [[Key]]: key, [[Value]]: "" }. - supported_keyword = Unicode::Keyword { MUST(String::from_utf8(key)), {} }; + supported_keyword = Unicode::Keyword { Utf16String::from_utf16(key), {} }; } } @@ -583,13 +584,14 @@ ResolvedLocale resolve_locale(ReadonlySpan requested_locales, LocaleOpti auto options_value = find_key_in_value(options, key); // j. If optionsValue is a String, then - if (auto* options_string = options_value.has_value() ? options_value->get_pointer() : nullptr) { + if (auto* options_string = options_value.has_value() ? options_value->get_pointer() : nullptr) { // i. Let ukey be the ASCII-lowercase of key. // NOTE: `key` is always lowercase, and this step is likely to be removed: // https://github.com/tc39/ecma402/pull/846#discussion_r1428263375 // ii. Set optionsValue to CanonicalizeUValue(ukey, optionsValue). - *options_string = Unicode::canonicalize_unicode_extension_values(key, *options_string); + auto canonicalized = Unicode::canonicalize_unicode_extension_values(key.bytes(), options_string->utf16_view()); + *options_string = move(canonicalized); // iii. If optionsValue is the empty String, then if (options_string->is_empty()) { @@ -611,31 +613,36 @@ ResolvedLocale resolve_locale(ReadonlySpan requested_locales, LocaleOpti if (supported_keyword.has_value()) supported_keywords.append(supported_keyword.release_value()); - if (auto* value_string = value.get_pointer()) - icu_keywords.empend(MUST(String::from_utf8(key)), *value_string); + if (auto* value_string = value.get_pointer()) + icu_keywords.empend(Utf16String::from_utf16(key), *value_string); // m. Set result.[[]] to value. - find_key_in_value(result, key) = move(value); + if (auto* value_string = value.get_pointer()) + find_key_in_value(result, key) = *value_string; + else + find_key_in_value(result, key) = Empty {}; } // AD-HOC: For ICU, we need to form a locale with all relevant extension keys present. if (icu_keywords.is_empty()) { result.icu_locale = found_locale; } else { - auto locale_id = Unicode::parse_unicode_locale_id(found_locale); + auto locale_id = Unicode::parse_unicode_locale_id(found_locale.utf16_view()); VERIFY(locale_id.has_value()); - result.icu_locale = insert_unicode_extension_and_canonicalize(locale_id.release_value(), {}, move(icu_keywords)); + auto icu_locale = insert_unicode_extension_and_canonicalize(locale_id.release_value(), {}, move(icu_keywords)); + result.icu_locale = move(icu_locale); } // 14. If supportedKeywords is not empty, then if (!supported_keywords.is_empty()) { - auto locale_id = Unicode::parse_unicode_locale_id(found_locale); + auto locale_id = Unicode::parse_unicode_locale_id(found_locale.utf16_view()); VERIFY(locale_id.has_value()); // a. Let supportedAttributes be a new empty List. // b. Set foundLocale to InsertUnicodeExtensionAndCanonicalize(foundLocale, supportedAttributes, supportedKeywords). - found_locale = insert_unicode_extension_and_canonicalize(locale_id.release_value(), {}, move(supported_keywords)); + auto supported_locale = insert_unicode_extension_and_canonicalize(locale_id.release_value(), {}, move(supported_keywords)); + found_locale = move(supported_locale); } // 15. Set result.[[Locale]] to foundLocale. @@ -662,7 +669,7 @@ ThrowCompletionOr resolve_options(VM& vm, IntlObject& object, V : TRY(get_options_object(vm, options_value)); // 4. Let matcher be ? GetOption(options, "localeMatcher", STRING, « "lookup", "best fit" », "best fit"). - auto matcher = TRY(get_option(vm, options, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); + auto matcher = TRY(get_option(vm, options, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, u"best fit"sv)); // 5. Let opt be the Record { [[localeMatcher]]: matcher }. LocaleOptions opt {}; @@ -687,10 +694,10 @@ ThrowCompletionOr resolve_options(VM& vm, IntlObject& object, V auto value_string_view = value_string.utf16_view(); // ii. If value cannot be matched by the type Unicode locale nonterminal, throw a RangeError exception. - if (!value_string_view.is_ascii() || !Unicode::is_type_identifier(value_string_view)) + if (!value_string_view.has_ascii_storage() || !Unicode::is_type_identifier(value_string_view)) return vm.throw_completion(ErrorType::OptionIsNotValidValue, value_string_view, descriptor.property); - locale_key = MUST(value_string_view.to_utf8()); + locale_key = move(value_string); } // e. Let key be desc.[[Key]]. @@ -715,7 +722,7 @@ ThrowCompletionOr resolve_options(VM& vm, IntlObject& object, V } // 9.2.9 FilterLocales ( availableLocales, requestedLocales, options ), https://tc39.es/ecma402/#sec-lookupsupportedlocales -ThrowCompletionOr> filter_locales(VM& vm, ReadonlySpan requested_locales, Value options_value) +ThrowCompletionOr> filter_locales(VM& vm, ReadonlySpan requested_locales, Value options_value) { auto& realm = *vm.current_realm(); @@ -723,10 +730,10 @@ ThrowCompletionOr> filter_locales(VM& vm, ReadonlySpan re auto options = TRY(coerce_options_to_object(vm, options_value)); // 2. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). - auto matcher = TRY(get_option(vm, options, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); + auto matcher = TRY(get_option(vm, options, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, u"best fit"sv)); // 3. Let subset be a new empty List. - Vector subset; + Vector subset; // 4. For each element locale of requestedLocales, do for (auto const& locale : requested_locales) { @@ -749,7 +756,7 @@ ThrowCompletionOr> filter_locales(VM& vm, ReadonlySpan re } // 5. Return CreateArrayFromList(subset). - return Array::create_from(realm, subset, [&vm](auto& locale) { return PrimitiveString::create(vm, move(locale)); }); + return Array::create_from(realm, subset, [&vm](auto const& locale) { return PrimitiveString::create(vm, locale); }); } // 9.2.11 CoerceOptionsToObject ( options ), https://tc39.es/ecma402/#sec-coerceoptionstoobject diff --git a/Libraries/LibJS/Runtime/Intl/AbstractOperations.h b/Libraries/LibJS/Runtime/Intl/AbstractOperations.h index 25b2377408..f11678183e 100644 --- a/Libraries/LibJS/Runtime/Intl/AbstractOperations.h +++ b/Libraries/LibJS/Runtime/Intl/AbstractOperations.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -20,7 +21,8 @@ namespace JS::Intl { -using LocaleKey = Variant; +using LocaleKey = Variant; +using ResolvedLocaleKey = Variant; struct LocaleOptions { Value locale_matcher; @@ -34,19 +36,19 @@ struct LocaleOptions { }; struct MatchedLocale { - String locale; + Utf16String locale; Optional extension; }; struct ResolvedLocale { - String locale; - String icu_locale; - LocaleKey ca; // [[Calendar]] - LocaleKey co; // [[Collation]] - LocaleKey hc; // [[HourCycle]] - LocaleKey kf; // [[CaseFirst]] - LocaleKey kn; // [[Numeric]] - LocaleKey nu; // [[NumberingSystem]] + Utf16String locale; + Utf16String icu_locale; + ResolvedLocaleKey ca; // [[Calendar]] + ResolvedLocaleKey co; // [[Collation]] + ResolvedLocaleKey hc; // [[HourCycle]] + ResolvedLocaleKey kf; // [[CaseFirst]] + ResolvedLocaleKey kn; // [[Numeric]] + ResolvedLocaleKey nu; // [[NumberingSystem]] }; struct ResolvedOptions { @@ -66,20 +68,20 @@ using StringOrBoolean = Variant; bool is_well_formed_language_tag(StringView locale); bool is_well_formed_language_tag(Utf16View locale); -String canonicalize_unicode_locale_id(StringView locale); -String canonicalize_unicode_locale_id(Utf16View locale); +Utf16String canonicalize_unicode_locale_id(StringView locale); +Utf16String canonicalize_unicode_locale_id(Utf16View locale); bool is_well_formed_currency_code(StringView currency); bool is_well_formed_currency_code(Utf16View currency); Vector const& available_named_time_zone_identifiers(); -Optional get_available_named_time_zone_identifier(StringView time_zone_identifier); +Optional get_available_named_time_zone_identifier(Utf16View time_zone_identifier); bool is_well_formed_unit_identifier(Utf16View unit_identifier); -ThrowCompletionOr> canonicalize_locale_list(VM&, Value locales); -Optional lookup_matching_locale_by_prefix(ReadonlySpan requested_locales); -Optional lookup_matching_locale_by_best_fit(ReadonlySpan requested_locales); -String insert_unicode_extension_and_canonicalize(Unicode::LocaleID locale_id, Vector attributes, Vector keywords); -ResolvedLocale resolve_locale(ReadonlySpan requested_locales, LocaleOptions const& options, ReadonlySpan relevant_extension_keys); +ThrowCompletionOr> canonicalize_locale_list(VM&, Value locales); +Optional lookup_matching_locale_by_prefix(ReadonlySpan requested_locales); +Optional lookup_matching_locale_by_best_fit(ReadonlySpan requested_locales); +Utf16String insert_unicode_extension_and_canonicalize(Unicode::LocaleID locale_id, Vector attributes, Vector keywords); +ResolvedLocale resolve_locale(ReadonlySpan requested_locales, LocaleOptions const& options, ReadonlySpan relevant_extension_keys); ThrowCompletionOr resolve_options(VM& vm, IntlObject& object, Value locales, Value options_value, SpecialBehaviors special_behaviours = SpecialBehaviors::None, Function modify_resolution_options = {}); -ThrowCompletionOr> filter_locales(VM& vm, ReadonlySpan requested_locales, Value options); +ThrowCompletionOr> filter_locales(VM& vm, ReadonlySpan requested_locales, Value options); ThrowCompletionOr> coerce_options_to_object(VM&, Value options); ThrowCompletionOr get_boolean_or_string_number_format_option(VM& vm, Object const& options, PropertyKey const& property, ReadonlySpan string_values, StringOrBoolean fallback); ThrowCompletionOr> default_number_option(VM&, Value value, int minimum, int maximum, Optional fallback); diff --git a/Libraries/LibJS/Runtime/Intl/Collator.cpp b/Libraries/LibJS/Runtime/Intl/Collator.cpp index 04905180e7..e5a2c52e3a 100644 --- a/Libraries/LibJS/Runtime/Intl/Collator.cpp +++ b/Libraries/LibJS/Runtime/Intl/Collator.cpp @@ -24,10 +24,10 @@ void Collator::visit_edges(Visitor& visitor) } // 10.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl-collator-internal-slots -ReadonlySpan Collator::relevant_extension_keys() const +ReadonlySpan Collator::relevant_extension_keys() const { // The value of the [[RelevantExtensionKeys]] internal slot is a List that must include the element "co", may include any or all of the elements "kf" and "kn", and must not include any other elements. - static constexpr AK::Array keys { "co"sv, "kf"sv, "kn"sv }; + static constexpr AK::Array keys { "co"sv, "kf"sv, "kn"sv }; return keys; } diff --git a/Libraries/LibJS/Runtime/Intl/Collator.h b/Libraries/LibJS/Runtime/Intl/Collator.h index 6b397faaac..6c02f70fa7 100644 --- a/Libraries/LibJS/Runtime/Intl/Collator.h +++ b/Libraries/LibJS/Runtime/Intl/Collator.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -22,26 +23,26 @@ class Collator final : public IntlObject { public: virtual ~Collator() override = default; - virtual ReadonlySpan relevant_extension_keys() const override; + virtual ReadonlySpan relevant_extension_keys() const override; virtual ReadonlySpan resolution_option_descriptors(VM&) const override; - String const& locale() const { return m_locale; } - void set_locale(String locale) { m_locale = move(locale); } + Utf16String const& locale() const { return m_locale; } + void set_locale(Utf16String locale) { m_locale = move(locale); } Unicode::Usage usage() const { return m_usage; } void set_usage(Utf16View usage) { m_usage = Unicode::usage_from_string(usage); } - StringView usage_string() const LIFETIME_BOUND { return Unicode::usage_to_string(m_usage); } + Utf16String usage_string() const { return Unicode::usage_to_string(m_usage); } Unicode::Sensitivity sensitivity() const { return m_sensitivity; } void set_sensitivity(Unicode::Sensitivity sensitivity) { m_sensitivity = sensitivity; } - StringView sensitivity_string() const LIFETIME_BOUND { return Unicode::sensitivity_to_string(m_sensitivity); } + Utf16String sensitivity_string() const { return Unicode::sensitivity_to_string(m_sensitivity); } Unicode::CaseFirst case_first() const { return m_case_first; } - void set_case_first(StringView case_first) { m_case_first = Unicode::case_first_from_string(case_first); } - StringView case_first_string() const LIFETIME_BOUND { return Unicode::case_first_to_string(m_case_first); } + void set_case_first(Utf16View case_first) { m_case_first = Unicode::case_first_from_string(case_first); } + Utf16String case_first_string() const { return Unicode::case_first_to_string(m_case_first); } - String const& collation() const { return m_collation; } - void set_collation(String collation) { m_collation = move(collation); } + Utf16String const& collation() const { return m_collation; } + void set_collation(Utf16String collation) { m_collation = move(collation); } bool ignore_punctuation() const { return m_ignore_punctuation; } void set_ignore_punctuation(bool ignore_punctuation) { m_ignore_punctuation = ignore_punctuation; } @@ -60,11 +61,11 @@ private: virtual void visit_edges(Visitor&) override; - String m_locale; // [[Locale]] + Utf16String m_locale; // [[Locale]] Unicode::Usage m_usage { Unicode::Usage::Sort }; // [[Usage]] Unicode::Sensitivity m_sensitivity { Unicode::Sensitivity::Variant }; // [[Sensitivity]] Unicode::CaseFirst m_case_first { Unicode::CaseFirst::False }; // [[CaseFirst]] - String m_collation; // [[Collation]] + Utf16String m_collation; // [[Collation]] bool m_ignore_punctuation { false }; // [[IgnorePunctuation]] bool m_numeric { false }; // [[Numeric]] GC::Ptr m_bound_compare; // [[BoundCompare]] diff --git a/Libraries/LibJS/Runtime/Intl/CollatorCompareFunction.cpp b/Libraries/LibJS/Runtime/Intl/CollatorCompareFunction.cpp index 48d5cec5c3..ae2309cfac 100644 --- a/Libraries/LibJS/Runtime/Intl/CollatorCompareFunction.cpp +++ b/Libraries/LibJS/Runtime/Intl/CollatorCompareFunction.cpp @@ -28,7 +28,7 @@ void CollatorCompareFunction::initialize(Realm& realm) Base::initialize(realm); auto& vm = this->vm(); define_direct_property(vm.names.length, Value(2), Attribute::Configurable); - define_direct_property(vm.names.name, PrimitiveString::create(vm, String {}), Attribute::Configurable); + define_direct_property(vm.names.name, PrimitiveString::create(vm, Utf16String {}), Attribute::Configurable); } void CollatorCompareFunction::visit_edges(Visitor& visitor) diff --git a/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp b/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp index ff3b281068..71ac2425bc 100644 --- a/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp @@ -66,7 +66,7 @@ ThrowCompletionOr> CollatorConstructor::construct(FunctionObject auto options = TRY(coerce_options_to_object(vm, options_value)); // 7. Let usage be ? GetOption(options, "usage", string, « "sort", "search" », "sort"). - auto usage = TRY(get_option(vm, options, vm.names.usage, OptionType::String, { "sort"sv, "search"sv }, "sort"sv)); + auto usage = TRY(get_option(vm, options, vm.names.usage, OptionType::String, { "sort"sv, "search"sv }, u"sort"sv)); // 8. Set collator.[[Usage]] to usage. collator->set_usage(usage.as_string().utf16_string_view()); @@ -77,7 +77,7 @@ ThrowCompletionOr> CollatorConstructor::construct(FunctionObject // a. Let localeData be %Intl.Collator%.[[SearchLocaleData]]. // 11. Let optionsResolution be ? ResolveOptions(%Intl.Collator%, localeData, CreateArrayFromList(requestedLocales), options). - auto requested_locales_array = Array::create_from(realm, requested_locales, [&](auto& locale) { return PrimitiveString::create(vm, move(locale)); }); + auto requested_locales_array = Array::create_from(realm, requested_locales, [&](auto const& locale) { return PrimitiveString::create(vm, locale); }); auto options_resolution = TRY(resolve_options(vm, collator, requested_locales_array, options_value)); // 12. Let r be optionsResolution.[[ResolvedLocale]]. @@ -88,25 +88,25 @@ ThrowCompletionOr> CollatorConstructor::construct(FunctionObject // 14. If r.[[co]] is null, let collation be "default". Otherwise, let collation be r.[[co]]. auto collation = result.co.has() - ? "default"_string - : move(result.co.get()); + ? "default"_utf16 + : move(result.co.get()); // 15. Set collator.[[Collation]] to collation. collator->set_collation(move(collation)); // 16. Set collator.[[Numeric]] to SameValue(r.[[kn]], "true"). - collator->set_numeric(result.kn == "true"_string); + collator->set_numeric(result.kn.has() && result.kn.get().utf16_view() == "true"sv); // 17. Set collator.[[CaseFirst]] to r.[[kf]]. - if (auto const* resolved_case_first = result.kf.get_pointer()) - collator->set_case_first(*resolved_case_first); + if (auto const* resolved_case_first = result.kf.get_pointer()) + collator->set_case_first(resolved_case_first->utf16_view()); // 18. Let resolvedLocaleData be r.[[LocaleData]]. // 19. If usage is "sort", let defaultSensitivity be "variant". Otherwise, let defaultSensitivity be resolvedLocaleData.[[sensitivity]]. // NOTE: We do not acquire resolvedLocaleData.[[sensitivity]] here. Instead, we let LibUnicode fill in the // default value if an override was not provided here. - auto default_sensitivity = collator->usage() == Unicode::Usage::Sort ? "variant"sv : OptionDefault {}; + auto default_sensitivity = collator->usage() == Unicode::Usage::Sort ? OptionDefault { u"variant"sv } : OptionDefault {}; // 20. Set collator.[[Sensitivity]] to ? GetOption(options, "sensitivity", string, « "base", "accent", "case", "variant" », defaultSensitivity). auto sensitivity_value = TRY(get_option(vm, options, vm.names.sensitivity, OptionType::String, { "base"sv, "accent"sv, "case"sv, "variant"sv }, default_sensitivity)); @@ -128,9 +128,9 @@ ThrowCompletionOr> CollatorConstructor::construct(FunctionObject // Non-standard, create an ICU collator for this Intl object. auto icu_collator = Unicode::Collator::create( - collator->locale(), + result.icu_locale.utf16_view().bytes(), collator->usage(), - collator->collation(), + collator->collation().utf16_view().bytes(), sensitivity, collator->case_first(), collator->numeric(), diff --git a/Libraries/LibJS/Runtime/Intl/CollatorPrototype.cpp b/Libraries/LibJS/Runtime/Intl/CollatorPrototype.cpp index f1a194252f..2d7d225efd 100644 --- a/Libraries/LibJS/Runtime/Intl/CollatorPrototype.cpp +++ b/Libraries/LibJS/Runtime/Intl/CollatorPrototype.cpp @@ -26,7 +26,7 @@ void CollatorPrototype::initialize(Realm& realm) auto& vm = this->vm(); // 10.3.4 Intl.Collator.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma402/#sec-intl.collator.prototype-%symbol.tostringtag% - define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.Collator"_string), Attribute::Configurable); + define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.Collator"_utf16_fly_string), Attribute::Configurable); u8 attr = Attribute::Writable | Attribute::Configurable; define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr); diff --git a/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp b/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp index d719f85645..cad7ac93b4 100644 --- a/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp +++ b/Libraries/LibJS/Runtime/Intl/DateTimeFormat.cpp @@ -36,10 +36,10 @@ void DateTimeFormat::visit_edges(Cell::Visitor& visitor) } // 11.2.3 Internal slots, https://tc39.es/ecma402/#sec-intl.datetimeformat-internal-slots -ReadonlySpan DateTimeFormat::relevant_extension_keys() const +ReadonlySpan DateTimeFormat::relevant_extension_keys() const { // The value of the [[RelevantExtensionKeys]] internal slot is « "ca", "hc", "nu" ». - static constexpr AK::Array keys { "ca"sv, "hc"sv, "nu"sv }; + static constexpr AK::Array keys { "ca"sv, "hc"sv, "nu"sv }; return keys; } @@ -75,32 +75,32 @@ static Optional get_or_create_formatter(StringVi Optional DateTimeFormat::temporal_plain_date_formatter() { - return get_or_create_formatter(m_icu_locale, "GMT+00:00"sv, m_temporal_plain_date_formatter, m_temporal_plain_date_format); + return get_or_create_formatter(m_icu_locale.utf16_view().bytes(), "GMT+00:00"sv, m_temporal_plain_date_formatter, m_temporal_plain_date_format); } Optional DateTimeFormat::temporal_plain_year_month_formatter() { - return get_or_create_formatter(m_icu_locale, "GMT+00:00"sv, m_temporal_plain_year_month_formatter, m_temporal_plain_year_month_format); + return get_or_create_formatter(m_icu_locale.utf16_view().bytes(), "GMT+00:00"sv, m_temporal_plain_year_month_formatter, m_temporal_plain_year_month_format); } Optional DateTimeFormat::temporal_plain_month_day_formatter() { - return get_or_create_formatter(m_icu_locale, "GMT+00:00"sv, m_temporal_plain_month_day_formatter, m_temporal_plain_month_day_format); + return get_or_create_formatter(m_icu_locale.utf16_view().bytes(), "GMT+00:00"sv, m_temporal_plain_month_day_formatter, m_temporal_plain_month_day_format); } Optional DateTimeFormat::temporal_plain_time_formatter() { - return get_or_create_formatter(m_icu_locale, "GMT+00:00"sv, m_temporal_plain_time_formatter, m_temporal_plain_time_format); + return get_or_create_formatter(m_icu_locale.utf16_view().bytes(), "GMT+00:00"sv, m_temporal_plain_time_formatter, m_temporal_plain_time_format); } Optional DateTimeFormat::temporal_plain_date_time_formatter() { - return get_or_create_formatter(m_icu_locale, "GMT+00:00"sv, m_temporal_plain_date_time_formatter, m_temporal_plain_date_time_format); + return get_or_create_formatter(m_icu_locale.utf16_view().bytes(), "GMT+00:00"sv, m_temporal_plain_date_time_formatter, m_temporal_plain_date_time_format); } Optional DateTimeFormat::temporal_instant_formatter() { - return get_or_create_formatter(m_icu_locale, m_temporal_time_zone, m_temporal_instant_formatter, m_temporal_instant_format); + return get_or_create_formatter(m_icu_locale.utf16_view().bytes(), m_temporal_time_zone.utf16_view().bytes(), m_temporal_instant_formatter, m_temporal_instant_format); } // 11.5.5 FormatDateTimePattern ( dateTimeFormat, patternParts, x, rangeFormatOptions ), https://tc39.es/ecma402/#sec-formatdatetimepattern @@ -162,7 +162,7 @@ ThrowCompletionOr> format_date_time_to_parts(VM& vm, DateTimeForm auto object = Object::create(realm, realm.intrinsics().object_prototype()); // b. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]). - MUST(object->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, part.type))); + MUST(object->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, move(part.type)))); // c. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]). MUST(object->create_data_property_or_throw(vm.names.value, PrimitiveString::create(vm, move(part.value)))); @@ -249,13 +249,13 @@ ThrowCompletionOr> format_date_time_range_to_parts(VM& vm, DateTi auto object = Object::create(realm, realm.intrinsics().object_prototype()); // b. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]). - MUST(object->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, part.type))); + MUST(object->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, move(part.type)))); // c. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]). MUST(object->create_data_property_or_throw(vm.names.value, PrimitiveString::create(vm, move(part.value)))); // d. Perform ! CreateDataPropertyOrThrow(O, "source", part.[[Source]]). - MUST(object->create_data_property_or_throw(vm.names.source, PrimitiveString::create(vm, part.source))); + MUST(object->create_data_property_or_throw(vm.names.source, PrimitiveString::create(vm, move(part.source)))); // e. Perform ! CreateDataProperty(result, ! ToString(n), O). MUST(result->create_data_property_or_throw(n, object)); @@ -535,7 +535,8 @@ static double to_epoch_milliseconds(Crypto::SignedBigInteger const& epoch_nanose ThrowCompletionOr handle_date_time_temporal_date(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainDate const& temporal_date) { // 1. If temporalDate.[[Calendar]] is not either dateTimeFormat.[[Calendar]] or "iso8601", throw a RangeError exception. - if (!temporal_date.calendar().is_one_of(date_time_format.calendar(), "iso8601"sv)) + auto date_time_format_calendar = date_time_format.calendar().utf16_view().bytes(); + if (!temporal_date.calendar().is_one_of(date_time_format_calendar, "iso8601"sv)) return vm.throw_completion(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainDate"sv, temporal_date.calendar(), date_time_format.calendar()); // 2. Let isoDateTime be CombineISODateAndTimeRecord(temporalDate.[[ISODate]], NoonTimeRecord()). @@ -559,7 +560,7 @@ ThrowCompletionOr handle_date_time_temporal_date(VM& vm, DateTimeFo ThrowCompletionOr handle_date_time_temporal_year_month(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainYearMonth const& temporal_year_month) { // 1. If temporalYearMonth.[[Calendar]] is not equal to dateTimeFormat.[[Calendar]], throw a RangeError exception. - if (temporal_year_month.calendar() != date_time_format.calendar()) + if (temporal_year_month.calendar() != date_time_format.calendar().utf16_view().bytes()) return vm.throw_completion(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainYearMonth"sv, temporal_year_month.calendar(), date_time_format.calendar()); // 2. Let isoDateTime be CombineISODateAndTimeRecord(temporalYearMonth.[[ISODate]], NoonTimeRecord()). @@ -583,7 +584,7 @@ ThrowCompletionOr handle_date_time_temporal_year_month(VM& vm, Date ThrowCompletionOr handle_date_time_temporal_month_day(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainMonthDay const& temporal_month_day) { // 1. If temporalMonthDay.[[Calendar]] is not equal to dateTimeFormat.[[Calendar]], throw a RangeError exception. - if (temporal_month_day.calendar() != date_time_format.calendar()) + if (temporal_month_day.calendar() != date_time_format.calendar().utf16_view().bytes()) return vm.throw_completion(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainMonthDay"sv, temporal_month_day.calendar(), date_time_format.calendar()); // 2. Let isoDateTime be CombineISODateAndTimeRecord(temporalMonthDay.[[ISODate]], NoonTimeRecord()). @@ -630,7 +631,8 @@ ThrowCompletionOr handle_date_time_temporal_time(VM& vm, DateTimeFo ThrowCompletionOr handle_date_time_temporal_date_time(VM& vm, DateTimeFormat& date_time_format, Temporal::PlainDateTime const& date_time) { // 1. If dateTime.[[Calendar]] is not "iso8601" and not equal to dateTimeFormat.[[Calendar]], throw a RangeError exception. - if (!date_time.calendar().is_one_of(date_time_format.calendar(), "iso8601"sv)) + auto date_time_format_calendar = date_time_format.calendar().utf16_view().bytes(); + if (!date_time.calendar().is_one_of(date_time_format_calendar, "iso8601"sv)) return vm.throw_completion(ErrorType::IntlTemporalInvalidCalendar, "Temporal.PlainDateTime"sv, date_time.calendar(), date_time_format.calendar()); // 2. Let epochNs be GetUTCEpochNanoseconds(dateTime.[[ISODateTime]]). diff --git a/Libraries/LibJS/Runtime/Intl/DateTimeFormat.h b/Libraries/LibJS/Runtime/Intl/DateTimeFormat.h index 4a36843663..d72917c346 100644 --- a/Libraries/LibJS/Runtime/Intl/DateTimeFormat.h +++ b/Libraries/LibJS/Runtime/Intl/DateTimeFormat.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -29,32 +30,32 @@ class DateTimeFormat final : public IntlObject { public: virtual ~DateTimeFormat() override = default; - virtual ReadonlySpan relevant_extension_keys() const override; + virtual ReadonlySpan relevant_extension_keys() const override; virtual ReadonlySpan resolution_option_descriptors(VM&) const override; - String const& locale() const { return m_locale; } - void set_locale(String locale) { m_locale = move(locale); } + Utf16String const& locale() const { return m_locale; } + void set_locale(Utf16String locale) { m_locale = move(locale); } - String const& icu_locale() const { return m_icu_locale; } - void set_icu_locale(String icu_locale) { m_icu_locale = move(icu_locale); } + Utf16String const& icu_locale() const { return m_icu_locale; } + void set_icu_locale(Utf16String icu_locale) { m_icu_locale = move(icu_locale); } - String const& calendar() const { return m_calendar; } - void set_calendar(String calendar) { m_calendar = move(calendar); } + Utf16String const& calendar() const { return m_calendar; } + void set_calendar(Utf16String calendar) { m_calendar = move(calendar); } - String const& numbering_system() const { return m_numbering_system; } - void set_numbering_system(String numbering_system) { m_numbering_system = move(numbering_system); } + Utf16String const& numbering_system() const { return m_numbering_system; } + void set_numbering_system(Utf16String numbering_system) { m_numbering_system = move(numbering_system); } - String const& time_zone() const { return m_time_zone; } - void set_time_zone(String time_zone) { m_time_zone = move(time_zone); } + Utf16String const& time_zone() const { return m_time_zone; } + void set_time_zone(Utf16String time_zone) { m_time_zone = move(time_zone); } bool has_date_style() const { return m_date_style.has_value(); } Optional const& date_style() const { return m_date_style; } - StringView date_style_string() const { return Unicode::date_time_style_to_string(*m_date_style); } + Utf16String date_style_string() const { return Unicode::date_time_style_to_string(*m_date_style); } void set_date_style(Utf16View style) { m_date_style = Unicode::date_time_style_from_string(style); } bool has_time_style() const { return m_time_style.has_value(); } Optional const& time_style() const { return m_time_style; } - StringView time_style_string() const { return Unicode::date_time_style_to_string(*m_time_style); } + Utf16String time_style_string() const { return Unicode::date_time_style_to_string(*m_time_style); } void set_time_style(Utf16View style) { m_time_style = Unicode::date_time_style_from_string(style); } Unicode::CalendarPattern& date_time_format() { return m_date_time_format; } @@ -84,17 +85,17 @@ public: Optional temporal_instant_formatter(); void set_temporal_instant_format(Optional temporal_instant_format) { m_temporal_instant_format = move(temporal_instant_format); } - void set_temporal_time_zone(String temporal_time_zone) { m_temporal_time_zone = move(temporal_time_zone); } + void set_temporal_time_zone(Utf16String temporal_time_zone) { m_temporal_time_zone = move(temporal_time_zone); } private: explicit DateTimeFormat(Object& prototype); virtual void visit_edges(Visitor&) override; - String m_locale; // [[Locale]] - String m_calendar; // [[Calendar]] - String m_numbering_system; // [[NumberingSystem]] - String m_time_zone; // [[TimeZone]] + Utf16String m_locale; // [[Locale]] + Utf16String m_calendar; // [[Calendar]] + Utf16String m_numbering_system; // [[NumberingSystem]] + Utf16String m_time_zone; // [[TimeZone]] Optional m_date_style; // [[DateStyle]] Optional m_time_style; // [[TimeStyle]] Unicode::CalendarPattern m_date_time_format; // [[DateTimeFormat]] @@ -107,7 +108,7 @@ private: GC::Ptr m_bound_format; // [[BoundFormat]] // Non-standard. Stores the ICU date-time formatters for the Intl object's formatting options. - String m_icu_locale; + Utf16String m_icu_locale; OwnPtr m_formatter; OwnPtr m_temporal_plain_date_formatter; OwnPtr m_temporal_plain_year_month_formatter; @@ -115,7 +116,7 @@ private: OwnPtr m_temporal_plain_time_formatter; OwnPtr m_temporal_plain_date_time_formatter; OwnPtr m_temporal_instant_formatter; - String m_temporal_time_zone; + Utf16String m_temporal_time_zone; }; using FormattableDateTime = Variant< diff --git a/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp b/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp index 3bfef10276..b0143ef781 100644 --- a/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp @@ -85,7 +85,7 @@ JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatConstructor::supported_locales_of) // 11.1.2 CreateDateTimeFormat ( newTarget, locales, options, required, defaults ), https://tc39.es/ecma402/#sec-createdatetimeformat // 15.4.1 CreateDateTimeFormat ( newTarget, locales, options, required, defaults [ , toLocaleStringTimeZone ] ), https://tc39.es/proposal-temporal/#sec-createdatetimeformat // 3.1.1 CreateDateTimeFormat ( newTarget, locales, options, required, defaults ), https://tc39.es/proposal-intl-era-monthcode/#sec-ecma402-intl-datetimeformat-constructor -ThrowCompletionOr> create_date_time_format(VM& vm, FunctionObject& new_target, Value locales_value, Value options_value, OptionRequired required, OptionDefaults defaults, Optional const& to_locale_string_time_zone) +ThrowCompletionOr> create_date_time_format(VM& vm, FunctionObject& new_target, Value locales_value, Value options_value, OptionRequired required, OptionDefaults defaults, Optional const& to_locale_string_time_zone) { // 1. Let dateTimeFormat be ? OrdinaryCreateFromConstructor(newTarget, "%Intl.DateTimeFormat.prototype%", « [[InitializedDateTimeFormat]], [[Locale]], [[Calendar]], [[NumberingSystem]], [[TimeZone]], [[HourCycle]], [[DateStyle]], [[TimeStyle]], [[DateTimeFormat]], [[BoundFormat]] »). auto date_time_format = TRY(ordinary_create_from_constructor(vm, new_target, &Intrinsics::intl_date_time_format_prototype)); @@ -115,27 +115,27 @@ ThrowCompletionOr> create_date_time_format(VM& vm, Funct date_time_format->set_locale(move(result.locale)); // 8. Let resolvedCalendar be r.[[ca]]. - if (auto* resolved_calendar = result.ca.get_pointer()) { + if (auto* resolved_calendar = result.ca.get_pointer()) { // 9. If resolvedCalendar is "islamic", then // NB: We also make "islamic-rgsa" fall back to "islamic-tbla", as test262 relies on this behavior. This falls // within implementation-defined behavior. See: // https://github.com/tc39/ecma402/pull/1044#discussion_r2926804980 - if (resolved_calendar->is_one_of("islamic"sv, "islamic-rgsa"sv)) { + if (resolved_calendar->utf16_view().is_one_of("islamic"sv, "islamic-rgsa"sv)) { // a. Set resolvedCalendar to "islamic-tbla". - *resolved_calendar = "islamic-tbla"_string; + date_time_format->set_calendar("islamic-tbla"_utf16); // b. If the ECMAScript implementation has a mechanism for reporting diagnostic warning messages, a warning // should be issued. + } else { + // 10. Set dateTimeFormat.[[Calendar]] to resolvedCalendar. + date_time_format->set_calendar(move(*resolved_calendar)); } - - // 10. Set dateTimeFormat.[[Calendar]] to resolvedCalendar. - date_time_format->set_calendar(move(*resolved_calendar)); } date_time_format->set_icu_locale(move(result.icu_locale)); // 11. Set dateTimeFormat.[[NumberingSystem]] to r.[[nu]]. - if (auto* resolved_numbering_system = result.nu.get_pointer()) + if (auto* resolved_numbering_system = result.nu.get_pointer()) date_time_format->set_numbering_system(move(*resolved_numbering_system)); // 12. Let resolvedLocaleData be r.[[LocaleData]]. @@ -157,12 +157,12 @@ ThrowCompletionOr> create_date_time_format(VM& vm, Funct VERIFY(hour12.is_undefined()); // b. Let hc be r.[[hc]]. - if (auto* resolved_hour_cycle = result.hc.get_pointer()) - hour_cycle_value = Unicode::hour_cycle_from_string(*resolved_hour_cycle); + if (auto* resolved_hour_cycle = result.hc.get_pointer()) + hour_cycle_value = Unicode::hour_cycle_from_string(resolved_hour_cycle->utf16_view()); // c. If hc is null, set hc to resolvedLocaleData.[[hourCycle]]. if (!hour_cycle_value.has_value()) - hour_cycle_value = Unicode::default_hour_cycle(date_time_format->locale()); + hour_cycle_value = Unicode::default_hour_cycle(date_time_format->icu_locale().utf16_view()); } // 16. Set dateTimeFormat.[[HourCycle]] to hc. @@ -170,22 +170,21 @@ ThrowCompletionOr> create_date_time_format(VM& vm, Funct // 17. Let timeZone be ? Get(options, "timeZone"). auto time_zone_value = TRY(options->get(vm.names.timeZone)); - String time_zone; - Utf16String time_zone_string; + Utf16String icu_time_zone; + Utf16String time_zone; // 18. If timeZone is undefined, then if (time_zone_value.is_undefined()) { // a. If toLocaleStringTimeZone is present, then if (to_locale_string_time_zone.has_value()) { // i. Set timeZone to toLocaleStringTimeZone. - time_zone = *to_locale_string_time_zone; - time_zone_string = Utf16String::from_utf8(time_zone); + time_zone = Utf16String::from_utf16(*to_locale_string_time_zone); } // b. Else, else { // i. Set timeZone to SystemTimeZoneIdentifier(). time_zone = system_time_zone_identifier(); - time_zone_string = Utf16String::from_utf8(time_zone); + icu_time_zone = time_zone; } } // 19. Else, @@ -195,10 +194,10 @@ ThrowCompletionOr> create_date_time_format(VM& vm, Funct return vm.throw_completion(ErrorType::IntlInvalidDateTimeFormatOption, vm.names.timeZone, "a toLocaleString time zone"sv); // b. Set timeZone to ? ToString(timeZone). - time_zone_string = TRY(time_zone_value.to_utf16_string(vm)); + time_zone = TRY(time_zone_value.to_utf16_string(vm)); } - auto time_zone_view = time_zone_string.utf16_view(); + auto time_zone_view = time_zone.utf16_view(); // 20. If IsTimeZoneOffsetString(timeZone) is true, then auto parse_result = Temporal::parse_utc_offset(time_zone_view, Temporal::SubMinutePrecision::No); @@ -218,33 +217,31 @@ ThrowCompletionOr> create_date_time_format(VM& vm, Funct // e. Set timeZone to FormatOffsetTimeZoneIdentifier(offsetMinutes). time_zone = format_offset_time_zone_identifier(offset_minutes); + icu_time_zone = time_zone; } // 21. Else, else { // a. Let timeZoneIdentifierRecord be GetAvailableNamedTimeZoneIdentifier(timeZone). - if (!time_zone_view.is_ascii()) - return vm.throw_completion(ErrorType::OptionIsNotValidValue, time_zone_view, vm.names.timeZone); - - time_zone = MUST(time_zone_view.to_utf8()); - auto time_zone_identifier_record = get_available_named_time_zone_identifier(time_zone); + auto time_zone_identifier_record = get_available_named_time_zone_identifier(time_zone_view); // b. If timeZoneIdentifierRecord is EMPTY, throw a RangeError exception. if (!time_zone_identifier_record.has_value()) - return vm.throw_completion(ErrorType::OptionIsNotValidValue, time_zone, vm.names.timeZone); + return vm.throw_completion(ErrorType::OptionIsNotValidValue, time_zone_view, vm.names.timeZone); // c. Set timeZone to timeZoneIdentifierRecord.[[Identifier]]. time_zone = time_zone_identifier_record->identifier; + icu_time_zone = time_zone; } // 22. Set dateTimeFormat.[[TimeZone]] to timeZone. - date_time_format->set_time_zone(time_zone); + date_time_format->set_time_zone(move(time_zone)); // NOTE: ICU requires time zone offset strings to be of the form "GMT+00:00" if (is_time_zone_offset_string) - time_zone = MUST(String::formatted("GMT{}", time_zone)); + icu_time_zone = Utf16String::formatted("GMT{}", icu_time_zone); // AD-HOC: We must store the massaged time zone for creating ICU formatters for Temporal objects. - date_time_format->set_temporal_time_zone(time_zone); + date_time_format->set_temporal_time_zone(icu_time_zone); // 23. Let formatOptions be a new Record. Unicode::CalendarPattern format_options {}; @@ -297,7 +294,7 @@ ThrowCompletionOr> create_date_time_format(VM& vm, Funct })); // 27. Let formatMatcher be ? GetOption(options, "formatMatcher", string, « "basic", "best fit" », "best fit"). - [[maybe_unused]] auto format_matcher = TRY(get_option(vm, *options, vm.names.formatMatcher, OptionType::String, AK::Array { "basic"sv, "best fit"sv }, "best fit"sv)); + [[maybe_unused]] auto format_matcher = TRY(get_option(vm, *options, vm.names.formatMatcher, OptionType::String, AK::Array { "basic"sv, "best fit"sv }, u"best fit"sv)); // 28. Let dateStyle be ? GetOption(options, "dateStyle", string, « "full", "long", "medium", "short" », undefined). auto date_style = TRY(get_option(vm, *options, vm.names.dateStyle, OptionType::String, AK::Array { "full"sv, "long"sv, "medium"sv, "short"sv }, Empty {})); @@ -340,8 +337,8 @@ ThrowCompletionOr> create_date_time_format(VM& vm, Funct // d. Let styles be resolvedLocaleData.[[styles]].[[]]. // e. Let bestFormat be DateTimeStyleFormat(dateStyle, timeStyle, styles). formatter = Unicode::DateTimeFormat::create_for_date_and_time_style( - date_time_format->icu_locale(), - time_zone, + date_time_format->icu_locale().utf16_view().bytes(), + icu_time_zone.utf16_view().bytes(), format_options.hour_cycle, format_options.hour12, date_time_format->date_style(), @@ -428,8 +425,8 @@ ThrowCompletionOr> create_date_time_format(VM& vm, Funct } formatter = Unicode::DateTimeFormat::create_for_pattern_options( - date_time_format->icu_locale(), - time_zone, + date_time_format->icu_locale().utf16_view().bytes(), + icu_time_zone.utf16_view().bytes(), best_format); } @@ -444,7 +441,7 @@ ThrowCompletionOr> create_date_time_format(VM& vm, Funct } // 11.1.3 FormatOffsetTimeZoneIdentifier ( offsetMinutes ), https://tc39.es/ecma402/#sec-formatoffsettimezoneidentifier -String format_offset_time_zone_identifier(double offset_minutes) +Utf16String format_offset_time_zone_identifier(double offset_minutes) { // 1. If offsetMinutes ≥ 0, let sign be the code unit 0x002B (PLUS SIGN); otherwise, let sign be the code unit 0x002D (HYPHEN-MINUS). auto sign = offset_minutes >= 0.0 ? '+' : '-'; @@ -459,7 +456,7 @@ String format_offset_time_zone_identifier(double offset_minutes) auto minutes = static_cast(modulo(absolute_minutes, 60.0)); // 5. Return the string-concatenation of sign, ToZeroPaddedDecimalString(hours, 2), the code unit 0x003A (COLON), and ToZeroPaddedDecimalString(minutes, 2). - return MUST(String::formatted("{}{:02}:{:02}", sign, hours, minutes)); + return Utf16String::formatted("{}{:02}:{:02}", sign, hours, minutes); } } diff --git a/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.h b/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.h index 1890f8926b..76bd97c798 100644 --- a/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.h +++ b/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.h @@ -51,7 +51,7 @@ enum class OptionInherit { Relevant, }; -ThrowCompletionOr> create_date_time_format(VM&, FunctionObject& new_target, Value locales_value, Value options_value, OptionRequired, OptionDefaults, Optional const& to_locale_string_time_zone = {}); -String format_offset_time_zone_identifier(double offset_minutes); +ThrowCompletionOr> create_date_time_format(VM&, FunctionObject& new_target, Value locales_value, Value options_value, OptionRequired, OptionDefaults, Optional const& to_locale_string_time_zone = {}); +Utf16String format_offset_time_zone_identifier(double offset_minutes); } diff --git a/Libraries/LibJS/Runtime/Intl/DateTimeFormatFunction.cpp b/Libraries/LibJS/Runtime/Intl/DateTimeFormatFunction.cpp index 2b6daca036..e4fe5e514b 100644 --- a/Libraries/LibJS/Runtime/Intl/DateTimeFormatFunction.cpp +++ b/Libraries/LibJS/Runtime/Intl/DateTimeFormatFunction.cpp @@ -35,7 +35,7 @@ void DateTimeFormatFunction::initialize(Realm& realm) Base::initialize(realm); define_direct_property(vm.names.length, Value(1), Attribute::Configurable); - define_direct_property(vm.names.name, PrimitiveString::create(vm, String {}), Attribute::Configurable); + define_direct_property(vm.names.name, PrimitiveString::create(vm, Utf16String {}), Attribute::Configurable); } ThrowCompletionOr DateTimeFormatFunction::call() diff --git a/Libraries/LibJS/Runtime/Intl/DateTimeFormatPrototype.cpp b/Libraries/LibJS/Runtime/Intl/DateTimeFormatPrototype.cpp index 50eeb2b1d5..402e42dc90 100644 --- a/Libraries/LibJS/Runtime/Intl/DateTimeFormatPrototype.cpp +++ b/Libraries/LibJS/Runtime/Intl/DateTimeFormatPrototype.cpp @@ -29,7 +29,7 @@ void DateTimeFormatPrototype::initialize(Realm& realm) auto& vm = this->vm(); // 11.3.7 Intl.DateTimeFormat.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype-%symbol.tostringtag% - define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.DateTimeFormat"_string), Attribute::Configurable); + define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.DateTimeFormat"_utf16_fly_string), Attribute::Configurable); define_native_accessor(realm, vm.names.format, format, nullptr, Attribute::Configurable); @@ -104,7 +104,7 @@ JS_DEFINE_NATIVE_FUNCTION(DateTimeFormatPrototype::resolved_options) MUST(options->create_data_property_or_throw(property, Value(*option))); } else { auto name = Unicode::calendar_pattern_style_to_string(*option); - MUST(options->create_data_property_or_throw(property, PrimitiveString::create(vm, name))); + MUST(options->create_data_property_or_throw(property, PrimitiveString::create(vm, move(name)))); } return {}; diff --git a/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp b/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp index 2b00413345..6e81267e34 100644 --- a/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp +++ b/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp @@ -19,7 +19,7 @@ DisplayNames::DisplayNames(Object& prototype) } // 12.2.3 Internal slots, https://tc39.es/ecma402/#sec-Intl.DisplayNames-internal-slots -ReadonlySpan DisplayNames::relevant_extension_keys() const +ReadonlySpan DisplayNames::relevant_extension_keys() const { // The value of the [[RelevantExtensionKeys]] internal slot is « ». return {}; @@ -50,21 +50,21 @@ void DisplayNames::set_type(Utf16View type) VERIFY_NOT_REACHED(); } -StringView DisplayNames::type_string() const +Utf16String DisplayNames::type_string() const { switch (m_type) { case Type::Language: - return "language"sv; + return "language"_utf16; case Type::Region: - return "region"sv; + return "region"_utf16; case Type::Script: - return "script"sv; + return "script"_utf16; case Type::Currency: - return "currency"sv; + return "currency"_utf16; case Type::Calendar: - return "calendar"sv; + return "calendar"_utf16; case Type::DateTimeField: - return "dateTimeField"sv; + return "dateTimeField"_utf16; default: VERIFY_NOT_REACHED(); } @@ -80,13 +80,13 @@ void DisplayNames::set_fallback(Utf16View fallback) VERIFY_NOT_REACHED(); } -StringView DisplayNames::fallback_string() const +Utf16String DisplayNames::fallback_string() const { switch (m_fallback) { case Fallback::None: - return "none"sv; + return "none"_utf16; case Fallback::Code: - return "code"sv; + return "code"_utf16; default: VERIFY_NOT_REACHED(); } @@ -97,22 +97,17 @@ ThrowCompletionOr canonical_code_for_display_names(VM& vm, DisplayNames:: { // 1. If type is "language", then if (type == DisplayNames::Type::Language) { - if (!code.is_ascii()) - return vm.throw_completion(ErrorType::OptionIsNotValidValue, code, "language"sv); - - auto code_string = MUST(code.to_utf8()); - // a. If code does not match the unicode_language_id production, throw a RangeError exception. - if (!Unicode::parse_unicode_language_id(code_string).has_value()) + if (!Unicode::parse_unicode_language_id(code).has_value()) return vm.throw_completion(ErrorType::OptionIsNotValidValue, code, "language"sv); // b. If IsWellFormedLanguageTag(code) is false, throw a RangeError exception. - if (!is_well_formed_language_tag(code_string)) + if (!is_well_formed_language_tag(code)) return vm.throw_completion(ErrorType::IntlInvalidLanguageTag, code); // c. Return ! CanonicalizeUnicodeLocaleId(code). - auto canonicalized_tag = canonicalize_unicode_locale_id(code_string); - return PrimitiveString::create(vm, move(canonicalized_tag)); + auto canonicalized_tag = canonicalize_unicode_locale_id(code); + return PrimitiveString::create(vm, canonicalized_tag); } // 2. If type is "region", then diff --git a/Libraries/LibJS/Runtime/Intl/DisplayNames.h b/Libraries/LibJS/Runtime/Intl/DisplayNames.h index 712f8b8458..495245b98f 100644 --- a/Libraries/LibJS/Runtime/Intl/DisplayNames.h +++ b/Libraries/LibJS/Runtime/Intl/DisplayNames.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -39,37 +40,43 @@ class DisplayNames final : public IntlObject { public: virtual ~DisplayNames() override = default; - virtual ReadonlySpan relevant_extension_keys() const override; + virtual ReadonlySpan relevant_extension_keys() const override; virtual ReadonlySpan resolution_option_descriptors(VM&) const override; - String const& locale() const { return m_locale; } - void set_locale(String locale) { m_locale = move(locale); } + Utf16String const& locale() const { return m_locale; } + void set_locale(Utf16String locale) { m_locale = move(locale); } + + Utf16String const& icu_locale() const { return m_icu_locale; } + void set_icu_locale(Utf16String icu_locale) { m_icu_locale = move(icu_locale); } Unicode::Style style() const { return m_style; } void set_style(Utf16View style) { m_style = Unicode::style_from_string(style); } - StringView style_string() const { return Unicode::style_to_string(m_style); } + Utf16String style_string() const { return Unicode::style_to_string(m_style); } Type type() const { return m_type; } void set_type(Utf16View type); - StringView type_string() const; + Utf16String type_string() const; Fallback fallback() const { return m_fallback; } void set_fallback(Utf16View fallback); - StringView fallback_string() const; + Utf16String fallback_string() const; bool has_language_display() const { return m_language_display.has_value(); } Unicode::LanguageDisplay language_display() const { return *m_language_display; } void set_language_display(Utf16View language_display) { m_language_display = Unicode::language_display_from_string(language_display); } - StringView language_display_string() const { return Unicode::language_display_to_string(*m_language_display); } + Utf16String language_display_string() const { return Unicode::language_display_to_string(*m_language_display); } private: explicit DisplayNames(Object& prototype); - String m_locale; // [[Locale]] + Utf16String m_locale; // [[Locale]] Unicode::Style m_style { Unicode::Style::Long }; // [[Style]] Type m_type { Type::Invalid }; // [[Type]] Fallback m_fallback { Fallback::Invalid }; // [[Fallback]] Optional m_language_display; // [[LanguageDisplay]] + + // Non-standard. Stores the ICU locale for display-name lookups. + Utf16String m_icu_locale; }; ThrowCompletionOr canonical_code_for_display_names(VM&, DisplayNames::Type, Utf16View code); diff --git a/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp b/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp index 75bf0d9d06..003b0baf7f 100644 --- a/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp @@ -61,7 +61,7 @@ ThrowCompletionOr> DisplayNamesConstructor::construct(FunctionOb auto [options, result, _] = TRY(resolve_options(vm, display_names, locales_value, options_value, SpecialBehaviors::RequireOptions)); // 6. Let style be ? GetOption(options, "style", string, « "narrow", "short", "long" », "long"). - auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "narrow"sv, "short"sv, "long"sv }, "long"sv)); + auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "narrow"sv, "short"sv, "long"sv }, u"long"sv)); // 7. Set displayNames.[[Style]] to style. display_names->set_style(style.as_string().utf16_string_view()); @@ -77,20 +77,21 @@ ThrowCompletionOr> DisplayNamesConstructor::construct(FunctionOb display_names->set_type(type.as_string().utf16_string_view()); // 11. Let fallback be ? GetOption(options, "fallback", string, « "code", "none" », "code"). - auto fallback = TRY(get_option(vm, *options, vm.names.fallback, OptionType::String, { "code"sv, "none"sv }, "code"sv)); + auto fallback = TRY(get_option(vm, *options, vm.names.fallback, OptionType::String, { "code"sv, "none"sv }, u"code"sv)); // 12. Set displayNames.[[Fallback]] to fallback. display_names->set_fallback(fallback.as_string().utf16_string_view()); // 13. Set displayNames.[[Locale]] to r.[[Locale]]. display_names->set_locale(move(result.locale)); + display_names->set_icu_locale(move(result.icu_locale)); // 14. Let resolvedLocaleData be r.[[LocaleData]]. // 15. Let types be resolvedLocaleData.[[types]]. // 16. Assert: types is a Record (see 12.2.3). // 17. Let languageDisplay be ? GetOption(options, "languageDisplay", string, « "dialect", "standard" », "dialect"). - auto language_display = TRY(get_option(vm, *options, vm.names.languageDisplay, OptionType::String, { "dialect"sv, "standard"sv }, "dialect"sv)); + auto language_display = TRY(get_option(vm, *options, vm.names.languageDisplay, OptionType::String, { "dialect"sv, "standard"sv }, u"dialect"sv)); // 18. Let typeFields be types.[[]]. // 19. Assert: typeFields is a Record (see 12.2.3). diff --git a/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp b/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp index b827db8c0e..ac56f78bdb 100644 --- a/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp +++ b/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp @@ -28,7 +28,7 @@ void DisplayNamesPrototype::initialize(Realm& realm) auto& vm = this->vm(); // 12.3.4 Intl.DisplayNames.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma402/#sec-intl.displaynames.prototype-%symbol.tostringtag% - define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.DisplayNames"_string), Attribute::Configurable); + define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.DisplayNames"_utf16_fly_string), Attribute::Configurable); u8 attr = Attribute::Writable | Attribute::Configurable; define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr); @@ -78,7 +78,7 @@ JS_DEFINE_NATIVE_FUNCTION(DisplayNamesPrototype::of) // 4. Let code be ? CanonicalCodeForDisplayNames(displayNames.[[Type]], code). code = TRY(canonical_code_for_display_names(vm, display_names->type(), code.as_string().utf16_string_view())); - auto code_string = MUST(code.as_string().utf16_string_view().to_utf8()); + auto code_view = code.as_string().utf16_string_view(); // 5. Let fields be displayNames.[[Fields]]. // 6. If fields has a field [[]], return fields.[[]]. @@ -86,22 +86,22 @@ JS_DEFINE_NATIVE_FUNCTION(DisplayNamesPrototype::of) switch (display_names->type()) { case DisplayNames::Type::Language: - result = Unicode::language_display_name(display_names->locale(), code_string, display_names->language_display()); + result = Unicode::language_display_name(display_names->icu_locale().utf16_view().bytes(), code_view, display_names->language_display()); break; case DisplayNames::Type::Region: - result = Unicode::region_display_name(display_names->locale(), code_string); + result = Unicode::region_display_name(display_names->icu_locale().utf16_view().bytes(), code_view); break; case DisplayNames::Type::Script: - result = Unicode::script_display_name(display_names->locale(), code_string); + result = Unicode::script_display_name(display_names->icu_locale().utf16_view().bytes(), code_view); break; case DisplayNames::Type::Currency: - result = Unicode::currency_display_name(display_names->locale(), code_string, display_names->style()); + result = Unicode::currency_display_name(display_names->icu_locale().utf16_view().bytes(), code_view, display_names->style()); break; case DisplayNames::Type::Calendar: - result = Unicode::calendar_display_name(display_names->locale(), code_string); + result = Unicode::calendar_display_name(display_names->icu_locale().utf16_view().bytes(), code_view); break; case DisplayNames::Type::DateTimeField: - result = Unicode::date_time_field_display_name(display_names->locale(), code_string, display_names->style()); + result = Unicode::date_time_field_display_name(display_names->icu_locale().utf16_view().bytes(), code_view, display_names->style()); break; default: VERIFY_NOT_REACHED(); diff --git a/Libraries/LibJS/Runtime/Intl/DurationFormat.cpp b/Libraries/LibJS/Runtime/Intl/DurationFormat.cpp index b2dc11818e..a0a174df77 100644 --- a/Libraries/LibJS/Runtime/Intl/DurationFormat.cpp +++ b/Libraries/LibJS/Runtime/Intl/DurationFormat.cpp @@ -29,10 +29,10 @@ DurationFormat::DurationFormat(Object& prototype) } // 13.2.3 Internal slots, https://tc39.es/ecma402/#sec-Intl.DurationFormat-internal-slots -ReadonlySpan DurationFormat::relevant_extension_keys() const +ReadonlySpan DurationFormat::relevant_extension_keys() const { // The value of the [[RelevantExtensionKeys]] internal slot is « "nu" ». - static constexpr AK::Array keys { "nu"sv }; + static constexpr AK::Array keys { "nu"sv }; return keys; } @@ -63,17 +63,17 @@ DurationFormat::Style DurationFormat::style_from_string(Utf16View style) VERIFY_NOT_REACHED(); } -StringView DurationFormat::style_to_string(Style style) +Utf16String DurationFormat::style_to_string(Style style) { switch (style) { case Style::Long: - return "long"sv; + return "long"_utf16; case Style::Short: - return "short"sv; + return "short"_utf16; case Style::Narrow: - return "narrow"sv; + return "narrow"_utf16; case Style::Digital: - return "digital"sv; + return "digital"_utf16; default: VERIFY_NOT_REACHED(); } @@ -105,32 +105,32 @@ DurationFormat::ValueStyle DurationFormat::value_style_from_string(Utf16View val VERIFY_NOT_REACHED(); } -StringView DurationFormat::value_style_to_string(ValueStyle value_style) +Utf16String DurationFormat::value_style_to_string(ValueStyle value_style) { switch (value_style) { case ValueStyle::Long: - return "long"sv; + return "long"_utf16; case ValueStyle::Short: - return "short"sv; + return "short"_utf16; case ValueStyle::Narrow: - return "narrow"sv; + return "narrow"_utf16; case ValueStyle::Numeric: - return "numeric"sv; + return "numeric"_utf16; case ValueStyle::TwoDigit: - return "2-digit"sv; + return "2-digit"_utf16; case ValueStyle::Fractional: - return "fractional"sv; + return "fractional"_utf16; } VERIFY_NOT_REACHED(); } -StringView DurationFormat::display_to_string(Display display) +Utf16String DurationFormat::display_to_string(Display display) { switch (display) { case Display::Auto: - return "auto"sv; + return "auto"_utf16; case Display::Always: - return "always"sv; + return "always"_utf16; default: VERIFY_NOT_REACHED(); } @@ -238,7 +238,7 @@ ThrowCompletionOr get_duration_unit_options DurationFormat::ValueStyle style; // 2. Let displayDefault be "always". - auto display_default = "always"sv; + Utf16View display_default = u"always"sv; // 3. If style is undefined, then if (style_value.is_undefined()) { @@ -249,7 +249,7 @@ ThrowCompletionOr get_duration_unit_options // ii. If unit is not one of "hours", "minutes", or "seconds", set displayDefault to "auto". if (!first_is_one_of(unit, DurationFormat::Unit::Hours, DurationFormat::Unit::Minutes, DurationFormat::Unit::Seconds)) - display_default = "auto"sv; + display_default = u"auto"sv; } // b. Else if prevStyle is one of "fractional", "numeric" or "2-digit", then else if (first_is_one_of(previous_style, DurationFormat::ValueStyle::Fractional, DurationFormat::ValueStyle::Numeric, DurationFormat::ValueStyle::TwoDigit)) { @@ -258,7 +258,7 @@ ThrowCompletionOr get_duration_unit_options // ii. If unit is not "minutes" or "seconds", set displayDefault to "auto". if (!first_is_one_of(unit, DurationFormat::Unit::Minutes, DurationFormat::Unit::Seconds)) - display_default = "auto"sv; + display_default = u"auto"sv; } // c. Else, else { @@ -266,7 +266,7 @@ ThrowCompletionOr get_duration_unit_options style = static_cast(base_style); // ii. Set displayDefault to "auto". - display_default = "auto"sv; + display_default = u"auto"sv; } } else { style = DurationFormat::value_style_from_string(style_value.as_string().utf16_string_view()); @@ -278,7 +278,7 @@ ThrowCompletionOr get_duration_unit_options style = DurationFormat::ValueStyle::Fractional; // b. Set displayDefault to "auto". - display_default = "auto"sv; + display_default = u"auto"sv; } // 5. Let displayField be the string-concatenation of unit and "Display". @@ -395,7 +395,7 @@ Vector format_numeric_hours(VM& vm, DurationFormat const& du // 8. If signDisplayed is false, then if (!sign_displayed) { // a. Perform ! CreateDataPropertyOrThrow(nfOpts, "signDisplay", "never"). - MUST(number_format_options->create_data_property_or_throw(vm.names.signDisplay, PrimitiveString::create(vm, "never"sv))); + MUST(number_format_options->create_data_property_or_throw(vm.names.signDisplay, PrimitiveString::create(vm, "never"_utf16_fly_string))); } // 9. Perform ! CreateDataPropertyOrThrow(nfOpts, "useGrouping", false). @@ -412,7 +412,7 @@ Vector format_numeric_hours(VM& vm, DurationFormat const& du for (auto& part : hours_parts) { // a. Append the Record { [[Type]]: part.[[Type]], [[Value]]: part.[[Value]], [[Unit]]: "hour" } to result. - result.unchecked_append({ .type = part.type, .value = move(part.value), .unit = "hour"sv }); + result.unchecked_append({ .type = move(part.type), .value = move(part.value), .unit = "hour"_utf16 }); } // 13. Return result. @@ -433,7 +433,7 @@ Vector format_numeric_minutes(VM& vm, DurationFormat const& auto separator = duration_format.hour_minute_separator(); // b. Append the Record { [[Type]]: "literal", [[Value]]: separator, [[Unit]]: EMPTY } to result. - result.append({ .type = "literal"sv, .value = move(separator), .unit = {} }); + result.append({ .type = "literal"_utf16, .value = move(separator), .unit = {} }); } // 3. Let minutesStyle be durationFormat.[[MinutesOptions]].[[Style]]. @@ -460,7 +460,7 @@ Vector format_numeric_minutes(VM& vm, DurationFormat const& // 9. If signDisplayed is false, then if (!sign_displayed) { // a. Perform ! CreateDataPropertyOrThrow(nfOpts, "signDisplay", "never"). - MUST(number_format_options->create_data_property_or_throw(vm.names.signDisplay, PrimitiveString::create(vm, "never"sv))); + MUST(number_format_options->create_data_property_or_throw(vm.names.signDisplay, PrimitiveString::create(vm, "never"_utf16_fly_string))); } // 10. Perform ! CreateDataPropertyOrThrow(nfOpts, "useGrouping", false). @@ -477,7 +477,7 @@ Vector format_numeric_minutes(VM& vm, DurationFormat const& for (auto& part : minutes_parts) { // a. Append the Record { [[Type]]: part.[[Type]], [[Value]]: part.[[Value]], [[Unit]]: "minute" } to result. - result.unchecked_append({ .type = part.type, .value = move(part.value), .unit = "minute"sv }); + result.unchecked_append({ .type = move(part.type), .value = move(part.value), .unit = "minute"_utf16 }); } // 14. Return result. @@ -498,7 +498,7 @@ Vector format_numeric_seconds(VM& vm, DurationFormat const& auto separator = duration_format.minute_second_separator(); // b. Append the Record { [[Type]]: "literal", [[Value]]: separator, [[Unit]]: EMPTY } to result. - result.append({ .type = "literal"sv, .value = move(separator), .unit = {} }); + result.append({ .type = "literal"_utf16, .value = move(separator), .unit = {} }); } // 3. Let secondsStyle be durationFormat.[[SecondsOptions]].[[Style]]. @@ -525,7 +525,7 @@ Vector format_numeric_seconds(VM& vm, DurationFormat const& // 9. If signDisplayed is false, then if (!sign_displayed) { // a. Perform ! CreateDataPropertyOrThrow(nfOpts, "signDisplay", "never"). - MUST(number_format_options->create_data_property_or_throw(vm.names.signDisplay, PrimitiveString::create(vm, "never"sv))); + MUST(number_format_options->create_data_property_or_throw(vm.names.signDisplay, PrimitiveString::create(vm, "never"_utf16_fly_string))); } // 10. Perform ! CreateDataPropertyOrThrow(nfOpts, "useGrouping", false). @@ -552,7 +552,7 @@ Vector format_numeric_seconds(VM& vm, DurationFormat const& } // 14. Perform ! CreateDataPropertyOrThrow(nfOpts, "roundingMode", "trunc"). - MUST(number_format_options->create_data_property_or_throw(vm.names.roundingMode, PrimitiveString::create(vm, "trunc"sv))); + MUST(number_format_options->create_data_property_or_throw(vm.names.roundingMode, PrimitiveString::create(vm, "trunc"_utf16_fly_string))); // 15. Let nf be ! Construct(%Intl.NumberFormat%, « durationFormat.[[Locale]], nfOpts »). auto number_format = construct_number_format(vm, duration_format, number_format_options); @@ -565,7 +565,7 @@ Vector format_numeric_seconds(VM& vm, DurationFormat const& for (auto& part : seconds_parts) { // a. Append the Record { [[Type]]: part.[[Type]], [[Value]]: part.[[Value]], [[Unit]]: "second" } to result. - result.unchecked_append({ .type = part.type, .value = move(part.value), .unit = "second"sv }); + result.unchecked_append({ .type = move(part.type), .value = move(part.value), .unit = "second"_utf16 }); } // 18. Return result. @@ -690,7 +690,7 @@ Vector format_numeric_units(VM& vm, DurationFormat const& du // 18. If secondsFormatted is true, then if (seconds_formatted) { // a. Let secondsParts be FormatNumericSeconds(durationFormat, secondsValue, minutesFormatted, signDisplayed). - auto seconds_value_mv = MathematicalValue { Utf16String::from_utf8(seconds_value.to_string(9)) }; + auto seconds_value_mv = MathematicalValue { seconds_value.to_utf16_string(9) }; auto seconds_parts = format_numeric_seconds(vm, duration_format, seconds_value_mv, minutes_formatted, sign_displayed); // b. Set numericPartsList to the list-concatenation of numericPartsList and secondsParts. @@ -718,7 +718,7 @@ Vector list_format_parts(VM& vm, DurationFormat const& durat auto list_format_options = Object::create(realm, nullptr); // 2. Perform ! CreateDataPropertyOrThrow(lfOpts, "type", "unit"). - MUST(list_format_options->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, "unit"sv))); + MUST(list_format_options->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, "unit"_utf16_fly_string))); // 3. Let listStyle be durationFormat.[[Style]]. auto list_style = duration_format.style(); @@ -731,7 +731,7 @@ Vector list_format_parts(VM& vm, DurationFormat const& durat // 5. Perform ! CreateDataPropertyOrThrow(lfOpts, "style", listStyle). auto locale_list_style = Unicode::style_to_string(static_cast(list_style)); - MUST(list_format_options->create_data_property_or_throw(vm.names.style, PrimitiveString::create(vm, locale_list_style))); + MUST(list_format_options->create_data_property_or_throw(vm.names.style, PrimitiveString::create(vm, move(locale_list_style)))); // 6. Let lf be ! Construct(%Intl.ListFormat%, « durationFormat.[[Locale]], lfOpts »). auto list_format = construct_list_format(vm, duration_format, list_format_options); @@ -792,7 +792,7 @@ Vector list_format_parts(VM& vm, DurationFormat const& durat VERIFY(list_part.type == "literal"sv); // ii. Append the Record { [[Type]]: "literal", [[Value]]: listPart.[[Value]], [[Unit]]: empty } to flattenedPartsList. - flattened_parts_list.append({ .type = "literal"sv, .value = move(list_part.value), .unit = {} }); + flattened_parts_list.append({ .type = "literal"_utf16, .value = move(list_part.value), .unit = {} }); } } @@ -876,7 +876,7 @@ Vector partition_duration_format_pattern(VM& vm, DurationFor } // 5. Perform ! CreateDataPropertyOrThrow(nfOpts, "roundingMode", "trunc"). - MUST(number_format_options->create_data_property_or_throw(vm.names.roundingMode, PrimitiveString::create(vm, "trunc"sv))); + MUST(number_format_options->create_data_property_or_throw(vm.names.roundingMode, PrimitiveString::create(vm, "trunc"_utf16_fly_string))); // 6. Set numericUnitFound to true. numeric_unit_found = true; @@ -884,7 +884,7 @@ Vector partition_duration_format_pattern(VM& vm, DurationFor // iii. If display is "always" or value is not 0, then if (display == DurationFormat::Display::Always || !value.is_zero()) { - auto value_mv = MathematicalValue { Utf16String::from_utf8(value.to_string(9)) }; + auto value_mv = MathematicalValue { value.to_utf16_string(9) }; // 1. Perform ! CreateDataPropertyOrThrow(nfOpts, "numberingSystem", durationFormat.[[NumberingSystem]]). MUST(number_format_options->create_data_property_or_throw(vm.names.numberingSystem, PrimitiveString::create(vm, duration_format.numbering_system()))); @@ -901,18 +901,18 @@ Vector partition_duration_format_pattern(VM& vm, DurationFor // 3. Else, else { // a. Perform ! CreateDataPropertyOrThrow(nfOpts, "signDisplay", "never"). - MUST(number_format_options->create_data_property_or_throw(vm.names.signDisplay, PrimitiveString::create(vm, "never"sv))); + MUST(number_format_options->create_data_property_or_throw(vm.names.signDisplay, PrimitiveString::create(vm, "never"_utf16_fly_string))); } // 3. Perform ! CreateDataPropertyOrThrow(nfOpts, "style", "unit"). - MUST(number_format_options->create_data_property_or_throw(vm.names.style, PrimitiveString::create(vm, "unit"sv))); + MUST(number_format_options->create_data_property_or_throw(vm.names.style, PrimitiveString::create(vm, "unit"_utf16_fly_string))); // 4. Perform ! CreateDataPropertyOrThrow(nfOpts, "unit", numberFormatUnit). MUST(number_format_options->create_data_property_or_throw(vm.names.unit, PrimitiveString::create(vm, number_format_unit.as_string()))); // 5. Perform ! CreateDataPropertyOrThrow(nfOpts, "unitDisplay", style). auto locale_style = Unicode::style_to_string(static_cast(style)); - MUST(number_format_options->create_data_property_or_throw(vm.names.unitDisplay, PrimitiveString::create(vm, locale_style))); + MUST(number_format_options->create_data_property_or_throw(vm.names.unitDisplay, PrimitiveString::create(vm, move(locale_style)))); // 6. Let nf be ! Construct(%Intl.NumberFormat%, « durationFormat.[[Locale]], nfOpts »). auto number_format = construct_number_format(vm, duration_format, number_format_options); @@ -925,10 +925,11 @@ Vector partition_duration_format_pattern(VM& vm, DurationFor // 10. For each Record { [[Type]], [[Value]] } part of parts, do list.ensure_capacity(parts.size()); + auto unit = number_format_unit.as_string().to_utf16_string(); for (auto& part : parts) { // a. Append the Record { [[Type]]: part.[[Type]], [[Value]]: part.[[Value]], [[Unit]]: numberFormatUnit } to list. - list.unchecked_append({ .type = part.type, .value = move(part.value), .unit = number_format_unit.as_string().view() }); + list.unchecked_append({ .type = move(part.type), .value = move(part.value), .unit = unit }); } // 11. Append list to result. diff --git a/Libraries/LibJS/Runtime/Intl/DurationFormat.h b/Libraries/LibJS/Runtime/Intl/DurationFormat.h index 8da8fc81f9..7aea4f875b 100644 --- a/Libraries/LibJS/Runtime/Intl/DurationFormat.h +++ b/Libraries/LibJS/Runtime/Intl/DurationFormat.h @@ -8,7 +8,7 @@ #pragma once #include -#include +#include #include #include #include @@ -29,7 +29,7 @@ public: Digital, }; static Style style_from_string(Utf16View style); - static StringView style_to_string(Style); + static Utf16String style_to_string(Style); enum class ValueStyle { Long, @@ -40,7 +40,7 @@ public: Fractional, }; static ValueStyle value_style_from_string(Utf16View); - static StringView value_style_to_string(ValueStyle); + static Utf16String value_style_to_string(ValueStyle); static_assert(to_underlying(ValueStyle::Long) == to_underlying(Unicode::Style::Long)); static_assert(to_underlying(ValueStyle::Short) == to_underlying(Unicode::Style::Short)); @@ -51,7 +51,7 @@ public: Always, }; static Display display_from_string(Utf16View display); - static StringView display_to_string(Display); + static Utf16String display_to_string(Display); enum class Unit { Years, @@ -74,14 +74,14 @@ public: virtual ~DurationFormat() override = default; - virtual ReadonlySpan relevant_extension_keys() const override; + virtual ReadonlySpan relevant_extension_keys() const override; virtual ReadonlySpan resolution_option_descriptors(VM&) const override; - void set_locale(String locale) { m_locale = move(locale); } - String const& locale() const { return m_locale; } + void set_locale(Utf16String locale) { m_locale = move(locale); } + Utf16String const& locale() const { return m_locale; } - void set_numbering_system(String numbering_system) { m_numbering_system = move(numbering_system); } - String const& numbering_system() const { return m_numbering_system; } + void set_numbering_system(Utf16String numbering_system) { m_numbering_system = move(numbering_system); } + Utf16String const& numbering_system() const { return m_numbering_system; } void set_hour_minute_separator(Utf16String hour_minute_separator) { m_hour_minute_separator = move(hour_minute_separator); } Utf16String const& hour_minute_separator() const { return m_hour_minute_separator; } @@ -91,7 +91,7 @@ public: void set_style(Utf16View style) { m_style = style_from_string(style); } Style style() const { return m_style; } - StringView style_string() const { return style_to_string(m_style); } + Utf16String style_string() const { return style_to_string(m_style); } void set_years_options(DurationUnitOptions years_options) { m_years_options = years_options; } DurationUnitOptions years_options() const { return m_years_options; } @@ -130,8 +130,8 @@ public: private: explicit DurationFormat(Object& prototype); - String m_locale; // [[Locale]] - String m_numbering_system; // [[NumberingSystem]] + Utf16String m_locale; // [[Locale]] + Utf16String m_numbering_system; // [[NumberingSystem]] Utf16String m_hour_minute_separator; // [[HourMinutesSeparator]] Utf16String m_minute_second_separator; // [[MinutesSecondsSeparator]] @@ -178,9 +178,9 @@ static constexpr auto duration_instances_components = to_array get_duration_unit_options(VM&, DurationFormat::Unit unit, Object const& options, DurationFormat::Style base_style, ReadonlySpan styles_list, DurationFormat::ValueStyle digital_base, Optional previous_style, bool two_digit_hours); diff --git a/Libraries/LibJS/Runtime/Intl/DurationFormatConstructor.cpp b/Libraries/LibJS/Runtime/Intl/DurationFormatConstructor.cpp index c904f21c0e..89930e29c0 100644 --- a/Libraries/LibJS/Runtime/Intl/DurationFormatConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/DurationFormatConstructor.cpp @@ -67,7 +67,7 @@ ThrowCompletionOr> DurationFormatConstructor::construct(Function // 7. Let resolvedLocaleData be r.[[LocaleData]]. // 8. Let digitalFormat be resolvedLocaleData.[[DigitalFormat]]. - auto digital_format = Unicode::digital_format(result.icu_locale); + auto digital_format = Unicode::digital_format(result.icu_locale.utf16_view()); // 9. Set durationFormat.[[HourMinuteSeparator]] to digitalFormat.[[HourMinuteSeparator]]. duration_format->set_hour_minute_separator(move(digital_format.hours_minutes_separator)); @@ -76,11 +76,11 @@ ThrowCompletionOr> DurationFormatConstructor::construct(Function duration_format->set_minute_second_separator(move(digital_format.minutes_seconds_separator)); // 11. Set durationFormat.[[NumberingSystem]] to r.[[nu]]. - if (auto* resolved_numbering_system = result.nu.get_pointer()) + if (auto* resolved_numbering_system = result.nu.get_pointer()) duration_format->set_numbering_system(move(*resolved_numbering_system)); // 12. Let style be ? GetOption(options, "style", STRING, « "long", "short", "narrow", "digital" », "short"). - auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv, "digital"sv }, "short"sv)); + auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv, "digital"sv }, u"short"sv)); // 13. Set durationFormat.[[Style]] to style. duration_format->set_style(style.as_string().utf16_string_view()); diff --git a/Libraries/LibJS/Runtime/Intl/DurationFormatPrototype.cpp b/Libraries/LibJS/Runtime/Intl/DurationFormatPrototype.cpp index faa379a8e1..df2455b0f4 100644 --- a/Libraries/LibJS/Runtime/Intl/DurationFormatPrototype.cpp +++ b/Libraries/LibJS/Runtime/Intl/DurationFormatPrototype.cpp @@ -29,7 +29,7 @@ void DurationFormatPrototype::initialize(Realm& realm) auto& vm = this->vm(); // 13.3.5 Intl.DurationFormat.prototype [ %Symbol.toStringTag% ], https://tc39.es/ecma402/#sec-Intl.DurationFormat.prototype-%symbol.tostringtag% - define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.DurationFormat"_string), Attribute::Configurable); + define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl.DurationFormat"_utf16_fly_string), Attribute::Configurable); u8 attr = Attribute::Writable | Attribute::Configurable; define_native_function(realm, vm.names.resolvedOptions, resolved_options, 0, attr); @@ -77,11 +77,13 @@ JS_DEFINE_NATIVE_FUNCTION(DurationFormatPrototype::resolved_options) } // 5. Perform ! CreateDataPropertyOrThrow(options, p, style). - MUST(options->create_data_property_or_throw(property, PrimitiveString::create(vm, DurationFormat::value_style_to_string(style)))); + auto style_string = DurationFormat::value_style_to_string(style); + MUST(options->create_data_property_or_throw(property, PrimitiveString::create(vm, move(style_string)))); // 6. Set p to the string-concatenation of p and "Display". // 7. Set v to v.[[Display]]. - MUST(options->create_data_property_or_throw(*display_property, PrimitiveString::create(vm, DurationFormat::display_to_string(value.display)))); + auto display_string = DurationFormat::display_to_string(value.display); + MUST(options->create_data_property_or_throw(*display_property, PrimitiveString::create(vm, move(display_string)))); } else { // iv. Perform ! CreateDataPropertyOrThrow(options, p, v). MUST(options->create_data_property_or_throw(property, PrimitiveString::create(vm, move(value)))); @@ -162,14 +164,14 @@ JS_DEFINE_NATIVE_FUNCTION(DurationFormatPrototype::format_to_parts) auto object = Object::create(realm, realm.intrinsics().object_prototype()); // b. Perform ! CreateDataPropertyOrThrow(obj, "type", part.[[Type]]). - MUST(object->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, part.type))); + MUST(object->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, move(part.type)))); // c. Perform ! CreateDataPropertyOrThrow(obj, "value", part.[[Value]]). MUST(object->create_data_property_or_throw(vm.names.value, PrimitiveString::create(vm, move(part.value)))); // d. If part.[[Unit]] is not empty, perform ! CreateDataPropertyOrThrow(obj, "unit", part.[[Unit]]). if (!part.unit.is_empty()) - MUST(object->create_data_property_or_throw(vm.names.unit, PrimitiveString::create(vm, part.unit))); + MUST(object->create_data_property_or_throw(vm.names.unit, PrimitiveString::create(vm, move(part.unit)))); // e. Perform ! CreateDataPropertyOrThrow(result, ! ToString(n), obj). MUST(result->create_data_property_or_throw(n, object)); diff --git a/Libraries/LibJS/Runtime/Intl/Intl.cpp b/Libraries/LibJS/Runtime/Intl/Intl.cpp index 58ab214c26..95315ee923 100644 --- a/Libraries/LibJS/Runtime/Intl/Intl.cpp +++ b/Libraries/LibJS/Runtime/Intl/Intl.cpp @@ -45,7 +45,7 @@ void Intl::initialize(Realm& realm) auto& vm = this->vm(); // 8.1.1 Intl[ @@toStringTag ], https://tc39.es/ecma402/#sec-Intl-toStringTag - define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl"_string), Attribute::Configurable); + define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl"_utf16_fly_string), Attribute::Configurable); u8 attr = Attribute::Writable | Attribute::Configurable; define_intrinsic_accessor(vm.names.Collator, attr, [](auto& realm) -> Value { return realm.intrinsics().intl_collator_constructor(); }); @@ -73,24 +73,20 @@ JS_DEFINE_NATIVE_FUNCTION(Intl::get_canonical_locales) // 1. Let ll be ? CanonicalizeLocaleList(locales). auto locale_list = TRY(canonicalize_locale_list(vm, locales)); - GC::RootVector marked_locale_list; - marked_locale_list.ensure_capacity(locale_list.size()); - - for (auto& locale : locale_list) - marked_locale_list.unchecked_append(PrimitiveString::create(vm, move(locale))); - // 2. Return CreateArrayFromList(ll). - return Array::create_from(realm, marked_locale_list); + return Array::create_from(realm, locale_list, [&vm](auto const& locale) { + return PrimitiveString::create(vm, locale); + }); } // 6.5.4 AvailablePrimaryTimeZoneIdentifiers ( ), https://tc39.es/ecma402/#sec-availableprimarytimezoneidentifiers -static Vector available_primary_time_zone_identifiers() +static Vector available_primary_time_zone_identifiers() { // 1. Let records be AvailableNamedTimeZoneIdentifiers(). auto const& records = available_named_time_zone_identifiers(); // 2. Let result be a new empty List. - Vector result; + Vector result; // 3. For each element timeZoneIdentifierRecord of records, do for (auto const& time_zone_identifier_record : records) { @@ -113,7 +109,7 @@ JS_DEFINE_NATIVE_FUNCTION(Intl::supported_values_of) // 1. Let key be ? ToString(key). auto key = TRY(vm.argument(0).to_utf16_string(vm)); - Optional, ReadonlySpan>> list; + Optional> list; // 2. If key is "calendar", then if (key == "calendar"sv) { @@ -138,13 +134,20 @@ JS_DEFINE_NATIVE_FUNCTION(Intl::supported_values_of) // 6. Else if key is "timeZone", then else if (key == "timeZone"sv) { // a. Let list be ! AvailablePrimaryTimeZoneIdentifiers( ). - static NeverDestroyed> time_zones { available_primary_time_zone_identifiers() }; + static NeverDestroyed> time_zones { available_primary_time_zone_identifiers() }; list = time_zones->span(); } // 7. Else if key is "unit", then else if (key == "unit"sv) { // a. Let list be ! AvailableCanonicalUnits( ). - static NeverDestroyed> units { sanctioned_single_unit_identifiers() }; + static NeverDestroyed> units { [] { + Vector units; + auto sanctioned_units = sanctioned_single_unit_identifiers(); + units.ensure_capacity(sanctioned_units.size()); + for (auto unit : sanctioned_units) + units.unchecked_append(Utf16String::from_utf16(unit)); + return units; + }() }; list = units->span(); } // 8. Else, @@ -154,10 +157,8 @@ JS_DEFINE_NATIVE_FUNCTION(Intl::supported_values_of) } // 9. Return CreateArrayFromList( list ). - return list->visit([&](ReadonlySpan list) { - return Array::create_from(realm, list, [&](auto value) { - return PrimitiveString::create(vm, value); - }); + return Array::create_from(realm, *list, [&](auto const& value) { + return PrimitiveString::create(vm, value); }); } diff --git a/Libraries/LibJS/Runtime/Intl/IntlObject.h b/Libraries/LibJS/Runtime/Intl/IntlObject.h index 759113bee1..8d2adb8af3 100644 --- a/Libraries/LibJS/Runtime/Intl/IntlObject.h +++ b/Libraries/LibJS/Runtime/Intl/IntlObject.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -16,7 +17,7 @@ namespace JS::Intl { // https://tc39.es/ecma402/#resolution-option-descriptor struct ResolutionOptionDescriptor { - StringView key; + Utf16View key; PropertyKey property; OptionType type { OptionType::String }; ReadonlySpan values {}; @@ -26,7 +27,7 @@ class IntlObject : public Object { JS_OBJECT(IntlObject, Object); public: - virtual ReadonlySpan relevant_extension_keys() const = 0; + virtual ReadonlySpan relevant_extension_keys() const = 0; virtual ReadonlySpan resolution_option_descriptors(VM&) const = 0; protected: diff --git a/Libraries/LibJS/Runtime/Intl/ListFormat.cpp b/Libraries/LibJS/Runtime/Intl/ListFormat.cpp index 02d8600bf2..b923481789 100644 --- a/Libraries/LibJS/Runtime/Intl/ListFormat.cpp +++ b/Libraries/LibJS/Runtime/Intl/ListFormat.cpp @@ -21,7 +21,7 @@ ListFormat::ListFormat(Object& prototype) } // 14.2.3 Internal slots, https://tc39.es/ecma402/#sec-Intl.ListFormat-internal-slots -ReadonlySpan ListFormat::relevant_extension_keys() const +ReadonlySpan ListFormat::relevant_extension_keys() const { // The value of the [[RelevantExtensionKeys]] internal slot is « ». return {}; @@ -71,7 +71,7 @@ GC::Ref format_list_to_parts(VM& vm, ListFormat const& list_format, Reado auto object = Object::create(realm, realm.intrinsics().object_prototype()); // b. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]). - MUST(object->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, part.type))); + MUST(object->create_data_property_or_throw(vm.names.type, PrimitiveString::create(vm, move(part.type)))); // c. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]). MUST(object->create_data_property_or_throw(vm.names.value, PrimitiveString::create(vm, move(part.value)))); diff --git a/Libraries/LibJS/Runtime/Intl/ListFormat.h b/Libraries/LibJS/Runtime/Intl/ListFormat.h index 1cdea60398..00dec81c2b 100644 --- a/Libraries/LibJS/Runtime/Intl/ListFormat.h +++ b/Libraries/LibJS/Runtime/Intl/ListFormat.h @@ -6,8 +6,8 @@ #pragma once -#include #include +#include #include #include #include @@ -30,21 +30,19 @@ public: virtual ~ListFormat() override = default; - virtual ReadonlySpan relevant_extension_keys() const override; + virtual ReadonlySpan relevant_extension_keys() const override; virtual ReadonlySpan resolution_option_descriptors(VM&) const override; - String const& locale() const { return m_locale; } - void set_locale(String locale) { m_locale = move(locale); } + Utf16String const& locale() const { return m_locale; } + void set_locale(Utf16String locale) { m_locale = move(locale); } Unicode::ListFormatType type() const { return m_type; } - void set_type(StringView type) { m_type = Unicode::list_format_type_from_string(type); } void set_type(Utf16View type) { m_type = Unicode::list_format_type_from_string(type); } - StringView type_string() const { return Unicode::list_format_type_to_string(m_type); } + Utf16String type_string() const { return Unicode::list_format_type_to_string(m_type); } Unicode::Style style() const { return m_style; } - void set_style(StringView style) { m_style = Unicode::style_from_string(style); } void set_style(Utf16View style) { m_style = Unicode::style_from_string(style); } - StringView style_string() const { return Unicode::style_to_string(m_style); } + Utf16String style_string() const { return Unicode::style_to_string(m_style); } Unicode::ListFormat const& formatter() const { return *m_formatter; } void set_formatter(NonnullOwnPtr formatter) { m_formatter = move(formatter); } @@ -52,7 +50,7 @@ public: private: explicit ListFormat(Object& prototype); - String m_locale; // [[Locale]] + Utf16String m_locale; // [[Locale]] Unicode::ListFormatType m_type { Unicode::ListFormatType::Conjunction }; // [[Type]] Unicode::Style m_style { Unicode::Style::Long }; // [[Style]] diff --git a/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp b/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp index d8edc70336..6fec1a0df3 100644 --- a/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp +++ b/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp @@ -63,13 +63,13 @@ ThrowCompletionOr> ListFormatConstructor::construct(FunctionObje list_format->set_locale(move(result.locale)); // 7. Let type be ? GetOption(options, "type", string, « "conjunction", "disjunction", "unit" », "conjunction"). - auto type = TRY(get_option(vm, *options, vm.names.type, OptionType::String, { "conjunction"sv, "disjunction"sv, "unit"sv }, "conjunction"sv)); + auto type = TRY(get_option(vm, *options, vm.names.type, OptionType::String, { "conjunction"sv, "disjunction"sv, "unit"sv }, u"conjunction"sv)); // 8. Set listFormat.[[Type]] to type. list_format->set_type(type.as_string().utf16_string_view()); // 9. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow" », "long"). - auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv }, "long"sv)); + auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv }, u"long"sv)); // 10. Set listFormat.[[Style]] to style. list_format->set_style(style.as_string().utf16_string_view()); @@ -78,7 +78,7 @@ ThrowCompletionOr> ListFormatConstructor::construct(FunctionObje // 12. Let dataLocaleTypes be resolvedLocaleData.[[]]. // 13. Set listFormat.[[Templates]] to dataLocaleTypes.[[