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