• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/js_stable_array.h"
17 
18 #include "ecmascript/base/array_helper.h"
19 #include "ecmascript/base/builtins_base.h"
20 #include "ecmascript/base/typed_array_helper-inl.h"
21 #include "ecmascript/builtins/builtins_arraybuffer.h"
22 #include "ecmascript/ecma_macros.h"
23 #include "ecmascript/ecma_vm.h"
24 #include "ecmascript/ecma_string-inl.h"
25 #include "ecmascript/element_accessor.h"
26 #include "ecmascript/element_accessor-inl.h"
27 #include "ecmascript/global_env.h"
28 #include "ecmascript/interpreter/fast_runtime_stub-inl.h"
29 #include "ecmascript/js_array.h"
30 #include "ecmascript/js_tagged_value-inl.h"
31 #include "ecmascript/js_tagged_value.h"
32 #include "ecmascript/object_factory.h"
33 #include "ecmascript/tagged_array.h"
34 #include "macros.h"
35 
36 namespace panda::ecmascript {
37 using TypedArrayHelper = base::TypedArrayHelper;
38 using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer;
39 
Push(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)40 JSTaggedValue JSStableArray::Push(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
41 {
42     JSThread *thread = argv->GetThread();
43     uint32_t argc = argv->GetArgsNumber();
44     uint32_t oldLength = receiver->GetArrayLength();
45     uint32_t newLength = argc + oldLength;
46     JSHandle<JSObject> thisObjHandle(receiver);
47 
48     TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
49     if (newLength > ElementAccessor::GetElementsLength(thisObjHandle)) {
50         elements = *JSObject::GrowElementsCapacity(thread, JSHandle<JSObject>::Cast(receiver), newLength, true);
51     }
52     bool needTransition = true;
53     for (uint32_t k = 0; k < argc; k++) {
54         JSHandle<JSTaggedValue> value = argv->GetCallArg(k);
55         ElementAccessor::Set(thread, thisObjHandle, oldLength + k, value.GetTaggedValue(), needTransition);
56     }
57     receiver->SetArrayLength(thread, newLength);
58 
59     return JSTaggedValue(newLength);
60 }
61 
Pop(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)62 JSTaggedValue JSStableArray::Pop(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
63 {
64     JSThread *thread = argv->GetThread();
65     uint32_t length = receiver->GetArrayLength();
66     if (length == 0) {
67         return JSTaggedValue::Undefined();
68     }
69 
70     JSArray::CheckAndCopyArray(thread, receiver);
71     JSHandle<JSObject> obj(receiver);
72     uint32_t capacity = ElementAccessor::GetElementsLength(obj);
73     uint32_t index = length - 1;
74     auto result = JSTaggedValue::Hole();
75     if (index < capacity) {
76         result = ElementAccessor::Get(obj, index);
77     }
78     if (!result.IsHole()) {
79         if (TaggedArray::ShouldTrim(capacity, index)) {
80             TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
81             elements->Trim(thread, index);
82         } else {
83             ElementAccessor::Set(thread, obj, index, JSTaggedValue::Hole(), false);
84         }
85     } else {
86         JSHandle<JSTaggedValue> thisObjVal(receiver);
87         result = JSArray::FastGetPropertyByValue(thread, thisObjVal, index).GetTaggedValue();
88     }
89     receiver->SetArrayLength(thread, index);
90     return result.IsHole() ? JSTaggedValue::Undefined() : result;
91 }
92 
Splice(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv,uint32_t start,uint32_t insertCount,uint32_t actualDeleteCount,JSHandle<JSObject> newArrayHandle,uint32_t len)93 JSTaggedValue JSStableArray::Splice(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv,
94                                     uint32_t start, uint32_t insertCount, uint32_t actualDeleteCount,
95                                     JSHandle<JSObject> newArrayHandle, uint32_t len)
96 {
97     JSThread *thread = argv->GetThread();
98     uint32_t argc = argv->GetArgsNumber();
99 
100     JSHandle<JSObject> thisObjHandle(receiver);
101     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
102     JSArray::CheckAndCopyArray(thread, receiver);
103     JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
104     TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements().GetTaggedObject());
105     JSMutableHandle<TaggedArray> srcElementsHandle(thread, srcElements);
106     bool needTransition = true;
107     if (newArrayHandle.GetTaggedValue().IsStableJSArray(thread)) {
108         TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
109         if (actualDeleteCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
110             destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, actualDeleteCount);
111         }
112 
113         for (uint32_t idx = 0; idx < actualDeleteCount; idx++) {
114             if ((start + idx) >= ElementAccessor::GetElementsLength(thisObjHandle)) {
115                 ElementAccessor::Set(thread, newArrayHandle, idx, JSTaggedValue::Hole(), needTransition);
116             } else {
117                 ElementAccessor::Set(thread, newArrayHandle, idx,
118                                      ElementAccessor::Get(thisObjHandle, start + idx), needTransition);
119             }
120         }
121         JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, actualDeleteCount);
122     } else {
123         JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
124         JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
125         uint32_t k = 0;
126         while (k < actualDeleteCount) {
127             uint32_t from = start + k;
128             fromKey.Update(JSTaggedValue(from));
129             bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
130             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
131             if (exists) {
132                 JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
133                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
134                 toKey.Update(JSTaggedValue(k));
135                 if (newArrayHandle->IsJSProxy()) {
136                     toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue());
137                     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
138                 }
139                 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue);
140                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
141             }
142             k++;
143         }
144 
145         JSHandle<JSTaggedValue> deleteCount(thread, JSTaggedValue(actualDeleteCount));
146         JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, deleteCount,
147                                    true);
148         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
149     }
150     uint32_t oldCapacity = ElementAccessor::GetElementsLength(thisObjHandle);
151     uint32_t newCapacity = len - actualDeleteCount + insertCount;
152     if (newCapacity > oldCapacity) {
153         srcElementsHandle.Update(JSObject::GrowElementsCapacity(thread, thisObjHandle, newCapacity));
154     }
155     if (insertCount < actualDeleteCount) {
156         JSArray::CheckAndCopyArray(thread, receiver);
157         srcElementsHandle.Update(receiver->GetElements());
158         for (uint32_t idx = start; idx < len - actualDeleteCount; idx++) {
159             auto element = JSTaggedValue::Hole();
160             if ((idx + actualDeleteCount) < ElementAccessor::GetElementsLength(thisObjHandle)) {
161                 element = ElementAccessor::Get(thisObjHandle, idx + actualDeleteCount);
162             }
163             if ((idx + insertCount) < ElementAccessor::GetElementsLength(thisObjHandle)) {
164                 ElementAccessor::Set(thread, thisObjHandle, idx + insertCount, element, needTransition);
165             }
166         }
167 
168         if ((oldCapacity > newCapacity) && TaggedArray::ShouldTrim(oldCapacity, newCapacity)) {
169             srcElementsHandle->Trim(thread, newCapacity);
170         } else {
171             for (uint32_t idx = newCapacity; idx < len; idx++) {
172                 if (idx < ElementAccessor::GetElementsLength(thisObjHandle)) {
173                     ElementAccessor::Set(thread, thisObjHandle, idx, JSTaggedValue::Hole(), needTransition);
174                 }
175             }
176         }
177     } else {
178         for (uint32_t idx = len - actualDeleteCount; idx > start; idx--) {
179             auto element = ElementAccessor::Get(thisObjHandle, idx + actualDeleteCount - 1);
180             ElementAccessor::Set(thread, thisObjHandle, idx + insertCount - 1, element, needTransition);
181         }
182     }
183 
184     for (uint32_t i = 2, idx = start; i < argc; i++, idx++) {
185         ElementAccessor::Set(thread, thisObjHandle, idx, argv->GetCallArg(i), needTransition);
186     }
187 
188     JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newCapacity));
189     JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
190     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
191     return newArrayHandle.GetTaggedValue();
192 }
193 
Shift(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)194 JSTaggedValue JSStableArray::Shift(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
195 {
196     JSThread *thread = argv->GetThread();
197     JSHandle<JSObject> thisObjHandle(receiver);
198     uint32_t length = receiver->GetArrayLength();
199     if (length == 0) {
200         return JSTaggedValue::Undefined();
201     }
202     JSArray::CheckAndCopyArray(thread, receiver);
203     TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
204     auto result = ElementAccessor::Get(thisObjHandle, 0);
205     bool needTransition = false;
206     for (uint32_t k = 1; k < length; k++) {
207         auto kValue = ElementAccessor::Get(thisObjHandle, k);
208         ElementAccessor::Set(thread, thisObjHandle, k - 1, kValue, needTransition);
209     }
210     uint32_t capacity = ElementAccessor::GetElementsLength(thisObjHandle);
211     uint32_t index = length - 1;
212     if (TaggedArray::ShouldTrim(capacity, index)) {
213         elements->Trim(thread, index);
214     } else {
215         ElementAccessor::Set(thread, thisObjHandle, index, JSTaggedValue::Hole(), needTransition);
216     }
217     receiver->SetArrayLength(thread, index);
218     return result.IsHole() ? JSTaggedValue::Undefined() : result;
219 }
220 
SetSepValue(JSHandle<EcmaString> sepStringHandle,int & sep,uint32_t & sepLength)221 void JSStableArray::SetSepValue(JSHandle<EcmaString> sepStringHandle, int &sep, uint32_t &sepLength)
222 {
223     if (EcmaStringAccessor(sepStringHandle).IsUtf8() && EcmaStringAccessor(sepStringHandle).GetLength() == 1) {
224         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
225         sep = EcmaStringAccessor(sepStringHandle).Get(0);
226     } else if (EcmaStringAccessor(sepStringHandle).GetLength() == 0) {
227         sep = JSStableArray::SeparatorFlag::MINUS_TWO;
228         sepLength = 0;
229     } else {
230         sep = JSStableArray::SeparatorFlag::MINUS_ONE;
231         sepLength = EcmaStringAccessor(sepStringHandle).GetLength();
232     }
233 }
234 
Join(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)235 JSTaggedValue JSStableArray::Join(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
236 {
237     JSThread *thread = argv->GetThread();
238     uint32_t length = receiver->GetArrayLength();
239     JSHandle<JSTaggedValue> sepHandle = base::BuiltinsBase::GetCallArg(argv, 0);
240     int sep = ',';
241     uint32_t sepLength = 1;
242     JSHandle<EcmaString> sepStringHandle;
243     auto context = thread->GetCurrentEcmaContext();
244     JSHandle<JSTaggedValue> receiverValue = JSHandle<JSTaggedValue>::Cast(receiver);
245     if (!sepHandle->IsUndefined()) {
246         if (sepHandle->IsString()) {
247             sepStringHandle = JSHandle<EcmaString>::Cast(sepHandle);
248         } else {
249             sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
250             RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
251         }
252         SetSepValue(sepStringHandle, sep, sepLength);
253     }
254     if (length == 0) {
255         const GlobalEnvConstants *globalConst = thread->GlobalConstants();
256         context->JoinStackPopFastPath(receiverValue);
257         return globalConst->GetEmptyString();
258     }
259     JSHandle<JSObject> obj(thread, receiverValue.GetTaggedValue());
260     size_t allocateLength = 0;
261     bool isOneByte = (sep != JSStableArray::SeparatorFlag::MINUS_ONE) || EcmaStringAccessor(sepStringHandle).IsUtf8();
262     CVector<JSHandle<EcmaString>> vec;
263     JSMutableHandle<JSTaggedValue> elementHandle(thread, JSTaggedValue::Undefined());
264     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
265     uint32_t elementsLength = ElementAccessor::GetElementsLength(obj);
266     uint32_t len = elementsLength > length ? length : elementsLength;
267     if (elementsLength == 0 && length != 0) {
268         len = length;
269     }
270     if (len <= 1) {
271         // sep unused, set isOneByte to default(true)
272         isOneByte = true;
273     }
274     for (uint32_t k = 0; k < len; k++) {
275         JSTaggedValue element = JSTaggedValue::Undefined();
276         if (k < elementsLength) {
277             element = ElementAccessor::Get(obj, k);
278         }
279         if (!element.IsUndefinedOrNull() && !element.IsHole()) {
280             if (!element.IsString()) {
281                 elementHandle.Update(element);
282                 JSHandle<EcmaString> strElement = JSTaggedValue::ToString(thread, elementHandle);
283                 RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
284                 element = strElement.GetTaggedValue();
285             }
286             auto nextStr = EcmaString::Cast(element.GetTaggedObject());
287             JSHandle<EcmaString> nextStrHandle(thread, nextStr);
288             vec.push_back(nextStrHandle);
289             isOneByte = EcmaStringAccessor(nextStr).IsUtf8() ? isOneByte : false;
290             allocateLength += EcmaStringAccessor(nextStr).GetLength();
291         } else {
292             vec.push_back(JSHandle<EcmaString>(globalConst->GetHandledEmptyString()));
293         }
294     }
295     if (len > 0) {
296         allocateLength += sepLength * (len - 1);
297     }
298     auto newString = EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), allocateLength, isOneByte);
299     int current = 0;
300     DISALLOW_GARBAGE_COLLECTION;
301     for (uint32_t k = 0; k < len; k++) {
302         if (k > 0) {
303             if (sep >= 0) {
304                 EcmaStringAccessor(newString).Set(current, static_cast<uint16_t>(sep));
305             } else if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
306                 EcmaStringAccessor::ReadData(newString, *sepStringHandle, current,
307                     allocateLength - static_cast<uint32_t>(current), sepLength);
308             }
309             current += static_cast<int>(sepLength);
310         }
311         JSHandle<EcmaString> nextStr = vec[k];
312         int nextLength = static_cast<int>(EcmaStringAccessor(nextStr).GetLength());
313         EcmaStringAccessor::ReadData(newString, *nextStr, current,
314             allocateLength - static_cast<uint32_t>(current), nextLength);
315         current += nextLength;
316     }
317     ASSERT_PRINT(
318         isOneByte == EcmaStringAccessor::CanBeCompressed(newString), "isOneByte does not match the real value!");
319     context->JoinStackPopFastPath(receiverValue);
320     return JSTaggedValue(newString);
321 }
322 
HandleFindIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)323 JSTaggedValue JSStableArray::HandleFindIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
324                                                      JSHandle<JSTaggedValue> callbackFnHandle,
325                                                      JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
326 {
327     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
328     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
329     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
330     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
331     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
332     const int32_t argsLength = 3; // 3: ?kValue, k, O?
333     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
334     while (k < len) {
335         // Elements of thisObjHandle may change.
336         JSTaggedValue val = ElementAccessor::Get(thisObjHandle, k);
337         if (val.IsHole()) {
338             auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
339             if (res.IsHole()) {
340                 kValue.Update(JSTaggedValue::Undefined());
341             } else {
342                 kValue.Update(res);
343             }
344         } else {
345             kValue.Update(val);
346         }
347         EcmaRuntimeCallInfo *info =
348             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
349         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
350         info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
351         callResult = JSFunction::Call(info);
352         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
353         if (callResult.ToBoolean()) {
354             return callResult;
355         }
356         if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
357             len = ElementAccessor::GetElementsLength(thisObjHandle);
358         }
359         k++;
360         if (!thisObjVal->IsStableJSArray(thread)) {
361             return callResult;
362         }
363     }
364     return callResult;
365 }
366 
HandleFindLastIndexOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,int64_t & k)367 JSTaggedValue JSStableArray::HandleFindLastIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
368                                                          JSHandle<JSTaggedValue> callbackFnHandle,
369                                                          JSHandle<JSTaggedValue> thisArgHandle, int64_t &k)
370 {
371     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
372     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
373     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false);
374     const int32_t argsLength = 3; // 3: ?kValue, k, O?
375     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
376     while (k >= 0) {
377         // Elements of thisObjHandle may change.
378         JSTaggedValue val = ElementAccessor::Get(thisObjHandle, k);
379         if (val.IsHole()) {
380             auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
381             if (res.IsHole()) {
382                 kValue.Update(JSTaggedValue::Undefined());
383             } else {
384                 kValue.Update(res);
385             }
386         } else {
387             kValue.Update(val);
388         }
389         EcmaRuntimeCallInfo *info =
390             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
391         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
392         info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
393         callResult = JSFunction::Call(info);
394         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult);
395         if (callResult.ToBoolean()) {
396             return callResult;
397         }
398         k--;
399         if (base::ArrayHelper::GetArrayLength(thread, thisObjVal) - 1 < k) {
400             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
401             return callResult;
402         }
403         if (!thisObjVal->IsStableJSArray(thread)) {
404             return callResult;
405         }
406     }
407     return callResult;
408 }
409 
HandleEveryOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t & k)410 JSTaggedValue JSStableArray::HandleEveryOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
411                                                  JSHandle<JSTaggedValue> callbackFnHandle,
412                                                  JSHandle<JSTaggedValue> thisArgHandle, uint32_t &k)
413 {
414     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
415     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
416     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
417     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
418     const int32_t argsLength = 3; // 3: ?kValue, k, O?
419     JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(true);
420     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
421     while (k < len) {
422         // Elements of thisObjHandle may change.
423         kValue.Update(ElementAccessor::Get(thisObjHandle, k));
424         if (!kValue.GetTaggedValue().IsHole()) {
425             EcmaRuntimeCallInfo *info =
426                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
427             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
428             info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
429             callResult = JSFunction::Call(info);
430             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
431             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
432                 len = ElementAccessor::GetElementsLength(thisObjHandle);
433             }
434         } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
435             JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
436             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
437             EcmaRuntimeCallInfo *info =
438                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
439             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
440             info->SetCallArg(kValue1.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue());
441             callResult = JSFunction::Call(info);
442             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
443         }
444         if (!callResult.ToBoolean()) {
445             return base::BuiltinsBase::GetTaggedBoolean(false);
446         }
447         k++;
448         if (!thisObjVal->IsStableJSArray(thread)) {
449             return base::BuiltinsBase::GetTaggedBoolean(true);
450         }
451     }
452     return base::BuiltinsBase::GetTaggedBoolean(true);
453 }
454 
HandleforEachOfStable(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSHandle<JSTaggedValue> thisArgHandle,uint32_t len,uint32_t & k)455 JSTaggedValue JSStableArray::HandleforEachOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
456                                                    JSHandle<JSTaggedValue> callbackFnHandle,
457                                                    JSHandle<JSTaggedValue> thisArgHandle, uint32_t len, uint32_t &k)
458 {
459     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
460     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
461     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
462     const int32_t argsLength = 3; // 3: ?kValue, k, O?
463     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
464     if (ElementAccessor::GetElementsLength(thisObjHandle) <= k) {
465         return base::BuiltinsBase::GetTaggedBoolean(false);
466     }
467     while (k < len) {
468         // Elements of thisObjHandle may change.
469         kValue.Update(ElementAccessor::Get(thisObjHandle, k));
470         if (!kValue.GetTaggedValue().IsHole()) {
471             key.Update(JSTaggedValue(k));
472             EcmaRuntimeCallInfo *info =
473                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
474             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
475             info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
476             JSTaggedValue funcResult = JSFunction::Call(info);
477             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
478             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
479                 len = ElementAccessor::GetElementsLength(thisObjHandle);
480             }
481         } else if (JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
482             key.Update(JSTaggedValue(k));
483             JSHandle<JSTaggedValue> kValue1 = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
484             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
485             EcmaRuntimeCallInfo *info =
486                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
487             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
488             info->SetCallArg(kValue1.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
489             JSTaggedValue funcResult = JSFunction::Call(info);
490             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
491         }
492         k++;
493         if (!thisObjVal->IsStableJSArray(thread)) {
494             break;
495         }
496     }
497     return base::BuiltinsBase::GetTaggedBoolean(true);
498 }
499 
500 template <class Predicate>
FindRawData(IndexOfContext & ctx,Predicate && predicate)501 JSTaggedValue JSStableArray::FindRawData(IndexOfContext &ctx, Predicate &&predicate)
502 {
503     DISALLOW_GARBAGE_COLLECTION;
504     JSTaggedType *data = nullptr;
505     JSTaggedValue elementsValue = JSHandle<JSObject>::Cast(ctx.receiver)->GetElements();
506     ElementsKind kind = ElementsKind::GENERIC;
507     if (elementsValue.IsMutantTaggedArray()) {
508         JSHandle<MutantTaggedArray> elements(ctx.thread, elementsValue);
509         data = elements->GetData();
510         kind = JSHandle<JSObject>::Cast(ctx.receiver)->GetClass()->GetElementsKind();
511     } else {
512         JSHandle<TaggedArray> elements(ctx.thread, elementsValue);
513         // Note: GC is guaranteed not to happen since no new object is created during the searching process.
514         data = elements->GetData();
515         // Note: for stable arrays, elements->GetLength() returns the CAPACITY, instead of actual length!
516     }
517     JSTaggedType *first = data + ctx.fromIndex;
518     JSTaggedType *last = data + ctx.length;
519 
520     JSMutableHandle<JSTaggedValue> indexHandle(ctx.thread, JSTaggedValue::Undefined());
521     for (JSTaggedType *cur = first; cur < last; ++cur) {
522         JSTaggedValue convertedCur = JSTaggedValue(*cur);
523         if (elementsValue.IsMutantTaggedArray()) {
524             convertedCur = ElementAccessor::GetTaggedValueWithElementsKind(*cur, kind);
525         }
526         if (LIKELY(convertedCur.GetRawData() != JSTaggedValue::VALUE_HOLE)) {
527             if (UNLIKELY(std::invoke(predicate, convertedCur.GetRawData()))) {
528                 return base::BuiltinsBase::GetTaggedInt64(cur - data);
529             }
530             continue;
531         }
532         // Fallback slow path
533         indexHandle.Update(base::BuiltinsBase::GetTaggedInt64(cur - data));
534         bool found = base::ArrayHelper::ElementIsStrictEqualTo(
535             ctx.thread, ctx.receiver, indexHandle, ctx.searchElement);
536         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(ctx.thread);
537         if (found) {
538             return indexHandle.GetTaggedValue();
539         }
540     }
541     return JSTaggedValue(-1); // Not found
542 }
543 
544 template <class Predicate>
FindLastRawData(IndexOfContext & ctx,Predicate && predicate)545 JSTaggedValue JSStableArray::FindLastRawData(IndexOfContext &ctx, Predicate &&predicate)
546 {
547     DISALLOW_GARBAGE_COLLECTION;
548     JSTaggedType *data = nullptr;
549     JSTaggedValue elementsValue = JSHandle<JSObject>::Cast(ctx.receiver)->GetElements();
550     ElementsKind kind = ElementsKind::GENERIC;
551     if (elementsValue.IsMutantTaggedArray()) {
552         JSHandle<MutantTaggedArray> elements(ctx.thread, JSHandle<JSObject>::Cast(ctx.receiver)->GetElements());
553         data = elements->GetData();
554         kind = JSHandle<JSObject>::Cast(ctx.receiver)->GetClass()->GetElementsKind();
555     } else {
556         JSHandle<TaggedArray> elements(ctx.thread, JSHandle<JSObject>::Cast(ctx.receiver)->GetElements());
557         // Note: GC is guaranteed not to happen since no new object is created during the searching process.
558         data = elements->GetData();
559     }
560     JSTaggedType *beforeFirst = data - 1;
561     JSTaggedType *beforeLast = data + ctx.fromIndex;
562 
563     JSMutableHandle<JSTaggedValue> indexHandle(ctx.thread, JSTaggedValue::Undefined());
564     for (JSTaggedType *cur = beforeLast; cur > beforeFirst; --cur) {
565         JSTaggedValue convertedCur = JSTaggedValue(*cur);
566         if (elementsValue.IsMutantTaggedArray()) {
567             convertedCur = ElementAccessor::GetTaggedValueWithElementsKind(*cur, kind);
568         }
569         if (LIKELY(convertedCur.GetRawData() != JSTaggedValue::VALUE_HOLE)) {
570             if (UNLIKELY(std::invoke(predicate, convertedCur.GetRawData()))) {
571                 return base::BuiltinsBase::GetTaggedInt64(cur - data);
572             }
573             continue;
574         }
575         // Fallback slow path
576         indexHandle.Update(base::BuiltinsBase::GetTaggedInt64(cur - data));
577         bool found = base::ArrayHelper::ElementIsStrictEqualTo(
578             ctx.thread, ctx.receiver, indexHandle, ctx.searchElement);
579         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(ctx.thread);
580         if (found) {
581             return indexHandle.GetTaggedValue();
582         }
583     }
584     return JSTaggedValue(-1); // Not found
585 }
586 
587 template <class Predicate>
FindRawDataDispatch(IndexOfType type,IndexOfContext & ctx,Predicate && predicate)588 JSTaggedValue JSStableArray::FindRawDataDispatch(IndexOfType type, IndexOfContext &ctx, Predicate &&predicate)
589 {
590     switch (type) {
591         case IndexOfType::IndexOf:
592             return FindRawData(ctx, std::forward<Predicate>(predicate));
593         case IndexOfType::LastIndexOf:
594             return FindLastRawData(ctx, std::forward<Predicate>(predicate));
595         default:
596             UNREACHABLE();
597     }
598 }
599 
600 // Zeros need special judge
IndexOfZero(IndexOfType type,IndexOfContext & ctx)601 JSTaggedValue JSStableArray::IndexOfZero(IndexOfType type, IndexOfContext &ctx)
602 {
603     return FindRawDataDispatch(type, ctx, [](JSTaggedType cur) {
604         return JSTaggedValue(cur).IsExactlyZero();
605     });
606 }
607 
IndexOfInt32(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)608 JSTaggedValue JSStableArray::IndexOfInt32(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
609 {
610     ASSERT(searchElement.IsInt());
611     int32_t untagged = searchElement.GetInt();
612     if (untagged == 0) {
613         return IndexOfZero(type, ctx);
614     }
615     JSTaggedType targetInt32 = searchElement.GetRawData();
616     JSTaggedType targetDouble = JSTaggedValue(static_cast<double>(untagged)).GetRawData();
617     return FindRawDataDispatch(type, ctx, [targetInt32, targetDouble](JSTaggedType cur) {
618         return cur == targetInt32 || cur == targetDouble;
619     });
620 }
621 
IndexOfDouble(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)622 JSTaggedValue JSStableArray::IndexOfDouble(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
623 {
624     ASSERT(searchElement.IsDouble());
625     double untagged = searchElement.GetDouble();
626     if (std::isnan(untagged)) {
627         return JSTaggedValue(-1);
628     }
629     if (untagged == 0.0) {
630         return IndexOfZero(type, ctx);
631     }
632     JSTaggedType targetDouble = searchElement.GetRawData();
633     if (searchElement.WithinInt32()) {
634         JSTaggedType targetInt32 = JSTaggedValue(static_cast<int32_t>(untagged)).GetRawData();
635         return FindRawDataDispatch(type, ctx, [targetDouble, targetInt32](JSTaggedType cur) {
636             return cur == targetDouble || cur == targetInt32;
637         });
638     } else {
639         return FindRawDataDispatch(type, ctx, [targetDouble](JSTaggedType cur) {
640             return cur == targetDouble;
641         });
642     }
643 }
644 
IndexOfObjectAddress(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)645 JSTaggedValue JSStableArray::IndexOfObjectAddress(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
646 {
647     ASSERT(searchElement.IsObject());
648     JSTaggedType targetAddress = searchElement.GetRawData();
649     return FindRawDataDispatch(type, ctx, [targetAddress](JSTaggedType cur) {
650         return cur == targetAddress;
651     });
652 }
653 
IndexOfString(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)654 JSTaggedValue JSStableArray::IndexOfString(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
655 {
656     ASSERT(searchElement.IsString());
657     JSTaggedType targetAddress = searchElement.GetRawData();
658     return FindRawDataDispatch(type, ctx, [searchElement, targetAddress](JSTaggedType cur) {
659         if (targetAddress == cur) {
660             return true;
661         }
662         JSTaggedValue curValue(cur);
663         if (!curValue.IsString()) {
664             return false;
665         }
666         return JSTaggedValue::StringCompare(
667             EcmaString::Cast(curValue.GetTaggedObject()),
668             EcmaString::Cast(searchElement.GetTaggedObject()));
669     });
670 }
671 
IndexOfBigInt(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)672 JSTaggedValue JSStableArray::IndexOfBigInt(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
673 {
674     ASSERT(searchElement.IsBigInt());
675     JSTaggedType targetAddress = searchElement.GetRawData();
676     return FindRawDataDispatch(type, ctx, [searchElement, targetAddress](JSTaggedType cur) {
677         if (cur == targetAddress) {
678             return true;
679         }
680         JSTaggedValue curValue(cur);
681         if (!curValue.IsBigInt()) {
682             return false;
683         }
684         return BigInt::Equal(curValue, searchElement);
685     });
686 }
687 
IndexOfDispatch(IndexOfType type,IndexOfContext & ctx,JSTaggedValue searchElement)688 JSTaggedValue JSStableArray::IndexOfDispatch(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement)
689 {
690     if (searchElement.IsInt()) {
691         return IndexOfInt32(type, ctx, searchElement);
692     } else if (searchElement.IsDouble()) {
693         return IndexOfDouble(type, ctx, searchElement);
694     } else if (searchElement.IsString()) {
695         return IndexOfString(type, ctx, searchElement);
696     } else if (searchElement.IsBigInt()) {
697         return IndexOfBigInt(type, ctx, searchElement);
698     } else {
699         return IndexOfObjectAddress(type, ctx, searchElement);
700     }
701 }
702 
IndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)703 JSTaggedValue JSStableArray::IndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
704                                      JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
705 {
706     IndexOfContext ctx;
707     ctx.thread = thread;
708     ctx.receiver = receiver;
709     ctx.searchElement = searchElement;
710     ctx.fromIndex = from;
711     ctx.length = len;
712     return IndexOfDispatch(IndexOfType::IndexOf, ctx, searchElement.GetTaggedValue());
713 }
714 
LastIndexOf(JSThread * thread,JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> searchElement,uint32_t from,uint32_t len)715 JSTaggedValue JSStableArray::LastIndexOf(JSThread *thread, JSHandle<JSTaggedValue> receiver,
716                                          JSHandle<JSTaggedValue> searchElement, uint32_t from, uint32_t len)
717 {
718     IndexOfContext ctx;
719     ctx.thread = thread;
720     ctx.receiver = receiver;
721     ctx.searchElement = searchElement;
722     ctx.fromIndex = from;
723     ctx.length = len;
724     return IndexOfDispatch(IndexOfType::LastIndexOf, ctx, searchElement.GetTaggedValue());
725 }
726 
Filter(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t & toIndex)727 JSTaggedValue JSStableArray::Filter(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
728                                     EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t &toIndex)
729 {
730     JSThread *thread = argv->GetThread();
731     JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
732     JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
733     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
734     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
735     JSMutableHandle<JSTaggedValue> toIndexHandle(thread, JSTaggedValue::Undefined());
736     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
737     const int32_t argsLength = 3; // 3: ?kValue, k, O?
738     uint64_t len = static_cast<uint64_t>(base::ArrayHelper::GetArrayLength(thread, thisObjVal));
739     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
740     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
741     while (k < len) {
742         // Elements of thisObjHandle may change.
743         JSTaggedValue value = ElementAccessor::Get(thisObjHandle, k);
744         if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
745             value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
746             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
747         }
748         kValue.Update(value);
749         if (!kValue.GetTaggedValue().IsHole()) {
750             key.Update(JSTaggedValue(k));
751             EcmaRuntimeCallInfo *info =
752                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
753             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
754             info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
755             JSTaggedValue callResult = JSFunction::Call(info);
756             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
757             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
758                 len = ElementAccessor::GetElementsLength(thisObjHandle);
759             }
760             bool boolResult = callResult.ToBoolean();
761             if (boolResult) {
762                 toIndexHandle.Update(JSTaggedValue(toIndex));
763                 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toIndexHandle, kValue);
764                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
765                 toIndex++;
766             }
767         }
768         k++;
769         if (!thisObjVal->IsStableJSArray(thread)) {
770             break;
771         }
772     }
773     return base::BuiltinsBase::GetTaggedDouble(true);
774 }
775 
Map(JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,EcmaRuntimeCallInfo * argv,uint32_t & k,uint32_t len)776 JSTaggedValue JSStableArray::Map(JSHandle<JSObject> newArrayHandle, JSHandle<JSObject> thisObjHandle,
777                                  EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t len)
778 {
779     JSThread *thread = argv->GetThread();
780     JSHandle<JSTaggedValue> callbackFnHandle = base::BuiltinsBase::GetCallArg(argv, 0);
781     JSHandle<JSTaggedValue> thisArgHandle = base::BuiltinsBase::GetCallArg(argv, 1);
782     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
783     JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
784     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
785     JSMutableHandle<JSTaggedValue> mapResultHandle(thread, JSTaggedValue::Undefined());
786     JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
787     const int32_t argsLength = 3; // 3: ?kValue, k, O?
788     while (k < len) {
789         // Elements of thisObjHandle may change.
790         JSTaggedValue value = ElementAccessor::Get(thisObjHandle, k);
791         if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, k)) {
792             value = JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue();
793             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
794         }
795         kValue.Update(value);
796         if (!kValue.GetTaggedValue().IsHole()) {
797             key.Update(JSTaggedValue(k));
798             EcmaRuntimeCallInfo *info =
799                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
800             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
801             info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
802             JSTaggedValue mapResult = JSFunction::Call(info);
803             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
804             mapResultHandle.Update(mapResult);
805             JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, mapResultHandle);
806             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
807             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
808                 len = ElementAccessor::GetElementsLength(thisObjHandle);
809             }
810         }
811         k++;
812         if (!thisObjVal->IsStableJSArray(thread)) {
813             break;
814         }
815     }
816     return base::BuiltinsBase::GetTaggedDouble(true);
817 }
818 
Reverse(JSThread * thread,JSHandle<JSObject> thisObjHandle,int64_t & lower,uint32_t len)819 JSTaggedValue JSStableArray::Reverse(JSThread *thread, JSHandle<JSObject> thisObjHandle,
820                                      int64_t &lower, uint32_t len)
821 {
822     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
823     if (thisObjHandle->IsJSArray()) {
824         JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObjHandle));
825     }
826     JSMutableHandle<JSTaggedValue> lowerP(thread, JSTaggedValue::Undefined());
827     JSMutableHandle<JSTaggedValue> upperP(thread, JSTaggedValue::Undefined());
828     JSMutableHandle<JSTaggedValue> lowerValueHandle(thread, JSTaggedValue::Undefined());
829     JSMutableHandle<JSTaggedValue> upperValueHandle(thread, JSTaggedValue::Undefined());
830     int64_t middle = std::floor(len / 2);
831     bool needTransition = true;
832     while (lower != middle) {
833         if (ElementAccessor::GetElementsLength(thisObjHandle) != len) {
834             break;
835         }
836         int64_t upper = static_cast<int64_t>(len) - lower - 1;
837         lowerP.Update(JSTaggedValue(lower));
838         upperP.Update(JSTaggedValue(upper));
839         lowerValueHandle.Update(ElementAccessor::Get(thisObjHandle, lower));
840         upperValueHandle.Update(ElementAccessor::Get(thisObjHandle, upper));
841         ElementAccessor::Set(thread, thisObjHandle, lower, upperValueHandle.GetTaggedValue(), needTransition);
842         ElementAccessor::Set(thread, thisObjHandle, upper, lowerValueHandle.GetTaggedValue(), needTransition);
843         lower++;
844     }
845     return base::BuiltinsBase::GetTaggedDouble(true);
846 }
847 
Concat(JSThread * thread,JSHandle<JSObject> newArrayHandle,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & n)848 JSTaggedValue JSStableArray::Concat(JSThread *thread, JSHandle<JSObject> newArrayHandle,
849                                     JSHandle<JSObject> thisObjHandle, int64_t &k, int64_t &n)
850 {
851     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
852     int64_t thisLen = base::ArrayHelper::GetArrayLength(thread, thisObjVal);
853     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
854     JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
855     while (k < thisLen) {
856         if (ElementAccessor::GetElementsLength(thisObjHandle) != thisLen) {
857             break;
858         }
859         toKey.Update(JSTaggedValue(n));
860         JSTaggedValue kValue = ElementAccessor::Get(thisObjHandle, k);
861         if (!kValue.IsHole()) {
862             JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, JSHandle<JSTaggedValue>(thread, kValue));
863             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
864         }
865         n++;
866         k++;
867     }
868     return base::BuiltinsBase::GetTaggedDouble(true);
869 }
870 
FastCopyFromArrayToTypedArray(JSThread * thread,JSHandle<JSTypedArray> & targetArray,DataViewType targetType,uint64_t targetOffset,uint32_t srcLength,JSHandle<JSObject> & obj)871 JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray(JSThread *thread, JSHandle<JSTypedArray> &targetArray,
872                                                            DataViewType targetType, uint64_t targetOffset,
873                                                            uint32_t srcLength, JSHandle<JSObject> &obj)
874 {
875     JSHandle<JSTaggedValue> targetBuffer(thread, targetArray->GetViewedArrayBufferOrByteArray());
876     // If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
877     if (BuiltinsArrayBuffer::IsDetachedBuffer(targetBuffer.GetTaggedValue())) {
878         THROW_TYPE_ERROR_AND_RETURN(thread, "The targetBuffer of This value is detached buffer.",
879                                     JSTaggedValue::Exception());
880     }
881     uint32_t targetLength = targetArray->GetArrayLength();
882     uint32_t targetByteOffset = targetArray->GetByteOffset();
883     uint32_t targetElementSize = TypedArrayHelper::GetSizeFromType(targetType);
884     if (srcLength + targetOffset > targetLength) {
885         THROW_RANGE_ERROR_AND_RETURN(thread, "The sum of length and targetOffset is greater than targetLength.",
886                                      JSTaggedValue::Exception());
887     }
888     uint32_t targetByteIndex = static_cast<uint32_t>(targetOffset * targetElementSize + targetByteOffset);
889     ContentType contentType = targetArray->GetContentType();
890     uint32_t elemLen = ElementAccessor::GetElementsLength(obj);
891     if (contentType == ContentType::BigInt) {
892         JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Hole());
893         JSMutableHandle<JSTaggedValue> elem(thread, JSTaggedValue::Hole());
894         for (uint32_t i = 0; i < srcLength; i++) {
895             if (i < elemLen) {
896                 elem.Update(ElementAccessor::Get(obj, i));
897             } else {
898                 elem.Update(JSTaggedValue::Hole());
899             }
900             kValue.Update(JSTaggedValue::ToBigInt(thread, elem));
901             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
902             BuiltinsArrayBuffer::SetValueInBuffer(thread, targetBuffer.GetTaggedValue(), targetByteIndex,
903                                                   targetType, kValue, true);
904             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
905             targetByteIndex += targetElementSize;
906         }
907     } else {
908         double val = 0.0;
909         uint32_t copyLen = srcLength > elemLen ? elemLen : srcLength;
910         for (uint32_t i = 0; i < copyLen; i++) {
911             JSTaggedValue taggedVal = ElementAccessor::Get(obj, i);
912             if (!taggedVal.IsNumber()) {
913                 JSTaggedNumber taggedNumber = JSTaggedValue::ToNumber(thread, taggedVal);
914                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
915                 val = taggedNumber.GetNumber();
916             } else {
917                 val = taggedVal.GetNumber();
918             }
919             BuiltinsArrayBuffer::FastSetValueInBuffer(thread, targetBuffer.GetTaggedValue(), targetByteIndex,
920                                                       targetType, val, true);
921             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
922             targetByteIndex += targetElementSize;
923         }
924 
925         for (uint32_t i = copyLen; i < srcLength; i++) {
926             val = JSTaggedNumber(base::NAN_VALUE).GetNumber();
927             BuiltinsArrayBuffer::FastSetValueInBuffer(thread, targetBuffer.GetTaggedValue(), targetByteIndex,
928                                                       targetType, val, true);
929             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
930             targetByteIndex += targetElementSize;
931         }
932     }
933     return JSTaggedValue::Undefined();
934 }
935 
At(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv)936 JSTaggedValue JSStableArray::At(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
937 {
938     JSThread *thread = argv->GetThread();
939     uint32_t thisLen = receiver->GetArrayLength();
940     if (thisLen == 0) {
941         return JSTaggedValue::Undefined();
942     }
943     JSTaggedNumber index = JSTaggedValue::ToInteger(thread, base::BuiltinsBase::GetCallArg(argv, 0));
944     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
945     int64_t relativeIndex = index.GetNumber();
946     int64_t k = 0;
947     if (relativeIndex >= 0) {
948         k = relativeIndex;
949     } else {
950         k = static_cast<int64_t>(thisLen) + relativeIndex;
951     }
952     if (k < 0 || k >= thisLen) {
953         return JSTaggedValue::Undefined();
954     }
955 
956     auto result = JSTaggedValue::Hole();
957     result = ElementAccessor::Get(JSHandle<JSObject>::Cast(receiver), k);
958     return result.IsHole() ? JSTaggedValue::Undefined() : result;
959 }
960 
With(JSThread * thread,JSHandle<JSArray> receiver,int64_t insertCount,int64_t index,JSHandle<JSTaggedValue> value)961 JSTaggedValue JSStableArray::With(JSThread *thread, JSHandle<JSArray> receiver,
962                                   int64_t insertCount, int64_t index, JSHandle<JSTaggedValue> value)
963 {
964     JSHandle<JSObject> thisObjHandle(receiver);
965     JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle,
966                                                          JSTaggedNumber(static_cast<uint32_t>(insertCount)));
967     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
968     JSHandle<JSObject> newArrayHandle(thread, newArray);
969 
970     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
971     TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
972 
973     if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
974         destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
975     }
976 
977     bool needTransition = true;
978     for (uint32_t idx = 0; idx < insertCount; idx++) {
979         if (idx == index) {
980             ElementAccessor::Set(thread, newArrayHandle, idx, value.GetTaggedValue(), needTransition);
981         } else {
982             auto kValue = ElementAccessor::Get(thisObjHandle, idx);
983             if (kValue.IsHole()) {
984                 ElementAccessor::Set(thread, newArrayHandle, idx, JSTaggedValue::Undefined(), needTransition);
985             } else {
986                 ElementAccessor::Set(thread, newArrayHandle, idx, kValue, needTransition);
987             }
988         }
989     }
990     JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
991     return newArrayHandle.GetTaggedValue();
992 }
993 
ToSpliced(JSHandle<JSArray> receiver,EcmaRuntimeCallInfo * argv,int64_t argc,int64_t actualStart,int64_t actualSkipCount,int64_t insertCount)994 JSTaggedValue JSStableArray::ToSpliced(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv,
995                                        int64_t argc, int64_t actualStart, int64_t actualSkipCount, int64_t insertCount)
996 {
997     JSThread *thread = argv->GetThread();
998 
999     JSHandle<JSObject> thisObjHandle(receiver);
1000     JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle,
1001                                                          JSTaggedNumber(static_cast<uint32_t>(insertCount)));
1002     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1003     JSHandle<JSObject> newArrayHandle(thread, newArray);
1004 
1005     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1006     TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
1007 
1008     if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
1009         destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1010     }
1011 
1012     int64_t i = 0;
1013     int64_t r = actualStart + actualSkipCount;
1014     bool needTransition = true;
1015     for (int64_t idx = 0; idx < actualStart; idx++, i++) {
1016         auto kValue = ElementAccessor::Get(thisObjHandle, idx);
1017         if (kValue.IsHole()) {
1018             ElementAccessor::Set(thread, newArrayHandle, i, JSTaggedValue::Undefined(), needTransition);
1019         } else {
1020             ElementAccessor::Set(thread, newArrayHandle, i, kValue, needTransition);
1021         }
1022     }
1023 
1024     for (uint32_t pos = 2; pos < argc; ++pos) { // 2:2 means there two arguments before the insert items.
1025         auto element = base::BuiltinsBase::GetCallArg(argv, pos);
1026         ElementAccessor::Set(thread, newArrayHandle, i, element.GetTaggedValue(), needTransition);
1027         ++i;
1028     }
1029 
1030     while (i < insertCount) {
1031         auto kValue = ElementAccessor::Get(thisObjHandle, r);
1032         if (kValue.IsHole()) {
1033             ElementAccessor::Set(thread, newArrayHandle, i, JSTaggedValue::Undefined(), needTransition);
1034         } else {
1035             ElementAccessor::Set(thread, newArrayHandle, i, kValue, needTransition);
1036         }
1037         ++i;
1038         ++r;
1039     }
1040 
1041     JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1042 
1043     return newArrayHandle.GetTaggedValue();
1044 }
1045 
ToReversed(JSThread * thread,JSHandle<JSArray> receiver,int64_t insertCount)1046 JSTaggedValue JSStableArray::ToReversed(JSThread *thread, JSHandle<JSArray> receiver,
1047                                         int64_t insertCount)
1048 {
1049     JSHandle<JSObject> thisObjHandle(receiver);
1050     JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle,
1051                                                          JSTaggedNumber(static_cast<uint32_t>(insertCount)));
1052     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1053     JSHandle<JSObject> newArrayHandle(thread, newArray);
1054 
1055     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1056     TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject());
1057 
1058     if (insertCount > ElementAccessor::GetElementsLength(newArrayHandle)) {
1059         destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount);
1060     }
1061 
1062     bool needTransition = true;
1063     for (uint32_t idx = 0; idx < insertCount; idx++) {
1064         auto kValue = ElementAccessor::Get(thisObjHandle, idx);
1065         if (kValue.IsHole()) {
1066             ElementAccessor::Set(thread, newArrayHandle, insertCount - idx - 1,
1067                                  JSTaggedValue::Undefined(), needTransition);
1068         } else {
1069             ElementAccessor::Set(thread, newArrayHandle, insertCount - idx - 1, kValue, needTransition);
1070         }
1071     }
1072     JSHandle<JSArray>::Cast(newArrayHandle)->SetArrayLength(thread, insertCount);
1073 
1074     return newArrayHandle.GetTaggedValue();
1075 }
1076 
Reduce(JSThread * thread,JSHandle<JSObject> thisObjHandle,JSHandle<JSTaggedValue> callbackFnHandle,JSMutableHandle<JSTaggedValue> accumulator,int64_t & k,int64_t & len)1077 JSTaggedValue JSStableArray::Reduce(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1078                                     JSHandle<JSTaggedValue> callbackFnHandle,
1079                                     JSMutableHandle<JSTaggedValue> accumulator, int64_t &k, int64_t &len)
1080 {
1081     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1082     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1083     JSTaggedValue callResult = JSTaggedValue::Undefined();
1084     while (k < len) {
1085         JSTaggedValue kValue(ElementAccessor::Get(thisObjHandle, k));
1086         if (!kValue.IsHole()) {
1087             JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1088             const uint32_t argsLength = 4; // 4: «accumulator, kValue, k, O»
1089             EcmaRuntimeCallInfo *info =
1090                 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, undefined, undefined, argsLength);
1091             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1092             info->SetCallArg(accumulator.GetTaggedValue(), kValue, JSTaggedValue(k),
1093                              thisObjVal.GetTaggedValue());
1094             callResult = JSFunction::Call(info);
1095             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1096             if (ElementAccessor::GetElementsLength(thisObjHandle) < len) {
1097                 len = ElementAccessor::GetElementsLength(thisObjHandle);
1098             }
1099             accumulator.Update(callResult);
1100         }
1101         k++;
1102         if (!thisObjVal->IsStableJSArray(thread)) {
1103             break;
1104         }
1105     }
1106     return base::BuiltinsBase::GetTaggedDouble(true);
1107 }
1108 
Slice(JSThread * thread,JSHandle<JSObject> thisObjHandle,int64_t & k,int64_t & count)1109 JSTaggedValue JSStableArray::Slice(JSThread *thread, JSHandle<JSObject> thisObjHandle,
1110                                    int64_t &k, int64_t &count)
1111 {
1112     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1113     JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
1114     int64_t len = static_cast<int64_t>(ElementAccessor::GetElementsLength(thisObjHandle));
1115     int64_t oldLen;
1116     if (len > k + count) {
1117         oldLen = count;
1118     } else {
1119         oldLen = len - k;
1120     }
1121     JSHandle<JSObject> arrayObj = factory->NewAndCopyJSArrayObject(thisObjHandle, count, oldLen, k);
1122     for (int i = 0; i < count; i++) {
1123         JSTaggedValue value = ElementAccessor::Get(arrayObj, i);
1124         if (value.IsHole() && JSTaggedValue::HasProperty(thread, thisObjVal, i + k)) {
1125             value = JSArray::FastGetPropertyByValue(thread, thisObjVal, i + k).GetTaggedValue();
1126             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1127             ElementAccessor::Set(thread, arrayObj, i, value, true);
1128         }
1129     }
1130     return arrayObj.GetTaggedValue();
1131 }
1132 
Sort(JSThread * thread,const JSHandle<JSObject> & thisObj,const JSHandle<JSTaggedValue> & callbackFnHandle)1133 JSTaggedValue JSStableArray::Sort(JSThread *thread, const JSHandle<JSObject> &thisObj,
1134                                   const JSHandle<JSTaggedValue> &callbackFnHandle)
1135 {
1136     JSArray::SortElementsByObject(thread, thisObj, callbackFnHandle);
1137     return thisObj.GetTaggedValue();
1138 }
1139 
Fill(JSThread * thread,const JSHandle<JSObject> & thisObj,const JSHandle<JSTaggedValue> & value,int64_t start,int64_t end,int64_t len)1140 JSTaggedValue JSStableArray::Fill(JSThread *thread, const JSHandle<JSObject> &thisObj,
1141                                   const JSHandle<JSTaggedValue> &value, int64_t start,
1142                                   int64_t end, int64_t len)
1143 {
1144     JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(thisObj));
1145     uint32_t length = ElementAccessor::GetElementsLength(thisObj);
1146     ElementsKind oldKind = thisObj->GetClass()->GetElementsKind();
1147     if (JSHClass::TransitToElementsKind(thread, thisObj, value)) {
1148         ElementsKind newKind = thisObj->GetClass()->GetElementsKind();
1149         Elements::MigrateArrayWithKind(thread, thisObj, oldKind, newKind);
1150     }
1151     if (length >= end) {
1152         if (thisObj->GetElements().IsMutantTaggedArray()) {
1153             ElementsKind kind = thisObj->GetClass()->GetElementsKind();
1154             TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements());
1155             JSTaggedValue migratedValue = JSTaggedValue(ElementAccessor::ConvertTaggedValueWithElementsKind(
1156                 value.GetTaggedValue(), kind));
1157             for (int64_t idx = start; idx < end; idx++) {
1158                 elements->Set<false>(thread, idx, migratedValue);
1159             }
1160         } else {
1161             TaggedArray *elements = TaggedArray::Cast(thisObj->GetElements());
1162             for (int64_t idx = start; idx < end; idx++) {
1163                 elements->Set(thread, idx, value);
1164             }
1165         }
1166         return thisObj.GetTaggedValue();
1167     } else {
1168         if (thisObj->GetElements().IsMutantTaggedArray()) {
1169             ElementsKind kind = thisObj->GetClass()->GetElementsKind();
1170             JSHandle<MutantTaggedArray> newElements = thread->GetEcmaVM()->GetFactory()->NewMutantTaggedArray(len);
1171             JSTaggedValue migratedValue = JSTaggedValue(ElementAccessor::ConvertTaggedValueWithElementsKind(
1172                 value.GetTaggedValue(), kind));
1173             for (int64_t idx = start; idx < end; idx++) {
1174                 newElements->Set<false>(thread, idx, migratedValue);
1175             }
1176             thisObj->SetElements(thread, newElements);
1177         } else {
1178             JSHandle<TaggedArray> newElements = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len);
1179             for (int64_t idx = start; idx < end; idx++) {
1180                 newElements->Set(thread, idx, value);
1181             }
1182             thisObj->SetElements(thread, newElements);
1183         }
1184         return thisObj.GetTaggedValue();
1185     }
1186 }
1187 }  // namespace panda::ecmascript
1188