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