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