| // Copyright 2013 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_OBJECTS_INTL_OBJECTS_H_ |
| #define V8_OBJECTS_INTL_OBJECTS_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <set> |
| #include <string> |
| |
| #include "include/v8-internal.h" |
| #include "src/base/compiler-specific.h" |
| #include "src/base/timezone-cache.h" |
| #include "src/objects/contexts.h" |
| #include "src/objects/managed.h" |
| #include "src/objects/objects.h" |
| #include "unicode/brkiter.h" |
| #include "unicode/locid.h" |
| #include "unicode/uversion.h" |
| |
| #ifndef V8_INTL_SUPPORT |
| #error Internationalization is expected to be enabled. |
| #endif // V8_INTL_SUPPORT |
| |
| #define V8_MINIMUM_ICU_VERSION 73 |
| |
| namespace U_ICU_NAMESPACE { |
| class Locale; |
| class ListFormatter; |
| class RelativeDateTimeFormatter; |
| class SimpleDateFormat; |
| class DateIntervalFormat; |
| class PluralRules; |
| class Collator; |
| class FormattedValue; |
| class StringEnumeration; |
| class TimeZone; |
| class UnicodeString; |
| namespace number { |
| class LocalizedNumberFormatter; |
| class Notation; |
| } // namespace number |
| } // namespace U_ICU_NAMESPACE |
| |
| namespace v8::internal { |
| |
| #define ICU_EXTERNAL_POINTER_TAG_LIST(V) \ |
| V(icu::BreakIterator, kIcuBreakIteratorTag) \ |
| V(icu::Locale, kIcuLocaleTag) \ |
| V(icu::SimpleDateFormat, kIcuSimpleDateFormatTag) \ |
| V(icu::DateIntervalFormat, kIcuDateIntervalFormatTag) \ |
| V(icu::RelativeDateTimeFormatter, kIcuRelativeDateTimeFormatterTag) \ |
| V(icu::ListFormatter, kIcuListFormatterTag) \ |
| V(icu::Collator, kIcuCollatorTag) \ |
| V(icu::PluralRules, kIcuPluralRulesTag) \ |
| V(icu::number::LocalizedNumberFormatter, kIcuLocalizedNumberFormatterTag) |
| ICU_EXTERNAL_POINTER_TAG_LIST(ASSIGN_EXTERNAL_POINTER_TAG_FOR_MANAGED) |
| #undef ICU_EXTERNAL_POINTER_TAG_LIST |
| |
| struct NumberFormatSpan { |
| int32_t field_id; |
| int32_t begin_pos; |
| int32_t end_pos; |
| |
| NumberFormatSpan() = default; |
| NumberFormatSpan(int32_t field_id, int32_t begin_pos, int32_t end_pos) |
| : field_id(field_id), begin_pos(begin_pos), end_pos(end_pos) {} |
| }; |
| |
| V8_EXPORT_PRIVATE std::vector<NumberFormatSpan> FlattenRegionsToParts( |
| std::vector<NumberFormatSpan>* regions); |
| |
| class JSCollator; |
| |
| // Owns the ICU break iterator together with the string buffer it refers to. |
| class IcuBreakIteratorWithText final { |
| public: |
| static constexpr ManagedTypeId kTypeID = |
| ManagedTypeId::kIcuBreakIteratorWithText; |
| |
| // Adopts the given iterator with an empty string. |
| explicit IcuBreakIteratorWithText( |
| std::unique_ptr<icu::BreakIterator> iterator); |
| // Clones the given iterator and the string. |
| IcuBreakIteratorWithText(Isolate* isolate, |
| std::unique_ptr<icu::BreakIterator> iterator, |
| DirectHandle<String> string); |
| IcuBreakIteratorWithText(const IcuBreakIteratorWithText&) = delete; |
| IcuBreakIteratorWithText(IcuBreakIteratorWithText&&) V8_NOEXCEPT; |
| IcuBreakIteratorWithText& operator=(const IcuBreakIteratorWithText&) = delete; |
| IcuBreakIteratorWithText& operator=(IcuBreakIteratorWithText&&) V8_NOEXCEPT; |
| ~IcuBreakIteratorWithText(); |
| |
| const icu::BreakIterator* iterator() const { return iterator_.get(); } |
| icu::BreakIterator* iterator() { return iterator_.get(); } |
| const icu::UnicodeString* text() const { return text_.get(); } |
| icu::UnicodeString* text() { return text_.get(); } |
| |
| void SetText(Isolate* isolate, DirectHandle<String> string); |
| |
| private: |
| std::unique_ptr<icu::UnicodeString> text_; |
| std::unique_ptr<icu::BreakIterator> iterator_; // may refer to `text_` buffer |
| }; |
| |
| class Intl { |
| public: |
| enum class BoundFunctionContextSlot { |
| kBoundFunction = Context::MIN_CONTEXT_SLOTS, |
| kLength |
| }; |
| |
| // [[Notation]] is one of the String values "standard", "scientific", |
| // "engineering", or "compact", specifying whether the number should be |
| // displayed without scaling, scaled to the units place with the power of ten |
| // in scientific notation, scaled to the nearest thousand with the power of |
| // ten in scientific notation, or scaled to the nearest locale-dependent |
| // compact decimal notation power of ten with the corresponding compact |
| // decimal notation affix. |
| |
| enum class Notation { |
| STANDARD, |
| SCIENTIFIC, |
| ENGINEERING, |
| COMPACT, |
| }; |
| |
| // [[CompactDisplay]] is one of the String values "short" or "long", |
| // specifying whether to display compact notation affixes in short form ("5K") |
| // or long form ("5 thousand") if formatting with the "compact" notation. It |
| // is only used when [[Notation]] has the value "compact". |
| enum class CompactDisplay { |
| SHORT, |
| LONG, |
| }; |
| |
| enum class FormatRangeSource { kShared, kStartRange, kEndRange }; |
| |
| class FormatRangeSourceTracker { |
| public: |
| FormatRangeSourceTracker(); |
| void Add(int32_t field, int32_t start, int32_t limit); |
| FormatRangeSource GetSource(int32_t start, int32_t limit) const; |
| |
| private: |
| int32_t start_[2]; |
| int32_t limit_[2]; |
| |
| bool FieldContains(int32_t field, int32_t start, int32_t limit) const; |
| }; |
| |
| static DirectHandle<String> SourceString(Isolate* isolate, |
| FormatRangeSource source); |
| |
| // Build a set of ICU locales from a list of Locales. If there is a locale |
| // with a script tag then the locales also include a locale without the |
| // script; eg, pa_Guru_IN (language=Panjabi, script=Gurmukhi, country-India) |
| // would include pa_IN. |
| static std::set<std::string> BuildLocaleSet( |
| const std::vector<std::string>& locales, const char* path, |
| const char* validate_key); |
| |
| static Maybe<std::string> ToLanguageTag(const icu::Locale& locale); |
| |
| // Validate and canonicalize the locale. |
| // https://tc39.es/ecma402/#sec-isstructurallyvalidlanguagetag |
| // https://tc39.es/ecma402/#sec-canonicalizeunicodelocaleid |
| static Maybe<std::string> ValidateAndCanonicalizeUnicodeLocaleId( |
| Isolate* isolate, std::string_view locale); |
| |
| // Get the name of the numbering system from locale. |
| // ICU doesn't expose numbering system in any way, so we have to assume that |
| // for given locale NumberingSystem constructor produces the same digits as |
| // NumberFormat/Calendar would. |
| static std::string GetNumberingSystem(const icu::Locale& icu_locale); |
| |
| static V8_WARN_UNUSED_RESULT MaybeDirectHandle<JSObject> SupportedLocalesOf( |
| Isolate* isolate, const char* method_name, |
| const std::set<std::string>& available_locales, |
| DirectHandle<Object> locales_in, DirectHandle<Object> options_in); |
| |
| // https://tc39.es/ecma402/#sec-canonicalizelocalelist |
| // {only_return_one_result} is an optimization for callers that only |
| // care about the first result. |
| static Maybe<std::vector<std::string>> CanonicalizeLocaleList( |
| Isolate* isolate, DirectHandle<Object> locales, |
| bool only_return_one_result = false); |
| |
| // ecma-402 https://tc39.es/ecma262/#sec-intl.getcanonicallocales |
| V8_WARN_UNUSED_RESULT static MaybeDirectHandle<JSArray> GetCanonicalLocales( |
| Isolate* isolate, DirectHandle<Object> locales); |
| |
| // ecma-402 https://tc39.es/ecma262/#sec-intl.supportedvaluesof |
| V8_WARN_UNUSED_RESULT static MaybeDirectHandle<JSArray> SupportedValuesOf( |
| Isolate* isolate, DirectHandle<Object> key); |
| |
| // For locale sensitive functions |
| V8_WARN_UNUSED_RESULT static MaybeDirectHandle<String> |
| StringLocaleConvertCase(Isolate* isolate, DirectHandle<String> s, |
| bool is_upper, DirectHandle<Object> locales); |
| |
| V8_WARN_UNUSED_RESULT static MaybeDirectHandle<String> ConvertToUpper( |
| Isolate* isolate, DirectHandle<String> s); |
| |
| V8_WARN_UNUSED_RESULT static MaybeHandle<String> ConvertToLower( |
| Isolate* isolate, DirectHandle<String> s); |
| |
| V8_WARN_UNUSED_RESULT static std::optional<int> StringLocaleCompare( |
| Isolate* isolate, DirectHandle<String> s1, DirectHandle<String> s2, |
| DirectHandle<Object> locales, DirectHandle<Object> options, |
| const char* method_name); |
| |
| enum class CompareStringsOptions { |
| kNone, |
| kTryFastPath, |
| }; |
| template <class IsolateT> |
| V8_EXPORT_PRIVATE static CompareStringsOptions CompareStringsOptionsFor( |
| IsolateT* isolate, DirectHandle<Object> locales, |
| DirectHandle<Object> options); |
| V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT static int CompareStrings( |
| Isolate* isolate, const icu::Collator& collator, DirectHandle<String> s1, |
| DirectHandle<String> s2, |
| CompareStringsOptions compare_strings_options = |
| CompareStringsOptions::kNone); |
| |
| // https://tc39.es/ecma402/#sup-properties-of-the-number-prototype-object |
| V8_WARN_UNUSED_RESULT static MaybeDirectHandle<String> NumberToLocaleString( |
| Isolate* isolate, Handle<Object> num, DirectHandle<Object> locales, |
| DirectHandle<Object> options, const char* method_name); |
| |
| // [[RoundingPriority]] is one of the String values "auto", "morePrecision", |
| // or "lessPrecision", specifying the rounding priority for the number. |
| enum class RoundingPriority { |
| kAuto, |
| kMorePrecision, |
| kLessPrecision, |
| }; |
| |
| enum class RoundingType { |
| kFractionDigits, |
| kSignificantDigits, |
| kMorePrecision, |
| kLessPrecision, |
| }; |
| |
| // [[RoundingMode]] is one of the String values "ceil", "floor", "expand", |
| // "trunc", "halfCeil", "halfFloor", "halfExpand", "halfTrunc", or "halfEven", |
| // specifying the rounding strategy for the number. |
| enum class RoundingMode { |
| kCeil, |
| kFloor, |
| kExpand, |
| kTrunc, |
| kHalfCeil, |
| kHalfFloor, |
| kHalfExpand, |
| kHalfTrunc, |
| kHalfEven, |
| }; |
| |
| // [[TrailingZeroDisplay]] is one of the String values "auto" or |
| // "stripIfInteger", specifying the strategy for displaying trailing zeros on |
| // whole number. |
| enum class TrailingZeroDisplay { |
| kAuto, |
| kStripIfInteger, |
| }; |
| |
| // https://tc39.es/ecma402/#sec-setnfdigitoptions |
| struct NumberFormatDigitOptions { |
| int minimum_integer_digits; |
| int minimum_fraction_digits; |
| int maximum_fraction_digits; |
| int minimum_significant_digits; |
| int maximum_significant_digits; |
| RoundingPriority rounding_priority; |
| RoundingType rounding_type; |
| int rounding_increment; |
| RoundingMode rounding_mode; |
| TrailingZeroDisplay trailing_zero_display; |
| }; |
| V8_WARN_UNUSED_RESULT static Maybe<NumberFormatDigitOptions> |
| SetNumberFormatDigitOptions(Isolate* isolate, |
| DirectHandle<JSReceiver> options, |
| int mnfd_default, int mxfd_default, |
| bool notation_is_compact, const char* service); |
| |
| // Helper function to convert a UnicodeString to a Handle<String> |
| V8_WARN_UNUSED_RESULT static MaybeHandle<String> ToString( |
| Isolate* isolate, const icu::UnicodeString& string); |
| |
| // Helper function to convert a substring of UnicodeString to a Handle<String> |
| V8_WARN_UNUSED_RESULT static MaybeDirectHandle<String> ToString( |
| Isolate* isolate, const icu::UnicodeString& string, int32_t begin, |
| int32_t end); |
| |
| // Helper function to convert a FormattedValue to String |
| V8_WARN_UNUSED_RESULT static MaybeDirectHandle<String> FormattedToString( |
| Isolate* isolate, const icu::FormattedValue& formatted); |
| |
| // Helper function to convert number field id to type string. |
| static DirectHandle<String> NumberFieldToType(Isolate* isolate, |
| const NumberFormatSpan& part, |
| const icu::UnicodeString& text, |
| bool is_nan); |
| |
| // A helper function to implement formatToParts which add element to array as |
| // $array[$index] = { type: $field_type_string, value: $value } |
| static void AddElement(Isolate* isolate, DirectHandle<JSArray> array, |
| uint32_t index, DirectHandle<String> field_type_string, |
| DirectHandle<String> value); |
| |
| // A helper function to implement formatToParts which add element to array as |
| // $array[$index] = { |
| // type: $field_type_string, value: $value, |
| // $additional_property_name: $additional_property_value |
| // } |
| static void AddElement(Isolate* isolate, DirectHandle<JSArray> array, |
| uint32_t index, DirectHandle<String> field_type_string, |
| DirectHandle<String> value, |
| DirectHandle<String> additional_property_name, |
| DirectHandle<String> additional_property_value); |
| |
| // A helper function to implement formatToParts which add element to array |
| static Maybe<int> AddNumberElements(Isolate* isolate, |
| const icu::FormattedValue& formatted, |
| DirectHandle<JSArray> result, |
| int start_index, |
| DirectHandle<String> unit); |
| |
| // In ECMA 402 v1, Intl constructors supported a mode of operation |
| // where calling them with an existing object as a receiver would |
| // transform the receiver into the relevant Intl instance with all |
| // internal slots. In ECMA 402 v2, this capability was removed, to |
| // avoid adding internal slots on existing objects. In ECMA 402 v3, |
| // the capability was re-added as "normative optional" in a mode |
| // which chains the underlying Intl instance on any object, when the |
| // constructor is called |
| // |
| // See https://tc39.es/ecma402/#legacy-constructor. |
| V8_WARN_UNUSED_RESULT static MaybeDirectHandle<Object> LegacyUnwrapReceiver( |
| Isolate* isolate, DirectHandle<JSReceiver> receiver, |
| DirectHandle<JSFunction> constructor, bool has_initialized_slot); |
| |
| // enum for "localeMatcher" option: shared by many Intl objects. |
| enum class MatcherOption { kBestFit, kLookup }; |
| |
| // Shared function to read the "localeMatcher" option. |
| V8_WARN_UNUSED_RESULT static Maybe<MatcherOption> GetLocaleMatcher( |
| Isolate* isolate, DirectHandle<JSReceiver> options, |
| const char* method_name); |
| |
| // Shared function to read the "numberingSystem" option. |
| V8_WARN_UNUSED_RESULT static Maybe<bool> GetNumberingSystem( |
| Isolate* isolate, DirectHandle<JSReceiver> options, |
| const char* method_name, std::string& result); |
| |
| // Check the calendar is valid or not for that locale. |
| static bool IsValidCalendar(const icu::Locale& locale, |
| const std::string& value); |
| |
| // Check the collation is valid or not for that locale. |
| static bool IsValidCollation(const icu::Locale& locale, |
| const std::string& value); |
| |
| // Check the numberingSystem is valid. |
| static bool IsValidNumberingSystem(const std::string& value); |
| |
| // Check the calendar is well formed. |
| static bool IsWellFormedCalendar(std::string_view value); |
| |
| // Check the currency is well formed. |
| static bool IsWellFormedCurrency(std::string_view value); |
| |
| struct ResolvedLocale { |
| std::string locale; |
| icu::Locale icu_locale; |
| std::map<std::string, std::string> extensions; |
| }; |
| |
| static Maybe<ResolvedLocale> ResolveLocale( |
| Isolate* isolate, const std::set<std::string>& available_locales, |
| const std::vector<std::string>& requested_locales, MatcherOption options, |
| const std::set<std::string>& relevant_extension_keys); |
| |
| // A helper template to implement the GetAvailableLocales |
| // Usage in src/objects/js-XXX.cc |
| // const std::set<std::string>& JSXxx::GetAvailableLocales() { |
| // static base::LazyInstance<Intl::AvailableLocales<icu::YYY>>::type |
| // available_locales = LAZY_INSTANCE_INITIALIZER; |
| // return available_locales.Pointer()->Get(); |
| // } |
| |
| struct SkipResourceCheck { |
| static const char* key() { return nullptr; } |
| static const char* path() { return nullptr; } |
| }; |
| |
| template <typename C = SkipResourceCheck> |
| class AvailableLocales { |
| public: |
| AvailableLocales() { |
| UErrorCode status = U_ZERO_ERROR; |
| UEnumeration* uenum = |
| uloc_openAvailableByType(ULOC_AVAILABLE_WITH_LEGACY_ALIASES, &status); |
| DCHECK(U_SUCCESS(status)); |
| |
| std::vector<std::string> all_locales; |
| const char* loc; |
| while ((loc = uenum_next(uenum, nullptr, &status)) != nullptr) { |
| DCHECK(U_SUCCESS(status)); |
| std::string locstr(loc); |
| std::replace(locstr.begin(), locstr.end(), '_', '-'); |
| // Handle special case |
| if (locstr == "en-US-POSIX") locstr = "en-US-u-va-posix"; |
| all_locales.push_back(locstr); |
| } |
| uenum_close(uenum); |
| |
| set_ = Intl::BuildLocaleSet(all_locales, C::path(), C::key()); |
| } |
| const std::set<std::string>& Get() const { return set_; } |
| |
| private: |
| std::set<std::string> set_; |
| }; |
| |
| // https://tc39.es/ecma262/#sec-string.prototype.normalize |
| V8_WARN_UNUSED_RESULT static MaybeDirectHandle<String> Normalize( |
| Isolate* isolate, DirectHandle<String> string, |
| DirectHandle<Object> form_input); |
| static base::TimezoneCache* CreateTimeZoneCache(); |
| |
| // Convert a Handle<String> to icu::UnicodeString |
| static icu::UnicodeString ToICUUnicodeString(Isolate* isolate, |
| DirectHandle<String> string, |
| uint32_t offset = 0); |
| |
| static const uint8_t* ToLatin1LowerTable(); |
| |
| static const uint8_t* AsciiCollationWeightsL1(); |
| static const uint8_t* AsciiCollationWeightsL3(); |
| static const int kAsciiCollationWeightsLength; |
| |
| // Maximum min(left.length, right.length) for which the localeCompare |
| // ASCII fast path is inlined; longer inputs go straight to the builtin. |
| static constexpr int kInlineLocaleCompareMaxMinLength = 32; |
| |
| static Tagged<String> ConvertOneByteToLower(Tagged<String> src, |
| Tagged<String> dst); |
| |
| static const std::set<std::string>& GetAvailableLocales(); |
| |
| static const std::set<std::string>& GetAvailableLocalesForDateFormat(); |
| |
| V8_WARN_UNUSED_RESULT static MaybeDirectHandle<JSArray> ToJSArray( |
| Isolate* isolate, |
| const std::function<std::string(const char*)>& transforms, |
| icu::StringEnumeration* enumeration, |
| const std::function<bool(const char*)>& removes, bool sort); |
| |
| static bool RemoveCollation(const char* collation); |
| |
| static std::set<std::string> SanctionedSimpleUnits(); |
| |
| V8_WARN_UNUSED_RESULT static MaybeDirectHandle<JSArray> AvailableCalendars( |
| Isolate* isolate); |
| |
| V8_WARN_UNUSED_RESULT static bool IsValidTimeZoneName( |
| const icu::TimeZone& tz); |
| V8_WARN_UNUSED_RESULT static bool IsValidTimeZoneName(Isolate* isolate, |
| const std::string& id); |
| V8_WARN_UNUSED_RESULT static bool IsValidTimeZoneName( |
| Isolate* isolate, DirectHandle<String> id); |
| |
| // Function to support Temporal |
| V8_WARN_UNUSED_RESULT static std::string TimeZoneIdFromIndex(int32_t index); |
| |
| // Return the index of timezone which later could be used with |
| // TimeZoneIdFromIndex. Returns -1 while the identifier is not a built-in |
| // TimeZone name. |
| static int32_t GetTimeZoneIndex(Isolate* isolate, |
| DirectHandle<String> identifier); |
| |
| enum class Transition { kNext, kPrevious }; |
| |
| // Functions to support Temporal |
| |
| // Return the epoch of transition in BigInt or null if there are no |
| // transition. |
| static DirectHandle<Object> GetTimeZoneOffsetTransitionNanoseconds( |
| Isolate* isolate, int32_t time_zone_index, |
| DirectHandle<BigInt> nanosecond_epoch, Transition transition); |
| |
| // Return the Time Zone offset, in the unit of nanosecond by int64_t, during |
| // the time of the nanosecond_epoch. |
| static int64_t GetTimeZoneOffsetNanoseconds( |
| Isolate* isolate, int32_t time_zone_index, |
| DirectHandle<BigInt> nanosecond_epoch); |
| |
| // This function may return the result, the std::vector<int64_t> in one of |
| // the following three conditions: |
| // 1. While nanosecond_epoch fall into the daylight saving time change |
| // moment that skipped one (or two or even six, in some Time Zone) hours |
| // later in local time: |
| // [], |
| // 2. In other moment not during daylight saving time change: |
| // [offset_former], and |
| // 3. when nanosecond_epoch fall into they daylight saving time change hour |
| // which the clock time roll back one (or two or six, in some Time Zone) hour: |
| // [offset_former, offset_later] |
| // The unit of the return values in BigInt is nanosecond. |
| static DirectHandleVector<BigInt> GetTimeZonePossibleOffsetNanoseconds( |
| Isolate* isolate, int32_t time_zone_index, |
| DirectHandle<BigInt> nanosecond_epoch); |
| |
| V8_WARN_UNUSED_RESULT static MaybeHandle<String> TimeZoneIdToString( |
| Isolate* isolate, const icu::UnicodeString& id); |
| static std::string TimeZoneIdToString(const icu::UnicodeString& id); |
| static std::string DefaultTimeZone(); |
| |
| static icu::number::Notation ToICUNotation( |
| Intl::Notation notation, Intl::CompactDisplay compact_display); |
| |
| V8_WARN_UNUSED_RESULT static Notation NotationFromSkeleton( |
| const icu::UnicodeString& skeleton); |
| V8_WARN_UNUSED_RESULT static DirectHandle<String> NotationAsString( |
| Isolate* isolate, Notation notation); |
| |
| V8_WARN_UNUSED_RESULT static DirectHandle<String> CompactDisplayString( |
| Isolate* isolate, const icu::UnicodeString& skeleton); |
| }; |
| |
| } // namespace v8::internal |
| |
| #endif // V8_OBJECTS_INTL_OBJECTS_H_ |