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