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