v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
intl-objects.cc
Go to the documentation of this file.
1// Copyright 2013 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <algorithm>
8#include <limits>
9#include <memory>
10#include <optional>
11#include <string>
12#include <vector>
13
14#include "src/api/api-inl.h"
15#include "src/base/logging.h"
16#include "src/base/strings.h"
17#include "src/common/globals.h"
18#include "src/date/date.h"
22#include "src/heap/factory.h"
33#include "src/objects/smi.h"
34#include "src/objects/string.h"
36#pragma GCC diagnostic push
37#pragma GCC diagnostic ignored "-Wshadow"
38#include "unicode/basictz.h"
39#include "unicode/brkiter.h"
40#include "unicode/calendar.h"
41#include "unicode/coll.h"
42#include "unicode/datefmt.h"
43#include "unicode/decimfmt.h"
44#include "unicode/formattedvalue.h"
45#include "unicode/localebuilder.h"
46#include "unicode/localematcher.h"
47#include "unicode/locid.h"
48#include "unicode/normalizer2.h"
49#include "unicode/numberformatter.h"
50#include "unicode/numfmt.h"
51#include "unicode/numsys.h"
52#include "unicode/timezone.h"
53#include "unicode/ures.h"
54#include "unicode/ustring.h"
55#include "unicode/uvernum.h" // U_ICU_VERSION_MAJOR_NUM
56#pragma GCC diagnostic pop
57
58#ifndef V8_INTL_SUPPORT
59#error Internationalization is expected to be enabled.
60#endif // V8_INTL_SUPPORT
61
62#define XSTR(s) STR(s)
63#define STR(s) #s
64static_assert(
65 V8_MINIMUM_ICU_VERSION <= U_ICU_VERSION_MAJOR_NUM,
66 "v8 is required to build with ICU " XSTR(V8_MINIMUM_ICU_VERSION) " and up");
67#undef STR
68#undef XSTR
69
70namespace v8::internal {
71
72namespace {
73
74inline constexpr uint8_t AsOneByte(uint16_t ch) {
76 return static_cast<uint8_t>(ch);
77}
78
79constexpr uint8_t kToLower[256] = {
80 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
81 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
82 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23,
83 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
84 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
85 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
86 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73,
87 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
88 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B,
89 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
90 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83,
91 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
92 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B,
93 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
94 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3,
95 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
96 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB,
97 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xD7,
98 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3,
99 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
100 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB,
101 0xFC, 0xFD, 0xFE, 0xFF,
102};
103
104inline constexpr uint8_t ToLatin1Lower(uint8_t ch) {
105 static_assert(std::numeric_limits<decltype(ch)>::max() < arraysize(kToLower));
106 return kToLower[ch];
107}
108// Ensure callers explicitly truncate uint16_t.
109inline constexpr uint8_t ToLatin1Lower(uint16_t ch) = delete;
110
111// Does not work for U+00DF (sharp-s), U+00B5 (micron), U+00FF, or two-byte
112// values.
113inline constexpr uint8_t ToLatin1Upper(uint8_t ch) {
114 DCHECK(ch != 0xDF && ch != 0xB5 && ch != 0xFF);
115 return ch &
116 ~((IsAsciiLower(ch) || (((ch & 0xE0) == 0xE0) && ch != 0xF7)) << 5);
117}
118// Ensure callers explicitly truncate uint16_t.
119inline constexpr uint8_t ToLatin1Upper(uint16_t ch) = delete;
120
121bool ToUpperFastASCII(base::Vector<const uint16_t> src,
122 DirectHandle<SeqOneByteString> result) {
123 // Do a faster loop for the case where all the characters are ASCII.
124 uint16_t ored = 0;
125 int32_t index = 0;
126 for (const uint16_t* it = src.begin(); it != src.end(); ++it) {
127 uint16_t ch = *it;
128 ored |= ch;
129 result->SeqOneByteStringSet(index++, ToAsciiUpper(ch));
130 }
131 return !(ored & ~0x7F);
132}
133
134const uint16_t sharp_s = 0xDF;
135
136template <typename Char>
137bool ToUpperOneByte(base::Vector<const Char> src, uint8_t* dest,
138 int* sharp_s_count) {
139 // Still pretty-fast path for the input with non-ASCII Latin-1 characters.
140
141 // There are two special cases.
142 // 1. U+00B5 and U+00FF are mapped to a character beyond U+00FF.
143 // 2. Lower case sharp-S converts to "SS" (two characters)
144 *sharp_s_count = 0;
145 for (auto it = src.begin(); it != src.end(); ++it) {
146 uint8_t ch = AsOneByte(*it);
147 if (V8_UNLIKELY(ch == sharp_s)) {
148 ++(*sharp_s_count);
149 continue;
150 }
151 if (V8_UNLIKELY(ch == 0xB5 || ch == 0xFF)) {
152 // Since this upper-cased character does not fit in an 8-bit string, we
153 // need to take the 16-bit path.
154 return false;
155 }
156 *dest++ = ToLatin1Upper(ch);
157 }
158
159 return true;
160}
161
162template <typename Char>
163void ToUpperWithSharpS(base::Vector<const Char> src,
164 DirectHandle<SeqOneByteString> result) {
165 int32_t dest_index = 0;
166 for (auto it = src.begin(); it != src.end(); ++it) {
167 uint8_t ch = AsOneByte(*it);
168 if (ch == sharp_s) {
169 result->SeqOneByteStringSet(dest_index++, 'S');
170 result->SeqOneByteStringSet(dest_index++, 'S');
171 } else {
172 result->SeqOneByteStringSet(dest_index++, ToLatin1Upper(ch));
173 }
174 }
175}
176
177inline int FindFirstUpperOrNonAscii(Tagged<String> s, int length) {
178 for (int index = 0; index < length; ++index) {
179 uint16_t ch = s->Get(index);
180 if (V8_UNLIKELY(IsAsciiUpper(ch) || ch & ~0x7F)) {
181 return index;
182 }
183 }
184 return length;
185}
186
187const UChar* GetUCharBufferFromFlat(const String::FlatContent& flat,
188 std::unique_ptr<base::uc16[]>* dest,
189 int32_t length) {
190 DCHECK(flat.IsFlat());
191 if (flat.IsOneByte()) {
192 if (!*dest) {
193 dest->reset(NewArray<base::uc16>(length));
194 CopyChars(dest->get(), flat.ToOneByteVector().begin(), length);
195 }
196 return reinterpret_cast<const UChar*>(dest->get());
197 } else {
198 return reinterpret_cast<const UChar*>(flat.ToUC16Vector().begin());
199 }
200}
201
202template <typename T>
203MaybeDirectHandle<T> New(Isolate* isolate, DirectHandle<JSFunction> constructor,
204 DirectHandle<Object> locales,
205 DirectHandle<Object> options,
206 const char* method_name) {
207 DirectHandle<Map> map;
209 isolate, map,
210 JSFunction::GetDerivedMap(isolate, constructor, constructor));
211 return T::New(isolate, map, locales, options, method_name);
212}
213} // namespace
214
215const uint8_t* Intl::ToLatin1LowerTable() { return &kToLower[0]; }
216
217icu::UnicodeString Intl::ToICUUnicodeString(Isolate* isolate,
219 int offset) {
220 DCHECK(string->IsFlat());
222 std::unique_ptr<base::uc16[]> sap;
223 // Short one-byte strings can be expanded on the stack to avoid allocating a
224 // temporary buffer.
225 constexpr unsigned int kShortStringSize = 80;
226 UChar short_string_buffer[kShortStringSize];
227 const UChar* uchar_buffer = nullptr;
228 const String::FlatContent& flat = string->GetFlatContent(no_gc);
229 // We read the length from the heap, so it may be untrusted (in the sandbox
230 // attacker model) and we therefore need to use an unsigned int here when
231 // comparing it against the kShortStringSize.
232 uint32_t length = string->length();
233 DCHECK_LE(offset, length);
234 if (flat.IsOneByte() && length <= kShortStringSize) {
235 CopyChars(short_string_buffer, flat.ToOneByteVector().begin(), length);
236 uchar_buffer = short_string_buffer;
237 } else {
238 uchar_buffer = GetUCharBufferFromFlat(flat, &sap, length);
239 }
240 return icu::UnicodeString(uchar_buffer + offset, length - offset);
241}
242
243namespace {
244
245icu::StringPiece ToICUStringPiece(Isolate* isolate, DirectHandle<String> string,
246 int offset = 0) {
247 DCHECK(string->IsFlat());
249
250 const String::FlatContent& flat = string->GetFlatContent(no_gc);
251 if (!flat.IsOneByte()) return icu::StringPiece();
252
253 int32_t length = string->length();
254 const char* char_buffer =
255 reinterpret_cast<const char*>(flat.ToOneByteVector().begin());
256 if (!String::IsAscii(char_buffer, length)) {
257 return icu::StringPiece();
258 }
259
260 return icu::StringPiece(char_buffer + offset, length - offset);
261}
262
263MaybeHandle<String> LocaleConvertCase(Isolate* isolate, DirectHandle<String> s,
264 bool is_to_upper, const char* lang) {
265 auto case_converter = is_to_upper ? u_strToUpper : u_strToLower;
266 uint32_t src_length = s->length();
267 uint32_t dest_length = src_length;
268 UErrorCode status;
270 std::unique_ptr<base::uc16[]> sap;
271
272 if (dest_length == 0) return isolate->factory()->empty_string();
273
274 // This is not a real loop. It'll be executed only once (no overflow) or
275 // twice (overflow).
276 for (int i = 0; i < 2; ++i) {
277 // Case conversion can increase the string length (e.g. sharp-S => SS) so
278 // that we have to handle RangeError exceptions here.
280 isolate, result, isolate->factory()->NewRawTwoByteString(dest_length));
282 DCHECK(s->IsFlat());
283 String::FlatContent flat = s->GetFlatContent(no_gc);
284 const UChar* src = GetUCharBufferFromFlat(flat, &sap, src_length);
285 status = U_ZERO_ERROR;
286 dest_length =
287 case_converter(reinterpret_cast<UChar*>(result->GetChars(no_gc)),
288 dest_length, src, src_length, lang, &status);
289 if (status != U_BUFFER_OVERFLOW_ERROR) break;
290 }
291
292 // In most cases, the output will fill the destination buffer completely
293 // leading to an unterminated string (U_STRING_NOT_TERMINATED_WARNING).
294 // Only in rare cases, it'll be shorter than the destination buffer and
295 // |result| has to be truncated.
296 DCHECK(U_SUCCESS(status));
297 if (V8_LIKELY(status == U_STRING_NOT_TERMINATED_WARNING)) {
298 DCHECK(dest_length == result->length());
299 return result;
300 }
301 DCHECK(dest_length < result->length());
302 return SeqString::Truncate(isolate, result, dest_length);
303}
304
305} // namespace
306
307// A stripped-down version of ConvertToLower that can only handle flat one-byte
308// strings and does not allocate. Note that {src} could still be, e.g., a
309// one-byte sliced string with a two-byte parent string.
310// Called from TF builtins.
312 Tagged<String> dst) {
313 DCHECK_EQ(src->length(), dst->length());
314 DCHECK(src->IsOneByteRepresentation());
315 DCHECK(src->IsFlat());
316 DCHECK(IsSeqOneByteString(dst));
317
319
320 const int length = src->length();
321 String::FlatContent src_flat = src->GetFlatContent(no_gc);
322 uint8_t* dst_data = Cast<SeqOneByteString>(dst)->GetChars(no_gc);
323
324 if (src_flat.IsOneByte()) {
325 const uint8_t* src_data = src_flat.ToOneByteVector().begin();
326
327 int ascii_prefix_length = FastAsciiConvert<unibrow::ToLowercase>(
328 reinterpret_cast<char*>(dst_data),
329 reinterpret_cast<const char*>(src_data), length);
330
331 if (ascii_prefix_length == length) return dst;
332
333 // If not ASCII, we keep the result up to index_to_first_unprocessed and
334 // process the rest.
335 for (int index = ascii_prefix_length; index < length; ++index) {
336 dst_data[index] = ToLatin1Lower(src_data[index]);
337 }
338 } else {
339 DCHECK(src_flat.IsTwoByte());
340 int index_to_first_unprocessed = FindFirstUpperOrNonAscii(src, length);
341 if (index_to_first_unprocessed == length) return src;
342
343 const uint16_t* src_data = src_flat.ToUC16Vector().begin();
344 CopyChars(dst_data, src_data, index_to_first_unprocessed);
345 for (int index = index_to_first_unprocessed; index < length; ++index) {
346 // Truncating cast of two-byte src character to one-byte value. For valid
347 // cases (where a one-byte sliced string points to a two-byte parent) this
348 // will not lose any information, but we need to truncate anyway to
349 // avoid undefined behavior if the parent string is corrupted.
350 dst_data[index] = ToLatin1Lower(AsOneByte(src_data[index]));
351 }
352 }
353
354 return dst;
355}
356
359 if (!s->IsOneByteRepresentation()) {
360 // Use a slower implementation for strings with characters beyond U+00FF.
361 return LocaleConvertCase(isolate, s, false, "");
362 }
363
364 int length = s->length();
365
366 // We depend here on the invariant that the length of a Latin1
367 // string is invariant under ToLowerCase, and the result always
368 // fits in the Latin1 range in the *root locale*. It does not hold
369 // for ToUpperCase even in the root locale.
370
371 // Scan the string for uppercase and non-ASCII characters for strings
372 // shorter than a machine-word without any memory allocation overhead.
373 // TODO(jshin): Apply this to a longer input by breaking FastAsciiConvert()
374 // to two parts, one for scanning the prefix with no change and the other for
375 // handling ASCII-only characters.
376
377 bool is_short = length < static_cast<int>(sizeof(uintptr_t));
378 if (is_short) {
379 bool is_lower_ascii = FindFirstUpperOrNonAscii(*s, length) == length;
380 if (is_lower_ascii) return indirect_handle(s, isolate);
381 }
382
384 isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
385
386 return handle(Intl::ConvertOneByteToLower(*s, *result), isolate);
387}
388
391 uint32_t length = s->length();
392 if (s->IsOneByteRepresentation() && length > 0) {
393 uint32_t prefix;
394 {
396 String::FlatContent flat = s->GetFlatContent(no_gc);
398 reinterpret_cast<const char*>(flat.ToOneByteVector().begin()),
399 length);
400 if (prefix == length) {
401 return indirect_handle(s, isolate);
402 }
403 }
405 isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
406
407 DCHECK(s->IsFlat());
408 int sharp_s_count;
409 bool is_result_single_byte;
410 {
412 String::FlatContent flat = s->GetFlatContent(no_gc);
413 uint8_t* dest = result->GetChars(no_gc);
414 if (flat.IsOneByte()) {
416 std::memcpy(result->GetChars(no_gc), src.begin(), prefix);
417 uint32_t ascii_prefix_length =
419 reinterpret_cast<char*>(result->GetChars(no_gc)) + prefix,
420 reinterpret_cast<const char*>(src.begin()) + prefix,
421 length - prefix) +
422 prefix;
423 if (ascii_prefix_length == length) return result;
424 // If not ASCII, we keep the result up to index_to_first_unprocessed and
425 // process the rest.
426 is_result_single_byte =
427 ToUpperOneByte(src.SubVector(ascii_prefix_length, length),
428 dest + ascii_prefix_length, &sharp_s_count);
429 } else {
430 DCHECK(flat.IsTwoByte());
432 if (ToUpperFastASCII(src, result)) return result;
433 is_result_single_byte = ToUpperOneByte(src, dest, &sharp_s_count);
434 }
435 }
436
437 // Go to the full Unicode path if there are characters whose uppercase
438 // is beyond the Latin-1 range (cannot be represented in OneByteString).
439 if (V8_UNLIKELY(!is_result_single_byte)) {
440 return LocaleConvertCase(isolate, s, true, "");
441 }
442
443 if (sharp_s_count == 0) return result;
444
445 // We have sharp_s_count sharp-s characters, but the result is still
446 // in the Latin-1 range.
448 isolate, result,
449 isolate->factory()->NewRawOneByteString(length + sharp_s_count));
451 String::FlatContent flat = s->GetFlatContent(no_gc);
452 if (flat.IsOneByte()) {
453 ToUpperWithSharpS(flat.ToOneByteVector(), result);
454 } else {
455 ToUpperWithSharpS(flat.ToUC16Vector(), result);
456 }
457
458 return result;
459 }
460
461 return LocaleConvertCase(isolate, s, true, "");
462}
463
464std::string Intl::GetNumberingSystem(const icu::Locale& icu_locale) {
465 // Ugly hack. ICU doesn't expose numbering system in any way, so we have
466 // to assume that for given locale NumberingSystem constructor produces the
467 // same digits as NumberFormat/Calendar would.
468 UErrorCode status = U_ZERO_ERROR;
469 std::unique_ptr<icu::NumberingSystem> numbering_system(
470 icu::NumberingSystem::createInstance(icu_locale, status));
471 if (U_SUCCESS(status) && !numbering_system->isAlgorithmic()) {
472 return numbering_system->getName();
473 }
474 return "latn";
475}
476
477namespace {
478
479Maybe<icu::Locale> CreateICULocale(const std::string& bcp47_locale) {
481
482 // Convert BCP47 into ICU locale format.
483 UErrorCode status = U_ZERO_ERROR;
484
485 icu::Locale icu_locale = icu::Locale::forLanguageTag(bcp47_locale, status);
486 if (U_FAILURE(status) || icu_locale.isBogus()) {
487 return Nothing<icu::Locale>();
488 }
489
490 return Just(icu_locale);
491}
492
493} // anonymous namespace
494
495// static
496
498 const icu::UnicodeString& string) {
499 return isolate->factory()->NewStringFromTwoByte(base::Vector<const uint16_t>(
500 reinterpret_cast<const uint16_t*>(string.getBuffer()), string.length()));
501}
502
504 const icu::UnicodeString& string,
505 int32_t begin, int32_t end) {
506 return Intl::ToString(isolate, string.tempSubStringBetween(begin, end));
507}
508
509namespace {
510
511Handle<JSObject> InnerAddElement(Isolate* isolate, DirectHandle<JSArray> array,
512 int index,
513 DirectHandle<String> field_type_string,
514 DirectHandle<String> value) {
515 // let element = $array[$index] = {
516 // type: $field_type_string,
517 // value: $value
518 // }
519 // return element;
520 Factory* factory = isolate->factory();
521 Handle<JSObject> element = factory->NewJSObject(isolate->object_function());
522 JSObject::AddProperty(isolate, element, factory->type_string(),
523 field_type_string, NONE);
524
525 JSObject::AddProperty(isolate, element, factory->value_string(), value, NONE);
526 // TODO(victorgomes): Temporarily forcing a fatal error here in case of
527 // overflow, until Intl::AddElement can handle exceptions.
528 if (JSObject::AddDataElement(array, index, element, NONE).IsNothing()) {
529 FATAL("Fatal JavaScript invalid size error when adding element");
530 UNREACHABLE();
531 }
532 return element;
533}
534
535} // namespace
536
537void Intl::AddElement(Isolate* isolate, DirectHandle<JSArray> array, int index,
538 DirectHandle<String> field_type_string,
539 DirectHandle<String> value) {
540 // Same as $array[$index] = {type: $field_type_string, value: $value};
541 InnerAddElement(isolate, array, index, field_type_string, value);
542}
543
544void Intl::AddElement(Isolate* isolate, DirectHandle<JSArray> array, int index,
545 DirectHandle<String> field_type_string,
547 DirectHandle<String> additional_property_name,
548 DirectHandle<String> additional_property_value) {
549 // Same as $array[$index] = {
550 // type: $field_type_string, value: $value,
551 // $additional_property_name: $additional_property_value
552 // }
553 DirectHandle<JSObject> element =
554 InnerAddElement(isolate, array, index, field_type_string, value);
555 JSObject::AddProperty(isolate, element, additional_property_name,
556 additional_property_value, NONE);
557}
558
559namespace {
560
561// Build the shortened locale; eg, convert xx_Yyyy_ZZ to xx_ZZ.
562//
563// If locale has a script tag then return true and the locale without the
564// script else return false and an empty string.
565bool RemoveLocaleScriptTag(const std::string& icu_locale,
566 std::string* locale_less_script) {
567 icu::Locale new_locale = icu::Locale::createCanonical(icu_locale.c_str());
568 const char* icu_script = new_locale.getScript();
569 if (icu_script == nullptr || strlen(icu_script) == 0) {
570 *locale_less_script = std::string();
571 return false;
572 }
573
574 const char* icu_language = new_locale.getLanguage();
575 const char* icu_country = new_locale.getCountry();
576 icu::Locale short_locale = icu::Locale(icu_language, icu_country);
577 *locale_less_script = short_locale.getName();
578 return true;
579}
580
581bool ValidateResource(const icu::Locale locale, const char* path,
582 const char* key) {
583 bool result = false;
584 UErrorCode status = U_ZERO_ERROR;
585 UResourceBundle* bundle = ures_open(path, locale.getName(), &status);
586 if (bundle != nullptr && status == U_ZERO_ERROR) {
587 if (key == nullptr) {
588 result = true;
589 } else {
590 UResourceBundle* key_bundle =
591 ures_getByKey(bundle, key, nullptr, &status);
592 result = key_bundle != nullptr && (status == U_ZERO_ERROR);
593 ures_close(key_bundle);
594 }
595 }
596 ures_close(bundle);
597 if (!result) {
598 if ((locale.getCountry()[0] != '\0') && (locale.getScript()[0] != '\0')) {
599 // Fallback to try without country.
600 std::string without_country(locale.getLanguage());
601 without_country = without_country.append("-").append(locale.getScript());
602 return ValidateResource(without_country.c_str(), path, key);
603 } else if ((locale.getCountry()[0] != '\0') ||
604 (locale.getScript()[0] != '\0')) {
605 // Fallback to try with only language.
606 std::string language(locale.getLanguage());
607 return ValidateResource(language.c_str(), path, key);
608 }
609 }
610 return result;
611}
612
613} // namespace
614
615std::set<std::string> Intl::BuildLocaleSet(
616 const std::vector<std::string>& icu_available_locales, const char* path,
617 const char* validate_key) {
618 std::set<std::string> locales;
619 for (const std::string& locale : icu_available_locales) {
620 if (path != nullptr || validate_key != nullptr) {
621 if (!ValidateResource(icu::Locale(locale.c_str()), path, validate_key)) {
622 // FIXME(chromium:1215606) Find a better fix for nb->no fallback
623 if (locale != "nb") {
624 continue;
625 }
626 // Try no for nb
627 if (!ValidateResource(icu::Locale("no"), path, validate_key)) {
628 continue;
629 }
630 }
631 }
632 locales.insert(locale);
633 std::string shortened_locale;
634 if (RemoveLocaleScriptTag(locale, &shortened_locale)) {
635 std::replace(shortened_locale.begin(), shortened_locale.end(), '_', '-');
636 locales.insert(shortened_locale);
637 }
638 }
639 return locales;
640}
641
642Maybe<std::string> Intl::ToLanguageTag(const icu::Locale& locale) {
643 UErrorCode status = U_ZERO_ERROR;
644 std::string res = locale.toLanguageTag<std::string>(status);
645 if (U_FAILURE(status)) {
646 return Nothing<std::string>();
647 }
648 DCHECK(U_SUCCESS(status));
649 return Just(res);
650}
651
652// See ecma402/#legacy-constructor.
655 DirectHandle<JSFunction> constructor, bool has_initialized_slot) {
656 DirectHandle<Object> obj_ordinary_has_instance;
658 isolate, obj_ordinary_has_instance,
659 Object::OrdinaryHasInstance(isolate, constructor, receiver));
660 bool ordinary_has_instance =
661 Object::BooleanValue(*obj_ordinary_has_instance, isolate);
662
663 // 2. If receiver does not have an [[Initialized...]] internal slot
664 // and ? OrdinaryHasInstance(constructor, receiver) is true, then
665 if (!has_initialized_slot && ordinary_has_instance) {
666 // 2. a. Let new_receiver be ? Get(receiver, %Intl%.[[FallbackSymbol]]).
667 DirectHandle<Object> new_receiver;
669 isolate, new_receiver,
671 isolate->factory()->intl_fallback_symbol()));
672 return new_receiver;
673 }
674
675 return receiver;
676}
677
678namespace {
679
680bool IsTwoLetterLanguage(const std::string& locale) {
681 // Two letters, both in range 'a'-'z'...
682 return locale.length() == 2 && IsAsciiLower(locale[0]) &&
683 IsAsciiLower(locale[1]);
684}
685
686bool IsDeprecatedOrLegacyLanguage(const std::string& locale) {
687 // Check if locale is one of the deprecated language tags:
688 return locale == "in" || locale == "iw" || locale == "ji" || locale == "jw" ||
689 locale == "mo" ||
690 // Check if locale is one of the legacy language tags:
691 locale == "sh" || locale == "tl" || locale == "no";
692}
693
694bool IsStructurallyValidLanguageTag(const std::string& tag) {
696}
697
698// Canonicalize the locale.
699// https://tc39.github.io/ecma402/#sec-canonicalizelanguagetag,
700// including type check and structural validity check.
701Maybe<std::string> CanonicalizeLanguageTag(Isolate* isolate,
702 const std::string& locale_in) {
703 std::string locale = locale_in;
704
705 if (locale.empty() ||
706 !String::IsAscii(locale.data(), static_cast<int>(locale.length()))) {
708 isolate,
709 NewRangeError(
710 MessageTemplate::kInvalidLanguageTag,
711 isolate->factory()->NewStringFromAsciiChecked(locale.c_str())),
713 }
714
715 // Optimize for the most common case: a 2-letter language code in the
716 // canonical form/lowercase that is not one of the deprecated codes
717 // (in, iw, ji, jw). Don't check for ~70 of 3-letter deprecated language
718 // codes. Instead, let them be handled by ICU in the slow path. However,
719 // fast-track 'fil' (3-letter canonical code).
720 if ((IsTwoLetterLanguage(locale) && !IsDeprecatedOrLegacyLanguage(locale)) ||
721 locale == "fil") {
722 return Just(locale);
723 }
724
725 // Because per BCP 47 2.1.1 language tags are case-insensitive, lowercase
726 // the input before any more check.
727 std::transform(locale.begin(), locale.end(), locale.begin(), ToAsciiLower);
728
729 // // ECMA 402 6.2.3
730 // TODO(jshin): uloc_{for,to}LanguageTag can fail even for a structurally
731 // valid language tag if it's too long (much longer than 100 chars). Even if
732 // we allocate a longer buffer, ICU will still fail if it's too long. Either
733 // propose to Ecma 402 to put a limit on the locale length or change ICU to
734 // handle long locale names better. See
735 // https://unicode-org.atlassian.net/browse/ICU-13417
736 UErrorCode error = U_ZERO_ERROR;
737 // uloc_forLanguageTag checks the structural validity. If the input BCP47
738 // language tag is parsed all the way to the end, it indicates that the input
739 // is structurally valid. Due to a couple of bugs, we can't use it
740 // without Chromium patches or ICU 62 or earlier.
741 icu::Locale icu_locale = icu::Locale::forLanguageTag(locale.c_str(), error);
742
743 if (U_FAILURE(error) || icu_locale.isBogus()) {
745 isolate,
746 NewRangeError(
747 MessageTemplate::kInvalidLanguageTag,
748 isolate->factory()->NewStringFromAsciiChecked(locale.c_str())),
750 }
751
752 // Use LocaleBuilder to validate locale.
753 icu_locale = icu::LocaleBuilder().setLocale(icu_locale).build(error);
754 icu_locale.canonicalize(error);
755 if (U_FAILURE(error) || icu_locale.isBogus()) {
757 isolate,
758 NewRangeError(
759 MessageTemplate::kInvalidLanguageTag,
760 isolate->factory()->NewStringFromAsciiChecked(locale.c_str())),
762 }
763 Maybe<std::string> maybe_to_language_tag = Intl::ToLanguageTag(icu_locale);
764 if (maybe_to_language_tag.IsNothing()) {
766 isolate,
767 NewRangeError(
768 MessageTemplate::kInvalidLanguageTag,
769 isolate->factory()->NewStringFromAsciiChecked(locale.c_str())),
771 }
772
773 return maybe_to_language_tag;
774}
775
776Maybe<std::string> CanonicalizeLanguageTag(Isolate* isolate,
777 DirectHandle<Object> locale_in) {
778 DirectHandle<String> locale_str;
779 // This does part of the validity checking spec'ed in CanonicalizeLocaleList:
780 // 7c ii. If Type(kValue) is not String or Object, throw a TypeError
781 // exception.
782 // 7c iii. Let tag be ? ToString(kValue).
783 // 7c iv. If IsStructurallyValidLanguageTag(tag) is false, throw a
784 // RangeError exception.
785
786 if (IsString(*locale_in)) {
787 locale_str = Cast<String>(locale_in);
788 } else if (IsJSReceiver(*locale_in)) {
789 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, locale_str,
790 Object::ToString(isolate, locale_in),
792 } else {
794 NewTypeError(MessageTemplate::kLanguageID),
796 }
797 std::string locale(locale_str->ToCString().get());
798
799 if (!IsStructurallyValidLanguageTag(locale)) {
801 isolate, NewRangeError(MessageTemplate::kLocaleBadParameters),
803 }
804 return CanonicalizeLanguageTag(isolate, locale);
805}
806
807} // anonymous namespace
808
810 Isolate* isolate, DirectHandle<Object> locales,
811 bool only_return_one_result) {
812 // 1. If locales is undefined, then
813 if (IsUndefined(*locales, isolate)) {
814 // 1a. Return a new empty List.
815 return Just(std::vector<std::string>());
816 }
817 // 2. Let seen be a new empty List.
818 std::vector<std::string> seen;
819 // 3. If Type(locales) is String or locales has an [[InitializedLocale]]
820 // internal slot, then
821 if (IsJSLocale(*locales)) {
822 // Since this value came from JSLocale, which is already went though the
823 // CanonializeLanguageTag process once, therefore there are no need to
824 // call CanonializeLanguageTag again.
825 seen.push_back(JSLocale::ToString(Cast<JSLocale>(locales)));
826 return Just(seen);
827 }
828 if (IsString(*locales)) {
829 // 3a. Let O be CreateArrayFromList(« locales »).
830 // Instead of creating a one-element array and then iterating over it,
831 // we inline the body of the iteration:
832 std::string canonicalized_tag;
833 if (!CanonicalizeLanguageTag(isolate, locales).To(&canonicalized_tag)) {
835 }
836 seen.push_back(canonicalized_tag);
837 return Just(seen);
838 }
839 // 4. Else,
840 // 4a. Let O be ? ToObject(locales).
843 Object::ToObject(isolate, locales),
844 Nothing<std::vector<std::string>>());
845 // 5. Let len be ? ToLength(? Get(O, "length")).
846 DirectHandle<Object> length_obj;
847 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, length_obj,
849 Nothing<std::vector<std::string>>());
850 // TODO(jkummerow): Spec violation: strictly speaking, we have to iterate
851 // up to 2^53-1 if {length_obj} says so. Since cases above 2^32 probably
852 // don't happen in practice (and would be very slow if they do), we'll keep
853 // the code simple for now by using a saturating to-uint32 conversion.
854 double raw_length = Object::NumberValue(*length_obj);
855 uint32_t len =
856 raw_length >= kMaxUInt32 ? kMaxUInt32 : static_cast<uint32_t>(raw_length);
857 // 6. Let k be 0.
858 // 7. Repeat, while k < len
859 for (uint32_t k = 0; k < len; k++) {
860 // 7a. Let Pk be ToString(k).
861 // 7b. Let kPresent be ? HasProperty(O, Pk).
862 LookupIterator it(isolate, o, k);
863 Maybe<bool> maybe_found = JSReceiver::HasProperty(&it);
864 MAYBE_RETURN(maybe_found, Nothing<std::vector<std::string>>());
865 // 7c. If kPresent is true, then
866 if (!maybe_found.FromJust()) continue;
867 // 7c i. Let kValue be ? Get(O, Pk).
868 DirectHandle<Object> k_value;
870 Nothing<std::vector<std::string>>());
871 // 7c ii. If Type(kValue) is not String or Object, throw a TypeError
872 // exception.
873 // 7c iii. If Type(kValue) is Object and kValue has an [[InitializedLocale]]
874 // internal slot, then
875 std::string canonicalized_tag;
876 if (IsJSLocale(*k_value)) {
877 // 7c iii. 1. Let tag be kValue.[[Locale]].
878 canonicalized_tag = JSLocale::ToString(Cast<JSLocale>(k_value));
879 // 7c iv. Else,
880 } else {
881 // 7c iv 1. Let tag be ? ToString(kValue).
882 // 7c v. If IsStructurallyValidLanguageTag(tag) is false, throw a
883 // RangeError exception.
884 // 7c vi. Let canonicalizedTag be CanonicalizeLanguageTag(tag).
885 if (!CanonicalizeLanguageTag(isolate, k_value).To(&canonicalized_tag)) {
887 }
888 }
889 // 7c vi. If canonicalizedTag is not an element of seen, append
890 // canonicalizedTag as the last element of seen.
891 if (std::find(seen.begin(), seen.end(), canonicalized_tag) == seen.end()) {
892 seen.push_back(canonicalized_tag);
893 }
894 // 7d. Increase k by 1. (See loop header.)
895 // Optimization: some callers only need one result.
896 if (only_return_one_result) return Just(seen);
897 }
898 // 8. Return seen.
899 return Just(seen);
900}
901
902// ecma402 #sup-string.prototype.tolocalelowercase
903// ecma402 #sup-string.prototype.tolocaleuppercase
905 Isolate* isolate, DirectHandle<String> s, bool to_upper,
906 DirectHandle<Object> locales) {
907 std::vector<std::string> requested_locales;
908 if (!CanonicalizeLocaleList(isolate, locales, true).To(&requested_locales))
909 return {};
910 std::string requested_locale = requested_locales.empty()
911 ? isolate->DefaultLocale()
912 : requested_locales[0];
913 size_t dash = requested_locale.find('-');
914 if (dash != std::string::npos) {
915 requested_locale = requested_locale.substr(0, dash);
916 }
917
918 // Primary language tag can be up to 8 characters long in theory.
919 // https://tools.ietf.org/html/bcp47#section-2.2.1
920 DCHECK_LE(requested_locale.length(), 8);
921 s = String::Flatten(isolate, s);
922
923 // All the languages requiring special-handling have two-letter codes.
924 // Note that we have to check for '!= 2' here because private-use language
925 // tags (x-foo) or grandfathered irregular tags (e.g. i-enochian) would have
926 // only 'x' or 'i' when they get here.
927 if (V8_UNLIKELY(requested_locale.length() != 2)) {
928 if (to_upper) {
929 return ConvertToUpper(isolate, s);
930 }
931 return ConvertToLower(isolate, s);
932 }
933 // TODO(jshin): Consider adding a fast path for ASCII or Latin-1. The fastpath
934 // in the root locale needs to be adjusted for az, lt and tr because even case
935 // mapping of ASCII range characters are different in those locales.
936 // Greek (el) does not require any adjustment.
937 if (V8_UNLIKELY((requested_locale == "tr") || (requested_locale == "el") ||
938 (requested_locale == "lt") || (requested_locale == "az"))) {
939 return LocaleConvertCase(isolate, s, to_upper, requested_locale.c_str());
940 } else {
941 if (to_upper) {
942 return ConvertToUpper(isolate, s);
943 }
944 return ConvertToLower(isolate, s);
945 }
946}
947
948// static
949template <class IsolateT>
951 IsolateT* isolate, DirectHandle<Object> locales,
952 DirectHandle<Object> options) {
953 if (!IsUndefined(*options, isolate)) {
955 }
956
957 // Lists all of the available locales that are statically known to fulfill
958 // fast path conditions. See the StringLocaleCompareFastPath test as a
959 // starting point to update this list.
960 //
961 // Locale entries are roughly sorted s.t. common locales come first.
962 //
963 // The actual conditions are verified in debug builds in
964 // CollatorAllowsFastComparison.
965 static const char* const kFastLocales[] = {
966 "en-US", "en", "fr", "es", "de", "pt", "it", "ca",
967 "de-AT", "fi", "id", "id-ID", "ms", "nl", "pl", "ro",
968 "sl", "sv", "sw", "vi", "en-DE", "en-GB",
969 };
970
971 if (IsUndefined(*locales, isolate)) {
972 const std::string& default_locale = isolate->DefaultLocale();
973 for (const char* fast_locale : kFastLocales) {
974 if (strcmp(fast_locale, default_locale.c_str()) == 0) {
976 }
977 }
978
980 }
981
982 if (!IsString(*locales)) return CompareStringsOptions::kNone;
983
984 auto locales_string = Cast<String>(locales);
985 for (const char* fast_locale : kFastLocales) {
986 if (locales_string->IsEqualTo(base::CStrVector(fast_locale), isolate)) {
988 }
989 }
990
992}
993
994// Instantiations.
999
1000std::optional<int> Intl::StringLocaleCompare(Isolate* isolate,
1001 DirectHandle<String> string1,
1002 DirectHandle<String> string2,
1003 DirectHandle<Object> locales,
1004 DirectHandle<Object> options,
1005 const char* method_name) {
1006 // We only cache the instance when locales is a string/undefined and
1007 // options is undefined, as that is the only case when the specified
1008 // side-effects of examining those arguments are unobservable.
1009 const bool can_cache =
1010 (IsString(*locales) || IsUndefined(*locales, isolate)) &&
1011 IsUndefined(*options, isolate);
1012 // We may be able to take the fast path, depending on the `locales` and
1013 // `options` arguments.
1014 const CompareStringsOptions compare_strings_options =
1015 CompareStringsOptionsFor(isolate, locales, options);
1016 if (can_cache) {
1017 // Both locales and options are undefined, check the cache.
1018 icu::Collator* cached_icu_collator =
1019 static_cast<icu::Collator*>(isolate->get_cached_icu_object(
1020 Isolate::ICUObjectCacheType::kDefaultCollator, locales));
1021 // We may use the cached icu::Collator for a fast path.
1022 if (cached_icu_collator != nullptr) {
1023 return Intl::CompareStrings(isolate, *cached_icu_collator, string1,
1024 string2, compare_strings_options);
1025 }
1026 }
1027
1028 DirectHandle<JSFunction> constructor(
1030 isolate->context()->native_context()->intl_collator_function()),
1031 isolate);
1032
1033 DirectHandle<JSCollator> collator;
1034 MaybeDirectHandle<JSCollator> maybe_collator =
1035 New<JSCollator>(isolate, constructor, locales, options, method_name);
1036 if (!maybe_collator.ToHandle(&collator)) return {};
1037 if (can_cache) {
1038 isolate->set_icu_object_in_cache(
1039 Isolate::ICUObjectCacheType::kDefaultCollator, locales,
1040 std::static_pointer_cast<icu::UMemory>(
1041 collator->icu_collator()->get()));
1042 }
1043 icu::Collator* icu_collator = collator->icu_collator()->raw();
1044 return Intl::CompareStrings(isolate, *icu_collator, string1, string2,
1045 compare_strings_options);
1046}
1047
1048namespace {
1049
1050// Weights for the Unicode Collation Algorithm for charcodes [0x00,0x7F].
1051// https://unicode.org/reports/tr10/.
1052//
1053// Generated from:
1054//
1055// $ wget http://www.unicode.org/Public/UCA/latest/allkeys.txt
1056// $ cat ~/allkeys.txt | grep '^00[0-7]. ;' | sort | sed 's/[*.]/ /g' |\
1057// sed 's/.*\[ \‍(.*\‍)\].*/\1/' | python ~/gen_weights.py
1058//
1059// Where gen_weights.py does an ordinal rank s.t. weights fit in a uint8_t:
1060//
1061// import sys
1062//
1063// def to_ordinal(ws):
1064// weight_map = {}
1065// weights_uniq_sorted = sorted(set(ws))
1066// for i in range(0, len(weights_uniq_sorted)):
1067// weight_map[weights_uniq_sorted[i]] = i
1068// return [weight_map[x] for x in ws]
1069//
1070// def print_weight_list(array_name, ws):
1071// print("constexpr uint8_t %s[256] = {" % array_name, end = "")
1072// i = 0
1073// for w in ws:
1074// if (i % 16) == 0:
1075// print("\n ", end = "")
1076// print("%3d," % w, end = "")
1077// i += 1
1078// print("\n};\n")
1079//
1080// if __name__ == "__main__":
1081// l1s = []
1082// l3s = []
1083// for line in sys.stdin:
1084// weights = line.split()
1085// l1s.append(int(weights[0], 16))
1086// l3s.append(int(weights[2], 16))
1087// print_weight_list("kCollationWeightsL1", to_ordinal(l1s))
1088// print_weight_list("kCollationWeightsL3", to_ordinal(l3s))
1089
1090// clang-format off
1091constexpr uint8_t kCollationWeightsL1[256] = {
1092 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 0, 0,
1093 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1094 6, 12, 16, 28, 38, 29, 27, 15, 17, 18, 24, 32, 9, 8, 14, 25,
1095 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 11, 10, 33, 34, 35, 13,
1096 23, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
1097 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 19, 26, 20, 31, 7,
1098 30, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
1099 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 21, 36, 22, 37, 0,
1100};
1101constexpr uint8_t kCollationWeightsL3[256] = {
1102 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
1103 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1104 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1105 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1106 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1107 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1,
1108 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1109 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1110};
1111constexpr int kCollationWeightsLength = arraysize(kCollationWeightsL1);
1112static_assert(kCollationWeightsLength == arraysize(kCollationWeightsL3));
1113// clang-format on
1114
1115// Normalize a comparison delta (usually `lhs - rhs`) to UCollationResult
1116// values.
1117constexpr UCollationResult ToUCollationResult(int delta) {
1118 return delta < 0 ? UCollationResult::UCOL_LESS
1119 : (delta > 0 ? UCollationResult::UCOL_GREATER
1120 : UCollationResult::UCOL_EQUAL);
1121}
1122
1123struct FastCompareStringsData {
1124 UCollationResult l1_result = UCollationResult::UCOL_EQUAL;
1125 UCollationResult l3_result = UCollationResult::UCOL_EQUAL;
1127 int first_diff_at = 0; // The first relevant diff (L1 if exists, else L3).
1128 bool has_diff = false;
1129
1130 std::optional<UCollationResult> FastCompareFailed(
1131 int* processed_until_out) const {
1132 if (has_diff) {
1133 // Found some difference, continue there to ensure the generic algorithm
1134 // picks it up.
1135 *processed_until_out = first_diff_at;
1136 } else {
1137 // No difference found, reprocess the last processed character since it
1138 // may be followed by a unicode combining character (which alters it's
1139 // meaning).
1140 *processed_until_out = std::max(processed_until - 1, 0);
1141 }
1142 return {};
1143 }
1144};
1145
1146template <class CharT>
1147constexpr bool CanFastCompare(CharT c) {
1148 return c < kCollationWeightsLength && kCollationWeightsL1[c] != 0;
1149}
1150
1151template <class Char1T, class Char2T>
1152bool FastCompareFlatString(const Char1T* lhs, const Char2T* rhs, int length,
1153 FastCompareStringsData* d) {
1154 for (int i = 0; i < length; i++) {
1155 const Char1T l = lhs[i];
1156 const Char2T r = rhs[i];
1157 if (!CanFastCompare(l) || !CanFastCompare(r)) {
1158 d->processed_until = i;
1159 return false;
1160 }
1161 UCollationResult l1_result =
1162 ToUCollationResult(kCollationWeightsL1[l] - kCollationWeightsL1[r]);
1163 if (l1_result != UCollationResult::UCOL_EQUAL) {
1164 d->has_diff = true;
1165 d->first_diff_at = i;
1166 d->processed_until = i;
1167 d->l1_result = l1_result;
1168 return true;
1169 }
1170 if (l != r && d->l3_result == UCollationResult::UCOL_EQUAL) {
1171 // Collapse the two-pass algorithm into one: if we find a difference in
1172 // L1 weights, that is our result. If not, use the first L3 weight
1173 // difference.
1174 UCollationResult l3_result =
1175 ToUCollationResult(kCollationWeightsL3[l] - kCollationWeightsL3[r]);
1176 d->l3_result = l3_result;
1177 if (!d->has_diff) {
1178 d->has_diff = true;
1179 d->first_diff_at = i;
1180 }
1181 }
1182 }
1183 d->processed_until = length;
1184 return true;
1185}
1186
1187bool FastCompareStringFlatContent(const String::FlatContent& lhs,
1188 const String::FlatContent& rhs, int length,
1189 FastCompareStringsData* d) {
1190 if (lhs.IsOneByte()) {
1191 base::Vector<const uint8_t> l = lhs.ToOneByteVector();
1192 if (rhs.IsOneByte()) {
1193 base::Vector<const uint8_t> r = rhs.ToOneByteVector();
1194 return FastCompareFlatString(l.data(), r.data(), length, d);
1195 } else {
1196 base::Vector<const uint16_t> r = rhs.ToUC16Vector();
1197 return FastCompareFlatString(l.data(), r.data(), length, d);
1198 }
1199 } else {
1200 base::Vector<const uint16_t> l = lhs.ToUC16Vector();
1201 if (rhs.IsOneByte()) {
1202 base::Vector<const uint8_t> r = rhs.ToOneByteVector();
1203 return FastCompareFlatString(l.data(), r.data(), length, d);
1204 } else {
1205 base::Vector<const uint16_t> r = rhs.ToUC16Vector();
1206 return FastCompareFlatString(l.data(), r.data(), length, d);
1207 }
1208 }
1209 UNREACHABLE();
1210}
1211
1212bool CharIsAsciiOrOutOfBounds(const String::FlatContent& string,
1213 int string_length, int index) {
1214 DCHECK_EQ(string.length(), string_length);
1215 return index >= string_length || isascii(string.Get(index));
1216}
1217
1218bool CharCanFastCompareOrOutOfBounds(const String::FlatContent& string,
1219 int string_length, int index) {
1220 DCHECK_EQ(string.length(), string_length);
1221 return index >= string_length || CanFastCompare(string.Get(index));
1222}
1223
1224#ifdef DEBUG
1225bool USetContainsAllAsciiItem(USet* set) {
1226 static constexpr int kBufferSize = 64;
1227 UChar buffer[kBufferSize];
1228
1229 const int length = uset_getItemCount(set);
1230 for (int i = 0; i < length; i++) {
1231 UChar32 start, end;
1232 UErrorCode status = U_ZERO_ERROR;
1233 const int item_length =
1234 uset_getItem(set, i, &start, &end, buffer, kBufferSize, &status);
1235 CHECK(U_SUCCESS(status));
1236 DCHECK_GE(item_length, 0);
1237
1238 if (item_length == 0) {
1239 // Empty string or a range.
1240 if (isascii(start)) return true;
1241 } else {
1242 // A non-empty string.
1243 bool all_ascii = true;
1244 for (int j = 0; j < item_length; j++) {
1245 if (!isascii(buffer[j])) {
1246 all_ascii = false;
1247 break;
1248 }
1249 }
1250
1251 if (all_ascii) return true;
1252 }
1253 }
1254
1255 return false;
1256}
1257
1258bool CollatorAllowsFastComparison(const icu::Collator& icu_collator) {
1259 UErrorCode status = U_ZERO_ERROR;
1260
1261 icu::Locale icu_locale(icu_collator.getLocale(ULOC_VALID_LOCALE, status));
1262 DCHECK(U_SUCCESS(status));
1263
1264 static constexpr int kBufferSize = 64;
1265 char buffer[kBufferSize];
1266 const int collation_keyword_length =
1267 icu_locale.getKeywordValue("collation", buffer, kBufferSize, status);
1268 DCHECK(U_SUCCESS(status));
1269 if (collation_keyword_length != 0) return false;
1270
1271 // These attributes must be set to the expected value for fast comparisons.
1272 static constexpr struct {
1273 UColAttribute attribute;
1274 UColAttributeValue legal_value;
1275 } kAttributeChecks[] = {
1276 {UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE},
1277 {UCOL_CASE_FIRST, UCOL_OFF},
1278 {UCOL_CASE_LEVEL, UCOL_OFF},
1279 {UCOL_FRENCH_COLLATION, UCOL_OFF},
1280 {UCOL_NUMERIC_COLLATION, UCOL_OFF},
1281 {UCOL_STRENGTH, UCOL_TERTIARY},
1282 };
1283
1284 for (const auto& check : kAttributeChecks) {
1285 if (icu_collator.getAttribute(check.attribute, status) !=
1286 check.legal_value) {
1287 return false;
1288 }
1289 DCHECK(U_SUCCESS(status));
1290 }
1291
1292 // No reordering codes are allowed.
1293 int num_reorder_codes =
1294 ucol_getReorderCodes(icu_collator.toUCollator(), nullptr, 0, &status);
1295 if (num_reorder_codes != 0) return false;
1296 DCHECK(U_SUCCESS(status)); // Must check *after* num_reorder_codes != 0.
1297
1298 // No tailored rules are allowed.
1299 int32_t rules_length = 0;
1300 ucol_getRules(icu_collator.toUCollator(), &rules_length);
1301 if (rules_length != 0) return false;
1302
1303 USet* tailored_set = ucol_getTailoredSet(icu_collator.toUCollator(), &status);
1304 DCHECK(U_SUCCESS(status));
1305 if (USetContainsAllAsciiItem(tailored_set)) return false;
1306 uset_close(tailored_set);
1307
1308 // No ASCII contractions or expansions are allowed.
1309 USet* contractions = uset_openEmpty();
1310 USet* expansions = uset_openEmpty();
1311 ucol_getContractionsAndExpansions(icu_collator.toUCollator(), contractions,
1312 expansions, true, &status);
1313 if (USetContainsAllAsciiItem(contractions)) return false;
1314 if (USetContainsAllAsciiItem(expansions)) return false;
1315 DCHECK(U_SUCCESS(status));
1316 uset_close(contractions);
1317 uset_close(expansions);
1318
1319 return true;
1320}
1321#endif // DEBUG
1322
1323// Fast comparison is implemented for charcodes for which the L1 collation
1324// weight (see kCollationWeightsL1 above) is not 0.
1325//
1326// Note it's possible to partially process strings as long as their leading
1327// characters all satisfy the above criteria. In that case, and if the L3
1328// result is EQUAL, we set `processed_until_out` to the first non-processed
1329// index - future processing can begin at that offset.
1330//
1331// This fast path looks somewhat complex; mostly because it combines multiple
1332// passes into one. The pseudo-code for simplified multi-pass algorithm is:
1333//
1334// {
1335// // We can only fast-compare a certain subset of the ASCII range.
1336// // Additionally, unicode characters can change the meaning of preceding
1337// // characters, for example: "o\u0308" is treated like "ö".
1338// //
1339// // Note, in the actual single-pass algorithm below, we tolerate non-ASCII
1340// // contents outside the relevant range.
1341// for (int i = 0; i < string1.length; i++) {
1342// if (!CanFastCompare(string1[i])) return {};
1343// }
1344// for (int i = 0; i < string2.length; i++) {
1345// if (!CanFastCompare(string2[i])) return {};
1346// }
1347//
1348// // Apply L1 weights.
1349// for (int i = 0; i < common_length; i++) {
1350// Char1T c1 = string1[i];
1351// Char2T c2 = string2[i];
1352// if (L1Weight[c1] != L1Weight[c2]) {
1353// return L1Weight[c1] - L1Weight[c2];
1354// }
1355// }
1356//
1357// // Strings are L1-equal up to the common length; if lengths differ, the
1358// // longer string is treated as 'greater'.
1359// if (string1.length != string2.length) string1.length - string2.length;
1360//
1361// // Apply L3 weights.
1362// for (int i = 0; i < common_length; i++) {
1363// Char1T c1 = string1[i];
1364// Char2T c2 = string2[i];
1365// if (L3Weight[c1] != L3Weight[c2]) {
1366// return L3Weight[c1] - L3Weight[c2];
1367// }
1368// }
1369//
1370// return UCOL_EQUAL;
1371// }
1372std::optional<UCollationResult> TryFastCompareStrings(
1373 Isolate* isolate, const icu::Collator& icu_collator,
1374 DirectHandle<String> string1, DirectHandle<String> string2,
1375 int* processed_until_out) {
1376 // TODO(jgruber): We could avoid the flattening (done by the caller) as well
1377 // by implementing comparison through string iteration. This has visible
1378 // performance benefits (e.g. 7% on CDJS) but complicates the code. Consider
1379 // doing this in the future.
1380 DCHECK(string1->IsFlat());
1381 DCHECK(string2->IsFlat());
1382
1383 *processed_until_out = 0;
1384
1385#ifdef DEBUG
1386 // Checked by the caller, see CompareStringsOptionsFor.
1387 SLOW_DCHECK(CollatorAllowsFastComparison(icu_collator));
1388 USE(CollatorAllowsFastComparison);
1389#endif // DEBUG
1390
1393
1394 const int length1 = string1->length();
1395 const int length2 = string2->length();
1396 int common_length = std::min(length1, length2);
1397
1398 FastCompareStringsData d;
1400 const String::FlatContent& flat1 = string1->GetFlatContent(no_gc);
1401 const String::FlatContent& flat2 = string2->GetFlatContent(no_gc);
1402 if (!FastCompareStringFlatContent(flat1, flat2, common_length, &d)) {
1403 DCHECK_EQ(d.l1_result, UCollationResult::UCOL_EQUAL);
1404 return d.FastCompareFailed(processed_until_out);
1405 }
1406
1407 // The result is only valid if the last processed character is not followed
1408 // by a unicode combining character (we are overly strict and restrict to
1409 // ASCII).
1410 if (!CharIsAsciiOrOutOfBounds(flat1, length1, d.processed_until + 1) ||
1411 !CharIsAsciiOrOutOfBounds(flat2, length2, d.processed_until + 1)) {
1412 return d.FastCompareFailed(processed_until_out);
1413 }
1414
1415 if (d.l1_result != UCollationResult::UCOL_EQUAL) {
1416 return d.l1_result;
1417 }
1418
1419 // Strings are L1-equal up to their common length, length differences win.
1420 UCollationResult length_result = ToUCollationResult(length1 - length2);
1421 if (length_result != UCollationResult::UCOL_EQUAL) {
1422 // Strings of different lengths may still compare as equal if the longer
1423 // string has a fully ignored suffix, e.g. "a" vs. "a\u{1}".
1424 if (!CharCanFastCompareOrOutOfBounds(flat1, length1, common_length) ||
1425 !CharCanFastCompareOrOutOfBounds(flat2, length2, common_length)) {
1426 return d.FastCompareFailed(processed_until_out);
1427 }
1428 return length_result;
1429 }
1430
1431 // L1-equal and same length, the L3 result wins.
1432 return d.l3_result;
1433}
1434
1435} // namespace
1436
1437// static
1439 return &kCollationWeightsL1[0];
1440}
1441
1442// static
1444 return &kCollationWeightsL3[0];
1445}
1446
1447// static
1448const int Intl::kAsciiCollationWeightsLength = kCollationWeightsLength;
1449
1450// ecma402/#sec-collator-comparestrings
1451int Intl::CompareStrings(Isolate* isolate, const icu::Collator& icu_collator,
1452 DirectHandle<String> string1,
1453 DirectHandle<String> string2,
1454 CompareStringsOptions compare_strings_options) {
1455 // Early return for identical strings.
1456 if (string1.is_identical_to(string2)) {
1457 return UCollationResult::UCOL_EQUAL;
1458 }
1459
1460 // We cannot return early for 0-length strings because of Unicode
1461 // ignorable characters. See also crbug.com/1347690.
1462
1463 string1 = String::Flatten(isolate, string1);
1464 string2 = String::Flatten(isolate, string2);
1465
1466 int processed_until = 0;
1467 if (compare_strings_options == CompareStringsOptions::kTryFastPath) {
1468 std::optional<int> maybe_result = TryFastCompareStrings(
1469 isolate, icu_collator, string1, string2, &processed_until);
1470 if (maybe_result.has_value()) return maybe_result.value();
1471 }
1472
1473 UCollationResult result;
1474 UErrorCode status = U_ZERO_ERROR;
1475 icu::StringPiece string_piece1 =
1476 ToICUStringPiece(isolate, string1, processed_until);
1477 if (!string_piece1.empty()) {
1478 icu::StringPiece string_piece2 =
1479 ToICUStringPiece(isolate, string2, processed_until);
1480 if (!string_piece2.empty()) {
1481 result = icu_collator.compareUTF8(string_piece1, string_piece2, status);
1482 DCHECK(U_SUCCESS(status));
1483 return result;
1484 }
1485 }
1486
1487 icu::UnicodeString string_val1 =
1488 Intl::ToICUUnicodeString(isolate, string1, processed_until);
1489 icu::UnicodeString string_val2 =
1490 Intl::ToICUUnicodeString(isolate, string2, processed_until);
1491 result = icu_collator.compare(string_val1, string_val2, status);
1492 DCHECK(U_SUCCESS(status));
1493 return result;
1494}
1495
1496// ecma402/#sup-properties-of-the-number-prototype-object
1498 Isolate* isolate, Handle<Object> num, DirectHandle<Object> locales,
1499 DirectHandle<Object> options, const char* method_name) {
1500 Handle<Object> numeric_obj;
1501 ASSIGN_RETURN_ON_EXCEPTION(isolate, numeric_obj,
1502 Object::ToNumeric(isolate, num));
1503
1504 // We only cache the instance when locales is a string/undefined and
1505 // options is undefined, as that is the only case when the specified
1506 // side-effects of examining those arguments are unobservable.
1507 bool can_cache = (IsString(*locales) || IsUndefined(*locales, isolate)) &&
1508 IsUndefined(*options, isolate);
1509 if (can_cache) {
1510 icu::number::LocalizedNumberFormatter* cached_number_format =
1511 static_cast<icu::number::LocalizedNumberFormatter*>(
1512 isolate->get_cached_icu_object(
1513 Isolate::ICUObjectCacheType::kDefaultNumberFormat, locales));
1514 // We may use the cached icu::NumberFormat for a fast path.
1515 if (cached_number_format != nullptr) {
1516 return JSNumberFormat::FormatNumeric(isolate, *cached_number_format,
1517 numeric_obj);
1518 }
1519 }
1520
1521 DirectHandle<JSFunction> constructor(
1523 isolate->context()->native_context()->intl_number_format_function()),
1524 isolate);
1525 DirectHandle<JSNumberFormat> number_format;
1526 // 2. Let numberFormat be ? Construct(%NumberFormat%, « locales, options »).
1527 StackLimitCheck stack_check(isolate);
1528 // New<JSNumberFormat>() requires a lot of stack space.
1529 const int kStackSpaceRequiredForNewJSNumberFormat = 16 * KB;
1530 if (stack_check.JsHasOverflowed(kStackSpaceRequiredForNewJSNumberFormat)) {
1531 isolate->StackOverflow();
1532 return {};
1533 }
1535 isolate, number_format,
1536 New<JSNumberFormat>(isolate, constructor, locales, options, method_name));
1537
1538 if (can_cache) {
1539 isolate->set_icu_object_in_cache(
1540 Isolate::ICUObjectCacheType::kDefaultNumberFormat, locales,
1541 std::static_pointer_cast<icu::UMemory>(
1542 number_format->icu_number_formatter()->get()));
1543 }
1544
1545 // Return FormatNumber(numberFormat, x).
1546 icu::number::LocalizedNumberFormatter* icu_number_format =
1547 number_format->icu_number_formatter()->raw();
1548 return JSNumberFormat::FormatNumeric(isolate, *icu_number_format,
1549 numeric_obj);
1550}
1551
1552namespace {
1553
1554// 22. is in « 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500,
1555// 5000 »
1556bool IsValidRoundingIncrement(int value) {
1557 switch (value) {
1558 case 1:
1559 case 2:
1560 case 5:
1561 case 10:
1562 case 20:
1563 case 25:
1564 case 50:
1565 case 100:
1566 case 200:
1567 case 250:
1568 case 500:
1569 case 1000:
1570 case 2000:
1571 case 2500:
1572 case 5000:
1573 return true;
1574 default:
1575 return false;
1576 }
1577}
1578
1579} // namespace
1580
1582 Isolate* isolate, DirectHandle<JSReceiver> options, int mnfd_default,
1583 int mxfd_default, bool notation_is_compact, const char* service) {
1584 Factory* factory = isolate->factory();
1585 Intl::NumberFormatDigitOptions digit_options;
1586
1587 // 1. Let mnid be ? GetNumberOption(options, "minimumIntegerDigits,", 1, 21,
1588 // 1).
1589 int mnid = 1;
1590 if (!GetNumberOption(isolate, options, factory->minimumIntegerDigits_string(),
1591 1, 21, 1)
1592 .To(&mnid)) {
1594 }
1595
1596 // 2. Let mnfd be ? Get(options, "minimumFractionDigits").
1597 DirectHandle<Object> mnfd_obj;
1599 isolate, mnfd_obj,
1600 JSReceiver::GetProperty(isolate, options,
1601 factory->minimumFractionDigits_string()),
1603
1604 // 3. Let mxfd be ? Get(options, "maximumFractionDigits").
1605 DirectHandle<Object> mxfd_obj;
1607 isolate, mxfd_obj,
1608 JSReceiver::GetProperty(isolate, options,
1609 factory->maximumFractionDigits_string()),
1611
1612 // 4. Let mnsd be ? Get(options, "minimumSignificantDigits").
1613 DirectHandle<Object> mnsd_obj;
1615 isolate, mnsd_obj,
1616 JSReceiver::GetProperty(isolate, options,
1617 factory->minimumSignificantDigits_string()),
1619
1620 // 5. Let mxsd be ? Get(options, "maximumSignificantDigits").
1621 DirectHandle<Object> mxsd_obj;
1623 isolate, mxsd_obj,
1624 JSReceiver::GetProperty(isolate, options,
1625 factory->maximumSignificantDigits_string()),
1627
1629 digit_options.minimum_significant_digits = 0;
1630 digit_options.maximum_significant_digits = 0;
1631
1632 // 6. Set intlObj.[[MinimumIntegerDigits]] to mnid.
1633 digit_options.minimum_integer_digits = mnid;
1634
1635 // 7. Let roundingIncrement be ? GetNumberOption(options, "roundingIncrement",
1636 // 1, 5000, 1).
1637 Maybe<int> maybe_rounding_increment = GetNumberOption(
1638 isolate, options, factory->roundingIncrement_string(), 1, 5000, 1);
1639 if (!maybe_rounding_increment.To(&digit_options.rounding_increment)) {
1641 }
1642 // 8. If roundingIncrement is not in « 1, 2, 5, 10, 20, 25, 50, 100, 200, 250,
1643 // 500, 1000, 2000, 2500, 5000 », throw a RangeError exception.
1644 if (!IsValidRoundingIncrement(digit_options.rounding_increment)) {
1646 isolate,
1647 NewRangeError(MessageTemplate::kPropertyValueOutOfRange,
1648 factory->roundingIncrement_string()),
1650 }
1651
1652 // 9. Let roundingMode be ? GetOption(options, "roundingMode", string, «
1653 // "ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", "halfExpand",
1654 // "halfTrunc", "halfEven" », "halfExpand").
1656 isolate, options, "roundingMode", service,
1657 {"ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor",
1658 "halfExpand", "halfTrunc", "halfEven"},
1664 MAYBE_RETURN(maybe_rounding_mode, Nothing<NumberFormatDigitOptions>());
1665 digit_options.rounding_mode = maybe_rounding_mode.FromJust();
1666
1667 // 10. Let roundingPriority be ? GetOption(options, "roundingPriority",
1668 // "string", « "auto", "morePrecision", "lessPrecision" », "auto").
1669
1670 Maybe<RoundingPriority> maybe_rounding_priority =
1672 isolate, options, "roundingPriority", service,
1673 {"auto", "morePrecision", "lessPrecision"},
1677 MAYBE_RETURN(maybe_rounding_priority, Nothing<NumberFormatDigitOptions>());
1678 digit_options.rounding_priority = maybe_rounding_priority.FromJust();
1679
1680 // 11. Let trailingZeroDisplay be ? GetOption(options, "trailingZeroDisplay",
1681 // string, « "auto", "stripIfInteger" », "auto").
1682 Maybe<TrailingZeroDisplay> maybe_trailing_zero_display =
1684 isolate, options, "trailingZeroDisplay", service,
1685 {"auto", "stripIfInteger"},
1688 MAYBE_RETURN(maybe_trailing_zero_display,
1690 digit_options.trailing_zero_display = maybe_trailing_zero_display.FromJust();
1691
1692 // 12. NOTE: All fields required by SetNumberFormatDigitOptions have now been
1693 // read from options. The remainder of this AO interprets the options and may
1694 // throw exceptions.
1695
1696 // 17. If mnsd is not undefined or mxsd is not undefined, then
1697 // a. Set hasSd to true.
1698 // 18. Else,
1699 // a. Set hasSd to false.
1700 bool has_sd =
1701 (!IsUndefined(*mnsd_obj, isolate)) || (!IsUndefined(*mxsd_obj, isolate));
1702
1703 // 19. If mnfd is not undefined or mxfd is not undefined, then
1704 // a. Set hasFd to true.
1705 // 22. Else,
1706 // a. Set hasFd to false.
1707 bool has_fd =
1708 (!IsUndefined(*mnfd_obj, isolate)) || (!IsUndefined(*mxfd_obj, isolate));
1709
1710 // 21. Let needSd be true.
1711 bool need_sd = true;
1712 // 22. Let needFd be true.
1713 bool need_fd = true;
1714 // 23. If roundingPriority is "auto", then
1715 if (RoundingPriority::kAuto == digit_options.rounding_priority) {
1716 // a. Set needSd to hasSd.
1717 need_sd = has_sd;
1718 // b. If needSd is true, or hasFd is false and notation is "compact", then
1719 if (need_sd || ((!has_fd) && notation_is_compact)) {
1720 // i. Set needFd to false.
1721 need_fd = false;
1722 }
1723 }
1724 // 24. If needSd is true, then
1725 if (need_sd) {
1726 // 24.a If hasSd is true, then
1727 if (has_sd) {
1728 // i. Set intlObj.[[MinimumSignificantDigits]] to ?
1729 // DefaultNumberOption(mnsd, 1, 21, 1).
1730 int mnsd;
1731 if (!DefaultNumberOption(isolate, mnsd_obj, 1, 21, 1,
1732 factory->minimumSignificantDigits_string())
1733 .To(&mnsd)) {
1735 }
1736 digit_options.minimum_significant_digits = mnsd;
1737 // ii. Set intlObj.[[MaximumSignificantDigits]] to ?
1738 // DefaultNumberOption(mxsd, intlObj.[[MinimumSignificantDigits]], 21,
1739 // 21).
1740 int mxsd;
1741 if (!DefaultNumberOption(isolate, mxsd_obj, mnsd, 21, 21,
1742 factory->maximumSignificantDigits_string())
1743 .To(&mxsd)) {
1745 }
1746 digit_options.maximum_significant_digits = mxsd;
1747 } else {
1748 // 24.b Else
1749 // 24.b.i Set intlObj.[[MinimumSignificantDigits]] to 1.
1750 digit_options.minimum_significant_digits = 1;
1751 // 24.b.ii Set intlObj.[[MaximumSignificantDigits]] to 21.
1752 digit_options.maximum_significant_digits = 21;
1753 }
1754 }
1755
1756 DirectHandle<String> mxfd_str = factory->maximumFractionDigits_string();
1757 // 25. If needFd is true, then
1758 if (need_fd) {
1759 // a. If hasFd is true, then
1760 if (has_fd) {
1761 DirectHandle<String> mnfd_str = factory->minimumFractionDigits_string();
1762 // i. Let mnfd be ? DefaultNumberOption(mnfd, 0, 100, undefined).
1763 int mnfd;
1764 if (!DefaultNumberOption(isolate, mnfd_obj, 0, 100, -1, mnfd_str)
1765 .To(&mnfd)) {
1767 }
1768 // ii. Let mxfd be ? DefaultNumberOption(mxfd, 0, 100, undefined).
1769 int mxfd;
1770 if (!DefaultNumberOption(isolate, mxfd_obj, 0, 100, -1, mxfd_str)
1771 .To(&mxfd)) {
1773 }
1774 // iii. If mnfd is undefined, set mnfd to min(mnfdDefault, mxfd).
1775 if (IsUndefined(*mnfd_obj, isolate)) {
1776 mnfd = std::min(mnfd_default, mxfd);
1777 } else if (IsUndefined(*mxfd_obj, isolate)) {
1778 // iv. Else if mxfd is undefined, set mxfd to max(mxfdDefault,
1779 // mnfd).
1780 mxfd = std::max(mxfd_default, mnfd);
1781 } else if (mnfd > mxfd) {
1782 // v. Else if mnfd is greater than mxfd, throw a RangeError
1783 // exception.
1785 isolate,
1786 NewRangeError(MessageTemplate::kPropertyValueOutOfRange, mxfd_str),
1788 }
1789 // vi. Set intlObj.[[MinimumFractionDigits]] to mnfd.
1790 digit_options.minimum_fraction_digits = mnfd;
1791 // vii. Set intlObj.[[MaximumFractionDigits]] to mxfd.
1792 digit_options.maximum_fraction_digits = mxfd;
1793 } else { // b. Else
1794 // i. Set intlObj.[[MinimumFractionDigits]] to mnfdDefault.
1795 digit_options.minimum_fraction_digits = mnfd_default;
1796 // ii. Set intlObj.[[MaximumFractionDigits]] to mxfdDefault.
1797 digit_options.maximum_fraction_digits = mxfd_default;
1798 }
1799 }
1800
1801 // 26. If needSd is false and needFd is false, then
1802 if ((!need_sd) && (!need_fd)) {
1803 // a. Set intlObj.[[MinimumFractionDigits]] to 0.
1804 digit_options.minimum_fraction_digits = 0;
1805 // b. Set intlObj.[[MaximumFractionDigits]] to 0.
1806 digit_options.maximum_fraction_digits = 0;
1807 // c. Set intlObj.[[MinimumSignificantDigits]] to 1.
1808 digit_options.minimum_significant_digits = 1;
1809 // d. Set intlObj.[[MaximumSignificantDigits]] to 2.
1810 digit_options.maximum_significant_digits = 2;
1811 // e. Set intlObj.[[RoundingType]] to morePrecision.
1813 // 27. Else if roundingPriority is "morePrecision", then
1814 } else if (digit_options.rounding_priority ==
1816 // i. Set intlObj.[[RoundingType]] to morePrecision.
1818 // 28. Else if roundingPriority is "lessPrecision", then
1819 } else if (digit_options.rounding_priority ==
1821 // i. Set intlObj.[[RoundingType]] to lessPrecision.
1823 // 29. Else if hasSd, then
1824 } else if (has_sd) {
1825 // i. Set intlObj.[[RoundingType]] to significantDigits.
1827 // 30. Else,
1828 } else {
1829 // i.Set intlObj.[[RoundingType]] to fractionDigits.
1831 }
1832 // 31. If roundingIncrement is not 1, then
1833 if (digit_options.rounding_increment != 1) {
1834 // a. If intlObj.[[RoundingType]] is not fractionDigits, throw a TypeError
1835 // exception.
1836 if (digit_options.rounding_type != RoundingType::kFractionDigits) {
1838 isolate, NewTypeError(MessageTemplate::kBadRoundingType),
1840 }
1841 // b. If intlObj.[[MaximumFractionDigits]] is not equal to
1842 // intlObj.[[MinimumFractionDigits]], throw a RangeError exception.
1843 if (digit_options.maximum_fraction_digits !=
1844 digit_options.minimum_fraction_digits) {
1846 isolate,
1847 NewRangeError(MessageTemplate::kPropertyValueOutOfRange, mxfd_str),
1849 }
1850 }
1851 return Just(digit_options);
1852}
1853
1854namespace {
1855
1856// ecma402/#sec-bestavailablelocale
1857std::string BestAvailableLocale(const std::set<std::string>& available_locales,
1858 const std::string& locale) {
1859 // 1. Let candidate be locale.
1860 std::string candidate = locale;
1861
1862 // 2. Repeat,
1863 while (true) {
1864 // 2.a. If availableLocales contains an element equal to candidate, return
1865 // candidate.
1866 if (available_locales.find(candidate) != available_locales.end()) {
1867 return candidate;
1868 }
1869
1870 // 2.b. Let pos be the character index of the last occurrence of "-"
1871 // (U+002D) within candidate. If that character does not occur, return
1872 // undefined.
1873 size_t pos = candidate.rfind('-');
1874 if (pos == std::string::npos) {
1875 return std::string();
1876 }
1877
1878 // 2.c. If pos ≥ 2 and the character "-" occurs at index pos-2 of candidate,
1879 // decrease pos by 2.
1880 if (pos >= 2 && candidate[pos - 2] == '-') {
1881 pos -= 2;
1882 }
1883
1884 // 2.d. Let candidate be the substring of candidate from position 0,
1885 // inclusive, to position pos, exclusive.
1886 candidate = candidate.substr(0, pos);
1887 }
1888}
1889
1890struct ParsedLocale {
1892 std::string extension;
1893};
1894
1895// Returns a struct containing a bcp47 tag without unicode extensions
1896// and the removed unicode extensions.
1897//
1898// For example, given 'en-US-u-co-emoji' returns 'en-US' and
1899// 'u-co-emoji'.
1900ParsedLocale ParseBCP47Locale(const std::string& locale) {
1901 size_t length = locale.length();
1902 ParsedLocale parsed_locale;
1903
1904 // Privateuse or grandfathered locales have no extension sequences.
1905 if ((length > 1) && (locale[1] == '-')) {
1906 // Check to make sure that this really is a grandfathered or
1907 // privateuse extension. ICU can sometimes mess up the
1908 // canonicalization.
1909 DCHECK(locale[0] == 'x' || locale[0] == 'i');
1910 parsed_locale.no_extensions_locale = locale;
1911 return parsed_locale;
1912 }
1913
1914 size_t unicode_extension_start = locale.find("-u-");
1915
1916 // No unicode extensions found.
1917 if (unicode_extension_start == std::string::npos) {
1918 parsed_locale.no_extensions_locale = locale;
1919 return parsed_locale;
1920 }
1921
1922 size_t private_extension_start = locale.find("-x-");
1923
1924 // Unicode extensions found within privateuse subtags don't count.
1925 if (private_extension_start != std::string::npos &&
1926 private_extension_start < unicode_extension_start) {
1927 parsed_locale.no_extensions_locale = locale;
1928 return parsed_locale;
1929 }
1930
1931 const std::string beginning = locale.substr(0, unicode_extension_start);
1932 size_t unicode_extension_end = length;
1933 DCHECK_GT(length, 2);
1934
1935 // Find the end of the extension production as per the bcp47 grammar
1936 // by looking for '-' followed by 2 chars and then another '-'.
1937 for (size_t i = unicode_extension_start + 1; i < length - 2; i++) {
1938 if (locale[i] != '-') continue;
1939
1940 if (locale[i + 2] == '-') {
1941 unicode_extension_end = i;
1942 break;
1943 }
1944
1945 i += 2;
1946 }
1947
1948 const std::string end = locale.substr(unicode_extension_end);
1949 parsed_locale.no_extensions_locale = beginning + end;
1950 parsed_locale.extension = locale.substr(
1951 unicode_extension_start, unicode_extension_end - unicode_extension_start);
1952 return parsed_locale;
1953}
1954
1955// ecma402/#sec-lookupsupportedlocales
1956std::vector<std::string> LookupSupportedLocales(
1957 const std::set<std::string>& available_locales,
1958 const std::vector<std::string>& requested_locales) {
1959 // 1. Let subset be a new empty List.
1960 std::vector<std::string> subset;
1961
1962 // 2. For each element locale of requestedLocales in List order, do
1963 for (const std::string& locale : requested_locales) {
1964 // 2. a. Let noExtensionsLocale be the String value that is locale
1965 // with all Unicode locale extension sequences removed.
1966 std::string no_extension_locale =
1967 ParseBCP47Locale(locale).no_extensions_locale;
1968
1969 // 2. b. Let availableLocale be
1970 // BestAvailableLocale(availableLocales, noExtensionsLocale).
1971 std::string available_locale =
1972 BestAvailableLocale(available_locales, no_extension_locale);
1973
1974 // 2. c. If availableLocale is not undefined, append locale to the
1975 // end of subset.
1976 if (!available_locale.empty()) {
1977 subset.push_back(locale);
1978 }
1979 }
1980
1981 // 3. Return subset.
1982 return subset;
1983}
1984
1985icu::LocaleMatcher BuildLocaleMatcher(
1986 Isolate* isolate, const std::set<std::string>& available_locales,
1987 UErrorCode* status) {
1988 icu::Locale default_locale =
1989 icu::Locale::forLanguageTag(isolate->DefaultLocale(), *status);
1990 icu::LocaleMatcher::Builder builder;
1991 if (U_FAILURE(*status)) {
1992 return builder.build(*status);
1993 }
1994 builder.setDefaultLocale(&default_locale);
1995 for (auto it = available_locales.begin(); it != available_locales.end();
1996 ++it) {
1997 *status = U_ZERO_ERROR;
1998 icu::Locale l = icu::Locale::forLanguageTag(it->c_str(), *status);
1999 // skip invalid locale such as no-NO-NY
2000 if (U_SUCCESS(*status)) {
2001 builder.addSupportedLocale(l);
2002 }
2003 }
2004 return builder.build(*status);
2005}
2006
2007class Iterator : public icu::Locale::Iterator {
2008 public:
2009 Iterator(std::vector<std::string>::const_iterator begin,
2010 std::vector<std::string>::const_iterator end)
2011 : iter_(begin), end_(end) {}
2012 ~Iterator() override = default;
2013
2014 UBool hasNext() const override { return iter_ != end_; }
2015
2016 const icu::Locale& next() override {
2017 UErrorCode status = U_ZERO_ERROR;
2018 locale_ = icu::Locale::forLanguageTag(iter_->c_str(), status);
2019 DCHECK(U_SUCCESS(status));
2020 ++iter_;
2021 return locale_;
2022 }
2023
2024 private:
2025 std::vector<std::string>::const_iterator iter_;
2026 std::vector<std::string>::const_iterator end_;
2027 icu::Locale locale_;
2028};
2029
2030// ecma402/#sec-bestfitmatcher
2031// The BestFitMatcher abstract operation compares requestedLocales, which must
2032// be a List as returned by CanonicalizeLocaleList, against the locales in
2033// availableLocales and determines the best available language to meet the
2034// request. The algorithm is implementation dependent, but should produce
2035// results that a typical user of the requested locales would perceive
2036// as at least as good as those produced by the LookupMatcher abstract
2037// operation. Options specified through Unicode locale extension sequences must
2038// be ignored by the algorithm. Information about such subsequences is returned
2039// separately. The abstract operation returns a record with a [[locale]] field,
2040// whose value is the language tag of the selected locale, which must be an
2041// element of availableLocales. If the language tag of the request locale that
2042// led to the selected locale contained a Unicode locale extension sequence,
2043// then the returned record also contains an [[extension]] field whose value is
2044// the first Unicode locale extension sequence within the request locale
2045// language tag.
2046std::string BestFitMatcher(Isolate* isolate,
2047 const std::set<std::string>& available_locales,
2048 const std::vector<std::string>& requested_locales) {
2049 UErrorCode status = U_ZERO_ERROR;
2050 Iterator iter(requested_locales.cbegin(), requested_locales.cend());
2051 std::string bestfit = BuildLocaleMatcher(isolate, available_locales, &status)
2052 .getBestMatchResult(iter, status)
2053 .makeResolvedLocale(status)
2054 .toLanguageTag<std::string>(status);
2055 DCHECK(U_SUCCESS(status));
2056 return bestfit;
2057}
2058
2059// ECMA 402 9.2.8 BestFitSupportedLocales(availableLocales, requestedLocales)
2060// https://tc39.github.io/ecma402/#sec-bestfitsupportedlocales
2061std::vector<std::string> BestFitSupportedLocales(
2062 Isolate* isolate, const std::set<std::string>& available_locales,
2063 const std::vector<std::string>& requested_locales) {
2064 UErrorCode status = U_ZERO_ERROR;
2065 icu::LocaleMatcher matcher =
2066 BuildLocaleMatcher(isolate, available_locales, &status);
2067 std::vector<std::string> result;
2068 if (U_SUCCESS(status)) {
2069 for (auto it = requested_locales.cbegin(); it != requested_locales.cend();
2070 it++) {
2071 status = U_ZERO_ERROR;
2072 icu::Locale desired = icu::Locale::forLanguageTag(it->c_str(), status);
2073 icu::LocaleMatcher::Result matched =
2074 matcher.getBestMatchResult(desired, status);
2075 if (U_FAILURE(status)) continue;
2076 if (matched.getSupportedIndex() < 0) continue;
2077
2078 // The BestFitSupportedLocales abstract operation returns the *SUBSET* of
2079 // the provided BCP 47 language priority list requestedLocales for which
2080 // availableLocales has a matching locale when using the Best Fit Matcher
2081 // algorithm. Locales appear in the same order in the returned list as in
2082 // requestedLocales. The steps taken are implementation dependent.
2083 std::string bestfit = desired.toLanguageTag<std::string>(status);
2084 if (U_FAILURE(status)) continue;
2085 result.push_back(bestfit);
2086 }
2087 }
2088 return result;
2089}
2090
2091// ecma262 #sec-createarrayfromlist
2092MaybeDirectHandle<JSArray> CreateArrayFromList(
2093 Isolate* isolate, std::vector<std::string> elements,
2094 PropertyAttributes attr) {
2095 Factory* factory = isolate->factory();
2096 // Let array be ! ArrayCreate(0).
2097 DirectHandle<JSArray> array = factory->NewJSArray(0);
2098
2099 uint32_t length = static_cast<uint32_t>(elements.size());
2100 // 3. Let n be 0.
2101 // 4. For each element e of elements, do
2102 for (uint32_t i = 0; i < length; i++) {
2103 // a. Let status be CreateDataProperty(array, ! ToString(n), e).
2104 const std::string& part = elements[i];
2105 DirectHandle<String> value =
2106 factory->NewStringFromUtf8(base::CStrVector(part.c_str()))
2107 .ToHandleChecked();
2108 MAYBE_RETURN(JSObject::AddDataElement(array, i, value, attr), {});
2109 }
2110 // 5. Return array.
2111 return array;
2112}
2113
2114// ECMA 402 9.2.9 SupportedLocales(availableLocales, requestedLocales, options)
2115// https://tc39.github.io/ecma402/#sec-supportedlocales
2116MaybeDirectHandle<JSObject> SupportedLocales(
2117 Isolate* isolate, const char* method_name,
2118 const std::set<std::string>& available_locales,
2119 const std::vector<std::string>& requested_locales,
2120 DirectHandle<Object> options) {
2121 std::vector<std::string> supported_locales;
2122
2123 // 1. Set options to ? CoerceOptionsToObject(options).
2124 DirectHandle<JSReceiver> options_obj;
2126 isolate, options_obj,
2127 CoerceOptionsToObject(isolate, options, method_name));
2128
2129 // 2. Let matcher be ? GetOption(options, "localeMatcher", "string",
2130 // « "lookup", "best fit" », "best fit").
2131 Maybe<Intl::MatcherOption> maybe_locale_matcher =
2132 Intl::GetLocaleMatcher(isolate, options_obj, method_name);
2133 MAYBE_RETURN(maybe_locale_matcher, {});
2134 Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
2135
2136 // 3. If matcher is "best fit", then
2137 // a. Let supportedLocales be BestFitSupportedLocales(availableLocales,
2138 // requestedLocales).
2139 if (matcher == Intl::MatcherOption::kBestFit &&
2140 v8_flags.harmony_intl_best_fit_matcher) {
2141 supported_locales =
2142 BestFitSupportedLocales(isolate, available_locales, requested_locales);
2143 } else {
2144 // 4. Else,
2145 // a. Let supportedLocales be LookupSupportedLocales(availableLocales,
2146 // requestedLocales).
2147 supported_locales =
2148 LookupSupportedLocales(available_locales, requested_locales);
2149 }
2150
2151 // 5. Return CreateArrayFromList(supportedLocales).
2152 return CreateArrayFromList(isolate, supported_locales,
2154}
2155
2156} // namespace
2157
2158// ecma-402 #sec-intl.getcanonicallocales
2160 Isolate* isolate, DirectHandle<Object> locales) {
2161 // 1. Let ll be ? CanonicalizeLocaleList(locales).
2163 CanonicalizeLocaleList(isolate, locales, false);
2164 MAYBE_RETURN(maybe_ll, {});
2165
2166 // 2. Return CreateArrayFromList(ll).
2167 return CreateArrayFromList(isolate, maybe_ll.FromJust(),
2169}
2170
2171namespace {
2172
2173MaybeDirectHandle<JSArray> AvailableCollations(Isolate* isolate) {
2174 UErrorCode status = U_ZERO_ERROR;
2175 std::unique_ptr<icu::StringEnumeration> enumeration(
2176 icu::Collator::getKeywordValues("collation", status));
2177 if (U_FAILURE(status)) {
2178 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
2179 }
2180 return Intl::ToJSArray(isolate, "co", enumeration.get(),
2181 Intl::RemoveCollation, true);
2182}
2183
2184MaybeDirectHandle<JSArray> VectorToJSArray(
2185 Isolate* isolate, const std::vector<std::string>& array) {
2186 Factory* factory = isolate->factory();
2187 DirectHandle<FixedArray> fixed_array =
2188 factory->NewFixedArray(static_cast<int32_t>(array.size()));
2189 int32_t index = 0;
2190 for (const std::string& item : array) {
2191 DirectHandle<String> str = factory->NewStringFromAsciiChecked(item.c_str());
2192 fixed_array->set(index++, *str);
2193 }
2194 return factory->NewJSArrayWithElements(fixed_array);
2195}
2196
2197namespace {
2198
2199class ResourceAvailableCurrencies {
2200 public:
2201 ResourceAvailableCurrencies() {
2202 UErrorCode status = U_ZERO_ERROR;
2203 UEnumeration* uenum =
2204 ucurr_openISOCurrencies(UCURR_COMMON | UCURR_NON_DEPRECATED, &status);
2205 DCHECK(U_SUCCESS(status));
2206 const char* next = nullptr;
2207 while (U_SUCCESS(status) &&
2208 (next = uenum_next(uenum, nullptr, &status)) != nullptr) {
2209 // Work around the issue that we do not support VEF currency code
2210 // in DisplayNames by not reporting it.
2211 if (strcmp(next, "VEF") == 0) continue;
2212 AddIfAvailable(next);
2213 }
2214 // Work around the issue that we do support the following currency codes
2215 // in DisplayNames but the ICU API is not reporting it.
2216 AddIfAvailable("SVC");
2217 AddIfAvailable("XDR");
2218 AddIfAvailable("XSU");
2219 AddIfAvailable("ZWL");
2220 std::sort(list_.begin(), list_.end());
2221 uenum_close(uenum);
2222 }
2223
2224 const std::vector<std::string>& Get() const { return list_; }
2225
2226 void AddIfAvailable(const char* currency) {
2227 icu::UnicodeString code(currency, -1, US_INV);
2228 UErrorCode status = U_ZERO_ERROR;
2229 int32_t len = 0;
2230 const UChar* result =
2231 ucurr_getName(code.getTerminatedBuffer(), "en", UCURR_LONG_NAME,
2232 nullptr, &len, &status);
2233 if (U_SUCCESS(status) &&
2234 u_strcmp(result, code.getTerminatedBuffer()) != 0) {
2235 list_.push_back(currency);
2236 }
2237 }
2238
2239 private:
2240 std::vector<std::string> list_;
2241};
2242
2243const std::vector<std::string>& GetAvailableCurrencies() {
2245 available_currencies = LAZY_INSTANCE_INITIALIZER;
2246 return available_currencies.Pointer()->Get();
2247}
2248} // namespace
2249
2250MaybeDirectHandle<JSArray> AvailableCurrencies(Isolate* isolate) {
2251 return VectorToJSArray(isolate, GetAvailableCurrencies());
2252}
2253
2254MaybeDirectHandle<JSArray> AvailableNumberingSystems(Isolate* isolate) {
2255 UErrorCode status = U_ZERO_ERROR;
2256 std::unique_ptr<icu::StringEnumeration> enumeration(
2257 icu::NumberingSystem::getAvailableNames(status));
2258 if (U_FAILURE(status)) {
2259 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
2260 }
2261 // Need to filter out isAlgorithmic
2262 return Intl::ToJSArray(
2263 isolate, "nu", enumeration.get(),
2264 [](const char* value) {
2265 UErrorCode status = U_ZERO_ERROR;
2266 std::unique_ptr<icu::NumberingSystem> numbering_system(
2267 icu::NumberingSystem::createInstanceByName(value, status));
2268 // Skip algorithmic one since chrome filter out the resource.
2269 return U_FAILURE(status) || numbering_system->isAlgorithmic();
2270 },
2271 true);
2272}
2273
2274MaybeDirectHandle<JSArray> AvailableTimeZones(Isolate* isolate) {
2275 UErrorCode status = U_ZERO_ERROR;
2276 std::unique_ptr<icu::StringEnumeration> enumeration(
2277 icu::TimeZone::createTimeZoneIDEnumeration(
2278 UCAL_ZONE_TYPE_CANONICAL_LOCATION, nullptr, nullptr, status));
2279 if (U_FAILURE(status)) {
2280 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
2281 }
2282 return Intl::ToJSArray(isolate, nullptr, enumeration.get(), nullptr, true);
2283}
2284
2285MaybeDirectHandle<JSArray> AvailableUnits(Isolate* isolate) {
2286 Factory* factory = isolate->factory();
2287 std::set<std::string> sanctioned(Intl::SanctionedSimpleUnits());
2288 DirectHandle<FixedArray> fixed_array =
2289 factory->NewFixedArray(static_cast<int32_t>(sanctioned.size()));
2290 int32_t index = 0;
2291 for (const std::string& item : sanctioned) {
2292 DirectHandle<String> str = factory->NewStringFromAsciiChecked(item.c_str());
2293 fixed_array->set(index++, *str);
2294 }
2295 return factory->NewJSArrayWithElements(fixed_array);
2296}
2297
2298} // namespace
2299
2300// ecma-402 #sec-intl.supportedvaluesof
2302 Isolate* isolate, DirectHandle<Object> key_obj) {
2303 Factory* factory = isolate->factory();
2304 // 1. 1. Let key be ? ToString(key).
2305 DirectHandle<String> key_str;
2306 ASSIGN_RETURN_ON_EXCEPTION(isolate, key_str,
2307 Object::ToString(isolate, key_obj));
2308 // 2. If key is "calendar", then
2309 if (factory->calendar_string()->Equals(*key_str)) {
2310 // a. Let list be ! AvailableCalendars( ).
2311 return Intl::AvailableCalendars(isolate);
2312 }
2313 // 3. Else if key is "collation", then
2314 if (factory->collation_string()->Equals(*key_str)) {
2315 // a. Let list be ! AvailableCollations( ).
2316 return AvailableCollations(isolate);
2317 }
2318 // 4. Else if key is "currency", then
2319 if (factory->currency_string()->Equals(*key_str)) {
2320 // a. Let list be ! AvailableCurrencies( ).
2321 return AvailableCurrencies(isolate);
2322 }
2323 // 5. Else if key is "numberingSystem", then
2324 if (factory->numberingSystem_string()->Equals(*key_str)) {
2325 // a. Let list be ! AvailableNumberingSystems( ).
2326 return AvailableNumberingSystems(isolate);
2327 }
2328 // 6. Else if key is "timeZone", then
2329 if (factory->timeZone_string()->Equals(*key_str)) {
2330 // a. Let list be ! AvailableTimeZones( ).
2331 return AvailableTimeZones(isolate);
2332 }
2333 // 7. Else if key is "unit", then
2334 if (factory->unit_string()->Equals(*key_str)) {
2335 // a. Let list be ! AvailableUnits( ).
2336 return AvailableUnits(isolate);
2337 }
2338 // 8. Else,
2339 // a. Throw a RangeError exception.
2340 // 9. Return ! CreateArrayFromList( list ).
2341
2343 isolate,
2344 NewRangeError(MessageTemplate::kInvalid,
2345 factory->NewStringFromStaticChars("key"), key_str));
2346}
2347
2348// ECMA 402 Intl.*.supportedLocalesOf
2350 Isolate* isolate, const char* method_name,
2351 const std::set<std::string>& available_locales,
2352 DirectHandle<Object> locales, DirectHandle<Object> options) {
2353 // Let availableLocales be %Collator%.[[AvailableLocales]].
2354
2355 // Let requestedLocales be ? CanonicalizeLocaleList(locales).
2356 Maybe<std::vector<std::string>> requested_locales =
2357 CanonicalizeLocaleList(isolate, locales, false);
2358 MAYBE_RETURN(requested_locales, {});
2359
2360 // Return ? SupportedLocales(availableLocales, requestedLocales, options).
2361 return SupportedLocales(isolate, method_name, available_locales,
2362 requested_locales.FromJust(), options);
2363}
2364
2365namespace {
2366
2367template <typename T>
2368bool IsValidExtension(const icu::Locale& locale, const char* key,
2369 const std::string& value) {
2370 const char* legacy_type = uloc_toLegacyType(key, value.c_str());
2371 if (legacy_type == nullptr) {
2372 return false;
2373 }
2374 UErrorCode status = U_ZERO_ERROR;
2375 std::unique_ptr<icu::StringEnumeration> enumeration(
2376 T::getKeywordValuesForLocale(key, icu::Locale(locale.getBaseName()),
2377 false, status));
2378 if (U_FAILURE(status)) {
2379 return false;
2380 }
2381 int32_t length;
2382 for (const char* item = enumeration->next(&length, status);
2383 U_SUCCESS(status) && item != nullptr;
2384 item = enumeration->next(&length, status)) {
2385 if (strcmp(legacy_type, item) == 0) {
2386 return true;
2387 }
2388 }
2389 return false;
2390}
2391
2392} // namespace
2393
2394bool Intl::IsValidCollation(const icu::Locale& locale,
2395 const std::string& value) {
2396 std::set<std::string> invalid_values = {"standard", "search"};
2397 if (invalid_values.find(value) != invalid_values.end()) return false;
2398 return IsValidExtension<icu::Collator>(locale, "collation", value);
2399}
2400
2401bool Intl::IsWellFormedCalendar(const std::string& value) {
2402 return JSLocale::Is38AlphaNumList(value);
2403}
2404
2405// ecma402/#sec-iswellformedcurrencycode
2406bool Intl::IsWellFormedCurrency(const std::string& currency) {
2407 return JSLocale::Is3Alpha(currency);
2408}
2409
2410bool Intl::IsValidCalendar(const icu::Locale& locale,
2411 const std::string& value) {
2412 return IsValidExtension<icu::Calendar>(locale, "calendar", value);
2413}
2414
2415bool Intl::IsValidNumberingSystem(const std::string& value) {
2416 std::set<std::string> invalid_values = {"native", "traditio", "finance"};
2417 if (invalid_values.find(value) != invalid_values.end()) return false;
2418 UErrorCode status = U_ZERO_ERROR;
2419 std::unique_ptr<icu::NumberingSystem> numbering_system(
2420 icu::NumberingSystem::createInstanceByName(value.c_str(), status));
2421 return U_SUCCESS(status) && numbering_system != nullptr &&
2422 !numbering_system->isAlgorithmic();
2423}
2424
2425namespace {
2426
2427bool IsWellFormedNumberingSystem(const std::string& value) {
2428 return JSLocale::Is38AlphaNumList(value);
2429}
2430
2431std::map<std::string, std::string> LookupAndValidateUnicodeExtensions(
2432 icu::Locale* icu_locale, const std::set<std::string>& relevant_keys) {
2433 std::map<std::string, std::string> extensions;
2434
2435 UErrorCode status = U_ZERO_ERROR;
2436 icu::LocaleBuilder builder;
2437 builder.setLocale(*icu_locale).clearExtensions();
2438 std::unique_ptr<icu::StringEnumeration> keywords(
2439 icu_locale->createKeywords(status));
2440 if (U_FAILURE(status)) return extensions;
2441
2442 if (!keywords) return extensions;
2443 char value[ULOC_FULLNAME_CAPACITY];
2444
2445 int32_t length;
2446 status = U_ZERO_ERROR;
2447 for (const char* keyword = keywords->next(&length, status);
2448 keyword != nullptr; keyword = keywords->next(&length, status)) {
2449 // Ignore failures in ICU and skip to the next keyword.
2450 //
2451 // This is fine.™
2452 if (U_FAILURE(status)) {
2453 status = U_ZERO_ERROR;
2454 continue;
2455 }
2456
2457 icu_locale->getKeywordValue(keyword, value, ULOC_FULLNAME_CAPACITY, status);
2458
2459 // Ignore failures in ICU and skip to the next keyword.
2460 //
2461 // This is fine.™
2462 if (U_FAILURE(status)) {
2463 status = U_ZERO_ERROR;
2464 continue;
2465 }
2466
2467 const char* bcp47_key = uloc_toUnicodeLocaleKey(keyword);
2468
2469 if (bcp47_key && (relevant_keys.find(bcp47_key) != relevant_keys.end())) {
2470 const char* bcp47_value = uloc_toUnicodeLocaleType(bcp47_key, value);
2471 bool is_valid_value = false;
2472 // 8.h.ii.1.a If keyLocaleData contains requestedValue, then
2473 if (strcmp("ca", bcp47_key) == 0) {
2474 is_valid_value = Intl::IsValidCalendar(*icu_locale, bcp47_value);
2475 } else if (strcmp("co", bcp47_key) == 0) {
2476 is_valid_value = Intl::IsValidCollation(*icu_locale, bcp47_value);
2477 } else if (strcmp("hc", bcp47_key) == 0) {
2478 // https://www.unicode.org/repos/cldr/tags/latest/common/bcp47/calendar.xml
2479 std::set<std::string> valid_values = {"h11", "h12", "h23", "h24"};
2480 is_valid_value = valid_values.find(bcp47_value) != valid_values.end();
2481 } else if (strcmp("lb", bcp47_key) == 0) {
2482 // https://www.unicode.org/repos/cldr/tags/latest/common/bcp47/segmentation.xml
2483 std::set<std::string> valid_values = {"strict", "normal", "loose"};
2484 is_valid_value = valid_values.find(bcp47_value) != valid_values.end();
2485 } else if (strcmp("kn", bcp47_key) == 0) {
2486 // https://www.unicode.org/repos/cldr/tags/latest/common/bcp47/collation.xml
2487 std::set<std::string> valid_values = {"true", "false"};
2488 is_valid_value = valid_values.find(bcp47_value) != valid_values.end();
2489 } else if (strcmp("kf", bcp47_key) == 0) {
2490 // https://www.unicode.org/repos/cldr/tags/latest/common/bcp47/collation.xml
2491 std::set<std::string> valid_values = {"upper", "lower", "false"};
2492 is_valid_value = valid_values.find(bcp47_value) != valid_values.end();
2493 } else if (strcmp("nu", bcp47_key) == 0) {
2494 is_valid_value = Intl::IsValidNumberingSystem(bcp47_value);
2495 }
2496 if (is_valid_value) {
2497 extensions.insert(
2498 std::pair<std::string, std::string>(bcp47_key, bcp47_value));
2499 builder.setUnicodeLocaleKeyword(bcp47_key, bcp47_value);
2500 }
2501 }
2502 }
2503
2504 status = U_ZERO_ERROR;
2505 *icu_locale = builder.build(status);
2506
2507 return extensions;
2508}
2509
2510// ecma402/#sec-lookupmatcher
2511std::string LookupMatcher(Isolate* isolate,
2512 const std::set<std::string>& available_locales,
2513 const std::vector<std::string>& requested_locales) {
2514 // 1. Let result be a new Record.
2515 std::string result;
2516
2517 // 2. For each element locale of requestedLocales in List order, do
2518 for (const std::string& locale : requested_locales) {
2519 // 2. a. Let noExtensionsLocale be the String value that is locale
2520 // with all Unicode locale extension sequences removed.
2521 ParsedLocale parsed_locale = ParseBCP47Locale(locale);
2522 std::string no_extensions_locale = parsed_locale.no_extensions_locale;
2523
2524 // 2. b. Let availableLocale be
2525 // BestAvailableLocale(availableLocales, noExtensionsLocale).
2526 std::string available_locale =
2527 BestAvailableLocale(available_locales, no_extensions_locale);
2528
2529 // 2. c. If availableLocale is not undefined, append locale to the
2530 // end of subset.
2531 if (!available_locale.empty()) {
2532 // Note: The following steps are not performed here because we
2533 // can use ICU to parse the unicode locale extension sequence
2534 // as part of Intl::ResolveLocale.
2535 //
2536 // There's no need to separate the unicode locale extensions
2537 // right here. Instead just return the available locale with the
2538 // extensions.
2539 //
2540 // 2. c. i. Set result.[[locale]] to availableLocale.
2541 // 2. c. ii. If locale and noExtensionsLocale are not the same
2542 // String value, then
2543 // 2. c. ii. 1. Let extension be the String value consisting of
2544 // the first substring of locale that is a Unicode locale
2545 // extension sequence.
2546 // 2. c. ii. 2. Set result.[[extension]] to extension.
2547 // 2. c. iii. Return result.
2548 return available_locale + parsed_locale.extension;
2549 }
2550 }
2551
2552 // 3. Let defLocale be DefaultLocale();
2553 // 4. Set result.[[locale]] to defLocale.
2554 // 5. Return result.
2555 return isolate->DefaultLocale();
2556}
2557
2558} // namespace
2559
2560// This function doesn't correspond exactly with the spec. Instead
2561// we use ICU to do all the string manipulations that the spec
2562// performs.
2563//
2564// The spec uses this function to normalize values for various
2565// relevant extension keys (such as disallowing "search" for
2566// collation). Instead of doing this here, we let the callers of
2567// this method perform such normalization.
2568//
2569// ecma402/#sec-resolvelocale
2571 Isolate* isolate, const std::set<std::string>& available_locales,
2572 const std::vector<std::string>& requested_locales, MatcherOption matcher,
2573 const std::set<std::string>& relevant_extension_keys) {
2574 std::string locale;
2575 if (matcher == Intl::MatcherOption::kBestFit &&
2576 v8_flags.harmony_intl_best_fit_matcher) {
2577 locale = BestFitMatcher(isolate, available_locales, requested_locales);
2578 } else {
2579 locale = LookupMatcher(isolate, available_locales, requested_locales);
2580 }
2581
2582 Maybe<icu::Locale> maybe_icu_locale = CreateICULocale(locale);
2583 MAYBE_RETURN(maybe_icu_locale, Nothing<Intl::ResolvedLocale>());
2584 icu::Locale icu_locale = maybe_icu_locale.FromJust();
2585 std::map<std::string, std::string> extensions =
2586 LookupAndValidateUnicodeExtensions(&icu_locale, relevant_extension_keys);
2587
2588 std::string canonicalized_locale = Intl::ToLanguageTag(icu_locale).FromJust();
2589
2590 // TODO(gsathya): Remove privateuse subtags from extensions.
2591
2592 return Just(
2593 Intl::ResolvedLocale{canonicalized_locale, icu_locale, extensions});
2594}
2595
2597 Isolate* isolate, DirectHandle<String> text,
2598 icu::BreakIterator* break_iterator) {
2599 text = String::Flatten(isolate, text);
2600 std::shared_ptr<icu::UnicodeString> u_text{static_cast<icu::UnicodeString*>(
2601 Intl::ToICUUnicodeString(isolate, text).clone())};
2602
2604 Managed<icu::UnicodeString>::From(isolate, 0, u_text);
2605
2606 break_iterator->setText(*u_text);
2607 return new_u_text;
2608}
2609
2610// ecma262 #sec-string.prototype.normalize
2612 DirectHandle<String> string,
2613 DirectHandle<Object> form_input) {
2614 const char* form_name;
2615 UNormalization2Mode form_mode;
2616 if (IsUndefined(*form_input, isolate)) {
2617 // default is FNC
2618 form_name = "nfc";
2619 form_mode = UNORM2_COMPOSE;
2620 } else {
2622 ASSIGN_RETURN_ON_EXCEPTION(isolate, form,
2623 Object::ToString(isolate, form_input));
2624
2625 if (String::Equals(isolate, form, isolate->factory()->NFC_string())) {
2626 form_name = "nfc";
2627 form_mode = UNORM2_COMPOSE;
2628 } else if (String::Equals(isolate, form,
2629 isolate->factory()->NFD_string())) {
2630 form_name = "nfc";
2631 form_mode = UNORM2_DECOMPOSE;
2632 } else if (String::Equals(isolate, form,
2633 isolate->factory()->NFKC_string())) {
2634 form_name = "nfkc";
2635 form_mode = UNORM2_COMPOSE;
2636 } else if (String::Equals(isolate, form,
2637 isolate->factory()->NFKD_string())) {
2638 form_name = "nfkc";
2639 form_mode = UNORM2_DECOMPOSE;
2640 } else {
2641 DirectHandle<String> valid_forms =
2642 isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD");
2644 isolate,
2645 NewRangeError(MessageTemplate::kNormalizationForm, valid_forms));
2646 }
2647 }
2648
2649 uint32_t length = string->length();
2650 string = String::Flatten(isolate, string);
2651 icu::UnicodeString result;
2652 std::unique_ptr<base::uc16[]> sap;
2653 UErrorCode status = U_ZERO_ERROR;
2654 icu::UnicodeString input = ToICUUnicodeString(isolate, string);
2655 // Getting a singleton. Should not free it.
2656 const icu::Normalizer2* normalizer =
2657 icu::Normalizer2::getInstance(nullptr, form_name, form_mode, status);
2658 DCHECK(U_SUCCESS(status));
2659 DCHECK_NOT_NULL(normalizer);
2660 uint32_t normalized_prefix_length =
2661 normalizer->spanQuickCheckYes(input, status);
2662 // Quick return if the input is already normalized.
2663 if (length == normalized_prefix_length) return string;
2664 icu::UnicodeString unnormalized =
2665 input.tempSubString(normalized_prefix_length);
2666 // Read-only alias of the normalized prefix.
2667 result.setTo(false, input.getBuffer(), normalized_prefix_length);
2668 // copy-on-write; normalize the suffix and append to |result|.
2669 normalizer->normalizeSecondAndAppend(result, unnormalized, status);
2670
2671 if (U_FAILURE(status)) {
2672 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError));
2673 }
2674
2675 return Intl::ToString(isolate, result);
2676}
2677
2678// ICUTimezoneCache calls out to ICU for TimezoneCache
2679// functionality in a straightforward way.
2681 public:
2683
2685
2686 const char* LocalTimezone(double time_ms) override;
2687
2688 double DaylightSavingsOffset(double time_ms) override;
2689
2690 double LocalTimeOffset(double time_ms, bool is_utc) override;
2691
2692 void Clear(TimeZoneDetection time_zone_detection) override;
2693
2694 private:
2695 icu::TimeZone* GetTimeZone();
2696
2697 bool GetOffsets(double time_ms, bool is_utc, int32_t* raw_offset,
2698 int32_t* dst_offset);
2699
2700 icu::TimeZone* timezone_;
2701
2702 std::string timezone_name_;
2704};
2705
2706const char* ICUTimezoneCache::LocalTimezone(double time_ms) {
2707 bool is_dst = DaylightSavingsOffset(time_ms) != 0;
2708 std::string* name = is_dst ? &dst_timezone_name_ : &timezone_name_;
2709 if (name->empty()) {
2710 icu::UnicodeString result;
2711 GetTimeZone()->getDisplayName(is_dst, icu::TimeZone::LONG, result);
2712 result += '\0';
2713
2714 icu::StringByteSink<std::string> byte_sink(name);
2715 result.toUTF8(byte_sink);
2716 }
2717 DCHECK(!name->empty());
2718 return name->c_str();
2719}
2720
2722 if (timezone_ == nullptr) {
2723 timezone_ = icu::TimeZone::createDefault();
2724 }
2725 return timezone_;
2726}
2727
2728bool ICUTimezoneCache::GetOffsets(double time_ms, bool is_utc,
2729 int32_t* raw_offset, int32_t* dst_offset) {
2730 UErrorCode status = U_ZERO_ERROR;
2731 if (is_utc) {
2732 GetTimeZone()->getOffset(time_ms, false, *raw_offset, *dst_offset, status);
2733 } else {
2734 // Note that casting TimeZone to BasicTimeZone is safe because we know that
2735 // icu::TimeZone used here is a BasicTimeZone.
2736 static_cast<const icu::BasicTimeZone*>(GetTimeZone())
2737 ->getOffsetFromLocal(time_ms, UCAL_TZ_LOCAL_FORMER,
2738 UCAL_TZ_LOCAL_FORMER, *raw_offset, *dst_offset,
2739 status);
2740 }
2741
2742 return U_SUCCESS(status);
2743}
2744
2746 int32_t raw_offset, dst_offset;
2747 if (!GetOffsets(time_ms, true, &raw_offset, &dst_offset)) return 0;
2748 return dst_offset;
2749}
2750
2751double ICUTimezoneCache::LocalTimeOffset(double time_ms, bool is_utc) {
2752 int32_t raw_offset, dst_offset;
2753 if (!GetOffsets(time_ms, is_utc, &raw_offset, &dst_offset)) return 0;
2754 return raw_offset + dst_offset;
2755}
2756
2758 delete timezone_;
2759 timezone_ = nullptr;
2760 timezone_name_.clear();
2761 dst_timezone_name_.clear();
2762 if (time_zone_detection == TimeZoneDetection::kRedetect) {
2763 icu::TimeZone::adoptDefault(icu::TimeZone::detectHostTimeZone());
2764 }
2765}
2766
2771
2773 Isolate* isolate, DirectHandle<JSReceiver> options,
2774 const char* method_name) {
2776 isolate, options, "localeMatcher", method_name, {"best fit", "lookup"},
2779}
2780
2783 const char* method_name,
2784 std::unique_ptr<char[]>* result) {
2785 const std::vector<const char*> empty_values = {};
2786 Maybe<bool> maybe = GetStringOption(isolate, options, "numberingSystem",
2787 empty_values, method_name, result);
2788 MAYBE_RETURN(maybe, Nothing<bool>());
2789 if (maybe.FromJust() && *result != nullptr) {
2790 if (!IsWellFormedNumberingSystem(result->get())) {
2792 isolate,
2793 NewRangeError(
2794 MessageTemplate::kInvalid,
2795 isolate->factory()->numberingSystem_string(),
2796 isolate->factory()->NewStringFromAsciiChecked(result->get())),
2797 Nothing<bool>());
2798 }
2799 return Just(true);
2800 }
2801 return Just(false);
2802}
2803
2804const std::set<std::string>& Intl::GetAvailableLocales() {
2805 static base::LazyInstance<Intl::AvailableLocales<>>::type available_locales =
2807 return available_locales.Pointer()->Get();
2808}
2809
2810namespace {
2811
2812struct CheckCalendar {
2813 static const char* key() { return "calendar"; }
2814 static const char* path() { return nullptr; }
2815};
2816
2817} // namespace
2818
2819const std::set<std::string>& Intl::GetAvailableLocalesForDateFormat() {
2821 available_locales = LAZY_INSTANCE_INITIALIZER;
2822 return available_locales.Pointer()->Get();
2823}
2824
2825constexpr uint16_t kInfinityChar = 0x221e;
2826
2828 const NumberFormatSpan& part,
2829 const icu::UnicodeString& text,
2830 bool is_nan) {
2831 switch (static_cast<UNumberFormatFields>(part.field_id)) {
2832 case UNUM_INTEGER_FIELD:
2833 if (is_nan) return isolate->factory()->nan_string();
2834 if (text.charAt(part.begin_pos) == kInfinityChar ||
2835 // en-US-POSIX output "INF" for Infinity
2836 (part.end_pos - part.begin_pos == 3 &&
2837 text.tempSubString(part.begin_pos, 3) == "INF")) {
2838 return isolate->factory()->infinity_string();
2839 }
2840 return isolate->factory()->integer_string();
2841 case UNUM_FRACTION_FIELD:
2842 return isolate->factory()->fraction_string();
2843 case UNUM_DECIMAL_SEPARATOR_FIELD:
2844 return isolate->factory()->decimal_string();
2845 case UNUM_GROUPING_SEPARATOR_FIELD:
2846 return isolate->factory()->group_string();
2847 case UNUM_CURRENCY_FIELD:
2848 return isolate->factory()->currency_string();
2849 case UNUM_PERCENT_FIELD:
2850 return isolate->factory()->percentSign_string();
2851 case UNUM_SIGN_FIELD:
2852 return (text.charAt(part.begin_pos) == '+')
2853 ? isolate->factory()->plusSign_string()
2854 : isolate->factory()->minusSign_string();
2855 case UNUM_EXPONENT_SYMBOL_FIELD:
2856 return isolate->factory()->exponentSeparator_string();
2857
2858 case UNUM_EXPONENT_SIGN_FIELD:
2859 return isolate->factory()->exponentMinusSign_string();
2860
2861 case UNUM_EXPONENT_FIELD:
2862 return isolate->factory()->exponentInteger_string();
2863
2864 case UNUM_PERMILL_FIELD:
2865 // We're not creating any permill formatter, and it's not even clear how
2866 // that would be possible with the ICU API.
2867 UNREACHABLE();
2868
2869 case UNUM_COMPACT_FIELD:
2870 return isolate->factory()->compact_string();
2871 case UNUM_MEASURE_UNIT_FIELD:
2872 return isolate->factory()->unit_string();
2873
2874 case UNUM_APPROXIMATELY_SIGN_FIELD:
2875 return isolate->factory()->approximatelySign_string();
2876
2877 default:
2878 UNREACHABLE();
2879 }
2880}
2881
2882// A helper function to convert the FormattedValue for several Intl objects.
2884 Isolate* isolate, const icu::FormattedValue& formatted) {
2885 UErrorCode status = U_ZERO_ERROR;
2886 icu::UnicodeString result = formatted.toString(status);
2887 if (U_FAILURE(status)) {
2888 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError));
2889 }
2890 return Intl::ToString(isolate, result);
2891}
2892
2894 Isolate* isolate, const char* unicode_key,
2895 icu::StringEnumeration* enumeration,
2896 const std::function<bool(const char*)>& removes, bool sort) {
2897 UErrorCode status = U_ZERO_ERROR;
2898 std::vector<std::string> array;
2899 for (const char* item = enumeration->next(nullptr, status);
2900 U_SUCCESS(status) && item != nullptr;
2901 item = enumeration->next(nullptr, status)) {
2902 if (unicode_key != nullptr) {
2903 item = uloc_toUnicodeLocaleType(unicode_key, item);
2904 }
2905 if (removes == nullptr || !(removes)(item)) {
2906 array.push_back(item);
2907 }
2908 }
2909
2910 if (sort) {
2911 std::sort(array.begin(), array.end());
2912 }
2913 return VectorToJSArray(isolate, array);
2914}
2915
2916bool Intl::RemoveCollation(const char* collation) {
2917 return strcmp("standard", collation) == 0 || strcmp("search", collation) == 0;
2918}
2919
2920// See the list in ecma402 #sec-issanctionedsimpleunitidentifier
2921std::set<std::string> Intl::SanctionedSimpleUnits() {
2922 return std::set<std::string>(
2923 {"acre", "bit", "byte", "celsius",
2924 "centimeter", "day", "degree", "fahrenheit",
2925 "fluid-ounce", "foot", "gallon", "gigabit",
2926 "gigabyte", "gram", "hectare", "hour",
2927 "inch", "kilobit", "kilobyte", "kilogram",
2928 "kilometer", "liter", "megabit", "megabyte",
2929 "meter", "microsecond", "mile", "mile-scandinavian",
2930 "millimeter", "milliliter", "millisecond", "minute",
2931 "month", "nanosecond", "ounce", "percent",
2932 "petabyte", "pound", "second", "stone",
2933 "terabit", "terabyte", "week", "yard",
2934 "year"});
2935}
2936
2937// ecma-402/#sec-isvalidtimezonename
2938
2939namespace {
2940bool IsUnicodeStringValidTimeZoneName(const icu::UnicodeString& id) {
2941 UErrorCode status = U_ZERO_ERROR;
2942 icu::UnicodeString canonical;
2943 icu::TimeZone::getCanonicalID(id, canonical, status);
2944 return U_SUCCESS(status) &&
2945 canonical != icu::UnicodeString("Etc/Unknown", -1, US_INV);
2946}
2947} // namespace
2948
2951 UErrorCode status = U_ZERO_ERROR;
2952 std::string time_zone =
2954 icu::UnicodeString time_zone_ustring =
2955 icu::UnicodeString(time_zone.c_str(), -1, US_INV);
2956 icu::UnicodeString canonical;
2957 icu::TimeZone::getCanonicalID(time_zone_ustring, canonical, status);
2958 CHECK(U_SUCCESS(status));
2959
2960 return JSDateTimeFormat::TimeZoneIdToString(isolate, canonical);
2961}
2962
2964 std::string time_zone =
2965 JSDateTimeFormat::CanonicalizeTimeZoneID(id->ToCString().get());
2966 icu::UnicodeString time_zone_ustring =
2967 icu::UnicodeString(time_zone.c_str(), -1, US_INV);
2968 return IsUnicodeStringValidTimeZoneName(time_zone_ustring);
2969}
2970
2971bool Intl::IsValidTimeZoneName(const icu::TimeZone& tz) {
2972 icu::UnicodeString id;
2973 tz.getID(id);
2974 return IsUnicodeStringValidTimeZoneName(id);
2975}
2976
2977// Function to support Temporal
2978std::string Intl::TimeZoneIdFromIndex(int32_t index) {
2980 return "UTC";
2981 }
2982 std::unique_ptr<icu::StringEnumeration> enumeration(
2983 icu::TimeZone::createEnumeration());
2984 int32_t curr = 0;
2985 const char* id;
2986
2987 UErrorCode status = U_ZERO_ERROR;
2988 while (U_SUCCESS(status) && curr < index &&
2989 ((id = enumeration->next(nullptr, status)) != nullptr)) {
2990 CHECK(U_SUCCESS(status));
2991 curr++;
2992 }
2993 CHECK(U_SUCCESS(status));
2994 CHECK(id != nullptr);
2995 return id;
2996}
2997
3000 if (identifier->Equals(*isolate->factory()->UTC_string())) {
3001 return 0;
3002 }
3003
3004 std::string identifier_str(identifier->ToCString().get());
3005 std::unique_ptr<icu::TimeZone> tz(
3006 icu::TimeZone::createTimeZone(identifier_str.c_str()));
3007 if (!IsValidTimeZoneName(*tz)) {
3008 return -1;
3009 }
3010
3011 std::unique_ptr<icu::StringEnumeration> enumeration(
3012 icu::TimeZone::createEnumeration());
3013 int32_t curr = 0;
3014 const char* id;
3015
3016 UErrorCode status = U_ZERO_ERROR;
3017 while (U_SUCCESS(status) &&
3018 (id = enumeration->next(nullptr, status)) != nullptr) {
3019 curr++;
3020 if (identifier_str == id) {
3021 return curr;
3022 }
3023 }
3024 CHECK(U_SUCCESS(status));
3025 // We should not reach here, the !IsValidTimeZoneName should return earlier
3026 UNREACHABLE();
3027}
3028
3032
3033void Intl::FormatRangeSourceTracker::Add(int32_t field, int32_t start,
3034 int32_t limit) {
3035 DCHECK_LT(field, 2);
3036 start_[field] = start;
3037 limit_[field] = limit;
3038}
3039
3041 int32_t start, int32_t limit) const {
3043 if (FieldContains(0, start, limit)) {
3045 } else if (FieldContains(1, start, limit)) {
3047 }
3048 return source;
3049}
3050
3052 int32_t limit) const {
3053 DCHECK_LT(field, 2);
3054 return (start_[field] <= start) && (start <= limit_[field]) &&
3055 (start_[field] <= limit) && (limit <= limit_[field]);
3056}
3057
3059 FormatRangeSource source) {
3060 switch (source) {
3062 return isolate->factory()->shared_string();
3064 return isolate->factory()->startRange_string();
3066 return isolate->factory()->endRange_string();
3067 }
3068}
3069
3071 icu::UnicodeString id;
3072 {
3073 std::unique_ptr<icu::TimeZone> tz(icu::TimeZone::createDefault());
3074 tz->getID(id);
3075 }
3076 UErrorCode status = U_ZERO_ERROR;
3077 icu::UnicodeString canonical;
3078 icu::TimeZone::getCanonicalID(id, canonical, status);
3079 DCHECK(U_SUCCESS(status));
3080 return JSDateTimeFormat::TimeZoneIdToString(isolate, canonical)
3081 .ToHandleChecked();
3082}
3083
3084namespace {
3085
3086const icu::BasicTimeZone* CreateBasicTimeZoneFromIndex(
3087 int32_t time_zone_index) {
3088 DCHECK_NE(time_zone_index, 0);
3089 return static_cast<const icu::BasicTimeZone*>(
3090 icu::TimeZone::createTimeZone(icu::UnicodeString(
3091 Intl::TimeZoneIdFromIndex(time_zone_index).c_str(), -1, US_INV)));
3092}
3093
3094// ICU only support TimeZone information in millisecond but Temporal require
3095// nanosecond. For most of the case, we find an approximate millisecond by
3096// floor to the millisecond just past the nanosecond_epoch. For negative epoch
3097// value, the BigInt Divide will floor closer to zero so we need to minus 1 if
3098// the remainder is not zero. For the case of finding previous transition, we
3099// need to ceil to the millisecond in the near future of the nanosecond_epoch.
3100enum class Direction { kPast, kFuture };
3101int64_t ApproximateMillisecondEpoch(Isolate* isolate,
3102 DirectHandle<BigInt> nanosecond_epoch,
3103 Direction direction = Direction::kPast) {
3104 DirectHandle<BigInt> one_million = BigInt::FromUint64(isolate, 1000000);
3105 int64_t ms = BigInt::Divide(isolate, nanosecond_epoch, one_million)
3106 .ToHandleChecked()
3107 ->AsInt64();
3108 DirectHandle<BigInt> remainder =
3109 BigInt::Remainder(isolate, nanosecond_epoch, one_million)
3110 .ToHandleChecked();
3111 // If the nanosecond_epoch is not on the exact millisecond
3112 if (remainder->ToBoolean()) {
3113 if (direction == Direction::kPast) {
3114 if (remainder->IsNegative()) {
3115 // If the remaninder is negative, we know we have an negative epoch
3116 // We need to decrease one millisecond.
3117 // Move to the previous millisecond
3118 ms -= 1;
3119 }
3120 } else {
3121 if (!remainder->IsNegative()) {
3122 // Move to the future millisecond
3123 ms += 1;
3124 }
3125 }
3126 }
3127 return ms;
3128}
3129
3130// Helper function to convert the milliseconds in int64_t
3131// to a BigInt in nanoseconds.
3132DirectHandle<BigInt> MillisecondToNanosecond(Isolate* isolate, int64_t ms) {
3133 return BigInt::Multiply(isolate, BigInt::FromInt64(isolate, ms),
3134 BigInt::FromUint64(isolate, 1000000))
3135 .ToHandleChecked();
3136}
3137
3138} // namespace
3139
3141 Isolate* isolate, int32_t time_zone_index,
3142 DirectHandle<BigInt> nanosecond_epoch, Intl::Transition transition) {
3143 std::unique_ptr<const icu::BasicTimeZone> basic_time_zone(
3144 CreateBasicTimeZoneFromIndex(time_zone_index));
3145
3146 icu::TimeZoneTransition icu_transition;
3147 UBool has_transition;
3148 switch (transition) {
3150 has_transition = basic_time_zone->getNextTransition(
3151 ApproximateMillisecondEpoch(isolate, nanosecond_epoch), false,
3152 icu_transition);
3153 break;
3155 has_transition = basic_time_zone->getPreviousTransition(
3156 ApproximateMillisecondEpoch(isolate, nanosecond_epoch,
3157 Direction::kFuture),
3158 false, icu_transition);
3159 break;
3160 }
3161
3162 if (!has_transition) {
3163 return isolate->factory()->null_value();
3164 }
3165 // #sec-temporal-getianatimezonenexttransition and
3166 // #sec-temporal-getianatimezoneprevioustransition states:
3167 // "The operation returns null if no such transition exists for which t ≤
3168 // ℤ(nsMaxInstant)." and "The operation returns null if no such transition
3169 // exists for which t ≥ ℤ(nsMinInstant)."
3170 //
3171 // nsMinInstant = -nsMaxInstant = -8.64 Ă— 10^21 => msMinInstant = -8.64 x
3172 // 10^15
3173 constexpr int64_t kMsMinInstant = -8.64e15;
3174 // nsMaxInstant = 10^8 Ă— nsPerDay = 8.64 Ă— 10^21 => msMaxInstant = 8.64 x
3175 // 10^15
3176 constexpr int64_t kMsMaxInstant = 8.64e15;
3177 int64_t time_ms = static_cast<int64_t>(icu_transition.getTime());
3178 if (time_ms < kMsMinInstant || time_ms > kMsMaxInstant) {
3179 return isolate->factory()->null_value();
3180 }
3181 return MillisecondToNanosecond(isolate, time_ms);
3182}
3183
3185 Isolate* isolate, int32_t time_zone_index,
3186 DirectHandle<BigInt> nanosecond_epoch) {
3187 std::unique_ptr<const icu::BasicTimeZone> basic_time_zone(
3188 CreateBasicTimeZoneFromIndex(time_zone_index));
3189 int64_t time_ms = ApproximateMillisecondEpoch(isolate, nanosecond_epoch);
3190 int32_t raw_offset;
3191 int32_t dst_offset;
3192 UErrorCode status = U_ZERO_ERROR;
3193 basic_time_zone->getOffsetFromLocal(time_ms, UCAL_TZ_LOCAL_FORMER,
3194 UCAL_TZ_LOCAL_FORMER, raw_offset,
3195 dst_offset, status);
3196 DCHECK(U_SUCCESS(status));
3197 // offset for time_ms interpreted as before a time zone
3198 // transition
3199 int64_t offset_former = raw_offset + dst_offset;
3200
3201 basic_time_zone->getOffsetFromLocal(time_ms, UCAL_TZ_LOCAL_LATTER,
3202 UCAL_TZ_LOCAL_LATTER, raw_offset,
3203 dst_offset, status);
3204 DCHECK(U_SUCCESS(status));
3205 // offset for time_ms interpreted as after a time zone
3206 // transition
3207 int64_t offset_latter = raw_offset + dst_offset;
3208
3210 if (offset_former == offset_latter) {
3211 // For most of the time, when either interpretation are the same, we are not
3212 // in a moment of offset transition based on rule changing: Just return that
3213 // value.
3214 result.push_back(MillisecondToNanosecond(isolate, offset_former));
3215 } else if (offset_former > offset_latter) {
3216 // When the input represents a local time repeating multiple times at a
3217 // negative time zone transition (e.g. when the daylight saving time ends
3218 // or the time zone offset is decreased due to a time zone rule change).
3219 result.push_back(MillisecondToNanosecond(isolate, offset_former));
3220 result.push_back(MillisecondToNanosecond(isolate, offset_latter));
3221 } else {
3222 // If the offset after the transition is greater than the offset before the
3223 // transition, that mean it is in the moment the time "skip" an hour, or two
3224 // (or six in a Time Zone in south pole) in that case there are no possible
3225 // Time Zone offset for that moment and nothing will be added to the result.
3226 }
3227 return result;
3228}
3229
3231 Isolate* isolate, int32_t time_zone_index,
3232 DirectHandle<BigInt> nanosecond_epoch) {
3233 std::unique_ptr<const icu::BasicTimeZone> basic_time_zone(
3234 CreateBasicTimeZoneFromIndex(time_zone_index));
3235 int64_t time_ms = ApproximateMillisecondEpoch(isolate, nanosecond_epoch);
3236 int32_t raw_offset;
3237 int32_t dst_offset;
3238 UErrorCode status = U_ZERO_ERROR;
3239 basic_time_zone->getOffset(time_ms, false, raw_offset, dst_offset, status);
3240 DCHECK(U_SUCCESS(status));
3241 // Turn ms into ns
3242 return static_cast<int64_t>(raw_offset + dst_offset) * 1000000;
3243}
3244
3245} // namespace v8::internal
#define SLOW_DCHECK(condition)
Definition checks.h:21
SourcePosition pos
V8_WARN_UNUSED_RESULT V8_INLINE bool To(T *out) const
Definition v8-maybe.h:55
V8_INLINE T FromJust() const &
Definition v8-maybe.h:64
static TimezoneCache * CreateTimezoneCache()
constexpr T * begin() const
Definition vector.h:96
static MaybeHandle< BigInt > Remainder(Isolate *isolate, DirectHandle< BigInt > x, DirectHandle< BigInt > y)
Definition bigint.cc:488
static MaybeHandle< BigInt > Multiply(Isolate *isolate, DirectHandle< BigInt > x, DirectHandle< BigInt > y)
Definition bigint.cc:432
static MaybeHandle< BigInt > Divide(Isolate *isolate, DirectHandle< BigInt > x, DirectHandle< BigInt > y)
Definition bigint.cc:454
static V8_EXPORT_PRIVATE Handle< BigInt > FromUint64(Isolate *isolate, uint64_t n)
Definition bigint.cc:1355
static V8_EXPORT_PRIVATE Handle< BigInt > FromInt64(Isolate *isolate, int64_t n)
Definition bigint.cc:1333
V8_INLINE bool is_identical_to(Handle< S > other) const
Definition handles.h:716
Handle< JSArray > NewJSArray(ElementsKind elements_kind, int length, int capacity, ArrayStorageAllocationMode mode=ArrayStorageAllocationMode::DONT_INITIALIZE_ARRAY_ELEMENTS, AllocationType allocation=AllocationType::kYoung)
Definition factory.cc:3211
Handle< JSObject > NewJSObject(DirectHandle< JSFunction > constructor, AllocationType allocation=AllocationType::kYoung, NewJSObjectType=NewJSObjectType::kNoAPIWrapper)
Definition factory.cc:2985
Handle< String > NewStringFromStaticChars(const char(&str)[N], AllocationType allocation=AllocationType::kYoung)
Definition factory-inl.h:79
void Clear(TimeZoneDetection time_zone_detection) override
double DaylightSavingsOffset(double time_ms) override
bool GetOffsets(double time_ms, bool is_utc, int32_t *raw_offset, int32_t *dst_offset)
const char * LocalTimezone(double time_ms) override
double LocalTimeOffset(double time_ms, bool is_utc) override
bool FieldContains(int32_t field, int32_t start, int32_t limit) const
FormatRangeSource GetSource(int32_t start, int32_t limit) const
void Add(int32_t field, int32_t start, int32_t limit)
static void AddElement(Isolate *isolate, DirectHandle< JSArray > array, int index, DirectHandle< String > field_type_string, DirectHandle< String > value)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > NumberToLocaleString(Isolate *isolate, Handle< Object > num, DirectHandle< Object > locales, DirectHandle< Object > options, const char *method_name)
static base::TimezoneCache * CreateTimeZoneCache()
static bool IsValidCalendar(const icu::Locale &locale, const std::string &value)
static bool IsWellFormedCurrency(const std::string &value)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > StringLocaleConvertCase(Isolate *isolate, DirectHandle< String > s, bool is_upper, DirectHandle< Object > locales)
static std::string GetNumberingSystem(const icu::Locale &icu_locale)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< Object > LegacyUnwrapReceiver(Isolate *isolate, DirectHandle< JSReceiver > receiver, DirectHandle< JSFunction > constructor, bool has_initialized_slot)
static const std::set< std::string > & GetAvailableLocales()
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT int CompareStrings(Isolate *isolate, const icu::Collator &collator, DirectHandle< String > s1, DirectHandle< String > s2, CompareStringsOptions compare_strings_options=CompareStringsOptions::kNone)
static V8_WARN_UNUSED_RESULT MaybeHandle< String > ToString(Isolate *isolate, const icu::UnicodeString &string)
static V8_WARN_UNUSED_RESULT MaybeHandle< String > ConvertToLower(Isolate *isolate, DirectHandle< String > s)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > Normalize(Isolate *isolate, DirectHandle< String > string, DirectHandle< Object > form_input)
static const int kAsciiCollationWeightsLength
static DirectHandleVector< BigInt > GetTimeZonePossibleOffsetNanoseconds(Isolate *isolate, int32_t time_zone_index, DirectHandle< BigInt > nanosecond_epoch)
static int32_t GetTimeZoneIndex(Isolate *isolate, DirectHandle< String > identifier)
static Tagged< String > ConvertOneByteToLower(Tagged< String > src, Tagged< String > dst)
static bool RemoveCollation(const char *collation)
static Maybe< std::vector< std::string > > CanonicalizeLocaleList(Isolate *isolate, DirectHandle< Object > locales, bool only_return_one_result=false)
static DirectHandle< Object > GetTimeZoneOffsetTransitionNanoseconds(Isolate *isolate, int32_t time_zone_index, DirectHandle< BigInt > nanosecond_epoch, Transition transition)
static bool IsValidCollation(const icu::Locale &locale, const std::string &value)
static const std::set< std::string > & GetAvailableLocalesForDateFormat()
static DirectHandle< String > DefaultTimeZone(Isolate *isolate)
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)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSArray > ToJSArray(Isolate *isolate, const char *unicode_key, icu::StringEnumeration *enumeration, const std::function< bool(const char *)> &removes, bool sort)
static std::set< std::string > SanctionedSimpleUnits()
static const uint8_t * ToLatin1LowerTable()
static V8_WARN_UNUSED_RESULT std::optional< int > StringLocaleCompare(Isolate *isolate, DirectHandle< String > s1, DirectHandle< String > s2, DirectHandle< Object > locales, DirectHandle< Object > options, const char *method_name)
static icu::UnicodeString ToICUUnicodeString(Isolate *isolate, DirectHandle< String > string, int offset=0)
static V8_EXPORT_PRIVATE CompareStringsOptions CompareStringsOptionsFor(IsolateT *isolate, DirectHandle< Object > locales, DirectHandle< Object > options)
static V8_WARN_UNUSED_RESULT bool IsValidTimeZoneName(const icu::TimeZone &tz)
static std::set< std::string > BuildLocaleSet(const std::vector< std::string > &locales, const char *path, const char *validate_key)
static V8_WARN_UNUSED_RESULT std::string TimeZoneIdFromIndex(int32_t index)
static V8_WARN_UNUSED_RESULT MaybeHandle< String > CanonicalizeTimeZoneName(Isolate *isolate, DirectHandle< String > identifier)
static int64_t GetTimeZoneOffsetNanoseconds(Isolate *isolate, int32_t time_zone_index, DirectHandle< BigInt > nanosecond_epoch)
static V8_WARN_UNUSED_RESULT Maybe< MatcherOption > GetLocaleMatcher(Isolate *isolate, DirectHandle< JSReceiver > options, const char *method_name)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSArray > AvailableCalendars(Isolate *isolate)
Definition js-locale.cc:544
static bool IsWellFormedCalendar(const std::string &value)
static Maybe< std::string > ToLanguageTag(const icu::Locale &locale)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > FormattedToString(Isolate *isolate, const icu::FormattedValue &formatted)
static V8_WARN_UNUSED_RESULT Maybe< NumberFormatDigitOptions > SetNumberFormatDigitOptions(Isolate *isolate, DirectHandle< JSReceiver > options, int mnfd_default, int mxfd_default, bool notation_is_compact, const char *service)
static const uint8_t * AsciiCollationWeightsL1()
static bool IsValidNumberingSystem(const std::string &value)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSArray > GetCanonicalLocales(Isolate *isolate, DirectHandle< Object > locales)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > ConvertToUpper(Isolate *isolate, DirectHandle< String > s)
static DirectHandle< String > SourceString(Isolate *isolate, FormatRangeSource source)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSArray > SupportedValuesOf(Isolate *isolate, DirectHandle< Object > key)
static DirectHandle< Managed< icu::UnicodeString > > SetTextToBreakIterator(Isolate *isolate, DirectHandle< String > text, icu::BreakIterator *break_iterator)
static const uint8_t * AsciiCollationWeightsL3()
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)
static DirectHandle< String > NumberFieldToType(Isolate *isolate, const NumberFormatSpan &part, const icu::UnicodeString &text, bool is_nan)
static V8_WARN_UNUSED_RESULT MaybeHandle< String > TimeZoneIdToString(Isolate *isolate, const icu::UnicodeString &id)
static V8_EXPORT_PRIVATE std::string CanonicalizeTimeZoneID(const std::string &input)
static V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT MaybeHandle< Map > GetDerivedMap(Isolate *isolate, DirectHandle< JSFunction > constructor, DirectHandle< JSReceiver > new_target)
static bool Is38AlphaNumList(const std::string &value)
Definition js-locale.cc:206
static bool StartsWithUnicodeLanguageId(const std::string &value)
Definition js-locale.cc:224
static DirectHandle< String > ToString(Isolate *isolate, DirectHandle< JSLocale > locale)
Definition js-locale.cc:852
static bool Is3Alpha(const std::string &value)
Definition js-locale.cc:218
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > FormatNumeric(Isolate *isolate, const icu::number::LocalizedNumberFormatter &number_format, Handle< Object > numeric_obj)
static V8_EXPORT_PRIVATE void AddProperty(Isolate *isolate, DirectHandle< JSObject > object, DirectHandle< Name > name, DirectHandle< Object > value, PropertyAttributes attributes)
static V8_EXPORT_PRIVATE Maybe< bool > AddDataElement(DirectHandle< JSObject > receiver, uint32_t index, DirectHandle< Object > value, PropertyAttributes attributes)
static V8_WARN_UNUSED_RESULT MaybeHandle< Object > GetProperty(Isolate *isolate, DirectHandle< JSReceiver > receiver, const char *key)
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT Maybe< bool > HasProperty(LookupIterator *it)
Definition js-objects.cc:98
static constexpr int32_t kUTCTimeZoneIndex
static DirectHandle< Managed< CppType > > From(Isolate *isolate, size_t estimated_size, std::shared_ptr< CppType > shared_ptr, AllocationType allocation_type=AllocationType::kYoung)
Definition managed-inl.h:27
V8_WARN_UNUSED_RESULT V8_INLINE bool ToHandle(DirectHandle< S > *out) const
static V8_WARN_UNUSED_RESULT HandleType< String >::MaybeType ToString(Isolate *isolate, HandleType< T > input)
static V8_WARN_UNUSED_RESULT MaybeHandle< Object > OrdinaryHasInstance(Isolate *isolate, DirectHandle< JSAny > callable, DirectHandle< JSAny > object)
Definition objects.cc:1045
static V8_EXPORT_PRIVATE bool BooleanValue(Tagged< Object > obj, IsolateT *isolate)
static V8_WARN_UNUSED_RESULT HandleType< JSReceiver >::MaybeType ToObject(Isolate *isolate, HandleType< T > object, const char *method_name=nullptr)
static double NumberValue(Tagged< Number > obj)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< Object > GetLengthFromArrayLike(Isolate *isolate, DirectHandle< JSReceiver > object)
Definition objects.cc:1238
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT MaybeHandle< Object > GetProperty(LookupIterator *it, bool is_global_reference=false)
Definition objects.cc:1248
static V8_WARN_UNUSED_RESULT HandleType< Object >::MaybeType ToNumeric(Isolate *isolate, HandleType< T > input)
static V8_WARN_UNUSED_RESULT Handle< String > Truncate(Isolate *isolate, Handle< SeqString > string, uint32_t new_length)
Definition string.cc:1924
static bool IsNeeded(Tagged< String > str, LocalIsolate *local_isolate)
Definition string-inl.h:76
bool JsHasOverflowed(uintptr_t gap=0) const
Definition isolate.cc:7296
base::Vector< const uint8_t > ToOneByteVector() const
Definition string.h:139
base::Vector< const base::uc16 > ToUC16Vector() const
Definition string.h:145
static V8_INLINE HandleType< String > Flatten(Isolate *isolate, HandleType< T > string, AllocationType allocation=AllocationType::kYoung)
static bool IsAscii(const char *chars, uint32_t length)
Definition string.h:575
bool Equals(Tagged< String > other) const
Definition string-inl.h:535
void push_back(const T &value)
Handle< Code > code
uint8_t *const start_
Definition assembler.cc:131
const v8::base::TimeTicks end_
Definition sweeper.cc:54
int start
int end
#define THROW_NEW_ERROR_RETURN_VALUE(isolate, call, value)
Definition isolate.h:300
#define ASSIGN_RETURN_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:291
#define THROW_NEW_ERROR(isolate, call)
Definition isolate.h:307
#define ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, value)
Definition isolate.h:276
#define MAYBE_RETURN(call, value)
Definition isolate.h:408
int32_t offset
#define XSTR(s)
std::string extension
UCollationResult l1_result
bool has_diff
std::string no_extensions_locale
std::vector< std::string > list_
UCollationResult l3_result
icu::Locale locale_
std::vector< std::string >::const_iterator iter_
int first_diff_at
int processed_until
#define V8_MINIMUM_ICU_VERSION
const int limit_
Definition isolate.cc:1114
TNode< Object > receiver
ArrayReduceDirection direction
std::map< const std::string, const std::string > map
icu::number::FormattedNumber formatted
double remainder
TimeZoneRecord time_zone
ZoneVector< RpoNumber > & result
#define LAZY_INSTANCE_INITIALIZER
InstructionOperand source
int r
Definition mul-fft.cc:298
int int32_t
Definition unicode.cc:40
uint16_t uc16
Definition strings.h:18
Vector< const char > CStrVector(const char *data)
Definition vector.h:331
V8_INLINE const Operation & Get(const Graph &graph, OpIndex index)
Definition graph.h:1231
Node::Uses::const_iterator begin(const Node::Uses &uses)
Definition node.h:708
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
template uint32_t FastAsciiConvert< unibrow::ToLowercase >(char *dst, const char *src, uint32_t length)
template uint32_t FastAsciiCasePrefixLength< unibrow::ToUppercase >(const char *src, uint32_t length)
PerThreadAssertScopeDebugOnly< false, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > DisallowGarbageCollection
constexpr int kMaxUInt8
Definition globals.h:378
Tagged(T object) -> Tagged< T >
constexpr bool IsAsciiLower(base::uc32 c)
Maybe< int > DefaultNumberOption(Isolate *isolate, DirectHandle< Object > value, int min, int max, int fallback, DirectHandle< String > property)
V8_INLINE IndirectHandle< T > indirect_handle(DirectHandle< T > handle)
Definition handles.h:757
template uint32_t FastAsciiConvert< unibrow::ToUppercase >(char *dst, const char *src, uint32_t length)
refactor address components for immediate indexing make OptimizeMaglevOnNextCall optimize to turbofan instead of maglev filter for tracing turbofan compilation trace turbo cfg trace TurboFan s graph trimmer trace TurboFan s control equivalence trace TurboFan s register allocator trace stack load store counters for optimized code in run fuzzing &&concurrent_recompilation trace_turbo trace_turbo_scheduled trace_turbo_stack_accesses verify TurboFan machine graph of code stubs enable FixedArray bounds checks print TurboFan statistics of wasm compilations maximum cumulative size of bytecode considered for inlining scale factor of bytecode size used to calculate the inlining budget * KB
Definition flags.cc:1366
void CopyChars(DstType *dst, const SrcType *src, size_t count) V8_NONNULL(1
V8_EXPORT_PRIVATE FlagValues v8_flags
constexpr base::uc32 ToAsciiUpper(base::uc32 c)
constexpr bool IsAsciiUpper(base::uc32 c)
Maybe< int > GetNumberOption(Isolate *isolate, DirectHandle< JSReceiver > options, DirectHandle< String > property, int min, int max, int fallback)
MaybeDirectHandle< JSReceiver > CoerceOptionsToObject(Isolate *isolate, DirectHandle< Object > options, const char *method_name)
Maybe< bool > GetStringOption(Isolate *isolate, DirectHandle< JSReceiver > options, const char *property, const std::vector< const char * > &values, const char *method_name, std::unique_ptr< char[]> *result)
constexpr uint32_t kMaxUInt32
Definition globals.h:387
T * NewArray(size_t size)
Definition allocation.h:43
constexpr uint16_t kInfinityChar
template const char * string
constexpr base::uc32 ToAsciiLower(base::uc32 c)
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
char16_t UChar
Definition string-16.h:22
Local< T > Handle
Maybe< T > Nothing()
Definition v8-maybe.h:112
Maybe< T > Just(const T &t)
Definition v8-maybe.h:117
#define FATAL(...)
Definition logging.h:47
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
#define USE(...)
Definition macros.h:293
#define arraysize(array)
Definition macros.h:67
typename LazyStaticInstance< T, CreateTrait, InitOnceTrait, DestroyTrait >::type type
Symbol identifier
#define V8_LIKELY(condition)
Definition v8config.h:661
#define V8_UNLIKELY(condition)
Definition v8config.h:660