• 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     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
582     JSHandle<EcmaString> sepStringHandle = JSHandle<EcmaString>::Cast(globalConst->GetHandledCommaString());
583     auto context = thread->GetCurrentEcmaContext();
584     JSHandle<JSTaggedValue> receiverValue = JSHandle<JSTaggedValue>::Cast(receiver);
585     if (!sepHandle->IsUndefined()) {
586         if (sepHandle->IsString()) {
587             sepStringHandle = JSHandle<EcmaString>::Cast(sepHandle);
588         } else {
589             sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
590             RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
591         }
592         SetSepValue(sepStringHandle, sep, sepLength);
593     }
594     if (length == 0) {
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     uint32_t elementsLength = ElementAccessor::GetElementsLength(obj);
603     uint32_t len = elementsLength > length ? length : elementsLength;
604     if (elementsLength == 0 && length != 0) {
605         len = length;
606     }
607     if (len <= 1) {
608         // sep unused, set isOneByte to default(true)
609         isOneByte = true;
610     }
611     CVector<JSHandle<EcmaString>> vec;
612     vec.reserve(len);
613     for (uint32_t k = 0; k < len; k++) {
614         JSTaggedValue element = JSTaggedValue::Undefined();
615         if (k < elementsLength) {
616             element = ElementAccessor::Get(obj, k);
617         }
618         if (!element.IsUndefinedOrNull() && !element.IsHole()) {
619             if (!element.IsString()) {
620                 elementHandle.Update(element);
621                 JSHandle<EcmaString> strElement = JSTaggedValue::ToString(thread, elementHandle);
622                 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
623                 element = strElement.GetTaggedValue();
624             }
625             auto nextStr = EcmaString::Cast(element.GetTaggedObject());
626             vec.emplace_back(thread, nextStr);
627             isOneByte = EcmaStringAccessor(nextStr).IsUtf8() ? isOneByte : false;
628             allocateLength += EcmaStringAccessor(nextStr).GetLength();
629         } else {
630             vec.emplace_back(globalConst->GetHandledEmptyString());
631         }
632     }
633     if (len > 0) {
634         allocateLength += sepLength * (len - 1);
635     }
636     if (WorthUseTreeString(sep, allocateLength, len)) {
637         return JoinUseTreeString(thread, receiverValue, sepStringHandle, sep, vec);
638     }
639     auto newString = EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), allocateLength, isOneByte);
640     int current = 0;
641     DISALLOW_GARBAGE_COLLECTION;
642     for (uint32_t k = 0; k < len; k++) {
643         if (k > 0) {
644             if (sep >= 0) {
645                 EcmaStringAccessor(newString).Set(current, static_cast<uint16_t>(sep));
646             } else if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
647                 EcmaStringAccessor::ReadData(newString, *sepStringHandle, current,
648                     allocateLength - static_cast<uint32_t>(current), sepLength);
649             }
650             current += static_cast<int>(sepLength);
651         }
652         JSHandle<EcmaString> nextStr = vec[k];
653         int nextLength = static_cast<int>(EcmaStringAccessor(nextStr).GetLength());
654         EcmaStringAccessor::ReadData(newString, *nextStr, current,
655             allocateLength - static_cast<uint32_t>(current), nextLength);
656         current += nextLength;
657     }
658     ASSERT_PRINT(
659         isOneByte == EcmaStringAccessor::CanBeCompressed(newString), "isOneByte does not match the real value!");
660     context->JoinStackPopFastPath(receiverValue);
661     return JSTaggedValue(newString);
662 }
663 
HandleFindIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)664 JSTaggedValue JSStableArray::HandleFindIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
665                                                      JSHandle<JSTaggedValue> callbackFnHandle,
666                                                      JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
667 {
668     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
669     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
670     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
671     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
672     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
673     const int32_t argsLength = 3; // 3: ?kValue, k, O?
674     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
675     while (k < len) {
676         // Elements of thisObjHandle may change.
677         JSTaggedValue val = ElementAccessor::Get(thisObjHandle, k);
678         if (val.IsHole()) {
679             auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
680             if (res.IsHole()) {
681                 kValue.Update(JSTaggedValue::Undefined());
682             } else {
683                 kValue.Update(res);
684             }
685         } else {
686             kValue.Update(val);
687         }
688         EcmaRuntimeCallInfo *info =
689             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
690         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
691         info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
692         callResult = JSFunction::Call(info);
693         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
694         if (callResult.ToBoolean()) {
695             return callResult;
696         }
697         if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
698             len = ElementAccessor::GetElementsLength(thisObjHandle);
699         }
700         k++;
701         if (!thisObjVal->IsStableJSArray(thread)) {
702             return callResult;
703         }
704     }
705     return callResult;
706 }
707 
HandleFindLastIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,int64_t & k)708 JSTaggedValue JSStableArray::HandleFindLastIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
709                                                          JSHandle<JSTaggedValue> callbackFnHandle,
710                                                          JSHandle<JSTaggedValue> thisArgHandle, int64_t &k)
711 {
712     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
713     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
714     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
715     const int32_t argsLength = 3; // 3: ?kValue, k, O?
716     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
717     while (k >= 0) {
718         // Elements of thisObjHandle may change.
719         JSTaggedValue val = ElementAccessor::Get(thisObjHandle, k);
720         if (val.IsHole()) {
721             auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
722             if (res.IsHole()) {
723                 kValue.Update(JSTaggedValue::Undefined());
724             } else {
725                 kValue.Update(res);
726             }
727         } else {
728             kValue.Update(val);
729         }
730         EcmaRuntimeCallInfo *info =
731             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
732         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
733         info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
734         callResult = JSFunction::Call(info);
735         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
736         if (callResult.ToBoolean()) {
737             return callResult;
738         }
739         k--;
740         if (base::ArrayHelper::GetArrayLength(thread, thisObjVal) - 1 < k) {
741             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
742             return callResult;
743         }
744         if (!thisObjVal->IsStableJSArray(thread)) {
745             return callResult;
746         }
747     }
748     return callResult;
749 }
750 
HandleEveryOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)751 JSTaggedValue JSStableArray::HandleEveryOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
752                                                  JSHandle<JSTaggedValue> callbackFnHandle,
753                                                  JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
754 {
755     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
756     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
757     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
758     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
759     const int32_t argsLength = 3; // 3: ?kValue, k, O?
760     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(true);
761     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
762     while (k < len) {
763         // Elements of thisObjHandle may change.
764         kValue.Update(ElementAccessor::Get(thisObjHandle, k));
765         if (!kValue.GetTaggedValue().IsHole()) {
766             EcmaRuntimeCallInfo *info =
767                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
768             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
769             info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
770             callResult = JSFunction::Call(info);
771             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
772             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
773                 len = ElementAccessor::GetElementsLength(thisObjHandle);
774             }
775         } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
776             JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
777             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
778             EcmaRuntimeCallInfo *info =
779                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
780             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
781             info->SetCallArg(kValue1.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
782             callResult = JSFunction::Call(info);
783             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
784         }
785         if (!callResult.ToBoolean()) {
786             return base::BuiltinsBase::GetTaggedBoolean(false);
787         }
788         k++;
789         if (!thisObjVal->IsStableJSArray(thread)) {
790             return base::BuiltinsBase::GetTaggedBoolean(true);
791         }
792     }
793     return base::BuiltinsBase::GetTaggedBoolean(true);
794 }
795 
HandleSomeOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)796 JSTaggedValue JSStableArray::HandleSomeOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
797                                                 JSHandle<JSTaggedValue> callbackFnHandle,
798                                                 JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
799 {
800     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
801     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
802     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
803     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
804     const int32_t argsLength = 3; // 3: ?kValue, k, O?
805     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
806     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
807     while (k < len) {
808         // Elements of thisObjHandle may change.
809         kValue.Update(ElementAccessor::Get(thisObjHandle, k));
810         if (!kValue.GetTaggedValue().IsHole()) {
811             EcmaRuntimeCallInfo *info =
812                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
813             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
814             info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
815             callResult = JSFunction::Call(info);
816             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
817         } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
818             JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
819             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
820             EcmaRuntimeCallInfo *info =
821                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
822             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
823             info->SetCallArg(kValue1.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
824             callResult = JSFunction::Call(info);
825             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
826         }
827         if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
828             len = ElementAccessor::GetElementsLength(thisObjHandle);
829         }
830         if (callResult.ToBoolean()) {
831             return base::BuiltinsBase::GetTaggedBoolean(true);
832         }
833         k++;
834         if (!thisObjVal->IsStableJSArray(thread)) {
835             return base::BuiltinsBase::GetTaggedBoolean(false);
836         }
837     }
838     return base::BuiltinsBase::GetTaggedBoolean(false);
839 }
840 
HandleforEachOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t len,uint32_t & k)841 JSTaggedValue JSStableArray::HandleforEachOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
842                                                    JSHandle<JSTaggedValue> callbackFnHandle,
843                                                    JSHandle<JSTaggedValue> thisArgHandle, uint32_t len, uint32_t &k)
844 {
845     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
846     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
847     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
848     const int32_t argsLength = 3; // 3: ?kValue, k, O?
849     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
850     if (ElementAccessor::GetElementsLength(thisObjHandle) <= k) {
851         return base::BuiltinsBase::GetTaggedBoolean(false);
852     }
853     while (k < len) {
854         // Elements of thisObjHandle may change.
855         kValue.Update(ElementAccessor::Get(thisObjHandle, k));
856         if (!kValue.GetTaggedValue().IsHole()) {
857             key.Update(JSTaggedValue(k));
858             EcmaRuntimeCallInfo *info =
859                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
860             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
861             info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
862             JSTaggedValue funcResult = JSFunction::Call(info);
863             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
864             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
865                 len = ElementAccessor::GetElementsLength(thisObjHandle);
866             }
867         } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
868             key.Update(JSTaggedValue(k));
869             JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
870             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
871             EcmaRuntimeCallInfo *info =
872                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
873             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
874             info->SetCallArg(kValue1.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
875             JSTaggedValue funcResult = JSFunction::Call(info);
876             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
877         }
878         k++;
879         if (!thisObjVal->IsStableJSArray(thread)) {
880             break;
881         }
882     }
883     return base::BuiltinsBase::GetTaggedBoolean(true);
884 }
885 
886 template <class Predicate>
FindRawData(IndexOfContext & ctx,Predicate && predicate)887 JSTaggedValue JSStableArray::FindRawData(IndexOfContext &ctx, Predicate &&predicate)
888 {
889     DISALLOW_GARBAGE_COLLECTION;
890     JSTaggedType *data = nullptr;
891     JSTaggedValue elementsValue = JSHandle<JSObject>::Cast(ctx.receiver)->GetElements();
892     ElementsKind kind = ElementsKind::GENERIC;
893     if (elementsValue.IsMutantTaggedArray()) {
894         JSHandle<MutantTaggedArray> elements(ctx.thread, elementsValue);
895         data = elements->GetData();
896         kind = JSHandle<JSObject>::Cast(ctx.receiver)->GetClass()->GetElementsKind();
897     } else {
898         JSHandle<TaggedArray> elements(ctx.thread, elementsValue);
899         // Note: GC is guaranteed not to happen since no new object is created during the searching process.
900         data = elements->GetData();
901         // Note: for stable arrays, elements->GetLength() returns the CAPACITY, instead of actual length!
902     }
903     JSTaggedType *first = data + ctx.fromIndex;
904     JSTaggedType *last = data + ctx.length;
905 
906     JSMutableHandle<JSTaggedValue> indexHandle(ctx.thread, JSTaggedValue::Undefined());
907     for (JSTaggedType *cur = first; cur < last; ++cur) {
908         JSTaggedValue convertedCur = JSTaggedValue(*cur);
909         if (elementsValue.IsMutantTaggedArray()) {
910             convertedCur = ElementAccessor::GetTaggedValueWithElementsKind(*cur, kind);
911         }
912         if (LIKELY(convertedCur.GetRawData() != JSTaggedValue::VALUE_HOLE)) {
913             if (UNLIKELY(std::invoke(predicate, convertedCur.GetRawData()))) {
914                 return base::BuiltinsBase::GetTaggedInt64(cur - data);
915             }
916             continue;
917         }
918         // Fallback slow path
919         indexHandle.Update(base::BuiltinsBase::GetTaggedInt64(cur - data));
920         bool found = base::ArrayHelper::ElementIsStrictEqualTo(
921             ctx.thread, ctx.receiver, indexHandle, ctx.searchElement);
922         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(ctx.thread);
923         if (found) {
924             return indexHandle.GetTaggedValue();
925         }
926     }
927     return JSTaggedValue(-1); // Not found
928 }
929 
930 template <class Predicate>
FindLastRawData(IndexOfContext & ctx,Predicate && predicate)931 JSTaggedValue JSStableArray::FindLastRawData(IndexOfContext &ctx, Predicate &&predicate)
932 {
933     DISALLOW_GARBAGE_COLLECTION;
934     JSTaggedType *data = nullptr;
935     JSTaggedValue elementsValue = JSHandle<JSObject>::Cast(ctx.receiver)->GetElements();
936     ElementsKind kind = ElementsKind::GENERIC;
937     if (elementsValue.IsMutantTaggedArray()) {
938         JSHandle<MutantTaggedArray> elements(ctx.thread, JSHandle<JSObject>::Cast(ctx.receiver)->GetElements());
939         data = elements->GetData();
940         kind = JSHandle<JSObject>::Cast(ctx.receiver)->GetClass()->GetElementsKind();
941     } else {
942         JSHandle<TaggedArray> elements(ctx.thread, JSHandle<JSObject>::Cast(ctx.receiver)->GetElements());
943         // Note: GC is guaranteed not to happen since no new object is created during the searching process.
944         data = elements->GetData();
945     }
946     JSTaggedType *beforeFirst = data - 1;
947     JSTaggedType *beforeLast = data + ctx.fromIndex;
948 
949     JSMutableHandle<JSTaggedValue> indexHandle(ctx.thread, JSTaggedValue::Undefined());
950     for (JSTaggedType *cur = beforeLast; cur > beforeFirst; --cur) {
951         JSTaggedValue convertedCur = JSTaggedValue(*cur);
952         if (elementsValue.IsMutantTaggedArray()) {
953             convertedCur = ElementAccessor::GetTaggedValueWithElementsKind(*cur, kind);
954         }
955         if (LIKELY(convertedCur.GetRawData() != JSTaggedValue::VALUE_HOLE)) {
956             if (UNLIKELY(std::invoke(predicate, convertedCur.GetRawData()))) {
957                 return base::BuiltinsBase::GetTaggedInt64(cur - data);
958             }
959             continue;
960         }
961         // Fallback slow path
962         indexHandle.Update(base::BuiltinsBase::GetTaggedInt64(cur - data));
963         bool found = base::ArrayHelper::ElementIsStrictEqualTo(
964             ctx.thread, ctx.receiver, indexHandle, ctx.searchElement);
965         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(ctx.thread);
966         if (found) {
967             return indexHandle.GetTaggedValue();
968         }
969     }
970     return JSTaggedValue(-1); // Not found
971 }
972 
973 template <class Predicate>
FindRawDataDispatch(IndexOfType type,IndexOfContext & ctx,Predicate && predicate)974 JSTaggedValue JSStableArray::FindRawDataDispatch(IndexOfType type, IndexOfContext &ctx, Predicate &&predicate)
975 {
976     switch (type) {
977         case IndexOfType::IndexOf:
978             return FindRawData(ctx, std::forward<Predicate>(predicate));
979         case IndexOfType::LastIndexOf:
980             return FindLastRawData(ctx, std::forward<Predicate>(predicate));
981         default:
982             UNREACHABLE();
983     }
984 }
985 
986 // Zeros need special judge
IndexOfZero(IndexOfType type,IndexOfContext & ctx)987 JSTaggedValue JSStableArray::IndexOfZero(IndexOfType type, IndexOfContext &ctx)
988 {
989     return FindRawDataDispatch(type, ctx, [](JSTaggedType cur) {
990         return JSTaggedValue(cur).IsExactlyZero();
991     });
992 }
993 
IndexOfInt32(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)994 JSTaggedValue JSStableArray::IndexOfInt32(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
995 {
996     ASSERT(searchElement.IsInt());
997     int32_t untagged = searchElement.GetInt();
998     if (untagged == 0) {
999         return IndexOfZero(type, ctx);
1000     }
1001     JSTaggedType targetInt32 = searchElement.GetRawData();
1002     JSTaggedType targetDouble = JSTaggedValue(static_cast<double>(untagged)).GetRawData();
1003     return FindRawDataDispatch(type, ctx, [targetInt32, targetDouble](JSTaggedType cur) {
1004         return cur == targetInt32 || cur == targetDouble;
1005     });
1006 }
1007 
IndexOfDouble(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1008 JSTaggedValue JSStableArray::IndexOfDouble(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1009 {
1010     ASSERT(searchElement.IsDouble());
1011     double untagged = searchElement.GetDouble();
1012     if (std::isnan(untagged)) {
1013         return JSTaggedValue(-1);
1014     }
1015     if (untagged == 0.0) {
1016         return IndexOfZero(type, ctx);
1017     }
1018     JSTaggedType targetDouble = searchElement.GetRawData();
1019     if (searchElement.WithinInt32()) {
1020         JSTaggedType targetInt32 = JSTaggedValue(static_cast<int32_t>(untagged)).GetRawData();
1021         return FindRawDataDispatch(type, ctx, [targetDouble, targetInt32](JSTaggedType cur) {
1022             return cur == targetDouble || cur == targetInt32;
1023         });
1024     } else {
1025         return FindRawDataDispatch(type, ctx, [targetDouble](JSTaggedType cur) {
1026             return cur == targetDouble;
1027         });
1028     }
1029 }
1030 
IndexOfObjectAddress(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1031 JSTaggedValue JSStableArray::IndexOfObjectAddress(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1032 {
1033     ASSERT(searchElement.IsObject());
1034     JSTaggedType targetAddress = searchElement.GetRawData();
1035     return FindRawDataDispatch(type, ctx, [targetAddress](JSTaggedType cur) {
1036         return cur == targetAddress;
1037     });
1038 }
1039 
IndexOfString(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1040 JSTaggedValue JSStableArray::IndexOfString(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1041 {
1042     ASSERT(searchElement.IsString());
1043     JSTaggedType targetAddress = searchElement.GetRawData();
1044     return FindRawDataDispatch(type, ctx, [searchElement, targetAddress](JSTaggedType cur) {
1045         if (targetAddress == cur) {
1046             return true;
1047         }
1048         JSTaggedValue curValue(cur);
1049         if (!curValue.IsString()) {
1050             return false;
1051         }
1052         return JSTaggedValue::StringCompare(
1053             EcmaString::Cast(curValue.GetTaggedObject()),
1054             EcmaString::Cast(searchElement.GetTaggedObject()));
1055     });
1056 }
1057 
IndexOfBigInt(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1058 JSTaggedValue JSStableArray::IndexOfBigInt(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1059 {
1060     ASSERT(searchElement.IsBigInt());
1061     JSTaggedType targetAddress = searchElement.GetRawData();
1062     return FindRawDataDispatch(type, ctx, [searchElement, targetAddress](JSTaggedType cur) {
1063         if (cur == targetAddress) {
1064             return true;
1065         }
1066         JSTaggedValue curValue(cur);
1067         if (!curValue.IsBigInt()) {
1068             return false;
1069         }
1070         return BigInt::Equal(curValue, searchElement);
1071     });
1072 }
1073 
IndexOfDispatch(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)1074 JSTaggedValue JSStableArray::IndexOfDispatch(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
1075 {
1076     if (searchElement.IsInt()) {
1077         return IndexOfInt32(type, ctx, searchElement);
1078     } else if (searchElement.IsDouble()) {
1079         return IndexOfDouble(type, ctx, searchElement);
1080     } else if (searchElement.IsString()) {
1081         return IndexOfString(type, ctx, searchElement);
1082     } else if (searchElement.IsBigInt()) {
1083         return IndexOfBigInt(type, ctx, searchElement);
1084     } else {
1085         return IndexOfObjectAddress(type, ctx, searchElement);
1086     }
1087 }
1088 
IndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)1089 JSTaggedValue JSStableArray::IndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
1090                                      JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
1091 {
1092     IndexOfContext ctx;
1093     ctx.thread = thread;
1094     ctx.receiver = receiver;
1095     ctx.searchElement = searchElement;
1096     ctx.fromIndex = from;
1097     ctx.length = len;
1098     return IndexOfDispatch(IndexOfType::IndexOf, ctx, searchElement.GetTaggedValue());
1099 }
1100 
LastIndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)1101 JSTaggedValue JSStableArray::LastIndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
1102                                          JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
1103 {
1104     IndexOfContext ctx;
1105     ctx.thread = thread;
1106     ctx.receiver = receiver;
1107     ctx.searchElement = searchElement;
1108     ctx.fromIndex = from;
1109     ctx.length = len;
1110     return IndexOfDispatch(IndexOfType::LastIndexOf, ctx, searchElement.GetTaggedValue());
1111 }
1112 
Filter(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t & toIndex)1113 JSTaggedValue JSStableArray::Filter(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
1114                                     EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t &toIndex)
1115 {
1116     JSThread *thread = argv->GetThread();
1117     JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
1118     JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
1119     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1120     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1121     JSMutableHandle<JSTaggedValue> toIndexHandle(thread, JSTaggedValue::Undefined());
1122     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1123     const int32_t argsLength = 3; // 3: ?kValue, k, O?
1124     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1125     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1126     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
1127     while (k < len) {
1128         // Elements of thisObjHandle may change.
1129         JSTaggedValue value = ElementAccessor::Get(thisObjHandle, k);
1130         if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1131             value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1132             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1133         }
1134         kValue.Update(value);
1135         if (!kValue.GetTaggedValue().IsHole()) {
1136             key.Update(JSTaggedValue(k));
1137             EcmaRuntimeCallInfo *info =
1138                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1139             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1140             info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1141             JSTaggedValue callResult = JSFunction::Call(info);
1142             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1143             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
1144                 len = ElementAccessor::GetElementsLength(thisObjHandle);
1145             }
1146             bool boolResult = callResult.ToBoolean();
1147             if (boolResult) {
1148                 toIndexHandle.Update(JSTaggedValue(toIndex));
1149                 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toIndexHandle, kValue);
1150                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1151                 toIndex++;
1152             }
1153         }
1154         k++;
1155         if (!thisObjVal->IsStableJSArray(thread)) {
1156             break;
1157         }
1158     }
1159     return base::BuiltinsBase::GetTaggedDouble(true);
1160 }
1161 
Map(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t len)1162 JSTaggedValue JSStableArray::Map(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
1163                                  EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t len)
1164 {
1165     JSThread *thread = argv->GetThread();
1166     JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
1167     JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
1168     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1169     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1170     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1171     JSMutableHandle<JSTaggedValue> mapResultHandle(thread, JSTaggedValue::Undefined());
1172     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
1173     const int32_t argsLength = 3; // 3: ?kValue, k, O?
1174     while (k < len) {
1175         // Elements of thisObjHandle may change.
1176         JSTaggedValue value = ElementAccessor::Get(thisObjHandle, k);
1177         if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1178             value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1179             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1180         }
1181         kValue.Update(value);
1182         if (!kValue.GetTaggedValue().IsHole()) {
1183             key.Update(JSTaggedValue(k));
1184             EcmaRuntimeCallInfo *info =
1185                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1186             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1187             info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1188             JSTaggedValue mapResult = JSFunction::Call(info);
1189             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1190             mapResultHandle.Update(mapResult);
1191             JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, mapResultHandle);
1192             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1193             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
1194                 len = ElementAccessor::GetElementsLength(thisObjHandle);
1195             }
1196         }
1197         k++;
1198         if (!thisObjVal->IsStableJSArray(thread)) {
1199             break;
1200         }
1201     }
1202     return base::BuiltinsBase::GetTaggedDouble(true);
1203 }
1204 
Reverse(JSThread * thread,JSHandle<JSObject> thisObjHandle,int64_t & lower,uint32_t len)1205 JSTaggedValue JSStableArray::Reverse(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1206                                      int64_t &lower, uint32_t len)
1207 {
1208     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1209     if (thisObjHandle->IsJSArray()) {
1210         JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObjHandle));
1211     }
1212     ElementsKind kind = thisObjHandle->GetClass()->GetElementsKind();
1213     JSHandle<TaggedArray> elements(thread, thisObjHandle->GetElements());
1214     bool enableElementsKind = thread->GetEcmaVM()->IsEnableElementsKind();
1215     if (enableElementsKind) {
1216         if (kind == ElementsKind::INT || kind == ElementsKind::HOLE_INT) {
1217             return FastReverse(thread, elements, lower, len, ElementsKind::INT);
1218         } else if (kind == ElementsKind::NUMBER || kind == ElementsKind::HOLE_NUMBER) {
1219             return FastReverse(thread, elements, lower, len, ElementsKind::NUMBER);
1220         }
1221     }
1222     return FastReverse(thread, elements, lower, len, ElementsKind::TAGGED);
1223 }
1224 
FastReverse(JSThread * thread,JSHandle<TaggedArray> elements,int64_t & lower,uint32_t len,ElementsKind kind)1225 JSTaggedValue JSStableArray::FastReverse(JSThread *thread, JSHandle<TaggedArray> elements,
1226                                          int64_t &lower, uint32_t len, ElementsKind kind)
1227 {
1228     JSMutableHandle<JSTaggedValue> lowerValueHandle(thread, JSTaggedValue::Undefined());
1229     JSMutableHandle<JSTaggedValue> upperValueHandle(thread, JSTaggedValue::Undefined());
1230     int64_t middle = std::floor(len / 2);
1231     while (lower != middle) {
1232         if (elements->GetLength() != len) {
1233             break;
1234         }
1235         int64_t upper = static_cast<int64_t>(len) - lower - 1;
1236         lowerValueHandle.Update(ElementAccessor::FastGet(elements, lower, kind));
1237         upperValueHandle.Update(ElementAccessor::FastGet(elements, upper, kind));
1238         ElementAccessor::FastSet(thread, elements, lower, upperValueHandle, kind);
1239         ElementAccessor::FastSet(thread, elements, upper, lowerValueHandle, kind);
1240         lower++;
1241     }
1242     return base::BuiltinsBase::GetTaggedDouble(true);
1243 }
1244 
Concat(JSThread * thread,JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & n)1245 JSTaggedValue JSStableArray::Concat(JSThread *thread, JSHandle<JSObject> newArrayHandle,
1246                                     JSHandle<JSObject> thisObjHandle, int64_t &k, int64_t &n)
1247 {
1248     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1249     int64_t thisLen = base::ArrayHelper::GetArrayLength(thread, thisObjVal);
1250     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1251     JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
1252     while (k < thisLen) {
1253         if (ElementAccessor::GetElementsLength(thisObjHandle) != thisLen) {
1254             break;
1255         }
1256         toKey.Update(JSTaggedValue(n));
1257         JSTaggedValue kValue = ElementAccessor::Get(thisObjHandle, k);
1258         if (!kValue.IsHole()) {
1259             JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, JSHandle<JSTaggedValue>(thread, kValue));
1260             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1261         }
1262         n++;
1263         k++;
1264     }
1265     return base::BuiltinsBase::GetTaggedDouble(true);
1266 }
1267 
1268 template JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray<TypedArrayKind::SHARED>(
1269     JSThread *thread, JSHandle<JSTypedArray> &targetArray, DataViewType targetType,
1270     uint64_t targetOffset, uint32_t srcLength, JSHandle<JSObject> &obj);
1271 template JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray<TypedArrayKind::NON_SHARED>(
1272     JSThread *thread, JSHandle<JSTypedArray> &targetArray, DataViewType targetType,
1273     uint64_t targetOffset, uint32_t srcLength, JSHandle<JSObject> &obj);
1274 
1275 template<TypedArrayKind typedArrayKind>
FastCopyFromArrayToTypedArray(JSThread * thread,JSHandle<JSTypedArray> & targetArray,DataViewType targetType,uint64_t targetOffset,uint32_t srcLength,JSHandle<JSObject> & obj)1276 JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray(JSThread *thread, JSHandle<JSTypedArray> &targetArray,
1277                                                            DataViewType targetType, uint64_t targetOffset,
1278                                                            uint32_t srcLength, JSHandle<JSObject> &obj)
1279 {
1280     JSHandle<JSTaggedValue> targetBuffer(thread, targetArray->GetViewedArrayBufferOrByteArray());
1281     // If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
1282     if (BuiltinsArrayBufferType<typedArrayKind>::Type::IsDetachedBuffer(targetBuffer.GetTaggedValue())) {
1283         THROW_TYPE_ERROR_AND_RETURN(thread, "The targetBuffer of This value is detached buffer.",
1284                                     JSTaggedValue::Exception());
1285     }
1286     uint32_t targetLength = targetArray->GetArrayLength();
1287     uint32_t targetByteOffset = targetArray->GetByteOffset();
1288     uint32_t targetElementSize = TypedArrayHelper::GetSizeFromType(targetType);
1289     if (srcLength + targetOffset > targetLength) {
1290         THROW_RANGE_ERROR_AND_RETURN(thread, "The sum of length and targetOffset is greater than targetLength.",
1291                                      JSTaggedValue::Exception());
1292     }
1293     uint32_t targetByteIndex = static_cast<uint32_t>(targetOffset * targetElementSize + targetByteOffset);
1294     ContentType contentType = targetArray->GetContentType();
1295     uint32_t elemLen = ElementAccessor::GetElementsLength(obj);
1296     if (contentType == ContentType::BigInt) {
1297         JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Hole());
1298         JSMutableHandle<JSTaggedValue> elem(thread, JSTaggedValue::Hole());
1299         for (uint32_t i = 0; i < srcLength; i++) {
1300             if (i < elemLen) {
1301                 elem.Update(ElementAccessor::Get(obj, i));
1302             } else {
1303                 elem.Update(JSTaggedValue::Hole());
1304             }
1305             kValue.Update(JSTaggedValue::ToBigInt(thread, elem));
1306             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1307             BuiltinsArrayBufferType<typedArrayKind>::Type::SetValueInBuffer(
1308                 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, kValue, true);
1309             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1310             targetByteIndex += targetElementSize;
1311         }
1312     } else {
1313         double val = 0.0;
1314         uint32_t copyLen = srcLength > elemLen ? elemLen : srcLength;
1315         for (uint32_t i = 0; i < copyLen; i++) {
1316             JSTaggedValue taggedVal = ElementAccessor::Get(obj, i);
1317             if (!taggedVal.IsNumber()) {
1318                 JSTaggedNumber taggedNumber = JSTaggedValue::ToNumber(thread, taggedVal);
1319                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1320                 val = taggedNumber.GetNumber();
1321             } else {
1322                 val = taggedVal.GetNumber();
1323             }
1324             BuiltinsArrayBufferType<typedArrayKind>::Type::FastSetValueInBuffer(
1325                 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, val, true);
1326             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1327             targetByteIndex += targetElementSize;
1328         }
1329 
1330         for (uint32_t i = copyLen; i < srcLength; i++) {
1331             val = JSTaggedNumber(base::NAN_VALUE).GetNumber();
1332             BuiltinsArrayBufferType<typedArrayKind>::Type::FastSetValueInBuffer(
1333                 thread, targetBuffer.GetTaggedValue(), targetByteIndex, targetType, val, true);
1334             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1335             targetByteIndex += targetElementSize;
1336         }
1337     }
1338     return JSTaggedValue::Undefined();
1339 }
1340 
At(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)1341 JSTaggedValue JSStableArray::At(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
1342 {
1343     JSThread *thread = argv->GetThread();
1344     uint32_t thisLen = receiver->GetArrayLength();
1345     if (thisLen == 0) {
1346         return JSTaggedValue::Undefined();
1347     }
1348     JSTaggedNumber index = JSTaggedValue::ToInteger(thread, base::BuiltinsBase::GetCallArg(argv, 0));
1349     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1350     int64_t relativeIndex = index.GetNumber();
1351     int64_t k = 0;
1352     if (relativeIndex >= 0) {
1353         k = relativeIndex;
1354     } else {
1355         k = static_cast<int64_t>(thisLen) + relativeIndex;
1356     }
1357     if (k < 0 || k >= thisLen) {
1358         return JSTaggedValue::Undefined();
1359     }
1360 
1361     auto result = JSTaggedValue::Hole();
1362     result = ElementAccessor::Get(JSHandle<JSObject>::Cast(receiver), k);
1363     return result.IsHole() ? JSTaggedValue::Undefined() : result;
1364 }
1365 
At(JSHandle<JSSharedArray> receiver,EcmaRuntimeCallInfo * argv)1366 JSTaggedValue JSStableArray::At(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv)
1367 {
1368     JSThread *thread = argv->GetThread();
1369     uint32_t thisLen = receiver->GetArrayLength();
1370     if (thisLen == 0) {
1371         return JSTaggedValue::Undefined();
1372     }
1373     JSTaggedNumber index = JSTaggedValue::ToInteger(thread, base::BuiltinsBase::GetCallArg(argv, 0));
1374     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1375     int64_t relativeIndex = index.GetNumber();
1376     int64_t k = 0;
1377     if (relativeIndex >= 0) {
1378         k = relativeIndex;
1379     } else {
1380         k = static_cast<int64_t>(thisLen) + relativeIndex;
1381     }
1382     if (k < 0 || k >= thisLen) {
1383         return JSTaggedValue::Undefined();
1384     }
1385 
1386     auto result = JSTaggedValue::Hole();
1387     result = ElementAccessor::Get(JSHandle<JSObject>::Cast(receiver), k);
1388     return result.IsHole() ? JSTaggedValue::Undefined() : result;
1389 }
1390 
With(JSThread * thread,JSHandle<JSArray> receiver,int64_t insertCount,int64_t index,JSHandle<JSTaggedValue> value)1391 JSTaggedValue JSStableArray::With(JSThread *thread, JSHandle<JSArray> receiver,
1392                                   int64_t insertCount, int64_t index, JSHandle<JSTaggedValue> value)
1393 {
1394     JSHandle<JSObject> thisObjHandle(receiver);
1395     JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1396     JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1397     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1398     JSHandle<JSObject> newArrayHandle(newArray);
1399 
1400     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1401     TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
1402 
1403     if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
1404         destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1405     }
1406     ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1407     bool needTransition = true;
1408     for (uint32_t idx = 0; idx < insertCount; idx++) {
1409         if (idx == index) {
1410             ElementAccessor::Set(thread, newArrayHandle, idx, value, needTransition);
1411         } else {
1412             JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thisObjHandle, idx));
1413             if (kValue->IsHole()) {
1414                 ElementAccessor::Set(thread, newArrayHandle, idx, undefinedHandle, needTransition);
1415             } else {
1416                 ElementAccessor::Set(thread, newArrayHandle, idx, kValue, needTransition);
1417             }
1418         }
1419     }
1420     JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1421     return newArrayHandle.GetTaggedValue();
1422 }
1423 
ToSpliced(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv,int64_t argc,int64_t actualStart,int64_t actualSkipCount,int64_t insertCount)1424 JSTaggedValue JSStableArray::ToSpliced(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv,
1425                                        int64_t argc, int64_t actualStart, int64_t actualSkipCount, int64_t insertCount)
1426 {
1427     JSThread *thread = argv->GetThread();
1428 
1429     JSHandle<JSObject> thisObjHandle(receiver);
1430     JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1431     JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1432     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1433     JSHandle<JSObject> newArrayHandle(newArray);
1434 
1435     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1436     TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
1437 
1438     if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
1439         destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1440     }
1441     ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1442     int64_t i = 0;
1443     int64_t r = actualStart + actualSkipCount;
1444     bool needTransition = true;
1445     for (int64_t idx = 0; idx < actualStart; idx++, i++) {
1446         JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thisObjHandle, idx));
1447         if (kValue->IsHole()) {
1448             ElementAccessor::Set(thread, newArrayHandle, i, undefinedHandle, needTransition);
1449         } else {
1450             ElementAccessor::Set(thread, newArrayHandle, i, kValue, needTransition);
1451         }
1452     }
1453 
1454     for (uint32_t pos = 2; pos < argc; ++pos) { // 2:2 means there two arguments before the insert items.
1455         auto element = base::BuiltinsBase::GetCallArg(argv, pos);
1456         ElementAccessor::Set(thread, newArrayHandle, i, element, needTransition);
1457         ++i;
1458     }
1459 
1460     while (i < insertCount) {
1461         JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thisObjHandle, r));
1462         if (kValue->IsHole()) {
1463             ElementAccessor::Set(thread, newArrayHandle, i, undefinedHandle, needTransition);
1464         } else {
1465             ElementAccessor::Set(thread, newArrayHandle, i, kValue, needTransition);
1466         }
1467         ++i;
1468         ++r;
1469     }
1470 
1471     JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1472 
1473     return newArrayHandle.GetTaggedValue();
1474 }
1475 
ToReversed(JSThread * thread,JSHandle<JSArray> receiver,int64_t insertCount)1476 JSTaggedValue JSStableArray::ToReversed(JSThread *thread, JSHandle<JSArray> receiver,
1477                                         int64_t insertCount)
1478 {
1479     JSHandle<JSObject> thisObjHandle(receiver);
1480     JSHandle<JSTaggedValue> undefinedHandle(thread, JSTaggedValue::Undefined());
1481     JSHandle<JSTaggedValue> newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
1482     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1483     JSHandle<JSObject> newArrayHandle(newArray);
1484 
1485     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1486     TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
1487 
1488     if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
1489         destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1490     }
1491     ASSERT(!newArrayHandle->GetJSHClass()->IsDictionaryMode());
1492     bool needTransition = true;
1493     for (uint32_t idx = 0; idx < insertCount; idx++) {
1494         JSHandle<JSTaggedValue> kValue(thread, ElementAccessor::Get(thisObjHandle, idx));
1495         if (kValue->IsHole()) {
1496             ElementAccessor::Set(thread, newArrayHandle, insertCount - idx - 1,
1497                                  undefinedHandle, needTransition);
1498         } else {
1499             ElementAccessor::Set(thread, newArrayHandle, insertCount - idx - 1, kValue, needTransition);
1500         }
1501     }
1502     JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1503 
1504     return newArrayHandle.GetTaggedValue();
1505 }
1506 
Reduce(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSMutableHandle<JSTaggedValue> accumulator,int64_t & k,int64_t & len)1507 JSTaggedValue JSStableArray::Reduce(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1508                                     JSHandle<JSTaggedValue> callbackFnHandle,
1509                                     JSMutableHandle<JSTaggedValue> accumulator, int64_t &k, int64_t &len)
1510 {
1511     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1512     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1513     JSTaggedValue callResult = JSTaggedValue::Undefined();
1514     while (k < len) {
1515         JSTaggedValue kValue(ElementAccessor::Get(thisObjHandle, k));
1516         if (!kValue.IsHole()) {
1517             JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1518             const uint32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
1519             EcmaRuntimeCallInfo *info =
1520                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, undefined, undefined, argsLength);
1521             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1522             info->SetCallArg(accumulator.GetTaggedValue(), kValue, JSTaggedValue(k),
1523                              thisObjVal.GetTaggedValue());
1524             callResult = JSFunction::Call(info);
1525             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1526             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
1527                 len = ElementAccessor::GetElementsLength(thisObjHandle);
1528             }
1529             accumulator.Update(callResult);
1530         }
1531         k++;
1532         if (!thisObjVal->IsStableJSArray(thread)) {
1533             break;
1534         }
1535     }
1536     return base::BuiltinsBase::GetTaggedDouble(true);
1537 }
1538 
Slice(JSThread * thread,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & count)1539 JSTaggedValue JSStableArray::Slice(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1540                                    int64_t &k, int64_t &count)
1541 {
1542     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1543     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1544     int64_t len = static_cast<int64_t>(ElementAccessor::GetElementsLength(thisObjHandle));
1545     int64_t oldLen;
1546     if (len > k + count) {
1547         oldLen = count;
1548     } else {
1549         oldLen = std::max<int64_t>(len - k, 0);
1550     }
1551     JSHandle<JSObject> arrayObj = factory->NewAndCopyJSArrayObject(thisObjHandle, count, oldLen, k);
1552     for (int i = 0; i < count; i++) {
1553         JSMutableHandle<JSTaggedValue> value(thread, ElementAccessor::Get(arrayObj, i));
1554         if (value->IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, i + k)) {
1555             value.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, i + k).GetTaggedValue());
1556             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1557             ElementAccessor::Set(thread, arrayObj, i, value, true);
1558         }
1559     }
1560     return arrayObj.GetTaggedValue();
1561 }
1562 
Sort(JSThread * thread,const JSHandle<JSObject> & thisObj,const JSHandle<JSTaggedValue> & callbackFnHandle)1563 JSTaggedValue JSStableArray::Sort(JSThread *thread, const JSHandle<JSObject> &thisObj,
1564                                   const JSHandle<JSTaggedValue> &callbackFnHandle)
1565 {
1566     JSArray::SortElementsByObject(thread, thisObj, callbackFnHandle);
1567     return thisObj.GetTaggedValue();
1568 }
1569 
Fill(JSThread * thread,const JSHandle<JSObject> & thisObj,const JSHandle<JSTaggedValue> & value,int64_t start,int64_t end,int64_t len)1570 JSTaggedValue JSStableArray::Fill(JSThread *thread, const JSHandle<JSObject> &thisObj,
1571                                   const JSHandle<JSTaggedValue> &value, int64_t start,
1572                                   int64_t end, int64_t len)
1573 {
1574     JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObj));
1575     uint32_t length = ElementAccessor::GetElementsLength(thisObj);
1576     ElementsKind oldKind = thisObj->GetClass()->GetElementsKind();
1577     if (JSHClass::TransitToElementsKind(thread, thisObj, value)) {
1578         ElementsKind newKind = thisObj->GetClass()->GetElementsKind();
1579         Elements::MigrateArrayWithKind(thread, thisObj, oldKind, newKind);
1580     }
1581     if (length >= end) {
1582         if (thisObj->GetElements().IsMutantTaggedArray()) {
1583             ElementsKind kind = thisObj->GetClass()->GetElementsKind();
1584             TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements());
1585             JSTaggedValue migratedValue = JSTaggedValue(ElementAccessor::ConvertTaggedValueWithElementsKind(
1586                 value.GetTaggedValue(), kind));
1587             for (int64_t idx = start; idx < end; idx++) {
1588                 elements->Set<false>(thread, idx, migratedValue);
1589             }
1590         } else {
1591             TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements());
1592             for (int64_t idx = start; idx < end; idx++) {
1593                 elements->Set(thread, idx, value);
1594             }
1595         }
1596         return thisObj.GetTaggedValue();
1597     } else {
1598         if (thisObj->GetElements().IsMutantTaggedArray()) {
1599             ElementsKind kind = thisObj->GetClass()->GetElementsKind();
1600             JSHandle<MutantTaggedArray> newElements = thread->GetEcmaVM()->GetFactory()->NewMutantTaggedArray(len);
1601             JSTaggedValue migratedValue = JSTaggedValue(ElementAccessor::ConvertTaggedValueWithElementsKind(
1602                 value.GetTaggedValue(), kind));
1603             for (int64_t idx = start; idx < end; idx++) {
1604                 newElements->Set<false>(thread, idx, migratedValue);
1605             }
1606             thisObj->SetElements(thread, newElements);
1607         } else {
1608             JSHandle<TaggedArray> newElements = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len);
1609             for (int64_t idx = start; idx < end; idx++) {
1610                 newElements->Set(thread, idx, value);
1611             }
1612             thisObj->SetElements(thread, newElements);
1613         }
1614         return thisObj.GetTaggedValue();
1615     }
1616 }
1617 
HandleFindLastOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,JSMutableHandle<JSTaggedValue> & kValue,int64_t & k)1618 JSTaggedValue JSStableArray::HandleFindLastOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1619                                                     JSHandle<JSTaggedValue> callbackFnHandle,
1620                                                     JSHandle<JSTaggedValue> thisArgHandle,
1621                                                     JSMutableHandle<JSTaggedValue> &kValue, int64_t &k)
1622 {
1623     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1624     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1625     const uint32_t argsLength = 3; // 3: «kValue, k, O»
1626     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
1627     while (k >= 0) {
1628         JSTaggedValue val = ElementAccessor::Get(thisObjHandle, k);
1629         if (!val.IsHole()) {
1630             kValue.Update(val);
1631         } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
1632             auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
1633             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1634             kValue.Update(res);
1635         } else {
1636             kValue.Update(JSTaggedValue::Undefined());
1637         }
1638         EcmaRuntimeCallInfo *info =
1639             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1640         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1641         info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
1642         callResult = JSFunction::Call(info);
1643         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1644         if (callResult.ToBoolean()) {
1645             return callResult;
1646         }
1647         k--;
1648         ASSERT(ElementAccessor::GetElementsLength(thisObjHandle) > 0);
1649         if (ElementAccessor::GetElementsLength(thisObjHandle) - 1 < k) {
1650             break;
1651         }
1652         if (!thisObjVal->IsStableJSArray(thread)) {
1653             break;
1654         }
1655     }
1656     return callResult;
1657 }
1658 
HandleReduceRightOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSMutableHandle<JSTaggedValue> & accumulator,JSHandle<JSTaggedValue> thisArgHandle,int64_t & k)1659 JSTaggedValue JSStableArray::HandleReduceRightOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1660                                                        JSHandle<JSTaggedValue> callbackFnHandle,
1661                                                        JSMutableHandle<JSTaggedValue> &accumulator,
1662                                                        JSHandle<JSTaggedValue> thisArgHandle, int64_t &k)
1663 {
1664     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1665     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Hole());
1666     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
1667     JSTaggedValue callResult = JSTaggedValue::Undefined();
1668     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1669     const int32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
1670     int64_t len = static_cast<int64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1671     while (k >= 0) {
1672         key.Update(JSTaggedValue(k));
1673         kValue.Update(ElementAccessor::Get(thisObjHandle, k));
1674         if (!kValue.GetTaggedValue().IsHole()) {
1675             EcmaRuntimeCallInfo *info =
1676                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
1677             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1678             info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(),
1679                 key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1680             callResult = JSFunction::Call(info);
1681             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1682             accumulator.Update(callResult);
1683         } else {
1684             bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, key);
1685             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1686             if (exists) {
1687                 auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, key).GetTaggedValue();
1688                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1689                 kValue.Update(res);
1690                 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle,
1691                     thisArgHandle, undefined, argsLength);
1692                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1693                 info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(),
1694                     key.GetTaggedValue(), thisObjVal.GetTaggedValue());
1695                 callResult = JSFunction::Call(info);
1696                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1697                 accumulator.Update(callResult);
1698             }
1699         }
1700         k--;
1701         int64_t newLen = static_cast<int64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
1702         if (!thisObjVal->IsStableJSArray(thread) || newLen != len) {
1703             return base::BuiltinsBase::GetTaggedBoolean(false);
1704         }
1705     }
1706     return base::BuiltinsBase::GetTaggedBoolean(true);
1707 }
1708 }  // namespace panda::ecmascript
1709