blob: 6ea9427d1f49bbd346af7f0414e1332ea90fdb21 [file]
// 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_