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