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