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