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