1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/js_collator.h"
17
18 #include "ecmascript/intl/locale_helper.h"
19 #include "ecmascript/global_env.h"
20 #include "ecmascript/mem/c_string.h"
21 #include "ecmascript/mem/barriers-inl.h"
22
23 #include "unicode/udata.h"
24
25 namespace panda::ecmascript {
26 // NOLINTNEXTLINE (readability-identifier-naming, fuchsia-statically-constructed-objects)
27 const CString JSCollator::uIcuDataColl = U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "coll";
28 const std::map<std::string, CaseFirstOption> JSCollator::caseFirstMap = {
29 {"upper", CaseFirstOption::UPPER},
30 {"lower", CaseFirstOption::LOWER},
31 {"false", CaseFirstOption::FALSE_OPTION}
32 };
33 const std::map<CaseFirstOption, UColAttributeValue> JSCollator::uColAttributeValueMap = {
34 {CaseFirstOption::UPPER, UCOL_UPPER_FIRST},
35 {CaseFirstOption::LOWER, UCOL_LOWER_FIRST},
36 {CaseFirstOption::FALSE_OPTION, UCOL_OFF},
37 {CaseFirstOption::UNDEFINED, UCOL_OFF}
38 };
39
GetAvailableLocales(JSThread * thread,bool enableLocaleCache)40 JSHandle<TaggedArray> JSCollator::GetAvailableLocales(JSThread *thread, bool enableLocaleCache)
41 {
42 const char *key = nullptr;
43 const char *path = JSCollator::uIcuDataColl.c_str();
44 // key and path are const, so we can cache the result
45 if (enableLocaleCache) {
46 JSHandle<JSTaggedValue> cachedLocales = thread->GlobalConstants()->GetHandledCachedJSCollatorLocales();
47 if (cachedLocales->IsHeapObject()) {
48 return JSHandle<TaggedArray>(cachedLocales);
49 }
50 }
51 std::vector<std::string> availableStringLocales = intl::LocaleHelper::GetAvailableLocales(thread, key, path);
52 JSHandle<TaggedArray> availableLocales = JSLocale::ConstructLocaleList(thread, availableStringLocales);
53 if (enableLocaleCache) {
54 GlobalEnvConstants *constants = const_cast<GlobalEnvConstants *>(thread->GlobalConstants());
55 constants->SetCachedLocales(availableLocales.GetTaggedValue());
56 }
57 return availableLocales;
58 }
59
60 /* static */
SetIcuCollator(JSThread * thread,const JSHandle<JSCollator> & collator,icu::Collator * icuCollator,const DeleteEntryPoint & callback)61 void JSCollator::SetIcuCollator(JSThread *thread, const JSHandle<JSCollator> &collator,
62 icu::Collator *icuCollator, const DeleteEntryPoint &callback)
63 {
64 EcmaVM *ecmaVm = thread->GetEcmaVM();
65 ObjectFactory *factory = ecmaVm->GetFactory();
66
67 ASSERT(icuCollator != nullptr);
68 JSTaggedValue data = collator->GetIcuField();
69 if (data.IsJSNativePointer()) {
70 JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject());
71 native->ResetExternalPointer(icuCollator);
72 return;
73 }
74 JSHandle<JSNativePointer> pointer = factory->NewJSNativePointer(icuCollator, callback);
75 collator->SetIcuField(thread, pointer.GetTaggedValue());
76 }
77
InitializeCollator(JSThread * thread,const JSHandle<JSCollator> & collator,const JSHandle<JSTaggedValue> & locales,const JSHandle<JSTaggedValue> & options,bool forIcuCache,bool enableLocaleCache)78 JSHandle<JSCollator> JSCollator::InitializeCollator(JSThread *thread,
79 const JSHandle<JSCollator> &collator,
80 const JSHandle<JSTaggedValue> &locales,
81 const JSHandle<JSTaggedValue> &options,
82 bool forIcuCache,
83 bool enableLocaleCache)
84 {
85 EcmaVM *ecmaVm = thread->GetEcmaVM();
86 ObjectFactory *factory = ecmaVm->GetFactory();
87 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
88 // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
89 JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
90 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread);
91
92 // 2. If options is undefined, then
93 // a. Let options be ObjectCreate(null).
94 // 3. Else,
95 // a. Let options be ? ToObject(options).
96 JSHandle<JSObject> optionsObject;
97 if (options->IsUndefined()) {
98 optionsObject = factory->CreateNullJSObject();
99 } else {
100 optionsObject = JSTaggedValue::ToObject(thread, options);
101 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread);
102 }
103 // 4. Let usage be ? GetOption(options, "usage", "string", « "sort", "search" », "sort").
104 auto usage = JSLocale::GetOptionOfString<UsageOption>(thread, optionsObject, globalConst->GetHandledUsageString(),
105 {UsageOption::SORT, UsageOption::SEARCH}, {"sort", "search"},
106 UsageOption::SORT);
107 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread);
108 collator->SetUsage(usage);
109
110 // 5. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit").
111 auto matcher = JSLocale::GetOptionOfString<LocaleMatcherOption>(
112 thread, optionsObject, globalConst->GetHandledLocaleMatcherString(),
113 {LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT}, {"lookup", "best fit"},
114 LocaleMatcherOption::BEST_FIT);
115 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread);
116
117 // 6. Let collation be ? GetOption(options, "collation", "string", undefined, undefined).
118 // 7. If collation is not undefined, then
119 // a. If collation does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
120 JSHandle<JSTaggedValue> collation =
121 JSLocale::GetOption(thread, optionsObject, globalConst->GetHandledCollationString(), OptionType::STRING,
122 globalConst->GetHandledUndefined(), globalConst->GetHandledUndefined());
123 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread);
124 collator->SetCollation(thread, collation);
125 std::string collationStr;
126 if (!collation->IsUndefined()) {
127 JSHandle<EcmaString> collationEcmaStr = JSHandle<EcmaString>::Cast(collation);
128 collationStr = intl::LocaleHelper::ConvertToStdString(collationEcmaStr);
129 if (!JSLocale::IsWellAlphaNumList(collationStr)) {
130 THROW_RANGE_ERROR_AND_RETURN(thread, "invalid collation", collator);
131 }
132 }
133
134 // 8. Let numeric be ? GetOption(options, "numeric", "boolean", undefined, undefined).
135 bool numeric = false;
136 bool foundNumeric =
137 JSLocale::GetOptionOfBool(thread, optionsObject, globalConst->GetHandledNumericString(), false, &numeric);
138 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread);
139 collator->SetNumeric(numeric);
140
141 // 14. Let caseFirst be ? GetOption(options, "caseFirst", "string", « "upper", "lower", "false" », undefined).
142 CaseFirstOption caseFirst = JSLocale::GetOptionOfString<CaseFirstOption>(
143 thread, optionsObject, globalConst->GetHandledCaseFirstString(),
144 {CaseFirstOption::UPPER, CaseFirstOption::LOWER, CaseFirstOption::FALSE_OPTION}, {"upper", "lower", "false"},
145 CaseFirstOption::UNDEFINED);
146 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread);
147 collator->SetCaseFirst(caseFirst);
148
149 // 16. Let relevantExtensionKeys be %Collator%.[[RelevantExtensionKeys]].
150 std::set<std::string> relevantExtensionKeys = {"co", "kn", "kf"};
151
152 // 17. Let r be ResolveLocale(%Collator%.[[AvailableLocales]], requestedLocales, opt,
153 // %Collator%.[[RelevantExtensionKeys]], localeData).
154 JSHandle<TaggedArray> availableLocales;
155 if (requestedLocales->GetLength() == 0) {
156 availableLocales = factory->EmptyArray();
157 } else {
158 availableLocales = GetAvailableLocales(thread, enableLocaleCache);
159 }
160 ResolvedLocale r =
161 JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, relevantExtensionKeys);
162 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread);
163 icu::Locale icuLocale = r.localeData;
164 JSHandle<EcmaString> localeStr = intl::LocaleHelper::ToLanguageTag(thread, icuLocale);
165 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread);
166 collator->SetLocale(thread, localeStr.GetTaggedValue());
167 ASSERT_PRINT(!icuLocale.isBogus(), "icuLocale is bogus");
168
169 // If collation is undefined iterate RelevantExtensionKeys to find "co"
170 // if found, set ICU collator UnicodeKeyword to iterator->second
171 UErrorCode status = U_ZERO_ERROR;
172 if (!collation->IsUndefined()) {
173 auto extensionIter = r.extensions.find("co");
174 if (extensionIter != r.extensions.end() && extensionIter->second != collationStr) {
175 icuLocale.setUnicodeKeywordValue("co", nullptr, status);
176 ASSERT_PRINT(U_SUCCESS(status), "icuLocale set co failed");
177 }
178 }
179
180 // If usage is serach set co-serach to icu locale key word value
181 // Eles set collation string to icu locale key word value
182 if (usage == UsageOption::SEARCH) {
183 icuLocale.setUnicodeKeywordValue("co", "search", status);
184 ASSERT(U_SUCCESS(status));
185 } else {
186 if (!collationStr.empty() && JSLocale::IsWellCollation(icuLocale, collationStr)) {
187 icuLocale.setUnicodeKeywordValue("co", collationStr, status);
188 ASSERT(U_SUCCESS(status));
189 }
190 }
191
192 std::unique_ptr<icu::Collator> icuCollator(icu::Collator::createInstance(icuLocale, status));
193 if (U_FAILURE(status) || icuCollator == nullptr) { // NOLINT(readability-implicit-bool-conversion)
194 if (status == UErrorCode::U_MISSING_RESOURCE_ERROR) {
195 THROW_REFERENCE_ERROR_AND_RETURN(thread, "can not find icu data resources", collator);
196 }
197 status = U_ZERO_ERROR;
198 icu::Locale localeName(icuLocale.getBaseName());
199 icuCollator.reset(icu::Collator::createInstance(localeName, status));
200 if (U_FAILURE(status) || icuCollator == nullptr) { // NOLINT(readability-implicit-bool-conversion)
201 THROW_RANGE_ERROR_AND_RETURN(thread, "invalid collation", collator);
202 }
203 }
204 ASSERT(U_SUCCESS(status));
205 icu::Locale collatorLocale(icuCollator->getLocale(ULOC_VALID_LOCALE, status));
206
207 icuCollator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
208 ASSERT(U_SUCCESS(status));
209
210 // If numeric is found set ICU collator UCOL_NUMERIC_COLLATION to numeric
211 // Else iterate RelevantExtensionKeys to find "kn"
212 // if found, set ICU collator UCOL_NUMERIC_COLLATION to iterator->second
213 status = U_ZERO_ERROR;
214 if (foundNumeric) {
215 ASSERT(icuCollator.get() != nullptr);
216 icuCollator.get()->setAttribute(UCOL_NUMERIC_COLLATION, numeric ? UCOL_ON : UCOL_OFF, status);
217 ASSERT(U_SUCCESS(status));
218 } else {
219 auto extensionIter = r.extensions.find("kn");
220 if (extensionIter != r.extensions.end()) {
221 ASSERT(icuCollator.get() != nullptr);
222 bool found = (extensionIter->second == "true");
223 collator->SetNumeric(found);
224 icuCollator.get()->setAttribute(UCOL_NUMERIC_COLLATION, found ? UCOL_ON : UCOL_OFF, status);
225 ASSERT(U_SUCCESS(status));
226 }
227 }
228
229 // If caseFirst is not undefined set ICU collator UColAttributeValue to caseFirst
230 // Else iterate RelevantExtensionKeys to find "kf"
231 // if found, set ICU collator UColAttributeValue to iterator->second
232 status = U_ZERO_ERROR;
233 if (caseFirst != CaseFirstOption::UNDEFINED) {
234 ASSERT(icuCollator.get() != nullptr);
235 icuCollator.get()->setAttribute(UCOL_CASE_FIRST, OptionToUColAttribute(caseFirst), status);
236 ASSERT(U_SUCCESS(status));
237 } else {
238 auto extensionIter = r.extensions.find("kf");
239 if (extensionIter != r.extensions.end()) {
240 ASSERT(icuCollator.get() != nullptr);
241 auto mapIter = caseFirstMap.find(extensionIter->second);
242 if (mapIter != caseFirstMap.end()) {
243 icuCollator.get()->setAttribute(UCOL_CASE_FIRST, OptionToUColAttribute(mapIter->second), status);
244 collator->SetCaseFirst(mapIter->second);
245 } else {
246 icuCollator.get()->setAttribute(UCOL_CASE_FIRST, OptionToUColAttribute(CaseFirstOption::UNDEFINED),
247 status);
248 }
249 ASSERT(U_SUCCESS(status));
250 }
251 }
252
253 // 24. Let sensitivity be ? GetOption(options, "sensitivity", "string", « "base", "accent", "case", "variant" »,
254 // undefined).
255 SensitivityOption sensitivity = JSLocale::GetOptionOfString<SensitivityOption>(
256 thread, optionsObject, globalConst->GetHandledSensitivityString(),
257 {SensitivityOption::BASE, SensitivityOption::ACCENT, SensitivityOption::CASE, SensitivityOption::VARIANT},
258 {"base", "accent", "case", "variant"}, SensitivityOption::UNDEFINED);
259 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread);
260 // 25. If sensitivity is undefined, then
261 // a. If usage is "sort", then
262 // i. Let sensitivity be "variant".
263 if (sensitivity == SensitivityOption::UNDEFINED) {
264 if (usage == UsageOption::SORT) {
265 sensitivity = SensitivityOption::VARIANT;
266 }
267 }
268 collator->SetSensitivity(sensitivity);
269
270 // Trans SensitivityOption to Icu strength option
271 switch (sensitivity) {
272 case SensitivityOption::BASE:
273 icuCollator->setStrength(icu::Collator::PRIMARY);
274 break;
275 case SensitivityOption::ACCENT:
276 icuCollator->setStrength(icu::Collator::SECONDARY);
277 break;
278 case SensitivityOption::CASE:
279 icuCollator->setStrength(icu::Collator::PRIMARY);
280 icuCollator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status);
281 break;
282 case SensitivityOption::VARIANT:
283 icuCollator->setStrength(icu::Collator::TERTIARY);
284 break;
285 case SensitivityOption::UNDEFINED:
286 break;
287 case SensitivityOption::EXCEPTION:
288 LOG_ECMA(FATAL) << "this branch is unreachable";
289 UNREACHABLE();
290 }
291
292 // 27. Let ignorePunctuation be ? GetOption(options, "ignorePunctuation", "boolean", undefined, false).
293 // 28. Set collator.[[IgnorePunctuation]] to ignorePunctuation.
294 bool ignorePunctuation = false;
295 JSLocale::GetOptionOfBool(thread, optionsObject, globalConst->GetHandledIgnorePunctuationString(), false,
296 &ignorePunctuation);
297 collator->SetIgnorePunctuation(ignorePunctuation);
298 if (ignorePunctuation) {
299 status = U_ZERO_ERROR;
300 icuCollator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status);
301 ASSERT(U_SUCCESS(status));
302 }
303
304 if (forIcuCache) {
305 std::string cacheEntry =
306 locales->IsUndefined() ? "" : EcmaStringAccessor(locales.GetTaggedValue()).ToStdString();
307 thread->GetCurrentEcmaContext()->SetIcuFormatterToCache(IcuFormatterType::COLLATOR,
308 cacheEntry, icuCollator.release(), JSCollator::FreeIcuCollator);
309 } else {
310 SetIcuCollator(thread, collator, icuCollator.release(), JSCollator::FreeIcuCollator);
311 }
312 collator->SetBoundCompare(thread, JSTaggedValue::Undefined());
313 // 29. Return collator.
314 return collator;
315 }
316
GetCachedIcuCollator(JSThread * thread,const JSHandle<JSTaggedValue> & locales)317 icu::Collator *JSCollator::GetCachedIcuCollator(JSThread *thread, const JSHandle<JSTaggedValue> &locales)
318 {
319 std::string cacheEntry = locales->IsUndefined() ? "" : EcmaStringAccessor(locales.GetTaggedValue()).ToStdString();
320 void *cachedCollator =
321 thread->GetCurrentEcmaContext()->GetIcuFormatterFromCache(IcuFormatterType::COLLATOR, cacheEntry);
322 if (cachedCollator != nullptr) {
323 return reinterpret_cast<icu::Collator*>(cachedCollator);
324 }
325 return nullptr;
326 }
327
OptionToUColAttribute(CaseFirstOption caseFirstOption)328 UColAttributeValue JSCollator::OptionToUColAttribute(CaseFirstOption caseFirstOption)
329 {
330 auto iter = uColAttributeValueMap.find(caseFirstOption);
331 if (iter != uColAttributeValueMap.end()) {
332 return iter->second;
333 }
334 LOG_ECMA(FATAL) << "this branch is unreachable";
335 UNREACHABLE();
336 }
337
OptionsToEcmaString(JSThread * thread,UsageOption usage)338 JSHandle<JSTaggedValue> OptionsToEcmaString(JSThread *thread, UsageOption usage)
339 {
340 JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
341 auto globalConst = thread->GlobalConstants();
342 switch (usage) {
343 case UsageOption::SORT:
344 result.Update(globalConst->GetSortString());
345 break;
346 case UsageOption::SEARCH:
347 result.Update(globalConst->GetSearchString());
348 break;
349 default:
350 LOG_ECMA(FATAL) << "this branch is unreachable";
351 UNREACHABLE();
352 }
353 return result;
354 }
355
OptionsToEcmaString(JSThread * thread,SensitivityOption sensitivity)356 JSHandle<JSTaggedValue> OptionsToEcmaString(JSThread *thread, SensitivityOption sensitivity)
357 {
358 JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
359 auto globalConst = thread->GlobalConstants();
360 switch (sensitivity) {
361 case SensitivityOption::BASE:
362 result.Update(globalConst->GetBaseString());
363 break;
364 case SensitivityOption::ACCENT:
365 result.Update(globalConst->GetAccentString());
366 break;
367 case SensitivityOption::CASE:
368 result.Update(globalConst->GetCaseString());
369 break;
370 case SensitivityOption::VARIANT:
371 result.Update(globalConst->GetVariantString());
372 break;
373 case SensitivityOption::UNDEFINED:
374 break;
375 default:
376 LOG_ECMA(FATAL) << "this branch is unreachable";
377 UNREACHABLE();
378 }
379 return result;
380 }
381
OptionsToEcmaString(JSThread * thread,CaseFirstOption caseFirst)382 JSHandle<JSTaggedValue> OptionsToEcmaString(JSThread *thread, CaseFirstOption caseFirst)
383 {
384 JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
385 auto globalConst = thread->GlobalConstants();
386 switch (caseFirst) {
387 case CaseFirstOption::UPPER:
388 result.Update(globalConst->GetUpperString());
389 break;
390 case CaseFirstOption::LOWER:
391 result.Update(globalConst->GetLowerString());
392 break;
393 case CaseFirstOption::FALSE_OPTION:
394 result.Update(globalConst->GetFalseString());
395 break;
396 case CaseFirstOption::UNDEFINED:
397 result.Update(globalConst->GetUpperString());
398 break;
399 default:
400 LOG_ECMA(FATAL) << "this branch is unreachable";
401 UNREACHABLE();
402 }
403 return result;
404 }
405
406 // 11.3.4 Intl.Collator.prototype.resolvedOptions ()
ResolvedOptions(JSThread * thread,const JSHandle<JSCollator> & collator)407 JSHandle<JSObject> JSCollator::ResolvedOptions(JSThread *thread, const JSHandle<JSCollator> &collator)
408 {
409 auto ecmaVm = thread->GetEcmaVM();
410 auto globalConst = thread->GlobalConstants();
411 ObjectFactory *factory = ecmaVm->GetFactory();
412 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
413 JSHandle<JSFunction> funCtor(env->GetObjectFunction());
414 JSHandle<JSObject> options(factory->NewJSObjectByConstructor(funCtor));
415
416 // [[Locale]]
417 JSHandle<JSTaggedValue> property = globalConst->GetHandledLocaleString();
418 JSHandle<JSTaggedValue> locale(thread, collator->GetLocale());
419 JSObject::CreateDataPropertyOrThrow(thread, options, property, locale);
420 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
421
422 // [[Usage]]
423 UsageOption usageOption = collator->GetUsage();
424 JSHandle<JSTaggedValue> usageValue = OptionsToEcmaString(thread, usageOption);
425 JSObject::CreateDataProperty(thread, options, globalConst->GetHandledUsageString(), usageValue);
426
427 // [[Sensitivity]]
428 auto sentivityOption = collator->GetSensitivity();
429 JSHandle<JSTaggedValue> sensitivityValue = OptionsToEcmaString(thread, sentivityOption);
430 JSObject::CreateDataProperty(thread, options, globalConst->GetHandledSensitivityString(), sensitivityValue);
431
432 // [[IgnorePunctuation]]
433 JSHandle<JSTaggedValue> ignorePunctuationValue(thread, JSTaggedValue(collator->GetIgnorePunctuation()));
434 JSObject::CreateDataProperty(thread, options, globalConst->GetHandledIgnorePunctuationString(),
435 ignorePunctuationValue);
436
437 // [[Collation]]
438 JSMutableHandle<JSTaggedValue> collationValue(thread, collator->GetCollation());
439 if (collationValue->IsUndefined()) {
440 collationValue.Update(globalConst->GetDefaultString());
441 }
442 JSObject::CreateDataProperty(thread, options, globalConst->GetHandledCollationString(), collationValue);
443
444 // [[Numeric]]
445 JSHandle<JSTaggedValue> numericValue(thread, JSTaggedValue(collator->GetNumeric()));
446 JSObject::CreateDataProperty(thread, options, globalConst->GetHandledNumericString(), numericValue);
447
448 // [[CaseFirst]]
449 CaseFirstOption caseFirstOption = collator->GetCaseFirst();
450 // In Ecma402 spec, caseFirst is an optional property so we set it to Upper when input is undefined
451 // the requirement maybe change in the future
452 JSHandle<JSTaggedValue> caseFirstValue = OptionsToEcmaString(thread, caseFirstOption);
453 JSObject::CreateDataProperty(thread, options, globalConst->GetHandledCaseFirstString(), caseFirstValue);
454 return options;
455 }
456
EcmaStringToUString(const JSHandle<EcmaString> & string)457 icu::UnicodeString EcmaStringToUString(const JSHandle<EcmaString> &string)
458 {
459 std::string stdString(ConvertToString(*string, StringConvertedUsage::LOGICOPERATION));
460 icu::StringPiece sp(stdString);
461 icu::UnicodeString uString = icu::UnicodeString::fromUTF8(sp);
462 return uString;
463 }
464
CompareStrings(const icu::Collator * icuCollator,const JSHandle<EcmaString> & string1,const JSHandle<EcmaString> & string2)465 JSTaggedValue JSCollator::CompareStrings(const icu::Collator *icuCollator, const JSHandle<EcmaString> &string1,
466 const JSHandle<EcmaString> &string2)
467 {
468 icu::UnicodeString uString1 = EcmaStringToUString(string1);
469 icu::UnicodeString uString2 = EcmaStringToUString(string2);
470
471 UCollationResult result;
472 UErrorCode status = U_ZERO_ERROR;
473 result = icuCollator->compare(uString1, uString2, status);
474 ASSERT(U_SUCCESS(status));
475
476 return JSTaggedValue(result);
477 }
478
FastCompareStrings(JSThread * thread,const icu::Collator * icuCollator,const JSHandle<EcmaString> & string1,const JSHandle<EcmaString> & string2)479 JSTaggedValue JSCollator::FastCompareStrings(JSThread *thread, const icu::Collator *icuCollator,
480 const JSHandle<EcmaString> &string1,
481 const JSHandle<EcmaString> &string2)
482 {
483 if (*string1 == *string2) {
484 return JSTaggedValue(UCollationResult::UCOL_EQUAL);
485 }
486
487 auto flatString1 = JSHandle<EcmaString>(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), string1));
488 auto flatString2 = JSHandle<EcmaString>(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), string2));
489
490 UCollationResult result;
491 UErrorCode status = U_ZERO_ERROR;
492 {
493 DISALLOW_GARBAGE_COLLECTION;
494 CString str1 = ConvertToString(*flatString1, StringConvertedUsage::LOGICOPERATION);
495 icu::StringPiece stringPiece1(str1.c_str());
496 if (!stringPiece1.empty()) {
497 CString str2 = ConvertToString(*flatString2, StringConvertedUsage::LOGICOPERATION);
498 icu::StringPiece stringPiece2(str2.c_str());
499 if (!stringPiece2.empty()) {
500 result = icuCollator->compareUTF8(stringPiece1, stringPiece2, status);
501 return JSTaggedValue(result);
502 }
503 }
504
505 icu::UnicodeString uString1 = EcmaStringToUString(flatString1);
506 icu::UnicodeString uString2 = EcmaStringToUString(flatString2);
507
508 result = icuCollator->compare(uString1, uString2, status);
509 ASSERT(U_SUCCESS(status));
510 }
511 return JSTaggedValue(result);
512 }
513 } // namespace panda::ecmascript
514