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 EcmaRuntimeCallInfo *info =
624 EcmaInterpreter::NewRuntimeCallInfo(thread, matcher, regexp, undefined, 1);
625 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
626 info->SetCallArg(thisTag.GetTaggedValue());
627 return JSFunction::Call(info);
628 }
629 }
630 JSHandle<EcmaString> thisVal = JSTaggedValue::ToString(thread, thisTag);
631 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
632 JSHandle<JSTaggedValue> undifinedHandle = globalConst->GetHandledUndefined();
633 JSHandle<JSTaggedValue> rx(thread, BuiltinsRegExp::RegExpCreate(thread, regexp, undifinedHandle));
634 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
635 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, rx, undefined, 1);
636 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
637 info->SetCallArg(thisVal.GetTaggedValue());
638 return JSFunction::Invoke(info, matchTag);
639 }
640
MatchAll(EcmaRuntimeCallInfo * argv)641 JSTaggedValue BuiltinsString::MatchAll(EcmaRuntimeCallInfo *argv)
642 {
643 ASSERT(argv);
644 BUILTINS_API_TRACE(argv->GetThread(), String, MatchAll);
645 JSThread *thread = argv->GetThread();
646 [[maybe_unused]] EcmaHandleScope handleScope(thread);
647 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
648 // 1. Let O be ? RequireObjectCoercible(this value).
649 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
650 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
651 JSHandle<JSTaggedValue> regexp = BuiltinsString::GetCallArg(argv, 0);
652 EcmaVM *ecmaVm = thread->GetEcmaVM();
653 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
654 JSHandle<JSTaggedValue> matchAllTag = env->GetMatchAllSymbol();
655 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
656
657 // 2. If regexp is neither undefined nor null, then
658 if (!regexp->IsUndefined() && !regexp->IsNull()) {
659 // a. Let isRegExp be ? IsRegExp(searchValue).
660 if (regexp->IsECMAObject() &&
661 BuiltinsRegExp::IsFastRegExp(thread, regexp, BuiltinsRegExp::RegExpSymbol::MATCH)) {
662 bool isGlobal = BuiltinsRegExp::GetOriginalFlag(thread, regexp, RegExpParser::FLAG_GLOBAL);
663 if (!isGlobal) {
664 THROW_TYPE_ERROR_AND_RETURN(thread,
665 "matchAll called with a non-global RegExp argument",
666 JSTaggedValue::Exception());
667 }
668 } else if (JSObject::IsRegExp(thread, regexp)) {
669 // i. Let flags be ? Get(searchValue, "flags").
670 JSHandle<JSTaggedValue> flagsString(globalConst->GetHandledFlagsString());
671 JSHandle<JSTaggedValue> flags = JSObject::GetProperty(thread, regexp, flagsString).GetValue();
672 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
673 // ii. Perform ? RequireObjectCoercible(flags).
674 JSTaggedValue::RequireObjectCoercible(thread, flags);
675 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
676 // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception.
677 JSHandle<EcmaString> flagString = JSTaggedValue::ToString(thread, flags);
678 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
679 int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm,
680 flagString, ecmaVm->GetFactory()->NewFromASCII("g"));
681 if (pos == -1) {
682 THROW_TYPE_ERROR_AND_RETURN(thread,
683 "matchAll called with a non-global RegExp argument",
684 JSTaggedValue::Exception());
685 }
686 }
687
688 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
689 if (thisTag->IsString() && regexp->IsECMAObject()) {
690 if (PropertyDetector::IsRegExpSpeciesDetectorValid(env) &&
691 BuiltinsRegExp::IsFastRegExp(thread, regexp, BuiltinsRegExp::RegExpSymbol::MATCHALL)) {
692 JSHandle<EcmaString> string = JSHandle<EcmaString>::Cast(thisTag);
693 return BuiltinsRegExp::RegExpMatchAll(thread, regexp, string, true);
694 }
695 }
696 // c. Let matcher be ? GetMethod(regexp, @@matchAll).
697 // d. If matcher is not undefined, then
698 bool canSkip = (PropertyDetector::IsNumberStringNotRegexpLikeDetectorValid(env) &&
699 (regexp->IsString() || regexp->IsNumber()));
700 if (!canSkip) {
701 JSHandle<JSTaggedValue> matcher = JSObject::GetMethod(thread, regexp, matchAllTag);
702 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
703 if (!matcher->IsUndefined()) {
704 // i. Return ? Call(matcher, regexp, « O »).
705 EcmaRuntimeCallInfo *info =
706 EcmaInterpreter::NewRuntimeCallInfo(thread, matcher, regexp, undefined, 1);
707 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
708 info->SetCallArg(thisTag.GetTaggedValue());
709 return JSFunction::Call(info);
710 }
711 }
712 }
713 // 3. Let S be ? ToString(O).
714 JSHandle<EcmaString> thisVal = JSTaggedValue::ToString(thread, thisTag);
715 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
716 // 4. Let rx be ? RegExpCreate(regexp, "g").
717 JSHandle<JSTaggedValue> rx(thread, BuiltinsRegExp::RegExpCreate(
718 thread, regexp, JSHandle<JSTaggedValue>(ecmaVm->GetFactory()->NewFromASCII("g"))));
719 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
720 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, rx, undefined, 1);
721 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
722 info->SetCallArg(thisVal.GetTaggedValue());
723 return JSFunction::Invoke(info, matchAllTag);
724 }
725
IsWellFormed(EcmaRuntimeCallInfo * argv)726 JSTaggedValue BuiltinsString::IsWellFormed(EcmaRuntimeCallInfo *argv)
727 {
728 ASSERT(argv);
729 BUILTINS_API_TRACE(argv->GetThread(), String, IsWellFormed);
730 JSThread *thread = argv->GetThread();
731 [[maybe_unused]] EcmaHandleScope handleScope(thread);
732
733 // 1. Let O be ? RequireObjectCoercible(this value).
734 JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
735 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
736
737 // 2. Let S be ? ToString(O).
738 JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
739 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
740
741 // 3. Return IsStringWellFormedUnicode(S).
742 uint32_t size = EcmaStringAccessor(string).GetLength();
743 uint32_t position = 0;
744 while (position < size) {
745 // i.Let first be the code unit at index position within string.
746 uint16_t first = EcmaStringAccessor(string).Get(position);
747 uint32_t cp = first - CHAR16_LETTER_NULL;
748 uint8_t codeUnitCount = 0;
749 bool isUnpairedSurrogate = false;
750 // ii. If first is neither a leading surrogate nor a trailing surrogate, then
751 // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.
752 if (!IsUTF16HighSurrogate(first) && !IsUTF16LowSurrogate(first)) {
753 codeUnitCount = 1; // 1 means: code unit count
754 isUnpairedSurrogate = false;
755 } else if (IsUTF16HighSurrogate(first) && position + 1 == size) {
756 // iii. If first is a trailing surrogate or position + 1 = size, then
757 // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
758 codeUnitCount = 1;
759 isUnpairedSurrogate = true;
760 } else {
761 // iv. Let second be the code unit at index position + 1 within string.
762 uint16_t second = EcmaStringAccessor(string).Get(position + 1);
763 // v. If second is not a trailing surrogate, then
764 // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
765 if (!IsUTF16LowSurrogate(second)) {
766 codeUnitCount = 1; // 1 means: code unit count
767 isUnpairedSurrogate = true;
768 } else {
769 // vi. Set cp to UTF16SurrogatePairToCodePoint(first, second).
770 // vii. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.
771 cp = UTF16SurrogatePairToCodePoint(first, second);
772 codeUnitCount = 2; // 2 means: code unit count
773 isUnpairedSurrogate = false;
774 }
775 }
776 if (isUnpairedSurrogate) {
777 return JSTaggedValue::False();
778 } else {
779 position = position + codeUnitCount;
780 }
781 thread->CheckSafepointIfSuspended();
782 }
783 return JSTaggedValue::True();
784 }
785
ToWellFormed(EcmaRuntimeCallInfo * argv)786 JSTaggedValue BuiltinsString::ToWellFormed(EcmaRuntimeCallInfo *argv)
787 {
788 ASSERT(argv);
789 BUILTINS_API_TRACE(argv->GetThread(), String, ToWellFormed);
790 JSThread *thread = argv->GetThread();
791 [[maybe_unused]] EcmaHandleScope handleScope(thread);
792 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
793
794 // 1. Let O be ? RequireObjectCoercible(this value).
795 JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
796 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
797
798 // 2. Let S be ? ToString(O).
799 JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
800 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
801
802 // 3. Let strLen be the length of S.
803 // 4. Let k be 0.
804 uint32_t size = EcmaStringAccessor(string).GetLength();
805 uint32_t position = 0;
806
807 // 5. Let result be the empty String.
808 std::u16string r;
809
810 // Repeat, while k < strLen,
811 // a. Let cp be CodePointAt(S, k).
812 // b. If cp.[[IsUnpairedSurrogate]] is true, then
813 // i. Set result to the string-concatenation of result and 0xFFFD (REPLACEMENT CHARACTER).
814 // c. Else,
815 // i. Set result to the string-concatenation of result and UTF16EncodeCodePoint(cp.[[CodePoint]]).
816 // d. Set k to k + cp.[[CodeUnitCount]].
817 while (position < size) {
818 // i.Let first be the code unit at index position within string.
819 uint16_t first = EcmaStringAccessor(string).Get(position);
820 uint32_t cp = first - CHAR16_LETTER_NULL;
821 uint8_t codeUnitCount = 0;
822 bool isUnpairedSurrogate = false;
823 // ii. If first is neither a leading surrogate nor a trailing surrogate, then
824 // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.
825 if (!IsUTF16HighSurrogate(first) && !IsUTF16LowSurrogate(first)) {
826 codeUnitCount = 1; // 1 means: code unit count
827 isUnpairedSurrogate = false;
828 } else if (IsUTF16HighSurrogate(first) && position + 1 == size) {
829 // iii. If first is a trailing surrogate or position + 1 = size, then
830 // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
831 codeUnitCount = 1;
832 isUnpairedSurrogate = true;
833 } else {
834 // iv. Let second be the code unit at index position + 1 within string.
835 uint16_t second = EcmaStringAccessor(string).Get(position + 1);
836 // v. If second is not a trailing surrogate, then
837 // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
838 if (!IsUTF16LowSurrogate(second)) {
839 codeUnitCount = 1; // 1 means: code unit count
840 isUnpairedSurrogate = true;
841 } else {
842 // vi. Set cp to UTF16SurrogatePairToCodePoint(first, second).
843 // vii. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.
844 cp = UTF16SurrogatePairToCodePoint(first, second);
845 codeUnitCount = 2; // 2 means: code unit count
846 isUnpairedSurrogate = false;
847 }
848 }
849 if (isUnpairedSurrogate) {
850 r.push_back(0xFFFD);
851 } else {
852 if (cp < 0 || cp > ENCODE_MAX_UTF16) {
853 THROW_RANGE_ERROR_AND_RETURN(thread, "CodePoint < 0 or CodePoint > 0x10FFFF",
854 JSTaggedValue::Exception());
855 }
856 if (cp > UINT16_MAX) {
857 uint16_t cu1 = std::floor((static_cast<uint32_t>(cp) - ENCODE_SECOND_FACTOR) /
858 ENCODE_FIRST_FACTOR) + ENCODE_LEAD_LOW;
859 uint16_t cu2 = ((static_cast<uint32_t>(cp) - ENCODE_SECOND_FACTOR) %
860 ENCODE_FIRST_FACTOR) + ENCODE_TRAIL_LOW;
861 std::u16string nextU16str1 = base::StringHelper::Utf16ToU16String(&cu1, 1);
862 std::u16string nextU16str2 = base::StringHelper::Utf16ToU16String(&cu2, 1);
863 base::StringHelper::InplaceAppend(r, nextU16str1);
864 base::StringHelper::InplaceAppend(r, nextU16str2);
865 } else {
866 auto u16tCp = static_cast<uint16_t>(cp);
867 std::u16string nextU16str = base::StringHelper::Utf16ToU16String(&u16tCp, 1);
868 base::StringHelper::InplaceAppend(r, nextU16str);
869 }
870 }
871 position = position + codeUnitCount;
872 thread->CheckSafepointIfSuspended();
873 }
874 const char16_t *constChar16tData = r.data();
875 auto *char16tData = const_cast<char16_t *>(constChar16tData);
876 auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
877 uint32_t u16strSize = r.size();
878 return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue();
879 }
880
881 // Static Semantics: UTF16SurrogatePairToCodePoint ( lead, trail )
UTF16SurrogatePairToCodePoint(uint16_t lead,uint16_t trail)882 uint32_t BuiltinsString::UTF16SurrogatePairToCodePoint(uint16_t lead, uint16_t trail)
883 {
884 // 1. Assert: lead is a leading surrogate and trail is a trailing surrogate.
885 ASSERT(IsUTF16HighSurrogate(lead) && IsUTF16LowSurrogate(trail));
886 // 2. Let cp be (lead - 0xD800) × 0x400 + (trail - 0xDC00) + 0x10000.
887 uint32_t cp = ((lead - 0xD800) << 10UL) + (trail - 0xDC00) + 0x10000;
888 // 3. Return the code point cp.
889 return cp;
890 }
891
892 // 21.1.3.12
Normalize(EcmaRuntimeCallInfo * argv)893 JSTaggedValue BuiltinsString::Normalize(EcmaRuntimeCallInfo *argv)
894 {
895 ASSERT(argv);
896 BUILTINS_API_TRACE(argv->GetThread(), String, Normalize);
897 JSThread *thread = argv->GetThread();
898 [[maybe_unused]] EcmaHandleScope handleScope(thread);
899 auto vm = thread->GetEcmaVM();
900 ObjectFactory *factory = vm->GetFactory();
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 JSHandle<EcmaString> nfc = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfcString());
918 JSHandle<EcmaString> nfd = factory->NewFromASCII("NFD");
919 JSHandle<EcmaString> nfkc = factory->NewFromASCII("NFKC");
920 JSHandle<EcmaString> nfkd = factory->NewFromASCII("NFKD");
921 UNormalizationMode uForm;
922 if (EcmaStringAccessor::StringsAreEqual(vm, formValue, nfc)) {
923 uForm = UNORM_NFC;
924 } else if (EcmaStringAccessor::StringsAreEqual(vm, formValue, nfd)) {
925 uForm = UNORM_NFD;
926 } else if (EcmaStringAccessor::StringsAreEqual(vm, formValue, nfkc)) {
927 uForm = UNORM_NFKC;
928 } else if (EcmaStringAccessor::StringsAreEqual(vm, formValue, nfkd)) {
929 uForm = UNORM_NFKD;
930 } else {
931 THROW_RANGE_ERROR_AND_RETURN(thread, "compare not equal", JSTaggedValue::Exception());
932 }
933
934 std::u16string u16strThis = EcmaStringAccessor(thisHandle).ToU16String();
935 const char16_t *constChar16tData = u16strThis.data();
936 icu::UnicodeString src(constChar16tData, u16strThis.size());
937 icu::UnicodeString res;
938 UErrorCode errorCode = U_ZERO_ERROR;
939 int32_t option = 0;
940
941 icu::Normalizer::normalize(src, uForm, option, res, errorCode);
942 JSHandle<EcmaString> str = intl::LocaleHelper::UStringToString(thread, res);
943 return JSTaggedValue(*str);
944 }
945
PadStart(EcmaRuntimeCallInfo * argv)946 JSTaggedValue BuiltinsString::PadStart(EcmaRuntimeCallInfo *argv)
947 {
948 ASSERT(argv);
949 BUILTINS_API_TRACE(argv->GetThread(), String, PadStart);
950 return BuiltinsString::Pad(argv, true);
951 }
952
PadEnd(EcmaRuntimeCallInfo * argv)953 JSTaggedValue BuiltinsString::PadEnd(EcmaRuntimeCallInfo *argv)
954 {
955 ASSERT(argv);
956 BUILTINS_API_TRACE(argv->GetThread(), String, PadEnd);
957 return BuiltinsString::Pad(argv, false);
958 }
959
960 // 21.1.3.13
Repeat(EcmaRuntimeCallInfo * argv)961 JSTaggedValue BuiltinsString::Repeat(EcmaRuntimeCallInfo *argv)
962 {
963 ASSERT(argv);
964 BUILTINS_API_TRACE(argv->GetThread(), String, Repeat);
965 JSThread *thread = argv->GetThread();
966 [[maybe_unused]] EcmaHandleScope handleScope(thread);
967 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
968 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
969 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
970 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
971 uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
972 JSHandle<JSTaggedValue> countTag = BuiltinsString::GetCallArg(argv, 0);
973 int32_t count = 0;
974 if (countTag->IsInt()) {
975 count = countTag->GetInt();
976 } else {
977 JSTaggedNumber num = JSTaggedValue::ToInteger(thread, countTag);
978 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
979 double d = num.GetNumber();
980 if (d == base::POSITIVE_INFINITY) {
981 THROW_RANGE_ERROR_AND_RETURN(thread, "is infinity", JSTaggedValue::Exception());
982 }
983 count = base::NumberHelper::DoubleInRangeInt32(d);
984 }
985 if (count < 0) {
986 THROW_RANGE_ERROR_AND_RETURN(thread, "less than 0", JSTaggedValue::Exception());
987 }
988 if (count == 0) {
989 auto emptyStr = thread->GetEcmaVM()->GetFactory()->GetEmptyString();
990 return emptyStr.GetTaggedValue();
991 }
992 if (thisLen == 0) {
993 return thisHandle.GetTaggedValue();
994 }
995 if (static_cast<uint32_t>(count) >= static_cast<uint32_t>(EcmaString::MAX_STRING_LENGTH) / thisLen) {
996 THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
997 }
998 bool isUtf8 = EcmaStringAccessor(thisHandle).IsUtf8();
999 EcmaString *result = EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), thisLen * count, isUtf8);
1000 for (uint32_t index = 0; index < static_cast<uint32_t>(count); ++index) {
1001 EcmaStringAccessor::ReadData(result, *thisHandle, index * thisLen, (count - index) * thisLen, thisLen);
1002 }
1003 return JSTaggedValue(result);
1004 }
1005
1006 // 21.1.3.14
Replace(EcmaRuntimeCallInfo * argv)1007 JSTaggedValue BuiltinsString::Replace(EcmaRuntimeCallInfo *argv)
1008 {
1009 ASSERT(argv);
1010 BUILTINS_API_TRACE(argv->GetThread(), String, Replace);
1011 JSThread *thread = argv->GetThread();
1012 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1013 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
1014 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1015
1016 auto ecmaVm = thread->GetEcmaVM();
1017 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1018 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1019 JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
1020 JSHandle<JSTaggedValue> replaceTag = BuiltinsString::GetCallArg(argv, 1);
1021
1022 ObjectFactory *factory = ecmaVm->GetFactory();
1023
1024 if (searchTag->IsJSRegExp() && replaceTag->IsString()) {
1025 JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
1026 JSHandle<JSRegExp> re(searchTag);
1027 JSHandle<JSTaggedValue> pattern(thread, re->GetOriginalSource());
1028 JSHandle<JSTaggedValue> flags(thread, re->GetOriginalFlags());
1029 bool isFastPath = BuiltinsRegExp::IsFastRegExp(thread, searchTag);
1030 if (isFastPath) {
1031 uint32_t lastIndex = static_cast<uint32_t>(BuiltinsRegExp::GetLastIndex(thread, searchTag, true));
1032 JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, thisTag,
1033 RegExpExecResultCache::REPLACE_TYPE, searchTag, JSTaggedValue(lastIndex),
1034 replaceTag);
1035 if (!cacheResult.IsUndefined()) {
1036 return cacheResult;
1037 }
1038 }
1039 }
1040
1041 if (searchTag->IsJSRegExp() && PropertyDetector::IsRegExpReplaceDetectorValid(env)) {
1042 JSTaggedValue proto = JSObject::GetPrototype(JSHandle<JSObject>(searchTag));
1043 if (proto == env->GetTaggedRegExpPrototype()) {
1044 return BuiltinsRegExp::ReplaceInternal(thread, searchTag, thisTag, replaceTag);
1045 }
1046 }
1047
1048 // If searchValue is neither undefined nor null, then
1049 if (!searchTag->IsUndefined() && !searchTag->IsNull()) {
1050 JSHandle<JSTaggedValue> replaceKey = env->GetReplaceSymbol();
1051 // Let replacer be GetMethod(searchValue, @@replace).
1052 JSHandle<JSTaggedValue> replaceMethod = JSObject::GetMethod(thread, searchTag, replaceKey);
1053 // ReturnIfAbrupt(replacer).
1054 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1055 // If replacer is not undefined, then
1056 if (!replaceMethod->IsUndefined()) {
1057 // Return Call(replacer, searchValue, «O, replaceValue»).
1058 const uint32_t argsLength = 2;
1059 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1060 EcmaRuntimeCallInfo *info =
1061 EcmaInterpreter::NewRuntimeCallInfo(thread, replaceMethod, searchTag, undefined, argsLength);
1062 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1063 info->SetCallArg(thisTag.GetTaggedValue(), replaceTag.GetTaggedValue());
1064 return JSFunction::Call(info);
1065 }
1066 }
1067
1068 // Let string be ToString(O).
1069 JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
1070 // ReturnIfAbrupt(string).
1071 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1072 // Let searchString be ToString(searchValue).
1073 JSHandle<EcmaString> searchString = JSTaggedValue::ToString(thread, searchTag);
1074 // ReturnIfAbrupt(searchString).
1075 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1076 // Let functionalReplace be IsCallable(replaceValue).
1077 if (!replaceTag->IsCallable()) {
1078 // If functionalReplace is false, then
1079 // Let replaceValue be ToString(replaceValue).
1080 // ReturnIfAbrupt(replaceValue)
1081 replaceTag = JSHandle<JSTaggedValue>(JSTaggedValue::ToString(thread, replaceTag));
1082 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1083 }
1084 // Search string for the first occurrence of searchString and let pos be the index within string of the first code
1085 // unit of the matched substring and let matched be searchString. If no occurrences of searchString were found,
1086 // return string.
1087 int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString);
1088 if (pos == -1) {
1089 return thisString.GetTaggedValue();
1090 }
1091 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1092 JSMutableHandle<JSTaggedValue> replHandle(thread, factory->GetEmptyString().GetTaggedValue());
1093 // If functionalReplace is true, then
1094 if (replaceTag->IsCallable()) {
1095 // Let replValue be Call(replaceValue, undefined,«matched, pos, and string»).
1096 const uint32_t argsLength = 3; // 3: «matched, pos, and string»
1097 EcmaRuntimeCallInfo *info =
1098 EcmaInterpreter::NewRuntimeCallInfo(thread, replaceTag, undefined, undefined, argsLength);
1099 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1100 info->SetCallArg(searchString.GetTaggedValue(), JSTaggedValue(pos), thisString.GetTaggedValue());
1101 JSTaggedValue replStrDeocodeValue = JSFunction::Call(info);
1102 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1103 replHandle.Update(replStrDeocodeValue);
1104 } else {
1105 // Let captures be an empty List.
1106 JSHandle<TaggedArray> capturesList = factory->EmptyArray();
1107 ASSERT_PRINT(replaceTag->IsString(), "replace must be string");
1108 JSHandle<EcmaString> replacement(thread, replaceTag->GetTaggedObject());
1109 // Let replStr be GetSubstitution(matched, string, pos, captures, replaceValue)
1110 replHandle.Update(GetSubstitution(thread, searchString, thisString, pos, capturesList, undefined, replacement));
1111 }
1112 JSHandle<EcmaString> realReplaceStr = JSTaggedValue::ToString(thread, replHandle);
1113 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1114 // Let tailPos be pos + the number of code units in matched.
1115 int32_t tailPos = pos + static_cast<int32_t>(EcmaStringAccessor(searchString).GetLength());
1116 // Let newString be the String formed by concatenating the first pos code units of string,
1117 // replStr, and the trailing
1118 // substring of string starting at index tailPos. If pos is 0,
1119 // the first element of the concatenation will be the
1120 // empty String.
1121 // Return newString.
1122 JSHandle<EcmaString> prefixString(thread, EcmaStringAccessor::FastSubString(ecmaVm, thisString, 0, pos));
1123 auto thisLen = EcmaStringAccessor(thisString).GetLength();
1124 JSHandle<EcmaString> suffixString(thread,
1125 EcmaStringAccessor::FastSubString(ecmaVm, thisString, tailPos, thisLen - tailPos));
1126 EcmaString *tempStr = EcmaStringAccessor::Concat(ecmaVm, prefixString, realReplaceStr);
1127 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1128 JSHandle<EcmaString> tempString(thread, tempStr);
1129 EcmaString *resultStr = EcmaStringAccessor::Concat(ecmaVm, tempString, suffixString);
1130 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1131 return JSTaggedValue(resultStr);
1132 }
1133
ReplaceAll(EcmaRuntimeCallInfo * argv)1134 JSTaggedValue BuiltinsString::ReplaceAll(EcmaRuntimeCallInfo *argv)
1135 {
1136 ASSERT(argv);
1137 JSThread *thread = argv->GetThread();
1138 BUILTINS_API_TRACE(thread, String, ReplaceAll);
1139 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1140 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
1141 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1142
1143 auto ecmaVm = thread->GetEcmaVM();
1144 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1145 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1146 JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
1147 JSHandle<JSTaggedValue> replaceTag = BuiltinsString::GetCallArg(argv, 1);
1148
1149 ObjectFactory *factory = ecmaVm->GetFactory();
1150
1151 if (!searchTag->IsUndefined() && !searchTag->IsNull()) {
1152 // a. Let isRegExp be ? IsRegExp(searchValue).
1153 bool isJSRegExp = JSObject::IsRegExp(thread, searchTag);
1154 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1155 // b. If isRegExp is true, then
1156 if (isJSRegExp) {
1157 // i. Let flags be ? Get(searchValue, "flags").
1158 JSHandle<JSTaggedValue> flagsString(globalConst->GetHandledFlagsString());
1159 JSHandle<JSTaggedValue> flags = JSObject::GetProperty(thread, searchTag, flagsString).GetValue();
1160 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1161 // ii. Perform ? RequireObjectCoercible(flags).
1162 JSTaggedValue::RequireObjectCoercible(thread, flags);
1163 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1164 // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception.
1165 JSHandle<EcmaString> flagString = JSTaggedValue::ToString(thread, flags);
1166 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1167 JSHandle<EcmaString> gString(globalConst->GetHandledGString());
1168 int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, flagString, gString);
1169 if (pos == -1) {
1170 THROW_TYPE_ERROR_AND_RETURN(thread,
1171 "string.prototype.replaceAll called with a non-global RegExp argument",
1172 JSTaggedValue::Exception());
1173 }
1174 }
1175 // c. Let replacer be ? GetMethod(searchValue, @@replace).
1176 JSHandle<JSTaggedValue> replaceKey = env->GetReplaceSymbol();
1177 JSHandle<JSTaggedValue> replaceMethod = JSObject::GetMethod(thread, searchTag, replaceKey);
1178 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1179 // d. If replacer is not undefined, then
1180 if (!replaceMethod->IsUndefined()) {
1181 // i. Return ? Call(replacer, searchValue, «O, replaceValue»).
1182 const size_t argsLength = 2;
1183 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1184 EcmaRuntimeCallInfo *info =
1185 EcmaInterpreter::NewRuntimeCallInfo(thread, replaceMethod, searchTag, undefined, argsLength);
1186 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1187 info->SetCallArg(thisTag.GetTaggedValue(), replaceTag.GetTaggedValue());
1188 return JSFunction::Call(info);
1189 }
1190 }
1191
1192 // 3. Let string be ? ToString(O).
1193 JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
1194 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1195 // 4. Let searchString be ? ToString(searchValue).
1196 JSHandle<EcmaString> searchString = JSTaggedValue::ToString(thread, searchTag);
1197 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1198 // 5. Let functionalReplace be IsCallable(replaceValue).
1199 // 6. If functionalReplace is false, then
1200 if (!replaceTag->IsCallable()) {
1201 // a. Set replaceValue to ? ToString(replaceValue).
1202 replaceTag = JSHandle<JSTaggedValue>(JSTaggedValue::ToString(thread, replaceTag));
1203 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1204 }
1205
1206 // 7. Let searchLength be the length of searchString.
1207 // 8. Let advanceBy be max(1, searchLength).
1208 int32_t searchLength = static_cast<int32_t>(EcmaStringAccessor(searchString).GetLength());
1209 int32_t advanceBy = std::max(1, searchLength);
1210 // 9. Let matchPositions be a new empty List.
1211 JSMutableHandle<EcmaString> accumulatedResult(thread, factory->GetEmptyString());
1212 // 10. Let position be ! StringIndexOf(string, searchString, 0).
1213 int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString);
1214 int32_t endOfLastMatch = 0;
1215 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1216 JSMutableHandle<JSTaggedValue> replHandle(thread, factory->GetEmptyString().GetTaggedValue());
1217 while (pos != -1) {
1218 // If functionalReplace is true, then
1219 if (replaceTag->IsCallable()) {
1220 // Let replValue be Call(replaceValue, undefined,«matched, pos, and string»).
1221 const uint32_t argsLength = 3; // 3: «matched, pos, and string»
1222 EcmaRuntimeCallInfo *info =
1223 EcmaInterpreter::NewRuntimeCallInfo(thread, replaceTag, undefined, undefined, argsLength);
1224 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1225 info->SetCallArg(searchString.GetTaggedValue(), JSTaggedValue(pos), thisString.GetTaggedValue());
1226 JSTaggedValue replStrDeocodeValue = JSFunction::Call(info);
1227 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1228 replHandle.Update(replStrDeocodeValue);
1229 } else {
1230 // Let captures be an empty List.
1231 JSHandle<TaggedArray> capturesList = factory->NewTaggedArray(0);
1232 ASSERT_PRINT(replaceTag->IsString(), "replace must be string");
1233 JSHandle<EcmaString> replacement(thread, replaceTag->GetTaggedObject());
1234 // Let replStr be GetSubstitution(matched, string, pos, captures, replaceValue)
1235 replHandle.Update(GetSubstitution(thread, searchString, thisString, pos,
1236 capturesList, undefined, replacement));
1237 }
1238 JSHandle<EcmaString> realReplaceStr = JSTaggedValue::ToString(thread, replHandle);
1239 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1240 // Let tailPos be pos + the number of code units in matched.
1241 // Let newString be the String formed by concatenating the first pos code units of string,
1242 // replStr, and the trailing substring of string starting at index tailPos.
1243 // If pos is 0, the first element of the concatenation will be the
1244 // empty String.
1245 // Return newString.
1246 JSHandle<EcmaString> prefixString(thread,
1247 EcmaStringAccessor::FastSubString(ecmaVm, thisString, endOfLastMatch,
1248 pos - endOfLastMatch));
1249 accumulatedResult.Update(JSTaggedValue(EcmaStringAccessor::Concat(ecmaVm, accumulatedResult, prefixString)));
1250 accumulatedResult.Update(JSTaggedValue(EcmaStringAccessor::Concat(ecmaVm, accumulatedResult, realReplaceStr)));
1251 endOfLastMatch = pos + searchLength;
1252 pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString, pos + advanceBy);
1253 thread->CheckSafepointIfSuspended();
1254 }
1255
1256 if (endOfLastMatch < static_cast<int32_t>(EcmaStringAccessor(thisString).GetLength())) {
1257 auto thisLen = EcmaStringAccessor(thisString).GetLength();
1258 JSHandle<EcmaString> suffixString(thread,
1259 EcmaStringAccessor::FastSubString(ecmaVm, thisString, endOfLastMatch, thisLen - endOfLastMatch));
1260 accumulatedResult.Update(JSTaggedValue(EcmaStringAccessor::Concat(ecmaVm, accumulatedResult, suffixString)));
1261 }
1262
1263 return accumulatedResult.GetTaggedValue();
1264 }
1265
1266 // Handle $& - match case
ProcessDollarAmpersand(std::u16string & stringBuilder,const JSHandle<EcmaString> & matched,bool & canBeCompress)1267 void ProcessDollarAmpersand(std::u16string &stringBuilder, const JSHandle<EcmaString> &matched, bool &canBeCompress)
1268 {
1269 stringBuilder += EcmaStringAccessor(matched).ToU16String();
1270 if (EcmaStringAccessor(matched).IsUtf16()) {
1271 canBeCompress = false;
1272 }
1273 }
1274
1275 // Handle $` - prefix case
ProcessDollarBacktick(EcmaVM * ecmaVm,std::u16string & stringBuilder,const JSHandle<EcmaString> & srcString,int position,bool & canBeCompress)1276 void ProcessDollarBacktick(EcmaVM *ecmaVm, std::u16string &stringBuilder, const JSHandle<EcmaString> &srcString,
1277 int position, bool &canBeCompress)
1278 {
1279 if (position > 0) {
1280 EcmaString *prefix = EcmaStringAccessor::FastSubString(ecmaVm, srcString, 0, position);
1281 stringBuilder += EcmaStringAccessor(prefix).ToU16String();
1282 if (EcmaStringAccessor(prefix).IsUtf16()) {
1283 canBeCompress = false;
1284 }
1285 }
1286 }
1287
1288 // Handle $' - suffix case
ProcessDollarSingleQuote(EcmaVM * ecmaVm,std::u16string & stringBuilder,const JSHandle<EcmaString> & srcString,int tailPos,bool & canBeCompress)1289 void ProcessDollarSingleQuote(EcmaVM *ecmaVm, std::u16string &stringBuilder, const JSHandle<EcmaString> &srcString,
1290 int tailPos, bool &canBeCompress)
1291 {
1292 int32_t srcLength = static_cast<int32_t>(EcmaStringAccessor(srcString).GetLength());
1293 if (tailPos < srcLength) {
1294 EcmaString *suffix = EcmaStringAccessor::FastSubString(ecmaVm, srcString, tailPos, srcLength - tailPos);
1295 stringBuilder += EcmaStringAccessor(suffix).ToU16String();
1296 if (EcmaStringAccessor(suffix).IsUtf16()) {
1297 canBeCompress = false;
1298 }
1299 }
1300 }
1301
ProcessDigitCapture(const JSHandle<EcmaString> & replacementFlat,uint32_t peekIndex,uint32_t replaceLength,const JSHandle<TaggedArray> & captureList,std::u16string & stringBuilder)1302 std::pair<int32_t, bool> ProcessDigitCapture(const JSHandle<EcmaString> &replacementFlat, uint32_t peekIndex,
1303 uint32_t replaceLength, const JSHandle<TaggedArray> &captureList,
1304 std::u16string &stringBuilder)
1305 {
1306 uint32_t capturesLength = captureList->GetLength();
1307 uint16_t peek = EcmaStringAccessor(replacementFlat).Get(peekIndex);
1308 uint32_t scaledIndex = peek - '0';
1309 int32_t advance = 1;
1310 bool canBeCompress = true;
1311
1312 if (peekIndex + 1 < replaceLength) {
1313 uint16_t nextPeek = EcmaStringAccessor(replacementFlat).Get(peekIndex + 1);
1314 if (nextPeek >= '0' && nextPeek <= '9') {
1315 constexpr uint32_t tenBase = 10;
1316 uint32_t newScaledIndex = scaledIndex * tenBase + (nextPeek - '0');
1317 if (newScaledIndex <= capturesLength) {
1318 scaledIndex = newScaledIndex;
1319 advance = 2; // 2: 2 means from index needs to add two.
1320 }
1321 }
1322 }
1323
1324 if (scaledIndex == 0 || scaledIndex > capturesLength) {
1325 stringBuilder += '$';
1326 return {peekIndex, canBeCompress}; // No change in compressibility, just return the next index.
1327 }
1328
1329 JSTaggedValue capturesVal(captureList->Get(scaledIndex - 1));
1330 if (!capturesVal.IsUndefined()) {
1331 EcmaString *captureString = EcmaString::Cast(capturesVal.GetTaggedObject());
1332 stringBuilder += EcmaStringAccessor(captureString).ToU16String();
1333 if (EcmaStringAccessor(captureString).IsUtf16()) {
1334 canBeCompress = false;
1335 }
1336 }
1337 return {static_cast<int32_t>(peekIndex) + advance, canBeCompress};
1338 }
1339
1340 // Handle $< case
ProcessNamedCaptures(JSThread * thread,const JSHandle<EcmaString> & replacementFlat,int32_t peekIndex,const JSHandle<JSTaggedValue> & namedCaptures,std::u16string & stringBuilder)1341 std::pair<int32_t, bool> ProcessNamedCaptures(JSThread *thread, const JSHandle<EcmaString> &replacementFlat,
1342 int32_t peekIndex, const JSHandle<JSTaggedValue> &namedCaptures,
1343 std::u16string &stringBuilder)
1344 {
1345 bool canBeCompress = true;
1346 if (namedCaptures->IsUndefined()) {
1347 stringBuilder += '$';
1348 return {peekIndex, canBeCompress};
1349 }
1350 auto ecmaVm = thread->GetEcmaVM();
1351 ObjectFactory *factory = ecmaVm->GetFactory();
1352 JSHandle<EcmaString> greaterSymString = factory->NewFromASCII(">");
1353 int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, greaterSymString, peekIndex);
1354 if (pos == -1) {
1355 stringBuilder += '$';
1356 return {peekIndex, canBeCompress};
1357 }
1358 JSHandle<EcmaString> groupName = JSHandle<EcmaString>(
1359 thread, EcmaStringAccessor::FastSubString(ecmaVm, replacementFlat, peekIndex + 1, pos - peekIndex - 1));
1360 JSHandle<JSTaggedValue> names(groupName);
1361 JSHandle<JSTaggedValue> capture = JSObject::GetProperty(thread, namedCaptures, names).GetValue();
1362 if (capture->IsUndefined()) {
1363 return {pos + 1, canBeCompress};
1364 }
1365 JSHandle<EcmaString> captureName = JSTaggedValue::ToString(thread, capture);
1366 stringBuilder += EcmaStringAccessor(captureName).ToU16String();
1367 if (EcmaStringAccessor(captureName).IsUtf16()) {
1368 canBeCompress = false;
1369 }
1370 return {pos + 1, canBeCompress};
1371 }
1372
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)1373 JSTaggedValue BuiltinsString::GetSubstitution(JSThread *thread, const JSHandle<EcmaString> &matched,
1374 const JSHandle<EcmaString> &srcString, int position,
1375 const JSHandle<TaggedArray> &captureList,
1376 const JSHandle<JSTaggedValue> &namedCaptures,
1377 const JSHandle<EcmaString> &replacement)
1378 {
1379 BUILTINS_API_TRACE(thread, String, GetSubstitution);
1380 auto ecmaVm = thread->GetEcmaVM();
1381 ObjectFactory *factory = ecmaVm->GetFactory();
1382 JSHandle<EcmaString> dollarString = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledDollarString());
1383 JSHandle<EcmaString> replacementFlat(thread, EcmaStringAccessor::Flatten(ecmaVm, replacement));
1384 int32_t replaceLength = static_cast<int32_t>(EcmaStringAccessor(replacementFlat).GetLength());
1385 int32_t tailPos = position + static_cast<int32_t>(EcmaStringAccessor(matched).GetLength());
1386
1387 int32_t nextDollarIndex = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, dollarString);
1388 if (nextDollarIndex < 0) {
1389 return replacementFlat.GetTaggedValue();
1390 }
1391 std::u16string stringBuilder;
1392 bool canBeCompress = true;
1393 if (nextDollarIndex > 0) {
1394 stringBuilder = EcmaStringAccessor(replacementFlat).ToU16String(nextDollarIndex);
1395 if (EcmaStringAccessor(replacementFlat).IsUtf16()) {
1396 canBeCompress = false;
1397 }
1398 }
1399
1400 while (true) {
1401 int peekIndex = nextDollarIndex + 1;
1402 if (peekIndex >= replaceLength) {
1403 stringBuilder += '$';
1404 auto *char16tData = const_cast<char16_t *>(stringBuilder.c_str());
1405 auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
1406 return canBeCompress ?
1407 factory->NewFromUtf16LiteralCompress(uint16tData, stringBuilder.length()).GetTaggedValue() :
1408 factory->NewFromUtf16LiteralNotCompress(uint16tData, stringBuilder.length()).GetTaggedValue();
1409 }
1410 int continueFromIndex = -1;
1411 uint16_t peek = EcmaStringAccessor(replacementFlat).Get(peekIndex);
1412 switch (peek) {
1413 case '$': // $$
1414 stringBuilder += '$';
1415 continueFromIndex = peekIndex + 1;
1416 break;
1417 case '&': // $& - match
1418 ProcessDollarAmpersand(stringBuilder, matched, canBeCompress);
1419 continueFromIndex = peekIndex + 1;
1420 break;
1421 case '`': // $` - prefix
1422 ProcessDollarBacktick(ecmaVm, stringBuilder, srcString, position, canBeCompress);
1423 continueFromIndex = peekIndex + 1;
1424 break;
1425 case '\'': { // $' - suffix
1426 ProcessDollarSingleQuote(ecmaVm, stringBuilder, srcString, tailPos, canBeCompress);
1427 continueFromIndex = peekIndex + 1;
1428 break;
1429 }
1430 case '0':
1431 case '1':
1432 case '2':
1433 case '3':
1434 case '4':
1435 case '5':
1436 case '6':
1437 case '7':
1438 case '8':
1439 case '9': {
1440 auto result =
1441 ProcessDigitCapture(replacementFlat, peekIndex, replaceLength, captureList, stringBuilder);
1442 continueFromIndex = result.first;
1443 canBeCompress = result.second && canBeCompress; // 保留canBeCompress的值,只在需要时更新为false
1444 break;
1445 }
1446 case '<': {
1447 auto result = ProcessNamedCaptures(thread, replacementFlat, peekIndex, namedCaptures, stringBuilder);
1448 continueFromIndex = result.first;
1449 canBeCompress = result.second && canBeCompress; // 保留canBeCompress的值,只在需要时更新为false
1450 break;
1451 }
1452 default:
1453 stringBuilder += '$';
1454 continueFromIndex = peekIndex;
1455 break;
1456 }
1457 // Go the the next $ in the replacement.
1458 nextDollarIndex = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, dollarString, continueFromIndex);
1459 if (nextDollarIndex < 0) {
1460 if (continueFromIndex < replaceLength) {
1461 EcmaString *nextAppend = EcmaStringAccessor::FastSubString(ecmaVm, replacementFlat, continueFromIndex,
1462 replaceLength - continueFromIndex);
1463 stringBuilder += EcmaStringAccessor(nextAppend).ToU16String();
1464 if (EcmaStringAccessor(nextAppend).IsUtf16()) {
1465 canBeCompress = false;
1466 }
1467 }
1468 auto *char16tData = const_cast<char16_t *>(stringBuilder.c_str());
1469 auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
1470 return canBeCompress ?
1471 factory->NewFromUtf16LiteralCompress(uint16tData, stringBuilder.length()).GetTaggedValue() :
1472 factory->NewFromUtf16LiteralNotCompress(uint16tData, stringBuilder.length()).GetTaggedValue();
1473 }
1474 // Append substring between the previous and the next $ character.
1475 if (nextDollarIndex > continueFromIndex) {
1476 EcmaString *nextAppend = EcmaStringAccessor::FastSubString(
1477 ecmaVm, replacementFlat, continueFromIndex, nextDollarIndex - continueFromIndex);
1478 stringBuilder += EcmaStringAccessor(nextAppend).ToU16String();
1479 if (EcmaStringAccessor(nextAppend).IsUtf16()) {
1480 canBeCompress = false;
1481 }
1482 }
1483 thread->CheckSafepointIfSuspended();
1484 }
1485 LOG_ECMA(FATAL) << "this branch is unreachable";
1486 UNREACHABLE();
1487 }
1488
1489 // 21.1.3.15
Search(EcmaRuntimeCallInfo * argv)1490 JSTaggedValue BuiltinsString::Search(EcmaRuntimeCallInfo *argv)
1491 {
1492 ASSERT(argv);
1493 BUILTINS_API_TRACE(argv->GetThread(), String, Search);
1494 JSThread *thread = argv->GetThread();
1495 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1496 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1497 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1498 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1499 JSHandle<JSTaggedValue> regexp = BuiltinsString::GetCallArg(argv, 0);
1500 if (thisTag->IsString() && regexp->IsECMAObject()) {
1501 if (BuiltinsRegExp::IsFastRegExp(thread, regexp, BuiltinsRegExp::RegExpSymbol::SEARCH)) {
1502 return BuiltinsRegExp::RegExpSearchFast(thread, regexp, thisTag);
1503 }
1504 }
1505 JSHandle<JSTaggedValue> searchTag = thread->GetEcmaVM()->GetGlobalEnv()->GetSearchSymbol();
1506 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1507 if (!regexp->IsUndefined() && !regexp->IsNull()) {
1508 JSHandle<JSTaggedValue> searcher = JSObject::GetMethod(thread, regexp, searchTag);
1509 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1510 if (!searcher->IsUndefined()) {
1511 ASSERT(searcher->IsJSFunctionBase());
1512 EcmaRuntimeCallInfo *info =
1513 EcmaInterpreter::NewRuntimeCallInfo(thread, searcher, regexp, undefined, 1);
1514 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1515 info->SetCallArg(thisTag.GetTaggedValue());
1516 return JSFunction::Call(info);
1517 }
1518 }
1519 JSHandle<EcmaString> thisVal = JSTaggedValue::ToString(thread, thisTag);
1520 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1521 JSHandle<JSTaggedValue> rx(thread, BuiltinsRegExp::RegExpCreate(thread, regexp, undefined));
1522 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1523 EcmaRuntimeCallInfo *info =
1524 EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, rx, undefined, 1);
1525 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1526 info->SetCallArg(thisVal.GetTaggedValue());
1527 return JSFunction::Invoke(info, searchTag);
1528 }
1529
1530 // 21.1.3.16
Slice(EcmaRuntimeCallInfo * argv)1531 JSTaggedValue BuiltinsString::Slice(EcmaRuntimeCallInfo *argv)
1532 {
1533 ASSERT(argv);
1534 BUILTINS_API_TRACE(argv->GetThread(), String, Slice);
1535 JSThread *thread = argv->GetThread();
1536 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1537
1538 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1539 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1540 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1541 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1542 int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
1543 JSHandle<JSTaggedValue> startTag = BuiltinsString::GetCallArg(argv, 0);
1544 JSTaggedNumber startVal = JSTaggedValue::ToInteger(thread, startTag);
1545 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1546 int32_t start = ConvertDoubleToInt(startVal.GetNumber());
1547 int32_t end = 0;
1548 JSHandle<JSTaggedValue> endTag = BuiltinsString::GetCallArg(argv, 1);
1549 if (endTag->IsUndefined()) {
1550 end = thisLen;
1551 } else {
1552 JSTaggedNumber endVal = JSTaggedValue::ToInteger(thread, endTag);
1553 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1554 end = ConvertDoubleToInt(endVal.GetNumber());
1555 }
1556 int32_t from = 0;
1557 int32_t to = 0;
1558 if (start < 0) {
1559 from = std::max(start + thisLen, 0);
1560 } else {
1561 from = std::min(start, thisLen);
1562 }
1563 if (end < 0) {
1564 to = std::max(end + thisLen, 0);
1565 } else {
1566 to = std::min(end, thisLen);
1567 }
1568 int32_t len = std::max(to - from, 0);
1569 return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisHandle, from, len));
1570 }
1571
1572 // 21.1.3.17
Split(EcmaRuntimeCallInfo * argv)1573 JSTaggedValue BuiltinsString::Split(EcmaRuntimeCallInfo *argv)
1574 {
1575 ASSERT(argv);
1576 BUILTINS_API_TRACE(argv->GetThread(), String, Split);
1577 JSThread *thread = argv->GetThread();
1578 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1579 auto ecmaVm = thread->GetEcmaVM();
1580 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1581
1582 // Let O be RequireObjectCoercible(this value).
1583 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
1584 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1585 JSHandle<JSTaggedValue> seperatorTag = BuiltinsString::GetCallArg(argv, 0);
1586 JSHandle<JSTaggedValue> limitTag = BuiltinsString::GetCallArg(argv, 1);
1587
1588 if (thisTag->IsString() && seperatorTag->IsECMAObject()) {
1589 // this condition need change, all regexp should use RegExpSplit
1590 if (BuiltinsRegExp::IsFastRegExp(thread, seperatorTag)) {
1591 return BuiltinsRegExp::RegExpSplit(thread, seperatorTag, thisTag, limitTag, true);
1592 }
1593 }
1594 if (thisTag->IsString() && seperatorTag->IsString()) {
1595 JSHandle<EcmaString> thisString(thisTag);
1596 JSHandle<EcmaString> seperatorString(seperatorTag);
1597 auto thisLength = EcmaStringAccessor(thisString).GetLength();
1598 auto seperatorLength = EcmaStringAccessor(seperatorString).GetLength();
1599 if (limitTag->IsUndefined() && thisLength != 0 && seperatorLength != 0) {
1600 return CreateArrayThisStringAndSeperatorStringAreNotEmpty(
1601 thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength);
1602 }
1603 uint32_t lim = UINT32_MAX - 1;
1604 if (!limitTag->IsUndefined()) {
1605 JSTaggedNumber limitIntValue = JSTaggedValue::ToInteger(thread, limitTag);
1606 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1607 lim = limitIntValue.ToUint32();
1608 }
1609 // ReturnIfAbrupt(lim).
1610 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1611 if (lim == 0) {
1612 JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1613 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1614 return resultArray.GetTaggedValue();
1615 }
1616 return CreateArrayBySplitString(thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength, lim);
1617 }
1618
1619 // If separator is neither undefined nor null, then
1620 if (!seperatorTag->IsUndefined() && !seperatorTag->IsNull()) {
1621 JSHandle<JSTaggedValue> splitKey = env->GetSplitSymbol();
1622 // Let splitter be GetMethod(separator, @@split).
1623 JSHandle<JSTaggedValue> splitter = JSObject::GetMethod(thread, seperatorTag, splitKey);
1624 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1625 if (!splitter->IsUndefined()) {
1626 // Return Call(splitter, separator, «O, limit»).
1627 const uint32_t argsLength = 2;
1628 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1629 EcmaRuntimeCallInfo *info =
1630 EcmaInterpreter::NewRuntimeCallInfo(thread, splitter, seperatorTag, undefined, argsLength);
1631 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1632 info->SetCallArg(thisTag.GetTaggedValue(), limitTag.GetTaggedValue());
1633 return JSFunction::Call(info);
1634 }
1635 }
1636 // Let S be ToString(O).
1637 JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
1638 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1639
1640 // If limit is undefined, let lim = 2^53–1; else let lim = ToLength(limit).
1641 uint32_t lim = UINT32_MAX - 1;
1642 if (!limitTag->IsUndefined()) {
1643 JSTaggedNumber limitIntValue = JSTaggedValue::ToInteger(thread, limitTag);
1644 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1645 lim = limitIntValue.ToUint32();
1646 }
1647 // ReturnIfAbrupt(lim).
1648 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1649 // Let s be the number of elements in S.
1650 auto thisLength = EcmaStringAccessor(thisString).GetLength();
1651 JSHandle<EcmaString> seperatorString = JSTaggedValue::ToString(thread, seperatorTag);
1652 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1653 // If lim = 0, return A.
1654 if (lim == 0) {
1655 JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1656 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1657 return resultArray.GetTaggedValue();
1658 }
1659 auto seperatorLength = EcmaStringAccessor(seperatorString).GetLength();
1660 // If S is undefined or (this.length = 0 and S.length != 0), return array of size is 1 containing this string
1661 if (seperatorTag->IsUndefined()) {
1662 JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(1)));
1663 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1664 // Perform CreateDataProperty(A, "0", S), CreateDataProperty's fast path
1665 JSObject::CreateDataProperty(thread, resultArray, 0, JSHandle<JSTaggedValue>(thisString));
1666 ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty(A, \"0\", S) can't throw exception");
1667 return resultArray.GetTaggedValue();
1668 }
1669 return CreateArrayBySplitString(thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength, lim);
1670 }
1671
CreateArrayFromString(JSThread * thread,EcmaVM * ecmaVm,const JSHandle<EcmaString> & thisString,uint32_t thisLength,uint32_t lim)1672 JSTaggedValue BuiltinsString::CreateArrayFromString(JSThread *thread, EcmaVM *ecmaVm,
1673 const JSHandle<EcmaString> &thisString, uint32_t thisLength, uint32_t lim)
1674 {
1675 bool isUtf8 = EcmaStringAccessor(thisString).IsUtf8();
1676 bool canBeCompressed = false;
1677 if (EcmaStringAccessor(thisString).IsLineOrConstantString()) {
1678 canBeCompressed = EcmaStringAccessor::CanBeCompressed(*thisString);
1679 }
1680 bool isOneByte = isUtf8 & canBeCompressed;
1681 JSHandle<EcmaString> seperatorString = thread->GetEcmaVM()->GetFactory()->GetEmptyString();
1682 if (lim == UINT32_MAX - 1) {
1683 JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1684 JSTaggedValue cacheResult = StringSplitResultCache::FindCachedResult(thread, cacheTable, thisString,
1685 seperatorString, isOneByte);
1686 if (cacheResult != JSTaggedValue::Undefined()) {
1687 JSHandle<JSTaggedValue> resultArray(JSArray::CreateArrayFromList(thread,
1688 JSHandle<TaggedArray>(thread, cacheResult)));
1689 return resultArray.GetTaggedValue();
1690 }
1691 }
1692 uint32_t actualLength = std::min(thisLength, lim);
1693 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1694 JSHandle<TaggedArray> array = factory->NewTaggedArray(actualLength);
1695 for (uint32_t i = 0; i < actualLength; ++i) {
1696 EcmaString *elementString = EcmaStringAccessor::GetSubString(ecmaVm, thisString, i, 1);
1697 // Perform CreateDataProperty(A, "0", S), CreateDataProperty's fast path
1698 if (isOneByte) {
1699 array->Set<false>(thread, i, JSTaggedValue(elementString));
1700 } else {
1701 array->Set(thread, i, JSTaggedValue(elementString));
1702 }
1703 ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty can't throw exception");
1704 }
1705 JSHandle<JSArray> resultArray = JSArray::CreateArrayFromList(thread, array);
1706 if (lim == UINT32_MAX - 1) {
1707 JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1708 StringSplitResultCache::SetCachedResult(thread, cacheTable, thisString, seperatorString, array);
1709 }
1710 return resultArray.GetTaggedValue();
1711 }
1712
CreateArrayBySplitString(JSThread * thread,EcmaVM * ecmaVm,const JSHandle<EcmaString> & thisString,const JSHandle<EcmaString> & seperatorString,uint32_t thisLength,uint32_t seperatorLength,uint32_t lim)1713 JSTaggedValue BuiltinsString::CreateArrayBySplitString(JSThread *thread, EcmaVM *ecmaVm,
1714 const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &seperatorString,
1715 uint32_t thisLength, uint32_t seperatorLength, uint32_t lim)
1716 {
1717 if (thisLength != 0) {
1718 if (seperatorLength != 0) {
1719 return CreateArrayThisStringAndSeperatorStringAreNotEmpty(
1720 thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength, lim);
1721 }
1722 return CreateArrayFromString(thread, ecmaVm, thisString, thisLength, lim);
1723 } else {
1724 if (seperatorLength != 0) {
1725 JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(1)));
1726 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1727 // Perform CreateDataProperty(A, "0", S), CreateDataProperty's fast path
1728 JSObject::CreateDataProperty(thread, resultArray, 0, JSHandle<JSTaggedValue>(thisString));
1729 ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty(A, \"0\", S) can't throw exception");
1730 return resultArray.GetTaggedValue();
1731 }
1732 JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1733 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1734 return resultArray.GetTaggedValue();
1735 }
1736 }
1737
CreateArrayThisStringAndSeperatorStringAreNotEmpty(JSThread * thread,EcmaVM * ecmaVm,const JSHandle<EcmaString> & thisString,const JSHandle<EcmaString> & seperatorString,uint32_t thisLength,uint32_t seperatorLength,uint32_t lim)1738 JSTaggedValue BuiltinsString::CreateArrayThisStringAndSeperatorStringAreNotEmpty(JSThread *thread,
1739 EcmaVM *ecmaVm, const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &seperatorString,
1740 uint32_t thisLength, uint32_t seperatorLength, uint32_t lim)
1741 {
1742 if (lim == UINT32_MAX - 1) {
1743 JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1744 JSTaggedValue cacheResult = StringSplitResultCache::FindCachedResult(thread, cacheTable, thisString,
1745 seperatorString);
1746 if (cacheResult != JSTaggedValue::Undefined()) {
1747 JSHandle<JSTaggedValue> resultArray(JSArray::CreateArrayFromList(thread,
1748 JSHandle<TaggedArray>(thread, cacheResult)));
1749 return resultArray.GetTaggedValue();
1750 }
1751 }
1752 uint32_t arrayLength = 0;
1753 std::vector<int32_t> posArray;
1754 int32_t index = 0;
1755 int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, seperatorString);
1756 while (pos != -1) {
1757 posArray.emplace_back(pos);
1758 ++arrayLength;
1759 if (arrayLength == lim) {
1760 break;
1761 }
1762 index = pos + static_cast<int32_t>(seperatorLength);
1763 pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, seperatorString, index);
1764 thread->CheckSafepointIfSuspended();
1765 }
1766 uint32_t posArrLength = posArray.size();
1767 arrayLength = lim > posArrLength ? posArrLength + 1 : posArrLength;
1768 return JSArray::ArrayCreateWithInit(thread, arrayLength,
1769 [thread, ecmaVm, &thisString, &seperatorString, &posArray, thisLength, seperatorLength, lim, posArrLength]
1770 (const JSHandle<TaggedArray> &newElements, [[maybe_unused]] uint32_t length) {
1771 int32_t index = 0;
1772 int32_t pos = 0;
1773 JSMutableHandle<JSTaggedValue> elementTag(thread, JSTaggedValue::Undefined());
1774 for (uint32_t i = 0; i < posArrLength; i++) {
1775 pos = posArray[i];
1776 EcmaString *elementString = EcmaStringAccessor::GetSubString(ecmaVm, thisString, index, pos - index);
1777 elementTag.Update(JSTaggedValue(elementString));
1778 newElements->Set(thread, i, elementTag);
1779 index = pos + static_cast<int32_t>(seperatorLength);
1780 }
1781 if (lim > posArrLength) {
1782 EcmaString *elementString =
1783 EcmaStringAccessor::GetSubString(ecmaVm, thisString, index, thisLength - index);
1784 elementTag.Update(JSTaggedValue(elementString));
1785 newElements->Set(thread, posArrLength, elementTag);
1786 }
1787 if (lim == UINT32_MAX - 1) {
1788 JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1789 StringSplitResultCache::SetCachedResult(thread, cacheTable, thisString, seperatorString, newElements);
1790 }
1791 });
1792 }
1793
1794 // 21.1.3.18
StartsWith(EcmaRuntimeCallInfo * argv)1795 JSTaggedValue BuiltinsString::StartsWith(EcmaRuntimeCallInfo *argv)
1796 {
1797 ASSERT(argv);
1798 BUILTINS_API_TRACE(argv->GetThread(), String, StartsWith);
1799 JSThread *thread = argv->GetThread();
1800 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1801 JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
1802
1803 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1804 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1805 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1806 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1807 bool isRegexp = JSObject::IsRegExp(thread, searchTag);
1808 if (isRegexp) {
1809 THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception());
1810 }
1811
1812 JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
1813 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1814 uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
1815 uint32_t searchLen = EcmaStringAccessor(searchHandle).GetLength();
1816 int32_t pos = 0;
1817 JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 1);
1818 if (posTag->IsUndefined()) {
1819 pos = 0;
1820 } else if (posTag->IsInt()) {
1821 pos = posTag->GetInt();
1822 } else {
1823 JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
1824 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1825 if (posVal.GetNumber() == BuiltinsNumber::POSITIVE_INFINITY) {
1826 pos = thisLen;
1827 } else {
1828 pos = posVal.ToInt32();
1829 }
1830 }
1831 pos = std::min(std::max(pos, 0), static_cast<int32_t>(thisLen));
1832 if (static_cast<uint32_t>(pos) + searchLen > thisLen) {
1833 return BuiltinsString::GetTaggedBoolean(false);
1834 }
1835
1836 bool result = EcmaStringAccessor::IsSubStringAt(thread->GetEcmaVM(), thisHandle, searchHandle, pos);
1837
1838 return BuiltinsString::GetTaggedBoolean(result);
1839 }
1840
1841 // 21.1.3.19
Substring(EcmaRuntimeCallInfo * argv)1842 JSTaggedValue BuiltinsString::Substring(EcmaRuntimeCallInfo *argv)
1843 {
1844 ASSERT(argv);
1845 BUILTINS_API_TRACE(argv->GetThread(), String, Substring);
1846 JSThread *thread = argv->GetThread();
1847 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1848
1849 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1850 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1851 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1852 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1853 int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
1854 JSHandle<JSTaggedValue> startTag = BuiltinsString::GetCallArg(argv, 0);
1855 JSTaggedNumber startVal = JSTaggedValue::ToInteger(thread, startTag);
1856 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1857 int32_t start = ConvertDoubleToInt(startVal.GetNumber());
1858 int32_t end = 0;
1859 JSHandle<JSTaggedValue> endTag = BuiltinsString::GetCallArg(argv, 1);
1860 if (endTag->IsUndefined()) {
1861 end = thisLen;
1862 } else {
1863 JSTaggedNumber endVal = JSTaggedValue::ToInteger(thread, endTag);
1864 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1865 end = ConvertDoubleToInt(endVal.GetNumber());
1866 }
1867 start = std::min(std::max(start, 0), thisLen);
1868 end = std::min(std::max(end, 0), thisLen);
1869 int32_t from = std::min(start, end);
1870 int32_t to = std::max(start, end);
1871 int32_t len = to - from;
1872 return JSTaggedValue(EcmaStringAccessor::GetSubString(thread->GetEcmaVM(), thisHandle, from, len));
1873 }
1874
1875 // 21.1.3.20
ToLocaleLowerCase(EcmaRuntimeCallInfo * argv)1876 JSTaggedValue BuiltinsString::ToLocaleLowerCase(EcmaRuntimeCallInfo *argv)
1877 {
1878 ASSERT(argv);
1879 BUILTINS_API_TRACE(argv->GetThread(), String, ToLocaleLowerCase);
1880 JSThread *thread = argv->GetThread();
1881 EcmaVM *ecmaVm = thread->GetEcmaVM();
1882 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1883
1884 // Let O be RequireObjectCoercible(this value).
1885 JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1886 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1887 // Let S be ? ToString(O).
1888 JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
1889 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1890
1891 // Let requestedLocales be ? CanonicalizeLocaleList(locales).
1892 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
1893 // Fast path
1894 if (locales->IsUndefined() && EcmaStringAccessor(string).IsUtf8()) {
1895 EcmaString *result = EcmaStringAccessor::TryToLower(ecmaVm, string);
1896 return JSTaggedValue(result);
1897 }
1898 JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
1899 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1900
1901 // If requestedLocales is not an empty List, then Let requestedLocale be requestedLocales[0].
1902 // Else, Let requestedLocale be DefaultLocale().
1903 JSHandle<EcmaString> requestedLocale = intl::LocaleHelper::DefaultLocale(thread);
1904 if (requestedLocales->GetLength() != 0) {
1905 requestedLocale = JSHandle<EcmaString>(thread, requestedLocales->Get(0));
1906 }
1907
1908 // Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences
1909 // removed.
1910 intl::LocaleHelper::ParsedLocale noExtensionsLocale = intl::LocaleHelper::HandleLocale(requestedLocale);
1911 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1912
1913 // Let availableLocales be a List with language tags that includes the languages for which the Unicode Character
1914 // Database contains language sensitive case mappings. Implementations may add additional language tags
1915 // if they support case mapping for additional locales.
1916 std::vector<std::string> availableLocales = intl::LocaleHelper::GetAvailableLocales(thread, nullptr, nullptr);
1917 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1918
1919 // Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1920 std::string locale = intl::LocaleHelper::BestAvailableLocale(availableLocales, noExtensionsLocale.base);
1921 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1922
1923 // If locale is undefined, let locale be "und".
1924 if (locale.empty()) {
1925 locale = "und";
1926 }
1927
1928 // Let uString be a List containing in order the code points of S as defined in ES2020, 6.1.4,
1929 // starting at the first element of S.
1930 // Transform those elements in uString to the to the Unicode Default Case Conversion algorithm
1931 icu::Locale icuLocale = icu::Locale::createFromName(locale.c_str());
1932 EcmaString *result = EcmaStringAccessor::ToLocaleLower(ecmaVm, string, icuLocale);
1933 return JSTaggedValue(result);
1934 }
1935
1936 // 21.1.3.21
ToLocaleUpperCase(EcmaRuntimeCallInfo * argv)1937 JSTaggedValue BuiltinsString::ToLocaleUpperCase(EcmaRuntimeCallInfo *argv)
1938 {
1939 ASSERT(argv);
1940 BUILTINS_API_TRACE(argv->GetThread(), String, ToLocaleLowerCase);
1941 JSThread *thread = argv->GetThread();
1942 EcmaVM *ecmaVm = thread->GetEcmaVM();
1943 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1944
1945 // Let O be RequireObjectCoercible(this value).
1946 JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1947 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1948 // Let S be ? ToString(O).
1949 JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
1950 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1951
1952 // Let requestedLocales be ? CanonicalizeLocaleList(locales).
1953 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
1954 // Fast path
1955 if (locales->IsUndefined() && EcmaStringAccessor(string).IsUtf8()) {
1956 EcmaString *result = EcmaStringAccessor::TryToUpper(ecmaVm, string);
1957 return JSTaggedValue(result);
1958 }
1959 JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
1960 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1961
1962 // If requestedLocales is not an empty List, then Let requestedLocale be requestedLocales[0].
1963 // Else, Let requestedLocale be DefaultLocale().
1964 JSHandle<EcmaString> requestedLocale = intl::LocaleHelper::DefaultLocale(thread);
1965 if (requestedLocales->GetLength() != 0) {
1966 requestedLocale = JSHandle<EcmaString>(thread, requestedLocales->Get(0));
1967 }
1968
1969 // Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences
1970 // removed.
1971 intl::LocaleHelper::ParsedLocale noExtensionsLocale = intl::LocaleHelper::HandleLocale(requestedLocale);
1972 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1973
1974 // Let availableLocales be a List with language tags that includes the languages for which the Unicode Character
1975 // Database contains language sensitive case mappings. Implementations may add additional language tags
1976 // if they support case mapping for additional locales.
1977 std::vector<std::string> availableLocales = intl::LocaleHelper::GetAvailableLocales(thread, nullptr, nullptr);
1978 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1979
1980 // Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1981 std::string locale = intl::LocaleHelper::BestAvailableLocale(availableLocales, noExtensionsLocale.base);
1982 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1983
1984 // If locale is undefined, let locale be "und".
1985 if (locale.empty()) {
1986 locale = "und";
1987 }
1988
1989 // Let uString be a List containing in order the code points of S as defined in ES2020, 6.1.4,
1990 // starting at the first element of S.
1991 // Transform those elements in uString to the to the Unicode Default Case Conversion algorithm
1992 icu::Locale icuLocale = icu::Locale::createFromName(locale.c_str());
1993 EcmaString *result = EcmaStringAccessor::ToLocaleUpper(ecmaVm, string, icuLocale);
1994 return JSTaggedValue(result);
1995 }
1996
1997 // 21.1.3.22
ToLowerCase(EcmaRuntimeCallInfo * argv)1998 JSTaggedValue BuiltinsString::ToLowerCase(EcmaRuntimeCallInfo *argv)
1999 {
2000 ASSERT(argv);
2001 BUILTINS_API_TRACE(argv->GetThread(), String, ToLowerCase);
2002 JSThread *thread = argv->GetThread();
2003 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2004 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2005 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2006 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2007 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2008 EcmaString *result = EcmaStringAccessor::ToLower(thread->GetEcmaVM(), thisHandle);
2009 return JSTaggedValue(result);
2010 }
2011
2012 // 21.1.3.23
ToString(EcmaRuntimeCallInfo * argv)2013 JSTaggedValue BuiltinsString::ToString(EcmaRuntimeCallInfo *argv)
2014 {
2015 ASSERT(argv);
2016 return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue());
2017 }
2018
2019 // 21.1.3.24
ToUpperCase(EcmaRuntimeCallInfo * argv)2020 JSTaggedValue BuiltinsString::ToUpperCase(EcmaRuntimeCallInfo *argv)
2021 {
2022 ASSERT(argv);
2023 BUILTINS_API_TRACE(argv->GetThread(), String, ToUpperCase);
2024 JSThread *thread = argv->GetThread();
2025 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2026
2027 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2028 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2029 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2030 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2031 EcmaString *result = EcmaStringAccessor::ToUpper(thread->GetEcmaVM(), thisHandle);
2032 return JSTaggedValue(result);
2033 }
2034
2035 // 21.1.3.25
Trim(EcmaRuntimeCallInfo * argv)2036 JSTaggedValue BuiltinsString::Trim(EcmaRuntimeCallInfo *argv)
2037 {
2038 ASSERT(argv);
2039 BUILTINS_API_TRACE(argv->GetThread(), String, Trim);
2040 JSThread *thread = argv->GetThread();
2041 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2042 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2043 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2044 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2045 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2046 EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM);
2047 return JSTaggedValue(res);
2048 }
2049
TrimStart(EcmaRuntimeCallInfo * argv)2050 JSTaggedValue BuiltinsString::TrimStart(EcmaRuntimeCallInfo *argv)
2051 {
2052 ASSERT(argv);
2053 BUILTINS_API_TRACE(argv->GetThread(), String, TrimStart);
2054 JSThread *thread = argv->GetThread();
2055 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2056 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2057 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2058 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2059 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2060 EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_START);
2061 return JSTaggedValue(res);
2062 }
2063
TrimEnd(EcmaRuntimeCallInfo * argv)2064 JSTaggedValue BuiltinsString::TrimEnd(EcmaRuntimeCallInfo *argv)
2065 {
2066 ASSERT(argv);
2067 BUILTINS_API_TRACE(argv->GetThread(), String, TrimEnd);
2068 JSThread *thread = argv->GetThread();
2069 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2070 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2071 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2072 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2073 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2074 EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_END);
2075 return JSTaggedValue(res);
2076 }
2077
TrimLeft(EcmaRuntimeCallInfo * argv)2078 JSTaggedValue BuiltinsString::TrimLeft(EcmaRuntimeCallInfo *argv)
2079 {
2080 ASSERT(argv);
2081 BUILTINS_API_TRACE(argv->GetThread(), String, TrimLeft);
2082 JSThread *thread = argv->GetThread();
2083 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2084 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2085 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2086 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2087 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2088 EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_START);
2089 return JSTaggedValue(res);
2090 }
2091
TrimRight(EcmaRuntimeCallInfo * argv)2092 JSTaggedValue BuiltinsString::TrimRight(EcmaRuntimeCallInfo *argv)
2093 {
2094 ASSERT(argv);
2095 BUILTINS_API_TRACE(argv->GetThread(), String, TrimRight);
2096 JSThread *thread = argv->GetThread();
2097 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2098 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2099 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2100 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2101 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2102 EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_END);
2103 return JSTaggedValue(res);
2104 }
2105
2106 // 21.1.3.26
ValueOf(EcmaRuntimeCallInfo * argv)2107 JSTaggedValue BuiltinsString::ValueOf(EcmaRuntimeCallInfo *argv)
2108 {
2109 ASSERT(argv);
2110 return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue());
2111 }
2112
2113 // 21.1.3.27
GetStringIterator(EcmaRuntimeCallInfo * argv)2114 JSTaggedValue BuiltinsString::GetStringIterator(EcmaRuntimeCallInfo *argv)
2115 {
2116 ASSERT(argv);
2117 BUILTINS_API_TRACE(argv->GetThread(), String, GetStringIterator);
2118 JSThread *thread = argv->GetThread();
2119 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2120 // 1. Let O be RequireObjectCoercible(this value).
2121 JSHandle<JSTaggedValue> current(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2122 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2123 // Let S be ToString(O).
2124
2125 JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, current);
2126 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread());
2127 // Return CreateStringIterator(S).
2128 return JSStringIterator::CreateStringIterator(thread, string).GetTaggedValue();
2129 }
2130
2131 // B.2.3.1
SubStr(EcmaRuntimeCallInfo * argv)2132 JSTaggedValue BuiltinsString::SubStr(EcmaRuntimeCallInfo *argv)
2133 {
2134 ASSERT(argv);
2135 BUILTINS_API_TRACE(argv->GetThread(), String, SubStr);
2136 JSThread *thread = argv->GetThread();
2137
2138 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2139
2140 // 1. Let O be RequireObjectCoercible(this value).
2141 // 2. Let S be ToString(O).
2142
2143 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2144 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2145 JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
2146
2147 // 3. ReturnIfAbrupt(S).
2148 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2149 JSHandle<JSTaggedValue> intStart = GetCallArg(argv, 0);
2150 // 4. Let intStart be ToInteger(start).
2151 JSTaggedNumber numStart = JSTaggedValue::ToInteger(thread, intStart);
2152 // 5. ReturnIfAbrupt(intStart).
2153 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2154 int32_t start = base::NumberHelper::DoubleInRangeInt32(numStart.GetNumber());
2155 JSHandle<JSTaggedValue> lengthTag = GetCallArg(argv, 1);
2156 // 6. If length is undefined, let end be +; otherwise let end be ToInteger(length).
2157 int32_t end = 0;
2158 if (lengthTag->IsUndefined()) {
2159 end = INT_MAX;
2160 } else {
2161 JSTaggedNumber lengthNumber = JSTaggedValue::ToInteger(thread, lengthTag);
2162 // 7. ReturnIfAbrupt(end).
2163 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2164 end = base::NumberHelper::DoubleInRangeInt32(lengthNumber.GetNumber());
2165 }
2166 // 8. Let size be the number of code units in S.
2167 int32_t size = static_cast<int32_t>(EcmaStringAccessor(thisString).GetLength());
2168 // 9. If intStart < 0, let intStart be max(size + intStart,0).
2169 if (start < 0) {
2170 start = std::max(size + start, 0);
2171 }
2172 // 10. Let resultLength be min(max(end,0), size – intStart).
2173 int32_t resultLength = std::min(std::max(end, 0), size - start);
2174 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2175 // 11. If resultLength 0, return the empty String "".
2176 if (resultLength <= 0) {
2177 return factory->GetEmptyString().GetTaggedValue();
2178 }
2179 return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisString, start, resultLength));
2180 }
2181
2182 // 22.1.3.1
At(EcmaRuntimeCallInfo * argv)2183 JSTaggedValue BuiltinsString::At(EcmaRuntimeCallInfo *argv)
2184 {
2185 ASSERT(argv);
2186 BUILTINS_API_TRACE(argv->GetThread(), String, At);
2187 JSThread *thread = argv->GetThread();
2188 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2189
2190 // 1. Let O be RequireObjectCoercible(this value).
2191 // 2. Let S be ToString(O).
2192 JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2193 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2194 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2195 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2196
2197 // 3. Let len be the length of S.
2198 int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
2199
2200 // 4. Let relativeIndex be ? ToIntegerOrInfinity(index).
2201 JSHandle<JSTaggedValue> indexTag = BuiltinsString::GetCallArg(argv, 0);
2202 JSTaggedNumber indexVal = JSTaggedValue::ToInteger(thread, indexTag);
2203 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2204 int32_t relativeIndex = base::NumberHelper::DoubleInRangeInt32(indexVal.GetNumber());
2205
2206 // 5. If relativeIndex ≥ 0, then Let k be relativeIndex. 6. Else, Let k be len + relativeIndex.
2207 int32_t k = 0;
2208 if (relativeIndex >= 0) {
2209 k = relativeIndex;
2210 } else {
2211 k = thisLen + relativeIndex;
2212 }
2213 // 7. If k < 0 or k ≥ len, return undefined.
2214 if (k < 0 || k >= thisLen) {
2215 return JSTaggedValue::Undefined();
2216 }
2217 // 8. Return the substring of S from k to k + 1.
2218 return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisHandle, k, 1));
2219 }
2220
GetLength(EcmaRuntimeCallInfo * argv)2221 JSTaggedValue BuiltinsString::GetLength(EcmaRuntimeCallInfo *argv)
2222 {
2223 ASSERT(argv);
2224 JSThread *thread = argv->GetThread();
2225 BUILTINS_API_TRACE(thread, String, GetLength);
2226 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2227 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2228
2229 JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisHandle);
2230 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2231 return GetTaggedInt(EcmaStringAccessor(thisString).GetLength());
2232 }
2233
2234 // 21.1.3
ThisStringValue(JSThread * thread,JSTaggedValue value)2235 JSTaggedValue BuiltinsString::ThisStringValue(JSThread *thread, JSTaggedValue value)
2236 {
2237 BUILTINS_API_TRACE(thread, String, ThisStringValue);
2238 if (value.IsString()) {
2239 return value;
2240 }
2241 if (value.IsECMAObject()) {
2242 auto jshclass = value.GetTaggedObject()->GetClass();
2243 if (jshclass->GetObjectType() == JSType::JS_PRIMITIVE_REF) {
2244 JSTaggedValue primitive = JSPrimitiveRef::Cast(value.GetTaggedObject())->GetValue();
2245 if (primitive.IsString()) {
2246 return primitive;
2247 }
2248 }
2249 }
2250 THROW_TYPE_ERROR_AND_RETURN(thread, "can not convert to String", JSTaggedValue::Exception());
2251 }
2252
Pad(EcmaRuntimeCallInfo * argv,bool isStart)2253 JSTaggedValue BuiltinsString::Pad(EcmaRuntimeCallInfo *argv, bool isStart)
2254 {
2255 JSThread *thread = argv->GetThread();
2256 BUILTINS_API_TRACE(thread, String, Pad);
2257 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2258
2259 JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
2260 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2261 JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2262 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2263 JSHandle<JSTaggedValue> lengthTag = GetCallArg(argv, 0);
2264 JSTaggedNumber number = JSTaggedValue::ToNumber(thread, lengthTag);
2265 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2266 int64_t intMaxLength = base::NumberHelper::DoubleToInt64(number.GetNumber());
2267 int32_t stringLength = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
2268 if (intMaxLength <= stringLength) {
2269 return thisHandle.GetTaggedValue();
2270 }
2271 JSHandle<JSTaggedValue> fillString = GetCallArg(argv, 1);
2272 std::u16string stringBuilder;
2273 if (fillString->IsUndefined()) {
2274 stringBuilder = u" ";
2275 } else {
2276 JSHandle<EcmaString> filler = JSTaggedValue::ToString(thread, fillString);
2277 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2278 stringBuilder = EcmaStringAccessor(filler).ToU16String();
2279 }
2280 if (stringBuilder.size() == 0) {
2281 return thisHandle.GetTaggedValue();
2282 }
2283 std::u16string u16strSearch = EcmaStringAccessor(thisHandle).ToU16String();
2284 int64_t fillLen = intMaxLength - stringLength;
2285 int64_t len = static_cast<int64_t>(stringBuilder.length());
2286 if (static_cast<size_t>(intMaxLength) >= EcmaString::MAX_STRING_LENGTH) {
2287 THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
2288 }
2289 std::u16string fiString;
2290 for (int32_t i = 0; i < fillLen; ++i) {
2291 fiString += stringBuilder[i % len];
2292 }
2293 std::u16string resultString;
2294 if (isStart) {
2295 resultString = fiString + u16strSearch;
2296 } else {
2297 resultString = u16strSearch + fiString;
2298 }
2299 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2300 return factory->NewFromUtf16Literal(reinterpret_cast<const uint16_t *>(resultString.c_str()),
2301 resultString.size()).GetTaggedValue();
2302 }
2303
ConvertDoubleToInt(double d)2304 int32_t BuiltinsString::ConvertDoubleToInt(double d)
2305 {
2306 if (std::isnan(d) || d == -base::POSITIVE_INFINITY) {
2307 return 0;
2308 }
2309 if (d >= static_cast<double>(INT_MAX)) {
2310 return INT_MAX;
2311 }
2312 if (d <= static_cast<double>(INT_MIN)) {
2313 return INT_MIN;
2314 }
2315 return base::NumberHelper::DoubleToInt(d, base::INT32_BITS);
2316 }
2317
StringToList(JSThread * thread,JSHandle<EcmaString> & str)2318 JSTaggedValue BuiltinsString::StringToList(JSThread *thread, JSHandle<EcmaString> &str)
2319 {
2320 JSHandle<StringToListResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringToListResultCache());
2321 JSTaggedValue cacheResult = StringToListResultCache::FindCachedResult(thread, cacheTable, str);
2322 if (cacheResult != JSTaggedValue::Undefined()) {
2323 JSHandle<JSTaggedValue> resultArray(JSArray::CreateArrayFromList(thread,
2324 JSHandle<TaggedArray>(thread, cacheResult)));
2325 return resultArray.GetTaggedValue();
2326 }
2327
2328 JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue();
2329 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2330 JSHandle<JSObject> newArrayHandle(thread, newArray);
2331 JSHandle<EcmaString> iteratedString(str);
2332 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2333 JSHandle<TaggedArray> oldElements(thread, newArrayHandle->GetElements());
2334 uint32_t totalElements = EcmaStringAccessor(iteratedString).GetLength();
2335 JSHandle<TaggedArray> elements = (oldElements->GetLength() < totalElements) ?
2336 factory->ExtendArray(oldElements, totalElements) : oldElements;
2337 uint32_t index = 0;
2338 newArrayHandle->SetElements(thread, elements);
2339 while (index < totalElements) {
2340 uint16_t c = EcmaStringAccessor(iteratedString).Get(index);
2341 JSHandle<EcmaString> newStr = factory->NewFromUtf16Literal(&c, 1);
2342 ElementAccessor::Set(thread, newArrayHandle, index, newStr, true);
2343 index++;
2344 thread->CheckSafepointIfSuspended();
2345 }
2346 JSHandle<JSArray>(newArrayHandle)->SetArrayLength(thread, totalElements);
2347
2348 StringToListResultCache::SetCachedResult(thread, cacheTable, str, elements);
2349
2350 return newArrayHandle.GetTaggedValue();
2351 }
2352
StringToSList(JSThread * thread,JSHandle<EcmaString> & str)2353 JSTaggedValue BuiltinsString::StringToSList(JSThread *thread, JSHandle<EcmaString> &str)
2354 {
2355 JSHandle<StringToListResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringToListResultCache());
2356 JSTaggedValue cacheResult = StringToListResultCache::FindCachedResult(thread, cacheTable, str);
2357 if (cacheResult != JSTaggedValue::Undefined()) {
2358 JSHandle<JSTaggedValue> resultArray(
2359 JSSharedArray::CreateArrayFromList(thread, JSHandle<TaggedArray>(thread, cacheResult)));
2360 return resultArray.GetTaggedValue();
2361 }
2362
2363 JSTaggedValue newSharedArray = JSSharedArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue();
2364 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2365 JSHandle<JSObject> newSharedArrayHandle(thread, newSharedArray);
2366 JSHandle<EcmaString> iteratedString(str);
2367 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2368 JSHandle<TaggedArray> oldElements(thread, newSharedArrayHandle->GetElements());
2369 uint32_t totalElements = EcmaStringAccessor(iteratedString).GetLength();
2370 JSHandle<TaggedArray> elements =
2371 (oldElements->GetLength() < totalElements)
2372 ? factory->ExtendArray(oldElements, totalElements, JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE)
2373 : oldElements;
2374 uint32_t index = 0;
2375 newSharedArrayHandle->SetElements(thread, elements);
2376 while (index < totalElements) {
2377 uint16_t c = EcmaStringAccessor(iteratedString).Get(index);
2378 JSHandle<EcmaString> newStr = factory->NewFromUtf16Literal(&c, 1);
2379 ElementAccessor::Set(thread, newSharedArrayHandle, index, newStr, true);
2380 index++;
2381 thread->CheckSafepointIfSuspended();
2382 }
2383 JSHandle<JSSharedArray>(newSharedArrayHandle)->SetArrayLength(thread, totalElements);
2384
2385 StringToListResultCache::SetCachedResult(thread, cacheTable, str, elements);
2386 newSharedArrayHandle->GetJSHClass()->SetExtensible(false);
2387 return newSharedArrayHandle.GetTaggedValue();
2388 }
2389
CreateCacheTable(const JSThread * thread)2390 JSTaggedValue StringSplitResultCache::CreateCacheTable(const JSThread *thread)
2391 {
2392 int length = CACHE_SIZE * ENTRY_SIZE;
2393 auto table = static_cast<StringSplitResultCache*>(
2394 *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined()));
2395 return JSTaggedValue(table);
2396 }
2397
FindCachedResult(const JSThread * thread,const JSHandle<StringSplitResultCache> & cache,const JSHandle<EcmaString> & thisString,const JSHandle<EcmaString> & pattern,bool isOneByte)2398 JSTaggedValue StringSplitResultCache::FindCachedResult(const JSThread *thread,
2399 const JSHandle<StringSplitResultCache> &cache, const JSHandle<EcmaString> &thisString,
2400 const JSHandle<EcmaString> &pattern, bool isOneByte)
2401 {
2402 uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2403 uint32_t entry = hash & (CACHE_SIZE - 1);
2404 uint32_t index = entry * ENTRY_SIZE;
2405 JSTaggedValue cacheThis = cache->Get(index + STRING_INDEX);
2406 JSTaggedValue cachePattern = cache->Get(index + PATTERN_INDEX);
2407 if (!cacheThis.IsString() || !cachePattern.IsString()) {
2408 return JSTaggedValue::Undefined();
2409 }
2410 JSHandle<EcmaString> cacheStringHandle(thread, cacheThis);
2411 JSHandle<EcmaString> cachePatternHandle(thread, cachePattern);
2412
2413 if (EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(), thisString, cacheStringHandle) &&
2414 EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(), pattern, cachePatternHandle)) {
2415 JSHandle<TaggedArray> cacheArray(thread, cache->Get(index + ARRAY_INDEX));
2416 uint32_t arrayLength = cacheArray->GetLength();
2417 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2418 JSHandle<TaggedArray> copyArray;
2419 if (isOneByte) {
2420 copyArray = factory->NewAndCopyTaggedArraySkipBarrier(cacheArray, arrayLength, arrayLength);
2421 } else {
2422 copyArray = factory->NewAndCopyTaggedArray(cacheArray, arrayLength, arrayLength);
2423 }
2424 return copyArray.GetTaggedValue();
2425 }
2426 return JSTaggedValue::Undefined();
2427 }
2428
SetCachedResult(const JSThread * thread,const JSHandle<StringSplitResultCache> & cache,const JSHandle<EcmaString> & thisString,const JSHandle<EcmaString> & pattern,const JSHandle<TaggedArray> & resultArray)2429 void StringSplitResultCache::SetCachedResult(const JSThread *thread, const JSHandle<StringSplitResultCache> &cache,
2430 const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &pattern,
2431 const JSHandle<TaggedArray> &resultArray)
2432 {
2433 // clone to cache array
2434 uint32_t arrayLength = resultArray->GetLength();
2435 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2436 JSHandle<TaggedArray> newElements(factory->NewTaggedArray(arrayLength));
2437 for (uint32_t i = 0; i < arrayLength; i++) {
2438 newElements->Set(thread, i, resultArray->Get(i));
2439 }
2440 uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2441 uint32_t entry = hash & (CACHE_SIZE - 1);
2442 uint32_t index = entry * ENTRY_SIZE;
2443
2444 cache->Set(thread, index + STRING_INDEX, thisString);
2445 cache->Set(thread, index + PATTERN_INDEX, pattern);
2446 cache->Set(thread, index + ARRAY_INDEX, newElements);
2447 }
2448
CreateCacheTable(const JSThread * thread)2449 JSTaggedValue StringToListResultCache::CreateCacheTable(const JSThread *thread)
2450 {
2451 int length = CACHE_SIZE * ENTRY_SIZE;
2452 auto table = static_cast<StringToListResultCache*>(
2453 *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined()));
2454 return JSTaggedValue(table);
2455 }
2456
FindCachedResult(const JSThread * thread,const JSHandle<StringToListResultCache> & cache,const JSHandle<EcmaString> & thisString)2457 JSTaggedValue StringToListResultCache::FindCachedResult(const JSThread *thread,
2458 const JSHandle<StringToListResultCache> &cache, const JSHandle<EcmaString> &thisString)
2459 {
2460 if (EcmaStringAccessor(thisString).GetLength() > MAX_STRING_LENGTH) {
2461 return JSTaggedValue::Undefined();
2462 }
2463 uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2464 uint32_t entry = hash & (CACHE_SIZE - 1);
2465 uint32_t index = entry * ENTRY_SIZE;
2466 JSHandle<JSTaggedValue> cacheThis(thread, cache->Get(index + STRING_INDEX));
2467 if (!cacheThis->IsString()) {
2468 return JSTaggedValue::Undefined();
2469 }
2470 JSHandle<EcmaString> cacheStr(cacheThis);
2471 if (EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(), thisString, cacheStr)) {
2472 return cache->Get(index + ARRAY_INDEX);
2473 }
2474 return JSTaggedValue::Undefined();
2475 }
2476
SetCachedResult(const JSThread * thread,const JSHandle<StringToListResultCache> & cache,const JSHandle<EcmaString> & thisString,const JSHandle<TaggedArray> & resultArray)2477 void StringToListResultCache::SetCachedResult(const JSThread *thread, const JSHandle<StringToListResultCache> &cache,
2478 const JSHandle<EcmaString> &thisString, const JSHandle<TaggedArray> &resultArray)
2479 {
2480 if (EcmaStringAccessor(thisString).GetLength() > MAX_STRING_LENGTH ||
2481 EcmaStringAccessor(thisString).GetLength() == 0) {
2482 return;
2483 }
2484 if (!EcmaStringAccessor(thisString).IsInternString()) {
2485 return;
2486 }
2487 // clone to cache array
2488 uint32_t arrayLength = resultArray->GetLength();
2489 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2490 JSMutableHandle<TaggedArray> newElements(thread, JSTaggedValue::Undefined());
2491 if (resultArray.GetTaggedValue().IsInSharedHeap()) {
2492 newElements.Update(factory->NewSCOWTaggedArray(arrayLength));
2493 } else {
2494 newElements.Update(factory->NewCOWTaggedArray(arrayLength));
2495 }
2496 for (uint32_t i = 0; i < arrayLength; i++) {
2497 newElements->Set(thread, i, resultArray->Get(i));
2498 }
2499 uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2500 uint32_t entry = hash & (CACHE_SIZE - 1);
2501 uint32_t index = entry * ENTRY_SIZE;
2502 cache->Set(thread, index + STRING_INDEX, thisString);
2503 cache->Set(thread, index + ARRAY_INDEX, newElements);
2504 }
2505 } // namespace panda::ecmascript::builtins
2506