1 /*
2 * Copyright (c) 2021-2022 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_array.h"
17
18 #include <cmath>
19
20 #include "ecmascript/base/array_helper.h"
21 #include "ecmascript/base/number_helper.h"
22 #include "ecmascript/base/typed_array_helper-inl.h"
23 #include "ecmascript/base/typed_array_helper.h"
24 #include "ecmascript/ecma_macros.h"
25 #include "ecmascript/ecma_runtime_call_info.h"
26 #include "ecmascript/ecma_string.h"
27 #include "ecmascript/global_env.h"
28 #include "ecmascript/interpreter/interpreter.h"
29 #include "ecmascript/js_array.h"
30 #include "ecmascript/js_array_iterator.h"
31 #include "ecmascript/js_function.h"
32 #include "ecmascript/js_handle.h"
33 #include "ecmascript/js_map_iterator.h"
34 #include "ecmascript/js_stable_array.h"
35 #include "ecmascript/js_tagged_number.h"
36 #include "ecmascript/js_tagged_value.h"
37 #include "ecmascript/object_factory.h"
38 #include "ecmascript/object_fast_operator-inl.h"
39 #include "ecmascript/tagged_array-inl.h"
40 #include "ecmascript/builtins/builtins_string.h"
41
42 namespace panda::ecmascript::builtins {
43 using ArrayHelper = base::ArrayHelper;
44 using TypedArrayHelper = base::TypedArrayHelper;
45
46 // 22.1.1
ArrayConstructor(EcmaRuntimeCallInfo * argv)47 JSTaggedValue BuiltinsArray::ArrayConstructor(EcmaRuntimeCallInfo *argv)
48 {
49 BUILTINS_ENTRY_DEBUG_LOG();
50 ASSERT(argv);
51 BUILTINS_API_TRACE(argv->GetThread(), Array, Constructor);
52 JSThread *thread = argv->GetThread();
53 [[maybe_unused]] EcmaHandleScope handleScope(thread);
54
55 // 1. Let numberOfArgs be the number of arguments passed to this function call.
56 uint32_t argc = argv->GetArgsNumber();
57
58 // 3. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
59 JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
60 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
61 if (newTarget->IsUndefined()) {
62 newTarget = constructor;
63 }
64
65 // 4. Let proto be GetPrototypeFromConstructor(newTarget, "%ArrayPrototype%").
66 // In NewJSObjectByConstructor(), will get prototype.
67 // 5. ReturnIfAbrupt(proto).
68
69 // 22.1.1.1 Array ( )
70 if (argc == 0) {
71 // 6. Return ArrayCreate(0, proto).
72 return JSArray::ArrayCreate(thread, JSTaggedNumber(0), newTarget).GetTaggedValue();
73 }
74
75 // 22.1.1.2 Array(len)
76 if (argc == 1) {
77 // 6. Let array be ArrayCreate(0, proto).
78 JSHandle<JSObject> newArrayHandle(JSArray::ArrayCreate(thread, JSTaggedNumber(0), newTarget));
79 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
80 JSHandle<JSTaggedValue> len = GetCallArg(argv, 0);
81 // 7. If Type(len) is not Number, then
82 // a. Let defineStatus be CreateDataProperty(array, "0", len).
83 // b. Assert: defineStatus is true.
84 // c. Let intLen be 1.
85 // 8. Else,
86 // a. Let intLen be ToUint32(len).
87 // b. If intLen ≠ len, throw a RangeError exception.
88 // 9. Let setStatus be Set(array, "length", intLen, true).
89 // 10. Assert: setStatus is not an abrupt completion.
90 uint32_t newLen = 0;
91 if (!len->IsNumber()) {
92 JSHandle<JSTaggedValue> key0 = thread->GlobalConstants()->GetHandledZeroString();
93 JSObject::CreateDataProperty(thread, newArrayHandle, key0, len);
94 newLen = 1;
95 } else {
96 newLen = JSTaggedValue::ToUint32(thread, len);
97 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
98 if (JSTaggedNumber(len.GetTaggedValue()).GetNumber() != newLen) {
99 THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid array length", JSTaggedValue::Exception());
100 }
101 }
102 JSArray::SetCapacity(thread, newArrayHandle, 0, newLen, true);
103
104 // 11. Return array.
105 return newArrayHandle.GetTaggedValue();
106 }
107
108 // 22.1.1.3 Array(...items )
109 JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(argc), newTarget).GetTaggedValue();
110 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
111 if (!newArray.IsArray(thread)) {
112 THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to create array.", JSTaggedValue::Exception());
113 }
114 JSHandle<JSObject> newArrayHandle(thread, newArray);
115 // 8. Let k be 0.
116 // 9. Let items be a zero-origined List containing the argument items in order.
117 // 10. Repeat, while k < numberOfArgs
118 // a. Let Pk be ToString(k).
119 // b. Let itemK be items[k].
120 // c. Let defineStatus be CreateDataProperty(array, Pk, itemK).
121 // d. Assert: defineStatus is true.
122 // e. Increase k by 1.
123 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
124 JSMutableHandle<JSTaggedValue> itemK(thread, JSTaggedValue::Undefined());
125 for (uint32_t k = 0; k < argc; k++) {
126 key.Update(JSTaggedValue(k));
127 itemK.Update(GetCallArg(argv, k));
128 if (itemK.GetTaggedValue().IsHole()) {
129 itemK.Update(JSTaggedValue::Undefined());
130 }
131 JSObject::CreateDataProperty(thread, newArrayHandle, key, itemK);
132 }
133 // 11. Assert: the value of array’s length property is numberOfArgs.
134 // 12. Return array.
135 JSArray::Cast(*newArrayHandle)->SetArrayLength(thread, argc);
136 return newArrayHandle.GetTaggedValue();
137 }
138
139 // 22.1.2.1 Array.from ( items [ , mapfn [ , thisArg ] ] )
140 // NOLINTNEXTLINE(readability-function-size)
From(EcmaRuntimeCallInfo * argv)141 JSTaggedValue BuiltinsArray::From(EcmaRuntimeCallInfo *argv)
142 {
143 ASSERT(argv);
144 BUILTINS_API_TRACE(argv->GetThread(), Array, From);
145 JSThread *thread = argv->GetThread();
146 [[maybe_unused]] EcmaHandleScope handleScope(thread);
147 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
148 // 1. Let C be the this value.
149 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
150 // 2. If mapfn is undefined, let mapping be false.
151 bool mapping = false;
152 // 3. else
153 // a. If IsCallable(mapfn) is false, throw a TypeError exception.
154 // b. If thisArg was supplied, let T be thisArg; else let T be undefined.
155 // c. Let mapping be true
156 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, INDEX_TWO);
157 JSHandle<JSTaggedValue> mapfn = GetCallArg(argv, 1);
158 if (!mapfn->IsUndefined()) {
159 if (!mapfn->IsCallable()) {
160 THROW_TYPE_ERROR_AND_RETURN(thread, "the mapfn is not callable.", JSTaggedValue::Exception());
161 }
162 mapping = true;
163 }
164 // 4. Let usingIterator be GetMethod(items, @@iterator).
165 JSHandle<JSTaggedValue> items = GetCallArg(argv, 0);
166 if (items->IsNull()) {
167 THROW_TYPE_ERROR_AND_RETURN(thread, "The items is null.", JSTaggedValue::Exception());
168 }
169 if (!mapping && items->IsString()) {
170 JSHandle<EcmaString> strItems(items);
171 return BuiltinsString::StringToList(thread, strItems);
172 }
173 // Fast path for TypedArray
174 if (!mapping && items->IsTypedArray()) {
175 JSHandle<JSTypedArray> arrayItems(items);
176 return BuiltinsArrayBuffer::TypedArrayToList(thread, arrayItems);
177 }
178
179 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
180 JSHandle<JSTaggedValue> iteratorSymbol = env->GetIteratorSymbol();
181 JSHandle<JSTaggedValue> usingIterator = JSObject::GetMethod(thread, items, iteratorSymbol);
182 // 5. ReturnIfAbrupt(usingIterator).
183 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
184 // 6. If usingIterator is not undefined, then
185 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
186 if (!usingIterator->IsUndefined()) {
187 // Fast path for MapIterator
188 if (!mapping && items->IsJSMapIterator()) {
189 return JSMapIterator::MapIteratorToList(thread, items, usingIterator);
190 }
191
192 // a. If IsConstructor(C) is true, then
193 // i. Let A be Construct(C).
194 // b. Else,
195 // i. Let A be ArrayCreate(0).
196 // c. ReturnIfAbrupt(A).
197 JSTaggedValue newArray;
198 if (thisHandle->IsConstructor()) {
199 EcmaRuntimeCallInfo *info =
200 EcmaInterpreter::NewRuntimeCallInfo(thread, thisHandle, undefined, undefined, 0);
201 newArray = JSFunction::Construct(info);
202 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
203 } else {
204 newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue();
205 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
206 }
207 if (!newArray.IsECMAObject()) {
208 THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to construct the array.", JSTaggedValue::Exception());
209 }
210 JSHandle<JSObject> newArrayHandle(thread, newArray);
211 // d. Let iterator be GetIterator(items, usingIterator).
212 JSHandle<JSTaggedValue> iterator = JSIterator::GetIterator(thread, items, usingIterator);
213 // e. ReturnIfAbrupt(iterator).
214 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
215 // f. Let k be 0.
216 int k = 0;
217 // g. Repeat
218 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
219 JSMutableHandle<JSTaggedValue> mapValue(thread, JSTaggedValue::Undefined());
220 while (true) {
221 key.Update(JSTaggedValue(k));
222 // i. Let Pk be ToString(k).
223 // ii. Let next be IteratorStep(iterator).
224 JSHandle<JSTaggedValue> next = JSIterator::IteratorStep(thread, iterator);
225 // iii. ReturnIfAbrupt(next).
226 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
227 // iv. If next is false, then
228 // 1. Let setStatus be Set(A, "length", k, true).
229 // 2. ReturnIfAbrupt(setStatus).
230 // 3. Return A.
231 if (next->IsFalse()) {
232 JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, key, true);
233 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
234 return newArrayHandle.GetTaggedValue();
235 }
236 // v. Let nextValue be IteratorValue(next).
237 JSHandle<JSTaggedValue> nextValue = JSIterator::IteratorValue(thread, next);
238 // vi. ReturnIfAbrupt(nextValue).
239 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
240 // vii. If mapping is true, then
241 // 1. Let mappedValue be Call(mapfn, T, «nextValue, k»).
242 // 2. If mappedValue is an abrupt completion, return IteratorClose(iterator, mappedValue).
243 // 3. Let mappedValue be mappedValue.[[value]].
244 // viii. Else, let mappedValue be nextValue.
245 if (mapping) {
246 const uint32_t argsLength = 2; // 2: «nextValue, k»
247 EcmaRuntimeCallInfo *info =
248 EcmaInterpreter::NewRuntimeCallInfo(thread, mapfn, thisArgHandle, undefined, argsLength);
249 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
250 info->SetCallArg(nextValue.GetTaggedValue(), key.GetTaggedValue());
251 JSTaggedValue callResult = JSFunction::Call(info);
252 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
253 JSIterator::IteratorClose(thread, iterator, mapValue).GetTaggedValue());
254 mapValue.Update(callResult);
255 } else {
256 mapValue.Update(nextValue.GetTaggedValue());
257 }
258 // ix. Let defineStatus be CreateDataPropertyOrThrow(A, Pk, mappedValue).
259 // x. If defineStatus is an abrupt completion, return IteratorClose(iterator, defineStatus).
260 // xi. Increase k by 1.
261 JSHandle<JSTaggedValue> defineStatus(
262 thread, JSTaggedValue(JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, key, mapValue)));
263 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
264 JSIterator::IteratorClose(thread, iterator, defineStatus).GetTaggedValue());
265 k++;
266 }
267 }
268 // 7. Assert: items is not an Iterable so assume it is an array-like object.
269 // 8. Let arrayLike be ToObject(items).
270 JSHandle<JSObject> arrayLikeObj = JSTaggedValue::ToObject(thread, items);
271 // 9. ReturnIfAbrupt(arrayLike).
272 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
273 JSHandle<JSTaggedValue> arrayLike(arrayLikeObj);
274 // 10. Let len be ToLength(Get(arrayLike, "length")).
275 int64_t len = ArrayHelper::GetArrayLength(thread, arrayLike);
276 // 11. ReturnIfAbrupt(len).
277 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
278 // 12. If IsConstructor(C) is true, then
279 // a. Let A be Construct(C, «len»).
280 // 13. Else,
281 // a. Let A be ArrayCreate(len).
282 // 14. ReturnIfAbrupt(A).
283 JSTaggedValue newArray;
284 if (thisHandle->IsConstructor()) {
285 EcmaRuntimeCallInfo *info =
286 EcmaInterpreter::NewRuntimeCallInfo(thread, thisHandle, undefined, undefined, 1);
287 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
288 info->SetCallArg(JSTaggedValue(len));
289 newArray = JSFunction::Construct(info);
290 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
291 } else {
292 newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast<double>(len))).GetTaggedValue();
293 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
294 }
295 if (!newArray.IsECMAObject()) {
296 THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to construct the array.", JSTaggedValue::Exception());
297 }
298 JSHandle<JSObject> newArrayHandle(thread, newArray);
299 // 15. Let k be 0.
300 // 16. Repeat, while k < len
301 // a. Let Pk be ToString(k).
302 // b. Let kValue be Get(arrayLike, Pk).
303 // d. If mapping is true, then
304 // i. Let mappedValue be Call(mapfn, T, «kValue, k»).
305 // e. Else, let mappedValue be kValue.
306 // f. Let defineStatus be CreateDataPropertyOrThrow(A, Pk, mappedValue).
307 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
308 JSMutableHandle<JSTaggedValue> mapValue(thread, JSTaggedValue::Undefined());
309 int64_t k = 0;
310 while (k < len) {
311 JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, arrayLike, k);
312 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
313 if (mapping) {
314 key.Update(JSTaggedValue(k));
315 const uint32_t argsLength = 2; // 2: «kValue, k»
316 EcmaRuntimeCallInfo *info =
317 EcmaInterpreter::NewRuntimeCallInfo(thread, mapfn, thisArgHandle, undefined, argsLength);
318 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
319 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue());
320 JSTaggedValue callResult = JSFunction::Call(info);
321 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
322 mapValue.Update(callResult);
323 } else {
324 mapValue.Update(kValue.GetTaggedValue());
325 }
326 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, mapValue);
327 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
328 k++;
329 }
330 // 17. Let setStatus be Set(A, "length", len, true).
331 JSHandle<JSTaggedValue> lenHandle(thread, JSTaggedValue(len));
332 JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, lenHandle, true);
333 // 18. ReturnIfAbrupt(setStatus).
334 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
335 // 19. Return A.
336 return newArrayHandle.GetTaggedValue();
337 }
338
339 // 22.1.2.2 Array.isArray ( arg )
IsArray(EcmaRuntimeCallInfo * argv)340 JSTaggedValue BuiltinsArray::IsArray(EcmaRuntimeCallInfo *argv)
341 {
342 ASSERT(argv);
343 BUILTINS_API_TRACE(argv->GetThread(), Array, IsArray);
344 // 1. Return IsArray(arg).
345 if (GetCallArg(argv, 0)->IsArray(argv->GetThread())) {
346 return GetTaggedBoolean(true);
347 }
348 return GetTaggedBoolean(false);
349 }
350
351 // 22.1.2.3 Array.of ( ...items )
Of(EcmaRuntimeCallInfo * argv)352 JSTaggedValue BuiltinsArray::Of(EcmaRuntimeCallInfo *argv)
353 {
354 ASSERT(argv);
355 BUILTINS_API_TRACE(argv->GetThread(), Array, Of);
356 JSThread *thread = argv->GetThread();
357 [[maybe_unused]] EcmaHandleScope handleScope(thread);
358 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
359 JSHandle<JSTaggedValue> lengthKey = globalConst->GetHandledLengthString();
360
361 // 1. Let len be the actual number of arguments passed to this function.
362 uint32_t argc = argv->GetArgsNumber();
363
364 // 3. Let C be the this value.
365 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
366 // 4. If IsConstructor(C) is true, then
367 // a. Let A be Construct(C, «len»).
368 // 5. Else,
369 // a. Let A be ArrayCreate(len).
370 // 6. ReturnIfAbrupt(A).
371 JSHandle<JSTaggedValue> newArray;
372 if (thisHandle->IsConstructor()) {
373 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
374 EcmaRuntimeCallInfo *info =
375 EcmaInterpreter::NewRuntimeCallInfo(thread, thisHandle, undefined, undefined, 1);
376 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
377 info->SetCallArg(JSTaggedValue(argc));
378 JSTaggedValue taggedArray = JSFunction::Construct(info);
379 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
380 newArray = JSHandle<JSTaggedValue>(thread, taggedArray);
381 } else {
382 newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(argc));
383 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
384 }
385 if (!newArray->IsECMAObject()) {
386 THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to create Object.", JSTaggedValue::Exception());
387 }
388 JSHandle<JSObject> newArrayHandle(newArray);
389
390 // 7. Let k be 0.
391 // 8. Repeat, while k < len
392 // a. Let kValue be items[k].
393 // b. Let Pk be ToString(k).
394 // c. Let defineStatus be CreateDataPropertyOrThrow(A,Pk, kValue).
395 // d. ReturnIfAbrupt(defineStatus).
396 // e. Increase k by 1.
397 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
398 for (uint32_t k = 0; k < argc; k++) {
399 key.Update(JSTaggedValue(k));
400 JSHandle<JSTaggedValue> kValue = GetCallArg(argv, k);
401 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, key, kValue);
402 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
403 }
404 // 9. Let setStatus be Set(A, "length", len, true).
405 JSHandle<JSTaggedValue> lenHandle(thread, JSTaggedValue(argc));
406 JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, lenHandle, true);
407 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
408 // 11. Return A.
409 return newArrayHandle.GetTaggedValue();
410 }
411
412 // 22.1.2.5 get Array [ @@species ]
Species(EcmaRuntimeCallInfo * argv)413 JSTaggedValue BuiltinsArray::Species(EcmaRuntimeCallInfo *argv)
414 {
415 ASSERT(argv);
416 BUILTINS_API_TRACE(argv->GetThread(), Array, Species);
417 // 1. Return the this value.
418 return GetThis(argv).GetTaggedValue();
419 }
420
421 // 22.1.3.1 Array.prototype.concat ( ...arguments )
Concat(EcmaRuntimeCallInfo * argv)422 JSTaggedValue BuiltinsArray::Concat(EcmaRuntimeCallInfo *argv)
423 {
424 ASSERT(argv);
425 BUILTINS_API_TRACE(argv->GetThread(), Array, Concat);
426 JSThread *thread = argv->GetThread();
427 [[maybe_unused]] EcmaHandleScope handleScope(thread);
428 int argc = static_cast<int>(argv->GetArgsNumber());
429
430 // 1. Let O be ToObject(this value).
431 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
432 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
433 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
434 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
435
436 // 2. Let A be ArraySpeciesCreate(O, 0).
437 uint32_t arrayLen = 0;
438 JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(arrayLen));
439 if (!(newArray.IsECMAObject() || newArray.IsUndefined())) {
440 THROW_TYPE_ERROR_AND_RETURN(thread, "array must be object or undefined.", JSTaggedValue::Exception());
441 }
442 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
443 JSHandle<JSObject> newArrayHandle(thread, newArray);
444
445 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
446
447 // 3. Let n be 0.
448 int64_t n = 0;
449 JSMutableHandle<JSTaggedValue> ele(thread, JSTaggedValue::Undefined());
450 JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
451 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
452 // 4. Prepend O to items.
453 // 5. For each element E of items, do
454 for (int i = -1; i < argc; i++) {
455 if (i < 0) {
456 ele.Update(thisObjHandle.GetTaggedValue());
457 } else {
458 ele.Update(GetCallArg(argv, i));
459 }
460 // a. Let spreadable be ? IsConcatSpreadable(E).
461 bool isSpreadable = ArrayHelper::IsConcatSpreadable(thread, ele);
462 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
463 // b. If spreadable is true, then
464 if (isSpreadable) {
465 // i. Let k be 0.
466 // ii. Let len be ? LengthOfArrayLike(E).
467 // iii. If n + len > 253 - 1, throw a TypeError exception.
468 int64_t len = ArrayHelper::GetArrayLength(thread, ele);
469 int64_t k = 0;
470 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
471 if (n + len > base::MAX_SAFE_INTEGER) {
472 THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
473 }
474
475 if (ele->IsStableJSArray(thread)) {
476 JSStableArray::Concat(thread, newArrayHandle, JSHandle<JSObject>::Cast(ele), k, n);
477 }
478 // iv. Repeat, while k < len,
479 while (k < len) {
480 // 1. Let P be ToString(k).
481 // 2. Let exists be HasProperty(E, P).
482 // 3. If exists is true, then
483 fromKey.Update(JSTaggedValue::ToString(thread, JSTaggedValue(k)));
484 toKey.Update(JSTaggedValue(n));
485 bool exists = JSTaggedValue::HasProperty(thread, ele, fromKey);
486 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
487 if (exists) {
488 // a. Let subElement be Get(E, P).
489 JSHandle<JSTaggedValue> fromValHandle =
490 JSArray::FastGetPropertyByValue(thread, ele, fromKey);
491 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
492 // b. Perform ? CreateDataPropertyOrThrow(A, ! ToString(��(n)), subElement).
493 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValHandle);
494 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
495 }
496 // 4. Set n to n + 1.
497 // 5. Set k to k + 1.
498 n++;
499 k++;
500 }
501 //c. Else
502 } else {
503 // ii. If n ≥ 253 - 1, throw a TypeError exception.
504 if (n >= base::MAX_SAFE_INTEGER) {
505 THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
506 }
507 // iii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(��(n)), E).
508 // iv. Set n to n + 1.
509 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, n, ele);
510 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
511 n++;
512 }
513 }
514 // 6. Perform ? Set(A, "length", ��(n), true).
515 JSHandle<JSTaggedValue> lenHandle(thread, JSTaggedValue(n));
516 JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, lenHandle, true);
517 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
518
519 // 7. Return A.
520 return newArrayHandle.GetTaggedValue();
521 }
522
523 // 22.1.3.3 Array.prototype.copyWithin (target, start [ , end ] )
CopyWithin(EcmaRuntimeCallInfo * argv)524 JSTaggedValue BuiltinsArray::CopyWithin(EcmaRuntimeCallInfo *argv)
525 {
526 ASSERT(argv);
527 BUILTINS_API_TRACE(argv->GetThread(), Array, CopyWithin);
528 JSThread *thread = argv->GetThread();
529 [[maybe_unused]] EcmaHandleScope handleScope(thread);
530
531 // 1. Let O be ToObject(this value).
532 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, GetThis(argv));
533 // 2. ReturnIfAbrupt(O).
534 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
535 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
536
537 // 3. Let len be ToLength(Get(O, "length")).
538 int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
539 // 4. ReturnIfAbrupt(len).
540 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
541
542 int64_t copyTo = 0;
543 int64_t copyFrom = 0;
544 int64_t copyEnd = len;
545
546 // 5. Let relativeTarget be ToInteger(target).
547 JSTaggedNumber targetTemp = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0));
548 // 6. ReturnIfAbrupt(relativeTarget).
549 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
550 double target = targetTemp.GetNumber();
551 // 7. If relativeTarget < 0, let to be max((len + relativeTarget),0); else let to be min(relativeTarget, len).
552 if (target < 0) {
553 copyTo = target + len > 0 ? target + len : 0;
554 } else {
555 copyTo = target < len ? target : len;
556 }
557
558 // 8. Let relativeStart be ToInteger(start).
559 JSTaggedNumber start_t = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 1));
560 // 9. ReturnIfAbrupt(relativeStart).
561 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
562 double start = start_t.GetNumber();
563 // 10. If relativeStart < 0, let from be max((len + relativeStart),0); else let from be min(relativeStart, len).
564 if (start < 0) {
565 copyFrom = start + len > 0 ? start + len : 0;
566 } else {
567 copyFrom = start < len ? start : len;
568 }
569
570 // 11. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end).
571 double end = len;
572 JSHandle<JSTaggedValue> msg3 = GetCallArg(argv, INDEX_TWO);
573 if (!msg3->IsUndefined()) {
574 JSTaggedNumber temp = JSTaggedValue::ToInteger(thread, msg3);
575 // 12. ReturnIfAbrupt(relativeEnd).
576 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
577 end = temp.GetNumber();
578 }
579
580 // 13. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len).
581 if (end < 0) {
582 copyEnd = end + len > 0 ? end + len : 0;
583 } else {
584 copyEnd = end < len ? end : len;
585 }
586
587 // 14. Let count be min(final-from, len-to).
588 int64_t count = (copyEnd - copyFrom < len - copyTo) ? (copyEnd - copyFrom) : (len - copyTo);
589
590 // 15. If from<to and to<from+count
591 // a. Let direction be -1.
592 // b. Let from be from + count -1.
593 // c. Let to be to + count -1.
594 // 16. Else,
595 // a. Let direction = 1.
596 int64_t direction = 1;
597 if (copyFrom < copyTo && copyTo < copyFrom + count) {
598 direction = -1;
599 copyFrom = copyFrom + count - 1;
600 copyTo = copyTo + count - 1;
601 }
602
603 // 17. Repeat, while count > 0
604 // a. Let fromKey be ToString(from).
605 // b. Let toKey be ToString(to).
606 // c. Let fromPresent be HasProperty(O, fromKey).
607 // d. ReturnIfAbrupt(fromPresent).
608 // e. If fromPresent is true, then
609 // i. Let fromVal be Get(O, fromKey).
610 // ii. ReturnIfAbrupt(fromVal).
611 // iii. Let setStatus be Set(O, toKey, fromVal, true).
612 // iv. ReturnIfAbrupt(setStatus).
613 // f. Else fromPresent is false,
614 // i. Let deleteStatus be DeletePropertyOrThrow(O, toKey).
615 // ii. ReturnIfAbrupt(deleteStatus).
616 // g. Let from be from + direction.
617 // h. Let to be to + direction.
618 // i. Let count be count − 1.
619 JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
620 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
621 while (count > 0) {
622 fromKey.Update(JSTaggedValue(copyFrom));
623 toKey.Update(JSTaggedValue(copyTo));
624 bool exists = (thisObjVal->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, fromKey));
625 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
626 if (exists) {
627 JSHandle<JSTaggedValue> fromValHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
628 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
629 JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValHandle);
630 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
631 } else {
632 if (thisObjVal->IsJSProxy()) {
633 toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue());
634 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
635 }
636 JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey);
637 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
638 }
639 copyFrom = copyFrom + direction;
640 copyTo = copyTo + direction;
641 count--;
642 }
643
644 // 18. Return O.
645 return thisObjHandle.GetTaggedValue();
646 }
647
648 // 22.1.3.4 Array.prototype.entries ( )
Entries(EcmaRuntimeCallInfo * argv)649 JSTaggedValue BuiltinsArray::Entries(EcmaRuntimeCallInfo *argv)
650 {
651 ASSERT(argv);
652 BUILTINS_API_TRACE(argv->GetThread(), Array, Entries);
653 JSThread *thread = argv->GetThread();
654 [[maybe_unused]] EcmaHandleScope handleScope(thread);
655 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
656 // 1. Let O be ToObject(this value).
657 // 2. ReturnIfAbrupt(O).
658 JSHandle<JSObject> self = JSTaggedValue::ToObject(thread, GetThis(argv));
659 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
660 // 3. Return CreateArrayIterator(O, "key+value").
661 JSHandle<JSArrayIterator> iter(factory->NewJSArrayIterator(self, IterationKind::KEY_AND_VALUE));
662 return iter.GetTaggedValue();
663 }
664
665 // 22.1.3.5 Array.prototype.every ( callbackfn [ , thisArg] )
Every(EcmaRuntimeCallInfo * argv)666 JSTaggedValue BuiltinsArray::Every(EcmaRuntimeCallInfo *argv)
667 {
668 ASSERT(argv);
669 JSThread *thread = argv->GetThread();
670 BUILTINS_API_TRACE(thread, Array, Every);
671 [[maybe_unused]] EcmaHandleScope handleScope(thread);
672
673 // 1. Let O be ToObject(this value).
674 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
675 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
676 // 2. ReturnIfAbrupt(O).
677 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
678 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
679
680 // 3. Let len be ToLength(Get(O, "length")).
681 uint64_t len = static_cast<uint64_t>(ArrayHelper::GetArrayLength(thread, thisObjVal));
682 // 4. ReturnIfAbrupt(len).
683 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
684
685 // 5. If IsCallable(callbackfn) is false, throw a TypeError exception.
686 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
687 if (!callbackFnHandle->IsCallable()) {
688 THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
689 }
690
691 // 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
692 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
693
694 // 7. Let k be 0.
695 // 8. Repeat, while k < len
696 // a. Let Pk be ToString(k).
697 // b. Let kPresent be HasProperty(O, Pk).
698 // c. ReturnIfAbrupt(kPresent).
699 // d. If kPresent is true, then
700 // i. Let kValue be Get(O, Pk).
701 // ii. ReturnIfAbrupt(kValue).
702 // iii. Let testResult be ToBoolean(Call(callbackfn, T, «kValue, k, O»)).
703 // iv. ReturnIfAbrupt(testResult).
704 // v. If testResult is false, return false.
705 // e. Increase k by 1.
706 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
707 uint32_t k = 0;
708 JSTaggedValue callResult = GetTaggedBoolean(true);
709 if (thisObjVal->IsStableJSArray(thread)) {
710 callResult = JSStableArray::HandleEveryOfStable(thread, thisObjHandle, callbackFnHandle, thisArgHandle, k);
711 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
712 if (!callResult.ToBoolean()) {
713 return GetTaggedBoolean(false);
714 }
715 }
716 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
717 const uint32_t argsLength = 3; // 3: «kValue, k, O»
718 while (k < len) {
719 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k);
720 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
721 if (exists) {
722 JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
723 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
724 key.Update(JSTaggedValue(k));
725 EcmaRuntimeCallInfo *info =
726 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
727 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
728 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
729 callResult = JSFunction::Call(info);
730 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
731 if (!callResult.ToBoolean()) {
732 return GetTaggedBoolean(false);
733 }
734 }
735 k++;
736 }
737
738 // 9. Return true.
739 return GetTaggedBoolean(true);
740 }
741
742 // 22.1.3.6 Array.prototype.fill (value [ , start [ , end ] ] )
Fill(EcmaRuntimeCallInfo * argv)743 JSTaggedValue BuiltinsArray::Fill(EcmaRuntimeCallInfo *argv)
744 {
745 ASSERT(argv);
746 BUILTINS_API_TRACE(argv->GetThread(), Array, Fill);
747 JSThread *thread = argv->GetThread();
748 [[maybe_unused]] EcmaHandleScope handleScope(thread);
749
750 // 1. Let O be ToObject(this value).
751 JSHandle<JSTaggedValue> thisObjVal = GetThis(argv);
752 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisObjVal);
753
754 if (thisObjVal->IsJSArray()) {
755 bool isDictionary = thisObjHandle->GetJSHClass()->IsDictionaryElement();
756 if (isDictionary) {
757 uint32_t length = JSArray::Cast(*thisObjHandle)->GetLength();
758 uint32_t size = thisObjHandle->GetNumberOfElements();
759 if (length - size > JSObject::MAX_GAP) {
760 JSObject::TryOptimizeAsFastElements(thread, thisObjHandle);
761 }
762 }
763 }
764
765 // 2. ReturnIfAbrupt(O).
766 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
767
768 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
769 if (thisObjVal->IsTypedArray()) {
770 ContentType contentType = JSHandle<JSTypedArray>::Cast(thisObjVal)->GetContentType();
771 if (contentType == ContentType::BigInt) {
772 value = JSHandle<JSTaggedValue>(thread, JSTaggedValue::ToBigInt(thread, value));
773 } else {
774 value = JSHandle<JSTaggedValue>(thread, JSTaggedValue::ToNumber(thread, value));
775 }
776 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
777 }
778
779 // 3. Let len be ToLength(Get(O, "length")).
780 int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
781 // 4. ReturnIfAbrupt(len).
782 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
783
784 // 5. Let relativeStart be ToInteger(start).
785 JSHandle<JSTaggedValue> startArg = GetCallArg(argv, 1);
786 JSTaggedNumber argStartTemp = JSTaggedValue::ToInteger(thread, startArg);
787 // 6. ReturnIfAbrupt(relativeStart).
788 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
789 double argStart = argStartTemp.GetNumber();
790 // 7. If relativeStart < 0, let k be max((len + relativeStart),0); else let k be min(relativeStart, len).
791 int64_t start = 0;
792 if (argStart < 0) {
793 double tempStart = argStart + len;
794 start = tempStart > 0 ? tempStart : 0;
795 } else {
796 start = argStart < len ? argStart : len;
797 }
798
799 // 8. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end).
800 double argEnd = len;
801 JSHandle<JSTaggedValue> endArg = GetCallArg(argv, INDEX_TWO);
802 if (!endArg->IsUndefined()) {
803 JSTaggedNumber argEndTemp = JSTaggedValue::ToInteger(thread, endArg);
804 // 9. ReturnIfAbrupt(relativeEnd).
805 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
806 argEnd = argEndTemp.GetNumber();
807 }
808
809 // 10. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len).
810 int64_t end = len;
811 if (argEnd < 0) {
812 double tempEnd = argEnd + len;
813 end = tempEnd > 0 ? tempEnd : 0;
814 } else {
815 end = argEnd < len ? argEnd : len;
816 }
817 // 11. Repeat, while k < final
818 // a. Let Pk be ToString(k).
819 // b. Let setStatus be Set(O, Pk, value, true).
820 // c. ReturnIfAbrupt(setStatus).
821 // d. Increase k by 1.
822
823 if (thisObjVal->IsStableJSArray(thread) && !startArg->IsJSObject() && !endArg->IsJSObject()) {
824 return JSStableArray::Fill(thread, thisObjHandle, value, start, end, len);
825 }
826
827 if (thisObjVal->IsTypedArray()) {
828 bool result = JSTypedArray::FastTypedArrayFill(thread, thisObjVal, value, start, end);
829 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
830 if (result) {
831 return thisObjHandle.GetTaggedValue();
832 }
833 }
834
835 int64_t k = start;
836 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
837 while (k < end) {
838 key.Update(JSTaggedValue(k));
839 JSArray::FastSetPropertyByValue(thread, thisObjVal, key, value);
840 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
841 k++;
842 }
843
844 // 12. Return O.
845 return thisObjHandle.GetTaggedValue();
846 }
847
FilterUnStableJSArray(JSThread * thread,JSHandle<JSTaggedValue> & thisArgHandle,JSHandle<JSTaggedValue> & thisObjVal,int64_t k,int64_t len,uint32_t toIndex,JSHandle<JSObject> newArrayHandle,JSHandle<JSTaggedValue> & callbackFnHandle)848 JSTaggedValue BuiltinsArray::FilterUnStableJSArray(JSThread *thread, JSHandle<JSTaggedValue> &thisArgHandle,
849 JSHandle<JSTaggedValue> &thisObjVal, int64_t k, int64_t len, uint32_t toIndex, JSHandle<JSObject> newArrayHandle,
850 JSHandle<JSTaggedValue> &callbackFnHandle)
851 {
852 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
853 const uint32_t argsLength = 3; // 3: «kValue, k, O»
854 JSTaggedValue callResult = GetTaggedBoolean(true);
855 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
856 JSMutableHandle<JSTaggedValue> toIndexHandle(thread, JSTaggedValue::Undefined());
857 while (k < len) {
858 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k);
859 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
860 if (exists) {
861 JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
862 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
863 key.Update(JSTaggedValue(k));
864 EcmaRuntimeCallInfo *info =
865 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
866 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
867 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
868 callResult = JSFunction::Call(info);
869 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
870 if (callResult.ToBoolean()) {
871 toIndexHandle.Update(JSTaggedValue(toIndex));
872 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toIndexHandle, kValue);
873 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
874 toIndex++;
875 }
876 }
877 k++;
878 }
879 return newArrayHandle.GetTaggedValue();
880 }
881
882 // 22.1.3.7 Array.prototype.filter ( callbackfn [ , thisArg ] )
Filter(EcmaRuntimeCallInfo * argv)883 JSTaggedValue BuiltinsArray::Filter(EcmaRuntimeCallInfo *argv)
884 {
885 ASSERT(argv);
886 JSThread *thread = argv->GetThread();
887 BUILTINS_API_TRACE(thread, Array, Filter);
888 [[maybe_unused]] EcmaHandleScope handleScope(thread);
889
890 // 1. Let O be ToObject(this value).
891 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
892 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
893 // 2. ReturnIfAbrupt(O).
894 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
895 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
896
897 // 3. Let len be ToLength(Get(O, "length")).
898 uint64_t len = static_cast<uint64_t>(ArrayHelper::GetArrayLength(thread, thisObjVal));
899 // 4. ReturnIfAbrupt(len).
900 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
901
902 // 5. If IsCallable(callbackfn) is false, throw a TypeError exception.
903 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
904 if (!callbackFnHandle->IsCallable()) {
905 THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
906 }
907
908 // 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
909 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
910
911 // 7. Let A be ArraySpeciesCreate(O, 0).
912 int32_t arrayLen = 0;
913 JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(arrayLen));
914 // 8. ReturnIfAbrupt(A).
915 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
916 JSHandle<JSObject> newArrayHandle(thread, newArray);
917
918 // 9. Let k be 0.
919 // 10. Let to be 0.
920 // 11. Repeat, while k < len
921 // a. Let Pk be ToString(k).
922 // b. Let kPresent be HasProperty(O, Pk).
923 // c. ReturnIfAbrupt(kPresent).
924 // d. If kPresent is true, then
925 // i. Let kValue be Get(O, Pk).
926 // ii. ReturnIfAbrupt(kValue).
927 // iii. Let selected be ToBoolean(Call(callbackfn, T, «kValue, k, O»)).
928 // iv. ReturnIfAbrupt(selected).
929 // v. If selected is true, then
930 // 1. Let status be CreateDataPropertyOrThrow (A, ToString(to), kValue).
931 // 2. ReturnIfAbrupt(status).
932 // 3. Increase to by 1.
933 // e. Increase k by 1.
934 uint32_t toIndex = 0;
935 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
936 JSMutableHandle<JSTaggedValue> toIndexHandle(thread, JSTaggedValue::Undefined());
937 uint32_t k = 0;
938 if (thisObjVal->IsStableJSArray(thread)) {
939 JSStableArray::Filter(newArrayHandle, thisObjHandle, argv, k, toIndex);
940 }
941 return FilterUnStableJSArray(thread, thisArgHandle, thisObjVal, k, len, toIndex, newArrayHandle, callbackFnHandle);
942 }
943
944 // 22.1.3.8 Array.prototype.find ( predicate [ , thisArg ] )
Find(EcmaRuntimeCallInfo * argv)945 JSTaggedValue BuiltinsArray::Find(EcmaRuntimeCallInfo *argv)
946 {
947 ASSERT(argv);
948 BUILTINS_API_TRACE(argv->GetThread(), Array, Find);
949 JSThread *thread = argv->GetThread();
950 [[maybe_unused]] EcmaHandleScope handleScope(thread);
951
952 // 1. Let O be ToObject(this value).
953 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
954 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
955 // 2. ReturnIfAbrupt(O).
956 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
957 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
958
959 // 3. Let len be ToLength(Get(O, "length")).
960 int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
961 // 4. ReturnIfAbrupt(len).
962 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
963
964 // 5. If IsCallable(predicate) is false, throw a TypeError exception.
965 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
966 if (!callbackFnHandle->IsCallable()) {
967 THROW_TYPE_ERROR_AND_RETURN(thread, "the predicate is not callable.", JSTaggedValue::Exception());
968 }
969
970 // 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
971 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
972
973 // 7. Let k be 0.
974 // 8. Repeat, while k < len
975 // a. Let Pk be ToString(k).
976 // b. Let kValue be Get(O, Pk).
977 // c. ReturnIfAbrupt(kValue).
978 // d. Let testResult be ToBoolean(Call(predicate, T, «kValue, k, O»)).
979 // e. ReturnIfAbrupt(testResult).
980 // f. If testResult is true, return kValue.
981 // g. Increase k by 1.
982 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
983 int64_t k = 0;
984 while (k < len) {
985 JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
986 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
987 key.Update(JSTaggedValue(k));
988 const uint32_t argsLength = 3; // 3: «kValue, k, O»
989 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
990 EcmaRuntimeCallInfo *info =
991 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
992 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
993 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
994 JSTaggedValue callResult = JSFunction::Call(info);
995 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
996 if (callResult.ToBoolean()) {
997 return kValue.GetTaggedValue();
998 }
999 k++;
1000 }
1001
1002 // 9. Return undefined.
1003 return JSTaggedValue::Undefined();
1004 }
1005
1006 // 22.1.3.9 Array.prototype.findIndex ( predicate [ , thisArg ] )
FindIndex(EcmaRuntimeCallInfo * argv)1007 JSTaggedValue BuiltinsArray::FindIndex(EcmaRuntimeCallInfo *argv)
1008 {
1009 ASSERT(argv);
1010 BUILTINS_API_TRACE(argv->GetThread(), Array, FindIndex);
1011 JSThread *thread = argv->GetThread();
1012 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1013
1014 // 1. Let O be ToObject(this value).
1015 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
1016 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
1017 // 2. ReturnIfAbrupt(O).
1018 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1019 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1020
1021 // 3. Let len be ToLength(Get(O, "length")).
1022 uint64_t len = static_cast<uint64_t>(ArrayHelper::GetLength(thread, thisObjVal));
1023 // 4. ReturnIfAbrupt(len).
1024 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1025
1026 // 5. If IsCallable(predicate) is false, throw a TypeError exception.
1027 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
1028 if (!callbackFnHandle->IsCallable()) {
1029 THROW_TYPE_ERROR_AND_RETURN(thread, "the predicate is not callable.", JSTaggedValue::Exception());
1030 }
1031
1032 // 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
1033 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
1034
1035 // 7. Let k be 0.
1036 // 8. Repeat, while k < len
1037 // a. Let Pk be ToString(k).
1038 // b. Let kValue be Get(O, Pk).
1039 // c. ReturnIfAbrupt(kValue).
1040 // d. Let testResult be ToBoolean(Call(predicate, T, «kValue, k, O»)).
1041 // e. ReturnIfAbrupt(testResult).
1042 // f. If testResult is true, return k.
1043 // g. Increase k by 1.
1044 uint32_t k = 0;
1045 JSTaggedValue callResult = GetTaggedBoolean(true);
1046 if (thisObjVal->IsStableJSArray(thread)) {
1047 callResult = JSStableArray::HandleFindIndexOfStable(thread, thisObjHandle, callbackFnHandle, thisArgHandle, k);
1048 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1049 if (callResult.ToBoolean()) {
1050 return GetTaggedDouble(k);
1051 }
1052 }
1053 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1054 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1055 const uint32_t argsLength = 3; // 3: «kValue, k, O»
1056 while (k < len) {
1057 JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
1058 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1059 key.Update(JSTaggedValue(k));
1060 EcmaRuntimeCallInfo *info =
1061 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1062 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1063 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1064 callResult = JSFunction::Call(info);
1065 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1066 if (callResult.ToBoolean()) {
1067 return GetTaggedDouble(k);
1068 }
1069 k++;
1070 }
1071
1072 // 9. Return -1.
1073 return GetTaggedDouble(-1);
1074 }
1075
1076 // 22.1.3.10 Array.prototype.forEach ( callbackfn [ , thisArg ] )
ForEach(EcmaRuntimeCallInfo * argv)1077 JSTaggedValue BuiltinsArray::ForEach(EcmaRuntimeCallInfo *argv)
1078 {
1079 ASSERT(argv);
1080 JSThread *thread = argv->GetThread();
1081 BUILTINS_API_TRACE(thread, Array, ForEach);
1082 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1083
1084 // 1. Let O be ToObject(this value).
1085 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
1086 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
1087 // 2. ReturnIfAbrupt(O).
1088 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1089 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1090
1091 // 3. Let len be ToLength(Get(O, "length")).
1092 uint64_t len = static_cast<uint64_t>(ArrayHelper::GetArrayLength(thread, thisObjVal));
1093 // 4. ReturnIfAbrupt(len).
1094 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1095
1096 // 5. If IsCallable(callbackfn) is false, throw a TypeError exception.
1097 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
1098 if (!callbackFnHandle->IsCallable()) {
1099 THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
1100 }
1101
1102 // 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
1103 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
1104
1105 // 7. Let k be 0.
1106 // 8. Repeat, while k < len
1107 // a. Let Pk be ToString(k).
1108 // b. Let kPresent be HasProperty(O, Pk).
1109 // c. ReturnIfAbrupt(kPresent).
1110 // d. If kPresent is true, then
1111 // i. Let kValue be Get(O, Pk).
1112 // ii. ReturnIfAbrupt(kValue).
1113 // iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»).
1114 // iv. ReturnIfAbrupt(funcResult).
1115 // e. Increase k by 1.
1116 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1117 uint32_t k = 0;
1118 if (thisObjVal->IsStableJSArray(thread)) {
1119 JSStableArray::HandleforEachOfStable(thread, thisObjHandle, callbackFnHandle, thisArgHandle, len, k);
1120 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1121 }
1122 const uint32_t argsLength = 3; // 3: «kValue, k, O»
1123 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1124 while (k < len) {
1125 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k);
1126 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1127 if (exists) {
1128 JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
1129 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1130 key.Update(JSTaggedValue(k));
1131 EcmaRuntimeCallInfo *info =
1132 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1133 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1134 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1135 JSTaggedValue funcResult = JSFunction::Call(info);
1136 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
1137 }
1138 k++;
1139 }
1140
1141 // 9. Return undefined.
1142 return JSTaggedValue::Undefined();
1143 }
1144
IndexOfStable(EcmaRuntimeCallInfo * argv,JSThread * thread,const JSHandle<JSTaggedValue> & thisHandle)1145 JSTaggedValue BuiltinsArray::IndexOfStable(
1146 EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle)
1147 {
1148 int64_t length = JSHandle<JSArray>::Cast(thisHandle)->GetArrayLength();
1149 if (length == 0) {
1150 return JSTaggedValue(-1);
1151 }
1152 int64_t fromIndex = 0;
1153 uint32_t argc = argv->GetArgsNumber();
1154 // 2: [target, fromIndex]. Note that fromIndex is missing in most usage cases.
1155 if (UNLIKELY(argc >= 2)) {
1156 JSHandle<JSTaggedValue> fromIndexHandle = argv->GetCallArg(1);
1157 fromIndex = ArrayHelper::GetStartIndex(thread, fromIndexHandle, length);
1158 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1159 // Slow path when fromIndex is obtained from an ECMAObject
1160 // due to potential side effects in its 'toString' and 'valueOf' methods which modify the array object.
1161 if (UNLIKELY(fromIndexHandle->IsECMAObject())) {
1162 return IndexOfSlowPath(argv, thread, thisHandle, length, fromIndex);
1163 }
1164 }
1165 if (fromIndex >= length) {
1166 return JSTaggedValue(-1);
1167 }
1168 JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
1169 return JSStableArray::IndexOf(
1170 thread, thisHandle, target, static_cast<uint32_t>(fromIndex), static_cast<uint32_t>(length));
1171 }
1172
IndexOfSlowPath(EcmaRuntimeCallInfo * argv,JSThread * thread,const JSHandle<JSTaggedValue> & thisHandle)1173 JSTaggedValue BuiltinsArray::IndexOfSlowPath(
1174 EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle)
1175 {
1176 // 1. Let O be ToObject(this value).
1177 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
1178 // 2. ReturnIfAbrupt(O).
1179 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1180 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1181 // 3. Let len be ToLength(Get(O, "length")).
1182 int64_t length = ArrayHelper::GetLength(thread, thisObjVal);
1183 // 4. ReturnIfAbrupt(len).
1184 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1185 // 5. If len is 0, return −1.
1186 if (length == 0) {
1187 return JSTaggedValue(-1);
1188 }
1189 // 6. If argument fromIndex was passed let n be ToInteger(fromIndex); else let n be 0.
1190 int64_t fromIndex = ArrayHelper::GetStartIndexFromArgs(thread, argv, 1, length);
1191 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1192 return IndexOfSlowPath(argv, thread, thisObjVal, length, fromIndex);
1193 }
1194
IndexOfSlowPath(EcmaRuntimeCallInfo * argv,JSThread * thread,const JSHandle<JSTaggedValue> & thisObjVal,int64_t length,int64_t fromIndex)1195 JSTaggedValue BuiltinsArray::IndexOfSlowPath(
1196 EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle<JSTaggedValue> &thisObjVal,
1197 int64_t length, int64_t fromIndex)
1198 {
1199 if (fromIndex >= length) {
1200 return JSTaggedValue(-1);
1201 }
1202 JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
1203 JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
1204 // 11. Repeat, while k < len
1205 for (int64_t curIndex = fromIndex; curIndex < length; ++curIndex) {
1206 keyHandle.Update(JSTaggedValue(curIndex));
1207 bool found = ArrayHelper::ElementIsStrictEqualTo(thread, thisObjVal, keyHandle, target);
1208 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1209 if (UNLIKELY(found)) {
1210 return JSTaggedValue(curIndex);
1211 }
1212 }
1213 // 12. Return -1.
1214 return JSTaggedValue(-1);
1215 }
1216
1217 // 22.1.3.11 Array.prototype.indexOf ( searchElement [ , fromIndex ] )
IndexOf(EcmaRuntimeCallInfo * argv)1218 JSTaggedValue BuiltinsArray::IndexOf(EcmaRuntimeCallInfo *argv)
1219 {
1220 ASSERT(argv);
1221 JSThread *thread = argv->GetThread();
1222 BUILTINS_API_TRACE(thread, Array, IndexOf);
1223 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1224
1225 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
1226 if (thisHandle->IsStableJSArray(thread)) {
1227 return IndexOfStable(argv, thread, thisHandle);
1228 }
1229 return IndexOfSlowPath(argv, thread, thisHandle);
1230 }
1231
1232 // 22.1.3.12 Array.prototype.join (separator)
Join(EcmaRuntimeCallInfo * argv)1233 JSTaggedValue BuiltinsArray::Join(EcmaRuntimeCallInfo *argv)
1234 {
1235 ASSERT(argv);
1236 BUILTINS_API_TRACE(argv->GetThread(), Array, Join);
1237 JSThread *thread = argv->GetThread();
1238 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1239 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
1240 auto factory = thread->GetEcmaVM()->GetFactory();
1241 auto context = thread->GetCurrentEcmaContext();
1242 bool noCircular = context->JoinStackPushFastPath(thisHandle);
1243 if (!noCircular) {
1244 return factory->GetEmptyString().GetTaggedValue();
1245 }
1246 if (thisHandle->IsStableJSArray(thread)) {
1247 return JSStableArray::Join(JSHandle<JSArray>::Cast(thisHandle), argv);
1248 }
1249
1250 // 1. Let O be ToObject(this value).
1251 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
1252 // 2. ReturnIfAbrupt(O).
1253 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
1254 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1255
1256 // 3. Let len be ToLength(Get(O, "length")).
1257 int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
1258 if (len > UINT32_MAX) {
1259 THROW_TYPE_ERROR_AND_RETURN(thread, "Invalid array length", JSTaggedValue::Exception());
1260 }
1261 // 4. ReturnIfAbrupt(len).
1262 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
1263
1264 // 5. If separator is undefined, let separator be the single-element String ",".
1265 // 6. Let sep be ToString(separator).
1266 JSHandle<JSTaggedValue> sepHandle;
1267 if ((GetCallArg(argv, 0)->IsUndefined())) {
1268 sepHandle = thread->GlobalConstants()->GetHandledCommaString();
1269 } else {
1270 sepHandle = GetCallArg(argv, 0);
1271 }
1272
1273 JSHandle<EcmaString> sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
1274 uint32_t allocateLength = 0;
1275 uint32_t sepLength = EcmaStringAccessor(sepStringHandle).GetLength();
1276
1277 if (len > 0) {
1278 allocateLength = sepLength * (len - 1) + len;
1279 }
1280 if (allocateLength > EcmaString::MAX_STRING_LENGTH) {
1281 context->JoinStackPopFastPath(thisHandle);
1282 THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
1283 }
1284 // 7. ReturnIfAbrupt(sep).
1285 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
1286 std::u16string sepStr = EcmaStringAccessor(sepStringHandle).ToU16String();
1287
1288 // 8. If len is zero, return the empty String.
1289 if (len == 0) {
1290 context->JoinStackPopFastPath(thisHandle);
1291 return GetTaggedString(thread, "");
1292 }
1293
1294 // 9. Let element0 be Get(O, "0").
1295 // 10. If element0 is undefined or null, let R be the empty String; otherwise, let R be ToString(element0).
1296 // 11. ReturnIfAbrupt(R).
1297 // 12. Let k be 1.
1298 // 13. Repeat, while k < len
1299 // a. Let S be the String value produced by concatenating R and sep.
1300 // b. Let element be Get(O, ToString(k)).
1301 // c. If element is undefined or null, let next be the empty String; otherwise, let next be ToString(element).
1302 // d. ReturnIfAbrupt(next).
1303 // e. Let R be a String value produced by concatenating S and next.
1304 // f. Increase k by 1.
1305 std::u16string concatStr;
1306 for (int64_t k = 0; k < len; k++) {
1307 std::u16string nextStr;
1308 JSHandle<JSTaggedValue> element = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
1309 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
1310 if (!element->IsUndefined() && !element->IsNull()) {
1311 JSHandle<EcmaString> nextStringHandle = JSTaggedValue::ToString(thread, element);
1312 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
1313 nextStr = EcmaStringAccessor(nextStringHandle).ToU16String();
1314 }
1315 if (k > 0) {
1316 concatStr.append(sepStr);
1317 }
1318 concatStr.append(nextStr);
1319 }
1320
1321 // 14. Return R.
1322 const char16_t *constChar16tData = concatStr.data();
1323 auto *char16tData = const_cast<char16_t *>(constChar16tData);
1324 auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
1325 uint32_t u16strSize = concatStr.size();
1326 context->JoinStackPopFastPath(thisHandle);
1327 return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue();
1328 }
1329
1330 // 22.1.3.13 Array.prototype.keys ( )
Keys(EcmaRuntimeCallInfo * argv)1331 JSTaggedValue BuiltinsArray::Keys(EcmaRuntimeCallInfo *argv)
1332 {
1333 ASSERT(argv);
1334 BUILTINS_API_TRACE(argv->GetThread(), Array, Keys);
1335 JSThread *thread = argv->GetThread();
1336 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1337 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1338 // 1. Let O be ToObject(this value).
1339 // 2. ReturnIfAbrupt(O).
1340 JSHandle<JSObject> self = JSTaggedValue::ToObject(thread, GetThis(argv));
1341 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1342 // 3. Return CreateArrayIterator(O, "key").
1343 JSHandle<JSArrayIterator> iter(factory->NewJSArrayIterator(self, IterationKind::KEY));
1344 return iter.GetTaggedValue();
1345 }
1346
LastIndexOfStable(EcmaRuntimeCallInfo * argv,JSThread * thread,const JSHandle<JSTaggedValue> & thisHandle)1347 JSTaggedValue BuiltinsArray::LastIndexOfStable(
1348 EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle)
1349 {
1350 int64_t length = JSHandle<JSArray>::Cast(thisHandle)->GetArrayLength();
1351 if (length == 0) {
1352 return JSTaggedValue(-1);
1353 }
1354 int64_t fromIndex = length - 1;
1355 uint32_t argc = argv->GetArgsNumber();
1356 // 2: [target, fromIndex]. Note that fromIndex is missing in most usage cases.
1357 if (UNLIKELY(argc >= 2)) {
1358 JSHandle<JSTaggedValue> fromIndexHandle = argv->GetCallArg(1);
1359 fromIndex = ArrayHelper::GetLastStartIndex(thread, fromIndexHandle, length);
1360 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1361 // Slow path when fromIndex is obtained from an ECMAObject
1362 // due to potential side effects in its 'toString' and 'valueOf' methods which modify the array object.
1363 if (UNLIKELY(fromIndexHandle->IsECMAObject())) {
1364 return LastIndexOfSlowPath(argv, thread, thisHandle, fromIndex);
1365 }
1366 }
1367 if (fromIndex < 0) {
1368 return JSTaggedValue(-1);
1369 }
1370 JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
1371 return JSStableArray::LastIndexOf(
1372 thread, thisHandle, target, static_cast<uint32_t>(fromIndex), static_cast<uint32_t>(length));
1373 }
1374
LastIndexOfSlowPath(EcmaRuntimeCallInfo * argv,JSThread * thread,const JSHandle<JSTaggedValue> & thisHandle)1375 JSTaggedValue BuiltinsArray::LastIndexOfSlowPath(
1376 EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle)
1377 {
1378 // 1. Let O be ToObject(this value).
1379 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
1380 // 2. ReturnIfAbrupt(O).
1381 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1382 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1383 // 3. Let len be ToLength(Get(O, "length")).
1384 int64_t length = ArrayHelper::GetLength(thread, thisObjVal);
1385 // 4. ReturnIfAbrupt(len).
1386 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1387 // 5. If len is 0, return −1.
1388 if (length == 0) {
1389 return JSTaggedValue(-1);
1390 }
1391 // 6. If argument fromIndex was passed let n be ToInteger(fromIndex); else let n be 0.
1392 int64_t fromIndex = ArrayHelper::GetLastStartIndexFromArgs(thread, argv, 1, length);
1393 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1394 return LastIndexOfSlowPath(argv, thread, thisObjVal, fromIndex);
1395 }
1396
LastIndexOfSlowPath(EcmaRuntimeCallInfo * argv,JSThread * thread,const JSHandle<JSTaggedValue> & thisObjVal,int64_t fromIndex)1397 JSTaggedValue BuiltinsArray::LastIndexOfSlowPath(
1398 EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle<JSTaggedValue> &thisObjVal, int64_t fromIndex)
1399 {
1400 if (fromIndex < 0) {
1401 return JSTaggedValue(-1);
1402 }
1403 JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
1404 JSHandle<JSTaggedValue> target = base::BuiltinsBase::GetCallArg(argv, 0);
1405 // 11. Repeat, while k < len
1406 for (int64_t curIndex = fromIndex; curIndex >= 0; --curIndex) {
1407 keyHandle.Update(JSTaggedValue(curIndex));
1408 bool found = ArrayHelper::ElementIsStrictEqualTo(thread, thisObjVal, keyHandle, target);
1409 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1410 if (UNLIKELY(found)) {
1411 return JSTaggedValue(curIndex);
1412 }
1413 }
1414 // 12. Return -1.
1415 return JSTaggedValue(-1);
1416 }
1417
1418 // 22.1.3.14 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
LastIndexOf(EcmaRuntimeCallInfo * argv)1419 JSTaggedValue BuiltinsArray::LastIndexOf(EcmaRuntimeCallInfo *argv)
1420 {
1421 ASSERT(argv);
1422 BUILTINS_API_TRACE(argv->GetThread(), Array, IndexOf);
1423 JSThread *thread = argv->GetThread();
1424 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1425
1426 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
1427 if (thisHandle->IsStableJSArray(thread)) {
1428 return LastIndexOfStable(argv, thread, thisHandle);
1429 }
1430 return LastIndexOfSlowPath(argv, thread, thisHandle);
1431 }
1432
1433 // 22.1.3.15 Array.prototype.map ( callbackfn [ , thisArg ] )
Map(EcmaRuntimeCallInfo * argv)1434 JSTaggedValue BuiltinsArray::Map(EcmaRuntimeCallInfo *argv)
1435 {
1436 ASSERT(argv);
1437 BUILTINS_API_TRACE(argv->GetThread(), Array, Map);
1438 JSThread *thread = argv->GetThread();
1439 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1440
1441 // 1. Let O be ToObject(this value).
1442 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
1443 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
1444 // 2. ReturnIfAbrupt(O).
1445 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1446 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1447
1448 // 3. Let len be ToLength(Get(O, "length")).
1449 int64_t rawLen = ArrayHelper::GetArrayLength(thread, thisObjVal);
1450 // 4. ReturnIfAbrupt(len).
1451 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1452
1453 // 5. If IsCallable(callbackfn) is false, throw a TypeError exception.
1454 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
1455 if (!callbackFnHandle->IsCallable()) {
1456 THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
1457 }
1458
1459 // 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
1460 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
1461
1462 // 7. Let A be ArraySpeciesCreate(O, len).
1463 JSTaggedValue newArray =
1464 JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(static_cast<double>(rawLen)));
1465 // 8. ReturnIfAbrupt(A).
1466 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1467 if (!newArray.IsECMAObject()) {
1468 THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to create Object.", JSTaggedValue::Exception());
1469 }
1470 JSHandle<JSObject> newArrayHandle(thread, newArray);
1471
1472 // 9. Let k be 0.
1473 // 10. Repeat, while k < len
1474 // a. Let Pk be ToString(k).
1475 // b. Let kPresent be HasProperty(O, Pk).
1476 // c. ReturnIfAbrupt(kPresent).
1477 // d. If kPresent is true, then
1478 // i. Let kValue be Get(O, Pk).
1479 // ii. ReturnIfAbrupt(kValue).
1480 // iii. Let mappedValue be Call(callbackfn, T, «kValue, k, O»).
1481 // iv. ReturnIfAbrupt(mappedValue).
1482 // v. Let status be CreateDataPropertyOrThrow (A, Pk, mappedValue).
1483 // vi. ReturnIfAbrupt(status).
1484 // e. Increase k by 1.
1485 uint32_t k = 0;
1486 uint32_t len = static_cast<uint32_t>(rawLen);
1487 if (thisObjVal->IsStableJSArray(thread)) {
1488 JSStableArray::Map(newArrayHandle, thisObjHandle, argv, k, len);
1489 }
1490 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1491 JSMutableHandle<JSTaggedValue> mapResultHandle(thread, JSTaggedValue::Undefined());
1492 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1493 const uint32_t argsLength = 3; // 3: «kValue, k, O»
1494 while (k < len) {
1495 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k);
1496 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1497 if (exists) {
1498 JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
1499 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1500 key.Update(JSTaggedValue(k));
1501 EcmaRuntimeCallInfo *info =
1502 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1503 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1504 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1505 JSTaggedValue mapResult = JSFunction::Call(info);
1506 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1507 mapResultHandle.Update(mapResult);
1508 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, mapResultHandle);
1509 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1510 }
1511 k++;
1512 }
1513
1514 // 11. Return A.
1515 return newArrayHandle.GetTaggedValue();
1516 }
1517
1518 // 22.1.3.16 Array.prototype.pop ( )
Pop(EcmaRuntimeCallInfo * argv)1519 JSTaggedValue BuiltinsArray::Pop(EcmaRuntimeCallInfo *argv)
1520 {
1521 ASSERT(argv);
1522 BUILTINS_API_TRACE(argv->GetThread(), Array, Pop);
1523 JSThread *thread = argv->GetThread();
1524 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1525
1526 // 1. Let O be ToObject(this value).
1527 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
1528 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
1529 if (thisHandle->IsStableJSArray(thread) && JSObject::IsArrayLengthWritable(thread, thisObjHandle)) {
1530 return JSStableArray::Pop(JSHandle<JSArray>::Cast(thisHandle), argv);
1531 }
1532
1533 // 2. ReturnIfAbrupt(O).
1534 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1535 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1536
1537 // 3. Let len be ToLength(Get(O, "length")).
1538 int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
1539 // 4. ReturnIfAbrupt(len).
1540 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1541 // 5. If len is zero,
1542 // a. Let setStatus be Set(O, "length", 0, true).
1543 // b. ReturnIfAbrupt(setStatus).
1544 // c. Return undefined.
1545 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
1546 if (len == 0) {
1547 JSHandle<JSTaggedValue> lengthValue(thread, JSTaggedValue(0));
1548 JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, lengthValue, true);
1549 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1550 return JSTaggedValue::Undefined();
1551 }
1552
1553 // 6. Else len > 0,
1554 // a. Let newLen be len–1.
1555 // b. Let indx be ToString(newLen).
1556 // c. Let element be Get(O, indx).
1557 // d. ReturnIfAbrupt(element).
1558 // e. Let deleteStatus be DeletePropertyOrThrow(O, indx).
1559 // f. ReturnIfAbrupt(deleteStatus).
1560 // g. Let setStatus be Set(O, "length", newLen, true).
1561 // h. ReturnIfAbrupt(setStatus).
1562 // i. Return element.
1563 int64_t newLen = len - 1;
1564 JSHandle<JSTaggedValue> indexHandle(thread, JSTaggedValue(newLen));
1565 JSHandle<JSTaggedValue> element = JSTaggedValue::GetProperty(thread, thisObjVal, indexHandle).GetValue();
1566 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1567 JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, indexHandle);
1568 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1569 JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, indexHandle, true);
1570 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1571
1572 return element.GetTaggedValue();
1573 }
1574
1575 // 22.1.3.17 Array.prototype.push ( ...items )
Push(EcmaRuntimeCallInfo * argv)1576 JSTaggedValue BuiltinsArray::Push(EcmaRuntimeCallInfo *argv)
1577 {
1578 ASSERT(argv);
1579 BUILTINS_API_TRACE(argv->GetThread(), Array, Push);
1580 JSThread *thread = argv->GetThread();
1581 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1582 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
1583 if (thisHandle->IsStableJSArray(thread)) {
1584 return JSStableArray::Push(JSHandle<JSArray>::Cast(thisHandle), argv);
1585 }
1586 // 6. Let argCount be the number of elements in items.
1587 uint32_t argc = argv->GetArgsNumber();
1588
1589 // 1. Let O be ToObject(this value).
1590 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
1591 // 2. ReturnIfAbrupt(O).
1592 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1593 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1594
1595 // 3. Let len be ToLength(Get(O, "length")).
1596 int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
1597 // 4. ReturnIfAbrupt(len).
1598 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1599 // 7. If len + argCount > 253-1, throw a TypeError exception.
1600 if ((len + static_cast<int64_t>(argc)) > base::MAX_SAFE_INTEGER) {
1601 THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
1602 }
1603
1604 // 8. Repeat, while items is not empty
1605 // a. Remove the first element from items and let E be the value of the element.
1606 // b. Let setStatus be Set(O, ToString(len), E, true).
1607 // c. ReturnIfAbrupt(setStatus).
1608 // d. Let len be len+1.
1609 uint32_t k = 0;
1610 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1611 while (k < argc) {
1612 key.Update(JSTaggedValue(len));
1613 JSHandle<JSTaggedValue> kValue = GetCallArg(argv, k);
1614 JSArray::FastSetPropertyByValue(thread, thisObjVal, key, kValue);
1615 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1616 k++;
1617 len++;
1618 }
1619
1620 // 9. Let setStatus be Set(O, "length", len, true).
1621 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
1622 key.Update(JSTaggedValue(len));
1623 JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, key, true);
1624 // 10. ReturnIfAbrupt(setStatus).
1625 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1626
1627 // 11. Return len.
1628 return GetTaggedDouble(len);
1629 }
1630
ReduceUnStableJSArray(JSThread * thread,JSHandle<JSTaggedValue> & thisHandle,JSHandle<JSTaggedValue> & thisObjVal,int64_t k,int64_t len,JSMutableHandle<JSTaggedValue> & accumulator,JSHandle<JSTaggedValue> & callbackFnHandle)1631 JSTaggedValue BuiltinsArray::ReduceUnStableJSArray(JSThread *thread, JSHandle<JSTaggedValue> &thisHandle,
1632 JSHandle<JSTaggedValue> &thisObjVal, int64_t k, int64_t len, JSMutableHandle<JSTaggedValue> &accumulator,
1633 JSHandle<JSTaggedValue> &callbackFnHandle)
1634 {
1635 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1636 JSTaggedValue callResult = JSTaggedValue::Undefined();
1637 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1638 while (k < len) {
1639 bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k));
1640 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1641 if (exists) {
1642 JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
1643 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1644 key.Update(JSTaggedValue(k));
1645 JSHandle<JSTaggedValue> thisArgHandle = globalConst->GetHandledUndefined();
1646 const uint32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
1647 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1648 EcmaRuntimeCallInfo *info =
1649 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1650 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1651 info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(), key.GetTaggedValue(),
1652 thisObjVal.GetTaggedValue());
1653 callResult = JSFunction::Call(info);
1654 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1655 accumulator.Update(callResult);
1656 }
1657 k++;
1658 }
1659 return accumulator.GetTaggedValue();
1660 }
1661
1662 // 22.1.3.18 Array.prototype.reduce ( callbackfn [ , initialValue ] )
Reduce(EcmaRuntimeCallInfo * argv)1663 JSTaggedValue BuiltinsArray::Reduce(EcmaRuntimeCallInfo *argv)
1664 {
1665 ASSERT(argv);
1666 BUILTINS_API_TRACE(argv->GetThread(), Array, Reduce);
1667 JSThread *thread = argv->GetThread();
1668 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1669
1670 uint32_t argc = argv->GetArgsNumber();
1671 // 1. Let O be ToObject(this value).
1672 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
1673 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
1674 // 2. ReturnIfAbrupt(O).
1675 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1676 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1677
1678 // 3. Let len be ToLength(Get(O, "length")).
1679 int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
1680 // 4. ReturnIfAbrupt(len).
1681 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1682
1683 // 5. If IsCallable(callbackfn) is false, throw a TypeError exception.
1684 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
1685 if (!callbackFnHandle->IsCallable()) {
1686 THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
1687 }
1688
1689 // 6. If len is 0 and initialValue is not present, throw a TypeError exception.
1690 if (len == 0 && argc < 2) { // 2:2 means the number of parameters
1691 THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
1692 }
1693
1694 // 7. Let k be 0.
1695 // 8. If initialValue is present, then
1696 // a. Set accumulator to initialValue.
1697 // 9. Else initialValue is not present,
1698 // a. Let kPresent be false.
1699 // b. Repeat, while kPresent is false and k < len
1700 // i. Let Pk be ToString(k).
1701 // ii. Let kPresent be HasProperty(O, Pk).
1702 // iii. ReturnIfAbrupt(kPresent).
1703 // iv. If kPresent is true, then
1704 // 1. Let accumulator be Get(O, Pk).
1705 // 2. ReturnIfAbrupt(accumulator).
1706 // v. Increase k by 1.
1707 // c. If kPresent is false, throw a TypeError exception.
1708 int64_t k = 0;
1709 JSMutableHandle<JSTaggedValue> accumulator(thread, JSTaggedValue::Undefined());
1710 if (argc == 2) { // 2:2 means the number of parameters
1711 accumulator.Update(GetCallArg(argv, 1).GetTaggedValue());
1712 } else {
1713 bool kPresent = false;
1714 while (!kPresent && k < len) {
1715 kPresent = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k));
1716 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1717 if (kPresent) {
1718 accumulator.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue());
1719 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1720 }
1721 k++;
1722 }
1723 if (!kPresent) {
1724 THROW_TYPE_ERROR_AND_RETURN(thread, "accumulator can't be initialized.", JSTaggedValue::Exception());
1725 }
1726 }
1727
1728 if (thisObjVal->IsStableJSArray(thread)) {
1729 JSStableArray::Reduce(thread, thisObjHandle, callbackFnHandle, accumulator, k, len);
1730 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1731 }
1732 return ReduceUnStableJSArray(thread, thisHandle, thisObjVal, k, len, accumulator, callbackFnHandle);
1733 }
1734
1735 // 22.1.3.19 Array.prototype.reduceRight ( callbackfn [ , initialValue ] )
ReduceRight(EcmaRuntimeCallInfo * argv)1736 JSTaggedValue BuiltinsArray::ReduceRight(EcmaRuntimeCallInfo *argv)
1737 {
1738 ASSERT(argv);
1739 BUILTINS_API_TRACE(argv->GetThread(), Array, ReduceRight);
1740 JSThread *thread = argv->GetThread();
1741 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1742 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1743
1744 uint32_t argc = argv->GetArgsNumber();
1745
1746 // 1. Let O be ToObject(this value).
1747 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
1748 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
1749 // 2. ReturnIfAbrupt(O).
1750 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1751 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1752
1753 // 3. Let len be ToLength(Get(O, "length")).
1754 int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
1755 // 4. ReturnIfAbrupt(len).
1756 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1757
1758 // 5. If IsCallable(callbackfn) is false, throw a TypeError exception.
1759 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
1760 if (!callbackFnHandle->IsCallable()) {
1761 THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
1762 }
1763
1764 // 6. If len is 0 and initialValue is not present, throw a TypeError exception.
1765 if (len == 0 && argc < 2) { // 2:2 means the number of parameters
1766 THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
1767 }
1768
1769 // 7. Let k be len-1.
1770 int64_t k = len - 1;
1771 // 8. If initialValue is present, then
1772 // a. Set accumulator to initialValue.
1773 // 9. Else initialValue is not present,
1774 // a. Let kPresent be false.
1775 // b. Repeat, while kPresent is false and k ≥ 0
1776 // i. Let Pk be ToString(k).
1777 // ii. Let kPresent be HasProperty(O, Pk).
1778 // iii. ReturnIfAbrupt(kPresent).
1779 // iv. If kPresent is true, then
1780 // 1. Let accumulator be Get(O, Pk).
1781 // 2. ReturnIfAbrupt(accumulator).
1782 // v. Decrease k by 1.
1783 // c. If kPresent is false, throw a TypeError exception.
1784 JSMutableHandle<JSTaggedValue> accumulator(thread, JSTaggedValue::Undefined());
1785 if (argc == 2) { // 2:2 means the number of parameters
1786 accumulator.Update(GetCallArg(argv, 1).GetTaggedValue());
1787 } else {
1788 bool kPresent = false;
1789 while (!kPresent && k >= 0) {
1790 kPresent = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k));
1791 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1792 if (kPresent) {
1793 accumulator.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue());
1794 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1795 }
1796 k--;
1797 }
1798 if (!kPresent) {
1799 THROW_TYPE_ERROR_AND_RETURN(thread, "accumulator can't be initialized.", JSTaggedValue::Exception());
1800 }
1801 }
1802
1803 // 10. Repeat, while k ≥ 0
1804 // a. Let Pk be ToString(k).
1805 // b. Let kPresent be HasProperty(O, Pk).
1806 // c. ReturnIfAbrupt(kPresent).
1807 // d. If kPresent is true, then
1808 // i. Let kValue be Get(O, Pk).
1809 // ii. ReturnIfAbrupt(kValue).
1810 // iii. Let accumulator be Call(callbackfn, undefined, «accumulator, kValue, k, O»).
1811 // iv. ReturnIfAbrupt(accumulator).
1812 // e. Decrease k by 1.
1813 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1814 JSTaggedValue callResult = JSTaggedValue::Undefined();
1815 while (k >= 0) {
1816 key.Update(JSTaggedValue(k));
1817 bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, key));
1818 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1819 if (exists) {
1820 JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, key);
1821 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1822 JSHandle<JSTaggedValue> thisArgHandle = globalConst->GetHandledUndefined();
1823 const uint32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
1824 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1825 EcmaRuntimeCallInfo *info =
1826 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1827 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1828 info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(), key.GetTaggedValue(),
1829 thisObjVal.GetTaggedValue());
1830 callResult = JSFunction::Call(info);
1831 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1832 accumulator.Update(callResult);
1833 }
1834 k--;
1835 }
1836
1837 // 11. Return accumulator.
1838 return accumulator.GetTaggedValue();
1839 }
1840
1841 // 22.1.3.20 Array.prototype.reverse ( )
Reverse(EcmaRuntimeCallInfo * argv)1842 JSTaggedValue BuiltinsArray::Reverse(EcmaRuntimeCallInfo *argv)
1843 {
1844 ASSERT(argv);
1845 BUILTINS_API_TRACE(argv->GetThread(), Array, Reverse);
1846 JSThread *thread = argv->GetThread();
1847 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1848
1849 // 1. Let O be ToObject(this value).
1850 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
1851 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
1852 // 2. ReturnIfAbrupt(O).
1853 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1854 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1855
1856 // 3. Let len be ToLength(Get(O, "length")).
1857 int64_t len = 0;
1858 if (thisHandle->IsJSArray()) {
1859 len = JSArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength();
1860 } else {
1861 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
1862 JSHandle<JSTaggedValue> lenResult = JSTaggedValue::GetProperty(thread, thisHandle, lengthKey).GetValue();
1863 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
1864 JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread, lenResult);
1865 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
1866 len = lenNumber.GetNumber();
1867 }
1868 // 4. ReturnIfAbrupt(len).
1869 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1870
1871 // 5. Let middle be floor(len/2).
1872 int64_t middle = std::floor(len / 2);
1873
1874 // 6. Let lower be 0.
1875 int64_t lower = 0;
1876
1877 // 7. Repeat, while lower != middle
1878 // a. Let upper be len-lower-1.
1879 // b. Let upperP be ToString(upper).
1880 // c. Let lowerP be ToString(lower).
1881 // d. Let lowerExists be HasProperty(O, lowerP).
1882 // e. ReturnIfAbrupt(lowerExists).
1883 // f. If lowerExists is true, then
1884 // i. Let lowerValue be Get(O, lowerP).
1885 // ii. ReturnIfAbrupt(lowerValue).
1886 // g. Let upperExists be HasProperty(O, upperP).
1887 // h. ReturnIfAbrupt(upperExists).
1888 // i. If upperExists is true, then
1889 // i. Let upperValue be Get(O, upperP).
1890 // ii. ReturnIfAbrupt(upperValue).
1891 // j. If lowerExists is true and upperExists is true, then
1892 // i. Let setStatus be Set(O, lowerP, upperValue, true).
1893 // ii. ReturnIfAbrupt(setStatus).
1894 // iii. Let setStatus be Set(O, upperP, lowerValue, true).
1895 // iv. ReturnIfAbrupt(setStatus).
1896 // k. Else if lowerExists is false and upperExists is true, then
1897 // i. Let setStatus be Set(O, lowerP, upperValue, true).
1898 // ii. ReturnIfAbrupt(setStatus).
1899 // iii. Let deleteStatus be DeletePropertyOrThrow (O, upperP).
1900 // iv. ReturnIfAbrupt(deleteStatus).
1901 // l. Else if lowerExists is true and upperExists is false, then
1902 // i. Let deleteStatus be DeletePropertyOrThrow (O, lowerP).
1903 // ii. ReturnIfAbrupt(deleteStatus).
1904 // iii. Let setStatus be Set(O, upperP, lowerValue, true).
1905 // iv. ReturnIfAbrupt(setStatus).
1906 // m. Else both lowerExists and upperExists are false,
1907 // i. No action is required.
1908 // n. Increase lower by 1.
1909 JSMutableHandle<JSTaggedValue> lowerP(thread, JSTaggedValue::Undefined());
1910 JSMutableHandle<JSTaggedValue> upperP(thread, JSTaggedValue::Undefined());
1911 JSHandle<JSTaggedValue> lowerValueHandle(thread, JSTaggedValue::Undefined());
1912 JSHandle<JSTaggedValue> upperValueHandle(thread, JSTaggedValue::Undefined());
1913
1914 if (thisObjVal->IsStableJSArray(thread)) {
1915 JSStableArray::Reverse(thread, thisObjHandle, lower, len);
1916 }
1917 while (lower != middle) {
1918 int64_t upper = len - lower - 1;
1919 lowerP.Update(JSTaggedValue(lower));
1920 upperP.Update(JSTaggedValue(upper));
1921 bool lowerExists = (JSTaggedValue::HasProperty(thread, thisObjVal, lowerP));
1922 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1923 if (lowerExists) {
1924 lowerValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, lowerP);
1925 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1926 }
1927 bool upperExists = (JSTaggedValue::HasProperty(thread, thisObjVal, upperP));
1928 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1929 if (upperExists) {
1930 upperValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, upperP);
1931 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1932 }
1933 if (lowerExists && upperExists) {
1934 JSArray::FastSetPropertyByValue(thread, thisObjVal, lowerP, upperValueHandle);
1935 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1936 JSArray::FastSetPropertyByValue(thread, thisObjVal, upperP, lowerValueHandle);
1937 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1938 } else if (upperExists) {
1939 JSArray::FastSetPropertyByValue(thread, thisObjVal, lowerP, upperValueHandle);
1940 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1941 JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, upperP);
1942 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1943 } else if (lowerExists) {
1944 JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, lowerP);
1945 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1946 JSArray::FastSetPropertyByValue(thread, thisObjVal, upperP, lowerValueHandle);
1947 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1948 } else {
1949 }
1950 lower++;
1951 }
1952
1953 // 8. Return O .
1954 return thisObjHandle.GetTaggedValue();
1955 }
1956
1957 // 22.1.3.21 Array.prototype.shift ( )
Shift(EcmaRuntimeCallInfo * argv)1958 JSTaggedValue BuiltinsArray::Shift(EcmaRuntimeCallInfo *argv)
1959 {
1960 ASSERT(argv);
1961 BUILTINS_API_TRACE(argv->GetThread(), Array, Shift);
1962 JSThread *thread = argv->GetThread();
1963 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1964
1965 // 1. Let O be ToObject(this value).
1966 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
1967 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
1968 // 2. ReturnIfAbrupt(O).
1969 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1970 if (thisHandle->IsStableJSArray(thread) && JSObject::IsArrayLengthWritable(thread, thisObjHandle)) {
1971 return JSStableArray::Shift(JSHandle<JSArray>::Cast(thisHandle), argv);
1972 }
1973 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1974
1975 // 3. Let len be ToLength(Get(O, "length")).
1976 int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
1977 // 4. ReturnIfAbrupt(len).
1978 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1979 // 5. If len is zero, then
1980 // a. Let setStatus be Set(O, "length", 0, true).
1981 // b. ReturnIfAbrupt(setStatus).
1982 // c. Return undefined.
1983 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
1984 if (len == 0) {
1985 JSHandle<JSTaggedValue> zeroLenHandle(thread, JSTaggedValue(len));
1986 JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, zeroLenHandle, true);
1987 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1988 return JSTaggedValue::Undefined();
1989 }
1990
1991 // 6. Let first be Get(O, "0").
1992 JSHandle<JSTaggedValue> firstKey(thread, JSTaggedValue(0));
1993 JSHandle<JSTaggedValue> firstValue = JSTaggedValue::GetProperty(thread, thisObjVal, firstKey).GetValue();
1994 // 7. ReturnIfAbrupt(first).
1995 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1996
1997 // 8. Let k be 1.
1998 // 9. Repeat, while k < len
1999 // a. Let from be ToString(k).
2000 // b. Let to be ToString(k–1).
2001 // c. Let fromPresent be HasProperty(O, from).
2002 // d. ReturnIfAbrupt(fromPresent).
2003 // e. If fromPresent is true, then
2004 // i. Let fromVal be Get(O, from).
2005 // ii. ReturnIfAbrupt(fromVal).
2006 // iii. Let setStatus be Set(O, to, fromVal, true).
2007 // iv. ReturnIfAbrupt(setStatus).
2008 // f. Else fromPresent is false,
2009 // i. Let deleteStatus be DeletePropertyOrThrow(O, to).
2010 // ii. ReturnIfAbrupt(deleteStatus).
2011 // g. Increase k by 1.
2012 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
2013 int64_t k = 1;
2014 while (k < len) {
2015 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k);
2016 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2017 if (exists) {
2018 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
2019 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2020 JSArray::FastSetPropertyByValue(thread, thisObjVal, k - 1, fromValue);
2021 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2022 } else {
2023 toKey.Update(JSTaggedValue(k - 1));
2024 JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey);
2025 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2026 }
2027 k++;
2028 }
2029 // 10. Let deleteStatus be DeletePropertyOrThrow(O, ToString(len–1)).
2030 JSHandle<JSTaggedValue> deleteKey(thread, JSTaggedValue(len - 1));
2031 JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, deleteKey);
2032 // 11. ReturnIfAbrupt(deleteStatus).
2033 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2034
2035 // 12. Let setStatus be Set(O, "length", len–1, true).
2036 JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(len - 1));
2037 JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
2038 // 13. ReturnIfAbrupt(setStatus).
2039 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2040
2041 // 14. Return first.
2042 return firstValue.GetTaggedValue();
2043 }
2044
2045 // 22.1.3.22 Array.prototype.slice (start, end)
Slice(EcmaRuntimeCallInfo * argv)2046 JSTaggedValue BuiltinsArray::Slice(EcmaRuntimeCallInfo *argv)
2047 {
2048 BUILTINS_API_TRACE(argv->GetThread(), Array, Slice);
2049 ASSERT(argv);
2050 JSThread *thread = argv->GetThread();
2051 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2052
2053 // 1. Let O be ToObject(this value).
2054 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2055 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
2056 // 2. ReturnIfAbrupt(O).
2057 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2058 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
2059
2060 // 3. Let len be ToLength(Get(O, "length")).
2061 int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
2062 // 4. ReturnIfAbrupt(len).
2063 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2064
2065 JSHandle<JSTaggedValue> msg0 = GetCallArg(argv, 0);
2066 double argStart;
2067 if (msg0->IsInt()) {
2068 argStart = msg0->GetInt();
2069 } else {
2070 // 5. Let relativeStart be ToInteger(start).
2071 JSTaggedNumber argStartTemp = JSTaggedValue::ToInteger(thread, msg0);
2072 // 6. ReturnIfAbrupt(relativeStart).
2073 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2074 argStart = argStartTemp.GetNumber();
2075 }
2076
2077 // 7. If relativeStart < 0, let k be max((len + relativeStart),0); else let k be min(relativeStart, len).
2078 int64_t k = 0;
2079 if (argStart < 0) {
2080 double tempStart = len + argStart;
2081 k = tempStart > 0 ? tempStart : 0;
2082 } else {
2083 k = argStart < len ? argStart : len;
2084 }
2085
2086 // 8. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end).
2087 // 9. ReturnIfAbrupt(relativeEnd).
2088 // 10. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len).
2089 JSHandle<JSTaggedValue> msg1 = GetCallArg(argv, 1);
2090 double argEnd = len;
2091 if (!msg1->IsUndefined()) {
2092 if (msg1->IsInt()) {
2093 argEnd = msg1->GetInt();
2094 } else {
2095 JSTaggedNumber argEndTemp = JSTaggedValue::ToInteger(thread, msg1);
2096 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2097 argEnd = argEndTemp.GetNumber();
2098 }
2099 }
2100 int64_t final = 0;
2101 if (argEnd < 0) {
2102 double tempFinal = len + argEnd;
2103 final = tempFinal > 0 ? tempFinal : 0;
2104 } else {
2105 final = argEnd < len ? argEnd : len;
2106 }
2107 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2108
2109 // 11. Let count be max(final – k, 0).
2110 int64_t count = final > k ? (final - k) : 0;
2111
2112 if (thisHandle->IsStableJSArray(thread) && !thisObjHandle->GetJSHClass()->HasConstructor()
2113 && JSObject::GetPrototype(thisObjHandle).IsJSArray()) {
2114 return JSStableArray::Slice(thread, thisObjHandle, k, count);
2115 }
2116
2117 // 12. Let A be ArraySpeciesCreate(O, count).
2118 JSTaggedValue newArray =
2119 JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(static_cast<double>(count)));
2120 // 13. ReturnIfAbrupt(A).
2121 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2122 if (count == 0) {
2123 return newArray;
2124 }
2125 JSHandle<JSObject> newArrayHandle(thread, newArray);
2126
2127 // 14. Let n be 0.
2128 // 15. Repeat, while k < final
2129 // a. Let Pk be ToString(k).
2130 // b. Let kPresent be HasProperty(O, Pk).
2131 // c. ReturnIfAbrupt(kPresent).
2132 // d. If kPresent is true, then
2133 // i. Let kValue be Get(O, Pk).
2134 // ii. ReturnIfAbrupt(kValue).
2135 // iii. Let status be CreateDataPropertyOrThrow(A, ToString(n), kValue ).
2136 // iv. ReturnIfAbrupt(status).
2137 // e. Increase k by 1.
2138 // f. Increase n by 1.
2139 int64_t n = 0;
2140 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
2141 JSMutableHandle<JSTaggedValue> nKey(thread, JSTaggedValue::Undefined());
2142 while (k < final) {
2143 key.Update(JSTaggedValue(k));
2144 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, key);
2145 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2146 if (exists) {
2147 nKey.Update(JSTaggedValue(n));
2148 JSHandle<JSTaggedValue> kValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, key);
2149 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2150 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, nKey, kValueHandle);
2151 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2152 }
2153 k++;
2154 n++;
2155 }
2156
2157 // 16. Let setStatus be Set(A, "length", n, true).
2158 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
2159 JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(n));
2160 JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, newLenHandle, true);
2161 // 17. ReturnIfAbrupt(setStatus).
2162 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2163
2164 // 18. Return A.
2165 return newArrayHandle.GetTaggedValue();
2166 }
2167
2168 // 22.1.3.23 Array.prototype.some ( callbackfn [ , thisArg ] )
Some(EcmaRuntimeCallInfo * argv)2169 JSTaggedValue BuiltinsArray::Some(EcmaRuntimeCallInfo *argv)
2170 {
2171 ASSERT(argv);
2172 BUILTINS_API_TRACE(argv->GetThread(), Array, Some);
2173 JSThread *thread = argv->GetThread();
2174 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2175
2176 // 1. Let O be ToObject(this value).
2177 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2178 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
2179 // 2. ReturnIfAbrupt(O).
2180 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2181 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
2182
2183 // 3. Let len be ToLength(Get(O, "length")).
2184 int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
2185 // 4. ReturnIfAbrupt(len).
2186 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2187
2188 // 5. If IsCallable(callbackfn) is false, throw a TypeError exception.
2189 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
2190 if (!callbackFnHandle->IsCallable()) {
2191 THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
2192 }
2193
2194 // 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
2195 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
2196
2197 // 7. Let k be 0.
2198 // 8. Repeat, while k < len
2199 // a. Let Pk be ToString(k).
2200 // b. Let kPresent be HasProperty(O, Pk).
2201 // c. ReturnIfAbrupt(kPresent).
2202 // d. If kPresent is true, then
2203 // i. Let kValue be Get(O, Pk).
2204 // ii. ReturnIfAbrupt(kValue).
2205 // iii. Let testResult be ToBoolean(Call(callbackfn, T, «kValue, k, and O»)).
2206 // iv. ReturnIfAbrupt(testResult).
2207 // v. If testResult is true, return true.
2208 // e. Increase k by 1.
2209 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
2210 int64_t k = 0;
2211 while (k < len) {
2212 bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k));
2213 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2214 if (exists) {
2215 key.Update(JSTaggedValue(k));
2216 JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, key);
2217 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2218 const uint32_t argsLength = 3; // 3: «kValue, k, O»
2219 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
2220 EcmaRuntimeCallInfo *info =
2221 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
2222 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2223 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
2224 JSTaggedValue callResult = JSFunction::Call(info);
2225 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2226 if (callResult.ToBoolean()) {
2227 return GetTaggedBoolean(true);
2228 }
2229 }
2230 k++;
2231 }
2232
2233 // 9. Return false.
2234 return GetTaggedBoolean(false);
2235 }
2236
2237 // 22.1.3.24 Array.prototype.sort (comparefn)
Sort(EcmaRuntimeCallInfo * argv)2238 JSTaggedValue BuiltinsArray::Sort(EcmaRuntimeCallInfo *argv)
2239 {
2240 ASSERT(argv);
2241 JSThread *thread = argv->GetThread();
2242 BUILTINS_API_TRACE(thread, Array, Sort);
2243 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2244
2245 // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception.
2246 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
2247 if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable()) {
2248 THROW_TYPE_ERROR_AND_RETURN(thread, "Callable is false", JSTaggedValue::Exception());
2249 }
2250
2251 // 2. Let obj be ToObject(this value).
2252 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2253 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
2254 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2255
2256 // Array sort
2257 if (thisHandle->IsStableJSArray(thread) && callbackFnHandle->IsUndefined()) {
2258 JSStableArray::Sort(thread, thisObjHandle, callbackFnHandle);
2259 } else {
2260 JSArray::Sort(thread, JSHandle<JSTaggedValue>::Cast(thisObjHandle), callbackFnHandle);
2261 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2262 }
2263 return thisObjHandle.GetTaggedValue();
2264 }
2265
2266 // 22.1.3.25 Array.prototype.splice (start, deleteCount , ...items )
2267 // NOLINTNEXTLINE(readability-function-size)
Splice(EcmaRuntimeCallInfo * argv)2268 JSTaggedValue BuiltinsArray::Splice(EcmaRuntimeCallInfo *argv)
2269 {
2270 ASSERT(argv);
2271 BUILTINS_API_TRACE(argv->GetThread(), Array, Splice);
2272 JSThread *thread = argv->GetThread();
2273 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2274 uint32_t argc = argv->GetArgsNumber();
2275 // 1. Let O be ToObject(this value).
2276 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2277 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
2278 // 2. ReturnIfAbrupt(O).
2279 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2280 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
2281 // 3. Let len be ToLength(Get(O, "length")).
2282 int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
2283 // 4. ReturnIfAbrupt(len).
2284 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2285 // 5. Let relativeStart be ToInteger(start).
2286 int64_t start = 0;
2287 int64_t insertCount = 0;
2288 int64_t actualDeleteCount = 0;
2289 int64_t end = len;
2290 double argStart = 0;
2291 if (argc > 0) {
2292 JSHandle<JSTaggedValue> msg0 = GetCallArg(argv, 0);
2293 JSTaggedNumber argStartTemp = JSTaggedValue::ToInteger(thread, msg0);
2294 // 6. ReturnIfAbrupt(relativeStart).
2295 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2296 argStart = argStartTemp.GetNumber();
2297 // 7. If relativeStart < 0, let actualStart be max((len + relativeStart),0); else let actualStart be
2298 // min(relativeStart, len).
2299 if (argStart < 0) {
2300 double tempStart = argStart + len;
2301 start = tempStart > 0 ? tempStart : 0;
2302 } else {
2303 start = argStart < end ? argStart : end;
2304 }
2305 actualDeleteCount = len - start;
2306 }
2307 // 8. If the number of actual arguments is 0, then
2308 // a. Let insertCount be 0.
2309 // b. Let actualDeleteCount be 0.
2310 // 9. Else if the number of actual arguments is 1, then
2311 // a. Let insertCount be 0.
2312 // b. Let actualDeleteCount be len – actualStart.
2313 // 10. Else,
2314 // a. Let insertCount be the number of actual arguments minus 2.
2315 // b. Let dc be ToInteger(deleteCount).
2316 // c. ReturnIfAbrupt(dc).
2317 // d. Let actualDeleteCount be min(max(dc,0), len – actualStart).
2318 if (argc > 1) {
2319 insertCount = argc - 2; // 2:2 means there are two arguments before the insert items.
2320 JSHandle<JSTaggedValue> msg1 = GetCallArg(argv, 1);
2321 JSTaggedNumber argDeleteCount = JSTaggedValue::ToInteger(thread, msg1);
2322 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2323 double deleteCount = argDeleteCount.GetNumber();
2324 deleteCount = deleteCount > 0 ? deleteCount : 0;
2325 actualDeleteCount = deleteCount < (len - start) ? deleteCount : len - start;
2326 }
2327 // 11. If len+insertCount−actualDeleteCount > 253-1, throw a TypeError exception.
2328 if (len + insertCount - actualDeleteCount > base::MAX_SAFE_INTEGER) {
2329 THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
2330 }
2331 // 12. Let A be ArraySpeciesCreate(O, actualDeleteCount).
2332 JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle,
2333 JSTaggedNumber(static_cast<double>(actualDeleteCount)));
2334 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2335 JSHandle<JSObject> newArrayHandle(thread, newArray);
2336 if (thisHandle->IsStableJSArray(thread)) {
2337 return JSStableArray::Splice(JSHandle<JSArray>::Cast(thisHandle), argv, start, insertCount,
2338 actualDeleteCount, newArrayHandle, len);
2339 }
2340 // 14. Let k be 0.
2341 // 15. Repeat, while k < actualDeleteCount
2342 // a. Let from be ToString(actualStart+k).
2343 // b. Let fromPresent be HasProperty(O, from).
2344 // d. If fromPresent is true, then
2345 // i. Let fromValue be Get(O, from).
2346 // iii. Let status be CreateDataPropertyOrThrow(A, ToString(k), fromValue).
2347 // e. Increase k by 1.
2348 JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
2349 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
2350 int64_t k = 0;
2351 while (k < actualDeleteCount) {
2352 int64_t from = start + k;
2353 fromKey.Update(JSTaggedValue(from));
2354 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
2355 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2356 if (exists) {
2357 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
2358 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2359 toKey.Update(JSTaggedValue(k));
2360 if (newArrayHandle->IsJSProxy()) {
2361 toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue());
2362 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2363 }
2364 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue);
2365 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2366 }
2367 k++;
2368 }
2369 // 16. Let setStatus be Set(A, "length", actualDeleteCount, true).
2370 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
2371 JSHandle<JSTaggedValue> deleteCountHandle(thread, JSTaggedValue(actualDeleteCount));
2372 JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, deleteCountHandle,
2373 true);
2374 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2375 // 19. Let itemCount be the number of elements in items.
2376 // 20. If itemCount < actualDeleteCount, then
2377 // a. Let k be actualStart.
2378 // b. Repeat, while k < (len – actualDeleteCount)
2379 // i. Let from be ToString(k+actualDeleteCount).
2380 // ii. Let to be ToString(k+itemCount).
2381 // iii. Let fromPresent be HasProperty(O, from).
2382 // v. If fromPresent is true, then
2383 // 1. Let fromValue be Get(O, from).
2384 // 3. Let setStatus be Set(O, to, fromValue, true).
2385 // vi. Else fromPresent is false,
2386 // 1. Let deleteStatus be DeletePropertyOrThrow(O, to).
2387 // vii. Increase k by 1.
2388 // c. Let k be len.
2389 // d. Repeat, while k > (len – actualDeleteCount + itemCount)
2390 // i. Let deleteStatus be DeletePropertyOrThrow(O, ToString(k–1)).
2391 // iii. Decrease k by 1.
2392 if (insertCount < actualDeleteCount) {
2393 k = start;
2394 while (k < len - actualDeleteCount) {
2395 fromKey.Update(JSTaggedValue(k + actualDeleteCount));
2396 toKey.Update(JSTaggedValue(k + insertCount));
2397 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
2398 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2399 if (exists) {
2400 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
2401 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2402 JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValue);
2403 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2404 } else {
2405 JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey);
2406 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2407 }
2408 k++;
2409 }
2410 k = len;
2411 JSMutableHandle<JSTaggedValue> deleteKey(thread, JSTaggedValue::Undefined());
2412 while (k > len - actualDeleteCount + insertCount) {
2413 deleteKey.Update(JSTaggedValue(k - 1));
2414 JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, deleteKey);
2415 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2416 k--;
2417 }
2418 } else if (insertCount > actualDeleteCount) {
2419 // 21. Else if itemCount > actualDeleteCount, then
2420 // a. Let k be (len – actualDeleteCount).
2421 // b. Repeat, while k > actualStart
2422 // i. Let from be ToString(k + actualDeleteCount – 1).
2423 // ii. Let to be ToString(k + itemCount – 1)
2424 // iii. Let fromPresent be HasProperty(O, from).
2425 // iv. ReturnIfAbrupt(fromPresent).
2426 // v. If fromPresent is true, then
2427 // 1. Let fromValue be Get(O, from).
2428 // 2. ReturnIfAbrupt(fromValue).
2429 // 3. Let setStatus be Set(O, to, fromValue, true).
2430 // 4. ReturnIfAbrupt(setStatus).
2431 // vi. Else fromPresent is false,
2432 // 1. Let deleteStatus be DeletePropertyOrThrow(O, to).
2433 // 2. ReturnIfAbrupt(deleteStatus).
2434 // vii. Decrease k by 1.
2435 k = len - actualDeleteCount;
2436 while (k > start) {
2437 fromKey.Update(JSTaggedValue(k + actualDeleteCount - 1));
2438 toKey.Update(JSTaggedValue(k + insertCount - 1));
2439 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
2440 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2441 if (exists) {
2442 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
2443 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2444 JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValue);
2445 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2446 } else {
2447 JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey);
2448 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2449 }
2450 k--;
2451 }
2452 }
2453 // 22. Let k be actualStart.
2454 k = start;
2455 // 23. Repeat, while items is not empty
2456 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
2457 for (uint32_t i = 2; i < argc; i++) {
2458 JSHandle<JSTaggedValue> itemValue = GetCallArg(argv, i);
2459 key.Update(JSTaggedValue(k));
2460 JSArray::FastSetPropertyByValue(thread, thisObjVal, key, itemValue);
2461 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2462 k++;
2463 }
2464 // 24. Let setStatus be Set(O, "length", len – actualDeleteCount + itemCount, true).
2465 int64_t newLen = len - actualDeleteCount + insertCount;
2466 JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newLen));
2467 JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
2468 // 25. ReturnIfAbrupt(setStatus).
2469 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2470 // 26. Return A.
2471 return newArrayHandle.GetTaggedValue();
2472 }
2473
2474 // 22.1.3.26 Array.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] )
ToLocaleString(EcmaRuntimeCallInfo * argv)2475 JSTaggedValue BuiltinsArray::ToLocaleString(EcmaRuntimeCallInfo *argv)
2476 {
2477 ASSERT(argv);
2478 BUILTINS_API_TRACE(argv->GetThread(), Array, ToLocaleString);
2479 JSThread *thread = argv->GetThread();
2480 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2481 auto ecmaVm = thread->GetEcmaVM();
2482 ObjectFactory *factory = ecmaVm->GetFactory();
2483
2484 // 1. Let O be ToObject(this value).
2485 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2486 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
2487 // add this to join stack to avoid circular call
2488 auto context = thread->GetCurrentEcmaContext();
2489 bool noCircular = context->JoinStackPushFastPath(thisHandle);
2490 if (!noCircular) {
2491 return factory->GetEmptyString().GetTaggedValue();
2492 }
2493 // 2. ReturnIfAbrupt(O).
2494 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
2495 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
2496
2497 // 3. Let len be ToLength(Get(O, "length")).
2498 int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
2499 // 4. ReturnIfAbrupt(len).
2500 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
2501
2502 // 6. If len is zero, return the empty String.
2503 if (len == 0) {
2504 // pop this from join stack
2505 context->JoinStackPopFastPath(thisHandle);
2506 return GetTaggedString(thread, "");
2507 }
2508
2509 // Inject locales and options argument into a taggedArray
2510 JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
2511 JSHandle<JSTaggedValue> options = GetCallArg(argv, 1);
2512
2513 CString concatStr;
2514 // 7. Let firstElement be Get(array, "0").
2515 // 8. ReturnIfAbrupt(firstElement).
2516 // 9. If firstElement is undefined or null, then
2517 // a. Let R be the empty String.
2518 // 10. Else
2519 // a. Let R be ToString(Invoke(firstElement, "toLocaleString")).
2520 // b. ReturnIfAbrupt(R).
2521 // 11. Let k be 1.
2522 // 12. Repeat, while k < len
2523 // a. Let S be a String value produced by concatenating R and separator.
2524 // b. Let nextElement be Get(array, ToString(k)).
2525 // c. ReturnIfAbrupt(nextElement).
2526 // d. If nextElement is undefined or null, then
2527 // i. Let R be the empty String.
2528 // e. Else
2529 // i. Let R be ToString(Invoke(nextElement, "toLocaleString")).
2530 // ii. ReturnIfAbrupt(R).
2531 // f. Let R be a String value produced by concatenating S and R.
2532 // g. Increase k by 1.
2533 auto globalConst = thread->GlobalConstants();
2534 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
2535 for (int64_t k = 0; k < len; k++) {
2536 JSTaggedValue next = globalConst->GetEmptyString();
2537 JSHandle<JSTaggedValue> nextElement = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
2538 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
2539 if (!nextElement->IsUndefined() && !nextElement->IsNull()) {
2540 JSHandle<JSTaggedValue> nextValueHandle = nextElement;
2541 JSHandle<JSTaggedValue> key = globalConst->GetHandledToLocaleStringString();
2542 EcmaRuntimeCallInfo *info =
2543 EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, nextValueHandle, undefined, 2); // 2: two args
2544 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
2545 info->SetCallArg(locales.GetTaggedValue(), options.GetTaggedValue());
2546 JSTaggedValue callResult = JSFunction::Invoke(info, key);
2547 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
2548 next = callResult;
2549 }
2550 JSHandle<JSTaggedValue> nextHandle(thread, next);
2551 JSHandle<EcmaString> nextStringHandle = JSTaggedValue::ToString(thread, nextHandle);
2552 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
2553 CString nextString = ConvertToString(*nextStringHandle);
2554 if (k > 0) {
2555 concatStr += STRING_SEPERATOR;
2556 concatStr += nextString;
2557 continue;
2558 }
2559 concatStr += nextString;
2560 }
2561
2562 // pop this from join stack
2563 context->JoinStackPopFastPath(thisHandle);
2564 // 13. Return R.
2565 return factory->NewFromUtf8(concatStr).GetTaggedValue();
2566 }
2567
2568 // 22.1.3.27 Array.prototype.toString ( )
ToString(EcmaRuntimeCallInfo * argv)2569 JSTaggedValue BuiltinsArray::ToString(EcmaRuntimeCallInfo *argv)
2570 {
2571 ASSERT(argv);
2572 BUILTINS_API_TRACE(argv->GetThread(), Array, ToString);
2573 JSThread *thread = argv->GetThread();
2574 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2575 auto ecmaVm = thread->GetEcmaVM();
2576
2577 // 1. Let array be ToObject(this value).
2578 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2579 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
2580 // 2. ReturnIfAbrupt(array).
2581 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2582 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
2583
2584 // 3. Let func be Get(array, "join").
2585 JSHandle<JSTaggedValue> joinKey = thread->GlobalConstants()->GetHandledJoinString();
2586 JSHandle<JSTaggedValue> callbackFnHandle = JSTaggedValue::GetProperty(thread, thisObjVal, joinKey).GetValue();
2587
2588 // 4. ReturnIfAbrupt(func).
2589 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2590
2591 // 5. If IsCallable(func) is false, let func be the intrinsic function %ObjProto_toString% (19.1.3.6).
2592 if (!callbackFnHandle->IsCallable()) {
2593 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
2594 JSHandle<JSTaggedValue> objectPrototype = env->GetObjectFunctionPrototype();
2595 JSHandle<JSTaggedValue> toStringKey = thread->GlobalConstants()->GetHandledToStringString();
2596 callbackFnHandle = JSTaggedValue::GetProperty(thread, objectPrototype, toStringKey).GetValue();
2597 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2598 }
2599 const uint32_t argsLength = argv->GetArgsNumber();
2600 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
2601 EcmaRuntimeCallInfo *info =
2602 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisObjVal, undefined, argsLength);
2603 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2604 info->SetCallArg(argsLength, 0, argv, 0);
2605 return JSFunction::Call(info);
2606 }
2607
2608 // 22.1.3.28 Array.prototype.unshift ( ...items )
Unshift(EcmaRuntimeCallInfo * argv)2609 JSTaggedValue BuiltinsArray::Unshift(EcmaRuntimeCallInfo *argv)
2610 {
2611 ASSERT(argv);
2612 BUILTINS_API_TRACE(argv->GetThread(), Array, Unshift);
2613 JSThread *thread = argv->GetThread();
2614 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2615
2616 // 5. Let argCount be the number of actual arguments.
2617 int64_t argc = argv->GetArgsNumber();
2618
2619 // 1. Let O be ToObject(this value).
2620 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2621 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
2622 // 2. ReturnIfAbrupt(O).
2623 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2624 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
2625
2626 // 3. Let len be ToLength(Get(O, "length")).
2627 int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
2628 // 4. ReturnIfAbrupt(len).
2629 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2630
2631 // 6. If argCount > 0, then
2632 // a. If len+ argCount > 253-1, throw a TypeError exception.
2633 // b. Let k be len.
2634 // c. Repeat, while k > 0,
2635 // i. Let from be ToString(k–1).
2636 // ii. Let to be ToString(k+argCount –1).
2637 // iii. Let fromPresent be HasProperty(O, from).
2638 // iv. ReturnIfAbrupt(fromPresent).
2639 // v. If fromPresent is true, then
2640 // 1. Let fromValue be Get(O, from).
2641 // 2. ReturnIfAbrupt(fromValue).
2642 // 3. Let setStatus be Set(O, to, fromValue, true).
2643 // 4. ReturnIfAbrupt(setStatus).
2644 // vi. Else fromPresent is false,
2645 // 1. Let deleteStatus be DeletePropertyOrThrow(O, to).
2646 // 2. ReturnIfAbrupt(deleteStatus).
2647 // vii. Decrease k by 1.
2648 if (argc > 0) {
2649 if (len + argc > base::MAX_SAFE_INTEGER) {
2650 THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
2651 }
2652 JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
2653 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
2654 int64_t k = len;
2655 while (k > 0) {
2656 fromKey.Update(JSTaggedValue(k - 1));
2657 toKey.Update(JSTaggedValue(k + argc - 1));
2658 bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
2659 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2660 if (exists) {
2661 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
2662 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2663 JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValue);
2664 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2665 } else {
2666 JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey);
2667 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2668 }
2669 k--;
2670 }
2671 // d. Let j be 0.
2672 // e. Let items be a List whose elements are, in left to right order, the arguments that were passed to this
2673 // function invocation.
2674 // f. Repeat, while items is not empty
2675 // i. Remove the first element from items and let E be the value of that element.
2676 // ii. Let setStatus be Set(O, ToString(j), E, true).
2677 // iii. ReturnIfAbrupt(setStatus).
2678 // iv. Increase j by 1.
2679 int64_t j = 0;
2680 while (j < argc) {
2681 toKey.Update(JSTaggedValue(j));
2682 JSHandle<JSTaggedValue> toValue = GetCallArg(argv, j);
2683 JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, toValue);
2684 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2685 j++;
2686 }
2687 }
2688
2689 // 7. Let setStatus be Set(O, "length", len+argCount, true).
2690 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
2691 int64_t newLen = len + argc;
2692 JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newLen));
2693 JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
2694 // 8. ReturnIfAbrupt(setStatus).
2695 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2696
2697 // 9. Return len+argCount.
2698 return GetTaggedDouble(newLen);
2699 }
2700
2701 // 22.1.3.29 Array.prototype.values ( )
Values(EcmaRuntimeCallInfo * argv)2702 JSTaggedValue BuiltinsArray::Values(EcmaRuntimeCallInfo *argv)
2703 {
2704 ASSERT(argv);
2705 BUILTINS_API_TRACE(argv->GetThread(), Array, Values);
2706 JSThread *thread = argv->GetThread();
2707 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2708 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2709 // 1. Let O be ToObject(this value).
2710 // 2. ReturnIfAbrupt(O).
2711 JSHandle<JSObject> self = JSTaggedValue::ToObject(thread, GetThis(argv));
2712 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2713 // 3. Return CreateArrayIterator(O, "value").
2714 JSHandle<JSArrayIterator> iter(factory->NewJSArrayIterator(self, IterationKind::VALUE));
2715 return iter.GetTaggedValue();
2716 }
2717 // 22.1.3.31 Array.prototype [ @@unscopables ]
Unscopables(EcmaRuntimeCallInfo * argv)2718 JSTaggedValue BuiltinsArray::Unscopables(EcmaRuntimeCallInfo *argv)
2719 {
2720 JSThread *thread = argv->GetThread();
2721 BUILTINS_API_TRACE(thread, Array, Unscopables);
2722 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2723 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2724 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
2725
2726 JSHandle<JSObject> unscopableList = factory->CreateNullJSObject();
2727
2728 JSHandle<JSTaggedValue> trueVal(thread, JSTaggedValue::True());
2729
2730 JSHandle<JSTaggedValue> atKey((factory->NewFromASCII("at")));
2731 JSObject::CreateDataProperty(thread, unscopableList, atKey, trueVal);
2732
2733 JSHandle<JSTaggedValue> copyWithKey = globalConst->GetHandledCopyWithinString();
2734 JSObject::CreateDataProperty(thread, unscopableList, copyWithKey, trueVal);
2735
2736 JSHandle<JSTaggedValue> entriesKey = globalConst->GetHandledEntriesString();
2737 JSObject::CreateDataProperty(thread, unscopableList, entriesKey, trueVal);
2738
2739 JSHandle<JSTaggedValue> fillKey = globalConst->GetHandledFillString();
2740 JSObject::CreateDataProperty(thread, unscopableList, fillKey, trueVal);
2741
2742 JSHandle<JSTaggedValue> findKey = globalConst->GetHandledFindString();
2743 JSObject::CreateDataProperty(thread, unscopableList, findKey, trueVal);
2744
2745 JSHandle<JSTaggedValue> findIndexKey = globalConst->GetHandledFindIndexString();
2746 JSObject::CreateDataProperty(thread, unscopableList, findIndexKey, trueVal);
2747
2748 JSHandle<JSTaggedValue> findLastKey((factory->NewFromASCII("findLast")));
2749 JSObject::CreateDataProperty(thread, unscopableList, findLastKey, trueVal);
2750
2751 JSHandle<JSTaggedValue> findLastIndexKey((factory->NewFromASCII("findLastIndex")));
2752 JSObject::CreateDataProperty(thread, unscopableList, findLastIndexKey, trueVal);
2753
2754 JSHandle<JSTaggedValue> flatKey = globalConst->GetHandledFlatString();
2755 JSObject::CreateDataProperty(thread, unscopableList, flatKey, trueVal);
2756
2757 JSHandle<JSTaggedValue> flatMapKey = globalConst->GetHandledFlatMapString();
2758 JSObject::CreateDataProperty(thread, unscopableList, flatMapKey, trueVal);
2759
2760 JSHandle<JSTaggedValue> includesKey = globalConst->GetHandledIncludesString();
2761 JSObject::CreateDataProperty(thread, unscopableList, includesKey, trueVal);
2762
2763 JSHandle<JSTaggedValue> keysKey = globalConst->GetHandledKeysString();
2764 JSObject::CreateDataProperty(thread, unscopableList, keysKey, trueVal);
2765
2766 JSHandle<JSTaggedValue> valuesKey = globalConst->GetHandledValuesString();
2767 JSObject::CreateDataProperty(thread, unscopableList, valuesKey, trueVal);
2768
2769 JSHandle<JSTaggedValue> toReversedKey((factory->NewFromASCII("toReversed")));
2770 JSObject::CreateDataProperty(thread, unscopableList, toReversedKey, trueVal);
2771
2772 JSHandle<JSTaggedValue> toSortedKey((factory->NewFromASCII("toSorted")));
2773 JSObject::CreateDataProperty(thread, unscopableList, toSortedKey, trueVal);
2774
2775 JSHandle<JSTaggedValue> toSplicedKey((factory->NewFromASCII("toSpliced")));
2776 JSObject::CreateDataProperty(thread, unscopableList, toSplicedKey, trueVal);
2777 return unscopableList.GetTaggedValue();
2778 }
2779
2780 // es12 23.1.3.10
Flat(EcmaRuntimeCallInfo * argv)2781 JSTaggedValue BuiltinsArray::Flat(EcmaRuntimeCallInfo *argv)
2782 {
2783 ASSERT(argv);
2784 BUILTINS_API_TRACE(argv->GetThread(), Array, Flat);
2785 JSThread *thread = argv->GetThread();
2786 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2787
2788 // 1. Let O be ? ToObject(this value).
2789 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2790 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
2791 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2792
2793 uint32_t argc = argv->GetArgsNumber();
2794 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
2795
2796 // 2. Let sourceLen be ? LengthOfArrayLike(O).
2797 int64_t sourceLen = ArrayHelper::GetLength(thread, thisObjVal);
2798 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2799
2800 // 3. Let depthNum be 1.
2801 double depthNum = 1;
2802
2803 // 4. If depth is not undefined, then
2804 // a. Set depthNum to ? ToIntegerOrInfinity(depth).
2805 // b. If depthNum < 0, set depthNum to 0.
2806 if (argc > 0) {
2807 JSHandle<JSTaggedValue> msg1 = GetCallArg(argv, 0);
2808 if (!msg1->IsUndefined()) {
2809 JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, msg1);
2810 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2811 depthNum = base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber());
2812 depthNum = depthNum < 0 ? 0 : depthNum;
2813 }
2814 }
2815 // 5. Let A be ? ArraySpeciesCreate(O, 0).
2816 uint32_t arrayLen = 0;
2817 JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(arrayLen));
2818 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2819
2820 base::FlattenArgs args = { sourceLen, 0, depthNum };
2821 JSHandle<JSObject> newArrayHandle(thread, newArray);
2822 // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum).
2823 ArrayHelper::FlattenIntoArray(thread, newArrayHandle, thisObjVal, args,
2824 thread->GlobalConstants()->GetHandledUndefined(),
2825 thread->GlobalConstants()->GetHandledUndefined());
2826 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2827
2828 // 7. Return A.
2829 return newArrayHandle.GetTaggedValue();
2830 }
2831
2832 // es12 23.1.3.11
FlatMap(EcmaRuntimeCallInfo * argv)2833 JSTaggedValue BuiltinsArray::FlatMap(EcmaRuntimeCallInfo *argv)
2834 {
2835 ASSERT(argv);
2836 BUILTINS_API_TRACE(argv->GetThread(), Array, FlatMap);
2837 JSThread *thread = argv->GetThread();
2838 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2839
2840 // 1. Let O be ? ToObject(this value).
2841 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2842 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
2843 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2844 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
2845
2846 // 2. Let sourceLen be ? LengthOfArrayLike(O).
2847 int64_t sourceLen = ArrayHelper::GetLength(thread, thisObjVal);
2848 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2849
2850 // 3. If ! IsCallable(mapperFunction) is false, throw a TypeError exception.
2851 JSHandle<JSTaggedValue> mapperFunctionHandle = GetCallArg(argv, 0);
2852 if (!mapperFunctionHandle->IsCallable()) {
2853 THROW_TYPE_ERROR_AND_RETURN(thread, "the mapperFunction is not callable.", JSTaggedValue::Exception());
2854 }
2855 // 4. Let A be ? ArraySpeciesCreate(O, 0).
2856 uint32_t arrayLen = 0;
2857 JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(arrayLen));
2858 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2859
2860 base::FlattenArgs args = { sourceLen, 0, 1 };
2861 JSHandle<JSObject> newArrayHandle(thread, newArray);
2862 // 5. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, thisArg).
2863 ArrayHelper::FlattenIntoArray(thread, newArrayHandle, thisObjVal, args,
2864 mapperFunctionHandle, GetCallArg(argv, 1));
2865 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2866 // 6. Return A.
2867 return newArrayHandle.GetTaggedValue();
2868 }
2869
2870 // 23.1.3.13 Array.prototype.includes ( searchElement [ , fromIndex ] )
Includes(EcmaRuntimeCallInfo * argv)2871 JSTaggedValue BuiltinsArray::Includes(EcmaRuntimeCallInfo *argv)
2872 {
2873 ASSERT(argv);
2874 BUILTINS_API_TRACE(argv->GetThread(), Array, Includes);
2875 JSThread *thread = argv->GetThread();
2876 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2877 // 1. Let O be ? ToObject(this value).
2878 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2879 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
2880 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2881
2882 uint32_t argc = argv->GetArgsNumber();
2883 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
2884 JSHandle<JSTaggedValue> searchElement = GetCallArg(argv, 0);
2885
2886 // 2. Let len be ? LengthOfArrayLike(O).
2887 int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
2888 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2889 // 3. If len is 0, return false.
2890 if (len == 0) {
2891 return GetTaggedBoolean(false);
2892 }
2893 // 4. Let n be ? ToIntegerOrInfinity(fromIndex).
2894 // 5. Assert: If fromIndex is undefined, then n is 0.
2895 double fromIndex = 0;
2896 if (argc > 1) {
2897 JSHandle<JSTaggedValue> msg1 = GetCallArg(argv, 1);
2898 JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, msg1);
2899 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2900 fromIndex = base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber());
2901 }
2902
2903 // 6. If n is +∞, return false.
2904 // 7. Else if n is -∞, set n to 0.
2905 if (fromIndex >= len) {
2906 return GetTaggedBoolean(false);
2907 } else if (fromIndex < -len) {
2908 fromIndex = 0;
2909 }
2910 // 8. If n ≥ 0, then
2911 // a. Let k be n.
2912 // 9. Else,
2913 // a. Let k be len + n.
2914 // b. If k < 0, let k be 0.
2915 int64_t from = (fromIndex >= 0) ? fromIndex : ((len + fromIndex) >= 0 ? len + fromIndex : 0);
2916
2917 // 10. Repeat, while k < len,
2918 // a. Let elementK be ? Get(O, ! ToString(!(k))).
2919 // b. If SameValueZero(searchElement, elementK) is true, return true.
2920 // c. Set k to k + 1.
2921 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
2922 JSMutableHandle<JSTaggedValue> kValueHandle(thread, JSTaggedValue::Undefined());
2923 JSHandle<EcmaString> fromStr;
2924 while (from < len) {
2925 JSHandle<JSTaggedValue> handledFrom(thread, JSTaggedValue(from));
2926 fromStr = JSTaggedValue::ToString(thread, handledFrom);
2927 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2928 key.Update(fromStr.GetTaggedValue());
2929 kValueHandle.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, key).GetTaggedValue());
2930 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2931 if (JSTaggedValue::SameValueZero(searchElement.GetTaggedValue(), kValueHandle.GetTaggedValue())) {
2932 return GetTaggedBoolean(true);
2933 }
2934 from++;
2935 }
2936 // 11. Return false.
2937 return GetTaggedBoolean(false);
2938 }
2939
2940 // 23.1.3.1 Array.prototype.at ( index )
At(EcmaRuntimeCallInfo * argv)2941 JSTaggedValue BuiltinsArray::At(EcmaRuntimeCallInfo *argv)
2942 {
2943 ASSERT(argv);
2944 BUILTINS_API_TRACE(argv->GetThread(), Array, At);
2945 JSThread *thread = argv->GetThread();
2946 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2947
2948 // 1. Let O be ToObject(this value).
2949 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2950 if (thisHandle->IsStableJSArray(thread)) {
2951 return JSStableArray::At(JSHandle<JSArray>::Cast(thisHandle), argv);
2952 }
2953 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
2954 // ReturnIfAbrupt(O).
2955 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2956 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
2957
2958 // 2. Let len be ? LengthOfArrayLike(O).
2959 int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
2960 // ReturnIfAbrupt(len).
2961 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2962
2963 // 3. Let index be ? ToIntegerOrInfinity(index).
2964 JSTaggedNumber index = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0));
2965 // ReturnIfAbrupt(index).
2966 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2967
2968 // 4. If relativeIndex ≥ 0, then
2969 // a. Let k be relativeIndex.
2970 // 5. Else,
2971 // a. Let k be len + relativeIndex.
2972 int64_t relativeIndex = index.GetNumber();
2973 int64_t k = 0;
2974 if (relativeIndex >= 0) {
2975 k = relativeIndex;
2976 } else {
2977 k = len + relativeIndex;
2978 }
2979
2980 // 6. If k < 0 or k ≥ len, return undefined.
2981 if (k < 0 || k >= len) {
2982 // Return undefined.
2983 return JSTaggedValue::Undefined();
2984 }
2985 // 7. Return ? Get(O, ! ToString(��(k))).
2986 JSHandle<JSTaggedValue> element = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
2987 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2988 return element.GetTaggedValue();
2989 }
2990
2991 // 23.1.3.39 Array.prototype.with ( index, value )
2992 // NOLINTNEXTLINE(readability-function-size)
With(EcmaRuntimeCallInfo * argv)2993 JSTaggedValue BuiltinsArray::With(EcmaRuntimeCallInfo *argv)
2994 {
2995 ASSERT(argv);
2996 JSThread *thread = argv->GetThread();
2997 BUILTINS_API_TRACE(thread, Array, With);
2998 [[maybe_unused]] EcmaHandleScope handleScope(thread);
2999
3000 // 1. Let O be ToObject(this value).
3001 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
3002 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
3003 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3004 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
3005 // 2. Let len be ? LengthOfArrayLike(O).
3006 int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
3007 // ReturnIfAbrupt(len).
3008 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3009 // 3. Let relativeIndex be ? ToIntegerOrInfinity(relativeIndex).
3010 JSTaggedNumber index = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0));
3011 // ReturnIfAbrupt(index).
3012 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3013 int64_t relativeIndex = index.GetNumber();
3014 int64_t actualIndex = 0;
3015 JSHandle<JSTaggedValue> value = GetCallArg(argv, 1);
3016 // 4. If relativeIndex ≥ 0, let actualIndex be relativeIndex.
3017 // 5. Else, let actualIndex be len + relativeIndex.
3018 // 6. If actualIndex ≥ len or actualIndex < 0, throw a RangeError exception.
3019 if (relativeIndex >= 0) {
3020 actualIndex = relativeIndex;
3021 } else {
3022 actualIndex = len + relativeIndex;
3023 }
3024 if (actualIndex >= len || actualIndex < 0) {
3025 THROW_RANGE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
3026 }
3027 // 7. Let A be ? ArrayCreate(len).
3028 JSTaggedValue newArray =
3029 JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast<double>(len))).GetTaggedValue();
3030 // ReturnIfAbrupt(A).
3031 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3032 JSHandle<JSObject> newArrayHandle(thread, newArray);
3033 if (thisHandle->IsStableJSArray(thread) && !thisObjHandle->GetJSHClass()->HasConstructor()) {
3034 return JSStableArray::With(thread, JSHandle<JSArray>::Cast(thisHandle), len, actualIndex, value);
3035 }
3036 // 8. Let k be 0.
3037 int64_t k = 0;
3038 // 9. Repeat, while k < len,
3039 // a. Let Pk be ! ToString(��(k)).
3040 // b. If k is actualIndex, let fromValue be value.
3041 // c. Else, let fromValue be ? Get(O, Pk).
3042 // d. Perform ! CreateDataPropertyOrThrow(A, Pk, fromValue).
3043 // e. Set k to k + 1.
3044 JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
3045 JSHandle<JSTaggedValue> fromValue;
3046 while (k < len) {
3047 fromKey.Update(JSTaggedValue(k));
3048 if (k == actualIndex) {
3049 fromValue = value;
3050 } else {
3051 fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
3052 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3053 }
3054 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, fromKey, fromValue);
3055 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3056 ++k;
3057 }
3058 // 10. Return A.
3059 return newArrayHandle.GetTaggedValue();
3060 }
3061
3062 // 23.1.3.34 Array.prototype.toSorted ( comparefn )
ToSorted(EcmaRuntimeCallInfo * argv)3063 JSTaggedValue BuiltinsArray::ToSorted(EcmaRuntimeCallInfo *argv)
3064 {
3065 ASSERT(argv);
3066 JSThread *thread = argv->GetThread();
3067 BUILTINS_API_TRACE(thread, Array, ToSorted);
3068 [[maybe_unused]] EcmaHandleScope handleScope(thread);
3069
3070 // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception.
3071 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
3072 if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable()) {
3073 THROW_TYPE_ERROR_AND_RETURN(thread, "Callable is false", JSTaggedValue::Exception());
3074 }
3075
3076 // 2. Let obj be ToObject(this value).
3077 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
3078 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
3079 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3080
3081 // 3. Let len be ToLength(Get(obj, "length")).
3082 int64_t len = ArrayHelper::GetArrayLength(thread, JSHandle<JSTaggedValue>(thisObjHandle));
3083 // ReturnIfAbrupt(len).
3084 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3085
3086 // 4. Let A be ? ArrayCreate(len).
3087 JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast<double>(len))).GetTaggedValue();
3088 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3089 JSHandle<JSObject> newArrayHandle(thread, newArray);
3090
3091 // 5. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs
3092 // the following steps when called:
3093 // a. Return ? CompareArrayElements(x, y, comparefn).
3094 // 6. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, read-through-holes).
3095 JSHandle<TaggedArray> sortedList =
3096 ArrayHelper::SortIndexedProperties(thread, JSHandle<JSTaggedValue>::Cast(thisObjHandle), len, callbackFnHandle,
3097 base::HolesType::READ_THROUGH_HOLES);
3098 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3099
3100 //7. Let j be 0.
3101 int64_t j = 0;
3102 // 8. Repeat, while j < len,
3103 // a. Perform ! CreateDataPropertyOrThrow(A, ! ToString(��(j)), sortedList[j]).
3104 // b. Set j to j + 1.
3105 JSMutableHandle<JSTaggedValue> itemValue(thread, JSTaggedValue::Undefined());
3106 while (j < len) {
3107 itemValue.Update(sortedList->Get(j));
3108 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, j, itemValue);
3109 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3110 ++j;
3111 }
3112 // 9. Return A.
3113 return newArrayHandle.GetTaggedValue();
3114 }
3115
3116 // 23.1.3.35 Array.prototype.toSpliced ( start, skipCount, ...items )
ToSpliced(EcmaRuntimeCallInfo * argv)3117 JSTaggedValue BuiltinsArray::ToSpliced(EcmaRuntimeCallInfo *argv)
3118 {
3119 ASSERT(argv);
3120 JSThread *thread = argv->GetThread();
3121 BUILTINS_API_TRACE(thread, Array, ToSpliced);
3122 [[maybe_unused]] EcmaHandleScope handleScope(thread);
3123 uint32_t argc = argv->GetArgsNumber();
3124 // 1. Let O be ? ToObject(this value).
3125 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
3126 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
3127 // ReturnIfAbrupt(O).
3128 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3129 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
3130 // 2. Let len be ? LengthOfArrayLike(O).
3131 int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
3132 // ReturnIfAbrupt(len).
3133 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3134 int64_t actualStart = 0;
3135 int64_t actualSkipCount = 0;
3136 int64_t newLen = 0;
3137 int64_t insertCount = 0;
3138 // 3. Let relativeStart be ? ToIntegerOrInfinity(start).
3139 if (argc > 0) {
3140 JSTaggedNumber argStart = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0));
3141 // ReturnIfAbrupt(relativeStart).
3142 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3143 double relativeStart = argStart.GetNumber();
3144 // 4. If relativeStart = -∞, let k be 0.
3145 // 5. Else if relativeStart < 0, let k be max(len + relativeStart, 0).
3146 // 6. Else, let k be min(relativeStart, len).
3147 if (relativeStart < 0) {
3148 double tempStart = relativeStart + len;
3149 actualStart = tempStart > 0 ? tempStart : 0;
3150 } else {
3151 actualStart = relativeStart < len ? relativeStart : len;
3152 }
3153 actualSkipCount = len - actualStart;
3154 }
3155 // 7. Let insertCount be the number of elements in items.
3156 // 8. If start is not present, then
3157 // a. Let actualSkipCount be 0.
3158 // 9. Else if skipCount is not present, then
3159 // a. Let actualSkipCount be len - actualStart.
3160 // 10. Else,
3161 // a. Let sc be ? ToIntegerOrInfinity(skipCount).
3162 // b. Let actualSkipCount be the result of clamping sc between 0 and len - actualStart.
3163 if (argc > 1) {
3164 insertCount = argc - 2; // 2:2 means there two arguments before the insert items.
3165 JSTaggedNumber argSkipCount = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 1));
3166 // ReturnIfAbrupt(argSkipCount).
3167 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3168 double skipCount = argSkipCount.GetNumber();
3169 skipCount = skipCount > 0 ? skipCount : 0;
3170 actualSkipCount = skipCount < (len - actualStart) ? skipCount : len - actualStart;
3171 }
3172 // 11. Let newLen be len + insertCount - actualSkipCount.
3173 newLen = len + insertCount - actualSkipCount;
3174 // 12. If newLen > 2^53 - 1, throw a TypeError exception.
3175 if (newLen > base::MAX_SAFE_INTEGER) {
3176 THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
3177 }
3178 if (thisHandle->IsStableJSArray(thread) && !thisObjHandle->GetJSHClass()->HasConstructor()) {
3179 return JSStableArray::ToSpliced(JSHandle<JSArray>::Cast(thisHandle), argv, argc, actualStart,
3180 actualSkipCount, newLen);
3181 }
3182 // 13. Let A be ? ArrayCreate(newLen).
3183 JSHandle<JSTaggedValue> newJsTaggedArray =
3184 JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast<double>(newLen)));
3185 // ReturnIfAbrupt(newArray).
3186 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3187 JSHandle<JSObject> newArrayHandle(thread, newJsTaggedArray.GetTaggedValue());
3188 // 14. Let i be 0.
3189 int64_t i = 0;
3190 // 15. Let r be actualStart + actualSkipCount.
3191 int64_t r = actualStart + actualSkipCount;
3192 // 16. Repeat, while i < actualStart,
3193 // a. Let Pi be ! ToString(��(i)).
3194 // b. Let iValue be ? Get(O, Pi).
3195 // c. Perform ! CreateDataPropertyOrThrow(A, Pi, iValue).
3196 // d. Set i to i + 1.
3197 while (i < actualStart) {
3198 JSHandle<JSTaggedValue> iValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, i);
3199 // ReturnIfAbrupt(iValue).
3200 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3201 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, i, iValue);
3202 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3203 ++i;
3204 }
3205 // 17. For each element E of items, do
3206 // a. Let Pi be ! ToString(��(i)).
3207 // b. Perform ! CreateDataPropertyOrThrow(A, Pi, E).
3208 // c. Set i to i + 1.
3209 JSMutableHandle<JSTaggedValue> pi(thread, JSTaggedValue::Undefined());
3210 for (int64_t pos = 2; pos < argc; ++pos) { // 2:2 means there two arguments before the insert items.
3211 pi.Update(JSTaggedValue(i));
3212 JSHandle<JSTaggedValue> element = GetCallArg(argv, pos);
3213 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, pi, element);
3214 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3215 ++i;
3216 }
3217 // 18. Repeat, while i < newLen,
3218 // a. Let Pi be ! ToString(��(i)).
3219 // b. Let from be ! ToString(��(r)).
3220 // c. Let fromValue be ? Get(O, from).
3221 // d. Perform ! CreateDataPropertyOrThrow(A, Pi, fromValue).
3222 // e. Set i to i + 1.
3223 // f. Set r to r + 1.
3224 JSMutableHandle<JSTaggedValue> from(thread, JSTaggedValue::Undefined());
3225 while (i < newLen) {
3226 pi.Update(JSTaggedValue(i));
3227 from.Update(JSTaggedValue(r));
3228 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, from);
3229 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3230 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, pi, fromValue);
3231 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3232 ++i;
3233 ++r;
3234 }
3235 JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
3236 JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newLen));
3237 JSTaggedValue::SetProperty(thread, newJsTaggedArray, lengthKey, newLenHandle, true);
3238 // ReturnIfAbrupt(setStatus).
3239 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3240 // 19. Return A.
3241 return newArrayHandle.GetTaggedValue();
3242 }
3243
3244 // 23.1.3.11 Array.prototype.findLast ( predicate [ , thisArg ] )
FindLast(EcmaRuntimeCallInfo * argv)3245 JSTaggedValue BuiltinsArray::FindLast(EcmaRuntimeCallInfo *argv)
3246 {
3247 ASSERT(argv);
3248 JSThread *thread = argv->GetThread();
3249 BUILTINS_API_TRACE(thread, Array, FindLast);
3250 [[maybe_unused]] EcmaHandleScope handleScope(thread);
3251
3252 // 1. Let O be ToObject(this value).
3253 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
3254 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
3255 // 2. ReturnIfAbrupt(O).
3256 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3257 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
3258
3259 // 3. Let len be ToLength(Get(O, "length")).
3260 int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
3261 // 4. ReturnIfAbrupt(len).
3262 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3263
3264 // 5. If IsCallable(predicate) is false, throw a TypeError exception.
3265 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
3266 if (!callbackFnHandle->IsCallable()) {
3267 THROW_TYPE_ERROR_AND_RETURN(thread, "the predicate is not callable.", JSTaggedValue::Exception());
3268 }
3269
3270 // 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
3271 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
3272
3273 // 7. Let k be (len - 1).
3274 // 8. Repeat, while k >= 0
3275 // a. Let Pk be ToString(k).
3276 // b. Let kValue be Get(O, Pk).
3277 // c. ReturnIfAbrupt(kValue).
3278 // d. Let testResult be ToBoolean(Call(predicate, T, «kValue, k, O»)).
3279 // e. ReturnIfAbrupt(testResult).
3280 // f. If testResult is true, return kValue.
3281 // g. Decrease k by 1.
3282 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
3283 int64_t k = len - 1;
3284 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
3285 const uint32_t argsLength = 3; // 3: «kValue, k, O»
3286 while (k >= 0) {
3287 JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
3288 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3289 key.Update(JSTaggedValue(k));
3290 EcmaRuntimeCallInfo *info =
3291 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
3292 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3293 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
3294 JSTaggedValue callResult = JSFunction::Call(info);
3295 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3296 if (callResult.ToBoolean()) {
3297 return kValue.GetTaggedValue();
3298 }
3299 k--;
3300 }
3301
3302 // 9. Return undefined.
3303 return JSTaggedValue::Undefined();
3304 }
3305
3306 // 23.1.3.12 Array.prototype.findLastIndex ( predicate [ , thisArg ] )
FindLastIndex(EcmaRuntimeCallInfo * argv)3307 JSTaggedValue BuiltinsArray::FindLastIndex(EcmaRuntimeCallInfo *argv)
3308 {
3309 ASSERT(argv);
3310 JSThread *thread = argv->GetThread();
3311 BUILTINS_API_TRACE(thread, Array, FindLastIndex);
3312 [[maybe_unused]] EcmaHandleScope handleScope(thread);
3313
3314 // 1. Let O be ToObject(this value).
3315 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
3316 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
3317 // 2. ReturnIfAbrupt(O).
3318 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3319 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
3320
3321 // 3. Let len be ToLength(Get(O, "length")).
3322 int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
3323 // 4. ReturnIfAbrupt(len).
3324 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3325
3326 // 5. If IsCallable(predicate) is false, throw a TypeError exception.
3327 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
3328 if (!callbackFnHandle->IsCallable()) {
3329 THROW_TYPE_ERROR_AND_RETURN(thread, "the predicate is not callable.", JSTaggedValue::Exception());
3330 }
3331
3332 // 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
3333 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
3334
3335 // 7. Let k be (len - 1).
3336 // 8. Repeat, while k >=0
3337 // a. Let Pk be ToString(k).
3338 // b. Let kValue be Get(O, Pk).
3339 // c. ReturnIfAbrupt(kValue).
3340 // d. Let testResult be ToBoolean(Call(predicate, T, «kValue, k, O»)).
3341 // e. ReturnIfAbrupt(testResult).
3342 // f. If testResult is true, return k.
3343 // g. Decrease k by 1.
3344 int64_t k = len - 1;
3345 JSTaggedValue callResult = GetTaggedBoolean(true);
3346 if (thisObjVal->IsStableJSArray(thread)) {
3347 callResult =
3348 JSStableArray::HandleFindLastIndexOfStable(thread, thisObjHandle, callbackFnHandle, thisArgHandle, k);
3349 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3350 if (callResult.ToBoolean()) {
3351 return GetTaggedDouble(k);
3352 }
3353 }
3354 JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
3355 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
3356 const uint32_t argsLength = 3; // 3: «kValue, k, O»
3357 while (k >= 0) {
3358 JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
3359 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3360 key.Update(JSTaggedValue(k));
3361 EcmaRuntimeCallInfo *info =
3362 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
3363 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3364 info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
3365 callResult = JSFunction::Call(info);
3366 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3367 if (callResult.ToBoolean()) {
3368 return GetTaggedDouble(k);
3369 }
3370 k--;
3371 }
3372
3373 // 9. Return -1.
3374 return GetTaggedDouble(-1);
3375 }
3376
3377 // 23.1.3.33 Array.prototype.toReversed ( )
ToReversed(EcmaRuntimeCallInfo * argv)3378 JSTaggedValue BuiltinsArray::ToReversed(EcmaRuntimeCallInfo *argv)
3379 {
3380 ASSERT(argv);
3381 JSThread *thread = argv->GetThread();
3382 BUILTINS_API_TRACE(thread, Array, ToReversed);
3383 [[maybe_unused]] EcmaHandleScope handleScope(thread);
3384
3385 // 1. Let O be ToObject(this value).
3386 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
3387 JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
3388 // ReturnIfAbrupt(O).
3389 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3390 JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
3391
3392 // 2. Let len be ? LengthOfArrayLike(O).
3393 int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
3394 // ReturnIfAbrupt(len).
3395 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3396 if (thisHandle->IsStableJSArray(thread) && !thisObjHandle->GetJSHClass()->HasConstructor()) {
3397 return JSStableArray::ToReversed(thread, JSHandle<JSArray>::Cast(thisHandle), len);
3398 }
3399 // 3. Let A be ? ArrayCreate(len).
3400 JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast<double>(len))).GetTaggedValue();
3401 // ReturnIfAbrupt(len).
3402 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3403 JSHandle<JSObject> newArrayHandle(thread, newArray);
3404
3405 // 4. Let k be 0.
3406 // 5. Repeat, while k < len,
3407 // a. Let from be ! ToString(��(len - k - 1)).
3408 // b. Let Pk be ! ToString(��(k)).
3409 // c. Let fromValue be ? Get(O, from).
3410 // d. Perform ! CreateDataPropertyOrThrow(A, Pk, fromValue).
3411 // e. Set k to k + 1.
3412 JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
3413 JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
3414 int64_t k = 0;
3415 while (k < len) {
3416 int64_t from = len - k - 1;
3417 fromKey.Update(JSTaggedValue(from));
3418 toKey.Update(JSTaggedValue(k));
3419 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
3420 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3421 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue);
3422 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
3423 k++;
3424 }
3425 // 6. Return A.
3426 return newArrayHandle.GetTaggedValue();
3427 }
3428 } // namespace panda::ecmascript::builtins
3429