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