From b6bef6b688d2f818384ed7e84c725415e9634075 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 21 Jun 2026 19:03:10 +0200 Subject: [PATCH] Libraries: Use UTF-16 for JS-visible runtime strings Produce JS-visible string results as UTF-16 at their source, including numeric formatting, BigInt and BigFraction formatting, URI encoding, console formatting, parser errors, regular expression errors, Intl and Temporal records, LibUnicode locale boundaries, and LibWeb bindings. Handle fractional radix formatting through the UTF-16 builder view. --- AK/Base64.cpp | 34 + AK/Base64.h | 5 + AK/Utf16String.h | 17 + AK/Utf16StringData.cpp | 9 + AK/Utf16StringData.h | 9 + Libraries/LibCore/TimeZone.cpp | 6 +- .../LibCrypto/BigFraction/BigFraction.cpp | 45 +- Libraries/LibCrypto/BigFraction/BigFraction.h | 3 +- .../LibCrypto/BigInt/SignedBigInteger.cpp | 24 + Libraries/LibCrypto/BigInt/SignedBigInteger.h | 2 + .../LibCrypto/BigInt/UnsignedBigInteger.cpp | 24 + .../LibCrypto/BigInt/UnsignedBigInteger.h | 2 + .../Bytecode/AsmInterpreter/AsmSlowPaths.cpp | 4 +- Libraries/LibJS/Bytecode/Executable.cpp | 2 +- Libraries/LibJS/Console.cpp | 14 +- Libraries/LibJS/Console.h | 2 +- Libraries/LibJS/Contrib/Test262/262Object.cpp | 2 +- Libraries/LibJS/ParserError.cpp | 6 +- Libraries/LibJS/ParserError.h | 6 +- Libraries/LibJS/Print.cpp | 23 +- Libraries/LibJS/Print.h | 10 +- .../LibJS/Runtime/AbstractOperations.cpp | 11 +- Libraries/LibJS/Runtime/AbstractOperations.h | 3 +- .../LibJS/Runtime/AggregateErrorPrototype.cpp | 4 +- .../LibJS/Runtime/ArrayIteratorPrototype.cpp | 2 +- Libraries/LibJS/Runtime/ArrayPrototype.cpp | 4 +- .../LibJS/Runtime/AsyncGeneratorPrototype.cpp | 2 +- Libraries/LibJS/Runtime/AtomicsObject.cpp | 2 +- Libraries/LibJS/Runtime/BigInt.cpp | 7 +- Libraries/LibJS/Runtime/BigInt.h | 1 - Libraries/LibJS/Runtime/BigIntPrototype.cpp | 2 +- Libraries/LibJS/Runtime/BooleanPrototype.cpp | 2 +- Libraries/LibJS/Runtime/ConsoleObject.cpp | 2 +- Libraries/LibJS/Runtime/Date.cpp | 35 +- Libraries/LibJS/Runtime/Date.h | 9 +- Libraries/LibJS/Runtime/DatePrototype.cpp | 37 +- Libraries/LibJS/Runtime/DatePrototype.h | 4 +- Libraries/LibJS/Runtime/Error.cpp | 22 + Libraries/LibJS/Runtime/Error.h | 3 + Libraries/LibJS/Runtime/ErrorPrototype.cpp | 32 +- Libraries/LibJS/Runtime/ErrorTypes.cpp | 9 +- Libraries/LibJS/Runtime/ErrorTypes.h | 8 +- .../LibJS/Runtime/FunctionConstructor.cpp | 2 +- Libraries/LibJS/Runtime/FunctionPrototype.cpp | 6 +- .../Runtime/GeneratorFunctionPrototype.cpp | 2 +- .../LibJS/Runtime/GeneratorPrototype.cpp | 2 +- Libraries/LibJS/Runtime/GlobalObject.cpp | 116 +-- .../LibJS/Runtime/Intl/AbstractOperations.cpp | 113 +-- .../LibJS/Runtime/Intl/AbstractOperations.h | 40 +- Libraries/LibJS/Runtime/Intl/Collator.cpp | 4 +- Libraries/LibJS/Runtime/Intl/Collator.h | 23 +- .../Runtime/Intl/CollatorCompareFunction.cpp | 2 +- .../Runtime/Intl/CollatorConstructor.cpp | 20 +- .../LibJS/Runtime/Intl/CollatorPrototype.cpp | 2 +- .../LibJS/Runtime/Intl/DateTimeFormat.cpp | 32 +- Libraries/LibJS/Runtime/Intl/DateTimeFormat.h | 41 +- .../Intl/DateTimeFormatConstructor.cpp | 65 +- .../Runtime/Intl/DateTimeFormatConstructor.h | 4 +- .../Runtime/Intl/DateTimeFormatFunction.cpp | 2 +- .../Runtime/Intl/DateTimeFormatPrototype.cpp | 4 +- Libraries/LibJS/Runtime/Intl/DisplayNames.cpp | 35 +- Libraries/LibJS/Runtime/Intl/DisplayNames.h | 23 +- .../Runtime/Intl/DisplayNamesConstructor.cpp | 7 +- .../Runtime/Intl/DisplayNamesPrototype.cpp | 16 +- .../LibJS/Runtime/Intl/DurationFormat.cpp | 83 +-- Libraries/LibJS/Runtime/Intl/DurationFormat.h | 28 +- .../Intl/DurationFormatConstructor.cpp | 6 +- .../Runtime/Intl/DurationFormatPrototype.cpp | 12 +- Libraries/LibJS/Runtime/Intl/Intl.cpp | 35 +- Libraries/LibJS/Runtime/Intl/IntlObject.h | 5 +- Libraries/LibJS/Runtime/Intl/ListFormat.cpp | 4 +- Libraries/LibJS/Runtime/Intl/ListFormat.h | 16 +- .../Runtime/Intl/ListFormatConstructor.cpp | 6 +- .../Runtime/Intl/ListFormatPrototype.cpp | 2 +- Libraries/LibJS/Runtime/Intl/Locale.cpp | 60 +- Libraries/LibJS/Runtime/Intl/Locale.h | 57 +- .../LibJS/Runtime/Intl/LocaleConstructor.cpp | 121 ++-- .../LibJS/Runtime/Intl/LocalePrototype.cpp | 12 +- .../LibJS/Runtime/Intl/MathematicalValue.cpp | 10 + .../LibJS/Runtime/Intl/MathematicalValue.h | 7 +- Libraries/LibJS/Runtime/Intl/NumberFormat.cpp | 27 +- Libraries/LibJS/Runtime/Intl/NumberFormat.h | 61 +- .../Runtime/Intl/NumberFormatConstructor.cpp | 30 +- .../Runtime/Intl/NumberFormatFunction.cpp | 2 +- .../Runtime/Intl/NumberFormatPrototype.cpp | 2 +- Libraries/LibJS/Runtime/Intl/PluralRules.cpp | 2 +- Libraries/LibJS/Runtime/Intl/PluralRules.h | 6 +- .../Runtime/Intl/PluralRulesConstructor.cpp | 8 +- .../Runtime/Intl/PluralRulesPrototype.cpp | 2 +- .../LibJS/Runtime/Intl/RelativeTimeFormat.cpp | 8 +- .../LibJS/Runtime/Intl/RelativeTimeFormat.h | 22 +- .../Intl/RelativeTimeFormatConstructor.cpp | 10 +- .../Intl/RelativeTimeFormatPrototype.cpp | 2 +- .../Runtime/Intl/SegmentIteratorPrototype.cpp | 2 +- Libraries/LibJS/Runtime/Intl/Segmenter.cpp | 2 +- Libraries/LibJS/Runtime/Intl/Segmenter.h | 13 +- .../Runtime/Intl/SegmenterConstructor.cpp | 4 +- .../LibJS/Runtime/Intl/SegmenterPrototype.cpp | 2 +- .../Runtime/Intl/SingleUnitIdentifiers.h | 92 +-- Libraries/LibJS/Runtime/Intrinsics.cpp | 2 +- .../LibJS/Runtime/IteratorHelperPrototype.cpp | 2 +- Libraries/LibJS/Runtime/JSONObject.cpp | 6 +- Libraries/LibJS/Runtime/JSONObject.h | 4 +- Libraries/LibJS/Runtime/MapConstructor.cpp | 2 +- .../LibJS/Runtime/MapIteratorPrototype.cpp | 2 +- .../LibJS/Runtime/ModuleNamespaceObject.cpp | 2 +- Libraries/LibJS/Runtime/NumberPrototype.cpp | 99 +-- Libraries/LibJS/Runtime/ObjectConstructor.cpp | 2 +- Libraries/LibJS/Runtime/ObjectPrototype.cpp | 4 +- Libraries/LibJS/Runtime/PrimitiveString.cpp | 25 +- Libraries/LibJS/Runtime/PrimitiveString.h | 4 - Libraries/LibJS/Runtime/Promise.cpp | 6 +- .../LibJS/Runtime/PromiseConstructor.cpp | 8 +- .../PromiseResolvingElementFunctions.cpp | 4 +- Libraries/LibJS/Runtime/RegExpObject.cpp | 22 +- Libraries/LibJS/Runtime/RegExpObject.h | 2 +- Libraries/LibJS/Runtime/RegExpPrototype.cpp | 4 +- .../Runtime/RegExpStringIteratorPrototype.cpp | 2 +- .../LibJS/Runtime/SetIteratorPrototype.cpp | 2 +- Libraries/LibJS/Runtime/StringConstructor.cpp | 6 +- .../LibJS/Runtime/StringIteratorPrototype.cpp | 2 +- Libraries/LibJS/Runtime/StringPrototype.cpp | 18 +- .../Runtime/SuppressedErrorPrototype.cpp | 4 +- Libraries/LibJS/Runtime/SymbolPrototype.cpp | 2 +- .../Runtime/Temporal/AbstractOperations.cpp | 57 +- .../Runtime/Temporal/AbstractOperations.h | 5 +- Libraries/LibJS/Runtime/Temporal/Calendar.cpp | 188 +++-- Libraries/LibJS/Runtime/Temporal/Calendar.h | 86 ++- Libraries/LibJS/Runtime/Temporal/Duration.cpp | 14 +- Libraries/LibJS/Runtime/Temporal/Duration.h | 13 +- .../Runtime/Temporal/DurationPrototype.cpp | 2 +- Libraries/LibJS/Runtime/Temporal/ISORecords.h | 2 +- Libraries/LibJS/Runtime/Temporal/Instant.cpp | 10 +- Libraries/LibJS/Runtime/Temporal/Instant.h | 3 +- .../Runtime/Temporal/InstantPrototype.cpp | 8 +- Libraries/LibJS/Runtime/Temporal/Now.cpp | 15 +- .../LibJS/Runtime/Temporal/PlainDate.cpp | 20 +- Libraries/LibJS/Runtime/Temporal/PlainDate.h | 17 +- .../Runtime/Temporal/PlainDateConstructor.cpp | 2 +- .../Runtime/Temporal/PlainDatePrototype.cpp | 7 +- .../LibJS/Runtime/Temporal/PlainDateTime.cpp | 18 +- .../LibJS/Runtime/Temporal/PlainDateTime.h | 19 +- .../Temporal/PlainDateTimeConstructor.cpp | 2 +- .../Temporal/PlainDateTimePrototype.cpp | 5 +- .../LibJS/Runtime/Temporal/PlainMonthDay.cpp | 14 +- .../LibJS/Runtime/Temporal/PlainMonthDay.h | 13 +- .../Temporal/PlainMonthDayConstructor.cpp | 2 +- .../Temporal/PlainMonthDayPrototype.cpp | 2 +- Libraries/LibJS/Runtime/Temporal/PlainTime.h | 1 + .../Runtime/Temporal/PlainTimePrototype.cpp | 2 +- .../LibJS/Runtime/Temporal/PlainYearMonth.cpp | 14 +- .../LibJS/Runtime/Temporal/PlainYearMonth.h | 13 +- .../Temporal/PlainYearMonthConstructor.cpp | 2 +- .../Temporal/PlainYearMonthPrototype.cpp | 5 +- Libraries/LibJS/Runtime/Temporal/Temporal.cpp | 2 +- Libraries/LibJS/Runtime/Temporal/TimeZone.cpp | 98 +-- Libraries/LibJS/Runtime/Temporal/TimeZone.h | 34 +- .../LibJS/Runtime/Temporal/ZonedDateTime.cpp | 30 +- .../LibJS/Runtime/Temporal/ZonedDateTime.h | 25 +- .../Temporal/ZonedDateTimeConstructor.cpp | 6 +- .../Temporal/ZonedDateTimePrototype.cpp | 12 +- Libraries/LibJS/Runtime/Uint8Array.cpp | 10 +- Libraries/LibJS/Runtime/VM.cpp | 4 +- Libraries/LibJS/Runtime/VM.h | 6 +- Libraries/LibJS/Runtime/Value.cpp | 31 +- Libraries/LibJS/Runtime/Value.h | 3 - .../LibJS/Runtime/WeakMapConstructor.cpp | 2 +- Libraries/LibJS/RustIntegration.cpp | 30 +- Libraries/LibJS/RustIntegration.h | 4 +- Libraries/LibUnicode/Calendar.cpp | 110 +-- Libraries/LibUnicode/Calendar.h | 25 +- Libraries/LibUnicode/Collator.cpp | 26 +- Libraries/LibUnicode/Collator.h | 9 +- Libraries/LibUnicode/DateTimeFormat.cpp | 92 +-- Libraries/LibUnicode/DateTimeFormat.h | 16 +- Libraries/LibUnicode/DisplayNames.cpp | 72 +- Libraries/LibUnicode/DisplayNames.h | 14 +- Libraries/LibUnicode/DurationFormat.cpp | 6 +- Libraries/LibUnicode/DurationFormat.h | 3 +- Libraries/LibUnicode/ICU.cpp | 24 +- Libraries/LibUnicode/ICU.h | 7 +- Libraries/LibUnicode/ListFormat.cpp | 14 +- Libraries/LibUnicode/ListFormat.h | 4 +- Libraries/LibUnicode/Locale.cpp | 198 +++-- Libraries/LibUnicode/Locale.h | 41 +- Libraries/LibUnicode/Normalize.cpp | 15 - Libraries/LibUnicode/Normalize.h | 1 - Libraries/LibUnicode/NumberFormat.cpp | 155 ++-- Libraries/LibUnicode/NumberFormat.h | 28 +- Libraries/LibUnicode/PluralRules.cpp | 6 +- Libraries/LibUnicode/PluralRules.h | 3 +- Libraries/LibUnicode/RelativeTimeFormat.cpp | 36 +- Libraries/LibUnicode/RelativeTimeFormat.h | 8 +- Libraries/LibUnicode/Segmenter.cpp | 12 +- Libraries/LibUnicode/Segmenter.h | 3 +- Libraries/LibUnicode/TimeZone.cpp | 64 +- Libraries/LibUnicode/TimeZone.h | 12 +- Libraries/LibUnicode/UnicodeKeywords.cpp | 163 +++-- Libraries/LibUnicode/UnicodeKeywords.h | 26 +- .../LibWeb/Animations/KeyframeEffect.cpp | 8 +- .../LibWeb/Bindings/AudioConstructor.cpp | 2 +- .../LibWeb/Bindings/ImageConstructor.cpp | 2 +- Libraries/LibWeb/Bindings/InterfaceObject.cpp | 11 +- Libraries/LibWeb/Bindings/MainThreadVM.cpp | 4 +- .../LibWeb/Bindings/OptionConstructor.cpp | 2 +- Libraries/LibWeb/Bindings/PlatformObject.cpp | 2 +- Libraries/LibWeb/CSS/CSSDescriptors.cpp | 4 +- Libraries/LibWeb/CSS/CSSDescriptors.h | 2 +- .../LibWeb/CSS/CSSFontFeatureValuesMap.cpp | 2 +- Libraries/LibWeb/CSS/CSSStyleDeclaration.h | 3 +- Libraries/LibWeb/CSS/CSSStyleProperties.cpp | 8 +- Libraries/LibWeb/CSS/CSSStyleProperties.h | 2 +- Libraries/LibWeb/CSS/CSSUnparsedValue.cpp | 10 +- Libraries/LibWeb/CSS/CSSUnparsedValue.h | 2 +- Libraries/LibWeb/CSS/CSSUnparsedValue.idl | 2 +- Libraries/LibWeb/CSS/MediaList.cpp | 2 +- .../CSS/StyleValues/UnresolvedStyleValue.cpp | 2 +- Libraries/LibWeb/Clipboard/Clipboard.cpp | 10 +- Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp | 679 +++++++++--------- Libraries/LibWeb/Crypto/CryptoAlgorithms.h | 13 +- Libraries/LibWeb/Crypto/CryptoBindings.cpp | 11 +- Libraries/LibWeb/Crypto/CryptoBindings.h | 43 +- Libraries/LibWeb/Crypto/CryptoKey.cpp | 46 +- Libraries/LibWeb/Crypto/CryptoKey.h | 2 +- Libraries/LibWeb/Crypto/KeyAlgorithms.cpp | 2 +- Libraries/LibWeb/Crypto/SubtleCrypto.cpp | 59 +- Libraries/LibWeb/DOM/Attr.cpp | 8 +- Libraries/LibWeb/DOM/DOMTokenList.cpp | 2 +- Libraries/LibWeb/DOM/Element.cpp | 6 +- Libraries/LibWeb/DOM/EventTarget.cpp | 5 +- .../LibWeb/DOMURL/URLSearchParamsIterator.cpp | 6 +- .../LibWeb/Encoding/TextDecoderStream.cpp | 2 +- Libraries/LibWeb/Fetch/Body.cpp | 2 +- Libraries/LibWeb/Fetch/HeadersIterator.cpp | 6 +- Libraries/LibWeb/FileAPI/Blob.cpp | 2 +- .../LibWeb/Geometry/DOMMatrixReadOnly.cpp | 44 +- .../HTML/CrossOrigin/AbstractOperations.cpp | 2 +- .../HTML/CustomElements/CustomStateSet.cpp | 2 +- Libraries/LibWeb/HTML/DOMParser.cpp | 19 +- Libraries/LibWeb/HTML/DOMStringList.cpp | 2 +- Libraries/LibWeb/HTML/DOMStringMap.cpp | 2 +- Libraries/LibWeb/HTML/DataTransferItem.cpp | 2 +- Libraries/LibWeb/HTML/ErrorEvent.cpp | 4 +- Libraries/LibWeb/HTML/ErrorEvent.h | 8 +- Libraries/LibWeb/HTML/EventSource.cpp | 3 +- Libraries/LibWeb/HTML/NavigateEvent.h | 8 +- .../LibWeb/HTML/Scripting/ClassicScript.cpp | 16 +- .../LibWeb/HTML/Scripting/ModuleScript.cpp | 16 +- Libraries/LibWeb/HTML/Storage.cpp | 2 +- Libraries/LibWeb/HTML/StructuredSerialize.cpp | 6 +- .../LibWeb/HTML/WindowOrWorkerGlobalScope.cpp | 4 +- Libraries/LibWeb/IndexedDB/IDBFactory.cpp | 2 +- Libraries/LibWeb/IndexedDB/IDBIndex.cpp | 4 +- Libraries/LibWeb/IndexedDB/IDBObjectStore.cpp | 4 +- .../LibWeb/IndexedDB/Internal/Algorithms.cpp | 6 +- Libraries/LibWeb/Infra/JSON.cpp | 5 +- .../LibWeb/ServiceWorker/CacheStorage.cpp | 2 +- .../LibWeb/Streams/AbstractOperations.cpp | 3 +- Libraries/LibWeb/WebAssembly/Memory.cpp | 12 +- Libraries/LibWeb/WebAssembly/WebAssembly.cpp | 142 ++-- Libraries/LibWeb/WebDriver/JSON.cpp | 2 +- .../WebGL/WebGLRenderingContextImpl.cpp | 14 +- Libraries/LibWeb/WebIDL/AbstractOperations.h | 12 + Libraries/LibWeb/WebSockets/WebSocket.cpp | 4 +- Libraries/LibWeb/XHR/FormDataIterator.cpp | 6 +- Libraries/LibWeb/XHR/XMLHttpRequest.cpp | 4 +- .../generate_libweb_css_style_properties.py | 3 +- .../libweb_bindings/callback_interfaces.py | 2 +- Meta/Generators/libweb_bindings/interfaces.py | 6 +- Meta/Generators/libweb_bindings/iterables.py | 4 +- .../named_and_indexed_properties.py | 2 +- Meta/Generators/libweb_bindings/namespaces.py | 2 +- .../Generators/libweb_bindings/to_js_value.py | 20 +- Services/WebContent/DevToolsConsoleClient.cpp | 4 +- Tests/AK/TestBase64.cpp | 16 + Tests/LibCrypto/TestBigFraction.cpp | 2 +- .../Runtime/builtins/JSON/JSON.stringify.js | 1 + .../Number/Number.prototype.toString.js | 4 + Tests/LibJS/test-js.cpp | 9 +- Tests/LibJS/test-primitive-string.cpp | 34 +- Tests/LibUnicode/TestDisplayNames.cpp | 36 +- Tests/LibUnicode/TestTimeZone.cpp | 44 +- Tests/LibWasm/test-wasm.cpp | 62 +- .../SubtleCrypto-jwk-non-ascii-base64url.txt | 1 + .../SubtleCrypto-jwk-non-ascii-base64url.html | 16 + Utilities/js.cpp | 10 +- 286 files changed, 3042 insertions(+), 2631 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/Crypto/SubtleCrypto-jwk-non-ascii-base64url.txt create mode 100644 Tests/LibWeb/Text/input/Crypto/SubtleCrypto-jwk-non-ascii-base64url.html 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.[[