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