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