• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 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/intl/locale_helper.h"
17 
18 #include "ecmascript/checkpoint/thread_state_transition.h"
19 #include "ecmascript/global_env.h"
20 #include "ecmascript/checkpoint/thread_state_transition.h"
21 
22 #if defined(__clang__)
23 #pragma clang diagnostic push
24 #pragma clang diagnostic ignored "-Wshadow"
25 #elif defined(__GNUC__)
26 #pragma GCC diagnostic push
27 #pragma GCC diagnostic ignored "-Wshadow"
28 #endif
29 #include "unicode/localebuilder.h"
30 #if defined(__clang__)
31 #pragma clang diagnostic pop
32 #elif defined(__GNUC__)
33 #pragma GCC diagnostic pop
34 #endif
35 
36 namespace panda::ecmascript::intl {
UStringToString(JSThread * thread,const icu::UnicodeString & string)37 JSHandle<EcmaString> LocaleHelper::UStringToString(JSThread *thread, const icu::UnicodeString &string)
38 {
39     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
40     return factory->NewFromUtf16(reinterpret_cast<const uint16_t *>(string.getBuffer()), string.length());
41 }
42 
UStringToString(JSThread * thread,const icu::UnicodeString & string,int32_t begin,int32_t end)43 JSHandle<EcmaString> LocaleHelper::UStringToString(JSThread *thread, const icu::UnicodeString &string, int32_t begin,
44                                                    int32_t end)
45 {
46     return UStringToString(thread, string.tempSubStringBetween(begin, end));
47 }
48 
49 // 9.2.1 CanonicalizeLocaleList ( locales )
CanonicalizeLocaleList(JSThread * thread,const JSHandle<JSTaggedValue> & locales)50 JSHandle<TaggedArray> LocaleHelper::CanonicalizeLocaleList(JSThread *thread, const JSHandle<JSTaggedValue> &locales)
51 {
52     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
53     // 1. If locales is undefined, then
54     //    a. Return a new empty List.
55     if (locales->IsUndefined()) {
56         return factory->EmptyArray();
57     }
58     // 2. Let seen be a new empty List.
59     JSHandle<TaggedArray> localeSeen = factory->NewTaggedArray(1);
60     // 3. If Type(locales) is String or Type(locales) is Object and locales has an [[InitializedLocale]] internal slot,
61     //    then
62     //    a. Let O be CreateArrayFromList(« locales »).
63     // 4. Else,
64     //    a.Let O be ? ToObject(locales).
65     if (locales->IsString()) {
66         JSHandle<EcmaString> tag = JSHandle<EcmaString>::Cast(locales);
67         JSHandle<TaggedArray> temp = factory->NewTaggedArray(1);
68         temp->Set(thread, 0, tag.GetTaggedValue());
69         JSHandle<JSArray> obj = JSArray::CreateArrayFromList(thread, temp);
70         JSHandle<TaggedArray> finalSeen = CanonicalizeHelper<JSArray>(thread, obj, localeSeen);
71         return finalSeen;
72 #ifdef ARK_SUPPORT_INTL
73     } else if (locales->IsJSLocale()) {
74         JSHandle<EcmaString> tag = JSLocale::ToString(thread, JSHandle<JSLocale>::Cast(locales));
75         JSHandle<TaggedArray> temp = factory->NewTaggedArray(1);
76         RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
77         temp->Set(thread, 0, tag.GetTaggedValue());
78         JSHandle<JSArray> obj = JSArray::CreateArrayFromList(thread, temp);
79         JSHandle<TaggedArray> finalSeen = CanonicalizeHelper<JSArray>(thread, obj, localeSeen);
80         return finalSeen;
81 #endif
82     } else {
83         JSHandle<JSObject> obj = JSTaggedValue::ToObject(thread, locales);
84         RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
85         JSHandle<TaggedArray> finalSeen = CanonicalizeHelper<JSObject>(thread, obj, localeSeen);
86         return finalSeen;
87     }
88     return localeSeen;
89 }
90 
91 template<typename T>
CanonicalizeHelper(JSThread * thread,JSHandle<T> & obj,JSHandle<TaggedArray> & seen)92 JSHandle<TaggedArray> LocaleHelper::CanonicalizeHelper(JSThread *thread, JSHandle<T> &obj, JSHandle<TaggedArray> &seen)
93 {
94     OperationResult operationResult = JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(obj),
95                                                                  thread->GlobalConstants()->GetHandledLengthString());
96     RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
97     JSTaggedNumber len = JSTaggedValue::ToLength(thread, operationResult.GetValue());
98     RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
99     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
100     // 2. Let seen be a new empty List.
101     uint32_t requestedLocalesLen = len.ToUint32();
102     seen = factory->NewTaggedArray(requestedLocalesLen);
103     // 6. Let k be 0.
104     // 7. Repeat, while k < len
105     JSMutableHandle<JSTaggedValue> pk(thread, JSTaggedValue::Undefined());
106     JSMutableHandle<JSTaggedValue> tag(thread, JSTaggedValue::Undefined());
107     uint32_t index = 0;
108     JSHandle<JSTaggedValue> objTagged = JSHandle<JSTaggedValue>::Cast(obj);
109     for (uint32_t k = 0; k < requestedLocalesLen; k++) {
110         // a. Let Pk be ToString(k).
111         JSHandle<JSTaggedValue> kHandle(thread, JSTaggedValue(k));
112         JSHandle<EcmaString> str = JSTaggedValue::ToString(thread, kHandle);
113         RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
114         pk.Update(str.GetTaggedValue());
115         // b. Let kPresent be ? HasProperty(O, Pk).
116         bool kPresent = JSTaggedValue::HasProperty(thread, objTagged, pk);
117         RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
118 
119         // c. If kPresent is true, then
120         if (kPresent) {
121             // i. Let kValue be ? Get(O, Pk).
122             OperationResult result = JSTaggedValue::GetProperty(thread, objTagged, pk);
123             RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
124             JSHandle<JSTaggedValue> kValue = result.GetValue();
125             // ii. If Type(kValue) is not String or Object, throw a TypeError exception.
126             if (!kValue->IsString() && !kValue->IsJSObject()) {
127                 THROW_TYPE_ERROR_AND_RETURN(thread, "kValue is not String or Object.", factory->EmptyArray());
128             }
129             // iii. If Type(kValue) is Object and kValue has an [[InitializedLocale]] internal slot, then
130             //        1. Let tag be kValue.[[Locale]].
131             // iv.  Else,
132             //        1. Let tag be ? ToString(kValue).
133 #ifdef ARK_SUPPORT_INTL
134             if (kValue->IsJSLocale()) {
135                 JSHandle<EcmaString> kValueStr = JSLocale::ToString(thread, JSHandle<JSLocale>::Cast(kValue));
136                 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
137                 tag.Update(kValueStr.GetTaggedValue());
138             } else {
139                 JSHandle<EcmaString> kValueString = JSTaggedValue::ToString(thread, kValue);
140                 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
141                 JSHandle<EcmaString> canonicalStr = CanonicalizeUnicodeLocaleId(thread, kValueString);
142                 RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
143                 tag.Update(canonicalStr.GetTaggedValue());
144             }
145 #else
146             JSHandle<EcmaString> kValueString = JSTaggedValue::ToString(thread, kValue);
147             RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
148             JSHandle<EcmaString> canonicalStr = CanonicalizeUnicodeLocaleId(thread, kValueString);
149             RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
150             tag.Update(canonicalStr.GetTaggedValue());
151 #endif
152             // vii. If canonicalizedTag is not an element of seen, append canonicalizedTag as the last element of seen.
153             bool isExist = false;
154             uint32_t seenLen = seen->GetLength();
155             for (uint32_t i = 0; i < seenLen; i++) {
156                 if (JSTaggedValue::SameValue(thread, seen->Get(thread, i), tag.GetTaggedValue())) {
157                     isExist = true;
158                 }
159             }
160             if (!isExist) {
161                 seen->Set(thread, index++, JSHandle<JSTaggedValue>::Cast(tag));
162             }
163         }
164         // d. Increase k by 1.
165     }
166     // set capacity
167     seen = TaggedArray::SetCapacity(thread, seen, index);
168     // 8. Return seen.
169     return seen;
170 }
171 
172 // 6.2.3 CanonicalizeUnicodeLocaleId( locale )
CanonicalizeUnicodeLocaleId(JSThread * thread,const JSHandle<EcmaString> & locale)173 JSHandle<EcmaString> LocaleHelper::CanonicalizeUnicodeLocaleId(JSThread *thread, const JSHandle<EcmaString> &locale)
174 {
175     [[maybe_unused]] ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
176     if (!IsStructurallyValidLanguageTag(thread, locale)) {
177         THROW_RANGE_ERROR_AND_RETURN(thread, "invalid locale", factory->GetEmptyString());
178     }
179 
180     if (EcmaStringAccessor(locale).GetLength() == 0 || EcmaStringAccessor(locale).IsUtf16()) {
181         THROW_RANGE_ERROR_AND_RETURN(thread, "invalid locale", factory->GetEmptyString());
182     }
183 
184     std::string localeCStr = ConvertToStdString(thread, locale);
185     std::transform(localeCStr.begin(), localeCStr.end(), localeCStr.begin(), AsciiAlphaToLower);
186     UErrorCode status = U_ZERO_ERROR;
187     icu::Locale formalLocale;
188     {
189         // Third party libs call can be in Native state
190         ThreadNativeScope nativeScope(thread);
191         formalLocale = icu::Locale::forLanguageTag(localeCStr.c_str(), status);
192     }
193     if ((U_FAILURE(status) != 0) || (formalLocale.isBogus() != 0)) {
194         THROW_RANGE_ERROR_AND_RETURN(thread, "invalid locale", factory->GetEmptyString());
195     }
196 
197     // Resets the LocaleBuilder to match the locale.
198     // Returns an instance of Locale created from the fields set on this builder.
199     formalLocale = icu::LocaleBuilder().setLocale(formalLocale).build(status);
200     // Canonicalize the locale ID of this object according to CLDR.
201     formalLocale.canonicalize(status);
202     if ((U_FAILURE(status) != 0) || (formalLocale.isBogus() != 0)) {
203         THROW_RANGE_ERROR_AND_RETURN(thread, "invalid locale", factory->GetEmptyString());
204     }
205     JSHandle<EcmaString> languageTag = ToLanguageTag(thread, formalLocale);
206     RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread);
207     return languageTag;
208 }
209 
ToStdStringLanguageTag(JSThread * thread,const icu::Locale & locale)210 std::string LocaleHelper::ToStdStringLanguageTag(JSThread *thread, const icu::Locale &locale)
211 {
212     UErrorCode status = U_ZERO_ERROR;
213     auto result = locale.toLanguageTag<std::string>(status);
214     if (U_FAILURE(status) != 0) {
215         THROW_RANGE_ERROR_AND_RETURN(thread, "invalid locale", "");
216     }
217     size_t findBeginning = result.find("-u-");
218     std::string finalRes;
219     std::string tempRes;
220     if (findBeginning == std::string::npos) {
221         return result;
222     }
223     size_t specialBeginning = findBeginning + INTL_INDEX_THREE;
224     size_t specialCount = 0;
225     while ((specialBeginning < result.size()) && (result[specialBeginning] != '-')) {
226         specialCount++;
227         specialBeginning++;
228     }
229     thread->CheckSafepointIfSuspended();
230     if (findBeginning != std::string::npos) {
231         // It begin with "-u-xx" or with more elements.
232         tempRes = result.substr(0, findBeginning + INTL_INDEX_THREE + specialCount);
233         if (result.size() <= findBeginning + INTL_INDEX_THREE + specialCount) {
234             return result;
235         }
236         std::string leftStr = result.substr(findBeginning + INTL_INDEX_THREE + specialCount + 1);
237         std::istringstream temp(leftStr);
238         std::string buffer;
239         std::vector<std::string> resContainer;
240         while (getline(temp, buffer, '-')) {
241             if (buffer != "true" && buffer != "yes") {
242                 resContainer.push_back(buffer);
243             }
244         }
245         for (auto it = resContainer.begin(); it != resContainer.end(); it++) {
246             std::string tag = "-";
247             tag += *it;
248             finalRes += tag;
249         }
250     }
251     if (!finalRes.empty()) {
252         tempRes += finalRes;
253     }
254     result = tempRes;
255     return result;
256 }
257 
ToLanguageTag(JSThread * thread,const icu::Locale & locale)258 JSHandle<EcmaString> LocaleHelper::ToLanguageTag(JSThread *thread, const icu::Locale &locale)
259 {
260     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
261     return factory->NewFromStdString(ToStdStringLanguageTag(thread, locale));
262 }
263 
264 // 6.2.2 IsStructurallyValidLanguageTag( locale )
IsStructurallyValidLanguageTag(JSThread * thread,const JSHandle<EcmaString> & tag)265 bool LocaleHelper::IsStructurallyValidLanguageTag(JSThread *thread, const JSHandle<EcmaString> &tag)
266 {
267     std::string tagCollection = ConvertToStdString(thread, tag);
268     std::vector<std::string> containers;
269     std::string substring;
270     std::set<std::string> uniqueSubtags;
271     size_t address = 1;
272     for (auto it = tagCollection.begin(); it != tagCollection.end(); it++) {
273         if (*it != '-' && it != tagCollection.end() - 1) {
274             substring += *it;
275         } else {
276             if (it == tagCollection.end() - 1) {
277                 substring += *it;
278             }
279             containers.push_back(substring);
280             if (IsVariantSubtag(substring)) {
281                 std::transform(substring.begin(), substring.end(), substring.begin(), AsciiAlphaToLower);
282                 if (!uniqueSubtags.insert(substring).second) {
283                     return false;
284                 }
285             }
286             substring.clear();
287         }
288     }
289     bool result = DealwithLanguageTag(containers, address);
290     return result;
291 }
292 
ConvertToStdString(const JSThread * thread,const JSHandle<EcmaString> & ecmaStr)293 std::string LocaleHelper::ConvertToStdString(const JSThread *thread, const JSHandle<EcmaString> &ecmaStr)
294 {
295     return std::string(ConvertToString(thread, *ecmaStr, StringConvertedUsage::LOGICOPERATION));
296 }
297 
DealwithLanguageTag(const std::vector<std::string> & containers,size_t & address)298 bool LocaleHelper::DealwithLanguageTag(const std::vector<std::string> &containers, size_t &address)
299 {
300     // The abstract operation returns true if locale can be generated from the ABNF grammar in section 2.1 of the RFC,
301     // starting with Language-Tag, and does not contain duplicate variant or singleton subtags
302     // If language tag is empty, return false.
303     if (containers.empty()) {
304         return false;
305     }
306 
307     // a. if the first tag is not language, return false.
308     if (!IsLanguageSubtag(containers[0])) {
309         return false;
310     }
311 
312     // if the tag include language only, like "zh" or "de", return true;
313     if (containers.size() == 1) {
314         return true;
315     }
316 
317     // Else, then
318     // if is unique singleton subtag, script and region tag.
319     if (IsExtensionSingleton(containers[1])) {
320         return true;
321     }
322 
323     if (IsScriptSubtag(containers[address])) {
324         address++;
325         if (containers.size() == address) {
326             return true;
327         }
328     }
329 
330     if (IsRegionSubtag(containers[address])) {
331         address++;
332     }
333 
334     for (size_t i = address; i < containers.size(); i++) {
335         if (IsExtensionSingleton(containers[i])) {
336             return true;
337         }
338         if (!IsVariantSubtag(containers[i])) {
339             return false;
340         }
341     }
342     return true;
343 }
344 
345 // 6.2.4 DefaultLocale ()
DefaultLocale(JSThread * thread)346 JSHandle<EcmaString> LocaleHelper::DefaultLocale(JSThread *thread)
347 {
348     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
349     return factory->NewFromStdString(StdStringDefaultLocale(thread));
350 }
351 
StdStringDefaultLocale(JSThread * thread)352 const std::string& LocaleHelper::StdStringDefaultLocale(JSThread *thread)
353 {
354     auto& intlCache = thread->GetEcmaVM()->GetIntlCache();
355     const std::string& cachedLocale = intlCache.GetDefaultLocale();
356     if (!cachedLocale.empty()) {
357         return cachedLocale;
358     }
359     icu::Locale defaultLocale;
360     if (strcmp(defaultLocale.getName(), "en_US_POSIX") == 0 || strcmp(defaultLocale.getName(), "c") == 0) {
361         intlCache.SetDefaultLocale("en-US");
362     } else if (defaultLocale.isBogus() != 0) {
363         intlCache.SetDefaultLocale("und");
364     } else {
365         intlCache.SetDefaultLocale(ToStdStringLanguageTag(thread, defaultLocale));
366     }
367     return intlCache.GetDefaultLocale();
368 }
369 
HandleLocaleExtension(size_t & start,size_t & extensionEnd,const std::string result,size_t len)370 void LocaleHelper::HandleLocaleExtension(size_t &start, size_t &extensionEnd, const std::string result, size_t len)
371 {
372     while (start < len - INTL_INDEX_TWO) {
373         if (result[start] != '-') {
374             start++;
375             continue;
376         }
377         if (result[start + INTL_INDEX_TWO] == '-') {
378             extensionEnd = start;
379             break;
380         }
381         start += INTL_INDEX_THREE;
382     }
383 }
384 
HandleLocale(JSThread * thread,const JSHandle<EcmaString> & localeString)385 LocaleHelper::ParsedLocale LocaleHelper::HandleLocale(JSThread *thread, const JSHandle<EcmaString> &localeString)
386 {
387     return LocaleHelper::HandleLocale(ConvertToStdString(thread, localeString));
388 }
389 
HandleLocale(const std::string & localeString)390 LocaleHelper::ParsedLocale LocaleHelper::HandleLocale(const std::string &localeString)
391 {
392     size_t len = localeString.size();
393     ParsedLocale parsedResult;
394 
395     // a. The single-character subtag ’x’ as the primary subtag indicates
396     //    that the language tag consists solely of subtags whose meaning is
397     //    defined by private agreement.
398     // b. Extensions cannot be used in tags that are entirely private use.
399     if (IsPrivateSubTag(localeString, len)) {
400         parsedResult.base = localeString;
401         return parsedResult;
402     }
403     // If cannot find "-u-", return the whole string as base.
404     size_t foundExtension = localeString.find("-u-");
405     if (foundExtension == std::string::npos) {
406         parsedResult.base = localeString;
407         return parsedResult;
408     }
409     // Let privateIndex be Call(%StringProto_indexOf%, foundLocale, « "-x-" »).
410     size_t privateIndex = localeString.find("-x-");
411     if (privateIndex != std::string::npos && privateIndex < foundExtension) {
412         parsedResult.base = localeString;
413         return parsedResult;
414     }
415     const std::string basis = localeString.substr(0, foundExtension);
416     size_t extensionEnd = len;
417     ASSERT(len > INTL_INDEX_TWO);
418     size_t start = foundExtension + 1;
419     HandleLocaleExtension(start, extensionEnd, localeString, len);
420     const std::string end = localeString.substr(extensionEnd);
421     parsedResult.base = basis + end;
422     parsedResult.extension = localeString.substr(foundExtension, extensionEnd - foundExtension);
423     return parsedResult;
424 }
425 
GetAvailableLocales(JSThread * thread,const char * localeKey,const char * localePath)426 std::vector<std::string> LocaleHelper::GetAvailableLocales(JSThread *thread, const char *localeKey,
427                                                            const char *localePath)
428 {
429     UErrorCode status = U_ZERO_ERROR;
430     auto globalConst = thread->GlobalConstants();
431     JSHandle<EcmaString> specialValue = JSHandle<EcmaString>::Cast(globalConst->GetHandledEnUsPosixString());
432     std::string specialString = ConvertToStdString(thread, specialValue);
433     UEnumeration *uenum = nullptr;
434     {
435         ThreadNativeScope nativeScope(thread);
436         uenum = uloc_openAvailableByType(ULOC_AVAILABLE_WITH_LEGACY_ALIASES, &status);
437     }
438     std::vector<std::string> allLocales;
439     const char *loc = nullptr;
440     // Third party libs computing can be in Native state
441     ThreadNativeScope nativeScope(thread);
442     for (loc = uenum_next(uenum, nullptr, &status); loc != nullptr; loc = uenum_next(uenum, nullptr, &status)) {
443         ASSERT(U_SUCCESS(status));
444         std::string locStr(loc);
445         std::replace(locStr.begin(), locStr.end(), '_', '-');
446         if (locStr == specialString) {
447             locStr = "en-US-u-va-posix";
448         }
449 
450         if (localePath != nullptr || localeKey != nullptr) {
451             icu::Locale locale(locStr.c_str());
452             bool res = false;
453             if (!CheckLocales(locale, localeKey, localePath, res)) {
454                 continue;
455             }
456         }
457         allLocales.push_back(locStr);
458         icu::Locale formalLocale = icu::Locale::createCanonical(locStr.c_str());
459         std::string scriptStr = formalLocale.getScript();
460         if (!scriptStr.empty()) {
461             std::string languageStr = formalLocale.getLanguage();
462             std::string countryStr = formalLocale.getCountry();
463             std::string shortLocale = icu::Locale(languageStr.c_str(), countryStr.c_str()).getName();
464             std::replace(shortLocale.begin(), shortLocale.end(), '_', '-');
465             allLocales.push_back(shortLocale);
466         }
467     }
468     uenum_close(uenum);
469     return allLocales;
470 }
471 
472 // 9.2.2 BestAvailableLocale ( availableLocales, locale )
BestAvailableLocale(const std::vector<std::string> & availableLocales,const std::string & locale)473 std::string LocaleHelper::BestAvailableLocale(const std::vector<std::string> &availableLocales,
474                                               const std::string &locale)
475 {
476     // 1. Let candidate be locale.
477     std::string localeCandidate = locale;
478     std::string undefined = std::string();
479     // 2. Repeat,
480     uint32_t length = availableLocales.size();
481     while (true) {
482         // a. If availableLocales contains an element equal to candidate, return candidate.
483         for (uint32_t i = 0; i < length; ++i) {
484             std::string itemStr = availableLocales[i];
485             if (itemStr == localeCandidate) {
486                 return localeCandidate;
487             }
488         }
489         // b. Let pos be the character index of the last occurrence of "-" (U+002D) within candidate.
490         //    If that character does not occur, return undefined.
491         size_t pos = localeCandidate.rfind('-');
492         if (pos == std::string::npos) {
493             return undefined;
494         }
495         // c. If pos ≥ 2 and the character "-" occurs at index pos-2 of candidate, decrease pos by 2.
496         if (pos >= INTL_INDEX_TWO && localeCandidate[pos - INTL_INDEX_TWO] == '-') {
497             pos -= INTL_INDEX_TWO;
498         }
499         // d. Let candidate be the substring of candidate from position 0, inclusive, to position pos, exclusive.
500         localeCandidate.resize(pos);
501     }
502 }
503 } // namespace panda::ecmascript::base