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/builtins/builtins_string.h"
17
18 #include <algorithm>
19 #include <vector>
20 #include <map>
21
22 #include "ecmascript/intl/locale_helper.h"
23 #include "ecmascript/builtins/builtins_number.h"
24 #include "ecmascript/builtins/builtins_regexp.h"
25 #include "ecmascript/builtins/builtins_symbol.h"
26 #include "ecmascript/interpreter/fast_runtime_stub-inl.h"
27 #include "ecmascript/js_primitive_ref.h"
28 #include "ecmascript/js_regexp.h"
29 #include "ecmascript/js_string_iterator.h"
30 #include "ecmascript/property_detector-inl.h"
31 #ifdef ARK_SUPPORT_INTL
32 #include "ecmascript/js_collator.h"
33 #include "ecmascript/js_locale.h"
34 #else
35 #ifndef ARK_NOT_SUPPORT_INTL_GLOBAL
36 #include "ecmascript/intl/global_intl_helper.h"
37 #endif
38 #endif
39
40 #include "unicode/normalizer2.h"
41 #include "unicode/normlzr.h"
42 #include "unicode/unistr.h"
43
44 namespace panda::ecmascript::builtins {
45 using ObjectFactory = ecmascript::ObjectFactory;
46 using JSArray = ecmascript::JSArray;
47 constexpr std::uint16_t CHAR16_LETTER_NULL = u'\0';
48
49 // 21.1.1.1 String(value)
StringConstructor(EcmaRuntimeCallInfo *argv)50 JSTaggedValue BuiltinsString::StringConstructor(EcmaRuntimeCallInfo *argv)
51 {
52 ASSERT(argv);
53 BUILTINS_API_TRACE(argv->GetThread(), String, Constructor);
54 JSThread *thread = argv->GetThread();
55 [[maybe_unused]] EcmaHandleScope handleScope(thread);
56 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
57 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
58 if (argv->GetArgsNumber() > 0) {
59 JSHandle<JSTaggedValue> valTagNew = GetCallArg(argv, 0);
60 if (newTarget->IsUndefined() && valTagNew->IsSymbol()) {
61 return BuiltinsSymbol::SymbolDescriptiveString(thread, valTagNew.GetTaggedValue());
62 }
63 JSHandle<EcmaString> str = JSTaggedValue::ToString(thread, valTagNew);
64 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
65 if (newTarget->IsUndefined()) {
66 return str.GetTaggedValue();
67 }
68 JSHandle<JSTaggedValue> strTag(str);
69 return JSPrimitiveRef::StringCreate(thread, strTag, newTarget).GetTaggedValue();
70 }
71 JSHandle<EcmaString> val = factory->GetEmptyString();
72 JSHandle<JSTaggedValue> valTag(val);
73 if (newTarget->IsUndefined()) {
74 return factory->GetEmptyString().GetTaggedValue();
75 }
76 return JSPrimitiveRef::StringCreate(thread, valTag, newTarget).GetTaggedValue();
77 }
78
79 // 21.1.2.1
FromCharCode(EcmaRuntimeCallInfo *argv)80 JSTaggedValue BuiltinsString::FromCharCode(EcmaRuntimeCallInfo *argv)
81 {
82 ASSERT(argv);
83 BUILTINS_API_TRACE(argv->GetThread(), String, FromCharCode);
84 JSThread *thread = argv->GetThread();
85 [[maybe_unused]] EcmaHandleScope handleScope(thread);
86 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
87 uint32_t argLength = argv->GetArgsNumber();
88 if (argLength == 0) {
89 return factory->GetEmptyString().GetTaggedValue();
90 }
91 if (argLength == 1) {
92 JSHandle<JSTaggedValue> codePointTag = BuiltinsString::GetCallArg(argv, 0);
93 uint16_t codePointValue = JSTaggedValue::ToUint16(thread, codePointTag);
94 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
95 if (EcmaStringAccessor::CanBeCompressed(&codePointValue, 1)) {
96 JSHandle<SingleCharTable> singleCharTable(thread, thread->GetSingleCharTable());
97 return singleCharTable->GetStringFromSingleCharTable(codePointValue);
98 }
99 JSHandle<EcmaString> strHandle = factory->NewFromUtf16Literal(&codePointValue, 1);
100 return strHandle.GetTaggedValue();
101 }
102 CVector<uint16_t> valueTable;
103 valueTable.reserve(argLength);
104 for (uint32_t i = 0; i < argLength; i++) {
105 JSHandle<JSTaggedValue> nextCp = BuiltinsString::GetCallArg(argv, i);
106 uint16_t nextCv = JSTaggedValue::ToUint16(thread, nextCp);
107 valueTable.emplace_back(nextCv);
108 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
109 }
110 return factory->NewFromUtf16Literal(valueTable.data(), valueTable.size()).GetTaggedValue();
111 }
112
113 // 21.1.2.2
FromCodePoint(EcmaRuntimeCallInfo *argv)114 JSTaggedValue BuiltinsString::FromCodePoint(EcmaRuntimeCallInfo *argv)
115 {
116 ASSERT(argv);
117 BUILTINS_API_TRACE(argv->GetThread(), String, FromCodePoint);
118 JSThread *thread = argv->GetThread();
119 [[maybe_unused]] EcmaHandleScope handleScope(thread);
120 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
121 uint32_t argLength = argv->GetArgsNumber();
122 if (argLength == 0) {
123 return factory->GetEmptyString().GetTaggedValue();
124 }
125 std::u16string u16str;
126 uint32_t u16strSize = argLength;
127 for (uint32_t i = 0; i < argLength; i++) {
128 JSHandle<JSTaggedValue> nextCpTag = BuiltinsString::GetCallArg(argv, i);
129 JSTaggedNumber nextCpVal = JSTaggedValue::ToNumber(thread, nextCpTag);
130 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
131 if (!nextCpVal.IsInteger()) {
132 THROW_RANGE_ERROR_AND_RETURN(thread, "is not integer", JSTaggedValue::Exception());
133 }
134 int32_t cp = nextCpVal.ToInt32();
135 if (cp < 0 || cp > ENCODE_MAX_UTF16) {
136 THROW_RANGE_ERROR_AND_RETURN(thread, "CodePoint < 0 or CodePoint > 0x10FFFF", JSTaggedValue::Exception());
137 }
138 if (cp > UINT16_MAX) {
139 uint16_t cu1 =
140 std::floor((static_cast<uint32_t>(cp) - ENCODE_SECOND_FACTOR) / ENCODE_FIRST_FACTOR) + ENCODE_LEAD_LOW;
141 uint16_t cu2 =
142 ((static_cast<uint32_t>(cp) - ENCODE_SECOND_FACTOR) % ENCODE_FIRST_FACTOR) + ENCODE_TRAIL_LOW;
143 std::u16string nextU16str1 = base::StringHelper::Utf16ToU16String(&cu1, 1);
144 std::u16string nextU16str2 = base::StringHelper::Utf16ToU16String(&cu2, 1);
145 base::StringHelper::InplaceAppend(u16str, nextU16str1);
146 base::StringHelper::InplaceAppend(u16str, nextU16str2);
147 u16strSize++;
148 } else {
149 auto u16tCp = static_cast<uint16_t>(cp);
150 std::u16string nextU16str = base::StringHelper::Utf16ToU16String(&u16tCp, 1);
151 base::StringHelper::InplaceAppend(u16str, nextU16str);
152 }
153 }
154 const char16_t *constChar16tData = u16str.data();
155 auto *char16tData = const_cast<char16_t *>(constChar16tData);
156 auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
157 return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue();
158 }
159
160 // 21.1.2.4
Raw(EcmaRuntimeCallInfo *argv)161 JSTaggedValue BuiltinsString::Raw(EcmaRuntimeCallInfo *argv)
162 {
163 ASSERT(argv);
164 BUILTINS_API_TRACE(argv->GetThread(), String, Raw);
165 JSThread *thread = argv->GetThread();
166 [[maybe_unused]] EcmaHandleScope handleScope(thread);
167 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
168 // Let cooked be ToObject(template).
169 JSHandle<JSObject> cooked = JSTaggedValue::ToObject(thread, BuiltinsString::GetCallArg(argv, 0));
170 // ReturnIfAbrupt(cooked).
171 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
172 // Let raw be ToObject(Get(cooked, "raw")).
173 JSHandle<JSTaggedValue> rawKey(factory->NewFromASCII("raw"));
174 JSHandle<JSTaggedValue> rawTag =
175 JSObject::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(cooked), rawKey).GetValue();
176 JSHandle<JSObject> rawObj = JSTaggedValue::ToObject(thread, rawTag);
177 // ReturnIfAbrupt(rawObj).
178 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
179 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
180 JSHandle<JSTaggedValue> rawLen =
181 JSObject::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(rawObj), lengthKey).GetValue();
182 // ReturnIfAbrupt(rawLen).
183 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
184 JSTaggedNumber lengthNumber = JSTaggedValue::ToLength(thread, rawLen);
185 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
186 int length = static_cast<int>(lengthNumber.ToUint32());
187 if (length <= 0) {
188 return factory->GetEmptyString().GetTaggedValue();
189 }
190
191 std::u16string u16str;
192 uint32_t argc = argv->GetArgsNumber() - 1;
193 bool canBeCompress = true;
194 for (uint32_t i = 0, argsI = 1; i < static_cast<uint32_t>(length); ++i, ++argsI) {
195 // Let nextSeg be ToString(Get(raw, nextKey)).
196 JSHandle<JSTaggedValue> elementString =
197 JSObject::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(rawObj), i).GetValue();
198 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
199 EcmaString *nextSeg = *JSTaggedValue::ToString(thread, elementString);
200 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
201 u16str += EcmaStringAccessor(nextSeg).ToU16String();
202 if (EcmaStringAccessor(nextSeg).IsUtf16()) {
203 canBeCompress = false;
204 }
205 if (i + 1 == static_cast<uint32_t>(length)) {
206 break;
207 }
208 if (argsI <= argc) {
209 EcmaString *nextSub = *JSTaggedValue::ToString(thread, GetCallArg(argv, argsI));
210 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
211 u16str += EcmaStringAccessor(nextSub).ToU16String();
212 if (EcmaStringAccessor(nextSub).IsUtf16()) {
213 canBeCompress = false;
214 }
215 }
216 }
217 // return the result string
218 auto *uint16tData = reinterpret_cast<uint16_t *>(const_cast<char16_t *>(u16str.data()));
219 return canBeCompress ? factory->NewFromUtf16LiteralCompress(uint16tData, u16str.size()).GetTaggedValue() :
220 factory->NewFromUtf16LiteralNotCompress(uint16tData, u16str.size()).GetTaggedValue();
221 }
222
223 // 21.1.3.1
CharAt(EcmaRuntimeCallInfo *argv)224 JSTaggedValue BuiltinsString::CharAt(EcmaRuntimeCallInfo *argv)
225 {
226 ASSERT(argv);
227 BUILTINS_API_TRACE(argv->GetThread(), String, CharAt);
228 JSThread *thread = argv->GetThread();
229 [[maybe_unused]] EcmaHandleScope handleScope(thread);
230 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
231 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
232 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
233 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
234 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
235 JSHandle<EcmaString> thisFlat(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), thisHandle));
236 int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisFlat).GetLength());
237 JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 0);
238 int32_t pos = 0;
239 if (posTag->IsInt()) {
240 pos = posTag->GetInt();
241 } else if (posTag->IsUndefined()) {
242 pos = 0;
243 } else {
244 JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
245 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
246 double valueNumber = posVal.GetNumber();
247 if (!std::isfinite(valueNumber)) {
248 return factory->GetEmptyString().GetTaggedValue();
249 }
250 pos = posVal.ToInt32();
251 }
252 if (pos < 0 || pos >= thisLen) {
253 return factory->GetEmptyString().GetTaggedValue();
254 }
255 uint16_t res = EcmaStringAccessor(thisFlat).Get<false>(pos);
256 if (EcmaStringAccessor::CanBeCompressed(&res, 1)) {
257 JSHandle<SingleCharTable> singleCharTable(thread, thread->GetSingleCharTable());
258 return singleCharTable->GetStringFromSingleCharTable(res);
259 }
260 return factory->NewFromUtf16Literal(&res, 1).GetTaggedValue();
261 }
262
263 // 21.1.3.2
CharCodeAt(EcmaRuntimeCallInfo *argv)264 JSTaggedValue BuiltinsString::CharCodeAt(EcmaRuntimeCallInfo *argv)
265 {
266 ASSERT(argv);
267 BUILTINS_API_TRACE(argv->GetThread(), String, CharCodeAt);
268 JSThread *thread = argv->GetThread();
269 [[maybe_unused]] EcmaHandleScope handleScope(thread);
270 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
271 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
272 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
273 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
274 JSHandle<EcmaString> thisFlat(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), thisHandle));
275 int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisFlat).GetLength());
276 JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 0);
277 int32_t pos = 0;
278 if (posTag->IsInt()) {
279 pos = posTag->GetInt();
280 } else if (posTag->IsUndefined()) {
281 pos = 0;
282 } else {
283 JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
284 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
285 double valueNumber = posVal.GetNumber();
286 if (!std::isfinite(valueNumber)) {
287 return GetTaggedDouble(base::NAN_VALUE);
288 }
289 pos = posVal.ToInt32();
290 }
291 if (pos < 0 || pos >= thisLen) {
292 return GetTaggedDouble(base::NAN_VALUE);
293 }
294 uint16_t ret = EcmaStringAccessor(thisFlat).Get<false>(pos);
295 return GetTaggedInt(ret);
296 }
297
298 // 21.1.3.3
CodePointAt(EcmaRuntimeCallInfo *argv)299 JSTaggedValue BuiltinsString::CodePointAt(EcmaRuntimeCallInfo *argv)
300 {
301 ASSERT(argv);
302 BUILTINS_API_TRACE(argv->GetThread(), String, CodePointAt);
303 JSThread *thread = argv->GetThread();
304 [[maybe_unused]] EcmaHandleScope handleScope(thread);
305 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
306 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
307 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
308 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
309 JSHandle<EcmaString> thisFlat(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), thisHandle));
310 JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 0);
311
312 JSTaggedNumber posVal = JSTaggedValue::ToNumber(thread, posTag);
313 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
314 int32_t pos = base::NumberHelper::DoubleInRangeInt32(posVal.GetNumber());
315 int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisFlat).GetLength());
316 if (pos < 0 || pos >= thisLen) {
317 return JSTaggedValue::Undefined();
318 }
319 uint16_t first = EcmaStringAccessor(thisFlat).Get<false>(pos);
320 if (first < base::utf_helper::DECODE_LEAD_LOW || first > base::utf_helper::DECODE_LEAD_HIGH || pos + 1 == thisLen) {
321 return GetTaggedInt(first);
322 }
323 uint16_t second = EcmaStringAccessor(thisFlat).Get<false>(pos + 1);
324 if (second < base::utf_helper::DECODE_TRAIL_LOW || second > base::utf_helper::DECODE_TRAIL_HIGH) {
325 return GetTaggedInt(first);
326 }
327 uint32_t res = base::utf_helper::UTF16Decode(first, second);
328 return GetTaggedInt(res);
329 }
330
331 // 21.1.3.4
Concat(EcmaRuntimeCallInfo *argv)332 JSTaggedValue BuiltinsString::Concat(EcmaRuntimeCallInfo *argv)
333 {
334 ASSERT(argv);
335 BUILTINS_API_TRACE(argv->GetThread(), String, Concat);
336 JSThread *thread = argv->GetThread();
337 [[maybe_unused]] EcmaHandleScope handleScope(thread);
338 auto ecmaVm = thread->GetEcmaVM();
339 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
340 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
341 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
342 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
343 uint32_t argLength = argv->GetArgsNumber();
344 if (argLength == 0) {
345 return thisHandle.GetTaggedValue();
346 }
347 for (uint32_t i = 0; i < argLength; i++) {
348 JSHandle<JSTaggedValue> nextTag = BuiltinsString::GetCallArg(argv, i);
349 JSHandle<EcmaString> nextHandle = JSTaggedValue::ToString(thread, nextTag);
350 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
351 EcmaString *tempStr = EcmaStringAccessor::Concat(ecmaVm, thisHandle, nextHandle);
352 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
353 thisHandle = JSHandle<EcmaString>(thread, tempStr);
354 }
355 return thisHandle.GetTaggedValue();
356 }
357
358 // 21.1.3.5 String.prototype.constructor
359 // 21.1.3.6
EndsWith(EcmaRuntimeCallInfo *argv)360 JSTaggedValue BuiltinsString::EndsWith(EcmaRuntimeCallInfo *argv)
361 {
362 ASSERT(argv);
363 BUILTINS_API_TRACE(argv->GetThread(), String, EndsWith);
364 JSThread *thread = argv->GetThread();
365 [[maybe_unused]] EcmaHandleScope handleScope(thread);
366 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
367 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
368 JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
369 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
370 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
371 bool isRegexp = JSObject::IsRegExp(thread, searchTag);
372 if (isRegexp) {
373 THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception());
374 }
375 JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
376 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
377 uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
378 uint32_t searchLen = EcmaStringAccessor(searchHandle).GetLength();
379 int32_t pos = 0;
380 JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 1);
381 if (posTag->IsUndefined()) {
382 pos = static_cast<int32_t>(thisLen);
383 } else {
384 JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
385 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
386 if (posVal.GetNumber() == BuiltinsNumber::POSITIVE_INFINITY) {
387 pos = static_cast<int32_t>(thisLen);
388 } else {
389 pos = base::NumberHelper::DoubleInRangeInt32(posVal.GetNumber());
390 }
391 }
392 pos = std::min(std::max(pos, 0), static_cast<int32_t>(thisLen));
393 int32_t start = pos - static_cast<int32_t>(searchLen);
394 if (start < 0) {
395 return BuiltinsString::GetTaggedBoolean(false);
396 }
397
398 int32_t idx = EcmaStringAccessor::IndexOf(thread->GetEcmaVM(), thisHandle, searchHandle, start);
399 if (idx == start) {
400 return BuiltinsString::GetTaggedBoolean(true);
401 }
402 return BuiltinsString::GetTaggedBoolean(false);
403 }
404
405 // 21.1.3.7
Includes(EcmaRuntimeCallInfo *argv)406 JSTaggedValue BuiltinsString::Includes(EcmaRuntimeCallInfo *argv)
407 {
408 ASSERT(argv);
409 BUILTINS_API_TRACE(argv->GetThread(), String, Includes);
410 JSThread *thread = argv->GetThread();
411 [[maybe_unused]] EcmaHandleScope handleScope(thread);
412 JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
413 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
414 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
415 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
416 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
417 bool isRegexp = JSObject::IsRegExp(thread, searchTag);
418 if (isRegexp) {
419 THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception());
420 }
421 JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
422 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
423 uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
424 int32_t pos = 0;
425 JSHandle<JSTaggedValue> posTag = BuiltinsBase::GetCallArg(argv, 1);
426 if (argv->GetArgsNumber() == 1) {
427 pos = 0;
428 } else {
429 JSTaggedNumber posVal = JSTaggedValue::ToNumber(thread, posTag);
430 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
431 pos = base::NumberHelper::DoubleInRangeInt32(posVal.GetNumber());
432 }
433 int32_t start = std::min(std::max(pos, 0), static_cast<int32_t>(thisLen));
434 int32_t idx = EcmaStringAccessor::IndexOf(thread->GetEcmaVM(), thisHandle, searchHandle, start);
435 if (idx < 0 || idx > static_cast<int32_t>(thisLen)) {
436 return BuiltinsString::GetTaggedBoolean(false);
437 }
438 return BuiltinsString::GetTaggedBoolean(true);
439 }
440
441 // 21.1.3.8
IndexOf(EcmaRuntimeCallInfo *argv)442 JSTaggedValue BuiltinsString::IndexOf(EcmaRuntimeCallInfo *argv)
443 {
444 ASSERT(argv);
445 BUILTINS_API_TRACE(argv->GetThread(), String, IndexOf);
446 JSThread *thread = argv->GetThread();
447 [[maybe_unused]] EcmaHandleScope handleScope(thread);
448 JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
449 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
450 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
451 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
452 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
453 uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
454 JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
455 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
456 JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 1);
457 int32_t pos = 0;
458 if (posTag->IsInt()) {
459 pos = posTag->GetInt();
460 } else if (posTag->IsUndefined()) {
461 pos = 0;
462 } else {
463 JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
464 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
465 pos = posVal.ToInt32();
466 }
467 pos = std::min(std::max(pos, 0), static_cast<int32_t>(thisLen));
468 // If searching for an null string.
469 if (EcmaStringAccessor(searchHandle).GetLength() == 0) {
470 return GetTaggedInt(pos);
471 }
472 int32_t res = EcmaStringAccessor::IndexOf(thread->GetEcmaVM(), thisHandle, searchHandle, pos);
473 if (res >= 0 && res < static_cast<int32_t>(thisLen)) {
474 return GetTaggedInt(res);
475 }
476 return GetTaggedInt(-1);
477 }
478
479 // 21.1.3.9
LastIndexOf(EcmaRuntimeCallInfo *argv)480 JSTaggedValue BuiltinsString::LastIndexOf(EcmaRuntimeCallInfo *argv)
481 {
482 ASSERT(argv);
483 BUILTINS_API_TRACE(argv->GetThread(), String, LastIndexOf);
484 JSThread *thread = argv->GetThread();
485 [[maybe_unused]] EcmaHandleScope handleScope(thread);
486 JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
487 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
488 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
489 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
490 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
491 int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
492 JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
493 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
494 int32_t pos = 0;
495 if (argv->GetArgsNumber() == 1) {
496 pos = thisLen;
497 } else {
498 JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 1);
499 JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
500 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
501 if (std::isnan(JSTaggedValue::ToNumber(thread, posTag).GetNumber())) {
502 pos = thisLen;
503 } else {
504 pos = posVal.ToInt32();
505 }
506 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
507 }
508 pos = std::min(std::max(pos, 0), thisLen);
509 int32_t res = EcmaStringAccessor::LastIndexOf(thread->GetEcmaVM(), thisHandle, searchHandle, pos);
510 if (res >= 0 && res < thisLen) {
511 return GetTaggedInt(res);
512 }
513 res = -1;
514 return GetTaggedInt(static_cast<int32_t>(res));
515 }
516
517 // 21.1.3.10
LocaleCompare(EcmaRuntimeCallInfo *argv)518 JSTaggedValue BuiltinsString::LocaleCompare(EcmaRuntimeCallInfo *argv)
519 {
520 ASSERT(argv);
521 BUILTINS_API_TRACE(argv->GetThread(), String, LocaleCompare);
522 JSThread *thread = argv->GetThread();
523 [[maybe_unused]] EcmaHandleScope handleScope(thread);
524 JSHandle<JSTaggedValue> thatTag = BuiltinsString::GetCallArg(argv, 0);
525 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
526 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
527 [[maybe_unused]] JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
528 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
529 [[maybe_unused]] JSHandle<EcmaString> thatHandle = JSTaggedValue::ToString(thread, thatTag);
530 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
531
532 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 1);
533 JSHandle<JSTaggedValue> options = GetCallArg(argv, 2); // 2: the second argument
534 return DoLocaleCompare(thread, thisHandle, thatHandle, locales, options);
535 }
536
DoLocaleCompare(JSThread *thread, const JSHandle<EcmaString> &thisHandle, const JSHandle<EcmaString> &thatHandle, const JSHandle<JSTaggedValue> &locales, const JSHandle<JSTaggedValue> &options)537 JSTaggedValue BuiltinsString::DoLocaleCompare(JSThread *thread,
538 const JSHandle<EcmaString> &thisHandle,
539 const JSHandle<EcmaString> &thatHandle,
540 const JSHandle<JSTaggedValue> &locales,
541 const JSHandle<JSTaggedValue> &options)
542 {
543 [[maybe_unused]] bool cacheable = (locales->IsUndefined() || locales->IsString()) && options->IsUndefined();
544 const CompareStringsOption csOption = JSCollator::CompareStringsOptionFor(thread, locales, options);
545 #ifdef ARK_SUPPORT_INTL
546 if (cacheable) {
547 auto collator = JSCollator::GetCachedIcuCollator(thread, locales);
548 if (collator != nullptr) {
549 JSTaggedValue result = JSCollator::CompareStrings(thread, collator, thisHandle, thatHandle, csOption);
550 return result;
551 }
552 }
553 return LocaleCompareGC(thread, thisHandle, thatHandle, locales, options, csOption, cacheable);
554 #else
555 #ifdef ARK_NOT_SUPPORT_INTL_GLOBAL
556 ARK_SUPPORT_INTL_RETURN_JSVALUE(thread, "LocaleCompare");
557 #else
558 intl::GlobalIntlHelper gh(thread, intl::GlobalFormatterType::Collator);
559 auto collator = gh.GetGlobalObject<intl::GlobalCollator>(thread,
560 locales, options, intl::GlobalFormatterType::Collator, cacheable);
561 if (collator == nullptr) {
562 LOG_ECMA(ERROR) << "BuiltinsString::LocaleCompare:collator is nullptr";
563 }
564 ASSERT(collator != nullptr);
565 auto result = collator->Compare(EcmaStringAccessor(thisHandle).ToStdString(),
566 EcmaStringAccessor(thatHandle).ToStdString());
567 return JSTaggedValue(result);
568 #endif
569 #endif
570 }
571
LocaleCompareGC(JSThread *thread, const JSHandle<EcmaString> &thisHandle, const JSHandle<EcmaString> &thatHandle, const JSHandle<JSTaggedValue> &locales, const JSHandle<JSTaggedValue> &options, CompareStringsOption csOption, bool cacheable)572 JSTaggedValue BuiltinsString::LocaleCompareGC(JSThread *thread,
573 const JSHandle<EcmaString> &thisHandle,
574 const JSHandle<EcmaString> &thatHandle,
575 const JSHandle<JSTaggedValue> &locales,
576 const JSHandle<JSTaggedValue> &options,
577 CompareStringsOption csOption,
578 bool cacheable)
579 {
580 EcmaVM *ecmaVm = thread->GetEcmaVM();
581 ObjectFactory *factory = ecmaVm->GetFactory();
582 JSHandle<JSTaggedValue> ctor = ecmaVm->GetGlobalEnv()->GetCollatorFunction();
583 JSHandle<JSCollator> collator =
584 JSHandle<JSCollator>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor)));
585 JSHandle<JSCollator> initCollator =
586 JSCollator::InitializeCollator(thread, collator, locales, options, cacheable);
587 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
588 icu::Collator *icuCollator = nullptr;
589 if (cacheable) {
590 icuCollator = JSCollator::GetCachedIcuCollator(thread, locales);
591 ASSERT(icuCollator != nullptr);
592 } else {
593 icuCollator = initCollator->GetIcuCollator();
594 }
595 JSTaggedValue result = JSCollator::CompareStrings(thread, icuCollator, thisHandle, thatHandle, csOption);
596 return result;
597 }
598
599
600 // 21.1.3.11
Match(EcmaRuntimeCallInfo *argv)601 JSTaggedValue BuiltinsString::Match(EcmaRuntimeCallInfo *argv)
602 {
603 ASSERT(argv);
604 BUILTINS_API_TRACE(argv->GetThread(), String, Match);
605 JSThread *thread = argv->GetThread();
606 [[maybe_unused]] EcmaHandleScope handleScope(thread);
607 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
608 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
609 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
610 JSHandle<JSTaggedValue> regexp = BuiltinsString::GetCallArg(argv, 0);
611 if (thisTag->IsString() && regexp->IsECMAObject()) {
612 if (BuiltinsRegExp::IsFastRegExp(thread, regexp, BuiltinsRegExp::RegExpSymbol::MATCH)) {
613 return BuiltinsRegExp::RegExpMatch(thread, regexp, thisTag, true);
614 }
615 }
616
617 JSHandle<JSTaggedValue> matchTag = thread->GetEcmaVM()->GetGlobalEnv()->GetMatchSymbol();
618 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
619 if (!regexp->IsUndefined() && !regexp->IsNull()) {
620 JSHandle<JSTaggedValue> matcher = JSObject::GetMethod(thread, regexp, matchTag);
621 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
622 if (!matcher->IsUndefined()) {
623 ASSERT(matcher->IsJSFunctionBase());
624 EcmaRuntimeCallInfo *info =
625 EcmaInterpreter::NewRuntimeCallInfo(thread, matcher, regexp, undefined, 1);
626 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
627 info->SetCallArg(thisTag.GetTaggedValue());
628 return JSFunction::Call(info);
629 }
630 }
631 JSHandle<EcmaString> thisVal = JSTaggedValue::ToString(thread, thisTag);
632 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
633 JSHandle<JSTaggedValue> undifinedHandle = globalConst->GetHandledUndefined();
634 JSHandle<JSTaggedValue> rx(thread, BuiltinsRegExp::RegExpCreate(thread, regexp, undifinedHandle));
635 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
636 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, rx, undefined, 1);
637 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
638 info->SetCallArg(thisVal.GetTaggedValue());
639 return JSFunction::Invoke(info, matchTag);
640 }
641
MatchAll(EcmaRuntimeCallInfo *argv)642 JSTaggedValue BuiltinsString::MatchAll(EcmaRuntimeCallInfo *argv)
643 {
644 ASSERT(argv);
645 BUILTINS_API_TRACE(argv->GetThread(), String, MatchAll);
646 JSThread *thread = argv->GetThread();
647 [[maybe_unused]] EcmaHandleScope handleScope(thread);
648 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
649 // 1. Let O be ? RequireObjectCoercible(this value).
650 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
651 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
652 JSHandle<JSTaggedValue> regexp = BuiltinsString::GetCallArg(argv, 0);
653 EcmaVM *ecmaVm = thread->GetEcmaVM();
654 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
655 JSHandle<JSTaggedValue> matchAllTag = env->GetMatchAllSymbol();
656 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
657
658 // 2. If regexp is neither undefined nor null, then
659 if (!regexp->IsUndefined() && !regexp->IsNull()) {
660 // a. Let isRegExp be ? IsRegExp(searchValue).
661 if (regexp->IsECMAObject() &&
662 BuiltinsRegExp::IsFastRegExp(thread, regexp, BuiltinsRegExp::RegExpSymbol::MATCH)) {
663 bool isGlobal = BuiltinsRegExp::GetOriginalFlag(thread, regexp, RegExpParser::FLAG_GLOBAL);
664 if (!isGlobal) {
665 THROW_TYPE_ERROR_AND_RETURN(thread,
666 "matchAll called with a non-global RegExp argument",
667 JSTaggedValue::Exception());
668 }
669 } else if (JSObject::IsRegExp(thread, regexp)) {
670 // i. Let flags be ? Get(searchValue, "flags").
671 JSHandle<JSTaggedValue> flagsString(globalConst->GetHandledFlagsString());
672 JSHandle<JSTaggedValue> flags = JSObject::GetProperty(thread, regexp, flagsString).GetValue();
673 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
674 // ii. Perform ? RequireObjectCoercible(flags).
675 JSTaggedValue::RequireObjectCoercible(thread, flags);
676 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
677 // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception.
678 JSHandle<EcmaString> flagString = JSTaggedValue::ToString(thread, flags);
679 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
680 int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm,
681 flagString, ecmaVm->GetFactory()->NewFromASCII("g"));
682 if (pos == -1) {
683 THROW_TYPE_ERROR_AND_RETURN(thread,
684 "matchAll called with a non-global RegExp argument",
685 JSTaggedValue::Exception());
686 }
687 }
688
689 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
690 if (thisTag->IsString() && regexp->IsECMAObject()) {
691 if (PropertyDetector::IsRegExpSpeciesDetectorValid(env) &&
692 BuiltinsRegExp::IsFastRegExp(thread, regexp, BuiltinsRegExp::RegExpSymbol::MATCHALL)) {
693 JSHandle<EcmaString> string = JSHandle<EcmaString>::Cast(thisTag);
694 return BuiltinsRegExp::RegExpMatchAll(thread, regexp, string, true);
695 }
696 }
697 // c. Let matcher be ? GetMethod(regexp, @@matchAll).
698 // d. If matcher is not undefined, then
699 bool canSkip = (PropertyDetector::IsNumberStringNotRegexpLikeDetectorValid(env) &&
700 (regexp->IsString() || regexp->IsNumber()));
701 if (!canSkip) {
702 JSHandle<JSTaggedValue> matcher = JSObject::GetMethod(thread, regexp, matchAllTag);
703 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
704 if (!matcher->IsUndefined()) {
705 // i. Return ? Call(matcher, regexp, « O »).
706 EcmaRuntimeCallInfo *info =
707 EcmaInterpreter::NewRuntimeCallInfo(thread, matcher, regexp, undefined, 1);
708 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
709 info->SetCallArg(thisTag.GetTaggedValue());
710 return JSFunction::Call(info);
711 }
712 }
713 }
714 // 3. Let S be ? ToString(O).
715 JSHandle<EcmaString> thisVal = JSTaggedValue::ToString(thread, thisTag);
716 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
717 // 4. Let rx be ? RegExpCreate(regexp, "g").
718 JSHandle<JSTaggedValue> rx(thread, BuiltinsRegExp::RegExpCreate(
719 thread, regexp, JSHandle<JSTaggedValue>(ecmaVm->GetFactory()->NewFromASCII("g"))));
720 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
721 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, rx, undefined, 1);
722 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
723 info->SetCallArg(thisVal.GetTaggedValue());
724 return JSFunction::Invoke(info, matchAllTag);
725 }
726
IsWellFormed(EcmaRuntimeCallInfo *argv)727 JSTaggedValue BuiltinsString::IsWellFormed(EcmaRuntimeCallInfo *argv)
728 {
729 ASSERT(argv);
730 BUILTINS_API_TRACE(argv->GetThread(), String, IsWellFormed);
731 JSThread *thread = argv->GetThread();
732 [[maybe_unused]] EcmaHandleScope handleScope(thread);
733
734 // 1. Let O be ? RequireObjectCoercible(this value).
735 JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
736 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
737
738 // 2. Let S be ? ToString(O).
739 JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
740 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
741
742 // 3. Return IsStringWellFormedUnicode(S).
743 uint32_t size = EcmaStringAccessor(string).GetLength();
744 uint32_t position = 0;
745 while (position < size) {
746 // i.Let first be the code unit at index position within string.
747 uint16_t first = EcmaStringAccessor(string).Get(position);
748 uint32_t cp = first - CHAR16_LETTER_NULL;
749 uint8_t codeUnitCount = 0;
750 bool isUnpairedSurrogate = false;
751 // ii. If first is neither a leading surrogate nor a trailing surrogate, then
752 // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.
753 if (!IsUTF16HighSurrogate(first) && !IsUTF16LowSurrogate(first)) {
754 codeUnitCount = 1; // 1 means: code unit count
755 isUnpairedSurrogate = false;
756 } else if (IsUTF16HighSurrogate(first) && position + 1 == size) {
757 // iii. If first is a trailing surrogate or position + 1 = size, then
758 // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
759 codeUnitCount = 1;
760 isUnpairedSurrogate = true;
761 } else {
762 // iv. Let second be the code unit at index position + 1 within string.
763 uint16_t second = EcmaStringAccessor(string).Get(position + 1);
764 // v. If second is not a trailing surrogate, then
765 // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
766 if (!IsUTF16LowSurrogate(second)) {
767 codeUnitCount = 1; // 1 means: code unit count
768 isUnpairedSurrogate = true;
769 } else {
770 // vi. Set cp to UTF16SurrogatePairToCodePoint(first, second).
771 // vii. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.
772 cp = UTF16SurrogatePairToCodePoint(first, second);
773 codeUnitCount = 2; // 2 means: code unit count
774 isUnpairedSurrogate = false;
775 }
776 }
777 if (isUnpairedSurrogate) {
778 return JSTaggedValue::False();
779 } else {
780 position = position + codeUnitCount;
781 }
782 thread->CheckSafepointIfSuspended();
783 }
784 return JSTaggedValue::True();
785 }
786
ToWellFormed(EcmaRuntimeCallInfo *argv)787 JSTaggedValue BuiltinsString::ToWellFormed(EcmaRuntimeCallInfo *argv)
788 {
789 ASSERT(argv);
790 BUILTINS_API_TRACE(argv->GetThread(), String, ToWellFormed);
791 JSThread *thread = argv->GetThread();
792 [[maybe_unused]] EcmaHandleScope handleScope(thread);
793 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
794
795 // 1. Let O be ? RequireObjectCoercible(this value).
796 JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
797 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
798
799 // 2. Let S be ? ToString(O).
800 JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
801 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
802
803 // 3. Let strLen be the length of S.
804 // 4. Let k be 0.
805 uint32_t size = EcmaStringAccessor(string).GetLength();
806 uint32_t position = 0;
807
808 // 5. Let result be the empty String.
809 std::u16string r;
810
811 // Repeat, while k < strLen,
812 // a. Let cp be CodePointAt(S, k).
813 // b. If cp.[[IsUnpairedSurrogate]] is true, then
814 // i. Set result to the string-concatenation of result and 0xFFFD (REPLACEMENT CHARACTER).
815 // c. Else,
816 // i. Set result to the string-concatenation of result and UTF16EncodeCodePoint(cp.[[CodePoint]]).
817 // d. Set k to k + cp.[[CodeUnitCount]].
818 while (position < size) {
819 // i.Let first be the code unit at index position within string.
820 uint16_t first = EcmaStringAccessor(string).Get(position);
821 uint32_t cp = first - CHAR16_LETTER_NULL;
822 uint8_t codeUnitCount = 0;
823 bool isUnpairedSurrogate = false;
824 // ii. If first is neither a leading surrogate nor a trailing surrogate, then
825 // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.
826 if (!IsUTF16HighSurrogate(first) && !IsUTF16LowSurrogate(first)) {
827 codeUnitCount = 1; // 1 means: code unit count
828 isUnpairedSurrogate = false;
829 } else if (IsUTF16HighSurrogate(first) && position + 1 == size) {
830 // iii. If first is a trailing surrogate or position + 1 = size, then
831 // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
832 codeUnitCount = 1;
833 isUnpairedSurrogate = true;
834 } else {
835 // iv. Let second be the code unit at index position + 1 within string.
836 uint16_t second = EcmaStringAccessor(string).Get(position + 1);
837 // v. If second is not a trailing surrogate, then
838 // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
839 if (!IsUTF16LowSurrogate(second)) {
840 codeUnitCount = 1; // 1 means: code unit count
841 isUnpairedSurrogate = true;
842 } else {
843 // vi. Set cp to UTF16SurrogatePairToCodePoint(first, second).
844 // vii. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.
845 cp = UTF16SurrogatePairToCodePoint(first, second);
846 codeUnitCount = 2; // 2 means: code unit count
847 isUnpairedSurrogate = false;
848 }
849 }
850 if (isUnpairedSurrogate) {
851 r.push_back(0xFFFD);
852 } else {
853 if (cp < 0 || cp > ENCODE_MAX_UTF16) {
854 THROW_RANGE_ERROR_AND_RETURN(thread, "CodePoint < 0 or CodePoint > 0x10FFFF",
855 JSTaggedValue::Exception());
856 }
857 if (cp > UINT16_MAX) {
858 uint16_t cu1 = std::floor((static_cast<uint32_t>(cp) - ENCODE_SECOND_FACTOR) /
859 ENCODE_FIRST_FACTOR) + ENCODE_LEAD_LOW;
860 uint16_t cu2 = ((static_cast<uint32_t>(cp) - ENCODE_SECOND_FACTOR) %
861 ENCODE_FIRST_FACTOR) + ENCODE_TRAIL_LOW;
862 std::u16string nextU16str1 = base::StringHelper::Utf16ToU16String(&cu1, 1);
863 std::u16string nextU16str2 = base::StringHelper::Utf16ToU16String(&cu2, 1);
864 base::StringHelper::InplaceAppend(r, nextU16str1);
865 base::StringHelper::InplaceAppend(r, nextU16str2);
866 } else {
867 auto u16tCp = static_cast<uint16_t>(cp);
868 std::u16string nextU16str = base::StringHelper::Utf16ToU16String(&u16tCp, 1);
869 base::StringHelper::InplaceAppend(r, nextU16str);
870 }
871 }
872 position = position + codeUnitCount;
873 thread->CheckSafepointIfSuspended();
874 }
875 const char16_t *constChar16tData = r.data();
876 auto *char16tData = const_cast<char16_t *>(constChar16tData);
877 auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
878 uint32_t u16strSize = r.size();
879 return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue();
880 }
881
882 // Static Semantics: UTF16SurrogatePairToCodePoint ( lead, trail )
UTF16SurrogatePairToCodePoint(uint16_t lead, uint16_t trail)883 uint32_t BuiltinsString::UTF16SurrogatePairToCodePoint(uint16_t lead, uint16_t trail)
884 {
885 // 1. Assert: lead is a leading surrogate and trail is a trailing surrogate.
886 ASSERT(IsUTF16HighSurrogate(lead) && IsUTF16LowSurrogate(trail));
887 // 2. Let cp be (lead - 0xD800) × 0x400 + (trail - 0xDC00) + 0x10000.
888 uint32_t cp = ((lead - 0xD800) << 10UL) + (trail - 0xDC00) + 0x10000;
889 // 3. Return the code point cp.
890 return cp;
891 }
892
893 // 21.1.3.12
Normalize(EcmaRuntimeCallInfo *argv)894 JSTaggedValue BuiltinsString::Normalize(EcmaRuntimeCallInfo *argv)
895 {
896 ASSERT(argv);
897 BUILTINS_API_TRACE(argv->GetThread(), String, Normalize);
898 JSThread *thread = argv->GetThread();
899 [[maybe_unused]] EcmaHandleScope handleScope(thread);
900 auto vm = thread->GetEcmaVM();
901 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
902 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
903 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
904 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
905 JSHandle<EcmaString> formValue;
906 if (argv->GetArgsNumber() == 0) {
907 formValue = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfcString());
908 } else {
909 JSHandle<JSTaggedValue> formTag = BuiltinsString::GetCallArg(argv, 0);
910 if (formTag->IsUndefined()) {
911 formValue = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfcString());
912 } else {
913 formValue = JSTaggedValue::ToString(thread, formTag);
914 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
915 }
916 }
917 UNormalizationMode uForm;
918 if (JSHandle<EcmaString> nfc =
919 JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfcString());
920 EcmaStringAccessor::StringsAreEqual(vm, formValue, nfc)) {
921 uForm = UNORM_NFC;
922 } else if (JSHandle<EcmaString> nfd =
923 JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfdString());
924 EcmaStringAccessor::StringsAreEqual(vm, formValue, nfd)) {
925 uForm = UNORM_NFD;
926 } else if (JSHandle<EcmaString> nfkc =
927 JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfkcString());
928 EcmaStringAccessor::StringsAreEqual(vm, formValue, nfkc)) {
929 uForm = UNORM_NFKC;
930 } else if (JSHandle<EcmaString> nfkd =
931 JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfkdString());
932 EcmaStringAccessor::StringsAreEqual(vm, formValue, nfkd)) {
933 uForm = UNORM_NFKD;
934 } else {
935 THROW_RANGE_ERROR_AND_RETURN(thread, "compare not equal", JSTaggedValue::Exception());
936 }
937
938 std::u16string u16strThis = EcmaStringAccessor(thisHandle).ToU16String();
939 const char16_t *constChar16tData = u16strThis.data();
940 icu::UnicodeString src(constChar16tData, u16strThis.size());
941 icu::UnicodeString res;
942 UErrorCode errorCode = U_ZERO_ERROR;
943 int32_t option = 0;
944
945 icu::Normalizer::normalize(src, uForm, option, res, errorCode);
946 JSHandle<EcmaString> str = intl::LocaleHelper::UStringToString(thread, res);
947 return JSTaggedValue(*str);
948 }
949
PadStart(EcmaRuntimeCallInfo *argv)950 JSTaggedValue BuiltinsString::PadStart(EcmaRuntimeCallInfo *argv)
951 {
952 ASSERT(argv);
953 BUILTINS_API_TRACE(argv->GetThread(), String, PadStart);
954 return BuiltinsString::Pad(argv, true);
955 }
956
PadEnd(EcmaRuntimeCallInfo *argv)957 JSTaggedValue BuiltinsString::PadEnd(EcmaRuntimeCallInfo *argv)
958 {
959 ASSERT(argv);
960 BUILTINS_API_TRACE(argv->GetThread(), String, PadEnd);
961 return BuiltinsString::Pad(argv, false);
962 }
963
964 // 21.1.3.13
Repeat(EcmaRuntimeCallInfo *argv)965 JSTaggedValue BuiltinsString::Repeat(EcmaRuntimeCallInfo *argv)
966 {
967 ASSERT(argv);
968 BUILTINS_API_TRACE(argv->GetThread(), String, Repeat);
969 JSThread *thread = argv->GetThread();
970 [[maybe_unused]] EcmaHandleScope handleScope(thread);
971 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
972 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
973 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
974 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
975 uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
976 JSHandle<JSTaggedValue> countTag = BuiltinsString::GetCallArg(argv, 0);
977 int32_t count = 0;
978 if (countTag->IsInt()) {
979 count = countTag->GetInt();
980 } else {
981 JSTaggedNumber num = JSTaggedValue::ToInteger(thread, countTag);
982 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
983 double d = num.GetNumber();
984 if (d == base::POSITIVE_INFINITY) {
985 THROW_RANGE_ERROR_AND_RETURN(thread, "is infinity", JSTaggedValue::Exception());
986 }
987 count = base::NumberHelper::DoubleInRangeInt32(d);
988 }
989 if (count < 0) {
990 THROW_RANGE_ERROR_AND_RETURN(thread, "less than 0", JSTaggedValue::Exception());
991 }
992 if (count == 0) {
993 auto emptyStr = thread->GetEcmaVM()->GetFactory()->GetEmptyString();
994 return emptyStr.GetTaggedValue();
995 }
996 if (thisLen == 0) {
997 return thisHandle.GetTaggedValue();
998 }
999 if (static_cast<uint32_t>(count) >= static_cast<uint32_t>(EcmaString::MAX_STRING_LENGTH) / thisLen) {
1000 THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
1001 }
1002 bool isUtf8 = EcmaStringAccessor(thisHandle).IsUtf8();
1003 EcmaString *result = EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), thisLen * count, isUtf8);
1004 for (uint32_t index = 0; index < static_cast<uint32_t>(count); ++index) {
1005 EcmaStringAccessor::ReadData(result, *thisHandle, index * thisLen, (count - index) * thisLen, thisLen);
1006 }
1007 return JSTaggedValue(result);
1008 }
1009
1010 // 21.1.3.14
Replace(EcmaRuntimeCallInfo *argv)1011 JSTaggedValue BuiltinsString::Replace(EcmaRuntimeCallInfo *argv)
1012 {
1013 ASSERT(argv);
1014 BUILTINS_API_TRACE(argv->GetThread(), String, Replace);
1015 JSThread *thread = argv->GetThread();
1016 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1017 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
1018 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1019
1020 auto ecmaVm = thread->GetEcmaVM();
1021 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1022 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1023 JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
1024 JSHandle<JSTaggedValue> replaceTag = BuiltinsString::GetCallArg(argv, 1);
1025
1026 ObjectFactory *factory = ecmaVm->GetFactory();
1027
1028 if (searchTag->IsJSRegExp() && replaceTag->IsString()) {
1029 JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
1030 JSHandle<JSRegExp> re(searchTag);
1031 JSHandle<JSTaggedValue> pattern(thread, re->GetOriginalSource());
1032 JSHandle<JSTaggedValue> flags(thread, re->GetOriginalFlags());
1033 bool isFastPath = BuiltinsRegExp::IsFastRegExp(thread, searchTag);
1034 if (isFastPath) {
1035 uint32_t lastIndex = static_cast<uint32_t>(BuiltinsRegExp::GetLastIndex(thread, searchTag, true));
1036 JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, thisTag,
1037 RegExpExecResultCache::REPLACE_TYPE, searchTag, JSTaggedValue(lastIndex),
1038 replaceTag);
1039 if (!cacheResult.IsUndefined()) {
1040 return cacheResult;
1041 }
1042 }
1043 }
1044
1045 if (searchTag->IsJSRegExp() && PropertyDetector::IsRegExpReplaceDetectorValid(env)) {
1046 JSTaggedValue proto = JSObject::GetPrototype(JSHandle<JSObject>(searchTag));
1047 if (proto == env->GetTaggedRegExpPrototype()) {
1048 return BuiltinsRegExp::ReplaceInternal(thread, searchTag, thisTag, replaceTag);
1049 }
1050 }
1051
1052 // If searchValue is neither undefined nor null, then
1053 if (!searchTag->IsUndefined() && !searchTag->IsNull()) {
1054 JSHandle<JSTaggedValue> replaceKey = env->GetReplaceSymbol();
1055 // Let replacer be GetMethod(searchValue, @@replace).
1056 JSHandle<JSTaggedValue> replaceMethod = JSObject::GetMethod(thread, searchTag, replaceKey);
1057 // ReturnIfAbrupt(replacer).
1058 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1059 // If replacer is not undefined, then
1060 if (!replaceMethod->IsUndefined()) {
1061 // Return Call(replacer, searchValue, «O, replaceValue»).
1062 const uint32_t argsLength = 2;
1063 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1064 EcmaRuntimeCallInfo *info =
1065 EcmaInterpreter::NewRuntimeCallInfo(thread, replaceMethod, searchTag, undefined, argsLength);
1066 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1067 info->SetCallArg(thisTag.GetTaggedValue(), replaceTag.GetTaggedValue());
1068 return JSFunction::Call(info);
1069 }
1070 }
1071
1072 // Let string be ToString(O).
1073 JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
1074 // ReturnIfAbrupt(string).
1075 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1076 // Let searchString be ToString(searchValue).
1077 JSHandle<EcmaString> searchString = JSTaggedValue::ToString(thread, searchTag);
1078 // ReturnIfAbrupt(searchString).
1079 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1080 // Let functionalReplace be IsCallable(replaceValue).
1081 if (!replaceTag->IsCallable()) {
1082 // If functionalReplace is false, then
1083 // Let replaceValue be ToString(replaceValue).
1084 // ReturnIfAbrupt(replaceValue)
1085 replaceTag = JSHandle<JSTaggedValue>(JSTaggedValue::ToString(thread, replaceTag));
1086 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1087 }
1088 // Search string for the first occurrence of searchString and let pos be the index within string of the first code
1089 // unit of the matched substring and let matched be searchString. If no occurrences of searchString were found,
1090 // return string.
1091 int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString);
1092 if (pos == -1) {
1093 return thisString.GetTaggedValue();
1094 }
1095 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1096 JSMutableHandle<JSTaggedValue> replHandle(thread, factory->GetEmptyString().GetTaggedValue());
1097 // If functionalReplace is true, then
1098 if (replaceTag->IsCallable()) {
1099 // Let replValue be Call(replaceValue, undefined,«matched, pos, and string»).
1100 const uint32_t argsLength = 3; // 3: «matched, pos, and string»
1101 EcmaRuntimeCallInfo *info =
1102 EcmaInterpreter::NewRuntimeCallInfo(thread, replaceTag, undefined, undefined, argsLength);
1103 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1104 info->SetCallArg(searchString.GetTaggedValue(), JSTaggedValue(pos), thisString.GetTaggedValue());
1105 JSTaggedValue replStrDeocodeValue = JSFunction::Call(info);
1106 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1107 replHandle.Update(replStrDeocodeValue);
1108 } else {
1109 // Let captures be an empty List.
1110 JSHandle<TaggedArray> capturesList = factory->EmptyArray();
1111 ASSERT_PRINT(replaceTag->IsString(), "replace must be string");
1112 JSHandle<EcmaString> replacement(thread, replaceTag->GetTaggedObject());
1113 // Let replStr be GetSubstitution(matched, string, pos, captures, replaceValue)
1114 replHandle.Update(GetSubstitution(thread, searchString, thisString, pos, capturesList, undefined, replacement));
1115 }
1116 JSHandle<EcmaString> realReplaceStr = JSTaggedValue::ToString(thread, replHandle);
1117 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1118 // Let tailPos be pos + the number of code units in matched.
1119 int32_t tailPos = pos + static_cast<int32_t>(EcmaStringAccessor(searchString).GetLength());
1120 // Let newString be the String formed by concatenating the first pos code units of string,
1121 // replStr, and the trailing
1122 // substring of string starting at index tailPos. If pos is 0,
1123 // the first element of the concatenation will be the
1124 // empty String.
1125 // Return newString.
1126 JSHandle<EcmaString> prefixString(thread, EcmaStringAccessor::FastSubString(ecmaVm, thisString, 0, pos));
1127 auto thisLen = EcmaStringAccessor(thisString).GetLength();
1128 JSHandle<EcmaString> suffixString(thread,
1129 EcmaStringAccessor::FastSubString(ecmaVm, thisString, tailPos, thisLen - tailPos));
1130 EcmaString *tempStr = EcmaStringAccessor::Concat(ecmaVm, prefixString, realReplaceStr);
1131 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1132 JSHandle<EcmaString> tempString(thread, tempStr);
1133 EcmaString *resultStr = EcmaStringAccessor::Concat(ecmaVm, tempString, suffixString);
1134 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1135 return JSTaggedValue(resultStr);
1136 }
1137
ReplaceAll(EcmaRuntimeCallInfo *argv)1138 JSTaggedValue BuiltinsString::ReplaceAll(EcmaRuntimeCallInfo *argv)
1139 {
1140 ASSERT(argv);
1141 JSThread *thread = argv->GetThread();
1142 BUILTINS_API_TRACE(thread, String, ReplaceAll);
1143 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1144 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
1145 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1146
1147 auto ecmaVm = thread->GetEcmaVM();
1148 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1149 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1150 JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
1151 JSHandle<JSTaggedValue> replaceTag = BuiltinsString::GetCallArg(argv, 1);
1152
1153 ObjectFactory *factory = ecmaVm->GetFactory();
1154
1155 if (!searchTag->IsUndefined() && !searchTag->IsNull()) {
1156 // a. Let isRegExp be ? IsRegExp(searchValue).
1157 bool isJSRegExp = JSObject::IsRegExp(thread, searchTag);
1158 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1159 // b. If isRegExp is true, then
1160 if (isJSRegExp) {
1161 // i. Let flags be ? Get(searchValue, "flags").
1162 JSHandle<JSTaggedValue> flagsString(globalConst->GetHandledFlagsString());
1163 JSHandle<JSTaggedValue> flags = JSObject::GetProperty(thread, searchTag, flagsString).GetValue();
1164 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1165 // ii. Perform ? RequireObjectCoercible(flags).
1166 JSTaggedValue::RequireObjectCoercible(thread, flags);
1167 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1168 // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception.
1169 JSHandle<EcmaString> flagString = JSTaggedValue::ToString(thread, flags);
1170 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1171 JSHandle<EcmaString> gString(globalConst->GetHandledGString());
1172 int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, flagString, gString);
1173 if (pos == -1) {
1174 THROW_TYPE_ERROR_AND_RETURN(thread,
1175 "string.prototype.replaceAll called with a non-global RegExp argument",
1176 JSTaggedValue::Exception());
1177 }
1178 }
1179 // c. Let replacer be ? GetMethod(searchValue, @@replace).
1180 JSHandle<JSTaggedValue> replaceKey = env->GetReplaceSymbol();
1181 JSHandle<JSTaggedValue> replaceMethod = JSObject::GetMethod(thread, searchTag, replaceKey);
1182 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1183 // d. If replacer is not undefined, then
1184 if (!replaceMethod->IsUndefined()) {
1185 // i. Return ? Call(replacer, searchValue, «O, replaceValue»).
1186 const size_t argsLength = 2;
1187 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1188 EcmaRuntimeCallInfo *info =
1189 EcmaInterpreter::NewRuntimeCallInfo(thread, replaceMethod, searchTag, undefined, argsLength);
1190 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1191 info->SetCallArg(thisTag.GetTaggedValue(), replaceTag.GetTaggedValue());
1192 return JSFunction::Call(info);
1193 }
1194 }
1195
1196 // 3. Let string be ? ToString(O).
1197 JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
1198 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1199 // 4. Let searchString be ? ToString(searchValue).
1200 JSHandle<EcmaString> searchString = JSTaggedValue::ToString(thread, searchTag);
1201 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1202 // 5. Let functionalReplace be IsCallable(replaceValue).
1203 // 6. If functionalReplace is false, then
1204 if (!replaceTag->IsCallable()) {
1205 // a. Set replaceValue to ? ToString(replaceValue).
1206 replaceTag = JSHandle<JSTaggedValue>(JSTaggedValue::ToString(thread, replaceTag));
1207 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1208 }
1209
1210 // 7. Let searchLength be the length of searchString.
1211 // 8. Let advanceBy be max(1, searchLength).
1212 int32_t searchLength = static_cast<int32_t>(EcmaStringAccessor(searchString).GetLength());
1213 int32_t advanceBy = std::max(1, searchLength);
1214 // 9. Let matchPositions be a new empty List.
1215 JSMutableHandle<EcmaString> accumulatedResult(thread, factory->GetEmptyString());
1216 // 10. Let position be ! StringIndexOf(string, searchString, 0).
1217 int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString);
1218 int32_t endOfLastMatch = 0;
1219 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1220 JSMutableHandle<JSTaggedValue> replHandle(thread, factory->GetEmptyString().GetTaggedValue());
1221 while (pos != -1) {
1222 // If functionalReplace is true, then
1223 if (replaceTag->IsCallable()) {
1224 // Let replValue be Call(replaceValue, undefined,«matched, pos, and string»).
1225 const uint32_t argsLength = 3; // 3: «matched, pos, and string»
1226 EcmaRuntimeCallInfo *info =
1227 EcmaInterpreter::NewRuntimeCallInfo(thread, replaceTag, undefined, undefined, argsLength);
1228 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1229 info->SetCallArg(searchString.GetTaggedValue(), JSTaggedValue(pos), thisString.GetTaggedValue());
1230 JSTaggedValue replStrDeocodeValue = JSFunction::Call(info);
1231 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1232 replHandle.Update(replStrDeocodeValue);
1233 } else {
1234 // Let captures be an empty List.
1235 JSHandle<TaggedArray> capturesList = factory->NewTaggedArray(0);
1236 ASSERT_PRINT(replaceTag->IsString(), "replace must be string");
1237 JSHandle<EcmaString> replacement(thread, replaceTag->GetTaggedObject());
1238 // Let replStr be GetSubstitution(matched, string, pos, captures, replaceValue)
1239 replHandle.Update(GetSubstitution(thread, searchString, thisString, pos,
1240 capturesList, undefined, replacement));
1241 }
1242 JSHandle<EcmaString> realReplaceStr = JSTaggedValue::ToString(thread, replHandle);
1243 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1244 // Let tailPos be pos + the number of code units in matched.
1245 // Let newString be the String formed by concatenating the first pos code units of string,
1246 // replStr, and the trailing substring of string starting at index tailPos.
1247 // If pos is 0, the first element of the concatenation will be the
1248 // empty String.
1249 // Return newString.
1250 JSHandle<EcmaString> prefixString(thread,
1251 EcmaStringAccessor::FastSubString(ecmaVm, thisString, endOfLastMatch,
1252 pos - endOfLastMatch));
1253 accumulatedResult.Update(JSTaggedValue(EcmaStringAccessor::Concat(ecmaVm, accumulatedResult, prefixString)));
1254 accumulatedResult.Update(JSTaggedValue(EcmaStringAccessor::Concat(ecmaVm, accumulatedResult, realReplaceStr)));
1255 endOfLastMatch = pos + searchLength;
1256 pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString, pos + advanceBy);
1257 thread->CheckSafepointIfSuspended();
1258 }
1259
1260 if (endOfLastMatch < static_cast<int32_t>(EcmaStringAccessor(thisString).GetLength())) {
1261 auto thisLen = EcmaStringAccessor(thisString).GetLength();
1262 JSHandle<EcmaString> suffixString(thread,
1263 EcmaStringAccessor::FastSubString(ecmaVm, thisString, endOfLastMatch, thisLen - endOfLastMatch));
1264 accumulatedResult.Update(JSTaggedValue(EcmaStringAccessor::Concat(ecmaVm, accumulatedResult, suffixString)));
1265 }
1266
1267 return accumulatedResult.GetTaggedValue();
1268 }
1269
1270 // Handle $& - match case
ProcessDollarAmpersand(std::u16string &stringBuilder, const JSHandle<EcmaString> &matched, bool &canBeCompress)1271 void ProcessDollarAmpersand(std::u16string &stringBuilder, const JSHandle<EcmaString> &matched, bool &canBeCompress)
1272 {
1273 stringBuilder += EcmaStringAccessor(matched).ToU16String();
1274 if (EcmaStringAccessor(matched).IsUtf16()) {
1275 canBeCompress = false;
1276 }
1277 }
1278
1279 // Handle $` - prefix case
ProcessDollarBacktick(EcmaVM *ecmaVm, std::u16string &stringBuilder, const JSHandle<EcmaString> &srcString, int position, bool &canBeCompress)1280 void ProcessDollarBacktick(EcmaVM *ecmaVm, std::u16string &stringBuilder, const JSHandle<EcmaString> &srcString,
1281 int position, bool &canBeCompress)
1282 {
1283 if (position > 0) {
1284 EcmaString *prefix = EcmaStringAccessor::FastSubString(ecmaVm, srcString, 0, position);
1285 stringBuilder += EcmaStringAccessor(prefix).ToU16String();
1286 if (EcmaStringAccessor(prefix).IsUtf16()) {
1287 canBeCompress = false;
1288 }
1289 }
1290 }
1291
1292 // Handle $' - suffix case
ProcessDollarSingleQuote(EcmaVM *ecmaVm, std::u16string &stringBuilder, const JSHandle<EcmaString> &srcString, int tailPos, bool &canBeCompress)1293 void ProcessDollarSingleQuote(EcmaVM *ecmaVm, std::u16string &stringBuilder, const JSHandle<EcmaString> &srcString,
1294 int tailPos, bool &canBeCompress)
1295 {
1296 int32_t srcLength = static_cast<int32_t>(EcmaStringAccessor(srcString).GetLength());
1297 if (tailPos < srcLength) {
1298 EcmaString *suffix = EcmaStringAccessor::FastSubString(ecmaVm, srcString, tailPos, srcLength - tailPos);
1299 stringBuilder += EcmaStringAccessor(suffix).ToU16String();
1300 if (EcmaStringAccessor(suffix).IsUtf16()) {
1301 canBeCompress = false;
1302 }
1303 }
1304 }
1305
ProcessDigitCapture(const JSHandle<EcmaString> &replacementFlat, uint32_t peekIndex, uint32_t replaceLength, const JSHandle<TaggedArray> &captureList, std::u16string &stringBuilder)1306 std::pair<int32_t, bool> ProcessDigitCapture(const JSHandle<EcmaString> &replacementFlat, uint32_t peekIndex,
1307 uint32_t replaceLength, const JSHandle<TaggedArray> &captureList,
1308 std::u16string &stringBuilder)
1309 {
1310 uint32_t capturesLength = captureList->GetLength();
1311 uint16_t peek = EcmaStringAccessor(replacementFlat).Get(peekIndex);
1312 uint32_t scaledIndex = peek - '0';
1313 int32_t advance = 1;
1314 bool canBeCompress = true;
1315
1316 if (peekIndex + 1 < replaceLength) {
1317 uint16_t nextPeek = EcmaStringAccessor(replacementFlat).Get(peekIndex + 1);
1318 if (nextPeek >= '0' && nextPeek <= '9') {
1319 constexpr uint32_t TEN_BASE = 10;
1320 uint32_t newScaledIndex = scaledIndex * TEN_BASE + (nextPeek - '0');
1321 if (newScaledIndex <= capturesLength) {
1322 scaledIndex = newScaledIndex;
1323 advance = 2; // 2: 2 means from index needs to add two.
1324 }
1325 }
1326 }
1327
1328 if (scaledIndex == 0 || scaledIndex > capturesLength) {
1329 stringBuilder += '$';
1330 return {peekIndex, canBeCompress}; // No change in compressibility, just return the next index.
1331 }
1332
1333 JSTaggedValue capturesVal(captureList->Get(scaledIndex - 1));
1334 if (!capturesVal.IsUndefined()) {
1335 EcmaString *captureString = EcmaString::Cast(capturesVal.GetTaggedObject());
1336 stringBuilder += EcmaStringAccessor(captureString).ToU16String();
1337 if (EcmaStringAccessor(captureString).IsUtf16()) {
1338 canBeCompress = false;
1339 }
1340 }
1341 return {static_cast<int32_t>(peekIndex) + advance, canBeCompress};
1342 }
1343
1344 // Handle $< case
ProcessNamedCaptures(JSThread *thread, const JSHandle<EcmaString> &replacementFlat, int32_t peekIndex, const JSHandle<JSTaggedValue> &namedCaptures, std::u16string &stringBuilder)1345 std::pair<int32_t, bool> ProcessNamedCaptures(JSThread *thread, const JSHandle<EcmaString> &replacementFlat,
1346 int32_t peekIndex, const JSHandle<JSTaggedValue> &namedCaptures,
1347 std::u16string &stringBuilder)
1348 {
1349 bool canBeCompress = true;
1350 if (namedCaptures->IsUndefined()) {
1351 stringBuilder += '$';
1352 return {peekIndex, canBeCompress};
1353 }
1354 auto ecmaVm = thread->GetEcmaVM();
1355 ObjectFactory *factory = ecmaVm->GetFactory();
1356 JSHandle<EcmaString> greaterSymString = factory->NewFromASCII(">");
1357 int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, greaterSymString, peekIndex);
1358 if (pos == -1) {
1359 stringBuilder += '$';
1360 return {peekIndex, canBeCompress};
1361 }
1362 JSHandle<EcmaString> groupName = JSHandle<EcmaString>(
1363 thread, EcmaStringAccessor::FastSubString(ecmaVm, replacementFlat, peekIndex + 1, pos - peekIndex - 1));
1364 JSHandle<JSTaggedValue> names(groupName);
1365 JSHandle<JSTaggedValue> capture = JSObject::GetProperty(thread, namedCaptures, names).GetValue();
1366 if (capture->IsUndefined()) {
1367 return {pos + 1, canBeCompress};
1368 }
1369 JSHandle<EcmaString> captureName = JSTaggedValue::ToString(thread, capture);
1370 stringBuilder += EcmaStringAccessor(captureName).ToU16String();
1371 if (EcmaStringAccessor(captureName).IsUtf16()) {
1372 canBeCompress = false;
1373 }
1374 return {pos + 1, canBeCompress};
1375 }
1376
GetSubstitution(JSThread *thread, const JSHandle<EcmaString> &matched, const JSHandle<EcmaString> &srcString, int position, const JSHandle<TaggedArray> &captureList, const JSHandle<JSTaggedValue> &namedCaptures, const JSHandle<EcmaString> &replacement)1377 JSTaggedValue BuiltinsString::GetSubstitution(JSThread *thread, const JSHandle<EcmaString> &matched,
1378 const JSHandle<EcmaString> &srcString, int position,
1379 const JSHandle<TaggedArray> &captureList,
1380 const JSHandle<JSTaggedValue> &namedCaptures,
1381 const JSHandle<EcmaString> &replacement)
1382 {
1383 BUILTINS_API_TRACE(thread, String, GetSubstitution);
1384 auto ecmaVm = thread->GetEcmaVM();
1385 ObjectFactory *factory = ecmaVm->GetFactory();
1386 JSHandle<EcmaString> dollarString = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledDollarString());
1387 JSHandle<EcmaString> replacementFlat(thread, EcmaStringAccessor::Flatten(ecmaVm, replacement));
1388 int32_t replaceLength = static_cast<int32_t>(EcmaStringAccessor(replacementFlat).GetLength());
1389 int32_t tailPos = position + static_cast<int32_t>(EcmaStringAccessor(matched).GetLength());
1390
1391 int32_t nextDollarIndex = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, dollarString);
1392 if (nextDollarIndex < 0) {
1393 return replacementFlat.GetTaggedValue();
1394 }
1395 std::u16string stringBuilder;
1396 bool canBeCompress = true;
1397 if (nextDollarIndex > 0) {
1398 stringBuilder = EcmaStringAccessor(replacementFlat).ToU16String(nextDollarIndex);
1399 if (EcmaStringAccessor(replacementFlat).IsUtf16()) {
1400 canBeCompress = false;
1401 }
1402 }
1403
1404 while (true) {
1405 int peekIndex = nextDollarIndex + 1;
1406 if (peekIndex >= replaceLength) {
1407 stringBuilder += '$';
1408 auto *char16tData = const_cast<char16_t *>(stringBuilder.c_str());
1409 auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
1410 return canBeCompress ?
1411 factory->NewFromUtf16LiteralCompress(uint16tData, stringBuilder.length()).GetTaggedValue() :
1412 factory->NewFromUtf16LiteralNotCompress(uint16tData, stringBuilder.length()).GetTaggedValue();
1413 }
1414 int continueFromIndex = -1;
1415 uint16_t peek = EcmaStringAccessor(replacementFlat).Get(peekIndex);
1416 switch (peek) {
1417 case '$': // $$
1418 stringBuilder += '$';
1419 continueFromIndex = peekIndex + 1;
1420 break;
1421 case '&': // $& - match
1422 ProcessDollarAmpersand(stringBuilder, matched, canBeCompress);
1423 continueFromIndex = peekIndex + 1;
1424 break;
1425 case '`': // $` - prefix
1426 ProcessDollarBacktick(ecmaVm, stringBuilder, srcString, position, canBeCompress);
1427 continueFromIndex = peekIndex + 1;
1428 break;
1429 case '\'': { // $' - suffix
1430 ProcessDollarSingleQuote(ecmaVm, stringBuilder, srcString, tailPos, canBeCompress);
1431 continueFromIndex = peekIndex + 1;
1432 break;
1433 }
1434 case '0':
1435 case '1':
1436 case '2':
1437 case '3':
1438 case '4':
1439 case '5':
1440 case '6':
1441 case '7':
1442 case '8':
1443 case '9': {
1444 auto result =
1445 ProcessDigitCapture(replacementFlat, peekIndex, replaceLength, captureList, stringBuilder);
1446 continueFromIndex = result.first;
1447 canBeCompress = result.second && canBeCompress; // 保留canBeCompress的值,只在需要时更新为false
1448 break;
1449 }
1450 case '<': {
1451 auto result = ProcessNamedCaptures(thread, replacementFlat, peekIndex, namedCaptures, stringBuilder);
1452 continueFromIndex = result.first;
1453 canBeCompress = result.second && canBeCompress; // 保留canBeCompress的值,只在需要时更新为false
1454 break;
1455 }
1456 default:
1457 stringBuilder += '$';
1458 continueFromIndex = peekIndex;
1459 break;
1460 }
1461 // Go the the next $ in the replacement.
1462 nextDollarIndex = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, dollarString, continueFromIndex);
1463 if (nextDollarIndex < 0) {
1464 if (continueFromIndex < replaceLength) {
1465 EcmaString *nextAppend = EcmaStringAccessor::FastSubString(ecmaVm, replacementFlat, continueFromIndex,
1466 replaceLength - continueFromIndex);
1467 stringBuilder += EcmaStringAccessor(nextAppend).ToU16String();
1468 if (EcmaStringAccessor(nextAppend).IsUtf16()) {
1469 canBeCompress = false;
1470 }
1471 }
1472 auto *char16tData = const_cast<char16_t *>(stringBuilder.c_str());
1473 auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
1474 return canBeCompress ?
1475 factory->NewFromUtf16LiteralCompress(uint16tData, stringBuilder.length()).GetTaggedValue() :
1476 factory->NewFromUtf16LiteralNotCompress(uint16tData, stringBuilder.length()).GetTaggedValue();
1477 }
1478 // Append substring between the previous and the next $ character.
1479 if (nextDollarIndex > continueFromIndex) {
1480 EcmaString *nextAppend = EcmaStringAccessor::FastSubString(
1481 ecmaVm, replacementFlat, continueFromIndex, nextDollarIndex - continueFromIndex);
1482 stringBuilder += EcmaStringAccessor(nextAppend).ToU16String();
1483 if (EcmaStringAccessor(nextAppend).IsUtf16()) {
1484 canBeCompress = false;
1485 }
1486 }
1487 thread->CheckSafepointIfSuspended();
1488 }
1489 LOG_ECMA(FATAL) << "this branch is unreachable";
1490 UNREACHABLE();
1491 }
1492
1493 // 21.1.3.15
Search(EcmaRuntimeCallInfo *argv)1494 JSTaggedValue BuiltinsString::Search(EcmaRuntimeCallInfo *argv)
1495 {
1496 ASSERT(argv);
1497 BUILTINS_API_TRACE(argv->GetThread(), String, Search);
1498 JSThread *thread = argv->GetThread();
1499 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1500 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1501 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1502 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1503 JSHandle<JSTaggedValue> regexp = BuiltinsString::GetCallArg(argv, 0);
1504 if (thisTag->IsString() && regexp->IsECMAObject()) {
1505 if (BuiltinsRegExp::IsFastRegExp(thread, regexp, BuiltinsRegExp::RegExpSymbol::SEARCH)) {
1506 return BuiltinsRegExp::RegExpSearchFast(thread, regexp, thisTag);
1507 }
1508 }
1509 JSHandle<JSTaggedValue> searchTag = thread->GetEcmaVM()->GetGlobalEnv()->GetSearchSymbol();
1510 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1511 if (!regexp->IsUndefined() && !regexp->IsNull()) {
1512 JSHandle<JSTaggedValue> searcher = JSObject::GetMethod(thread, regexp, searchTag);
1513 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1514 if (!searcher->IsUndefined()) {
1515 ASSERT(searcher->IsJSFunctionBase());
1516 EcmaRuntimeCallInfo *info =
1517 EcmaInterpreter::NewRuntimeCallInfo(thread, searcher, regexp, undefined, 1);
1518 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1519 info->SetCallArg(thisTag.GetTaggedValue());
1520 return JSFunction::Call(info);
1521 }
1522 }
1523 JSHandle<EcmaString> thisVal = JSTaggedValue::ToString(thread, thisTag);
1524 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1525 JSHandle<JSTaggedValue> rx(thread, BuiltinsRegExp::RegExpCreate(thread, regexp, undefined));
1526 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1527 EcmaRuntimeCallInfo *info =
1528 EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, rx, undefined, 1);
1529 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1530 info->SetCallArg(thisVal.GetTaggedValue());
1531 return JSFunction::Invoke(info, searchTag);
1532 }
1533
1534 // 21.1.3.16
Slice(EcmaRuntimeCallInfo *argv)1535 JSTaggedValue BuiltinsString::Slice(EcmaRuntimeCallInfo *argv)
1536 {
1537 ASSERT(argv);
1538 BUILTINS_API_TRACE(argv->GetThread(), String, Slice);
1539 JSThread *thread = argv->GetThread();
1540 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1541
1542 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1543 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1544 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1545 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1546 int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
1547 JSHandle<JSTaggedValue> startTag = BuiltinsString::GetCallArg(argv, 0);
1548 JSTaggedNumber startVal = JSTaggedValue::ToInteger(thread, startTag);
1549 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1550 int32_t start = ConvertDoubleToInt(startVal.GetNumber());
1551 int32_t end = 0;
1552 JSHandle<JSTaggedValue> endTag = BuiltinsString::GetCallArg(argv, 1);
1553 if (endTag->IsUndefined()) {
1554 end = thisLen;
1555 } else {
1556 JSTaggedNumber endVal = JSTaggedValue::ToInteger(thread, endTag);
1557 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1558 end = ConvertDoubleToInt(endVal.GetNumber());
1559 }
1560 int32_t from = 0;
1561 int32_t to = 0;
1562 if (start < 0) {
1563 from = std::max(start + thisLen, 0);
1564 } else {
1565 from = std::min(start, thisLen);
1566 }
1567 if (end < 0) {
1568 to = std::max(end + thisLen, 0);
1569 } else {
1570 to = std::min(end, thisLen);
1571 }
1572 int32_t len = std::max(to - from, 0);
1573 return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisHandle, from, len));
1574 }
1575
1576 // 21.1.3.17
Split(EcmaRuntimeCallInfo *argv)1577 JSTaggedValue BuiltinsString::Split(EcmaRuntimeCallInfo *argv)
1578 {
1579 ASSERT(argv);
1580 BUILTINS_API_TRACE(argv->GetThread(), String, Split);
1581 JSThread *thread = argv->GetThread();
1582 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1583 auto ecmaVm = thread->GetEcmaVM();
1584 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1585
1586 // Let O be RequireObjectCoercible(this value).
1587 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
1588 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1589 JSHandle<JSTaggedValue> seperatorTag = BuiltinsString::GetCallArg(argv, 0);
1590 JSHandle<JSTaggedValue> limitTag = BuiltinsString::GetCallArg(argv, 1);
1591
1592 if (thisTag->IsString() && seperatorTag->IsECMAObject()) {
1593 // this condition need change, all regexp should use RegExpSplit
1594 if (BuiltinsRegExp::IsFastRegExp(thread, seperatorTag)) {
1595 return BuiltinsRegExp::RegExpSplit(thread, seperatorTag, thisTag, limitTag, true);
1596 }
1597 }
1598 if (thisTag->IsString() && seperatorTag->IsString()) {
1599 JSHandle<EcmaString> thisString(thisTag);
1600 JSHandle<EcmaString> seperatorString(seperatorTag);
1601 auto thisLength = EcmaStringAccessor(thisString).GetLength();
1602 auto seperatorLength = EcmaStringAccessor(seperatorString).GetLength();
1603 if (limitTag->IsUndefined() && thisLength != 0 && seperatorLength != 0) {
1604 return CreateArrayThisStringAndSeperatorStringAreNotEmpty(
1605 thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength);
1606 }
1607 uint32_t lim = UINT32_MAX - 1;
1608 if (!limitTag->IsUndefined()) {
1609 JSTaggedNumber limitIntValue = JSTaggedValue::ToInteger(thread, limitTag);
1610 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1611 lim = limitIntValue.ToUint32();
1612 }
1613 // ReturnIfAbrupt(lim).
1614 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1615 if (lim == 0) {
1616 JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1617 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1618 return resultArray.GetTaggedValue();
1619 }
1620 return CreateArrayBySplitString(thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength, lim);
1621 }
1622
1623 // If separator is neither undefined nor null, then
1624 if (!seperatorTag->IsUndefined() && !seperatorTag->IsNull()) {
1625 JSHandle<JSTaggedValue> splitKey = env->GetSplitSymbol();
1626 // Let splitter be GetMethod(separator, @@split).
1627 JSHandle<JSTaggedValue> splitter = JSObject::GetMethod(thread, seperatorTag, splitKey);
1628 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1629 if (!splitter->IsUndefined()) {
1630 // Return Call(splitter, separator, «O, limit»).
1631 const uint32_t argsLength = 2;
1632 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1633 EcmaRuntimeCallInfo *info =
1634 EcmaInterpreter::NewRuntimeCallInfo(thread, splitter, seperatorTag, undefined, argsLength);
1635 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1636 info->SetCallArg(thisTag.GetTaggedValue(), limitTag.GetTaggedValue());
1637 return JSFunction::Call(info);
1638 }
1639 }
1640 // Let S be ToString(O).
1641 JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
1642 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1643
1644 // If limit is undefined, let lim = 2^53–1; else let lim = ToLength(limit).
1645 uint32_t lim = UINT32_MAX - 1;
1646 if (!limitTag->IsUndefined()) {
1647 JSTaggedNumber limitIntValue = JSTaggedValue::ToInteger(thread, limitTag);
1648 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1649 lim = limitIntValue.ToUint32();
1650 }
1651 // ReturnIfAbrupt(lim).
1652 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1653 // Let s be the number of elements in S.
1654 auto thisLength = EcmaStringAccessor(thisString).GetLength();
1655 JSHandle<EcmaString> seperatorString = JSTaggedValue::ToString(thread, seperatorTag);
1656 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1657 // If lim = 0, return A.
1658 if (lim == 0) {
1659 JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1660 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1661 return resultArray.GetTaggedValue();
1662 }
1663 auto seperatorLength = EcmaStringAccessor(seperatorString).GetLength();
1664 // If S is undefined or (this.length = 0 and S.length != 0), return array of size is 1 containing this string
1665 if (seperatorTag->IsUndefined()) {
1666 JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(1)));
1667 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1668 // Perform CreateDataProperty(A, "0", S), CreateDataProperty's fast path
1669 JSObject::CreateDataProperty(thread, resultArray, 0, JSHandle<JSTaggedValue>(thisString));
1670 ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty(A, \"0\", S) can't throw exception");
1671 return resultArray.GetTaggedValue();
1672 }
1673 return CreateArrayBySplitString(thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength, lim);
1674 }
1675
CreateArrayFromString(JSThread *thread, EcmaVM *ecmaVm, const JSHandle<EcmaString> &thisString, uint32_t thisLength, uint32_t lim)1676 JSTaggedValue BuiltinsString::CreateArrayFromString(JSThread *thread, EcmaVM *ecmaVm,
1677 const JSHandle<EcmaString> &thisString, uint32_t thisLength, uint32_t lim)
1678 {
1679 bool isUtf8 = EcmaStringAccessor(thisString).IsUtf8();
1680 bool canBeCompressed = false;
1681 if (EcmaStringAccessor(thisString).IsLineOrConstantString()) {
1682 canBeCompressed = EcmaStringAccessor::CanBeCompressed(*thisString);
1683 }
1684 bool isOneByte = isUtf8 & canBeCompressed;
1685 JSHandle<EcmaString> seperatorString = thread->GetEcmaVM()->GetFactory()->GetEmptyString();
1686 if (lim == UINT32_MAX - 1) {
1687 JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1688 JSTaggedValue cacheResult = StringSplitResultCache::FindCachedResult(thread, cacheTable, thisString,
1689 seperatorString, isOneByte);
1690 if (cacheResult != JSTaggedValue::Undefined()) {
1691 JSHandle<JSTaggedValue> resultArray(JSArray::CreateArrayFromList(thread,
1692 JSHandle<TaggedArray>(thread, cacheResult)));
1693 return resultArray.GetTaggedValue();
1694 }
1695 }
1696 uint32_t actualLength = std::min(thisLength, lim);
1697 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1698 JSHandle<TaggedArray> array = factory->NewTaggedArray(actualLength);
1699 for (uint32_t i = 0; i < actualLength; ++i) {
1700 EcmaString *elementString = EcmaStringAccessor::GetSubString(ecmaVm, thisString, i, 1);
1701 // Perform CreateDataProperty(A, "0", S), CreateDataProperty's fast path
1702 if (isOneByte) {
1703 array->Set<false>(thread, i, JSTaggedValue(elementString));
1704 } else {
1705 array->Set(thread, i, JSTaggedValue(elementString));
1706 }
1707 ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty can't throw exception");
1708 }
1709 JSHandle<JSArray> resultArray = JSArray::CreateArrayFromList(thread, array);
1710 if (lim == UINT32_MAX - 1) {
1711 JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1712 StringSplitResultCache::SetCachedResult(thread, cacheTable, thisString, seperatorString, array);
1713 }
1714 return resultArray.GetTaggedValue();
1715 }
1716
CreateArrayBySplitString(JSThread *thread, EcmaVM *ecmaVm, const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &seperatorString, uint32_t thisLength, uint32_t seperatorLength, uint32_t lim)1717 JSTaggedValue BuiltinsString::CreateArrayBySplitString(JSThread *thread, EcmaVM *ecmaVm,
1718 const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &seperatorString,
1719 uint32_t thisLength, uint32_t seperatorLength, uint32_t lim)
1720 {
1721 if (thisLength != 0) {
1722 if (seperatorLength != 0) {
1723 return CreateArrayThisStringAndSeperatorStringAreNotEmpty(
1724 thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength, lim);
1725 }
1726 return CreateArrayFromString(thread, ecmaVm, thisString, thisLength, lim);
1727 } else {
1728 if (seperatorLength != 0) {
1729 JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(1)));
1730 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1731 // Perform CreateDataProperty(A, "0", S), CreateDataProperty's fast path
1732 JSObject::CreateDataProperty(thread, resultArray, 0, JSHandle<JSTaggedValue>(thisString));
1733 ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty(A, \"0\", S) can't throw exception");
1734 return resultArray.GetTaggedValue();
1735 }
1736 JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1737 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1738 return resultArray.GetTaggedValue();
1739 }
1740 }
1741
CreateArrayThisStringAndSeperatorStringAreNotEmpty(JSThread *thread, EcmaVM *ecmaVm, const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &seperatorString, uint32_t thisLength, uint32_t seperatorLength, uint32_t lim)1742 JSTaggedValue BuiltinsString::CreateArrayThisStringAndSeperatorStringAreNotEmpty(JSThread *thread,
1743 EcmaVM *ecmaVm, const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &seperatorString,
1744 uint32_t thisLength, uint32_t seperatorLength, uint32_t lim)
1745 {
1746 if (lim == UINT32_MAX - 1) {
1747 JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1748 JSTaggedValue cacheResult = StringSplitResultCache::FindCachedResult(thread, cacheTable, thisString,
1749 seperatorString);
1750 if (cacheResult != JSTaggedValue::Undefined()) {
1751 JSHandle<JSTaggedValue> resultArray(JSArray::CreateArrayFromList(thread,
1752 JSHandle<TaggedArray>(thread, cacheResult)));
1753 return resultArray.GetTaggedValue();
1754 }
1755 }
1756 uint32_t arrayLength = 0;
1757 std::vector<int32_t> posArray;
1758 int32_t index = 0;
1759 int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, seperatorString);
1760 while (pos != -1) {
1761 posArray.emplace_back(pos);
1762 ++arrayLength;
1763 if (arrayLength == lim) {
1764 break;
1765 }
1766 index = pos + static_cast<int32_t>(seperatorLength);
1767 pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, seperatorString, index);
1768 thread->CheckSafepointIfSuspended();
1769 }
1770 uint32_t posArrLength = posArray.size();
1771 arrayLength = lim > posArrLength ? posArrLength + 1 : posArrLength;
1772 return JSArray::ArrayCreateWithInit(thread, arrayLength,
1773 [thread, ecmaVm, &thisString, &seperatorString, &posArray, thisLength, seperatorLength, lim, posArrLength]
1774 (const JSHandle<TaggedArray> &newElements, [[maybe_unused]] uint32_t length) {
1775 int32_t index = 0;
1776 int32_t pos = 0;
1777 JSMutableHandle<JSTaggedValue> elementTag(thread, JSTaggedValue::Undefined());
1778 for (uint32_t i = 0; i < posArrLength; i++) {
1779 pos = posArray[i];
1780 EcmaString *elementString = EcmaStringAccessor::GetSubString(ecmaVm, thisString, index, pos - index);
1781 elementTag.Update(JSTaggedValue(elementString));
1782 newElements->Set(thread, i, elementTag);
1783 index = pos + static_cast<int32_t>(seperatorLength);
1784 }
1785 if (lim > posArrLength) {
1786 EcmaString *elementString =
1787 EcmaStringAccessor::GetSubString(ecmaVm, thisString, index, thisLength - index);
1788 elementTag.Update(JSTaggedValue(elementString));
1789 newElements->Set(thread, posArrLength, elementTag);
1790 }
1791 if (lim == UINT32_MAX - 1) {
1792 JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1793 StringSplitResultCache::SetCachedResult(thread, cacheTable, thisString, seperatorString, newElements);
1794 }
1795 });
1796 }
1797
1798 // 21.1.3.18
StartsWith(EcmaRuntimeCallInfo *argv)1799 JSTaggedValue BuiltinsString::StartsWith(EcmaRuntimeCallInfo *argv)
1800 {
1801 ASSERT(argv);
1802 BUILTINS_API_TRACE(argv->GetThread(), String, StartsWith);
1803 JSThread *thread = argv->GetThread();
1804 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1805 JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
1806
1807 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1808 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1809 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1810 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1811 bool isRegexp = JSObject::IsRegExp(thread, searchTag);
1812 if (isRegexp) {
1813 THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception());
1814 }
1815
1816 JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
1817 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1818 uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
1819 uint32_t searchLen = EcmaStringAccessor(searchHandle).GetLength();
1820 int32_t pos = 0;
1821 JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 1);
1822 if (posTag->IsUndefined()) {
1823 pos = 0;
1824 } else if (posTag->IsInt()) {
1825 pos = posTag->GetInt();
1826 } else {
1827 JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
1828 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1829 if (posVal.GetNumber() == BuiltinsNumber::POSITIVE_INFINITY) {
1830 pos = thisLen;
1831 } else {
1832 pos = posVal.ToInt32();
1833 }
1834 }
1835 pos = std::min(std::max(pos, 0), static_cast<int32_t>(thisLen));
1836 if (static_cast<uint32_t>(pos) + searchLen > thisLen) {
1837 return BuiltinsString::GetTaggedBoolean(false);
1838 }
1839
1840 bool result = EcmaStringAccessor::IsSubStringAt(thread->GetEcmaVM(), thisHandle, searchHandle, pos);
1841
1842 return BuiltinsString::GetTaggedBoolean(result);
1843 }
1844
1845 // 21.1.3.19
Substring(EcmaRuntimeCallInfo *argv)1846 JSTaggedValue BuiltinsString::Substring(EcmaRuntimeCallInfo *argv)
1847 {
1848 ASSERT(argv);
1849 BUILTINS_API_TRACE(argv->GetThread(), String, Substring);
1850 JSThread *thread = argv->GetThread();
1851 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1852
1853 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1854 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1855 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1856 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1857 int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
1858 JSHandle<JSTaggedValue> startTag = BuiltinsString::GetCallArg(argv, 0);
1859 JSTaggedNumber startVal = JSTaggedValue::ToInteger(thread, startTag);
1860 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1861 int32_t start = ConvertDoubleToInt(startVal.GetNumber());
1862 int32_t end = 0;
1863 JSHandle<JSTaggedValue> endTag = BuiltinsString::GetCallArg(argv, 1);
1864 if (endTag->IsUndefined()) {
1865 end = thisLen;
1866 } else {
1867 JSTaggedNumber endVal = JSTaggedValue::ToInteger(thread, endTag);
1868 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1869 end = ConvertDoubleToInt(endVal.GetNumber());
1870 }
1871 start = std::min(std::max(start, 0), thisLen);
1872 end = std::min(std::max(end, 0), thisLen);
1873 int32_t from = std::min(start, end);
1874 int32_t to = std::max(start, end);
1875 int32_t len = to - from;
1876 return JSTaggedValue(EcmaStringAccessor::GetSubString(thread->GetEcmaVM(), thisHandle, from, len));
1877 }
1878
1879 // 21.1.3.20
ToLocaleLowerCase(EcmaRuntimeCallInfo *argv)1880 JSTaggedValue BuiltinsString::ToLocaleLowerCase(EcmaRuntimeCallInfo *argv)
1881 {
1882 ASSERT(argv);
1883 BUILTINS_API_TRACE(argv->GetThread(), String, ToLocaleLowerCase);
1884 JSThread *thread = argv->GetThread();
1885 EcmaVM *ecmaVm = thread->GetEcmaVM();
1886 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1887
1888 // Let O be RequireObjectCoercible(this value).
1889 JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1890 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1891 // Let S be ? ToString(O).
1892 JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
1893 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1894
1895 // Let requestedLocales be ? CanonicalizeLocaleList(locales).
1896 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
1897 // Fast path
1898 if (locales->IsUndefined() && EcmaStringAccessor(string).IsUtf8()) {
1899 EcmaString *result = EcmaStringAccessor::TryToLower(ecmaVm, string);
1900 return JSTaggedValue(result);
1901 }
1902 JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
1903 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1904
1905 // If requestedLocales is not an empty List, then Let requestedLocale be requestedLocales[0].
1906 // Else, Let requestedLocale be DefaultLocale().
1907 JSHandle<EcmaString> requestedLocale = intl::LocaleHelper::DefaultLocale(thread);
1908 if (requestedLocales->GetLength() != 0) {
1909 requestedLocale = JSHandle<EcmaString>(thread, requestedLocales->Get(0));
1910 }
1911
1912 // Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences
1913 // removed.
1914 intl::LocaleHelper::ParsedLocale noExtensionsLocale = intl::LocaleHelper::HandleLocale(requestedLocale);
1915 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1916
1917 // Let availableLocales be a List with language tags that includes the languages for which the Unicode Character
1918 // Database contains language sensitive case mappings. Implementations may add additional language tags
1919 // if they support case mapping for additional locales.
1920 std::vector<std::string> availableLocales = intl::LocaleHelper::GetAvailableLocales(thread, nullptr, nullptr);
1921 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1922
1923 // Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1924 std::string locale = intl::LocaleHelper::BestAvailableLocale(availableLocales, noExtensionsLocale.base);
1925 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1926
1927 // If locale is undefined, let locale be "und".
1928 if (locale.empty()) {
1929 locale = "und";
1930 }
1931
1932 // Let uString be a List containing in order the code points of S as defined in ES2020, 6.1.4,
1933 // starting at the first element of S.
1934 // Transform those elements in uString to the to the Unicode Default Case Conversion algorithm
1935 icu::Locale icuLocale = icu::Locale::createFromName(locale.c_str());
1936 EcmaString *result = EcmaStringAccessor::ToLocaleLower(ecmaVm, string, icuLocale);
1937 return JSTaggedValue(result);
1938 }
1939
1940 // 21.1.3.21
ToLocaleUpperCase(EcmaRuntimeCallInfo *argv)1941 JSTaggedValue BuiltinsString::ToLocaleUpperCase(EcmaRuntimeCallInfo *argv)
1942 {
1943 ASSERT(argv);
1944 BUILTINS_API_TRACE(argv->GetThread(), String, ToLocaleLowerCase);
1945 JSThread *thread = argv->GetThread();
1946 EcmaVM *ecmaVm = thread->GetEcmaVM();
1947 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1948
1949 // Let O be RequireObjectCoercible(this value).
1950 JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1951 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1952 // Let S be ? ToString(O).
1953 JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
1954 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1955
1956 // Let requestedLocales be ? CanonicalizeLocaleList(locales).
1957 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
1958 // Fast path
1959 if (locales->IsUndefined() && EcmaStringAccessor(string).IsUtf8()) {
1960 EcmaString *result = EcmaStringAccessor::TryToUpper(ecmaVm, string);
1961 return JSTaggedValue(result);
1962 }
1963 JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
1964 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1965
1966 // If requestedLocales is not an empty List, then Let requestedLocale be requestedLocales[0].
1967 // Else, Let requestedLocale be DefaultLocale().
1968 JSHandle<EcmaString> requestedLocale = intl::LocaleHelper::DefaultLocale(thread);
1969 if (requestedLocales->GetLength() != 0) {
1970 requestedLocale = JSHandle<EcmaString>(thread, requestedLocales->Get(0));
1971 }
1972
1973 // Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences
1974 // removed.
1975 intl::LocaleHelper::ParsedLocale noExtensionsLocale = intl::LocaleHelper::HandleLocale(requestedLocale);
1976 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1977
1978 // Let availableLocales be a List with language tags that includes the languages for which the Unicode Character
1979 // Database contains language sensitive case mappings. Implementations may add additional language tags
1980 // if they support case mapping for additional locales.
1981 std::vector<std::string> availableLocales = intl::LocaleHelper::GetAvailableLocales(thread, nullptr, nullptr);
1982 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1983
1984 // Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1985 std::string locale = intl::LocaleHelper::BestAvailableLocale(availableLocales, noExtensionsLocale.base);
1986 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1987
1988 // If locale is undefined, let locale be "und".
1989 if (locale.empty()) {
1990 locale = "und";
1991 }
1992
1993 // Let uString be a List containing in order the code points of S as defined in ES2020, 6.1.4,
1994 // starting at the first element of S.
1995 // Transform those elements in uString to the to the Unicode Default Case Conversion algorithm
1996 icu::Locale icuLocale = icu::Locale::createFromName(locale.c_str());
1997 EcmaString *result = EcmaStringAccessor::ToLocaleUpper(ecmaVm, string, icuLocale);
1998 return JSTaggedValue(result);
1999 }
2000
2001 // 21.1.3.22
ToLowerCase(EcmaRuntimeCallInfo *argv)2002 JSTaggedValue BuiltinsString::ToLowerCase(EcmaRuntimeCallInfo *argv)
2003 {
2004 ASSERT(argv);
2005 BUILTINS_API_TRACE(argv->GetThread(), String, ToLowerCase);
2006 JSThread *thread = argv->GetThread();
2007 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2008 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2009 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2010 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2011 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2012 EcmaString *result = EcmaStringAccessor::ToLower(thread->GetEcmaVM(), thisHandle);
2013 return JSTaggedValue(result);
2014 }
2015
2016 // 21.1.3.23
ToString(EcmaRuntimeCallInfo *argv)2017 JSTaggedValue BuiltinsString::ToString(EcmaRuntimeCallInfo *argv)
2018 {
2019 ASSERT(argv);
2020 return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue());
2021 }
2022
2023 // 21.1.3.24
ToUpperCase(EcmaRuntimeCallInfo *argv)2024 JSTaggedValue BuiltinsString::ToUpperCase(EcmaRuntimeCallInfo *argv)
2025 {
2026 ASSERT(argv);
2027 BUILTINS_API_TRACE(argv->GetThread(), String, ToUpperCase);
2028 JSThread *thread = argv->GetThread();
2029 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2030
2031 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2032 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2033 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2034 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2035 EcmaString *result = EcmaStringAccessor::ToUpper(thread->GetEcmaVM(), thisHandle);
2036 return JSTaggedValue(result);
2037 }
2038
2039 // 21.1.3.25
Trim(EcmaRuntimeCallInfo *argv)2040 JSTaggedValue BuiltinsString::Trim(EcmaRuntimeCallInfo *argv)
2041 {
2042 ASSERT(argv);
2043 BUILTINS_API_TRACE(argv->GetThread(), String, Trim);
2044 JSThread *thread = argv->GetThread();
2045 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2046 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2047 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2048 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2049 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2050 EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM);
2051 return JSTaggedValue(res);
2052 }
2053
TrimStart(EcmaRuntimeCallInfo *argv)2054 JSTaggedValue BuiltinsString::TrimStart(EcmaRuntimeCallInfo *argv)
2055 {
2056 ASSERT(argv);
2057 BUILTINS_API_TRACE(argv->GetThread(), String, TrimStart);
2058 JSThread *thread = argv->GetThread();
2059 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2060 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2061 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2062 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2063 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2064 EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_START);
2065 return JSTaggedValue(res);
2066 }
2067
TrimEnd(EcmaRuntimeCallInfo *argv)2068 JSTaggedValue BuiltinsString::TrimEnd(EcmaRuntimeCallInfo *argv)
2069 {
2070 ASSERT(argv);
2071 BUILTINS_API_TRACE(argv->GetThread(), String, TrimEnd);
2072 JSThread *thread = argv->GetThread();
2073 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2074 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2075 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2076 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2077 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2078 EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_END);
2079 return JSTaggedValue(res);
2080 }
2081
TrimLeft(EcmaRuntimeCallInfo *argv)2082 JSTaggedValue BuiltinsString::TrimLeft(EcmaRuntimeCallInfo *argv)
2083 {
2084 ASSERT(argv);
2085 BUILTINS_API_TRACE(argv->GetThread(), String, TrimLeft);
2086 JSThread *thread = argv->GetThread();
2087 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2088 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2089 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2090 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2091 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2092 EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_START);
2093 return JSTaggedValue(res);
2094 }
2095
TrimRight(EcmaRuntimeCallInfo *argv)2096 JSTaggedValue BuiltinsString::TrimRight(EcmaRuntimeCallInfo *argv)
2097 {
2098 ASSERT(argv);
2099 BUILTINS_API_TRACE(argv->GetThread(), String, TrimRight);
2100 JSThread *thread = argv->GetThread();
2101 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2102 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2103 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2104 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2105 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2106 EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_END);
2107 return JSTaggedValue(res);
2108 }
2109
2110 // 21.1.3.26
ValueOf(EcmaRuntimeCallInfo *argv)2111 JSTaggedValue BuiltinsString::ValueOf(EcmaRuntimeCallInfo *argv)
2112 {
2113 ASSERT(argv);
2114 return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue());
2115 }
2116
2117 // 21.1.3.27
GetStringIterator(EcmaRuntimeCallInfo *argv)2118 JSTaggedValue BuiltinsString::GetStringIterator(EcmaRuntimeCallInfo *argv)
2119 {
2120 ASSERT(argv);
2121 BUILTINS_API_TRACE(argv->GetThread(), String, GetStringIterator);
2122 JSThread *thread = argv->GetThread();
2123 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2124 // 1. Let O be RequireObjectCoercible(this value).
2125 JSHandle<JSTaggedValue> current(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2126 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2127 // Let S be ToString(O).
2128
2129 JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, current);
2130 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread());
2131 // Return CreateStringIterator(S).
2132 return JSStringIterator::CreateStringIterator(thread, string).GetTaggedValue();
2133 }
2134
2135 // B.2.3.1
SubStr(EcmaRuntimeCallInfo *argv)2136 JSTaggedValue BuiltinsString::SubStr(EcmaRuntimeCallInfo *argv)
2137 {
2138 ASSERT(argv);
2139 BUILTINS_API_TRACE(argv->GetThread(), String, SubStr);
2140 JSThread *thread = argv->GetThread();
2141
2142 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2143
2144 // 1. Let O be RequireObjectCoercible(this value).
2145 // 2. Let S be ToString(O).
2146
2147 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2148 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2149 JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
2150
2151 // 3. ReturnIfAbrupt(S).
2152 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2153 JSHandle<JSTaggedValue> intStart = GetCallArg(argv, 0);
2154 // 4. Let intStart be ToInteger(start).
2155 JSTaggedNumber numStart = JSTaggedValue::ToInteger(thread, intStart);
2156 // 5. ReturnIfAbrupt(intStart).
2157 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2158 int32_t start = base::NumberHelper::DoubleInRangeInt32(numStart.GetNumber());
2159 JSHandle<JSTaggedValue> lengthTag = GetCallArg(argv, 1);
2160 // 6. If length is undefined, let end be +; otherwise let end be ToInteger(length).
2161 int32_t end = 0;
2162 if (lengthTag->IsUndefined()) {
2163 end = INT_MAX;
2164 } else {
2165 JSTaggedNumber lengthNumber = JSTaggedValue::ToInteger(thread, lengthTag);
2166 // 7. ReturnIfAbrupt(end).
2167 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2168 end = base::NumberHelper::DoubleInRangeInt32(lengthNumber.GetNumber());
2169 }
2170 // 8. Let size be the number of code units in S.
2171 int32_t size = static_cast<int32_t>(EcmaStringAccessor(thisString).GetLength());
2172 // 9. If intStart < 0, let intStart be max(size + intStart,0).
2173 if (start < 0) {
2174 start = std::max(size + start, 0);
2175 }
2176 // 10. Let resultLength be min(max(end,0), size – intStart).
2177 int32_t resultLength = std::min(std::max(end, 0), size - start);
2178 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2179 // 11. If resultLength 0, return the empty String "".
2180 if (resultLength <= 0) {
2181 return factory->GetEmptyString().GetTaggedValue();
2182 }
2183 return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisString, start, resultLength));
2184 }
2185
2186 // 22.1.3.1
At(EcmaRuntimeCallInfo *argv)2187 JSTaggedValue BuiltinsString::At(EcmaRuntimeCallInfo *argv)
2188 {
2189 ASSERT(argv);
2190 BUILTINS_API_TRACE(argv->GetThread(), String, At);
2191 JSThread *thread = argv->GetThread();
2192 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2193
2194 // 1. Let O be RequireObjectCoercible(this value).
2195 // 2. Let S be ToString(O).
2196 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2197 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2198 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2199 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2200
2201 // 3. Let len be the length of S.
2202 int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
2203
2204 // 4. Let relativeIndex be ? ToIntegerOrInfinity(index).
2205 JSHandle<JSTaggedValue> indexTag = BuiltinsString::GetCallArg(argv, 0);
2206 JSTaggedNumber indexVal = JSTaggedValue::ToInteger(thread, indexTag);
2207 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2208 int32_t relativeIndex = base::NumberHelper::DoubleInRangeInt32(indexVal.GetNumber());
2209
2210 // 5. If relativeIndex ≥ 0, then Let k be relativeIndex. 6. Else, Let k be len + relativeIndex.
2211 int32_t k = 0;
2212 if (relativeIndex >= 0) {
2213 k = relativeIndex;
2214 } else {
2215 k = thisLen + relativeIndex;
2216 }
2217 // 7. If k < 0 or k ≥ len, return undefined.
2218 if (k < 0 || k >= thisLen) {
2219 return JSTaggedValue::Undefined();
2220 }
2221 // 8. Return the substring of S from k to k + 1.
2222 return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisHandle, k, 1));
2223 }
2224
GetLength(EcmaRuntimeCallInfo *argv)2225 JSTaggedValue BuiltinsString::GetLength(EcmaRuntimeCallInfo *argv)
2226 {
2227 ASSERT(argv);
2228 JSThread *thread = argv->GetThread();
2229 BUILTINS_API_TRACE(thread, String, GetLength);
2230 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2231 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2232
2233 JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisHandle);
2234 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2235 return GetTaggedInt(EcmaStringAccessor(thisString).GetLength());
2236 }
2237
2238 // 21.1.3
ThisStringValue(JSThread *thread, JSTaggedValue value)2239 JSTaggedValue BuiltinsString::ThisStringValue(JSThread *thread, JSTaggedValue value)
2240 {
2241 BUILTINS_API_TRACE(thread, String, ThisStringValue);
2242 if (value.IsString()) {
2243 return value;
2244 }
2245 if (value.IsECMAObject()) {
2246 auto jshclass = value.GetTaggedObject()->GetClass();
2247 if (jshclass->GetObjectType() == JSType::JS_PRIMITIVE_REF) {
2248 JSTaggedValue primitive = JSPrimitiveRef::Cast(value.GetTaggedObject())->GetValue();
2249 if (primitive.IsString()) {
2250 return primitive;
2251 }
2252 }
2253 }
2254 THROW_TYPE_ERROR_AND_RETURN(thread, "can not convert to String", JSTaggedValue::Exception());
2255 }
2256
Pad(EcmaRuntimeCallInfo *argv, bool isStart)2257 JSTaggedValue BuiltinsString::Pad(EcmaRuntimeCallInfo *argv, bool isStart)
2258 {
2259 JSThread *thread = argv->GetThread();
2260 BUILTINS_API_TRACE(thread, String, Pad);
2261 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2262
2263 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
2264 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2265 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2266 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2267 JSHandle<JSTaggedValue> lengthTag = GetCallArg(argv, 0);
2268 JSTaggedNumber number = JSTaggedValue::ToNumber(thread, lengthTag);
2269 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2270 int64_t intMaxLength = base::NumberHelper::DoubleToInt64(number.GetNumber());
2271 int32_t stringLength = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
2272 if (intMaxLength <= stringLength) {
2273 return thisHandle.GetTaggedValue();
2274 }
2275 JSHandle<JSTaggedValue> fillString = GetCallArg(argv, 1);
2276 std::u16string stringBuilder;
2277 if (fillString->IsUndefined()) {
2278 stringBuilder = u" ";
2279 } else {
2280 JSHandle<EcmaString> filler = JSTaggedValue::ToString(thread, fillString);
2281 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2282 stringBuilder = EcmaStringAccessor(filler).ToU16String();
2283 }
2284 if (stringBuilder.size() == 0) {
2285 return thisHandle.GetTaggedValue();
2286 }
2287 std::u16string u16strSearch = EcmaStringAccessor(thisHandle).ToU16String();
2288 int64_t fillLen = intMaxLength - stringLength;
2289 int64_t len = static_cast<int64_t>(stringBuilder.length());
2290 if (static_cast<size_t>(intMaxLength) >= EcmaString::MAX_STRING_LENGTH) {
2291 THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
2292 }
2293 std::u16string fiString;
2294 for (int32_t i = 0; i < fillLen; ++i) {
2295 fiString += stringBuilder[i % len];
2296 }
2297 std::u16string resultString;
2298 if (isStart) {
2299 resultString = fiString + u16strSearch;
2300 } else {
2301 resultString = u16strSearch + fiString;
2302 }
2303 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2304 return factory->NewFromUtf16Literal(reinterpret_cast<const uint16_t *>(resultString.c_str()),
2305 resultString.size()).GetTaggedValue();
2306 }
2307
ConvertDoubleToInt(double d)2308 int32_t BuiltinsString::ConvertDoubleToInt(double d)
2309 {
2310 if (std::isnan(d) || d == -base::POSITIVE_INFINITY) {
2311 return 0;
2312 }
2313 if (d >= static_cast<double>(INT_MAX)) {
2314 return INT_MAX;
2315 }
2316 if (d <= static_cast<double>(INT_MIN)) {
2317 return INT_MIN;
2318 }
2319 return base::NumberHelper::DoubleToInt(d, base::INT32_BITS);
2320 }
2321
StringToList(JSThread *thread, JSHandle<EcmaString> &str)2322 JSTaggedValue BuiltinsString::StringToList(JSThread *thread, JSHandle<EcmaString> &str)
2323 {
2324 JSHandle<StringToListResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringToListResultCache());
2325 JSTaggedValue cacheResult = StringToListResultCache::FindCachedResult(thread, cacheTable, str);
2326 if (cacheResult != JSTaggedValue::Undefined()) {
2327 JSHandle<JSTaggedValue> resultArray(JSArray::CreateArrayFromList(thread,
2328 JSHandle<TaggedArray>(thread, cacheResult)));
2329 return resultArray.GetTaggedValue();
2330 }
2331
2332 JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue();
2333 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2334 JSHandle<JSObject> newArrayHandle(thread, newArray);
2335 JSHandle<EcmaString> iteratedString(str);
2336 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2337 JSHandle<TaggedArray> oldElements(thread, newArrayHandle->GetElements());
2338 uint32_t totalElements = EcmaStringAccessor(iteratedString).GetLength();
2339 JSHandle<TaggedArray> elements = (oldElements->GetLength() < totalElements) ?
2340 factory->ExtendArray(oldElements, totalElements) : oldElements;
2341 uint32_t index = 0;
2342 newArrayHandle->SetElements(thread, elements);
2343 while (index < totalElements) {
2344 uint16_t c = EcmaStringAccessor(iteratedString).Get(index);
2345 JSHandle<EcmaString> newStr = factory->NewFromUtf16Literal(&c, 1);
2346 ElementAccessor::Set(thread, newArrayHandle, index, newStr, true);
2347 index++;
2348 thread->CheckSafepointIfSuspended();
2349 }
2350 JSHandle<JSArray>(newArrayHandle)->SetArrayLength(thread, totalElements);
2351
2352 StringToListResultCache::SetCachedResult(thread, cacheTable, str, elements);
2353
2354 return newArrayHandle.GetTaggedValue();
2355 }
2356
StringToSList(JSThread *thread, JSHandle<EcmaString> &str)2357 JSTaggedValue BuiltinsString::StringToSList(JSThread *thread, JSHandle<EcmaString> &str)
2358 {
2359 JSHandle<StringToListResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringToListResultCache());
2360 JSTaggedValue cacheResult = StringToListResultCache::FindCachedResult(thread, cacheTable, str);
2361 if (cacheResult != JSTaggedValue::Undefined()) {
2362 JSHandle<JSTaggedValue> resultArray(
2363 JSSharedArray::CreateArrayFromList(thread, JSHandle<TaggedArray>(thread, cacheResult)));
2364 return resultArray.GetTaggedValue();
2365 }
2366
2367 JSTaggedValue newSharedArray = JSSharedArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue();
2368 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2369 JSHandle<JSObject> newSharedArrayHandle(thread, newSharedArray);
2370 JSHandle<EcmaString> iteratedString(str);
2371 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2372 JSHandle<TaggedArray> oldElements(thread, newSharedArrayHandle->GetElements());
2373 uint32_t totalElements = EcmaStringAccessor(iteratedString).GetLength();
2374 JSHandle<TaggedArray> elements =
2375 (oldElements->GetLength() < totalElements)
2376 ? factory->ExtendArray(oldElements, totalElements, JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE)
2377 : oldElements;
2378 uint32_t index = 0;
2379 newSharedArrayHandle->SetElements(thread, elements);
2380 while (index < totalElements) {
2381 uint16_t c = EcmaStringAccessor(iteratedString).Get(index);
2382 JSHandle<EcmaString> newStr = factory->NewFromUtf16Literal(&c, 1);
2383 ElementAccessor::Set(thread, newSharedArrayHandle, index, newStr, true);
2384 index++;
2385 thread->CheckSafepointIfSuspended();
2386 }
2387 JSHandle<JSSharedArray>(newSharedArrayHandle)->SetArrayLength(thread, totalElements);
2388
2389 StringToListResultCache::SetCachedResult(thread, cacheTable, str, elements);
2390 newSharedArrayHandle->GetJSHClass()->SetExtensible(false);
2391 return newSharedArrayHandle.GetTaggedValue();
2392 }
2393
CreateCacheTable(const JSThread *thread)2394 JSTaggedValue StringSplitResultCache::CreateCacheTable(const JSThread *thread)
2395 {
2396 int length = CACHE_SIZE * ENTRY_SIZE;
2397 auto table = static_cast<StringSplitResultCache*>(
2398 *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined()));
2399 return JSTaggedValue(table);
2400 }
2401
FindCachedResult(const JSThread *thread, const JSHandle<StringSplitResultCache> &cache, const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &pattern, bool isOneByte)2402 JSTaggedValue StringSplitResultCache::FindCachedResult(const JSThread *thread,
2403 const JSHandle<StringSplitResultCache> &cache, const JSHandle<EcmaString> &thisString,
2404 const JSHandle<EcmaString> &pattern, bool isOneByte)
2405 {
2406 uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2407 uint32_t entry = hash & (CACHE_SIZE - 1);
2408 uint32_t index = entry * ENTRY_SIZE;
2409 JSTaggedValue cacheThis = cache->Get(index + STRING_INDEX);
2410 JSTaggedValue cachePattern = cache->Get(index + PATTERN_INDEX);
2411 if (!cacheThis.IsString() || !cachePattern.IsString()) {
2412 return JSTaggedValue::Undefined();
2413 }
2414 JSHandle<EcmaString> cacheStringHandle(thread, cacheThis);
2415 JSHandle<EcmaString> cachePatternHandle(thread, cachePattern);
2416
2417 if (EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(), thisString, cacheStringHandle) &&
2418 EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(), pattern, cachePatternHandle)) {
2419 JSHandle<TaggedArray> cacheArray(thread, cache->Get(index + ARRAY_INDEX));
2420 uint32_t arrayLength = cacheArray->GetLength();
2421 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2422 JSHandle<TaggedArray> copyArray;
2423 if (isOneByte) {
2424 copyArray = factory->NewAndCopyTaggedArraySkipBarrier(cacheArray, arrayLength, arrayLength);
2425 } else {
2426 copyArray = factory->NewAndCopyTaggedArray(cacheArray, arrayLength, arrayLength);
2427 }
2428 return copyArray.GetTaggedValue();
2429 }
2430 return JSTaggedValue::Undefined();
2431 }
2432
SetCachedResult(const JSThread *thread, const JSHandle<StringSplitResultCache> &cache, const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &pattern, const JSHandle<TaggedArray> &resultArray)2433 void StringSplitResultCache::SetCachedResult(const JSThread *thread, const JSHandle<StringSplitResultCache> &cache,
2434 const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &pattern,
2435 const JSHandle<TaggedArray> &resultArray)
2436 {
2437 // clone to cache array
2438 uint32_t arrayLength = resultArray->GetLength();
2439 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2440 JSHandle<TaggedArray> newElements(factory->NewTaggedArray(arrayLength));
2441 for (uint32_t i = 0; i < arrayLength; i++) {
2442 newElements->Set(thread, i, resultArray->Get(i));
2443 }
2444 uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2445 uint32_t entry = hash & (CACHE_SIZE - 1);
2446 uint32_t index = entry * ENTRY_SIZE;
2447
2448 cache->Set(thread, index + STRING_INDEX, thisString);
2449 cache->Set(thread, index + PATTERN_INDEX, pattern);
2450 cache->Set(thread, index + ARRAY_INDEX, newElements);
2451 }
2452
CreateCacheTable(const JSThread *thread)2453 JSTaggedValue StringToListResultCache::CreateCacheTable(const JSThread *thread)
2454 {
2455 int length = CACHE_SIZE * ENTRY_SIZE;
2456 auto table = static_cast<StringToListResultCache*>(
2457 *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined()));
2458 return JSTaggedValue(table);
2459 }
2460
FindCachedResult(const JSThread *thread, const JSHandle<StringToListResultCache> &cache, const JSHandle<EcmaString> &thisString)2461 JSTaggedValue StringToListResultCache::FindCachedResult(const JSThread *thread,
2462 const JSHandle<StringToListResultCache> &cache, const JSHandle<EcmaString> &thisString)
2463 {
2464 if (EcmaStringAccessor(thisString).GetLength() > MAX_STRING_LENGTH) {
2465 return JSTaggedValue::Undefined();
2466 }
2467 uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2468 uint32_t entry = hash & (CACHE_SIZE - 1);
2469 uint32_t index = entry * ENTRY_SIZE;
2470 JSHandle<JSTaggedValue> cacheThis(thread, cache->Get(index + STRING_INDEX));
2471 if (!cacheThis->IsString()) {
2472 return JSTaggedValue::Undefined();
2473 }
2474 JSHandle<EcmaString> cacheStr(cacheThis);
2475 if (EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(), thisString, cacheStr)) {
2476 return cache->Get(index + ARRAY_INDEX);
2477 }
2478 return JSTaggedValue::Undefined();
2479 }
2480
SetCachedResult(const JSThread *thread, const JSHandle<StringToListResultCache> &cache, const JSHandle<EcmaString> &thisString, const JSHandle<TaggedArray> &resultArray)2481 void StringToListResultCache::SetCachedResult(const JSThread *thread, const JSHandle<StringToListResultCache> &cache,
2482 const JSHandle<EcmaString> &thisString, const JSHandle<TaggedArray> &resultArray)
2483 {
2484 if (EcmaStringAccessor(thisString).GetLength() > MAX_STRING_LENGTH ||
2485 EcmaStringAccessor(thisString).GetLength() == 0) {
2486 return;
2487 }
2488 if (!EcmaStringAccessor(thisString).IsInternString()) {
2489 return;
2490 }
2491 // clone to cache array
2492 uint32_t arrayLength = resultArray->GetLength();
2493 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2494 JSHandle<TaggedArray> newElements;
2495 if (resultArray.GetTaggedValue().IsInSharedHeap()) {
2496 newElements = JSHandle<TaggedArray>(factory->NewSCOWTaggedArray(arrayLength));
2497 } else {
2498 newElements = JSHandle<TaggedArray>(factory->NewCOWTaggedArray(arrayLength));
2499 }
2500 for (uint32_t i = 0; i < arrayLength; i++) {
2501 newElements->Set(thread, i, resultArray->Get(i));
2502 }
2503 uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2504 uint32_t entry = hash & (CACHE_SIZE - 1);
2505 uint32_t index = entry * ENTRY_SIZE;
2506 cache->Set(thread, index + STRING_INDEX, thisString);
2507 cache->Set(thread, index + ARRAY_INDEX, newElements);
2508 }
2509 } // namespace panda::ecmascript::builtins
2510