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