1/*
2 * Copyright (c) 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/shared_objects/js_shared_array.h"
17
18#include "ecmascript/interpreter/interpreter.h"
19#include "ecmascript/object_fast_operator-inl.h"
20
21namespace panda::ecmascript {
22using base::ArrayHelper;
23
24JSTaggedValue JSSharedArray::LengthGetter([[maybe_unused]] JSThread *thread, const JSHandle<JSObject> &self,
25                                          SCheckMode checkMode)
26{
27    [[maybe_unused]] ConcurrentApiScope<JSSharedArray> scope(thread, JSHandle<JSTaggedValue>::Cast(self),
28                                                             checkMode);
29    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
30    return JSTaggedValue(JSSharedArray::Cast(*self)->GetLength());
31}
32
33bool JSSharedArray::DummyLengthSetter([[maybe_unused]] JSThread *thread,
34                                      [[maybe_unused]] const JSHandle<JSObject> &self,
35                                      [[maybe_unused]] const JSHandle<JSTaggedValue> &value,
36                                      [[maybe_unused]] bool mayThrow)
37{
38    // length is the read only property for Shared Array
39    return true;
40}
41
42bool JSSharedArray::LengthSetter(JSThread *thread, const JSHandle<JSObject> &self, const JSHandle<JSTaggedValue> &value,
43                                 bool mayThrow)
44{
45    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
46    uint32_t newLen = 0;
47    if (!JSTaggedValue::ToArrayLength(thread, value, &newLen) && mayThrow) {
48        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
49    }
50
51    uint32_t oldLen = JSSharedArray::Cast(*self)->GetArrayLength();
52    if (oldLen == newLen) {
53        return true;
54    }
55
56    if (!IsArrayLengthWritable(thread, self)) {
57        if (mayThrow) {
58            THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty), false);
59        }
60        return false;
61    }
62
63    JSSharedArray::SetCapacity(thread, self, oldLen, newLen);
64    uint32_t actualLen = JSSharedArray::Cast(*self)->GetArrayLength();
65    if (actualLen != newLen) { // LCOV_EXCL_START
66        if (mayThrow) {
67            THROW_TYPE_ERROR_AND_RETURN(thread, "Not all array elements is configurable", false);
68        }
69        return false;
70    } // LCOV_EXCL_STOP
71
72    return true;
73}
74
75JSHandle<JSTaggedValue> JSSharedArray::ArrayCreate(JSThread *thread, JSTaggedNumber length, ArrayMode mode)
76{
77    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
78    JSHandle<JSTaggedValue> sharedArrayFunction = env->GetSharedArrayFunction();
79    return JSSharedArray::ArrayCreate(thread, length, sharedArrayFunction, mode);
80}
81
82// 9.4.2.2 ArrayCreate(length, proto)
83JSHandle<JSTaggedValue> JSSharedArray::ArrayCreate(JSThread *thread, JSTaggedNumber length,
84                                                   const JSHandle<JSTaggedValue> &newTarget, ArrayMode mode)
85{
86    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
87    // Assert: length is an integer Number ≥ 0.
88    ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer");
89    // 2. If length is −0, let length be +0.
90    double arrayLength = length.GetNumber();
91    if (arrayLength > MAX_ARRAY_INDEX) {
92        JSHandle<JSTaggedValue> exception(thread, JSTaggedValue::Exception());
93        auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::TYPE_ERROR,
94                                                               "Parameter error.Array length must less than 2^32.");
95        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, exception);
96    }
97    uint32_t normalArrayLength = length.ToUint32();
98
99    // 8. Set the [[Prototype]] internal slot of A to proto.
100    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
101    JSHandle<JSFunction> arrayFunc(env->GetSharedArrayFunction());
102    JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(arrayFunc, newTarget);
103    RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
104    // 9. Set the [[Extensible]] internal slot of A to true.
105
106    // 10. Perform OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]:
107    // true, [[Enumerable]]: false, [[Configurable]]: false}).
108    if (mode == ArrayMode::LITERAL) {
109        JSSharedArray::Cast(*obj)->SetArrayLength(thread, normalArrayLength);
110    } else {
111        JSSharedArray::SetCapacity(thread, obj, 0, normalArrayLength, true);
112    }
113    return JSHandle<JSTaggedValue>(obj);
114}
115
116// 9.4.2.3 ArraySpeciesCreate(originalArray, length)
117JSTaggedValue JSSharedArray::ArraySpeciesCreate(JSThread *thread, const JSHandle<JSObject> &originalArray,
118                                                JSTaggedNumber length)
119{
120    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
121    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
122    // Assert: length is an integer Number ≥ 0.
123    ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer");
124    // If length is −0, let length be +0.
125    int64_t arrayLength = length.GetNumber();
126    if (arrayLength == -0) {
127        arrayLength = +0;
128    }
129    // Let C be undefined.
130    // Let isArray be IsArray(originalArray).
131    JSHandle<JSTaggedValue> originalValue(originalArray);
132    bool isSArray = originalValue->IsJSSharedArray();
133    // ReturnIfAbrupt(isArray).
134    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
135    // If isArray is true, then
136    JSHandle<JSTaggedValue> constructor(thread, JSTaggedValue::Undefined());
137    if (isSArray) {
138        // Let C be Get(originalArray, "constructor").
139        auto *hclass = originalArray->GetJSHClass();
140        JSTaggedValue proto = hclass->GetPrototype();
141        if (hclass->IsJSSharedArray() && !hclass->HasConstructor() && proto.IsJSSharedArray()) {
142            return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue();
143        }
144        JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString();
145        constructor = JSTaggedValue::GetProperty(thread, originalValue, constructorKey).GetValue();
146        // ReturnIfAbrupt(C).
147        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
148        // If IsConstructor(C) is true, then
149        if (constructor->IsConstructor()) {
150            // Let thisRealm be the running execution context’s Realm.
151            // Let realmC be GetFunctionRealm(C).
152            JSHandle<GlobalEnv> realmC = JSObject::GetFunctionRealm(thread, constructor);
153            // ReturnIfAbrupt(realmC).
154            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
155            // If thisRealm and realmC are not the same Realm Record, then
156            if (*realmC != *env) {
157                JSTaggedValue realmArrayConstructor = realmC->GetSharedArrayFunction().GetTaggedValue();
158                // If SameValue(C, realmC.[[intrinsics]].[[%Array%]]) is true, let C be undefined.
159                if (JSTaggedValue::SameValue(constructor.GetTaggedValue(), realmArrayConstructor)) {
160                    return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue();
161                }
162            }
163        }
164
165        // If Type(C) is Object, then
166        if (constructor->IsECMAObject()) {
167            // Let C be Get(C, @@species).
168            JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
169            constructor = JSTaggedValue::GetProperty(thread, constructor, speciesSymbol).GetValue();
170            // ReturnIfAbrupt(C).
171            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
172            // If C is null, let C be undefined.
173            if (constructor->IsNull()) {
174                return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue();
175            }
176        }
177    }
178
179    // If C is undefined, return ArrayCreate(length).
180    if (constructor->IsUndefined()) {
181        return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue();
182    }
183    // If IsConstructor(C) is false, throw a TypeError exception.
184    if (!constructor->IsConstructor()) {
185        THROW_TYPE_ERROR_AND_RETURN(thread, "Not a constructor", JSTaggedValue::Exception());
186    }
187    // Return Construct(C, «length»).
188    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
189    EcmaRuntimeCallInfo *info =
190        EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 1);
191    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
192    info->SetCallArg(JSTaggedValue(arrayLength));
193    JSTaggedValue result = JSFunction::Construct(info);
194    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
195
196    // NOTEIf originalArray was created using the standard built-in Array constructor for
197    // a Realm that is not the Realm of the running execution context, then a new Array is
198    // created using the Realm of the running execution context. This maintains compatibility
199    // with Web browsers that have historically had that behaviour for the Array.prototype methods
200    // that now are defined using ArraySpeciesCreate.
201    return result;
202}
203
204JSHandle<TaggedArray> JSSharedArray::SetCapacity(const JSThread *thread, const JSHandle<TaggedArray> &array,
205                                                 uint32_t capa)
206{
207    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
208    uint32_t oldLength = array->GetLength();
209    JSHandle<TaggedArray> newArray = factory->CopySArray(array, oldLength, capa);
210    return newArray;
211}
212
213void JSSharedArray::SetCapacity(JSThread *thread, const JSHandle<JSObject> &array, uint32_t oldLen, uint32_t newLen,
214                                bool isNew)
215{
216    TaggedArray *element = TaggedArray::Cast(array->GetElements().GetTaggedObject());
217
218    if (element->IsDictionaryMode()) { // LCOV_EXCL_START
219        THROW_TYPE_ERROR(thread, "SendableArray don't support dictionary mode.");
220    } // LCOV_EXCL_STOP
221    uint32_t capacity = element->GetLength();
222    if (newLen <= capacity) {
223        // judge if need to cut down the array size, else fill the unused tail with holes
224        CheckAndCopyArray(thread, JSHandle<JSSharedArray>(array));
225        array->FillElementsWithHoles(thread, newLen, oldLen < capacity ? oldLen : capacity);
226    }
227    if (newLen > capacity) {
228        JSObject::GrowElementsCapacity(thread, array, newLen, isNew);
229    }
230    JSSharedArray::Cast(*array)->SetArrayLength(thread, newLen);
231}
232
233bool JSSharedArray::ArraySetLength(JSThread *thread, const JSHandle<JSObject> &array, const PropertyDescriptor &desc)
234{
235    JSHandle<JSTaggedValue> lengthKeyHandle(thread->GlobalConstants()->GetHandledLengthString());
236
237    // 1. If the [[Value]] field of Desc is absent, then
238    if (!desc.HasValue()) {
239        // 1a. Return OrdinaryDefineOwnProperty(A, "length", Desc).
240        return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, desc);
241    }
242    // 2. Let newLenDesc be a copy of Desc.
243    // (Actual copying is not necessary.)
244    PropertyDescriptor newLenDesc = desc;
245    // 3. - 7. Convert Desc.[[Value]] to newLen.
246    uint32_t newLen = 0;
247    if (!JSTaggedValue::ToArrayLength(thread, desc.GetValue(), &newLen)) {
248        THROW_RANGE_ERROR_AND_RETURN(thread, "array length must equal or less than 2^32.", false);
249    }
250    // 8. Set newLenDesc.[[Value]] to newLen.
251    // (Done below, if needed.)
252    // 9. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
253    PropertyDescriptor oldLenDesc(thread);
254    [[maybe_unused]] bool success = GetOwnProperty(thread, array, lengthKeyHandle, oldLenDesc);
255    // 10. (Assert)
256    ASSERT(success);
257
258    // 11. Let oldLen be oldLenDesc.[[Value]].
259    uint32_t oldLen = 0;
260    JSTaggedValue::ToArrayLength(thread, oldLenDesc.GetValue(), &oldLen);
261    // 12. If newLen >= oldLen, then
262    if (newLen >= oldLen) {
263        // 8. Set newLenDesc.[[Value]] to newLen.
264        // 12a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc).
265        newLenDesc.SetValue(JSHandle<JSTaggedValue>(thread, JSTaggedValue(newLen)));
266        return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, newLenDesc);
267    }
268    // 13. If oldLenDesc.[[Writable]] is false, return false.
269    if (!oldLenDesc.IsWritable() ||
270        // Also handle the {configurable: true} case since we later use
271        // JSSharedArray::SetLength instead of OrdinaryDefineOwnProperty to change
272        // the length, and it doesn't have access to the descriptor anymore.
273        newLenDesc.IsConfigurable() ||
274        (newLenDesc.HasEnumerable() && (newLenDesc.IsEnumerable() != oldLenDesc.IsEnumerable()))) {
275        return false;
276    }
277    // 14. If newLenDesc.[[Writable]] is absent or has the value true,
278    // let newWritable be true.
279    bool newWritable = false;
280    if (!newLenDesc.HasWritable() || newLenDesc.IsWritable()) {
281        newWritable = true;
282    } else {
283    // 15. Else,
284    // 15a. Need to defer setting the [[Writable]] attribute to false in case
285    //      any elements cannot be deleted.
286    // 15b. Let newWritable be false. (It's initialized as "false" anyway.)
287    // 15c. Set newLenDesc.[[Writable]] to true.
288    // (Not needed.)
289    }
290
291    // Most of steps 16 through 19 is implemented by JSSharedArray::SetCapacity.
292    JSSharedArray::SetCapacity(thread, array, oldLen, newLen);
293    // Steps 19d-ii, 20.
294    if (!newWritable) { // LCOV_EXCL_START
295        PropertyDescriptor readonly(thread);
296        readonly.SetWritable(false);
297        success = JSObject::DefineOwnProperty(thread, array, lengthKeyHandle, readonly);
298        ASSERT_PRINT(success, "DefineOwnProperty of length must be success here!");
299    } // LCOV_EXCL_STOP
300
301    // Steps 19d-v, 21. Return false if there were non-deletable elements.
302    uint32_t arrayLength = JSSharedArray::Cast(*array)->GetArrayLength();
303    return arrayLength == newLen;
304}
305
306bool JSSharedArray::PropertyKeyToArrayIndex(JSThread *thread, const JSHandle<JSTaggedValue> &key, uint32_t *output)
307{
308    return JSTaggedValue::ToArrayLength(thread, key, output) && *output <= JSSharedArray::MAX_ARRAY_INDEX;
309}
310
311// 9.4.2.1 [[DefineOwnProperty]] ( P, Desc)
312bool JSSharedArray::DefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &array,
313                                      const JSHandle<JSTaggedValue> &key, const PropertyDescriptor &desc,
314                                      SCheckMode sCheckMode)
315{
316    if (!desc.GetValue()->IsSharedType() || (desc.HasGetter() && !desc.GetGetter()->IsSharedType()) ||
317        (desc.HasSetter() && !desc.GetSetter()->IsSharedType())) {
318        auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::TYPE_ERROR,
319                                                               "Parameter error. Only accept sendable value.");
320        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
321    }
322
323    if (sCheckMode == SCheckMode::CHECK && !(JSSharedArray::Cast(*array)->IsKeyInRange(key))) {
324        auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::RANGE_ERROR,
325                                                               "Key out of length.");
326        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
327    }
328
329    // 1. Assert: IsPropertyKey(P) is true.
330    ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key!");
331    // 2. If P is "length", then
332    if (IsLengthString(thread, key)) {
333        // a. Return ArraySetLength(A, Desc).
334        return ArraySetLength(thread, array, desc);
335    }
336
337    // 3. Else if P is an array index, then
338    // already do in step 4.
339    // 4. Return OrdinaryDefineOwnProperty(A, P, Desc).
340    bool success = JSObject::OrdinaryDefineOwnProperty(thread, array, key, desc);
341    if (success) {
342        JSTaggedValue constructorKey = thread->GlobalConstants()->GetConstructorString();
343        if (key.GetTaggedValue() == constructorKey) {
344            array->GetJSHClass()->SetHasConstructor(true);
345            return true;
346        }
347    }
348    return success;
349}
350
351bool JSSharedArray::IsLengthString(JSThread *thread, const JSHandle<JSTaggedValue> &key)
352{
353    return key.GetTaggedValue() == thread->GlobalConstants()->GetLengthString();
354}
355
356JSHandle<JSSharedArray> JSSharedArray::CreateArrayFromList(JSThread *thread, const JSHandle<TaggedArray> &elements)
357{
358    // Assert: elements is a List whose elements are all ECMAScript language values.
359    // 2. Let array be ArrayCreate(0).
360    uint32_t length = elements->GetLength();
361
362    // 4. For each element e of elements
363    auto env = thread->GetEcmaVM()->GetGlobalEnv();
364    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
365    JSHandle<JSFunction> arrayFunc(env->GetSharedArrayFunction());
366    JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(arrayFunc);
367    JSSharedArray::Cast(*obj)->SetArrayLength(thread, length);
368    obj->SetElements(thread, elements);
369    obj->GetJSHClass()->SetExtensible(false);
370    JSHandle<JSSharedArray> arr(obj);
371
372    return arr;
373}
374
375JSHandle<JSTaggedValue> JSSharedArray::FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
376                                                              uint32_t index)
377{
378    auto result = ObjectFastOperator::FastGetPropertyByIndex(thread, obj.GetTaggedValue(), index);
379    RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
380    return JSHandle<JSTaggedValue>(thread, result);
381}
382
383JSHandle<JSTaggedValue> JSSharedArray::FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
384                                                              const JSHandle<JSTaggedValue> &key, SCheckMode sCheckMode)
385{
386    auto result =
387        ObjectFastOperator::FastGetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(), sCheckMode);
388    RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
389    return JSHandle<JSTaggedValue>(thread, result);
390}
391
392bool JSSharedArray::FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, uint32_t index,
393                                           const JSHandle<JSTaggedValue> &value)
394{
395    return ObjectFastOperator::FastSetPropertyByIndex(thread, obj.GetTaggedValue(), index, value.GetTaggedValue());
396}
397
398bool JSSharedArray::FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
399                                           const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
400{
401    return ObjectFastOperator::FastSetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(),
402                                                      value.GetTaggedValue(), SCheckMode::SKIP);
403}
404
405OperationResult JSSharedArray::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
406                                           const JSHandle<JSTaggedValue> &key, SCheckMode sCheckMode)
407{
408    // Add Concurrent check for shared array
409    [[maybe_unused]] ConcurrentApiScope<JSSharedArray> scope(thread, obj,
410                                                             sCheckMode);
411    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
412                                      OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
413
414    ObjectOperator op(thread, obj, key);
415    // Out of bounds check for shared array
416    if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) {
417        return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(false));
418    }
419    return OperationResult(thread, JSObject::GetProperty(thread, &op), PropertyMetaData(op.IsFound()));
420}
421
422bool JSSharedArray::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
423                                const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value, bool mayThrow,
424                                SCheckMode sCheckMode)
425{
426    // Concurrent check for shared array
427    [[maybe_unused]] ConcurrentApiScope<JSSharedArray, ModType::WRITE> scope(
428        thread, obj, sCheckMode);
429    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
430    // 2 ~ 4 findProperty in Receiver, Obj and its parents
431    ObjectOperator op(thread, obj, key);
432    // Out of bounds check for shared array
433    if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) {
434        auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::RANGE_ERROR,
435                                                               "The value of index is out of range.");
436        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
437    }
438    return JSObject::SetProperty(&op, value, mayThrow);
439}
440
441bool JSSharedArray::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
442                                uint32_t index, const JSHandle<JSTaggedValue> &value, bool mayThrow,
443                                SCheckMode sCheckMode)
444{
445    // Concurrent check for shared array
446    [[maybe_unused]] ConcurrentApiScope<JSSharedArray, ModType::WRITE> scope(
447        thread, obj, sCheckMode);
448    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
449    // 2 ~ 4 findProperty in Receiver, Obj and its parents
450    ObjectOperator op(thread, obj, index);
451    // Out of bounds check for shared array
452    if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) {
453        auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::RANGE_ERROR,
454                                                               "The value of index is out of range.");
455        THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
456    }
457    return JSObject::SetProperty(&op, value, mayThrow);
458}
459
460// ecma2024 23.1.3.20 Array.prototype.sort(comparefn)
461JSTaggedValue JSSharedArray::Sort(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
462                                  const JSHandle<JSTaggedValue> &fn)
463{
464    ASSERT(fn->IsUndefined() || fn->IsCallable());
465    // 3. Let len be ?LengthOfArrayLike(obj).
466    int64_t len = ArrayHelper::GetArrayLength(thread, obj);
467    // ReturnIfAbrupt(len).
468    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
469    // If len is 0 or 1, no need to sort
470    if (len == 0 || len == 1) {
471        return obj.GetTaggedValue();
472    }
473
474    // 4. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs
475    // the following steps when called:
476    //    a. Return ? CompareArrayElements(x, y, comparefn).
477    // 5. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, SKIP-HOLES).
478    JSHandle<TaggedArray> sortedList =
479        ArrayHelper::SortIndexedProperties(thread, obj, len, fn, base::HolesType::SKIP_HOLES);
480    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
481    // 6. Let itemCount be the number of elements in sortedList.
482    uint32_t itemCount = sortedList->GetLength();
483
484    // 7. Let j be 0.
485    uint32_t j = 0;
486    // 8. Repeat, while j < itemCount,
487    //     a. Perform ! Set(obj, ! ToString((j)), sortedList[j], true).
488    //     b. Set j to j + 1.
489    JSMutableHandle<JSTaggedValue> item(thread, JSTaggedValue::Undefined());
490    while (j < itemCount) {
491        item.Update(sortedList->Get(j));
492        JSSharedArray::FastSetPropertyByValue(thread, obj, j, item);
493        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
494        ++j;
495    }
496    // 9. NOTE: The call to SortIndexedProperties in step 5 uses SKIP-HOLES.The remaining indices are deleted to
497    // preserve the number of holes that were detected and excluded from the sort.
498    // 10. Repeat, while j < len,
499    //       a. Perform ? DeletePropertyOrThrow(obj, ! ToString((j))).
500    //       b. Set j to j + 1.
501    while (j < len) {
502        item.Update(JSTaggedValue(j));
503        JSTaggedValue::DeletePropertyOrThrow(thread, obj, item);
504        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
505        ++j;
506    }
507
508    return obj.GetTaggedValue();
509}
510
511bool JSSharedArray::IncludeInSortedValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
512                                         const JSHandle<JSTaggedValue> &value)
513{
514    ASSERT(obj->IsJSSharedArray());
515    JSHandle<JSSharedArray> arrayObj = JSHandle<JSSharedArray>::Cast(obj);
516    int32_t length = static_cast<int32_t>(arrayObj->GetArrayLength());
517    if (length == 0) {
518        return false;
519    }
520    int32_t left = 0;
521    int32_t right = length - 1;
522    while (left <= right) {
523        int32_t middle = (left + right) / 2;
524        JSHandle<JSTaggedValue> vv = JSSharedArray::FastGetPropertyByValue(thread, obj, middle);
525        ComparisonResult res = JSTaggedValue::Compare(thread, vv, value);
526        if (res == ComparisonResult::EQUAL) {
527            return true;
528        } else if (res == ComparisonResult::LESS) {
529            left = middle + 1;
530        } else {
531            right = middle - 1;
532        }
533    }
534    return false;
535}
536
537void JSSharedArray::CheckAndCopyArray(const JSThread *thread, JSHandle<JSSharedArray> obj)
538{
539    JSHandle<TaggedArray> arr(thread, obj->GetElements());
540    // Check whether array is shared in the nonmovable space before set properties and elements.
541    // If true, then really copy array in the semi space.
542    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
543    if (arr.GetTaggedValue().IsCOWArray()) {
544        auto newArray = factory->CopyArray(arr, arr->GetLength(), arr->GetLength(),
545            JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE);
546        obj->SetElements(thread, newArray.GetTaggedValue());
547    }
548    JSHandle<TaggedArray> prop(thread, obj->GetProperties());
549    if (prop.GetTaggedValue().IsCOWArray()) {
550        auto newProps = factory->CopyArray(prop, prop->GetLength(), prop->GetLength(),
551            JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE);
552        obj->SetProperties(thread, newProps.GetTaggedValue());
553    }
554}
555
556void JSSharedArray::DeleteInElementMode(const JSThread *thread, JSHandle<JSSharedArray> &obj)
557{
558    JSHandle<TaggedArray> elements(thread, obj->GetElements());
559    ASSERT(!obj->GetJSHClass()->IsDictionaryElement());
560    uint32_t length = elements->GetLength();
561    // fixme(hzzhouzebin) Optimize Delete later.
562    uint32_t size = 0;
563    for (uint32_t i = 0; i < length; i++) {
564        JSTaggedValue value = ElementAccessor::Get(JSHandle<JSObject>(obj), i);
565        if (value.IsHole()) {
566            continue;
567        }
568        ++size;
569    }
570    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
571    JSHandle<TaggedArray> newElements(
572        factory->NewTaggedArray(length, JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE));
573    uint32_t newCurr = 0;
574    for (uint32_t i = 0; i < length; i++) {
575        JSTaggedValue value = ElementAccessor::Get(JSHandle<JSObject>(obj), i);
576        if (value.IsHole()) {
577            continue;
578        }
579        newElements->Set(thread, newCurr, value);
580        ++newCurr;
581    }
582    obj->SetElements(thread, newElements);
583}
584}  // namespace panda::ecmascript
585