• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/js_stable_array.h"
17 #include "ecmascript/base/config.h"
18 #include "ecmascript/base/sort_helper.h"
19 #include "ecmascript/base/typed_array_helper-inl.h"
20 #include "ecmascript/interpreter/fast_runtime_stub-inl.h"
21 
22 namespace panda::ecmascript {
23 using TypedArrayHelper = base::TypedArrayHelper;
24 using TypedArrayKind = base::TypedArrayKind;
25 using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer;
26 using BuiltinsSendableArrayBuffer = builtins::BuiltinsSendableArrayBuffer;
27 template<TypedArrayKind typedArrayKind>
28 using BuiltinsArrayBufferType = base::BuiltinsArrayBufferType<typedArrayKind>;
29 
Push(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)30 JSTaggedValue JSStableArray::Push(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
31 {
32     JSThread *thread = argv->GetThread();
33     uint32_t argc = argv->GetArgsNumber();
34     uint32_t oldLength = receiver->GetArrayLength();
35     uint32_t newLength = argc + oldLength;
36     JSHandle<JSObject> thisObjHandle(receiver);
37 
38     if (newLength > ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
39         JSObject::GrowElementsCapacity(thread, JSHandle<JSObject>::Cast(receiver), newLength, true);
40     }
41     bool needTransition = true;
42     for (uint32_t k = 0; k < argc; k++) {
43         JSHandle<JSTaggedValue> value = argv->GetCallArg(k);
44         ElementAccessor::Set(thread, thisObjHandle, oldLength + k, value, needTransition);
45     }
46     receiver->SetArrayLength(thread, newLength);
47 
48     return JSTaggedValue(newLength);
49 }
50 
Push(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)51 JSTaggedValue JSStableArray::Push(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
52 {
53     JSThread *thread = argv->GetThread();
54     uint32_t argc = argv->GetArgsNumber();
55     uint32_t oldLength = receiver->GetArrayLength();
56     uint32_t newLength = argc + oldLength;
57     JSHandle<JSObject> thisObjHandle(receiver);
58 
59     if (newLength > ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
60         JSObject::GrowElementsCapacity(thread, JSHandle<JSObject>::Cast(receiver), newLength, true);
61     }
62     bool needTransition = true;
63     for (uint32_t k = 0; k < argc; k++) {
64         JSHandle<JSTaggedValue> value = argv->GetCallArg(k);
65         ElementAccessor::Set(thread, thisObjHandle, oldLength + k, value, needTransition);
66     }
67     receiver->SetArrayLength(thread, newLength);
68 
69     return JSTaggedValue(newLength);
70 }
71 
Pop(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)72 JSTaggedValue JSStableArray::Pop(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
73 {
74     JSThread *thread = argv->GetThread();
75     uint32_t length = receiver->GetArrayLength();
76     if (length == 0) {
77         return JSTaggedValue::Undefined();
78     }
79     JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
80     JSSharedArray::CheckAndCopyArray(thread, receiver);
81     JSHandle<JSObject> obj(receiver);
82     uint32_t capacity = ElementAccessor::GetElementsLength(thread, obj);
83     uint32_t index = length - 1;
84     JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Hole());
85     if (index < capacity) {
86         result.Update(ElementAccessor::Get(thread, obj, index));
87     }
88     if (!result->IsHole()) {
89         if (TaggedArray::ShouldTrim(capacity, index)) {
90             TaggedArray *elements = TaggedArray::Cast(receiver->GetElements(thread).GetTaggedObject());
91             elements->Trim(thread, index);
92         } else {
93             ElementAccessor::Set(thread, obj, index, holeHandle, false);
94         }
95     } else {
96         JSHandle<JSTaggedValue> thisObjVal(receiver);
97         result.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, index).GetTaggedValue());
98     }
99     receiver->SetArrayLength(thread, index);
100     return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
101 }
102 
Pop(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)103 JSTaggedValue JSStableArray::Pop(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
104 {
105     JSThread *thread = argv->GetThread();
106     uint32_t length = receiver->GetArrayLength();
107     if (length == 0) {
108         return JSTaggedValue::Undefined();
109     }
110     JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
111     JSArray::CheckAndCopyArray(thread, receiver);
112     JSHandle<JSObject> obj(receiver);
113     uint32_t capacity = ElementAccessor::GetElementsLength(thread, obj);
114     uint32_t index = length - 1;
115     JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Hole());
116     if (index < capacity) {
117         result.Update(ElementAccessor::Get(thread, obj, index));
118     }
119     if (!result->IsHole()) {
120         if (TaggedArray::ShouldTrim(capacity, index)) {
121             TaggedArray *elements = TaggedArray::Cast(receiver->GetElements(thread).GetTaggedObject());
122             elements->Trim(thread, index);
123         } else {
124             ElementAccessor::Set(thread, obj, index, holeHandle, false);
125         }
126     } else {
127         JSHandle<JSTaggedValue> thisObjVal(receiver);
128         result.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, index).GetTaggedValue());
129     }
130     receiver->SetArrayLength(thread, index);
131     return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
132 }
133 
HandleArray(JSHandle<JSObject> & newArrayHandle,uint32_t & actualDeleteCount,JSThread * thread,uint32_t & start,JSHandle<JSObject> & thisObjHandle,JSHandle<JSTaggedValue> & holeHandle)134 void JSStableArray::HandleArray(JSHandle<JSObject> &newArrayHandle, uint32_t &actualDeleteCount,
135                                 JSThread *thread, uint32_t &start, JSHandle<JSObject> &thisObjHandle,
136                                 JSHandle<JSTaggedValue> &holeHandle)
137 {
138     TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements(thread).GetTaggedObject());
139     if (actualDeleteCount > ElementAccessor::GetElementsLength(thread, newArrayHandle)) {
140         destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, actualDeleteCount);
141     }
142 
143         for (uint32_t idx = 0; idx < actualDeleteCount; idx++) {
144             if ((start + idx) >= ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
145             ElementAccessor::Set(thread, newArrayHandle, idx, holeHandle, true);
146             } else {
147             JSHandle<JSTaggedValue> valueHandle(thread, ElementAccessor::Get(thread, thisObjHandle, start + idx));
148             ElementAccessor::Set(thread, newArrayHandle, idx, valueHandle, true);
149             }
150         }
151         JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, actualDeleteCount);
152 }
153 
UpdateArrayCapacity(JSHandle<JSObject> & thisObjHandle,uint32_t & len,uint32_t & insertCount,uint32_t & actualDeleteCount,JSHandle<JSArray> & receiver,uint32_t & start,JSThread * thread,bool & needTransition,JSHandle<JSTaggedValue> & holeHandle,EcmaRuntimeCallInfo * argv,JSHandle<JSTaggedValue> & thisObjVal,JSHandle<JSTaggedValue> & lengthKey)154 JSTaggedValue JSStableArray::UpdateArrayCapacity(JSHandle<JSObject> &thisObjHandle, uint32_t &len,
155                                                  uint32_t &insertCount, uint32_t &actualDeleteCount,
156                                                  JSHandle<JSArray> &receiver, uint32_t &start,
157                                                  JSThread *thread, bool &needTransition,
158                                                  JSHandle<JSTaggedValue> &holeHandle,
159                                                  EcmaRuntimeCallInfo *argv, JSHandle<JSTaggedValue> &thisObjVal,
160                                                  JSHandle<JSTaggedValue> &lengthKey)
161 {
162         uint32_t oldCapacity = ElementAccessor::GetElementsLength(thread, thisObjHandle);
163         ASSERT(len + insertCount >= actualDeleteCount);
164         uint32_t newCapacity = len - actualDeleteCount + insertCount;
165         TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements(thread).GetTaggedObject());
166         JSMutableHandle<TaggedArray> srcElementsHandle(thread, srcElements);
167         uint32_t argc = argv->GetArgsNumber();
168         if (newCapacity > oldCapacity) {
169             srcElementsHandle.Update(JSObject::GrowElementsCapacity(thread, thisObjHandle, newCapacity));
170     }
171     if (insertCount < actualDeleteCount) {
172         JSArray::CheckAndCopyArray(thread, receiver);
173         srcElementsHandle.Update(receiver->GetElements(thread));
174         for (uint32_t idx = start; idx < len - actualDeleteCount; idx++) {
175             JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Hole());
176             if ((idx + actualDeleteCount) < ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
177                 element.Update(ElementAccessor::Get(thread, thisObjHandle, idx + actualDeleteCount));
178             }
179             if ((idx + insertCount) < ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
180                 ElementAccessor::Set(thread, thisObjHandle, idx + insertCount, element, needTransition);
181             }
182         }
183 
184         if ((oldCapacity > newCapacity) && TaggedArray::ShouldTrim(oldCapacity, newCapacity)) {
185             srcElementsHandle->Trim(thread, newCapacity);
186         } else {
187             for (uint32_t idx = newCapacity; idx < len; idx++) {
188                 if (idx < ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
189                     ElementAccessor::Set(thread, thisObjHandle, idx, holeHandle, needTransition);
190                 }
191             }
192         }
193     } else {
194         ASSERT(len >= actualDeleteCount);
195         for (uint32_t idx = len - actualDeleteCount; idx > start; idx--) {
196             JSHandle<JSTaggedValue> element(thread,
197                 ElementAccessor::Get(thread, thisObjHandle, idx + actualDeleteCount - 1));
198             ElementAccessor::Set(thread, thisObjHandle, idx + insertCount - 1, element, needTransition);
199         }
200     }
201 
202     for (uint32_t i = 2, idx = start; i < argc; i++, idx++) {
203         ElementAccessor::Set(thread, thisObjHandle, idx, argv->GetCallArg(i), needTransition);
204     }
205 
206     JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newCapacity));
207     JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
208     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
209     return JSTaggedValue::Undefined();
210 }
211 
Splice(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv,uint32_t start,uint32_t insertCount,uint32_t actualDeleteCount,JSHandle<JSObject> newArrayHandle,uint32_t len)212 JSTaggedValue JSStableArray::Splice(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv,
213                                     uint32_t start, uint32_t insertCount, uint32_t actualDeleteCount,
214                                     JSHandle<JSObject> newArrayHandle, uint32_t len)
215 {
216     JSThread *thread = argv->GetThread();
217 
218     JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
219     JSHandle<JSObject> thisObjHandle(receiver);
220     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
221     JSArray::CheckAndCopyArray(thread, receiver);
222     JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
223     bool needTransition = true;
224     if (newArrayHandle.GetTaggedValue().IsStableJSArray(thread)) {
225         HandleArray(newArrayHandle, actualDeleteCount, thread, start, thisObjHandle, holeHandle);
226     } else {
227         JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
228         JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
229         uint32_t k = 0;
230         while (k < actualDeleteCount) {
231             uint32_t from = start + k;
232             fromKey.Update(JSTaggedValue(from));
233             bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
234             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
235             if (exists) {
236                 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
237                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
238                 toKey.Update(JSTaggedValue(k));
239                 if (newArrayHandle->IsJSProxy()) {
240                     toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue());
241                     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
242                 }
243                 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue);
244                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
245             }
246             k++;
247         }
248 
249         JSHandle<JSTaggedValue> deleteCount(thread, JSTaggedValue(actualDeleteCount));
250         JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, deleteCount,
251                                    true);
252         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
253     }
254     UpdateArrayCapacity(thisObjHandle, len, insertCount, actualDeleteCount, receiver, start,
255                         thread, needTransition, holeHandle, argv, thisObjVal, lengthKey);
256     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
257     return newArrayHandle.GetTaggedValue();
258 }
259 
Splice(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv,uint32_t start,uint32_t insertCount,uint32_t actualDeleteCount,JSHandle<JSObject> newArrayHandle,uint32_t len)260 JSTaggedValue JSStableArray::Splice(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv,
261                                     uint32_t start, uint32_t insertCount, uint32_t actualDeleteCount,
262                                     JSHandle<JSObject> newArrayHandle, uint32_t len)
263 {
264     JSThread *thread = argv->GetThread();
265     uint32_t argc = argv->GetArgsNumber();
266 
267     JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
268     JSHandle<JSObject> thisObjHandle(receiver);
269     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
270     JSSharedArray::CheckAndCopyArray(thread, receiver);
271     JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
272     TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements(thread).GetTaggedObject());
273     JSMutableHandle<TaggedArray> srcElementsHandle(thread, srcElements);
274     bool needTransition = true;
275     if (newArrayHandle.GetTaggedValue().IsStableJSArray(thread)) {
276         TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements(thread).GetTaggedObject());
277         if (actualDeleteCount > ElementAccessor::GetElementsLength(thread, newArrayHandle)) {
278             destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, actualDeleteCount);
279         }
280 
281         for (uint32_t idx = 0; idx < actualDeleteCount; idx++) {
282             if ((start + idx) >= ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
283                 ElementAccessor::Set(thread, newArrayHandle, idx, holeHandle, needTransition);
284             } else {
285                 JSHandle<JSTaggedValue> valueHandle(thread, ElementAccessor::Get(thread, thisObjHandle, start + idx));
286                 ElementAccessor::Set(thread, newArrayHandle, idx, valueHandle, needTransition);
287             }
288         }
289         JSHandle<JSSharedArray>::Cast(newArrayHandle)->SetArrayLength(thread, actualDeleteCount);
290     } else {
291         JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
292         JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
293         uint32_t k = 0;
294         while (k < actualDeleteCount) {
295             uint32_t from = start + k;
296             fromKey.Update(JSTaggedValue(from));
297             bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
298             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
299             if (exists) {
300                 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
301                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
302                 toKey.Update(JSTaggedValue(k));
303                 if (newArrayHandle->IsJSProxy()) {
304                     toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue());
305                     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
306                 }
307                 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue);
308                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
309             }
310             k++;
311         }
312 
313         JSHandle<JSTaggedValue> deleteCount(thread, JSTaggedValue(actualDeleteCount));
314         JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, deleteCount,
315                                    true);
316         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
317     }
318     uint32_t oldCapacity = ElementAccessor::GetElementsLength(thread, thisObjHandle);
319     ASSERT(len + insertCount >= actualDeleteCount);
320     uint32_t newCapacity = len - actualDeleteCount + insertCount;
321     if (newCapacity > oldCapacity) {
322         srcElementsHandle.Update(JSObject::GrowElementsCapacity(thread, thisObjHandle, newCapacity));
323     }
324     if (insertCount < actualDeleteCount) {
325         JSSharedArray::CheckAndCopyArray(thread, receiver);
326         srcElementsHandle.Update(receiver->GetElements(thread));
327         for (uint32_t idx = start; idx < len - actualDeleteCount; idx++) {
328             JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Hole());
329             if ((idx + actualDeleteCount) < ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
330                 element.Update(ElementAccessor::Get(thread, thisObjHandle, idx + actualDeleteCount));
331             }
332             if ((idx + insertCount) < ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
333                 ElementAccessor::Set(thread, thisObjHandle, idx + insertCount, element, needTransition);
334             }
335         }
336 
337         if ((oldCapacity > newCapacity) && TaggedArray::ShouldTrim(oldCapacity, newCapacity)) {
338             srcElementsHandle->Trim(thread, newCapacity);
339         } else {
340             for (uint32_t idx = newCapacity; idx < len; idx++) {
341                 if (idx < ElementAccessor::GetElementsLength(thread, thisObjHandle)) {
342                     ElementAccessor::Set(thread, thisObjHandle, idx, holeHandle, needTransition);
343                 }
344             }
345         }
346     } else {
347         ASSERT(len >= actualDeleteCount);
348         for (uint32_t idx = len - actualDeleteCount; idx > start; idx--) {
349             JSHandle<JSTaggedValue> element(thread,
350                 ElementAccessor::Get(thread, thisObjHandle, idx + actualDeleteCount - 1));
351             ElementAccessor::Set(thread, thisObjHandle, idx + insertCount - 1, element, needTransition);
352         }
353     }
354 
355     for (uint32_t i = 2, idx = start; i < argc; i++, idx++) {
356         ElementAccessor::Set(thread, thisObjHandle, idx, argv->GetCallArg(i), needTransition);
357     }
358 
359     JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newCapacity));
360     JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
361     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
362     return newArrayHandle.GetTaggedValue();
363 }
364 
Shift(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)365 JSTaggedValue JSStableArray::Shift(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
366 {
367     JSThread *thread = argv->GetThread();
368     JSHandle<JSObject> thisObjHandle(receiver);
369     JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
370     uint32_t length = receiver->GetArrayLength();
371     if (length == 0) {
372         return JSTaggedValue::Undefined();
373     }
374     JSSharedArray::CheckAndCopyArray(thread, receiver);
375     TaggedArray *elements = TaggedArray::Cast(receiver->GetElements(thread).GetTaggedObject());
376     JSHandle<JSTaggedValue> result(thread, ElementAccessor::Get(thread, thisObjHandle, 0));
377     bool needTransition = false;
378     for (uint32_t k = 1; k < length; k++) {
379         JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thread, thisObjHandle, k));
380         ElementAccessor::Set(thread, thisObjHandle, k - 1, kValue, needTransition);
381     }
382     uint32_t capacity = ElementAccessor::GetElementsLength(thread, thisObjHandle);
383     uint32_t index = length - 1;
384     if (TaggedArray::ShouldTrim(capacity, index)) {
385         elements->Trim(thread, index);
386     } else {
387         ElementAccessor::Set(thread, thisObjHandle, index, holeHandle, needTransition);
388     }
389     receiver->SetArrayLength(thread, index);
390     return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
391 }
392 
Shift(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)393 JSTaggedValue JSStableArray::Shift(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
394 {
395     JSThread *thread = argv->GetThread();
396     JSHandle<JSObject> thisObjHandle(receiver);
397     JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
398     uint32_t length = receiver->GetArrayLength();
399     if (length == 0) {
400         return JSTaggedValue::Undefined();
401     }
402     JSArray::CheckAndCopyArray(thread, receiver);
403     JSHandle<TaggedArray> elements(thread, TaggedArray::Cast(receiver->GetElements(thread).GetTaggedObject()));
404     JSHandle<JSTaggedValue> result(thread, ElementAccessor::Get(thread, thisObjHandle, 0));
405     bool needTransition = false;
406     for (uint32_t k = 1; k < length; k++) {
407         JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thread, thisObjHandle, k));
408         ElementAccessor::Set(thread, thisObjHandle, k - 1, kValue, needTransition);
409     }
410     uint32_t capacity = ElementAccessor::GetElementsLength(thread, thisObjHandle);
411     uint32_t index = length - 1;
412     if (TaggedArray::ShouldTrim(capacity, index)) {
413         elements->Trim(thread, index);
414     } else {
415         ElementAccessor::Set(thread, thisObjHandle, index, holeHandle, needTransition);
416     }
417     receiver->SetArrayLength(thread, index);
418     return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
419 }
420 
421 #if ENABLE_NEXT_OPTIMIZATION
WorthUseTreeString(uint32_t sepLength,size_t allocateLength,uint32_t len)422 bool JSStableArray::WorthUseTreeString(uint32_t sepLength, size_t allocateLength, uint32_t len)
423 {
424     if (allocateLength >= TREE_STRING_THRESHOLD) {
425         // if sepLength is 0, means all the elements in treeString is len -1;
426         // otherwise, the num of elements is (len-1)(string in vector) + (len -1)(num of seps)
427         size_t treeStringElementNum = (sepLength == 0) ? (len - 1) : (2 * (len - 1));
428 
429         if (treeStringElementNum * TreeString::SIZE <= allocateLength) {
430             // heuristic: if tree string uses less memory than linestring, it is worth.
431             // In other words, we hope tree string can work for the large strings join.
432             return true;
433         }
434     }
435     return false;
436 }
437 
438 template <typename Container>
JoinUseTreeString(const JSThread * thread,const JSHandle<JSTaggedValue> receiverValue,const JSHandle<EcmaString> sepStringHandle,uint32_t sepLength,Container & arrElements,uint32_t elemNum)439 JSTaggedValue JSStableArray::JoinUseTreeString(const JSThread *thread, const JSHandle<JSTaggedValue> receiverValue,
440                                                const JSHandle<EcmaString> sepStringHandle, uint32_t sepLength,
441                                                Container &arrElements, uint32_t elemNum)
442 {
443     // Do not concat the elements one by one, it will make the tree string unbalanced. Concat each element with its
444     // right neighbor first level by level, then the tree string will be balanced as possible.
445     if (sepLength != 0 && elemNum > 1) {
446         for (uint32_t k = 0; k < elemNum - 1; k++) {
447             arrElements[k] = JSHandle<EcmaString>(
448                 thread, EcmaStringAccessor::Concat(thread->GetEcmaVM(), arrElements[k], sepStringHandle));
449             RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
450         }
451     }
452 
453     while (elemNum > 1) {
454         uint32_t newNum = (elemNum + 1) / NUM_2;
455         for (uint32_t i = 0; i < elemNum / NUM_2; ++i) {
456             arrElements[i] = JSHandle<EcmaString>(
457                     thread,
458                     EcmaStringAccessor::Concat(thread->GetEcmaVM(), arrElements[NUM_2 * i], arrElements[NUM_2 * i + 1])
459                 );
460             RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
461         }
462         if (elemNum % NUM_2 == 1) {
463             arrElements[newNum - 1] = arrElements[elemNum - 1];
464         }
465         elemNum = newNum;
466     }
467     ArrayJoinStack::Pop(thread, receiverValue);
468     return arrElements[0].GetTaggedValue();
469 }
470 
471 template <typename Container>
ProcessElements(JSThread * thread,JSHandle<JSTaggedValue> receiverValue,uint32_t len,Container & arrElements,bool & isOneByte,uint64_t & allocateLength)472 void JSStableArray::ProcessElements(JSThread *thread, JSHandle<JSTaggedValue> receiverValue, uint32_t len,
473                                     Container &arrElements, bool &isOneByte, uint64_t &allocateLength)
474 {
475     JSMutableHandle<JSTaggedValue> elementHandle(thread, JSTaggedValue::Undefined());
476     JSHandle<JSObject> obj(thread, receiverValue.GetTaggedValue());
477     JSTaggedValue element = JSTaggedValue::Undefined();
478     for (uint32_t k = 0; k < len; k++) {
479         if (receiverValue->IsStableJSArray(thread) || receiverValue->IsJSSharedArray()) {
480             element = k < ElementAccessor::GetElementsLength(thread, obj) ? ElementAccessor::Get(thread, obj, k)
481                                                                           : JSTaggedValue::Hole();
482         } else {
483             element = JSArray::FastGetPropertyByValue(thread, receiverValue, k).GetTaggedValue();
484             RETURN_IF_ABRUPT_COMPLETION(thread);
485         }
486         if (!element.IsUndefinedOrNull() && !element.IsHole()) {
487             if (!element.IsString()) {
488                 elementHandle.Update(element);
489                 JSHandle<EcmaString> strElement = JSTaggedValue::ToString(thread, elementHandle);
490                 RETURN_IF_ABRUPT_COMPLETION(thread);
491                 element = strElement.GetTaggedValue();
492             }
493             auto nextStr = EcmaString::Cast(element.GetTaggedObject());
494             arrElements[k] = JSHandle<EcmaString>(thread, nextStr);
495             isOneByte = isOneByte & EcmaStringAccessor(nextStr).IsUtf8();
496             allocateLength += EcmaStringAccessor(nextStr).GetLength();
497         } else {
498             arrElements[k] = JSHandle<EcmaString>(thread->GlobalConstants()->GetHandledEmptyString());
499         }
500     }
501 }
502 
503 template <typename Container>
DoStableArrayJoin(JSThread * thread,JSHandle<JSTaggedValue> receiverValue,uint32_t len,Container & arrElements,bool & isOneByte,uint32_t sep,uint32_t sepLength,JSHandle<EcmaString> sepStringHandle)504 JSTaggedValue JSStableArray::DoStableArrayJoin(JSThread *thread, JSHandle<JSTaggedValue> receiverValue, uint32_t len,
505                                                Container &arrElements, bool &isOneByte, uint32_t sep,
506                                                uint32_t sepLength, JSHandle<EcmaString> sepStringHandle)
507 {
508     uint64_t allocateLength = 0;
509     ProcessElements(thread, receiverValue, len, arrElements, isOneByte, allocateLength);
510     RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
511 
512     if (len > 0) {
513         allocateLength += static_cast<uint64_t>(sepLength) * (len - 1);
514     }
515     if (allocateLength > BaseString::MAX_STRING_LENGTH) {
516         ArrayJoinStack::Pop(thread, receiverValue);
517         THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
518     }
519     if (WorthUseTreeString(sepLength, allocateLength, len)) {
520         return JoinUseTreeString(thread, receiverValue, sepStringHandle, sepLength, arrElements, len);
521     }
522 
523     // 5. Let R be the empty String.
524     auto newString =
525         EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), static_cast<size_t>(allocateLength), isOneByte);
526     int current = 0;
527     {
528         DISALLOW_GARBAGE_COLLECTION;
529         // 6. Repeat, while k < len
530         for (uint32_t k = 0; k < len; k++) {
531             // a. If k > 0, set R to the string-concatenation of R and sep.
532             if (k > 0) {
533                 if (sepLength == 1) {
534                     EcmaStringAccessor(newString).Set(current, static_cast<uint16_t>(sep));
535                 } else if (sepLength > 1) {
536                     EcmaStringAccessor::ReadData(thread, newString, *sepStringHandle, current,
537                                                  allocateLength - static_cast<uint32_t>(current), sepLength);
538                 }
539                 current += static_cast<int>(sepLength);
540             }
541             // b. Let element be ? Get(O, ToString(��(k))).
542             JSHandle<EcmaString> nextStr = arrElements[k];
543 
544             // c. Set R to the string-concatenation of R and S
545             int nextLength = static_cast<int>(EcmaStringAccessor(nextStr).GetLength());
546             EcmaStringAccessor::ReadData(thread, newString, *nextStr, current,
547                                          allocateLength - static_cast<uint32_t>(current), nextLength);
548             current += nextLength;
549         }
550     }
551     ASSERT_PRINT(isOneByte == EcmaStringAccessor::CanBeCompressed(newString),
552                  "isOneByte does not match the real value!");
553     JSHandle<JSTaggedValue> stringValue(thread, newString);
554     ArrayJoinStack::Pop(thread, receiverValue);
555     // return R
556     return stringValue.GetTaggedValue();
557 }
558 
Join(JSHandle<JSTaggedValue> receiverValue,EcmaRuntimeCallInfo * argv)559 JSTaggedValue JSStableArray::Join(JSHandle<JSTaggedValue> receiverValue, EcmaRuntimeCallInfo *argv)
560 {
561     JSThread *thread = argv->GetThread();
562     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
563 
564     // 1. Let O be ToObject(this.value)
565     JSHandle<JSObject> obj(thread, receiverValue.GetTaggedValue());
566 
567     // 2. Let len be ToLength(Get(O, "length"))
568     int64_t len = base::ArrayHelper::GetArrayLength(thread, receiverValue);
569 
570     int sep = ',';
571     uint32_t sepLength = 1;
572     JSHandle<JSTaggedValue> sepHandle = base::BuiltinsBase::GetCallArg(argv, 0);
573     JSHandle<EcmaString> sepStringHandle;
574     if (sepHandle->IsUndefined()) {
575         // 3. If separator is undefined, let sep be ",".
576         sepHandle = globalConst->GetHandledCommaString();
577         sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
578         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
579     } else {
580         // 4. Else, let sep be ? ToString(separator).
581         sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
582         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
583         sepLength = EcmaStringAccessor(sepStringHandle).GetLength();
584         if (sepLength == 1) {
585             sep = EcmaStringAccessor(sepStringHandle).Get<false>(thread, 0);
586         }
587     }
588 
589     bool isOneByte = EcmaStringAccessor(sepStringHandle).IsUtf8();
590 
591     // Fastpath should put after parsing "sep". Error may occur if sep cannot be transformed to string,
592     // which should be handled before fastpath return.
593     if (len == 0 || !ArrayJoinStack::Push(thread, receiverValue)) {
594         return globalConst->GetEmptyString();
595     }
596 
597     if (len == 1) {
598         // sep unused, set isOneByte to default(true)
599         isOneByte = true;
600     }
601 
602     // Use stack memory if the number of elements is less than USE_STACK_MEMORY_THRESHOLD.
603     // arr can be faster then vector.
604     if (len <= USE_STACK_MEMORY_THRESHOLD) {
605         std::array<JSHandle<EcmaString>, USE_STACK_MEMORY_THRESHOLD> arr;
606         return DoStableArrayJoin(thread, receiverValue, len, arr, isOneByte, sep, sepLength, sepStringHandle);
607     } else {
608         CVector<JSHandle<EcmaString>> vec(len);
609         return DoStableArrayJoin(thread, receiverValue, len, vec, isOneByte, sep, sepLength, sepStringHandle);
610     }
611 }
612 #endif
613 
614 #if !ENABLE_NEXT_OPTIMIZATION
SetSepValue(JSThread * thread,JSHandle<EcmaString> sepStringHandle,int & sep,uint32_t & sepLength)615 void JSStableArray::SetSepValue(JSThread *thread, JSHandle<EcmaString> sepStringHandle, int &sep, uint32_t &sepLength)
616 {
617     if (EcmaStringAccessor(sepStringHandle).IsUtf8() && EcmaStringAccessor(sepStringHandle).GetLength() == 1) {
618         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
619         sep = EcmaStringAccessor(sepStringHandle).Get(thread, 0);
620     } else if (EcmaStringAccessor(sepStringHandle).GetLength() == 0) {
621         sep = JSStableArray::SeparatorFlag::MINUS_TWO;
622         sepLength = 0;
623     } else {
624         sep = JSStableArray::SeparatorFlag::MINUS_ONE;
625         sepLength = EcmaStringAccessor(sepStringHandle).GetLength();
626     }
627 }
628 
WorthUseTreeString(int sep,size_t allocateLength,uint32_t len)629 bool JSStableArray::WorthUseTreeString(int sep, size_t allocateLength, uint32_t len)
630 {
631     if (allocateLength >= TREE_STRING_THRESHOLD) {
632         size_t treeStringElementNum = (sep == MINUS_TWO) ? (len - 1) : (2 * (len - 1));
633         // if sep is MINUS_TWO, means all the elements in treeString is len -1;
634         // otherwise, the num of elements is (len-1)(string in vector) + (len -1)(num of seps)
635         if (treeStringElementNum * TreeString::SIZE <= allocateLength) {
636             // heuristic: if tree string uses less memory than linestring, it is worth.
637             // In other words, we hope tree string can work for the large strings join.
638             return true;
639         }
640     }
641     return false;
642 }
643 
JoinUseTreeString(const JSThread * thread,const JSHandle<JSTaggedValue> receiverValue,const JSHandle<EcmaString> sepStringHandle,const int sep,CVector<JSHandle<EcmaString>> & vec)644 JSTaggedValue JSStableArray::JoinUseTreeString(const JSThread *thread,
645                                                const JSHandle<JSTaggedValue> receiverValue,
646                                                const JSHandle<EcmaString> sepStringHandle, const int sep,
647                                                CVector<JSHandle<EcmaString>> &vec)
648 {
649     // Do not concat the elements one by one, it will make the tree string unbalanced. Concat each element with its
650     // right neighbor first level by level, then the tree string will be balanced as possible.
651     if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
652         auto last = std::prev(vec.end());
653         for (auto iter = vec.begin(); iter != last; ++iter) {
654             *iter =
655                 JSHandle<EcmaString>(thread, EcmaStringAccessor::Concat(thread->GetEcmaVM(), *iter, sepStringHandle));
656             RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
657         }
658     }
659     size_t elemNum = vec.size();
660     while (elemNum > 1) {
661         size_t newNum = (elemNum + 1) / NUM_2;
662         for (size_t i = 0; i < elemNum / NUM_2; ++i) {
663             vec[i] = JSHandle<EcmaString>(
664                 thread, EcmaStringAccessor::Concat(thread->GetEcmaVM(), vec[NUM_2 * i], vec[NUM_2 * i + 1]));
665             RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
666         }
667         if (elemNum % NUM_2 == 1) {
668             vec[newNum - 1] = vec[elemNum - 1];
669         }
670         elemNum = newNum;
671     }
672     ArrayJoinStack::Pop(thread, receiverValue);
673     return vec.front().GetTaggedValue();
674 }
675 
Join(JSHandle<JSTaggedValue> receiverValue,EcmaRuntimeCallInfo * argv)676 JSTaggedValue JSStableArray::Join(JSHandle<JSTaggedValue> receiverValue, EcmaRuntimeCallInfo *argv)
677 {
678     JSThread *thread = argv->GetThread();
679     uint32_t length = base::ArrayHelper::GetArrayLength(thread, receiverValue);
680     JSHandle<JSTaggedValue> sepHandle = base::BuiltinsBase::GetCallArg(argv, 0);
681     int sep = ',';
682     uint32_t sepLength = 1;
683     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
684     JSHandle<EcmaString> sepStringHandle = JSHandle<EcmaString>::Cast(globalConst->GetHandledCommaString());
685     if (!sepHandle->IsUndefined()) {
686         if (sepHandle->IsString()) {
687             sepStringHandle = JSHandle<EcmaString>::Cast(sepHandle);
688         } else {
689             sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
690             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
691         }
692         SetSepValue(thread, sepStringHandle, sep, sepLength);
693     }
694     if (length == 0 || !ArrayJoinStack::Push(thread, receiverValue)) {
695         return globalConst->GetEmptyString();
696     }
697     JSHandle<JSObject> obj(thread, receiverValue.GetTaggedValue());
698     uint64_t allocateLength = 0;
699     bool isOneByte = (sep != JSStableArray::SeparatorFlag::MINUS_ONE) || EcmaStringAccessor(sepStringHandle).IsUtf8();
700     JSMutableHandle<JSTaggedValue> elementHandle(thread, JSTaggedValue::Undefined());
701     uint32_t elementsLength = ElementAccessor::GetElementsLength(thread, obj);
702     uint32_t len = elementsLength > length ? length : elementsLength;
703     if (elementsLength == 0 && length != 0) {
704         len = length;
705     }
706     if (len <= 1) {
707         // sep unused, set isOneByte to default(true)
708         isOneByte = true;
709     }
710     CVector<JSHandle<EcmaString>> vec;
711     vec.reserve(len);
712     JSTaggedValue element = JSTaggedValue::Hole();
713     for (uint32_t k = 0; k < len; k++) {
714         if (receiverValue->IsStableJSArray(thread)) {
715             element = k < ElementAccessor::GetElementsLength(thread, obj) ? ElementAccessor::Get(thread, obj, k)
716                                                                           : JSTaggedValue::Hole();
717         } else {
718             element = JSArray::FastGetPropertyByValue(thread, receiverValue, k).GetTaggedValue();
719             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
720         }
721         if (!element.IsUndefinedOrNull() && !element.IsHole()) {
722             if (!element.IsString()) {
723                 elementHandle.Update(element);
724                 JSHandle<EcmaString> strElement = JSTaggedValue::ToString(thread, elementHandle);
725                 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
726                 element = strElement.GetTaggedValue();
727             }
728             auto nextStr = EcmaString::Cast(element.GetTaggedObject());
729             vec.emplace_back(thread, nextStr);
730             isOneByte = EcmaStringAccessor(nextStr).IsUtf8() ? isOneByte : false;
731             allocateLength += EcmaStringAccessor(nextStr).GetLength();
732         } else {
733             vec.emplace_back(globalConst->GetHandledEmptyString());
734         }
735     }
736     if (len > 0) {
737         allocateLength += static_cast<uint64_t>(sepLength) * (len - 1);
738     }
739     if (allocateLength > BaseString::MAX_STRING_LENGTH) {
740         ArrayJoinStack::Pop(thread, receiverValue);
741         THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
742     }
743     if (WorthUseTreeString(sep, allocateLength, len)) {
744         return JoinUseTreeString(thread, receiverValue, sepStringHandle, sep, vec);
745     }
746     auto newString =
747         EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), static_cast<size_t>(allocateLength), isOneByte);
748     int current = 0;
749     {
750         DISALLOW_GARBAGE_COLLECTION;
751         for (uint32_t k = 0; k < len; k++) {
752             if (k > 0) {
753                 if (sep >= 0) {
754                     EcmaStringAccessor(newString).Set(current, static_cast<uint16_t>(sep));
755                 } else if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
756                     EcmaStringAccessor::ReadData(thread, newString, *sepStringHandle, current,
757                                                  allocateLength - static_cast<uint32_t>(current), sepLength);
758                 }
759                 current += static_cast<int>(sepLength);
760             }
761             JSHandle<EcmaString> nextStr = vec[k];
762             int nextLength = static_cast<int>(EcmaStringAccessor(nextStr).GetLength());
763             EcmaStringAccessor::ReadData(thread, newString, *nextStr, current,
764                                          allocateLength - static_cast<uint32_t>(current), nextLength);
765             current += nextLength;
766         }
767     }
768     ASSERT_PRINT(isOneByte == EcmaStringAccessor::CanBeCompressed(newString),
769                  "isOneByte does not match the real value!");
770     JSHandle<JSTaggedValue> stringValue(thread, newString);
771     ArrayJoinStack::Pop(thread, receiverValue);
772     return stringValue.GetTaggedValue();
773 }
774 #endif
775 
HandleFindIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)776 JSTaggedValue JSStableArray::HandleFindIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
777                                                      JSHandle<JSTaggedValue> callbackFnHandle,
778                                                      JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
779 {
780     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
781     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
782     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
783     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
784     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
785     const int32_t argsLength = 3; // 3: ?kValue, k, O?
786     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
787     while (k < len) {
788         // Elements of thisObjHandle may change.
789         JSTaggedValue val = ElementAccessor::Get(thread, thisObjHandle, k);
790         if (val.IsHole()) {
791             auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
792             if (res.IsHole()) {
793                 kValue.Update(JSTaggedValue::Undefined());
794             } else {
795                 kValue.Update(res);
796             }
797         } else {
798             kValue.Update(val);
799         }
800         EcmaRuntimeCallInfo *info =
801             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
802         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
803         info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
804         callResult = JSFunction::Call(info);
805         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
806         if (callResult.ToBoolean()) {
807             return callResult;
808         }
809         if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) {
810             len = ElementAccessor::GetElementsLength(thread, thisObjHandle);
811         }
812         k++;
813         if (!thisObjVal->IsStableJSArray(thread)) {
814             return callResult;
815         }
816     }
817     return callResult;
818 }
819 
HandleFindLastIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,int64_t & k)820 JSTaggedValue JSStableArray::HandleFindLastIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
821                                                          JSHandle<JSTaggedValue> callbackFnHandle,
822                                                          JSHandle<JSTaggedValue> thisArgHandle, int64_t &k)
823 {
824     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
825     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
826     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
827     const int32_t argsLength = 3; // 3: ?kValue, k, O?
828     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
829     while (k >= 0) {
830         // Elements of thisObjHandle may change.
831         JSTaggedValue val = ElementAccessor::Get(thread, thisObjHandle, k);
832         if (val.IsHole()) {
833             auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
834             if (res.IsHole()) {
835                 kValue.Update(JSTaggedValue::Undefined());
836             } else {
837                 kValue.Update(res);
838             }
839         } else {
840             kValue.Update(val);
841         }
842         EcmaRuntimeCallInfo *info =
843             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
844         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
845         info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
846         callResult = JSFunction::Call(info);
847         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
848         if (callResult.ToBoolean()) {
849             return callResult;
850         }
851         k--;
852         if (base::ArrayHelper::GetArrayLength(thread, thisObjVal) <= k) {
853             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
854             return callResult;
855         }
856         if (!thisObjVal->IsStableJSArray(thread)) {
857             return callResult;
858         }
859     }
860     return callResult;
861 }
862 
HandleEveryOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)863 JSTaggedValue JSStableArray::HandleEveryOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
864                                                  JSHandle<JSTaggedValue> callbackFnHandle,
865                                                  JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
866 {
867     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
868     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
869     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
870     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
871     const int32_t argsLength = 3; // 3: ?kValue, k, O?
872     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(true);
873     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
874     while (k < len) {
875         // Elements of thisObjHandle may change.
876         kValue.Update(ElementAccessor::Get(thread, thisObjHandle, k));
877         if (!kValue.GetTaggedValue().IsHole()) {
878             EcmaRuntimeCallInfo *info =
879                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
880             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
881             info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
882             callResult = JSFunction::Call(info);
883             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
884             if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) {
885                 len = ElementAccessor::GetElementsLength(thread, thisObjHandle);
886             }
887         } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
888             JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
889             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
890             EcmaRuntimeCallInfo *info =
891                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
892             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
893             info->SetCallArg(kValue1.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
894             callResult = JSFunction::Call(info);
895             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
896         }
897         if (!callResult.ToBoolean()) {
898             return base::BuiltinsBase::GetTaggedBoolean(false);
899         }
900         k++;
901         if (!thisObjVal->IsStableJSArray(thread)) {
902             return base::BuiltinsBase::GetTaggedBoolean(true);
903         }
904     }
905     return base::BuiltinsBase::GetTaggedBoolean(true);
906 }
907 
HandleSomeOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)908 JSTaggedValue JSStableArray::HandleSomeOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
909                                                 JSHandle<JSTaggedValue> callbackFnHandle,
910                                                 JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
911 {
912     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
913     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
914     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
915     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
916     const int32_t argsLength = 3; // 3: ?kValue, k, O?
917     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
918     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
919     while (k < len) {
920         // Elements of thisObjHandle may change.
921         kValue.Update(ElementAccessor::Get(thread, thisObjHandle, k));
922         if (!kValue.GetTaggedValue().IsHole()) {
923             EcmaRuntimeCallInfo *info =
924                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
925             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
926             info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
927             callResult = JSFunction::Call(info);
928             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
929         } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
930             JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
931             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
932             EcmaRuntimeCallInfo *info =
933                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
934             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
935             info->SetCallArg(kValue1.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
936             callResult = JSFunction::Call(info);
937             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
938         }
939         if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) {
940             len = ElementAccessor::GetElementsLength(thread, thisObjHandle);
941         }
942         if (callResult.ToBoolean()) {
943             return base::BuiltinsBase::GetTaggedBoolean(true);
944         }
945         k++;
946         if (!thisObjVal->IsStableJSArray(thread)) {
947             return base::BuiltinsBase::GetTaggedBoolean(false);
948         }
949     }
950     return base::BuiltinsBase::GetTaggedBoolean(false);
951 }
952 
HandleforEachOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t len,uint32_t & k)953 JSTaggedValue JSStableArray::HandleforEachOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
954                                                    JSHandle<JSTaggedValue> callbackFnHandle,
955                                                    JSHandle<JSTaggedValue> thisArgHandle, uint32_t len, uint32_t &k)
956 {
957     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
958     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
959     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
960     const int32_t argsLength = 3; // 3: ?kValue, k, O?
961     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
962     if (ElementAccessor::GetElementsLength(thread, thisObjHandle) <= k) {
963         return base::BuiltinsBase::GetTaggedBoolean(false);
964     }
965     while (k < len) {
966         // Elements of thisObjHandle may change.
967         kValue.Update(ElementAccessor::Get(thread, thisObjHandle, k));
968         if (!kValue.GetTaggedValue().IsHole()) {
969             key.Update(JSTaggedValue(k));
970             EcmaRuntimeCallInfo *info =
971                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
972             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
973             info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
974             JSTaggedValue funcResult = JSFunction::Call(info);
975             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
976             if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) {
977                 len = ElementAccessor::GetElementsLength(thread, thisObjHandle);
978             }
979         } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
980             key.Update(JSTaggedValue(k));
981             JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
982             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
983             EcmaRuntimeCallInfo *info =
984                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
985             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
986             info->SetCallArg(kValue1.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
987             JSTaggedValue funcResult = JSFunction::Call(info);
988             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
989         }
990         k++;
991         if (!thisObjVal->IsStableJSArray(thread)) {
992             break;
993         }
994     }
995     return base::BuiltinsBase::GetTaggedBoolean(true);
996 }
997 
998 template <RBMode mode, class Predicate>
IndexOfElements(JSThread * thread,Span<const JSTaggedType> elements,IndexOfOptions options,Predicate predicate)999 const JSTaggedType *JSStableArray::IndexOfElements(JSThread *thread, Span<const JSTaggedType> elements,
1000                                                    IndexOfOptions options, Predicate predicate)
1001 {
1002     static_assert(std::is_invocable_r_v<bool, Predicate, JSTaggedType>, "Invalid call signature.");
1003     if (options.reversedOrder) {
1004         for (auto cur = elements.end() - 1; cur >= elements.begin(); --cur) {
1005             if (UNLIKELY(std::invoke(predicate, Barriers::GetTaggedValue<mode>(thread, ToUintPtr(cur))))) {
1006                 return cur;
1007             }
1008         }
1009     } else {
1010         for (auto cur = elements.begin(); cur < elements.end(); ++cur) {
1011             if (UNLIKELY(std::invoke(predicate, Barriers::GetTaggedValue<mode>(thread, ToUintPtr(cur))))) {
1012                 return cur;
1013             }
1014         }
1015     }
1016     return nullptr;
1017 }
1018 
IndexOfUndefined(JSThread * thread,Span<const JSTaggedType> elements,IndexOfOptions options,bool isMutant)1019 const JSTaggedType *JSStableArray::IndexOfUndefined(JSThread *thread, Span<const JSTaggedType> elements,
1020                                                     IndexOfOptions options, bool isMutant)
1021 {
1022     // For mutant arrays, only raw int32, raw double and SPECIAL_HOLE may exist.
1023     if (isMutant) {
1024         if (!options.holeAsUndefined) {
1025             return nullptr;
1026         }
1027         return IndexOfElements(thread, elements, options,
1028                                [](JSTaggedType rawValue) { return rawValue == base::SPECIAL_HOLE; });
1029     }
1030     // For non-mutant arrays, taggedValue can never be SPECIAL_HOLE.
1031     if (!options.holeAsUndefined) {
1032         return IndexOfElements(thread, elements, options,
1033                                [](JSTaggedType taggedValue) { return JSTaggedValue(taggedValue).IsUndefined(); });
1034     }
1035     return IndexOfElements(thread, elements, options, [](JSTaggedType taggedValue) {
1036         return JSTaggedValue(taggedValue).IsHole() || JSTaggedValue(taggedValue).IsUndefined();
1037     });
1038 }
1039 
IndexOfTaggedZero(JSThread * thread,Span<const JSTaggedType> elements,IndexOfOptions options)1040 const JSTaggedType *JSStableArray::IndexOfTaggedZero(JSThread *thread, Span<const JSTaggedType> elements,
1041                                                      IndexOfOptions options)
1042 {
1043     return IndexOfElements(thread, elements, options,
1044                            [](JSTaggedType taggedValue) { return JSTaggedValue(taggedValue).IsExactlyZero(); });
1045 }
1046 
1047 // Raw int32 array (isMutant = true), or tagged array (isMutant = false)
IndexOfInt(JSThread * thread,Span<const JSTaggedType> elements,JSTaggedValue searchElement,IndexOfOptions options,bool isMutantInt32Array)1048 const JSTaggedType *JSStableArray::IndexOfInt(JSThread *thread, Span<const JSTaggedType> elements,
1049                                               JSTaggedValue searchElement, IndexOfOptions options,
1050                                               bool isMutantInt32Array)
1051 {
1052     ASSERT(!searchElement.IsUndefined());
1053     int32_t searchValue;
1054     if (searchElement.IsInt()) {
1055         searchValue = searchElement.GetInt();
1056     } else if (searchElement.WithinInt32(true)) {
1057         searchValue = static_cast<int32_t>(searchElement.GetDouble());
1058     } else {
1059         return nullptr;
1060     }
1061     if (isMutantInt32Array) {
1062         // For ElementsKind::INT: convertedValue = JSTaggedValue(static_cast<int>(rawValue))
1063         return IndexOfElements(thread, elements, options, [searchValue](JSTaggedType rawValue) {
1064             return rawValue != base::SPECIAL_HOLE && searchValue == static_cast<int32_t>(rawValue);
1065         });
1066     }
1067     if (searchValue == 0) {
1068         return IndexOfTaggedZero(thread, elements, options);
1069     }
1070     JSTaggedType taggedInt32 = JSTaggedValue(searchValue).GetRawData();
1071     JSTaggedType taggedDouble = JSTaggedValue(static_cast<double>(searchValue)).GetRawData();
1072     // Always false if taggedValue is not number
1073     return IndexOfElements(thread, elements, options, [taggedInt32, taggedDouble](JSTaggedType taggedValue) {
1074         return taggedValue == taggedInt32 || taggedValue == taggedDouble;
1075     });
1076 }
1077 
1078 // Raw double array (isMutant = true), or tagged array (isMutant = false)
IndexOfDouble(JSThread * thread,Span<const JSTaggedType> elements,JSTaggedValue searchElement,IndexOfOptions options,bool isMutantDoubleArray)1079 const JSTaggedType *JSStableArray::IndexOfDouble(JSThread *thread, Span<const JSTaggedType> elements,
1080                                                  JSTaggedValue searchElement, IndexOfOptions options,
1081                                                  bool isMutantDoubleArray)
1082 {
1083     ASSERT(!searchElement.IsUndefined());
1084     if (!searchElement.IsNumber()) {
1085         return nullptr;
1086     }
1087     double searchValue = searchElement.GetNumber();
1088     if (std::isnan(searchValue)) {
1089         if (options.compType != ComparisonType::SAME_VALUE_ZERO) {
1090             return nullptr;
1091         }
1092         if (isMutantDoubleArray) {
1093             // For ElementsKind::NUMBER: convertedValue = JSTaggedValue(base::bit_cast<double>(rawValue))
1094             return IndexOfElements(thread, elements, options, [](JSTaggedType rawValue) {
1095                 return rawValue != base::SPECIAL_HOLE && std::isnan(base::bit_cast<double>(rawValue));
1096             });
1097         }
1098         return IndexOfElements(thread, elements, options,
1099                                [](JSTaggedType taggedValue) { return JSTaggedValue(taggedValue).IsNaN(); });
1100     }
1101     if (isMutantDoubleArray) {
1102         // Including the cases of +inf, -inf, +0.0 and -0.0
1103         // We assume that bit representation of searchValue can never be SPECIAL_HOLE (which is NaN)
1104         return IndexOfElements(thread, elements, options, [searchValue](JSTaggedType rawValue) {
1105             return searchValue == base::bit_cast<double>(rawValue);
1106         });
1107     }
1108     if (searchValue == 0.0) {
1109         return IndexOfTaggedZero(thread, elements, options);
1110     }
1111     JSTaggedType taggedDouble = JSTaggedValue(searchValue).GetRawData();
1112     if (JSTaggedValue(taggedDouble).WithinInt32()) {
1113         JSTaggedType taggedInt32 = JSTaggedValue(static_cast<int32_t>(searchValue)).GetRawData();
1114         return IndexOfElements(thread, elements, options, [taggedDouble, taggedInt32](JSTaggedType taggedValue) {
1115             return taggedValue == taggedDouble || taggedValue == taggedInt32;
1116         });
1117     }
1118     return IndexOfElements(thread, elements, options,
1119                            [taggedDouble](JSTaggedType taggedValue) { return taggedValue == taggedDouble; });
1120 }
1121 
IndexOfString(JSThread * thread,Span<const JSTaggedType> elements,JSTaggedValue searchElement,IndexOfOptions options)1122 const JSTaggedType *JSStableArray::IndexOfString(JSThread *thread, Span<const JSTaggedType> elements,
1123                                                  JSTaggedValue searchElement, IndexOfOptions options)
1124 {
1125     ASSERT(!searchElement.IsUndefined());
1126     if (!searchElement.IsString()) {
1127         return nullptr;
1128     }
1129     if (g_isEnableCMCGC) {
1130         return IndexOfElements<RBMode::FAST_CMC_RB>(thread, elements, options,
1131             [searchElement, thread](JSTaggedType cur) {
1132             if (searchElement.GetRawData() == cur) {
1133                 return true;
1134             }
1135             JSTaggedValue curValue(cur);
1136             if (!curValue.IsString()) {
1137                 return false;
1138             }
1139             return JSTaggedValue::StringCompare<RBMode::FAST_CMC_RB>(thread,
1140                                                                      EcmaString::Cast(curValue.GetTaggedObject()),
1141                                                                      EcmaString::Cast(searchElement.GetTaggedObject()));
1142         });
1143     } else {
1144         return IndexOfElements<RBMode::FAST_NO_RB>(thread, elements, options,
1145             [searchElement, thread](JSTaggedType cur) {
1146             if (searchElement.GetRawData() == cur) {
1147                 return true;
1148             }
1149             JSTaggedValue curValue(cur);
1150             if (!curValue.IsString()) {
1151                 return false;
1152             }
1153             return JSTaggedValue::StringCompare<RBMode::FAST_NO_RB>(thread,
1154                                                                     EcmaString::Cast(curValue.GetTaggedObject()),
1155                                                                     EcmaString::Cast(searchElement.GetTaggedObject()));
1156         });
1157     }
1158 }
1159 
IndexOfBigInt(JSThread * thread,Span<const JSTaggedType> elements,JSTaggedValue searchElement,IndexOfOptions options)1160 const JSTaggedType *JSStableArray::IndexOfBigInt(JSThread *thread, Span<const JSTaggedType> elements,
1161                                                  JSTaggedValue searchElement, IndexOfOptions options)
1162 {
1163     ASSERT(searchElement.IsBigInt());
1164     return IndexOfElements(thread, elements, options, [searchElement](JSTaggedType cur) {
1165         if (searchElement.GetRawData() == cur) {
1166             return true;
1167         }
1168         JSTaggedValue curValue(cur);
1169         if (!curValue.IsBigInt()) {
1170             return false;
1171         }
1172         return BigInt::Equal(curValue, searchElement);
1173     });
1174 }
1175 
IndexOfObjectAddress(JSThread * thread,Span<const JSTaggedType> elements,JSTaggedValue searchElement,IndexOfOptions options)1176 const JSTaggedType *JSStableArray::IndexOfObjectAddress(JSThread *thread, Span<const JSTaggedType> elements,
1177                                                         JSTaggedValue searchElement, IndexOfOptions options)
1178 {
1179     // Note: searchElement may be true, false or null
1180     ASSERT(searchElement.IsObject());
1181     JSTaggedType targetAddress = searchElement.GetRawData();
1182     return IndexOfElements(thread, elements, options,
1183                            [targetAddress](JSTaggedType cur) { return cur == targetAddress; });
1184 }
1185 
IndexOfDispatch(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElementHandle,uint32_t from,uint32_t len,IndexOfOptions options)1186 JSTaggedValue JSStableArray::IndexOfDispatch(JSThread *thread, JSHandle<JSTaggedValue> receiver,
1187                                              JSHandle<JSTaggedValue> searchElementHandle, uint32_t from, uint32_t len,
1188                                              IndexOfOptions options)
1189 {
1190     // Note: GC is guaranteed not to happen since no new object is created during the searching process.
1191     DISALLOW_GARBAGE_COLLECTION;
1192     const JSTaggedType *data = nullptr;
1193     JSTaggedValue elementsValue = JSHandle<JSObject>::Cast(receiver)->GetElements(thread);
1194     bool isMutant = elementsValue.IsMutantTaggedArray();
1195     if (isMutant) {
1196         JSHandle<MutantTaggedArray> elements(thread, elementsValue);
1197         data = elements->GetData();
1198     } else {
1199         JSHandle<TaggedArray> elements(thread, elementsValue);
1200         data = elements->GetData();
1201     }
1202     Span<const JSTaggedType> range;
1203     if (options.reversedOrder) {
1204         range = {data, data + from + 1}; // lastIndexOf
1205     } else {
1206         range = {data + from, data + len}; // indexOf, includes
1207     }
1208     ElementsKind kind = JSHandle<JSObject>::Cast(receiver)->GetClass()->GetElementsKind();
1209     JSTaggedValue searchElement = searchElementHandle.GetTaggedValue();
1210 
1211     const JSTaggedType *foundPos = nullptr;
1212     if (searchElement.IsUndefined()) {
1213         foundPos = IndexOfUndefined(thread, range, options, isMutant);
1214     } else if (isMutant) {
1215         LOG_DEBUGGER(DEBUG) << "IndexOfDispatch: isMutant";
1216         if (Elements::IsIntOrHoleInt(kind)) {
1217             foundPos = IndexOfInt(thread, range, searchElement, options, true);  // raw int32
1218         } else {
1219             ASSERT(Elements::IsInNumbers(kind));
1220             foundPos = IndexOfDouble(thread, range, searchElement, options, true);  // raw double
1221         }
1222     } else if (searchElement.IsInt() || Elements::IsIntOrHoleInt(kind)) {
1223         foundPos = IndexOfInt(thread, range, searchElement, options, false);
1224     } else if (searchElement.IsDouble() || Elements::IsNumberOrHoleNumber(kind)) {
1225         foundPos = IndexOfDouble(thread, range, searchElement, options, false);
1226     } else if (searchElement.IsString() || Elements::IsStringOrHoleString(kind)) {
1227         foundPos = IndexOfString(thread, range, searchElement, options);
1228     } else if (searchElement.IsBigInt()) {
1229         foundPos = IndexOfBigInt(thread, range, searchElement, options);
1230     } else {
1231         foundPos = IndexOfObjectAddress(thread, range, searchElement, options);
1232     }
1233     if (options.returnType == IndexOfReturnType::TAGGED_FOUND_INDEX) {
1234         return foundPos == nullptr ? JSTaggedValue(-1) : JSTaggedValue(static_cast<int32_t>(foundPos - data));
1235     } else {
1236         ASSERT(options.returnType == IndexOfReturnType::TAGGED_FOUND_OR_NOT);
1237         return JSTaggedValue(foundPos != nullptr);
1238     }
1239 }
1240 
Includes(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)1241 JSTaggedValue JSStableArray::Includes(JSThread *thread, JSHandle<JSTaggedValue> receiver,
1242                                       JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
1243 {
1244     IndexOfOptions options;
1245     options.compType = ComparisonType::SAME_VALUE_ZERO;
1246     options.returnType = IndexOfReturnType::TAGGED_FOUND_OR_NOT;
1247     options.holeAsUndefined = true;
1248     options.reversedOrder = false;
1249     return IndexOfDispatch(thread, receiver, searchElement, from, len, options);
1250 }
1251 
IndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)1252 JSTaggedValue JSStableArray::IndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
1253                                      JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
1254 {
1255     IndexOfOptions options;
1256     options.compType = ComparisonType::STRICT_EQUAL;
1257     options.returnType = IndexOfReturnType::TAGGED_FOUND_INDEX;
1258     options.holeAsUndefined = false;
1259     options.reversedOrder = false;
1260     return IndexOfDispatch(thread, receiver, searchElement, from, len, options);
1261 }
1262 
LastIndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)1263 JSTaggedValue JSStableArray::LastIndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
1264                                          JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
1265 {
1266     IndexOfOptions options;
1267     options.compType = ComparisonType::STRICT_EQUAL;
1268     options.returnType = IndexOfReturnType::TAGGED_FOUND_INDEX;
1269     options.holeAsUndefined = false;
1270     options.reversedOrder = true;
1271     return IndexOfDispatch(thread, receiver, searchElement, from, len, options);
1272 }
1273 
Filter(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t & toIndex)1274 JSTaggedValue JSStableArray::Filter(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
1275                                     EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t &toIndex)
1276 {
1277     JSThread *thread = argv->GetThread();
1278     JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
1279     JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
1280     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1281     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1282     JSMutableHandle<JSTaggedValue> toIndexHandle(thread, JSTaggedValue::Undefined());
1283     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1284     const int32_t argsLength = 3; // 3: ?kValue, k, O?
1285     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1286     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1287     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
1288     while (k < len) {
1289         // Elements of thisObjHandle may change.
1290         JSTaggedValue value = ElementAccessor::Get(thread, thisObjHandle, k);
1291         if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1292             value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1293             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1294         }
1295         kValue.Update(value);
1296         if (!kValue.GetTaggedValue().IsHole()) {
1297             key.Update(JSTaggedValue(k));
1298             EcmaRuntimeCallInfo *info =
1299                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1300             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1301             info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1302             JSTaggedValue callResult = JSFunction::Call(info);
1303             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1304             if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) {
1305                 len = ElementAccessor::GetElementsLength(thread, thisObjHandle);
1306             }
1307             bool boolResult = callResult.ToBoolean();
1308             if (boolResult) {
1309                 toIndexHandle.Update(JSTaggedValue(toIndex));
1310                 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toIndexHandle, kValue);
1311                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1312                 toIndex++;
1313             }
1314         }
1315         k++;
1316         if (!thisObjVal->IsStableJSArray(thread)) {
1317             break;
1318         }
1319     }
1320     return base::BuiltinsBase::GetTaggedDouble(true);
1321 }
1322 
Map(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t len)1323 JSTaggedValue JSStableArray::Map(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
1324                                  EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t len)
1325 {
1326     JSThread *thread = argv->GetThread();
1327     JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
1328     JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
1329     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1330     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1331     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1332     JSMutableHandle<JSTaggedValue> mapResultHandle(thread, JSTaggedValue::Undefined());
1333     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
1334     const int32_t argsLength = 3; // 3: ?kValue, k, O?
1335     while (k < len) {
1336         // Elements of thisObjHandle may change.
1337         JSTaggedValue value = ElementAccessor::Get(thread, thisObjHandle, k);
1338         if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1339             value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1340             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1341         }
1342         kValue.Update(value);
1343         if (!kValue.GetTaggedValue().IsHole()) {
1344             key.Update(JSTaggedValue(k));
1345             EcmaRuntimeCallInfo *info =
1346                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1347             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1348             info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1349             JSTaggedValue mapResult = JSFunction::Call(info);
1350             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1351             mapResultHandle.Update(mapResult);
1352             JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, mapResultHandle);
1353             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1354             if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) {
1355                 len = ElementAccessor::GetElementsLength(thread, thisObjHandle);
1356             }
1357         }
1358         k++;
1359         if (!thisObjVal->IsStableJSArray(thread)) {
1360             break;
1361         }
1362     }
1363     return base::BuiltinsBase::GetTaggedDouble(true);
1364 }
1365 
Reverse(JSThread * thread,JSHandle<JSObject> thisObjHandle,int64_t & lower,uint32_t len)1366 JSTaggedValue JSStableArray::Reverse(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1367                                      int64_t &lower, uint32_t len)
1368 {
1369     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1370     if (thisObjHandle->IsJSArray()) {
1371         JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObjHandle));
1372     }
1373     ElementsKind kind = thisObjHandle->GetClass()->GetElementsKind();
1374     JSHandle<TaggedArray> elements(thread, thisObjHandle->GetElements(thread));
1375     if (thread->IsEnableMutantArray()) {
1376         if (kind == ElementsKind::INT || kind == ElementsKind::HOLE_INT) {
1377             return FastReverse(thread, elements, lower, len, ElementsKind::INT);
1378         } else if (kind == ElementsKind::NUMBER || kind == ElementsKind::HOLE_NUMBER) {
1379             return FastReverse(thread, elements, lower, len, ElementsKind::NUMBER);
1380         }
1381     }
1382     return FastReverse(thread, elements, lower, len, ElementsKind::TAGGED);
1383 }
1384 
FastReverse(JSThread * thread,JSHandle<TaggedArray> elements,int64_t & lower,uint32_t len,ElementsKind kind)1385 JSTaggedValue JSStableArray::FastReverse(JSThread *thread, JSHandle<TaggedArray> elements,
1386                                          int64_t &lower, uint32_t len, ElementsKind kind)
1387 {
1388     JSMutableHandle<JSTaggedValue> lowerValueHandle(thread, JSTaggedValue::Undefined());
1389     JSMutableHandle<JSTaggedValue> upperValueHandle(thread, JSTaggedValue::Undefined());
1390     int64_t middle = std::floor(len / 2);
1391     while (lower != middle) {
1392         if (elements->GetLength() != len) {
1393             break;
1394         }
1395         int64_t upper = static_cast<int64_t>(len) - lower - 1;
1396         lowerValueHandle.Update(ElementAccessor::FastGet(thread, elements, lower, kind));
1397         upperValueHandle.Update(ElementAccessor::FastGet(thread, elements, upper, kind));
1398         ElementAccessor::FastSet(thread, elements, lower, upperValueHandle, kind);
1399         ElementAccessor::FastSet(thread, elements, upper, lowerValueHandle, kind);
1400         lower++;
1401     }
1402     return base::BuiltinsBase::GetTaggedDouble(true);
1403 }
1404 
Concat(JSThread * thread,JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & n)1405 JSTaggedValue JSStableArray::Concat(JSThread *thread, JSHandle<JSObject> newArrayHandle,
1406                                     JSHandle<JSObject> thisObjHandle, int64_t &k, int64_t &n)
1407 {
1408     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1409     int64_t thisLen = base::ArrayHelper::GetArrayLength(thread, thisObjVal);
1410     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1411     JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
1412     while (k < thisLen) {
1413         if (ElementAccessor::GetElementsLength(thread, thisObjHandle) != thisLen) {
1414             break;
1415         }
1416         toKey.Update(JSTaggedValue(n));
1417         JSTaggedValue kValue = ElementAccessor::Get(thread, thisObjHandle, k);
1418         if (!kValue.IsHole()) {
1419             JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, JSHandle<JSTaggedValue>(thread, kValue));
1420             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1421         }
1422         n++;
1423         k++;
1424     }
1425     return base::BuiltinsBase::GetTaggedDouble(true);
1426 }
1427 
1428 template JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray<TypedArrayKind::SHARED>(
1429     JSThread *thread, JSHandle<JSTypedArray> &targetArray, DataViewType targetType,
1430     uint64_t targetOffset, uint32_t srcLength, JSHandle<JSObject> &obj);
1431 template JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray<TypedArrayKind::NON_SHARED>(
1432     JSThread *thread, JSHandle<JSTypedArray> &targetArray, DataViewType targetType,
1433     uint64_t targetOffset, uint32_t srcLength, JSHandle<JSObject> &obj);
1434 
1435 template<TypedArrayKind typedArrayKind>
FastCopyFromArrayToTypedArray(JSThread * thread,JSHandle<JSTypedArray> & targetArray,DataViewType targetType,uint64_t targetOffset,uint32_t srcLength,JSHandle<JSObject> & obj)1436 JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray(JSThread *thread, JSHandle<JSTypedArray> &targetArray,
1437                                                            DataViewType targetType, uint64_t targetOffset,
1438                                                            uint32_t srcLength, JSHandle<JSObject> &obj)
1439 {
1440     JSHandle<JSTaggedValue> targetBuffer(thread, targetArray->GetViewedArrayBufferOrByteArray(thread));
1441     // If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
1442     if (BuiltinsArrayBufferType<typedArrayKind>::Type::IsDetachedBuffer(thread, targetBuffer.GetTaggedValue())) {
1443         THROW_TYPE_ERROR_AND_RETURN(thread, "The targetBuffer of This value is detached buffer.",
1444                                     JSTaggedValue::Exception());
1445     }
1446     uint32_t targetLength = targetArray->GetArrayLength();
1447     uint32_t targetByteOffset = targetArray->GetByteOffset();
1448     uint32_t targetElementSize = TypedArrayHelper::GetSizeFromType(targetType);
1449     if (srcLength + targetOffset > targetLength) {
1450         THROW_RANGE_ERROR_AND_RETURN(thread, "The sum of length and targetOffset is greater than targetLength.",
1451                                      JSTaggedValue::Exception());
1452     }
1453     uint32_t targetByteIndex = static_cast<uint32_t>(targetOffset * targetElementSize + targetByteOffset);
1454     ContentType contentType = targetArray->GetContentType();
1455     uint32_t elemLen = ElementAccessor::GetElementsLength(thread, obj);
1456     if (contentType == ContentType::BigInt) {
1457         JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Hole());
1458         JSMutableHandle<JSTaggedValue> elem(thread, JSTaggedValue::Hole());
1459         for (uint32_t i = 0; i < srcLength; i++) {
1460             if (i < elemLen) {
1461                 elem.Update(ElementAccessor::Get(thread, obj, i));
1462             } else {
1463                 elem.Update(JSTaggedValue::Hole());
1464             }
1465             kValue.Update(JSTaggedValue::ToBigInt(thread, elem));
1466             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1467             BuiltinsArrayBufferType<typedArrayKind>::Type::SetValueInBuffer(
1468                 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, kValue, true);
1469             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1470             targetByteIndex += targetElementSize;
1471         }
1472     } else {
1473         double val = 0.0;
1474         uint32_t copyLen = srcLength > elemLen ? elemLen : srcLength;
1475         for (uint32_t i = 0; i < copyLen; i++) {
1476             JSTaggedValue taggedVal = ElementAccessor::Get(thread, obj, i);
1477             if (!taggedVal.IsNumber()) {
1478                 JSTaggedNumber taggedNumber = JSTaggedValue::ToNumber(thread, taggedVal);
1479                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1480                 val = taggedNumber.GetNumber();
1481             } else {
1482                 val = taggedVal.GetNumber();
1483             }
1484             BuiltinsArrayBufferType<typedArrayKind>::Type::FastSetValueInBuffer(
1485                 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, val, true);
1486             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1487             targetByteIndex += targetElementSize;
1488         }
1489 
1490         for (uint32_t i = copyLen; i < srcLength; i++) {
1491             val = JSTaggedNumber(base::NAN_VALUE).GetNumber();
1492             BuiltinsArrayBufferType<typedArrayKind>::Type::FastSetValueInBuffer(
1493                 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, val, true);
1494             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1495             targetByteIndex += targetElementSize;
1496         }
1497     }
1498     return JSTaggedValue::Undefined();
1499 }
1500 
At(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)1501 JSTaggedValue JSStableArray::At(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
1502 {
1503     JSThread *thread = argv->GetThread();
1504     uint32_t thisLen = receiver->GetArrayLength();
1505     if (thisLen == 0) {
1506         return JSTaggedValue::Undefined();
1507     }
1508     JSTaggedNumber index = JSTaggedValue::ToInteger(thread, base::BuiltinsBase::GetCallArg(argv, 0));
1509     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1510     int64_t relativeIndex = index.GetNumber();
1511     int64_t k = 0;
1512     if (relativeIndex >= 0) {
1513         k = relativeIndex;
1514     } else {
1515         k = static_cast<int64_t>(thisLen) + relativeIndex;
1516     }
1517     if (k < 0 || k >= thisLen) {
1518         return JSTaggedValue::Undefined();
1519     }
1520 
1521     auto result = JSTaggedValue::Hole();
1522     result = ElementAccessor::Get(thread, JSHandle<JSObject>::Cast(receiver), k);
1523     return result.IsHole() ? JSTaggedValue::Undefined() : result;
1524 }
1525 
At(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)1526 JSTaggedValue JSStableArray::At(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
1527 {
1528     JSThread *thread = argv->GetThread();
1529     uint32_t thisLen = receiver->GetArrayLength();
1530     if (thisLen == 0) {
1531         return JSTaggedValue::Undefined();
1532     }
1533     JSTaggedNumber index = JSTaggedValue::ToInteger(thread, base::BuiltinsBase::GetCallArg(argv, 0));
1534     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1535     int64_t relativeIndex = index.GetNumber();
1536     int64_t k = 0;
1537     if (relativeIndex >= 0) {
1538         k = relativeIndex;
1539     } else {
1540         k = static_cast<int64_t>(thisLen) + relativeIndex;
1541     }
1542     if (k < 0 || k >= thisLen) {
1543         return JSTaggedValue::Undefined();
1544     }
1545 
1546     auto result = JSTaggedValue::Hole();
1547     result = ElementAccessor::Get(thread, JSHandle<JSObject>::Cast(receiver), k);
1548     return result.IsHole() ? JSTaggedValue::Undefined() : result;
1549 }
1550 
With(JSThread * thread,JSHandle<JSArray> receiver,int64_t insertCount,int64_t index,JSHandle<JSTaggedValue> value)1551 JSTaggedValue JSStableArray::With(JSThread *thread, JSHandle<JSArray> receiver,
1552                                   int64_t insertCount, int64_t index, JSHandle<JSTaggedValue> value)
1553 {
1554     JSHandle<JSObject> thisObjHandle(receiver);
1555     JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1556     JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1557     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1558     JSHandle<JSObject> newArrayHandle(newArray);
1559 
1560     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1561     TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements(thread).GetTaggedObject());
1562 
1563     if (insertCount > ElementAccessor::GetElementsLength(thread, newArrayHandle)) {
1564         destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1565     }
1566     ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1567     bool needTransition = true;
1568     for (uint32_t idx = 0; idx < insertCount; idx++) {
1569         if (idx == index) {
1570             ElementAccessor::Set(thread, newArrayHandle, idx, value, needTransition);
1571         } else {
1572             JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thread, thisObjHandle, idx));
1573             if (kValue->IsHole()) {
1574                 ElementAccessor::Set(thread, newArrayHandle, idx, undefinedHandle, needTransition);
1575             } else {
1576                 ElementAccessor::Set(thread, newArrayHandle, idx, kValue, needTransition);
1577             }
1578         }
1579     }
1580     JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1581     return newArrayHandle.GetTaggedValue();
1582 }
1583 
ToSpliced(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv,int64_t argc,int64_t actualStart,int64_t actualSkipCount,int64_t insertCount)1584 JSTaggedValue JSStableArray::ToSpliced(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv,
1585                                        int64_t argc, int64_t actualStart, int64_t actualSkipCount, int64_t insertCount)
1586 {
1587     JSThread *thread = argv->GetThread();
1588 
1589     JSHandle<JSObject> thisObjHandle(receiver);
1590     JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1591     JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1592     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1593     JSHandle<JSObject> newArrayHandle(newArray);
1594 
1595     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1596     TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements(thread).GetTaggedObject());
1597 
1598     if (insertCount > ElementAccessor::GetElementsLength(thread, newArrayHandle)) {
1599         destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1600     }
1601     ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1602     int64_t i = 0;
1603     int64_t r = actualStart + actualSkipCount;
1604     bool needTransition = true;
1605     for (int64_t idx = 0; idx < actualStart; idx++, i++) {
1606         JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thread, thisObjHandle, idx));
1607         if (kValue->IsHole()) {
1608             ElementAccessor::Set(thread, newArrayHandle, i, undefinedHandle, needTransition);
1609         } else {
1610             ElementAccessor::Set(thread, newArrayHandle, i, kValue, needTransition);
1611         }
1612     }
1613 
1614     for (uint32_t pos = 2; pos < argc; ++pos) { // 2:2 means there two arguments before the insert items.
1615         auto element = base::BuiltinsBase::GetCallArg(argv, pos);
1616         ElementAccessor::Set(thread, newArrayHandle, i, element, needTransition);
1617         ++i;
1618     }
1619 
1620     while (i < insertCount) {
1621         JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thread, thisObjHandle, r));
1622         if (kValue->IsHole()) {
1623             ElementAccessor::Set(thread, newArrayHandle, i, undefinedHandle, needTransition);
1624         } else {
1625             ElementAccessor::Set(thread, newArrayHandle, i, kValue, needTransition);
1626         }
1627         ++i;
1628         ++r;
1629     }
1630 
1631     JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1632 
1633     return newArrayHandle.GetTaggedValue();
1634 }
1635 
ToReversed(JSThread * thread,JSHandle<JSArray> receiver,int64_t insertCount)1636 JSTaggedValue JSStableArray::ToReversed(JSThread *thread, JSHandle<JSArray> receiver,
1637                                         int64_t insertCount)
1638 {
1639     JSHandle<JSObject> thisObjHandle(receiver);
1640     JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1641     JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1642     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1643     JSHandle<JSObject> newArrayHandle(newArray);
1644 
1645     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1646     TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements(thread).GetTaggedObject());
1647 
1648     if (insertCount > ElementAccessor::GetElementsLength(thread, newArrayHandle)) {
1649         destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1650     }
1651     ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1652     bool needTransition = true;
1653     for (uint32_t idx = 0; idx < insertCount; idx++) {
1654         JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thread, thisObjHandle, idx));
1655         if (kValue->IsHole()) {
1656             ElementAccessor::Set(thread, newArrayHandle, insertCount - idx - 1,
1657                                  undefinedHandle, needTransition);
1658         } else {
1659             ElementAccessor::Set(thread, newArrayHandle, insertCount - idx - 1, kValue, needTransition);
1660         }
1661     }
1662     JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1663 
1664     return newArrayHandle.GetTaggedValue();
1665 }
1666 
Reduce(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSMutableHandle<JSTaggedValue> accumulator,int64_t & k,int64_t & len)1667 JSTaggedValue JSStableArray::Reduce(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1668                                     JSHandle<JSTaggedValue> callbackFnHandle,
1669                                     JSMutableHandle<JSTaggedValue> accumulator, int64_t &k, int64_t &len)
1670 {
1671     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1672     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1673     JSTaggedValue callResult = JSTaggedValue::Undefined();
1674     while (k < len) {
1675         JSTaggedValue kValue(ElementAccessor::Get(thread, thisObjHandle, k));
1676         if (!kValue.IsHole()) {
1677             JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1678             const uint32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
1679             EcmaRuntimeCallInfo *info =
1680                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, undefined, undefined, argsLength);
1681             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1682             info->SetCallArg(accumulator.GetTaggedValue(), kValue, JSTaggedValue(k),
1683                              thisObjVal.GetTaggedValue());
1684             callResult = JSFunction::Call(info);
1685             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1686             if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) {
1687                 len = ElementAccessor::GetElementsLength(thread, thisObjHandle);
1688             }
1689             accumulator.Update(callResult);
1690         }
1691         k++;
1692         if (!thisObjVal->IsStableJSArray(thread)) {
1693             break;
1694         }
1695     }
1696     return base::BuiltinsBase::GetTaggedDouble(true);
1697 }
1698 
Slice(JSThread * thread,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & count)1699 JSTaggedValue JSStableArray::Slice(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1700                                    int64_t &k, int64_t &count)
1701 {
1702     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1703     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1704     int64_t len = static_cast<int64_t>(ElementAccessor::GetElementsLength(thread, thisObjHandle));
1705     int64_t oldLen;
1706     if (len > k + count) {
1707         oldLen = count;
1708     } else {
1709         oldLen = std::max<int64_t>(len - k, 0);
1710     }
1711     JSHandle<JSObject> arrayObj = factory->NewAndCopyJSArrayObject(thisObjHandle, count, oldLen, k);
1712     for (int i = 0; i < count; i++) {
1713         JSMutableHandle<JSTaggedValue> value(thread, ElementAccessor::Get(thread, arrayObj, i));
1714         if (value->IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, i + k)) {
1715             value.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, i + k).GetTaggedValue());
1716             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1717             ElementAccessor::Set(thread, arrayObj, i, value, true);
1718         }
1719     }
1720     return arrayObj.GetTaggedValue();
1721 }
1722 
SortIndexedProperties(JSThread * thread,const JSHandle<JSTaggedValue> & thisObjVal,int64_t len,const JSHandle<JSTaggedValue> & callbackFnHandle,base::HolesType holes)1723 JSHandle<TaggedArray> JSStableArray::SortIndexedProperties(JSThread *thread, const JSHandle<JSTaggedValue> &thisObjVal,
1724                                                            int64_t len, const JSHandle<JSTaggedValue> &callbackFnHandle,
1725                                                            base::HolesType holes)
1726 {
1727     JSHandle<JSObject> thisObj(thread, thisObjVal.GetTaggedValue());
1728     JSHandle<TaggedArray> elements(thread, thisObj->GetElements(thread));
1729     ElementsKind kind = thisObj->GetClass()->GetElementsKind();
1730     if (!elements->GetClass()->IsMutantTaggedArray()) {
1731         kind = ElementsKind::GENERIC;
1732     }
1733     // 1. fill elements into items.
1734     JSHandle<TaggedArray> items(thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len));
1735     bool kRead = false;
1736     int64_t tmp = 0;
1737     for (int k = 0; k < len; k++) {
1738         JSTaggedValue kValue = ElementAccessor::FastGet(thread, elements, k, kind);
1739         if (holes == base::HolesType::SKIP_HOLES) {
1740             kRead = (kValue != JSTaggedValue::Hole());
1741         } else {
1742             ASSERT(holes == base::HolesType::READ_THROUGH_HOLES);
1743             kRead = true;
1744         }
1745         if (kRead) {
1746             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, items);
1747             items->Set(thread, tmp++, kValue);
1748         }
1749     }
1750     // 2. trim
1751     if (len > tmp) {
1752         items->Trim(thread, tmp);
1753     }
1754     // 3. Sort items using an implementation-defined sequence of calls to SortCompare.
1755     // If any such call returns an abrupt completion,
1756     // stop before performing any further calls to SortCompare and return that Completion Record.
1757     base::TimSort::Sort(thread, items, callbackFnHandle);
1758     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, items);
1759     // 4. Return items.
1760     return items;
1761 }
1762 
CopySortedListToReceiver(JSThread * thread,const JSHandle<JSTaggedValue> & thisObjVal,JSHandle<TaggedArray> sortedList,uint32_t len)1763 JSTaggedValue JSStableArray::CopySortedListToReceiver(JSThread *thread, const JSHandle<JSTaggedValue> &thisObjVal,
1764                                                       JSHandle<TaggedArray> sortedList, uint32_t len)
1765 {
1766     // 6. Let itemCount be the number of elements in sortedList.
1767     uint32_t itemCount = sortedList->GetLength();
1768 
1769     // grow elements if len > newLength.
1770     JSHandle<JSObject> thisObj(thisObjVal);
1771     uint32_t newLength = std::max(JSHandle<JSArray>::Cast(thisObjVal)->GetArrayLength(), itemCount);
1772     TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements(thread).GetTaggedObject());
1773     if (newLength > ElementAccessor::GetElementsLength(thread, thisObj)) {
1774         elements = *JSObject::GrowElementsCapacity(thread, thisObj, newLength, true);
1775     }
1776 
1777     JSMutableHandle<JSTaggedValue> valueHandle(thread, JSTaggedValue::Undefined());
1778     bool needTransition = true;
1779     // 7. Let j be 0.
1780     // 8. Repeat, while j < itemCount,
1781     //     a. Perform ! Set(obj, ! ToString((j)), sortedList[j], true).
1782     //     b. Set j to j + 1.
1783     for (uint32_t j = 0; j < itemCount; j++) {
1784         valueHandle.Update(sortedList->Get(thread, j));
1785         ElementAccessor::Set(thread, thisObj, j, valueHandle, needTransition);
1786     }
1787     // 9. NOTE: The call to SortIndexedProperties in step 5 uses SKIP-HOLES.The remaining indices are deleted to
1788     // preserve the number of holes that were detected and excluded from the sort.
1789     // 10. Repeat, while j < len,
1790     //       a. Perform ? DeletePropertyOrThrow(obj, ! ToString((j))).
1791     //       b. Set j to j + 1.
1792     valueHandle.Update(JSTaggedValue::Hole());
1793     for (uint32_t j = itemCount; j < newLength; j++) {
1794         ElementAccessor::Set(thread, thisObj, j, valueHandle, needTransition);
1795     }
1796     JSHandle<JSArray>::Cast(thisObj)->SetArrayLength(thread, newLength);
1797     return thisObj.GetTaggedValue();
1798 }
1799 
Sort(JSThread * thread,const JSHandle<JSTaggedValue> & thisObjVal,const JSHandle<JSTaggedValue> & callbackFnHandle)1800 JSTaggedValue JSStableArray::Sort(JSThread *thread, const JSHandle<JSTaggedValue> &thisObjVal,
1801                                   const JSHandle<JSTaggedValue> &callbackFnHandle)
1802 {
1803     // 3. Let len be ?LengthOfArrayLike(obj).
1804     int64_t len = base::ArrayHelper::GetArrayLength(thread, thisObjVal);
1805     // ReturnIfAbrupt(len).
1806     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1807     // If len is 0 or 1, no need to sort
1808     if (len == 0 || len == 1) {
1809         return thisObjVal.GetTaggedValue();
1810     }
1811     if (callbackFnHandle->IsUndefined()) {
1812         JSArray::SortElementsByObject(thread, JSHandle<JSObject>::Cast(thisObjVal), callbackFnHandle);
1813         return thisObjVal.GetTaggedValue();
1814     }
1815     JSHandle<TaggedArray> sortedList = JSStableArray::SortIndexedProperties(
1816         thread, thisObjVal, len, callbackFnHandle, base::HolesType::SKIP_HOLES);
1817     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1818     if (thisObjVal->IsStableJSArray(thread)) {
1819         CopySortedListToReceiver(thread, thisObjVal, sortedList, len);
1820     } else {
1821         JSArray::CopySortedListToReceiver(thread, thisObjVal, sortedList, len);
1822         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1823     }
1824     return thisObjVal.GetTaggedValue();
1825 }
1826 
Fill(JSThread * thread,const JSHandle<JSObject> & thisObj,const JSHandle<JSTaggedValue> & value,int64_t start,int64_t end)1827 JSTaggedValue JSStableArray::Fill(JSThread *thread, const JSHandle<JSObject> &thisObj,
1828                                   const JSHandle<JSTaggedValue> &value, int64_t start,
1829                                   int64_t end)
1830 {
1831     JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObj));
1832     uint32_t length = ElementAccessor::GetElementsLength(thread, thisObj);
1833     ElementsKind oldKind = thisObj->GetClass()->GetElementsKind();
1834     if (start == 0 && end == length) {
1835         if (oldKind != ElementsKind::GENERIC) {
1836             JSHClass::TransitToElementsKindUncheck(thread, thisObj, Elements::ToElementsKind(value.GetTaggedValue()));
1837         }
1838     } else if (JSHClass::TransitToElementsKind(thread, thisObj, value)) {
1839         ElementsKind newKind = thisObj->GetClass()->GetElementsKind();
1840         Elements::MigrateArrayWithKind(thread, thisObj, oldKind, newKind);
1841     }
1842     if (length >= end) {
1843         if (thisObj->GetElements(thread).IsMutantTaggedArray()) {
1844             ElementsKind kind = thisObj->GetClass()->GetElementsKind();
1845             TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements(thread));
1846             JSTaggedValue migratedValue = JSTaggedValue(ElementAccessor::ConvertTaggedValueWithElementsKind(
1847                 value.GetTaggedValue(), kind));
1848             for (int64_t idx = start; idx < end; idx++) {
1849                 elements->Set<false>(thread, idx, migratedValue);
1850             }
1851         } else {
1852             TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements(thread));
1853             for (int64_t idx = start; idx < end; idx++) {
1854                 elements->Set(thread, idx, value);
1855             }
1856         }
1857     } else {
1858         LOG_ECMA(FATAL) << "this branch is unreachable";
1859         UNREACHABLE();
1860     }
1861     if (JSHandle<JSArray>::Cast(thisObj)->GetArrayLength() < end) {
1862         JSHandle<JSArray>::Cast(thisObj)->SetArrayLength(thread, end);
1863     }
1864     return thisObj.GetTaggedValue();
1865 }
1866 
HandleFindLastOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,JSMutableHandle<JSTaggedValue> & kValue,int64_t & k)1867 JSTaggedValue JSStableArray::HandleFindLastOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1868                                                     JSHandle<JSTaggedValue> callbackFnHandle,
1869                                                     JSHandle<JSTaggedValue> thisArgHandle,
1870                                                     JSMutableHandle<JSTaggedValue> &kValue, int64_t &k)
1871 {
1872     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1873     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1874     const uint32_t argsLength = 3; // 3: «kValue, k, O»
1875     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
1876     while (k >= 0) {
1877         JSTaggedValue val = ElementAccessor::Get(thread, thisObjHandle, k);
1878         if (!val.IsHole()) {
1879             kValue.Update(val);
1880         } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1881             auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1882             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1883             kValue.Update(res);
1884         } else {
1885             kValue.Update(JSTaggedValue::Undefined());
1886         }
1887         EcmaRuntimeCallInfo *info =
1888             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1889         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1890         info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
1891         callResult = JSFunction::Call(info);
1892         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1893         if (callResult.ToBoolean()) {
1894             return callResult;
1895         }
1896         k--;
1897         if (base::ArrayHelper::GetArrayLength(thread, thisObjVal) <= k) {
1898             break;
1899         }
1900         if (!thisObjVal->IsStableJSArray(thread)) {
1901             break;
1902         }
1903     }
1904     return callResult;
1905 }
1906 
HandleReduceRightOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSMutableHandle<JSTaggedValue> & accumulator,JSHandle<JSTaggedValue> thisArgHandle,int64_t & k)1907 JSTaggedValue JSStableArray::HandleReduceRightOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1908                                                        JSHandle<JSTaggedValue> callbackFnHandle,
1909                                                        JSMutableHandle<JSTaggedValue> &accumulator,
1910                                                        JSHandle<JSTaggedValue> thisArgHandle, int64_t &k)
1911 {
1912     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1913     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Hole());
1914     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1915     JSTaggedValue callResult = JSTaggedValue::Undefined();
1916     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1917     const int32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
1918     int64_t len = static_cast<int64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1919     while (k >= 0) {
1920         key.Update(JSTaggedValue(k));
1921         kValue.Update(ElementAccessor::Get(thread, thisObjHandle, k));
1922         if (!kValue.GetTaggedValue().IsHole()) {
1923             EcmaRuntimeCallInfo *info =
1924                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1925             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1926             info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(),
1927                 key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1928             callResult = JSFunction::Call(info);
1929             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1930             accumulator.Update(callResult);
1931         } else {
1932             bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, key);
1933             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1934             if (exists) {
1935                 auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, key).GetTaggedValue();
1936                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1937                 kValue.Update(res);
1938                 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle,
1939                     thisArgHandle, undefined, argsLength);
1940                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1941                 info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(),
1942                     key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1943                 callResult = JSFunction::Call(info);
1944                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1945                 accumulator.Update(callResult);
1946             }
1947         }
1948         k--;
1949         int64_t newLen = static_cast<int64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1950         if (!thisObjVal->IsStableJSArray(thread) || newLen != len) {
1951             return base::BuiltinsBase::GetTaggedBoolean(false);
1952         }
1953     }
1954     return base::BuiltinsBase::GetTaggedBoolean(true);
1955 }
1956 }  // namespace panda::ecmascript
1957