• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 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 
5 #ifndef V8_INTL_SUPPORT
6 #error Internationalization is expected to be enabled.
7 #endif  // V8_INTL_SUPPORT
8 
9 #include "src/objects/js-collator.h"
10 
11 #include "src/execution/isolate.h"
12 #include "src/objects/js-collator-inl.h"
13 #include "src/objects/js-locale.h"
14 #include "src/objects/managed-inl.h"
15 #include "src/objects/objects-inl.h"
16 #include "src/objects/option-utils.h"
17 #include "unicode/coll.h"
18 #include "unicode/locid.h"
19 #include "unicode/strenum.h"
20 #include "unicode/ucol.h"
21 #include "unicode/udata.h"
22 #include "unicode/uloc.h"
23 #include "unicode/utypes.h"
24 
25 namespace v8 {
26 namespace internal {
27 
28 namespace {
29 
30 enum class Usage {
31   SORT,
32   SEARCH,
33 };
34 
35 enum class Sensitivity {
36   kBase,
37   kAccent,
38   kCase,
39   kVariant,
40   kUndefined,
41 };
42 
43 // enum for "caseFirst" option.
44 enum class CaseFirst { kUndefined, kUpper, kLower, kFalse };
45 
GetCaseFirst(Isolate * isolate,Handle<JSReceiver> options,const char * method_name)46 Maybe<CaseFirst> GetCaseFirst(Isolate* isolate, Handle<JSReceiver> options,
47                               const char* method_name) {
48   return GetStringOption<CaseFirst>(
49       isolate, options, "caseFirst", method_name, {"upper", "lower", "false"},
50       {CaseFirst::kUpper, CaseFirst::kLower, CaseFirst::kFalse},
51       CaseFirst::kUndefined);
52 }
53 
54 // TODO(gsathya): Consider internalizing the value strings.
CreateDataPropertyForOptions(Isolate * isolate,Handle<JSObject> options,Handle<String> key,const char * value)55 void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options,
56                                   Handle<String> key, const char* value) {
57   DCHECK_NOT_NULL(value);
58   Handle<String> value_str =
59       isolate->factory()->NewStringFromAsciiChecked(value);
60 
61   // This is a brand new JSObject that shouldn't already have the same
62   // key so this shouldn't fail.
63   Maybe<bool> maybe = JSReceiver::CreateDataProperty(
64       isolate, options, key, value_str, Just(kDontThrow));
65   DCHECK(maybe.FromJust());
66   USE(maybe);
67 }
68 
CreateDataPropertyForOptions(Isolate * isolate,Handle<JSObject> options,Handle<String> key,bool value)69 void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options,
70                                   Handle<String> key, bool value) {
71   Handle<Object> value_obj = isolate->factory()->ToBoolean(value);
72 
73   // This is a brand new JSObject that shouldn't already have the same
74   // key so this shouldn't fail.
75   Maybe<bool> maybe = JSReceiver::CreateDataProperty(
76       isolate, options, key, value_obj, Just(kDontThrow));
77   DCHECK(maybe.FromJust());
78   USE(maybe);
79 }
80 
81 }  // anonymous namespace
82 
83 // static
ResolvedOptions(Isolate * isolate,Handle<JSCollator> collator)84 Handle<JSObject> JSCollator::ResolvedOptions(Isolate* isolate,
85                                              Handle<JSCollator> collator) {
86   Handle<JSObject> options =
87       isolate->factory()->NewJSObject(isolate->object_function());
88 
89   icu::Collator* icu_collator = collator->icu_collator().raw();
90   DCHECK_NOT_NULL(icu_collator);
91 
92   UErrorCode status = U_ZERO_ERROR;
93   bool numeric =
94       icu_collator->getAttribute(UCOL_NUMERIC_COLLATION, status) == UCOL_ON;
95   DCHECK(U_SUCCESS(status));
96 
97   const char* case_first = nullptr;
98   status = U_ZERO_ERROR;
99   switch (icu_collator->getAttribute(UCOL_CASE_FIRST, status)) {
100     case UCOL_LOWER_FIRST:
101       case_first = "lower";
102       break;
103     case UCOL_UPPER_FIRST:
104       case_first = "upper";
105       break;
106     default:
107       case_first = "false";
108   }
109   DCHECK(U_SUCCESS(status));
110 
111   const char* sensitivity = nullptr;
112   status = U_ZERO_ERROR;
113   switch (icu_collator->getAttribute(UCOL_STRENGTH, status)) {
114     case UCOL_PRIMARY: {
115       DCHECK(U_SUCCESS(status));
116       status = U_ZERO_ERROR;
117       // case level: true + s1 -> case, s1 -> base.
118       if (UCOL_ON == icu_collator->getAttribute(UCOL_CASE_LEVEL, status)) {
119         sensitivity = "case";
120       } else {
121         sensitivity = "base";
122       }
123       DCHECK(U_SUCCESS(status));
124       break;
125     }
126     case UCOL_SECONDARY:
127       sensitivity = "accent";
128       break;
129     case UCOL_TERTIARY:
130       sensitivity = "variant";
131       break;
132     case UCOL_QUATERNARY:
133       // We shouldn't get quaternary and identical from ICU, but if we do
134       // put them into variant.
135       sensitivity = "variant";
136       break;
137     default:
138       sensitivity = "variant";
139   }
140   DCHECK(U_SUCCESS(status));
141 
142   status = U_ZERO_ERROR;
143   bool ignore_punctuation = icu_collator->getAttribute(UCOL_ALTERNATE_HANDLING,
144                                                        status) == UCOL_SHIFTED;
145   DCHECK(U_SUCCESS(status));
146 
147   status = U_ZERO_ERROR;
148 
149   icu::Locale icu_locale(icu_collator->getLocale(ULOC_VALID_LOCALE, status));
150   DCHECK(U_SUCCESS(status));
151 
152   const char* collation = "default";
153   const char* usage = "sort";
154   const char* collation_key = "co";
155   status = U_ZERO_ERROR;
156   std::string collation_value =
157       icu_locale.getUnicodeKeywordValue<std::string>(collation_key, status);
158 
159   std::string locale;
160   if (U_SUCCESS(status)) {
161     if (collation_value == "search") {
162       usage = "search";
163 
164       // Search is disallowed as a collation value per spec. Let's
165       // use `default`, instead.
166       //
167       // https://tc39.github.io/ecma402/#sec-properties-of-intl-collator-instances
168       collation = "default";
169 
170       // We clone the icu::Locale because we don't want the
171       // icu_collator to be affected when we remove the collation key
172       // below.
173       icu::Locale new_icu_locale = icu_locale;
174 
175       // The spec forbids the search as a collation value in the
176       // locale tag, so let's filter it out.
177       status = U_ZERO_ERROR;
178       new_icu_locale.setUnicodeKeywordValue(collation_key, nullptr, status);
179       DCHECK(U_SUCCESS(status));
180 
181       locale = Intl::ToLanguageTag(new_icu_locale).FromJust();
182     } else {
183       collation = collation_value.c_str();
184       locale = Intl::ToLanguageTag(icu_locale).FromJust();
185     }
186   } else {
187     locale = Intl::ToLanguageTag(icu_locale).FromJust();
188   }
189 
190   // 5. For each row of Table 2, except the header row, in table order, do
191   //    ...
192   // Table 2: Resolved Options of Collator Instances
193   //  Internal Slot            Property               Extension Key
194   //    [[Locale]                "locale"
195   //    [[Usage]                 "usage"
196   //    [[Sensitivity]]          "sensitivity"
197   //    [[IgnorePunctuation]]    "ignorePunctuation"
198   //    [[Collation]]            "collation"
199   //    [[Numeric]]              "numeric"              kn
200   //    [[CaseFirst]]            "caseFirst"            kf
201 
202   // If the collator return the locale differ from what got requested, we stored
203   // it in the collator->locale. Otherwise, we just use the one from the
204   // collator.
205   if (collator->locale().length() != 0) {
206     // Get the locale from collator->locale() since we know in some cases
207     // collator won't be able to return the requested one, such as zh_CN.
208     Handle<String> locale_from_collator(collator->locale(), isolate);
209     Maybe<bool> maybe = JSReceiver::CreateDataProperty(
210         isolate, options, isolate->factory()->locale_string(),
211         locale_from_collator, Just(kDontThrow));
212     DCHECK(maybe.FromJust());
213     USE(maybe);
214   } else {
215     // Just return from the collator for most of the cases that we can recover
216     // from the collator.
217     CreateDataPropertyForOptions(
218         isolate, options, isolate->factory()->locale_string(), locale.c_str());
219   }
220 
221   CreateDataPropertyForOptions(isolate, options,
222                                isolate->factory()->usage_string(), usage);
223   CreateDataPropertyForOptions(
224       isolate, options, isolate->factory()->sensitivity_string(), sensitivity);
225   CreateDataPropertyForOptions(isolate, options,
226                                isolate->factory()->ignorePunctuation_string(),
227                                ignore_punctuation);
228   CreateDataPropertyForOptions(
229       isolate, options, isolate->factory()->collation_string(), collation);
230   CreateDataPropertyForOptions(isolate, options,
231                                isolate->factory()->numeric_string(), numeric);
232   CreateDataPropertyForOptions(
233       isolate, options, isolate->factory()->caseFirst_string(), case_first);
234   return options;
235 }
236 
237 namespace {
238 
ToCaseFirst(const char * str)239 CaseFirst ToCaseFirst(const char* str) {
240   if (strcmp(str, "upper") == 0) return CaseFirst::kUpper;
241   if (strcmp(str, "lower") == 0) return CaseFirst::kLower;
242   if (strcmp(str, "false") == 0) return CaseFirst::kFalse;
243   return CaseFirst::kUndefined;
244 }
245 
ToUColAttributeValue(CaseFirst case_first)246 UColAttributeValue ToUColAttributeValue(CaseFirst case_first) {
247   switch (case_first) {
248     case CaseFirst::kUpper:
249       return UCOL_UPPER_FIRST;
250     case CaseFirst::kLower:
251       return UCOL_LOWER_FIRST;
252     case CaseFirst::kFalse:
253     case CaseFirst::kUndefined:
254       return UCOL_OFF;
255   }
256 }
257 
SetNumericOption(icu::Collator * icu_collator,bool numeric)258 void SetNumericOption(icu::Collator* icu_collator, bool numeric) {
259   DCHECK_NOT_NULL(icu_collator);
260   UErrorCode status = U_ZERO_ERROR;
261   icu_collator->setAttribute(UCOL_NUMERIC_COLLATION,
262                              numeric ? UCOL_ON : UCOL_OFF, status);
263   DCHECK(U_SUCCESS(status));
264 }
265 
SetCaseFirstOption(icu::Collator * icu_collator,CaseFirst case_first)266 void SetCaseFirstOption(icu::Collator* icu_collator, CaseFirst case_first) {
267   DCHECK_NOT_NULL(icu_collator);
268   UErrorCode status = U_ZERO_ERROR;
269   icu_collator->setAttribute(UCOL_CASE_FIRST, ToUColAttributeValue(case_first),
270                              status);
271   DCHECK(U_SUCCESS(status));
272 }
273 
274 }  // anonymous namespace
275 
276 // static
New(Isolate * isolate,Handle<Map> map,Handle<Object> locales,Handle<Object> options_obj,const char * service)277 MaybeHandle<JSCollator> JSCollator::New(Isolate* isolate, Handle<Map> map,
278                                         Handle<Object> locales,
279                                         Handle<Object> options_obj,
280                                         const char* service) {
281   // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
282   Maybe<std::vector<std::string>> maybe_requested_locales =
283       Intl::CanonicalizeLocaleList(isolate, locales);
284   MAYBE_RETURN(maybe_requested_locales, Handle<JSCollator>());
285   std::vector<std::string> requested_locales =
286       maybe_requested_locales.FromJust();
287 
288   // 2. Set options to ? CoerceOptionsToObject(options).
289   Handle<JSReceiver> options;
290   ASSIGN_RETURN_ON_EXCEPTION(
291       isolate, options, CoerceOptionsToObject(isolate, options_obj, service),
292       JSCollator);
293 
294   // 4. Let usage be ? GetOption(options, "usage", "string", « "sort",
295   // "search" », "sort").
296   Maybe<Usage> maybe_usage = GetStringOption<Usage>(
297       isolate, options, "usage", service, {"sort", "search"},
298       {Usage::SORT, Usage::SEARCH}, Usage::SORT);
299   MAYBE_RETURN(maybe_usage, MaybeHandle<JSCollator>());
300   Usage usage = maybe_usage.FromJust();
301 
302   // 9. Let matcher be ? GetOption(options, "localeMatcher", "string",
303   // « "lookup", "best fit" », "best fit").
304   // 10. Set opt.[[localeMatcher]] to matcher.
305   Maybe<Intl::MatcherOption> maybe_locale_matcher =
306       Intl::GetLocaleMatcher(isolate, options, service);
307   MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSCollator>());
308   Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
309 
310   // x. Let _collation_ be ? GetOption(_options_, *"collation"*, *"string"*,
311   // *undefined*, *undefined*).
312   std::unique_ptr<char[]> collation_str = nullptr;
313   const std::vector<const char*> empty_values = {};
314   Maybe<bool> maybe_collation = GetStringOption(
315       isolate, options, "collation", empty_values, service, &collation_str);
316   MAYBE_RETURN(maybe_collation, MaybeHandle<JSCollator>());
317   // x. If _collation_ is not *undefined*, then
318   if (maybe_collation.FromJust() && collation_str != nullptr) {
319     // 1. If _collation_ does not match the Unicode Locale Identifier `type`
320     // nonterminal, throw a *RangeError* exception.
321     if (!JSLocale::Is38AlphaNumList(collation_str.get())) {
322       THROW_NEW_ERROR_RETURN_VALUE(
323           isolate,
324           NewRangeError(MessageTemplate::kInvalid,
325                         isolate->factory()->collation_string(),
326                         isolate->factory()->NewStringFromAsciiChecked(
327                             collation_str.get())),
328           MaybeHandle<JSCollator>());
329     }
330   }
331   // x. Set _opt_.[[co]] to _collation_.
332 
333   // 11. Let numeric be ? GetOption(options, "numeric", "boolean",
334   // undefined, undefined).
335   // 12. If numeric is not undefined, then
336   //    a. Let numeric be ! ToString(numeric).
337   //
338   // Note: We omit the ToString(numeric) operation as it's not
339   // observable. GetBoolOption returns a Boolean and
340   // ToString(Boolean) is not side-effecting.
341   //
342   // 13. Set opt.[[kn]] to numeric.
343   bool numeric;
344   Maybe<bool> found_numeric =
345       GetBoolOption(isolate, options, "numeric", service, &numeric);
346   MAYBE_RETURN(found_numeric, MaybeHandle<JSCollator>());
347 
348   // 14. Let caseFirst be ? GetOption(options, "caseFirst", "string",
349   //     « "upper", "lower", "false" », undefined).
350   Maybe<CaseFirst> maybe_case_first = GetCaseFirst(isolate, options, service);
351   MAYBE_RETURN(maybe_case_first, MaybeHandle<JSCollator>());
352   CaseFirst case_first = maybe_case_first.FromJust();
353 
354   // The relevant unicode extensions accepted by Collator as specified here:
355   // https://tc39.github.io/ecma402/#sec-intl-collator-internal-slots
356   //
357   // 16. Let relevantExtensionKeys be %Collator%.[[RelevantExtensionKeys]].
358   std::set<std::string> relevant_extension_keys{"co", "kn", "kf"};
359 
360   // 17. Let r be ResolveLocale(%Collator%.[[AvailableLocales]],
361   // requestedLocales, opt, %Collator%.[[RelevantExtensionKeys]],
362   // localeData).
363   Maybe<Intl::ResolvedLocale> maybe_resolve_locale =
364       Intl::ResolveLocale(isolate, JSCollator::GetAvailableLocales(),
365                           requested_locales, matcher, relevant_extension_keys);
366   if (maybe_resolve_locale.IsNothing()) {
367     THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),
368                     JSCollator);
369   }
370   Intl::ResolvedLocale r = maybe_resolve_locale.FromJust();
371 
372   // 18. Set collator.[[Locale]] to r.[[locale]].
373   icu::Locale icu_locale = r.icu_locale;
374   DCHECK(!icu_locale.isBogus());
375 
376   // 19. Let collation be r.[[co]].
377   UErrorCode status = U_ZERO_ERROR;
378   if (collation_str != nullptr) {
379     auto co_extension_it = r.extensions.find("co");
380     if (co_extension_it != r.extensions.end() &&
381         co_extension_it->second != collation_str.get()) {
382       icu_locale.setUnicodeKeywordValue("co", nullptr, status);
383       DCHECK(U_SUCCESS(status));
384     }
385   }
386 
387   // 5. Set collator.[[Usage]] to usage.
388   //
389   // 6. If usage is "sort", then
390   //    a. Let localeData be %Collator%.[[SortLocaleData]].
391   // 7. Else,
392   //    a. Let localeData be %Collator%.[[SearchLocaleData]].
393   //
394   // The Intl spec doesn't allow us to use "search" as an extension
395   // value for collation as per:
396   // https://tc39.github.io/ecma402/#sec-intl-collator-internal-slots
397   //
398   // But the only way to pass the value "search" for collation from
399   // the options object to ICU is to use the 'co' extension keyword.
400   //
401   // This will need to be filtered out when creating the
402   // resolvedOptions object.
403   if (usage == Usage::SEARCH) {
404     UErrorCode set_status = U_ZERO_ERROR;
405     icu_locale.setUnicodeKeywordValue("co", "search", set_status);
406     DCHECK(U_SUCCESS(set_status));
407   } else {
408     if (collation_str != nullptr &&
409         Intl::IsValidCollation(icu_locale, collation_str.get())) {
410       icu_locale.setUnicodeKeywordValue("co", collation_str.get(), status);
411       DCHECK(U_SUCCESS(status));
412     }
413   }
414 
415   // 20. If collation is null, let collation be "default".
416   // 21. Set collator.[[Collation]] to collation.
417   //
418   // We don't store the collation value as per the above two steps
419   // here. The collation value can be looked up from icu::Collator on
420   // demand, as part of Intl.Collator.prototype.resolvedOptions.
421 
422   std::unique_ptr<icu::Collator> icu_collator(
423       icu::Collator::createInstance(icu_locale, status));
424   if (U_FAILURE(status) || icu_collator.get() == nullptr) {
425     status = U_ZERO_ERROR;
426     // Remove extensions and try again.
427     icu::Locale no_extension_locale(icu_locale.getBaseName());
428     icu_collator.reset(
429         icu::Collator::createInstance(no_extension_locale, status));
430 
431     if (U_FAILURE(status) || icu_collator.get() == nullptr) {
432       THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),
433                       JSCollator);
434     }
435   }
436   DCHECK(U_SUCCESS(status));
437 
438   icu::Locale collator_locale(
439       icu_collator->getLocale(ULOC_VALID_LOCALE, status));
440 
441   // 22. If relevantExtensionKeys contains "kn", then
442   //     a. Set collator.[[Numeric]] to ! SameValue(r.[[kn]], "true").
443   //
444   // If the numeric value is passed in through the options object,
445   // then we use it. Otherwise, we check if the numeric value is
446   // passed in through the unicode extensions.
447   status = U_ZERO_ERROR;
448   if (found_numeric.FromJust()) {
449     SetNumericOption(icu_collator.get(), numeric);
450   } else {
451     auto kn_extension_it = r.extensions.find("kn");
452     if (kn_extension_it != r.extensions.end()) {
453       SetNumericOption(icu_collator.get(), (kn_extension_it->second == "true"));
454     }
455   }
456 
457   // 23. If relevantExtensionKeys contains "kf", then
458   //     a. Set collator.[[CaseFirst]] to r.[[kf]].
459   //
460   // If the caseFirst value is passed in through the options object,
461   // then we use it. Otherwise, we check if the caseFirst value is
462   // passed in through the unicode extensions.
463   if (case_first != CaseFirst::kUndefined) {
464     SetCaseFirstOption(icu_collator.get(), case_first);
465   } else {
466     auto kf_extension_it = r.extensions.find("kf");
467     if (kf_extension_it != r.extensions.end()) {
468       SetCaseFirstOption(icu_collator.get(),
469                          ToCaseFirst(kf_extension_it->second.c_str()));
470     }
471   }
472 
473   // Normalization is always on, by the spec. We are free to optimize
474   // if the strings are already normalized (but we don't have a way to tell
475   // that right now).
476   status = U_ZERO_ERROR;
477   icu_collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
478   DCHECK(U_SUCCESS(status));
479 
480   // 24. Let sensitivity be ? GetOption(options, "sensitivity",
481   // "string", « "base", "accent", "case", "variant" », undefined).
482   Maybe<Sensitivity> maybe_sensitivity =
483       GetStringOption<Sensitivity>(isolate, options, "sensitivity", service,
484                                    {"base", "accent", "case", "variant"},
485                                    {Sensitivity::kBase, Sensitivity::kAccent,
486                                     Sensitivity::kCase, Sensitivity::kVariant},
487                                    Sensitivity::kUndefined);
488   MAYBE_RETURN(maybe_sensitivity, MaybeHandle<JSCollator>());
489   Sensitivity sensitivity = maybe_sensitivity.FromJust();
490 
491   // 25. If sensitivity is undefined, then
492   if (sensitivity == Sensitivity::kUndefined) {
493     // 25. a. If usage is "sort", then
494     if (usage == Usage::SORT) {
495       // 25. a. i. Let sensitivity be "variant".
496       sensitivity = Sensitivity::kVariant;
497     }
498   }
499   // 26. Set collator.[[Sensitivity]] to sensitivity.
500   switch (sensitivity) {
501     case Sensitivity::kBase:
502       icu_collator->setStrength(icu::Collator::PRIMARY);
503       break;
504     case Sensitivity::kAccent:
505       icu_collator->setStrength(icu::Collator::SECONDARY);
506       break;
507     case Sensitivity::kCase:
508       icu_collator->setStrength(icu::Collator::PRIMARY);
509       status = U_ZERO_ERROR;
510       icu_collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status);
511       DCHECK(U_SUCCESS(status));
512       break;
513     case Sensitivity::kVariant:
514       icu_collator->setStrength(icu::Collator::TERTIARY);
515       break;
516     case Sensitivity::kUndefined:
517       break;
518   }
519 
520   // 27.Let ignorePunctuation be ? GetOption(options,
521   // "ignorePunctuation", "boolean", undefined, false).
522   bool ignore_punctuation;
523   Maybe<bool> found_ignore_punctuation = GetBoolOption(
524       isolate, options, "ignorePunctuation", service, &ignore_punctuation);
525   MAYBE_RETURN(found_ignore_punctuation, MaybeHandle<JSCollator>());
526 
527   // 28. Set collator.[[IgnorePunctuation]] to ignorePunctuation.
528   if (found_ignore_punctuation.FromJust() && ignore_punctuation) {
529     status = U_ZERO_ERROR;
530     icu_collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status);
531     DCHECK(U_SUCCESS(status));
532   }
533 
534   Handle<Managed<icu::Collator>> managed_collator =
535       Managed<icu::Collator>::FromUniquePtr(isolate, 0,
536                                             std::move(icu_collator));
537 
538   // We only need to do so if it is different from the collator would return.
539   Handle<String> locale_str = isolate->factory()->NewStringFromAsciiChecked(
540       (collator_locale != icu_locale) ? r.locale.c_str() : "");
541   // Now all properties are ready, so we can allocate the result object.
542   Handle<JSCollator> collator = Handle<JSCollator>::cast(
543       isolate->factory()->NewFastOrSlowJSObjectFromMap(map));
544   DisallowGarbageCollection no_gc;
545   collator->set_icu_collator(*managed_collator);
546   collator->set_locale(*locale_str);
547 
548   // 29. Return collator.
549   return collator;
550 }
551 
552 namespace {
553 
554 class CollatorAvailableLocales {
555  public:
CollatorAvailableLocales()556   CollatorAvailableLocales() {
557     int32_t num_locales = 0;
558     const icu::Locale* icu_available_locales =
559         icu::Collator::getAvailableLocales(num_locales);
560     std::vector<std::string> locales;
561     for (int32_t i = 0; i < num_locales; ++i) {
562       locales.push_back(
563           Intl::ToLanguageTag(icu_available_locales[i]).FromJust());
564     }
565 #define U_ICUDATA_COLL U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "coll"
566     set_ = Intl::BuildLocaleSet(locales, U_ICUDATA_COLL, nullptr);
567 #undef U_ICUDATA_COLL
568   }
569   virtual ~CollatorAvailableLocales() = default;
Get() const570   const std::set<std::string>& Get() const { return set_; }
571 
572  private:
573   std::set<std::string> set_;
574 };
575 
576 }  // namespace
577 
GetAvailableLocales()578 const std::set<std::string>& JSCollator::GetAvailableLocales() {
579   static base::LazyInstance<CollatorAvailableLocales>::type available_locales =
580       LAZY_INSTANCE_INITIALIZER;
581   return available_locales.Pointer()->Get();
582 }
583 
584 }  // namespace internal
585 }  // namespace v8
586