• 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 
18 #include "ecmascript/base/typed_array_helper-inl.h"
19 #include "ecmascript/interpreter/fast_runtime_stub-inl.h"
20 
21 namespace panda::ecmascript {
22 using TypedArrayHelper = base::TypedArrayHelper;
23 using TypedArrayKind = base::TypedArrayKind;
24 using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer;
25 using BuiltinsSendableArrayBuffer = builtins::BuiltinsSendableArrayBuffer;
26 template<TypedArrayKind typedArrayKind>
27 using BuiltinsArrayBufferType = base::BuiltinsArrayBufferType<typedArrayKind>;
28 
Push(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)29 JSTaggedValue JSStableArray::Push(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
30 {
31     JSThread *thread = argv->GetThread();
32     uint32_t argc = argv->GetArgsNumber();
33     uint32_t oldLength = receiver->GetArrayLength();
34     uint32_t newLength = argc + oldLength;
35     JSHandle<JSObject> thisObjHandle(receiver);
36 
37     TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
38     if (newLength > ElementAccessor::GetElementsLength(thisObjHandle)) {
39         elements = *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     TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
60     if (newLength > ElementAccessor::GetElementsLength(thisObjHandle)) {
61         elements = *JSObject::GrowElementsCapacity(thread, JSHandle<JSObject>::Cast(receiver), newLength, true);
62     }
63     bool needTransition = true;
64     for (uint32_t k = 0; k < argc; k++) {
65         JSHandle<JSTaggedValue> value = argv->GetCallArg(k);
66         ElementAccessor::Set(thread, thisObjHandle, oldLength + k, value, needTransition);
67     }
68     receiver->SetArrayLength(thread, newLength);
69 
70     return JSTaggedValue(newLength);
71 }
72 
Pop(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)73 JSTaggedValue JSStableArray::Pop(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
74 {
75     JSThread *thread = argv->GetThread();
76     uint32_t length = receiver->GetArrayLength();
77     if (length == 0) {
78         return JSTaggedValue::Undefined();
79     }
80     JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
81     JSSharedArray::CheckAndCopyArray(thread, receiver);
82     JSHandle<JSObject> obj(receiver);
83     uint32_t capacity = ElementAccessor::GetElementsLength(obj);
84     uint32_t index = length - 1;
85     JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Hole());
86     if (index < capacity) {
87         result.Update(ElementAccessor::Get(obj, index));
88     }
89     if (!result->IsHole()) {
90         if (TaggedArray::ShouldTrim(capacity, index)) {
91             TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
92             elements->Trim(thread, index);
93         } else {
94             ElementAccessor::Set(thread, obj, index, holeHandle, false);
95         }
96     } else {
97         JSHandle<JSTaggedValue> thisObjVal(receiver);
98         result.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, index).GetTaggedValue());
99     }
100     receiver->SetArrayLength(thread, index);
101     return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
102 }
103 
Pop(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)104 JSTaggedValue JSStableArray::Pop(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
105 {
106     JSThread *thread = argv->GetThread();
107     uint32_t length = receiver->GetArrayLength();
108     if (length == 0) {
109         return JSTaggedValue::Undefined();
110     }
111     JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
112     JSArray::CheckAndCopyArray(thread, receiver);
113     JSHandle<JSObject> obj(receiver);
114     uint32_t capacity = ElementAccessor::GetElementsLength(obj);
115     uint32_t index = length - 1;
116     JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Hole());
117     if (index < capacity) {
118         result.Update(ElementAccessor::Get(obj, index));
119     }
120     if (!result->IsHole()) {
121         if (TaggedArray::ShouldTrim(capacity, index)) {
122             TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
123             elements->Trim(thread, index);
124         } else {
125             ElementAccessor::Set(thread, obj, index, holeHandle, false);
126         }
127     } else {
128         JSHandle<JSTaggedValue> thisObjVal(receiver);
129         result.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, index).GetTaggedValue());
130     }
131     receiver->SetArrayLength(thread, index);
132     return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
133 }
134 
HandleArray(JSHandle<JSObject> & newArrayHandle,uint32_t & actualDeleteCount,JSThread * thread,uint32_t & start,JSHandle<JSObject> & thisObjHandle,JSHandle<JSTaggedValue> & holeHandle)135 void JSStableArray::HandleArray(JSHandle<JSObject> &newArrayHandle, uint32_t &actualDeleteCount,
136                                 JSThread *thread, uint32_t &start, JSHandle<JSObject> &thisObjHandle,
137                                 JSHandle<JSTaggedValue> &holeHandle)
138 {
139     TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
140         if (actualDeleteCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
141             destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, actualDeleteCount);
142         }
143 
144         for (uint32_t idx = 0; idx < actualDeleteCount; idx++) {
145             if ((start + idx) >= ElementAccessor::GetElementsLength(thisObjHandle)) {
146                 ElementAccessor::Set(thread, newArrayHandle, idx, holeHandle, true);
147             } else {
148                 JSHandle<JSTaggedValue> valueHandle(thread, ElementAccessor::Get(thisObjHandle, start + idx));
149                 ElementAccessor::Set(thread, newArrayHandle, idx, valueHandle, true);
150             }
151         }
152         JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, actualDeleteCount);
153 }
154 
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)155 JSTaggedValue JSStableArray::UpdateArrayCapacity(JSHandle<JSObject> &thisObjHandle, uint32_t &len,
156                                                  uint32_t &insertCount, uint32_t &actualDeleteCount,
157                                                  JSHandle<JSArray> &receiver, uint32_t &start,
158                                                  JSThread *thread, bool &needTransition,
159                                                  JSHandle<JSTaggedValue> &holeHandle,
160                                                  EcmaRuntimeCallInfo *argv, JSHandle<JSTaggedValue> &thisObjVal,
161                                                  JSHandle<JSTaggedValue> &lengthKey)
162 {
163     uint32_t oldCapacity = ElementAccessor::GetElementsLength(thisObjHandle);
164     ASSERT(len + insertCount >= actualDeleteCount);
165     uint32_t newCapacity = len - actualDeleteCount + insertCount;
166     TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements().GetTaggedObject());
167     JSMutableHandle<TaggedArray> srcElementsHandle(thread, srcElements);
168     uint32_t argc = argv->GetArgsNumber();
169     if (newCapacity > oldCapacity) {
170         srcElementsHandle.Update(JSObject::GrowElementsCapacity(thread, thisObjHandle, newCapacity));
171     }
172     if (insertCount < actualDeleteCount) {
173         JSArray::CheckAndCopyArray(thread, receiver);
174         srcElementsHandle.Update(receiver->GetElements());
175         for (uint32_t idx = start; idx < len - actualDeleteCount; idx++) {
176             JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Hole());
177             if ((idx + actualDeleteCount) < ElementAccessor::GetElementsLength(thisObjHandle)) {
178                 element.Update(ElementAccessor::Get(thisObjHandle, idx + actualDeleteCount));
179             }
180             if ((idx + insertCount) < ElementAccessor::GetElementsLength(thisObjHandle)) {
181                 ElementAccessor::Set(thread, thisObjHandle, idx + insertCount, element, needTransition);
182             }
183         }
184 
185         if ((oldCapacity > newCapacity) && TaggedArray::ShouldTrim(oldCapacity, newCapacity)) {
186             srcElementsHandle->Trim(thread, newCapacity);
187         } else {
188             for (uint32_t idx = newCapacity; idx < len; idx++) {
189                 if (idx < ElementAccessor::GetElementsLength(thisObjHandle)) {
190                     ElementAccessor::Set(thread, thisObjHandle, idx, holeHandle, needTransition);
191                 }
192             }
193         }
194     } else {
195         ASSERT(len >= actualDeleteCount);
196         for (uint32_t idx = len - actualDeleteCount; idx > start; idx--) {
197             JSHandle<JSTaggedValue> element(thread, ElementAccessor::Get(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().GetTaggedObject());
273     JSMutableHandle<TaggedArray> srcElementsHandle(thread, srcElements);
274     bool needTransition = true;
275     if (newArrayHandle.GetTaggedValue().IsStableJSArray(thread)) {
276         TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
277         if (actualDeleteCount > ElementAccessor::GetElementsLength(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(thisObjHandle)) {
283                 ElementAccessor::Set(thread, newArrayHandle, idx, holeHandle, needTransition);
284             } else {
285                 JSHandle<JSTaggedValue> valueHandle(thread, ElementAccessor::Get(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(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());
327         for (uint32_t idx = start; idx < len - actualDeleteCount; idx++) {
328             JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Hole());
329             if ((idx + actualDeleteCount) < ElementAccessor::GetElementsLength(thisObjHandle)) {
330                 element.Update(ElementAccessor::Get(thisObjHandle, idx + actualDeleteCount));
331             }
332             if ((idx + insertCount) < ElementAccessor::GetElementsLength(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(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, ElementAccessor::Get(thisObjHandle, idx + actualDeleteCount - 1));
350             ElementAccessor::Set(thread, thisObjHandle, idx + insertCount - 1, element, needTransition);
351         }
352     }
353 
354     for (uint32_t i = 2, idx = start; i < argc; i++, idx++) {
355         ElementAccessor::Set(thread, thisObjHandle, idx, argv->GetCallArg(i), needTransition);
356     }
357 
358     JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newCapacity));
359     JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
360     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
361     return newArrayHandle.GetTaggedValue();
362 }
363 
Shift(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)364 JSTaggedValue JSStableArray::Shift(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
365 {
366     JSThread *thread = argv->GetThread();
367     JSHandle<JSObject> thisObjHandle(receiver);
368     JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
369     uint32_t length = receiver->GetArrayLength();
370     if (length == 0) {
371         return JSTaggedValue::Undefined();
372     }
373     JSSharedArray::CheckAndCopyArray(thread, receiver);
374     TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
375     JSHandle<JSTaggedValue> result(thread, ElementAccessor::Get(thisObjHandle, 0));
376     bool needTransition = false;
377     for (uint32_t k = 1; k < length; k++) {
378         JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thisObjHandle, k));
379         ElementAccessor::Set(thread, thisObjHandle, k - 1, kValue, needTransition);
380     }
381     uint32_t capacity = ElementAccessor::GetElementsLength(thisObjHandle);
382     uint32_t index = length - 1;
383     if (TaggedArray::ShouldTrim(capacity, index)) {
384         elements->Trim(thread, index);
385     } else {
386         ElementAccessor::Set(thread, thisObjHandle, index, holeHandle, needTransition);
387     }
388     receiver->SetArrayLength(thread, index);
389     return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
390 }
391 
Shift(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)392 JSTaggedValue JSStableArray::Shift(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
393 {
394     JSThread *thread = argv->GetThread();
395     JSHandle<JSObject> thisObjHandle(receiver);
396     JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
397     uint32_t length = receiver->GetArrayLength();
398     if (length == 0) {
399         return JSTaggedValue::Undefined();
400     }
401     JSArray::CheckAndCopyArray(thread, receiver);
402     JSHandle<TaggedArray> elements(thread, TaggedArray::Cast(receiver->GetElements().GetTaggedObject()));
403     JSHandle<JSTaggedValue> result(thread, ElementAccessor::Get(thisObjHandle, 0));
404     bool needTransition = false;
405     for (uint32_t k = 1; k < length; k++) {
406         JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thisObjHandle, k));
407         ElementAccessor::Set(thread, thisObjHandle, k - 1, kValue, needTransition);
408     }
409     uint32_t capacity = ElementAccessor::GetElementsLength(thisObjHandle);
410     uint32_t index = length - 1;
411     if (TaggedArray::ShouldTrim(capacity, index)) {
412         elements->Trim(thread, index);
413     } else {
414         ElementAccessor::Set(thread, thisObjHandle, index, holeHandle, needTransition);
415     }
416     receiver->SetArrayLength(thread, index);
417     return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
418 }
419 
SetSepValue(JSHandle<EcmaString> sepStringHandle,int & sep,uint32_t & sepLength)420 void JSStableArray::SetSepValue(JSHandle<EcmaString> sepStringHandle, int &sep, uint32_t &sepLength)
421 {
422     if (EcmaStringAccessor(sepStringHandle).IsUtf8() && EcmaStringAccessor(sepStringHandle).GetLength() == 1) {
423         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
424         sep = EcmaStringAccessor(sepStringHandle).Get(0);
425     } else if (EcmaStringAccessor(sepStringHandle).GetLength() == 0) {
426         sep = JSStableArray::SeparatorFlag::MINUS_TWO;
427         sepLength = 0;
428     } else {
429         sep = JSStableArray::SeparatorFlag::MINUS_ONE;
430         sepLength = EcmaStringAccessor(sepStringHandle).GetLength();
431     }
432 }
433 
WorthUseTreeString(int sep,size_t allocateLength,uint32_t len)434 bool JSStableArray::WorthUseTreeString(int sep, size_t allocateLength, uint32_t len)
435 {
436     if (allocateLength >= TREE_STRING_THRESHOLD) {
437         size_t treeStringElementNum = (sep == MINUS_TWO) ? (len - 1) : (2 * (len - 1));
438         // if sep is MINUS_TWO, means all the elements in treeString is len -1;
439         // otherwise, the num of elements is (len-1)(string in vector) + (len -1)(num of seps)
440         if (treeStringElementNum * TreeEcmaString::SIZE <= allocateLength) {
441             // heuristic: if tree string uses less memory than linestring, it is worth.
442             // In other words, we hope tree string can work for the large strings join.
443             return true;
444         }
445     }
446     return false;
447 }
448 
JoinUseTreeString(const JSThread * thread,const JSHandle<JSTaggedValue> receiverValue,const JSHandle<EcmaString> sepStringHandle,const int sep,CVector<JSHandle<EcmaString>> & vec)449 JSTaggedValue JSStableArray::JoinUseTreeString(const JSThread* thread, const JSHandle<JSTaggedValue> receiverValue,
450                                                const JSHandle<EcmaString> sepStringHandle, const int sep,
451                                                CVector<JSHandle<EcmaString>>& vec)
452 {
453     // Do not concat the elements one by one, it will make the tree string unbalanced. Concat each element with its
454     // right neighbor first level by level, then the tree string will be balanced as possible.
455     if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
456         auto last = std::prev(vec.end());
457         for (auto iter = vec.begin(); iter != last; ++iter) {
458             *iter = JSHandle<EcmaString>(
459                 thread, EcmaStringAccessor::Concat(thread->GetEcmaVM(), *iter, sepStringHandle));
460             RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
461         }
462     }
463     size_t elemNum = vec.size();
464     while (elemNum > 1) {
465         size_t newNum = (elemNum + 1) / NUM_2;
466         for (size_t i = 0; i < elemNum / NUM_2; ++i) {
467             vec[i] = JSHandle<EcmaString>(
468                 thread, EcmaStringAccessor::Concat(thread->GetEcmaVM(), vec[NUM_2 * i], vec[NUM_2 * i + 1]));
469             RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
470         }
471         if (elemNum % NUM_2 == 1) {
472             vec[newNum - 1] = vec[elemNum - 1];
473         }
474         elemNum = newNum;
475     }
476     thread->GetCurrentEcmaContext()->JoinStackPopFastPath(receiverValue);
477     return vec.front().GetTaggedValue();
478 }
479 
Join(JSThread * thread,JSHandle<JSArray> receiver,JSHandle<EcmaString> sepStringHandle,int64_t length)480 JSTaggedValue JSStableArray::Join(JSThread *thread, JSHandle<JSArray> receiver,
481                                   JSHandle<EcmaString> sepStringHandle, int64_t length)
482 {
483     int sep = ',';
484     uint32_t sepLength = 1;
485     auto context = thread->GetCurrentEcmaContext();
486     JSHandle<JSTaggedValue> receiverValue = JSHandle<JSTaggedValue>::Cast(receiver);
487     SetSepValue(sepStringHandle, sep, sepLength);
488     auto factory = thread->GetEcmaVM()->GetFactory();
489     bool noCircular = context->JoinStackPushFastPath(receiverValue);
490     if (!noCircular) {
491         return factory->GetEmptyString().GetTaggedValue();
492     }
493     if (length == 0) {
494         const GlobalEnvConstants *globalConst = thread->GlobalConstants();
495         context->JoinStackPopFastPath(receiverValue);
496         return globalConst->GetEmptyString();
497     }
498     JSHandle<JSObject> obj(thread, receiverValue.GetTaggedValue());
499     uint64_t allocateLength = 0;
500     bool isOneByte = (sep != JSStableArray::SeparatorFlag::MINUS_ONE) || EcmaStringAccessor(sepStringHandle).IsUtf8();
501     JSMutableHandle<JSTaggedValue> elementHandle(thread, JSTaggedValue::Undefined());
502     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
503     uint32_t elementsLength = ElementAccessor::GetElementsLength(obj);
504     uint32_t len = elementsLength > length ? length : elementsLength;
505     if (elementsLength == 0 && length != 0) {
506         len = length;
507     }
508     if (len <= 1) {
509         // sep unused, set isOneByte to default(true)
510         isOneByte = true;
511     }
512     CVector<JSHandle<EcmaString>> vec;
513     vec.reserve(len);
514     for (uint32_t k = 0; k < len; k++) {
515         JSTaggedValue element = JSTaggedValue::Undefined();
516         if (k < elementsLength) {
517             element = ElementAccessor::Get(obj, k);
518             if (element.IsHole() && JSTaggedValue::HasProperty(thread, receiverValue, k)) {
519                 element = JSArray::FastGetPropertyByValue(thread, receiverValue, k).GetTaggedValue();
520                 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
521             }
522         }
523         if (!element.IsUndefinedOrNull() && !element.IsHole()) {
524             if (!element.IsString()) {
525                 elementHandle.Update(element);
526                 JSHandle<EcmaString> strElement = JSTaggedValue::ToString(thread, elementHandle);
527                 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
528                 element = strElement.GetTaggedValue();
529             }
530             auto nextStr = EcmaString::Cast(element.GetTaggedObject());
531             vec.emplace_back(thread, nextStr);
532             isOneByte = EcmaStringAccessor(nextStr).IsUtf8() ? isOneByte : false;
533             allocateLength += EcmaStringAccessor(nextStr).GetLength();
534         } else {
535             vec.emplace_back(globalConst->GetHandledEmptyString());
536         }
537     }
538     if (len > 0) {
539         allocateLength += static_cast<uint64_t>(sepLength) * (len - 1);
540     }
541     if (allocateLength > EcmaString::MAX_STRING_LENGTH) {
542         context->JoinStackPopFastPath(receiverValue);
543         THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
544     }
545     if (WorthUseTreeString(sep, allocateLength, len)) {
546         return JoinUseTreeString(thread, receiverValue, sepStringHandle, sep, vec);
547     }
548     auto newString =
549     EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), static_cast<size_t>(allocateLength), isOneByte);
550     int current = 0;
551     DISALLOW_GARBAGE_COLLECTION;
552     for (uint32_t k = 0; k < len; k++) {
553         if (k > 0) {
554             if (sep >= 0) {
555                 EcmaStringAccessor(newString).Set(current, static_cast<uint16_t>(sep));
556             } else if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
557                 EcmaStringAccessor::ReadData(newString, *sepStringHandle, current,
558                     allocateLength - static_cast<uint32_t>(current), sepLength);
559             }
560             current += static_cast<int>(sepLength);
561         }
562         JSHandle<EcmaString> nextStr = vec[k];
563         int nextLength = static_cast<int>(EcmaStringAccessor(nextStr).GetLength());
564         EcmaStringAccessor::ReadData(newString, *nextStr, current,
565             allocateLength - static_cast<uint32_t>(current), nextLength);
566         current += nextLength;
567     }
568     ASSERT_PRINT(
569         isOneByte == EcmaStringAccessor::CanBeCompressed(newString), "isOneByte does not match the real value!");
570     context->JoinStackPopFastPath(receiverValue);
571     return JSTaggedValue(newString);
572 }
573 
Join(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)574 JSTaggedValue JSStableArray::Join(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
575 {
576     JSThread *thread = argv->GetThread();
577     uint32_t length = receiver->GetArrayLength();
578     JSHandle<JSTaggedValue> sepHandle = base::BuiltinsBase::GetCallArg(argv, 0);
579     int sep = ',';
580     uint32_t sepLength = 1;
581     JSHandle<EcmaString> sepStringHandle;
582     auto context = thread->GetCurrentEcmaContext();
583     JSHandle<JSTaggedValue> receiverValue = JSHandle<JSTaggedValue>::Cast(receiver);
584     if (!sepHandle->IsUndefined()) {
585         if (sepHandle->IsString()) {
586             sepStringHandle = JSHandle<EcmaString>::Cast(sepHandle);
587         } else {
588             sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
589             RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
590         }
591         SetSepValue(sepStringHandle, sep, sepLength);
592     }
593     if (length == 0) {
594         const GlobalEnvConstants *globalConst = thread->GlobalConstants();
595         context->JoinStackPopFastPath(receiverValue);
596         return globalConst->GetEmptyString();
597     }
598     JSHandle<JSObject> obj(thread, receiverValue.GetTaggedValue());
599     size_t allocateLength = 0;
600     bool isOneByte = (sep != JSStableArray::SeparatorFlag::MINUS_ONE) || EcmaStringAccessor(sepStringHandle).IsUtf8();
601     JSMutableHandle<JSTaggedValue> elementHandle(thread, JSTaggedValue::Undefined());
602     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
603     uint32_t elementsLength = ElementAccessor::GetElementsLength(obj);
604     uint32_t len = elementsLength > length ? length : elementsLength;
605     if (elementsLength == 0 && length != 0) {
606         len = length;
607     }
608     if (len <= 1) {
609         // sep unused, set isOneByte to default(true)
610         isOneByte = true;
611     }
612     CVector<JSHandle<EcmaString>> vec;
613     vec.reserve(len);
614     for (uint32_t k = 0; k < len; k++) {
615         JSTaggedValue element = JSTaggedValue::Undefined();
616         if (k < elementsLength) {
617             element = ElementAccessor::Get(obj, k);
618         }
619         if (!element.IsUndefinedOrNull() && !element.IsHole()) {
620             if (!element.IsString()) {
621                 elementHandle.Update(element);
622                 JSHandle<EcmaString> strElement = JSTaggedValue::ToString(thread, elementHandle);
623                 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
624                 element = strElement.GetTaggedValue();
625             }
626             auto nextStr = EcmaString::Cast(element.GetTaggedObject());
627             vec.emplace_back(thread, nextStr);
628             isOneByte = EcmaStringAccessor(nextStr).IsUtf8() ? isOneByte : false;
629             allocateLength += EcmaStringAccessor(nextStr).GetLength();
630         } else {
631             vec.emplace_back(globalConst->GetHandledEmptyString());
632         }
633     }
634     if (len > 0) {
635         allocateLength += sepLength * (len - 1);
636     }
637     if (WorthUseTreeString(sep, allocateLength, len)) {
638         return JoinUseTreeString(thread, receiverValue, sepStringHandle, sep, vec);
639     }
640     auto newString = EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), allocateLength, isOneByte);
641     int current = 0;
642     DISALLOW_GARBAGE_COLLECTION;
643     for (uint32_t k = 0; k < len; k++) {
644         if (k > 0) {
645             if (sep >= 0) {
646                 EcmaStringAccessor(newString).Set(current, static_cast<uint16_t>(sep));
647             } else if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
648                 EcmaStringAccessor::ReadData(newString, *sepStringHandle, current,
649                     allocateLength - static_cast<uint32_t>(current), sepLength);
650             }
651             current += static_cast<int>(sepLength);
652         }
653         JSHandle<EcmaString> nextStr = vec[k];
654         int nextLength = static_cast<int>(EcmaStringAccessor(nextStr).GetLength());
655         EcmaStringAccessor::ReadData(newString, *nextStr, current,
656             allocateLength - static_cast<uint32_t>(current), nextLength);
657         current += nextLength;
658     }
659     ASSERT_PRINT(
660         isOneByte == EcmaStringAccessor::CanBeCompressed(newString), "isOneByte does not match the real value!");
661     context->JoinStackPopFastPath(receiverValue);
662     return JSTaggedValue(newString);
663 }
664 
HandleFindIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)665 JSTaggedValue JSStableArray::HandleFindIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
666                                                      JSHandle<JSTaggedValue> callbackFnHandle,
667                                                      JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
668 {
669     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
670     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
671     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
672     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
673     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
674     const int32_t argsLength = 3; // 3: ?kValue, k, O?
675     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
676     while (k < len) {
677         // Elements of thisObjHandle may change.
678         JSTaggedValue val = ElementAccessor::Get(thisObjHandle, k);
679         if (val.IsHole()) {
680             auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
681             if (res.IsHole()) {
682                 kValue.Update(JSTaggedValue::Undefined());
683             } else {
684                 kValue.Update(res);
685             }
686         } else {
687             kValue.Update(val);
688         }
689         EcmaRuntimeCallInfo *info =
690             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
691         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
692         info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
693         callResult = JSFunction::Call(info);
694         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
695         if (callResult.ToBoolean()) {
696             return callResult;
697         }
698         if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
699             len = ElementAccessor::GetElementsLength(thisObjHandle);
700         }
701         k++;
702         if (!thisObjVal->IsStableJSArray(thread)) {
703             return callResult;
704         }
705     }
706     return callResult;
707 }
708 
HandleFindLastIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,int64_t & k)709 JSTaggedValue JSStableArray::HandleFindLastIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
710                                                          JSHandle<JSTaggedValue> callbackFnHandle,
711                                                          JSHandle<JSTaggedValue> thisArgHandle, int64_t &k)
712 {
713     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
714     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
715     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
716     const int32_t argsLength = 3; // 3: ?kValue, k, O?
717     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
718     while (k >= 0) {
719         // Elements of thisObjHandle may change.
720         JSTaggedValue val = ElementAccessor::Get(thisObjHandle, k);
721         if (val.IsHole()) {
722             auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
723             if (res.IsHole()) {
724                 kValue.Update(JSTaggedValue::Undefined());
725             } else {
726                 kValue.Update(res);
727             }
728         } else {
729             kValue.Update(val);
730         }
731         EcmaRuntimeCallInfo *info =
732             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
733         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
734         info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
735         callResult = JSFunction::Call(info);
736         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
737         if (callResult.ToBoolean()) {
738             return callResult;
739         }
740         k--;
741         if (base::ArrayHelper::GetArrayLength(thread, thisObjVal) - 1 < k) {
742             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
743             return callResult;
744         }
745         if (!thisObjVal->IsStableJSArray(thread)) {
746             return callResult;
747         }
748     }
749     return callResult;
750 }
751 
HandleEveryOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)752 JSTaggedValue JSStableArray::HandleEveryOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
753                                                  JSHandle<JSTaggedValue> callbackFnHandle,
754                                                  JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
755 {
756     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
757     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
758     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
759     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
760     const int32_t argsLength = 3; // 3: ?kValue, k, O?
761     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(true);
762     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
763     while (k < len) {
764         // Elements of thisObjHandle may change.
765         kValue.Update(ElementAccessor::Get(thisObjHandle, k));
766         if (!kValue.GetTaggedValue().IsHole()) {
767             EcmaRuntimeCallInfo *info =
768                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
769             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
770             info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
771             callResult = JSFunction::Call(info);
772             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
773             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
774                 len = ElementAccessor::GetElementsLength(thisObjHandle);
775             }
776         } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
777             JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
778             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
779             EcmaRuntimeCallInfo *info =
780                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
781             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
782             info->SetCallArg(kValue1.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
783             callResult = JSFunction::Call(info);
784             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
785         }
786         if (!callResult.ToBoolean()) {
787             return base::BuiltinsBase::GetTaggedBoolean(false);
788         }
789         k++;
790         if (!thisObjVal->IsStableJSArray(thread)) {
791             return base::BuiltinsBase::GetTaggedBoolean(true);
792         }
793     }
794     return base::BuiltinsBase::GetTaggedBoolean(true);
795 }
796 
HandleSomeOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)797 JSTaggedValue JSStableArray::HandleSomeOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
798                                                 JSHandle<JSTaggedValue> callbackFnHandle,
799                                                 JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
800 {
801     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
802     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
803     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
804     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
805     const int32_t argsLength = 3; // 3: ?kValue, k, O?
806     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
807     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
808     while (k < len) {
809         // Elements of thisObjHandle may change.
810         kValue.Update(ElementAccessor::Get(thisObjHandle, k));
811         if (!kValue.GetTaggedValue().IsHole()) {
812             EcmaRuntimeCallInfo *info =
813                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
814             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
815             info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
816             callResult = JSFunction::Call(info);
817             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
818         } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
819             JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
820             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
821             EcmaRuntimeCallInfo *info =
822                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
823             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
824             info->SetCallArg(kValue1.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
825             callResult = JSFunction::Call(info);
826             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
827         }
828         if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
829             len = ElementAccessor::GetElementsLength(thisObjHandle);
830         }
831         if (callResult.ToBoolean()) {
832             return base::BuiltinsBase::GetTaggedBoolean(true);
833         }
834         k++;
835         if (!thisObjVal->IsStableJSArray(thread)) {
836             return base::BuiltinsBase::GetTaggedBoolean(false);
837         }
838     }
839     return base::BuiltinsBase::GetTaggedBoolean(false);
840 }
841 
HandleforEachOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t len,uint32_t & k)842 JSTaggedValue JSStableArray::HandleforEachOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
843                                                    JSHandle<JSTaggedValue> callbackFnHandle,
844                                                    JSHandle<JSTaggedValue> thisArgHandle, uint32_t len, uint32_t &k)
845 {
846     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
847     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
848     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
849     const int32_t argsLength = 3; // 3: ?kValue, k, O?
850     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
851     if (ElementAccessor::GetElementsLength(thisObjHandle) <= k) {
852         return base::BuiltinsBase::GetTaggedBoolean(false);
853     }
854     while (k < len) {
855         // Elements of thisObjHandle may change.
856         kValue.Update(ElementAccessor::Get(thisObjHandle, k));
857         if (!kValue.GetTaggedValue().IsHole()) {
858             key.Update(JSTaggedValue(k));
859             EcmaRuntimeCallInfo *info =
860                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
861             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
862             info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
863             JSTaggedValue funcResult = JSFunction::Call(info);
864             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
865             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
866                 len = ElementAccessor::GetElementsLength(thisObjHandle);
867             }
868         } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
869             key.Update(JSTaggedValue(k));
870             JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
871             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
872             EcmaRuntimeCallInfo *info =
873                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
874             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
875             info->SetCallArg(kValue1.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
876             JSTaggedValue funcResult = JSFunction::Call(info);
877             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
878         }
879         k++;
880         if (!thisObjVal->IsStableJSArray(thread)) {
881             break;
882         }
883     }
884     return base::BuiltinsBase::GetTaggedBoolean(true);
885 }
886 
887 template <class Predicate>
FindRawData(IndexOfContext & ctx,Predicate && predicate)888 JSTaggedValue JSStableArray::FindRawData(IndexOfContext &ctx, Predicate &&predicate)
889 {
890     DISALLOW_GARBAGE_COLLECTION;
891     JSTaggedType *data = nullptr;
892     JSTaggedValue elementsValue = JSHandle<JSObject>::Cast(ctx.receiver)->GetElements();
893     ElementsKind kind = ElementsKind::GENERIC;
894     if (elementsValue.IsMutantTaggedArray()) {
895         JSHandle<MutantTaggedArray> elements(ctx.thread, elementsValue);
896         data = elements->GetData();
897         kind = JSHandle<JSObject>::Cast(ctx.receiver)->GetClass()->GetElementsKind();
898     } else {
899         JSHandle<TaggedArray> elements(ctx.thread, elementsValue);
900         // Note: GC is guaranteed not to happen since no new object is created during the searching process.
901         data = elements->GetData();
902         // Note: for stable arrays, elements->GetLength() returns the CAPACITY, instead of actual length!
903     }
904     JSTaggedType *first = data + ctx.fromIndex;
905     JSTaggedType *last = data + ctx.length;
906 
907     JSMutableHandle<JSTaggedValue> indexHandle(ctx.thread, JSTaggedValue::Undefined());
908     for (JSTaggedType *cur = first; cur < last; ++cur) {
909         JSTaggedValue convertedCur = JSTaggedValue(*cur);
910         if (elementsValue.IsMutantTaggedArray()) {
911             convertedCur = ElementAccessor::GetTaggedValueWithElementsKind(*cur, kind);
912         }
913         if (LIKELY(convertedCur.GetRawData() != JSTaggedValue::VALUE_HOLE)) {
914             if (UNLIKELY(std::invoke(predicate, convertedCur.GetRawData()))) {
915                 return base::BuiltinsBase::GetTaggedInt64(cur - data);
916             }
917             continue;
918         }
919         // Fallback slow path
920         indexHandle.Update(base::BuiltinsBase::GetTaggedInt64(cur - data));
921         bool found = base::ArrayHelper::ElementIsStrictEqualTo(
922             ctx.thread, ctx.receiver, indexHandle, ctx.searchElement);
923         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(ctx.thread);
924         if (found) {
925             return indexHandle.GetTaggedValue();
926         }
927     }
928     return JSTaggedValue(-1); // Not found
929 }
930 
931 template <class Predicate>
FindLastRawData(IndexOfContext & ctx,Predicate && predicate)932 JSTaggedValue JSStableArray::FindLastRawData(IndexOfContext &ctx, Predicate &&predicate)
933 {
934     DISALLOW_GARBAGE_COLLECTION;
935     JSTaggedType *data = nullptr;
936     JSTaggedValue elementsValue = JSHandle<JSObject>::Cast(ctx.receiver)->GetElements();
937     ElementsKind kind = ElementsKind::GENERIC;
938     if (elementsValue.IsMutantTaggedArray()) {
939         JSHandle<MutantTaggedArray> elements(ctx.thread, JSHandle<JSObject>::Cast(ctx.receiver)->GetElements());
940         data = elements->GetData();
941         kind = JSHandle<JSObject>::Cast(ctx.receiver)->GetClass()->GetElementsKind();
942     } else {
943         JSHandle<TaggedArray> elements(ctx.thread, JSHandle<JSObject>::Cast(ctx.receiver)->GetElements());
944         // Note: GC is guaranteed not to happen since no new object is created during the searching process.
945         data = elements->GetData();
946     }
947     JSTaggedType *beforeFirst = data - 1;
948     JSTaggedType *beforeLast = data + ctx.fromIndex;
949 
950     JSMutableHandle<JSTaggedValue> indexHandle(ctx.thread, JSTaggedValue::Undefined());
951     for (JSTaggedType *cur = beforeLast; cur > beforeFirst; --cur) {
952         JSTaggedValue convertedCur = JSTaggedValue(*cur);
953         if (elementsValue.IsMutantTaggedArray()) {
954             convertedCur = ElementAccessor::GetTaggedValueWithElementsKind(*cur, kind);
955         }
956         if (LIKELY(convertedCur.GetRawData() != JSTaggedValue::VALUE_HOLE)) {
957             if (UNLIKELY(std::invoke(predicate, convertedCur.GetRawData()))) {
958                 return base::BuiltinsBase::GetTaggedInt64(cur - data);
959             }
960             continue;
961         }
962         // Fallback slow path
963         indexHandle.Update(base::BuiltinsBase::GetTaggedInt64(cur - data));
964         bool found = base::ArrayHelper::ElementIsStrictEqualTo(
965             ctx.thread, ctx.receiver, indexHandle, ctx.searchElement);
966         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(ctx.thread);
967         if (found) {
968             return indexHandle.GetTaggedValue();
969         }
970     }
971     return JSTaggedValue(-1); // Not found
972 }
973 
974 template <class Predicate>
FindRawDataDispatch(IndexOfType type,IndexOfContext & ctx,Predicate && predicate)975 JSTaggedValue JSStableArray::FindRawDataDispatch(IndexOfType type, IndexOfContext &ctx, Predicate &&predicate)
976 {
977     switch (type) {
978         case IndexOfType::IndexOf:
979             return FindRawData(ctx, std::forward<Predicate>(predicate));
980         case IndexOfType::LastIndexOf:
981             return FindLastRawData(ctx, std::forward<Predicate>(predicate));
982         default:
983             UNREACHABLE();
984     }
985 }
986 
987 // Zeros need special judge
IndexOfZero(IndexOfType type,IndexOfContext & ctx)988 JSTaggedValue JSStableArray::IndexOfZero(IndexOfType type, IndexOfContext &ctx)
989 {
990     return FindRawDataDispatch(type, ctx, [](JSTaggedType cur) {
991         return JSTaggedValue(cur).IsExactlyZero();
992     });
993 }
994 
IndexOfInt32(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)995 JSTaggedValue JSStableArray::IndexOfInt32(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
996 {
997     ASSERT(searchElement.IsInt());
998     int32_t untagged = searchElement.GetInt();
999     if (untagged == 0) {
1000         return IndexOfZero(type, ctx);
1001     }
1002     JSTaggedType targetInt32 = searchElement.GetRawData();
1003     JSTaggedType targetDouble = JSTaggedValue(static_cast<double>(untagged)).GetRawData();
1004     return FindRawDataDispatch(type, ctx, [targetInt32, targetDouble](JSTaggedType cur) {
1005         return cur == targetInt32 || cur == targetDouble;
1006     });
1007 }
1008 
IndexOfDouble(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1009 JSTaggedValue JSStableArray::IndexOfDouble(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1010 {
1011     ASSERT(searchElement.IsDouble());
1012     double untagged = searchElement.GetDouble();
1013     if (std::isnan(untagged)) {
1014         return JSTaggedValue(-1);
1015     }
1016     if (untagged == 0.0) {
1017         return IndexOfZero(type, ctx);
1018     }
1019     JSTaggedType targetDouble = searchElement.GetRawData();
1020     if (searchElement.WithinInt32()) {
1021         JSTaggedType targetInt32 = JSTaggedValue(static_cast<int32_t>(untagged)).GetRawData();
1022         return FindRawDataDispatch(type, ctx, [targetDouble, targetInt32](JSTaggedType cur) {
1023             return cur == targetDouble || cur == targetInt32;
1024         });
1025     } else {
1026         return FindRawDataDispatch(type, ctx, [targetDouble](JSTaggedType cur) {
1027             return cur == targetDouble;
1028         });
1029     }
1030 }
1031 
IndexOfObjectAddress(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1032 JSTaggedValue JSStableArray::IndexOfObjectAddress(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1033 {
1034     ASSERT(searchElement.IsObject());
1035     JSTaggedType targetAddress = searchElement.GetRawData();
1036     return FindRawDataDispatch(type, ctx, [targetAddress](JSTaggedType cur) {
1037         return cur == targetAddress;
1038     });
1039 }
1040 
IndexOfString(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1041 JSTaggedValue JSStableArray::IndexOfString(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1042 {
1043     ASSERT(searchElement.IsString());
1044     JSTaggedType targetAddress = searchElement.GetRawData();
1045     return FindRawDataDispatch(type, ctx, [searchElement, targetAddress](JSTaggedType cur) {
1046         if (targetAddress == cur) {
1047             return true;
1048         }
1049         JSTaggedValue curValue(cur);
1050         if (!curValue.IsString()) {
1051             return false;
1052         }
1053         return JSTaggedValue::StringCompare(
1054             EcmaString::Cast(curValue.GetTaggedObject()),
1055             EcmaString::Cast(searchElement.GetTaggedObject()));
1056     });
1057 }
1058 
IndexOfBigInt(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1059 JSTaggedValue JSStableArray::IndexOfBigInt(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1060 {
1061     ASSERT(searchElement.IsBigInt());
1062     JSTaggedType targetAddress = searchElement.GetRawData();
1063     return FindRawDataDispatch(type, ctx, [searchElement, targetAddress](JSTaggedType cur) {
1064         if (cur == targetAddress) {
1065             return true;
1066         }
1067         JSTaggedValue curValue(cur);
1068         if (!curValue.IsBigInt()) {
1069             return false;
1070         }
1071         return BigInt::Equal(curValue, searchElement);
1072     });
1073 }
1074 
IndexOfDispatch(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1075 JSTaggedValue JSStableArray::IndexOfDispatch(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1076 {
1077     if (searchElement.IsInt()) {
1078         return IndexOfInt32(type, ctx, searchElement);
1079     } else if (searchElement.IsDouble()) {
1080         return IndexOfDouble(type, ctx, searchElement);
1081     } else if (searchElement.IsString()) {
1082         return IndexOfString(type, ctx, searchElement);
1083     } else if (searchElement.IsBigInt()) {
1084         return IndexOfBigInt(type, ctx, searchElement);
1085     } else {
1086         return IndexOfObjectAddress(type, ctx, searchElement);
1087     }
1088 }
1089 
IndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)1090 JSTaggedValue JSStableArray::IndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
1091                                      JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
1092 {
1093     IndexOfContext ctx;
1094     ctx.thread = thread;
1095     ctx.receiver = receiver;
1096     ctx.searchElement = searchElement;
1097     ctx.fromIndex = from;
1098     ctx.length = len;
1099     return IndexOfDispatch(IndexOfType::IndexOf, ctx, searchElement.GetTaggedValue());
1100 }
1101 
LastIndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)1102 JSTaggedValue JSStableArray::LastIndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
1103                                          JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
1104 {
1105     IndexOfContext ctx;
1106     ctx.thread = thread;
1107     ctx.receiver = receiver;
1108     ctx.searchElement = searchElement;
1109     ctx.fromIndex = from;
1110     ctx.length = len;
1111     return IndexOfDispatch(IndexOfType::LastIndexOf, ctx, searchElement.GetTaggedValue());
1112 }
1113 
Filter(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t & toIndex)1114 JSTaggedValue JSStableArray::Filter(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
1115                                     EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t &toIndex)
1116 {
1117     JSThread *thread = argv->GetThread();
1118     JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
1119     JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
1120     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1121     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1122     JSMutableHandle<JSTaggedValue> toIndexHandle(thread, JSTaggedValue::Undefined());
1123     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1124     const int32_t argsLength = 3; // 3: ?kValue, k, O?
1125     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1126     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1127     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
1128     while (k < len) {
1129         // Elements of thisObjHandle may change.
1130         JSTaggedValue value = ElementAccessor::Get(thisObjHandle, k);
1131         if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1132             value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1133             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1134         }
1135         kValue.Update(value);
1136         if (!kValue.GetTaggedValue().IsHole()) {
1137             key.Update(JSTaggedValue(k));
1138             EcmaRuntimeCallInfo *info =
1139                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1140             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1141             info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1142             JSTaggedValue callResult = JSFunction::Call(info);
1143             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1144             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
1145                 len = ElementAccessor::GetElementsLength(thisObjHandle);
1146             }
1147             bool boolResult = callResult.ToBoolean();
1148             if (boolResult) {
1149                 toIndexHandle.Update(JSTaggedValue(toIndex));
1150                 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toIndexHandle, kValue);
1151                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1152                 toIndex++;
1153             }
1154         }
1155         k++;
1156         if (!thisObjVal->IsStableJSArray(thread)) {
1157             break;
1158         }
1159     }
1160     return base::BuiltinsBase::GetTaggedDouble(true);
1161 }
1162 
Map(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t len)1163 JSTaggedValue JSStableArray::Map(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
1164                                  EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t len)
1165 {
1166     JSThread *thread = argv->GetThread();
1167     JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
1168     JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
1169     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1170     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1171     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1172     JSMutableHandle<JSTaggedValue> mapResultHandle(thread, JSTaggedValue::Undefined());
1173     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
1174     const int32_t argsLength = 3; // 3: ?kValue, k, O?
1175     while (k < len) {
1176         // Elements of thisObjHandle may change.
1177         JSTaggedValue value = ElementAccessor::Get(thisObjHandle, k);
1178         if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1179             value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1180             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1181         }
1182         kValue.Update(value);
1183         if (!kValue.GetTaggedValue().IsHole()) {
1184             key.Update(JSTaggedValue(k));
1185             EcmaRuntimeCallInfo *info =
1186                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1187             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1188             info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1189             JSTaggedValue mapResult = JSFunction::Call(info);
1190             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1191             mapResultHandle.Update(mapResult);
1192             JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, mapResultHandle);
1193             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1194             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
1195                 len = ElementAccessor::GetElementsLength(thisObjHandle);
1196             }
1197         }
1198         k++;
1199         if (!thisObjVal->IsStableJSArray(thread)) {
1200             break;
1201         }
1202     }
1203     return base::BuiltinsBase::GetTaggedDouble(true);
1204 }
1205 
Reverse(JSThread * thread,JSHandle<JSObject> thisObjHandle,int64_t & lower,uint32_t len)1206 JSTaggedValue JSStableArray::Reverse(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1207                                      int64_t &lower, uint32_t len)
1208 {
1209     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1210     if (thisObjHandle->IsJSArray()) {
1211         JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObjHandle));
1212     }
1213     ElementsKind kind = thisObjHandle->GetClass()->GetElementsKind();
1214     JSHandle<TaggedArray> elements(thread, thisObjHandle->GetElements());
1215     bool enableElementsKind = thread->GetEcmaVM()->IsEnableElementsKind();
1216     if (enableElementsKind) {
1217         if (kind == ElementsKind::INT || kind == ElementsKind::HOLE_INT) {
1218             return FastReverse(thread, elements, lower, len, ElementsKind::INT);
1219         } else if (kind == ElementsKind::NUMBER || kind == ElementsKind::HOLE_NUMBER) {
1220             return FastReverse(thread, elements, lower, len, ElementsKind::NUMBER);
1221         }
1222     }
1223     return FastReverse(thread, elements, lower, len, ElementsKind::TAGGED);
1224 }
1225 
FastReverse(JSThread * thread,JSHandle<TaggedArray> elements,int64_t & lower,uint32_t len,ElementsKind kind)1226 JSTaggedValue JSStableArray::FastReverse(JSThread *thread, JSHandle<TaggedArray> elements,
1227                                          int64_t &lower, uint32_t len, ElementsKind kind)
1228 {
1229     JSMutableHandle<JSTaggedValue> lowerValueHandle(thread, JSTaggedValue::Undefined());
1230     JSMutableHandle<JSTaggedValue> upperValueHandle(thread, JSTaggedValue::Undefined());
1231     int64_t middle = std::floor(len / 2);
1232     while (lower != middle) {
1233         if (elements->GetLength() != len) {
1234             break;
1235         }
1236         int64_t upper = static_cast<int64_t>(len) - lower - 1;
1237         lowerValueHandle.Update(ElementAccessor::FastGet(elements, lower, kind));
1238         upperValueHandle.Update(ElementAccessor::FastGet(elements, upper, kind));
1239         ElementAccessor::FastSet(thread, elements, lower, upperValueHandle, kind);
1240         ElementAccessor::FastSet(thread, elements, upper, lowerValueHandle, kind);
1241         lower++;
1242     }
1243     return base::BuiltinsBase::GetTaggedDouble(true);
1244 }
1245 
Concat(JSThread * thread,JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & n)1246 JSTaggedValue JSStableArray::Concat(JSThread *thread, JSHandle<JSObject> newArrayHandle,
1247                                     JSHandle<JSObject> thisObjHandle, int64_t &k, int64_t &n)
1248 {
1249     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1250     int64_t thisLen = base::ArrayHelper::GetArrayLength(thread, thisObjVal);
1251     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1252     JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
1253     while (k < thisLen) {
1254         if (ElementAccessor::GetElementsLength(thisObjHandle) != thisLen) {
1255             break;
1256         }
1257         toKey.Update(JSTaggedValue(n));
1258         JSTaggedValue kValue = ElementAccessor::Get(thisObjHandle, k);
1259         if (!kValue.IsHole()) {
1260             JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, JSHandle<JSTaggedValue>(thread, kValue));
1261             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1262         }
1263         n++;
1264         k++;
1265     }
1266     return base::BuiltinsBase::GetTaggedDouble(true);
1267 }
1268 
1269 template JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray<TypedArrayKind::SHARED>(
1270     JSThread *thread, JSHandle<JSTypedArray> &targetArray, DataViewType targetType,
1271     uint64_t targetOffset, uint32_t srcLength, JSHandle<JSObject> &obj);
1272 template JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray<TypedArrayKind::NON_SHARED>(
1273     JSThread *thread, JSHandle<JSTypedArray> &targetArray, DataViewType targetType,
1274     uint64_t targetOffset, uint32_t srcLength, JSHandle<JSObject> &obj);
1275 
1276 template<TypedArrayKind typedArrayKind>
FastCopyFromArrayToTypedArray(JSThread * thread,JSHandle<JSTypedArray> & targetArray,DataViewType targetType,uint64_t targetOffset,uint32_t srcLength,JSHandle<JSObject> & obj)1277 JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray(JSThread *thread, JSHandle<JSTypedArray> &targetArray,
1278                                                            DataViewType targetType, uint64_t targetOffset,
1279                                                            uint32_t srcLength, JSHandle<JSObject> &obj)
1280 {
1281     JSHandle<JSTaggedValue> targetBuffer(thread, targetArray->GetViewedArrayBufferOrByteArray());
1282     // If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
1283     if (BuiltinsArrayBufferType<typedArrayKind>::Type::IsDetachedBuffer(targetBuffer.GetTaggedValue())) {
1284         THROW_TYPE_ERROR_AND_RETURN(thread, "The targetBuffer of This value is detached buffer.",
1285                                     JSTaggedValue::Exception());
1286     }
1287     uint32_t targetLength = targetArray->GetArrayLength();
1288     uint32_t targetByteOffset = targetArray->GetByteOffset();
1289     uint32_t targetElementSize = TypedArrayHelper::GetSizeFromType(targetType);
1290     if (srcLength + targetOffset > targetLength) {
1291         THROW_RANGE_ERROR_AND_RETURN(thread, "The sum of length and targetOffset is greater than targetLength.",
1292                                      JSTaggedValue::Exception());
1293     }
1294     uint32_t targetByteIndex = static_cast<uint32_t>(targetOffset * targetElementSize + targetByteOffset);
1295     ContentType contentType = targetArray->GetContentType();
1296     uint32_t elemLen = ElementAccessor::GetElementsLength(obj);
1297     if (contentType == ContentType::BigInt) {
1298         JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Hole());
1299         JSMutableHandle<JSTaggedValue> elem(thread, JSTaggedValue::Hole());
1300         for (uint32_t i = 0; i < srcLength; i++) {
1301             if (i < elemLen) {
1302                 elem.Update(ElementAccessor::Get(obj, i));
1303             } else {
1304                 elem.Update(JSTaggedValue::Hole());
1305             }
1306             kValue.Update(JSTaggedValue::ToBigInt(thread, elem));
1307             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1308             BuiltinsArrayBufferType<typedArrayKind>::Type::SetValueInBuffer(
1309                 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, kValue, true);
1310             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1311             targetByteIndex += targetElementSize;
1312         }
1313     } else {
1314         double val = 0.0;
1315         uint32_t copyLen = srcLength > elemLen ? elemLen : srcLength;
1316         for (uint32_t i = 0; i < copyLen; i++) {
1317             JSTaggedValue taggedVal = ElementAccessor::Get(obj, i);
1318             if (!taggedVal.IsNumber()) {
1319                 JSTaggedNumber taggedNumber = JSTaggedValue::ToNumber(thread, taggedVal);
1320                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1321                 val = taggedNumber.GetNumber();
1322             } else {
1323                 val = taggedVal.GetNumber();
1324             }
1325             BuiltinsArrayBufferType<typedArrayKind>::Type::FastSetValueInBuffer(
1326                 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, val, true);
1327             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1328             targetByteIndex += targetElementSize;
1329         }
1330 
1331         for (uint32_t i = copyLen; i < srcLength; i++) {
1332             val = JSTaggedNumber(base::NAN_VALUE).GetNumber();
1333             BuiltinsArrayBufferType<typedArrayKind>::Type::FastSetValueInBuffer(
1334                 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, val, true);
1335             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1336             targetByteIndex += targetElementSize;
1337         }
1338     }
1339     return JSTaggedValue::Undefined();
1340 }
1341 
At(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)1342 JSTaggedValue JSStableArray::At(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
1343 {
1344     JSThread *thread = argv->GetThread();
1345     uint32_t thisLen = receiver->GetArrayLength();
1346     if (thisLen == 0) {
1347         return JSTaggedValue::Undefined();
1348     }
1349     JSTaggedNumber index = JSTaggedValue::ToInteger(thread, base::BuiltinsBase::GetCallArg(argv, 0));
1350     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1351     int64_t relativeIndex = index.GetNumber();
1352     int64_t k = 0;
1353     if (relativeIndex >= 0) {
1354         k = relativeIndex;
1355     } else {
1356         k = static_cast<int64_t>(thisLen) + relativeIndex;
1357     }
1358     if (k < 0 || k >= thisLen) {
1359         return JSTaggedValue::Undefined();
1360     }
1361 
1362     auto result = JSTaggedValue::Hole();
1363     result = ElementAccessor::Get(JSHandle<JSObject>::Cast(receiver), k);
1364     return result.IsHole() ? JSTaggedValue::Undefined() : result;
1365 }
1366 
At(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)1367 JSTaggedValue JSStableArray::At(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
1368 {
1369     JSThread *thread = argv->GetThread();
1370     uint32_t thisLen = receiver->GetArrayLength();
1371     if (thisLen == 0) {
1372         return JSTaggedValue::Undefined();
1373     }
1374     JSTaggedNumber index = JSTaggedValue::ToInteger(thread, base::BuiltinsBase::GetCallArg(argv, 0));
1375     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1376     int64_t relativeIndex = index.GetNumber();
1377     int64_t k = 0;
1378     if (relativeIndex >= 0) {
1379         k = relativeIndex;
1380     } else {
1381         k = static_cast<int64_t>(thisLen) + relativeIndex;
1382     }
1383     if (k < 0 || k >= thisLen) {
1384         return JSTaggedValue::Undefined();
1385     }
1386 
1387     auto result = JSTaggedValue::Hole();
1388     result = ElementAccessor::Get(JSHandle<JSObject>::Cast(receiver), k);
1389     return result.IsHole() ? JSTaggedValue::Undefined() : result;
1390 }
1391 
With(JSThread * thread,JSHandle<JSArray> receiver,int64_t insertCount,int64_t index,JSHandle<JSTaggedValue> value)1392 JSTaggedValue JSStableArray::With(JSThread *thread, JSHandle<JSArray> receiver,
1393                                   int64_t insertCount, int64_t index, JSHandle<JSTaggedValue> value)
1394 {
1395     JSHandle<JSObject> thisObjHandle(receiver);
1396     JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1397     JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1398     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1399     JSHandle<JSObject> newArrayHandle(newArray);
1400 
1401     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1402     TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
1403 
1404     if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
1405         destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1406     }
1407     ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1408     bool needTransition = true;
1409     for (uint32_t idx = 0; idx < insertCount; idx++) {
1410         if (idx == index) {
1411             ElementAccessor::Set(thread, newArrayHandle, idx, value, needTransition);
1412         } else {
1413             JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thisObjHandle, idx));
1414             if (kValue->IsHole()) {
1415                 ElementAccessor::Set(thread, newArrayHandle, idx, undefinedHandle, needTransition);
1416             } else {
1417                 ElementAccessor::Set(thread, newArrayHandle, idx, kValue, needTransition);
1418             }
1419         }
1420     }
1421     JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1422     return newArrayHandle.GetTaggedValue();
1423 }
1424 
ToSpliced(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv,int64_t argc,int64_t actualStart,int64_t actualSkipCount,int64_t insertCount)1425 JSTaggedValue JSStableArray::ToSpliced(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv,
1426                                        int64_t argc, int64_t actualStart, int64_t actualSkipCount, int64_t insertCount)
1427 {
1428     JSThread *thread = argv->GetThread();
1429 
1430     JSHandle<JSObject> thisObjHandle(receiver);
1431     JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1432     JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1433     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1434     JSHandle<JSObject> newArrayHandle(newArray);
1435 
1436     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1437     TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
1438 
1439     if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
1440         destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1441     }
1442     ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1443     int64_t i = 0;
1444     int64_t r = actualStart + actualSkipCount;
1445     bool needTransition = true;
1446     for (int64_t idx = 0; idx < actualStart; idx++, i++) {
1447         JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thisObjHandle, idx));
1448         if (kValue->IsHole()) {
1449             ElementAccessor::Set(thread, newArrayHandle, i, undefinedHandle, needTransition);
1450         } else {
1451             ElementAccessor::Set(thread, newArrayHandle, i, kValue, needTransition);
1452         }
1453     }
1454 
1455     for (uint32_t pos = 2; pos < argc; ++pos) { // 2:2 means there two arguments before the insert items.
1456         auto element = base::BuiltinsBase::GetCallArg(argv, pos);
1457         ElementAccessor::Set(thread, newArrayHandle, i, element, needTransition);
1458         ++i;
1459     }
1460 
1461     while (i < insertCount) {
1462         JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thisObjHandle, r));
1463         if (kValue->IsHole()) {
1464             ElementAccessor::Set(thread, newArrayHandle, i, undefinedHandle, needTransition);
1465         } else {
1466             ElementAccessor::Set(thread, newArrayHandle, i, kValue, needTransition);
1467         }
1468         ++i;
1469         ++r;
1470     }
1471 
1472     JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1473 
1474     return newArrayHandle.GetTaggedValue();
1475 }
1476 
ToReversed(JSThread * thread,JSHandle<JSArray> receiver,int64_t insertCount)1477 JSTaggedValue JSStableArray::ToReversed(JSThread *thread, JSHandle<JSArray> receiver,
1478                                         int64_t insertCount)
1479 {
1480     JSHandle<JSObject> thisObjHandle(receiver);
1481     JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1482     JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1483     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1484     JSHandle<JSObject> newArrayHandle(newArray);
1485 
1486     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1487     TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
1488 
1489     if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
1490         destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1491     }
1492     ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1493     bool needTransition = true;
1494     for (uint32_t idx = 0; idx < insertCount; idx++) {
1495         JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thisObjHandle, idx));
1496         if (kValue->IsHole()) {
1497             ElementAccessor::Set(thread, newArrayHandle, insertCount - idx - 1,
1498                                  undefinedHandle, needTransition);
1499         } else {
1500             ElementAccessor::Set(thread, newArrayHandle, insertCount - idx - 1, kValue, needTransition);
1501         }
1502     }
1503     JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1504 
1505     return newArrayHandle.GetTaggedValue();
1506 }
1507 
Reduce(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSMutableHandle<JSTaggedValue> accumulator,int64_t & k,int64_t & len)1508 JSTaggedValue JSStableArray::Reduce(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1509                                     JSHandle<JSTaggedValue> callbackFnHandle,
1510                                     JSMutableHandle<JSTaggedValue> accumulator, int64_t &k, int64_t &len)
1511 {
1512     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1513     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1514     JSTaggedValue callResult = JSTaggedValue::Undefined();
1515     while (k < len) {
1516         JSTaggedValue kValue(ElementAccessor::Get(thisObjHandle, k));
1517         if (!kValue.IsHole()) {
1518             JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1519             const uint32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
1520             EcmaRuntimeCallInfo *info =
1521                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, undefined, undefined, argsLength);
1522             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1523             info->SetCallArg(accumulator.GetTaggedValue(), kValue, JSTaggedValue(k),
1524                              thisObjVal.GetTaggedValue());
1525             callResult = JSFunction::Call(info);
1526             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1527             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
1528                 len = ElementAccessor::GetElementsLength(thisObjHandle);
1529             }
1530             accumulator.Update(callResult);
1531         }
1532         k++;
1533         if (!thisObjVal->IsStableJSArray(thread)) {
1534             break;
1535         }
1536     }
1537     return base::BuiltinsBase::GetTaggedDouble(true);
1538 }
1539 
Slice(JSThread * thread,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & count)1540 JSTaggedValue JSStableArray::Slice(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1541                                    int64_t &k, int64_t &count)
1542 {
1543     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1544     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1545     int64_t len = static_cast<int64_t>(ElementAccessor::GetElementsLength(thisObjHandle));
1546     int64_t oldLen;
1547     if (len > k + count) {
1548         oldLen = count;
1549     } else {
1550         oldLen = len - k;
1551     }
1552     JSHandle<JSObject> arrayObj = factory->NewAndCopyJSArrayObject(thisObjHandle, count, oldLen, k);
1553     for (int i = 0; i < count; i++) {
1554         JSMutableHandle<JSTaggedValue> value(thread, ElementAccessor::Get(arrayObj, i));
1555         if (value->IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, i + k)) {
1556             value.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, i + k).GetTaggedValue());
1557             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1558             ElementAccessor::Set(thread, arrayObj, i, value, true);
1559         }
1560     }
1561     return arrayObj.GetTaggedValue();
1562 }
1563 
Sort(JSThread * thread,const JSHandle<JSObject> & thisObj,const JSHandle<JSTaggedValue> & callbackFnHandle)1564 JSTaggedValue JSStableArray::Sort(JSThread *thread, const JSHandle<JSObject> &thisObj,
1565                                   const JSHandle<JSTaggedValue> &callbackFnHandle)
1566 {
1567     JSArray::SortElementsByObject(thread, thisObj, callbackFnHandle);
1568     return thisObj.GetTaggedValue();
1569 }
1570 
Fill(JSThread * thread,const JSHandle<JSObject> & thisObj,const JSHandle<JSTaggedValue> & value,int64_t start,int64_t end,int64_t len)1571 JSTaggedValue JSStableArray::Fill(JSThread *thread, const JSHandle<JSObject> &thisObj,
1572                                   const JSHandle<JSTaggedValue> &value, int64_t start,
1573                                   int64_t end, int64_t len)
1574 {
1575     JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObj));
1576     uint32_t length = ElementAccessor::GetElementsLength(thisObj);
1577     ElementsKind oldKind = thisObj->GetClass()->GetElementsKind();
1578     if (JSHClass::TransitToElementsKind(thread, thisObj, value)) {
1579         ElementsKind newKind = thisObj->GetClass()->GetElementsKind();
1580         Elements::MigrateArrayWithKind(thread, thisObj, oldKind, newKind);
1581     }
1582     if (length >= end) {
1583         if (thisObj->GetElements().IsMutantTaggedArray()) {
1584             ElementsKind kind = thisObj->GetClass()->GetElementsKind();
1585             TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements());
1586             JSTaggedValue migratedValue = JSTaggedValue(ElementAccessor::ConvertTaggedValueWithElementsKind(
1587                 value.GetTaggedValue(), kind));
1588             for (int64_t idx = start; idx < end; idx++) {
1589                 elements->Set<false>(thread, idx, migratedValue);
1590             }
1591         } else {
1592             TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements());
1593             for (int64_t idx = start; idx < end; idx++) {
1594                 elements->Set(thread, idx, value);
1595             }
1596         }
1597         return thisObj.GetTaggedValue();
1598     } else {
1599         if (thisObj->GetElements().IsMutantTaggedArray()) {
1600             ElementsKind kind = thisObj->GetClass()->GetElementsKind();
1601             JSHandle<MutantTaggedArray> newElements = thread->GetEcmaVM()->GetFactory()->NewMutantTaggedArray(len);
1602             JSTaggedValue migratedValue = JSTaggedValue(ElementAccessor::ConvertTaggedValueWithElementsKind(
1603                 value.GetTaggedValue(), kind));
1604             for (int64_t idx = start; idx < end; idx++) {
1605                 newElements->Set<false>(thread, idx, migratedValue);
1606             }
1607             thisObj->SetElements(thread, newElements);
1608         } else {
1609             JSHandle<TaggedArray> newElements = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len);
1610             for (int64_t idx = start; idx < end; idx++) {
1611                 newElements->Set(thread, idx, value);
1612             }
1613             thisObj->SetElements(thread, newElements);
1614         }
1615         return thisObj.GetTaggedValue();
1616     }
1617 }
1618 
HandleFindLastOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,JSMutableHandle<JSTaggedValue> & kValue,int64_t & k)1619 JSTaggedValue JSStableArray::HandleFindLastOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1620                                                     JSHandle<JSTaggedValue> callbackFnHandle,
1621                                                     JSHandle<JSTaggedValue> thisArgHandle,
1622                                                     JSMutableHandle<JSTaggedValue> &kValue, int64_t &k)
1623 {
1624     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1625     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1626     const uint32_t argsLength = 3; // 3: «kValue, k, O»
1627     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
1628     while (k >= 0) {
1629         JSTaggedValue val = ElementAccessor::Get(thisObjHandle, k);
1630         if (!val.IsHole()) {
1631             kValue.Update(val);
1632         } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1633             auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1634             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1635             kValue.Update(res);
1636         } else {
1637             kValue.Update(JSTaggedValue::Undefined());
1638         }
1639         EcmaRuntimeCallInfo *info =
1640             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1641         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1642         info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
1643         callResult = JSFunction::Call(info);
1644         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1645         if (callResult.ToBoolean()) {
1646             return callResult;
1647         }
1648         k--;
1649         ASSERT(ElementAccessor::GetElementsLength(thisObjHandle) > 0);
1650         if (ElementAccessor::GetElementsLength(thisObjHandle) - 1 < k) {
1651             break;
1652         }
1653         if (!thisObjVal->IsStableJSArray(thread)) {
1654             break;
1655         }
1656     }
1657     return callResult;
1658 }
1659 
HandleReduceRightOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSMutableHandle<JSTaggedValue> & accumulator,JSHandle<JSTaggedValue> thisArgHandle,int64_t & k)1660 JSTaggedValue JSStableArray::HandleReduceRightOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1661                                                        JSHandle<JSTaggedValue> callbackFnHandle,
1662                                                        JSMutableHandle<JSTaggedValue> &accumulator,
1663                                                        JSHandle<JSTaggedValue> thisArgHandle, int64_t &k)
1664 {
1665     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1666     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Hole());
1667     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1668     JSTaggedValue callResult = JSTaggedValue::Undefined();
1669     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1670     const int32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
1671     int64_t len = static_cast<int64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1672     while (k >= 0) {
1673         key.Update(JSTaggedValue(k));
1674         kValue.Update(ElementAccessor::Get(thisObjHandle, k));
1675         if (!kValue.GetTaggedValue().IsHole()) {
1676             EcmaRuntimeCallInfo *info =
1677                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1678             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1679             info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(),
1680                 key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1681             callResult = JSFunction::Call(info);
1682             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1683             accumulator.Update(callResult);
1684         } else {
1685             bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, key);
1686             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1687             if (exists) {
1688                 auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, key).GetTaggedValue();
1689                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1690                 kValue.Update(res);
1691                 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle,
1692                     thisArgHandle, undefined, argsLength);
1693                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1694                 info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(),
1695                     key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1696                 callResult = JSFunction::Call(info);
1697                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1698                 accumulator.Update(callResult);
1699             }
1700         }
1701         k--;
1702         int64_t newLen = static_cast<int64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1703         if (!thisObjVal->IsStableJSArray(thread) || newLen != len) {
1704             return base::BuiltinsBase::GetTaggedBoolean(false);
1705         }
1706     }
1707     return base::BuiltinsBase::GetTaggedBoolean(true);
1708 }
1709 }  // namespace panda::ecmascript
1710