| File: | out/../deps/v8/src/objects/js-collator.cc |
| Warning: | line 447, column 3 Value stored to 'status' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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_SUPPORT1 |
| 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 | |
| 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. |
| 55 | void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options, |
| 56 | Handle<String> key, const char* value) { |
| 57 | DCHECK_NOT_NULL(value)((void) 0); |
| 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())((void) 0); |
| 66 | USE(maybe)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{maybe}; (void)unused_tmp_array_for_use_macro; } while (false); |
| 67 | } |
| 68 | |
| 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())((void) 0); |
| 78 | USE(maybe)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{maybe}; (void)unused_tmp_array_for_use_macro; } while (false); |
| 79 | } |
| 80 | |
| 81 | } // anonymous namespace |
| 82 | |
| 83 | // static |
| 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)((void) 0); |
| 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))((void) 0); |
| 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))((void) 0); |
| 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))((void) 0); |
| 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))((void) 0); |
| 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))((void) 0); |
| 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))((void) 0); |
| 146 | |
| 147 | status = U_ZERO_ERROR; |
| 148 | |
| 149 | icu::Locale icu_locale(icu_collator->getLocale(ULOC_VALID_LOCALE, status)); |
| 150 | DCHECK(U_SUCCESS(status))((void) 0); |
| 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))((void) 0); |
| 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())((void) 0); |
| 213 | USE(maybe)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{maybe}; (void)unused_tmp_array_for_use_macro; } while (false); |
| 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 | |
| 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 | |
| 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 | |
| 258 | void SetNumericOption(icu::Collator* icu_collator, bool numeric) { |
| 259 | DCHECK_NOT_NULL(icu_collator)((void) 0); |
| 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))((void) 0); |
| 264 | } |
| 265 | |
| 266 | void SetCaseFirstOption(icu::Collator* icu_collator, CaseFirst case_first) { |
| 267 | DCHECK_NOT_NULL(icu_collator)((void) 0); |
| 268 | UErrorCode status = U_ZERO_ERROR; |
| 269 | icu_collator->setAttribute(UCOL_CASE_FIRST, ToUColAttributeValue(case_first), |
| 270 | status); |
| 271 | DCHECK(U_SUCCESS(status))((void) 0); |
| 272 | } |
| 273 | |
| 274 | } // anonymous namespace |
| 275 | |
| 276 | // static |
| 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>())do { if ((maybe_requested_locales).IsNothing()) return Handle <JSCollator>(); } while (false); |
| 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(do { if (!(CoerceOptionsToObject(isolate, options_obj, service )).ToHandle(&options)) { ((void) 0); return MaybeHandle< JSCollator>(); } } while (false) |
| 291 | isolate, options, CoerceOptionsToObject(isolate, options_obj, service),do { if (!(CoerceOptionsToObject(isolate, options_obj, service )).ToHandle(&options)) { ((void) 0); return MaybeHandle< JSCollator>(); } } while (false) |
| 292 | JSCollator)do { if (!(CoerceOptionsToObject(isolate, options_obj, service )).ToHandle(&options)) { ((void) 0); return MaybeHandle< JSCollator>(); } } while (false); |
| 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>())do { if ((maybe_usage).IsNothing()) return MaybeHandle<JSCollator >(); } while (false); |
| 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>())do { if ((maybe_locale_matcher).IsNothing()) return MaybeHandle <JSCollator>(); } while (false); |
| 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>())do { if ((maybe_collation).IsNothing()) return MaybeHandle< JSCollator>(); } while (false); |
| 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(do { auto* __isolate__ = (isolate); __isolate__->Throw(*__isolate__ ->factory()->NewRangeError(MessageTemplate::kInvalid, isolate ->factory()->collation_string(), isolate->factory()-> NewStringFromAsciiChecked( collation_str.get()))); return MaybeHandle <JSCollator>(); } while (false) |
| 323 | isolate,do { auto* __isolate__ = (isolate); __isolate__->Throw(*__isolate__ ->factory()->NewRangeError(MessageTemplate::kInvalid, isolate ->factory()->collation_string(), isolate->factory()-> NewStringFromAsciiChecked( collation_str.get()))); return MaybeHandle <JSCollator>(); } while (false) |
| 324 | NewRangeError(MessageTemplate::kInvalid,do { auto* __isolate__ = (isolate); __isolate__->Throw(*__isolate__ ->factory()->NewRangeError(MessageTemplate::kInvalid, isolate ->factory()->collation_string(), isolate->factory()-> NewStringFromAsciiChecked( collation_str.get()))); return MaybeHandle <JSCollator>(); } while (false) |
| 325 | isolate->factory()->collation_string(),do { auto* __isolate__ = (isolate); __isolate__->Throw(*__isolate__ ->factory()->NewRangeError(MessageTemplate::kInvalid, isolate ->factory()->collation_string(), isolate->factory()-> NewStringFromAsciiChecked( collation_str.get()))); return MaybeHandle <JSCollator>(); } while (false) |
| 326 | isolate->factory()->NewStringFromAsciiChecked(do { auto* __isolate__ = (isolate); __isolate__->Throw(*__isolate__ ->factory()->NewRangeError(MessageTemplate::kInvalid, isolate ->factory()->collation_string(), isolate->factory()-> NewStringFromAsciiChecked( collation_str.get()))); return MaybeHandle <JSCollator>(); } while (false) |
| 327 | collation_str.get())),do { auto* __isolate__ = (isolate); __isolate__->Throw(*__isolate__ ->factory()->NewRangeError(MessageTemplate::kInvalid, isolate ->factory()->collation_string(), isolate->factory()-> NewStringFromAsciiChecked( collation_str.get()))); return MaybeHandle <JSCollator>(); } while (false) |
| 328 | MaybeHandle<JSCollator>())do { auto* __isolate__ = (isolate); __isolate__->Throw(*__isolate__ ->factory()->NewRangeError(MessageTemplate::kInvalid, isolate ->factory()->collation_string(), isolate->factory()-> NewStringFromAsciiChecked( collation_str.get()))); return MaybeHandle <JSCollator>(); } while (false); |
| 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>())do { if ((found_numeric).IsNothing()) return MaybeHandle<JSCollator >(); } while (false); |
| 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>())do { if ((maybe_case_first).IsNothing()) return MaybeHandle< JSCollator>(); } while (false); |
| 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),do { auto* __isolate__ = (isolate); return __isolate__->template Throw<JSCollator>(__isolate__->factory()->NewRangeError (MessageTemplate::kIcuError)); } while (false) |
| 368 | JSCollator)do { auto* __isolate__ = (isolate); return __isolate__->template Throw<JSCollator>(__isolate__->factory()->NewRangeError (MessageTemplate::kIcuError)); } while (false); |
| 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())((void) 0); |
| 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))((void) 0); |
| 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))((void) 0); |
| 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))((void) 0); |
| 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),do { auto* __isolate__ = (isolate); return __isolate__->template Throw<JSCollator>(__isolate__->factory()->NewRangeError (MessageTemplate::kIcuError)); } while (false) |
| 433 | JSCollator)do { auto* __isolate__ = (isolate); return __isolate__->template Throw<JSCollator>(__isolate__->factory()->NewRangeError (MessageTemplate::kIcuError)); } while (false); |
| 434 | } |
| 435 | } |
| 436 | DCHECK(U_SUCCESS(status))((void) 0); |
| 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; |
Value stored to 'status' is never read | |
| 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))((void) 0); |
| 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>())do { if ((maybe_sensitivity).IsNothing()) return MaybeHandle< JSCollator>(); } while (false); |
| 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))((void) 0); |
| 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>())do { if ((found_ignore_punctuation).IsNothing()) return MaybeHandle <JSCollator>(); } while (false); |
| 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))((void) 0); |
| 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: |
| 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"icudt" "71" "l" U_TREE_SEPARATOR_STRING"-" "coll" |
| 566 | set_ = Intl::BuildLocaleSet(locales, U_ICUDATA_COLL, nullptr); |
| 567 | #undef U_ICUDATA_COLL |
| 568 | } |
| 569 | virtual ~CollatorAvailableLocales() = default; |
| 570 | const std::set<std::string>& Get() const { return set_; } |
| 571 | |
| 572 | private: |
| 573 | std::set<std::string> set_; |
| 574 | }; |
| 575 | |
| 576 | } // namespace |
| 577 | |
| 578 | const std::set<std::string>& JSCollator::GetAvailableLocales() { |
| 579 | static base::LazyInstance<CollatorAvailableLocales>::type available_locales = |
| 580 | LAZY_INSTANCE_INITIALIZER{ { 0 }, { {} } }; |
| 581 | return available_locales.Pointer()->Get(); |
| 582 | } |
| 583 | |
| 584 | } // namespace internal |
| 585 | } // namespace v8 |