• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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