• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/js_array.h"
17 
18 #include "ecmascript/interpreter/interpreter.h"
19 #include "ecmascript/object_fast_operator-inl.h"
20 
21 namespace panda::ecmascript {
22 using base::ArrayHelper;
23 
LengthGetter(JSThread * thread,const JSHandle<JSObject> & self)24 JSTaggedValue JSArray::LengthGetter([[maybe_unused]] JSThread *thread, const JSHandle<JSObject> &self)
25 {
26     return JSTaggedValue(JSArray::Cast(*self)->GetLength());
27 }
28 
LengthSetter(JSThread * thread,const JSHandle<JSObject> & self,const JSHandle<JSTaggedValue> & value,bool mayThrow)29 bool JSArray::LengthSetter(JSThread *thread, const JSHandle<JSObject> &self, const JSHandle<JSTaggedValue> &value,
30                            bool mayThrow)
31 {
32     uint32_t newLen = 0;
33     if (!JSTaggedValue::ToArrayLength(thread, value, &newLen)) {
34         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
35     }
36 
37     uint32_t oldLen = JSArray::Cast(*self)->GetArrayLength();
38     if (oldLen == newLen) {
39         return true;
40     }
41 
42     if (!IsArrayLengthWritable(thread, self)) {
43         if (mayThrow) {
44             THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty), false);
45         }
46         return false;
47     }
48 
49     JSArray::SetCapacity(thread, self, oldLen, newLen);
50     uint32_t actualLen = JSArray::Cast(*self)->GetArrayLength();
51     if (actualLen != newLen) {
52         if (mayThrow) {
53             THROW_TYPE_ERROR_AND_RETURN(thread, "Not all array elements is configurable", false);
54         }
55         return false;
56     }
57 
58     return true;
59 }
60 
ArrayCreate(JSThread * thread,JSTaggedNumber length,ArrayMode mode)61 JSHandle<JSTaggedValue> JSArray::ArrayCreate(JSThread *thread, JSTaggedNumber length, ArrayMode mode)
62 {
63     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
64     JSHandle<JSTaggedValue> arrayFunction = env->GetArrayFunction();
65     return JSArray::ArrayCreate(thread, length, arrayFunction, mode);
66 }
67 
68 // Used to check whether newArrayHandle's proto == Array.prototype.
CheckAndSetPrototypeModified(JSThread * thread,const JSHandle<JSObject> & newArrayHandle)69 void JSArray::CheckAndSetPrototypeModified(JSThread* thread, const JSHandle<JSObject> &newArrayHandle)
70 {
71     if (!JSArray::IsProtoNotChangeJSArray(thread, newArrayHandle)) {
72         newArrayHandle->GetJSHClass()->SetIsJSArrayPrototypeModified(true);
73     }
74 };
75 
76 // 9.4.2.2 ArrayCreate(length, proto)
ArrayCreate(JSThread * thread,JSTaggedNumber length,const JSHandle<JSTaggedValue> & newTarget,ArrayMode mode)77 JSHandle<JSTaggedValue> JSArray::ArrayCreate(JSThread *thread, JSTaggedNumber length,
78                                              const JSHandle<JSTaggedValue> &newTarget, ArrayMode mode)
79 {
80     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
81     // Assert: length is an integer Number ≥ 0.
82     ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer");
83     // 2. If length is −0, let length  be +0.
84     double arrayLength = length.GetNumber();
85     if (arrayLength > MAX_ARRAY_INDEX) {
86         JSHandle<JSTaggedValue> exception(thread, JSTaggedValue::Exception());
87         THROW_RANGE_ERROR_AND_RETURN(thread, "array length must equal or less than 2^32.", exception);
88     }
89     uint32_t normalArrayLength = length.ToUint32();
90 
91     // 8. Set the [[Prototype]] internal slot of A to proto.
92     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
93     JSHandle<JSFunction> arrayFunc(env->GetArrayFunction());
94     JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(arrayFunc, newTarget);
95     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
96     // 9. Set the [[Extensible]] internal slot of A to true.
97     obj->GetJSHClass()->SetExtensible(true);
98 
99     // 10. Perform OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]:
100     // true, [[Enumerable]]: false, [[Configurable]]: false}).
101     if (mode == ArrayMode::LITERAL) {
102         JSArray::Cast(*obj)->SetArrayLength(thread, normalArrayLength);
103     } else {
104         JSArray::SetCapacity(thread, obj, 0, normalArrayLength, true);
105     }
106 
107     // For new Array(Len), the elementsKind should be Hole
108     if (thread->GetEcmaVM()->IsEnableElementsKind()) {
109         if ((newTarget.GetTaggedValue() == arrayFunc.GetTaggedValue()) && normalArrayLength != 0) {
110             JSHandle<JSArray> newArray(obj);
111             #if ECMASCRIPT_ENABLE_ELEMENTSKIND_ALWAY_GENERIC
112             JSHClass::TransitToElementsKind(thread, newArray, ElementsKind::GENERIC);
113             #else
114             JSHClass::TransitToElementsKind(thread, newArray, ElementsKind::HOLE);
115             #endif
116         }
117     }
118     CheckAndSetPrototypeModified(thread, obj);
119     return JSHandle<JSTaggedValue>(obj);
120 }
121 
GetConstructorOrSpeciesInlinedProp(JSTaggedValue object,uint32_t inlinePropIndex)122 JSTaggedValue JSArray::GetConstructorOrSpeciesInlinedProp(JSTaggedValue object, uint32_t inlinePropIndex)
123 {
124     JSHClass *hclass = JSObject::Cast(object)->GetJSHClass();
125     ASSERT(!hclass->IsDictionaryMode() && "object can't be dictionary");
126     LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
127     PropertyAttributes attr(layoutInfo->GetAttr(inlinePropIndex));
128     ASSERT(attr.GetOffset() == inlinePropIndex && "offset of Attr must be inlinePropIndex");
129     ASSERT(attr.IsInlinedProps() && "attr must be inline prop");
130     JSTaggedValue value = JSObject::Cast(object)->GetPropertyInlinedPropsWithRep(hclass, attr.GetOffset(), attr);
131     ASSERT(!value.IsHole() && "object must have inlinePropIndex");
132     return value;
133 }
134 
135 // 9.4.2.3 ArraySpeciesCreate(originalArray, length)
ArraySpeciesCreate(JSThread * thread,const JSHandle<JSObject> & originalArray,JSTaggedNumber length)136 JSTaggedValue JSArray::ArraySpeciesCreate(JSThread *thread, const JSHandle<JSObject> &originalArray,
137                                           JSTaggedNumber length)
138 {
139     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
140     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
141     // Assert: length is an integer Number ≥ 0.
142     ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer");
143     // If length is −0, let length be +0.
144     int64_t arrayLength = length.GetNumber();
145     if (arrayLength == -0) {
146         arrayLength = +0;
147     }
148     // 1. Let isArray be ? IsArray(originalArray).
149     JSHandle<JSTaggedValue> originalValue(originalArray);
150     bool isArray = originalValue->IsArray(thread);
151     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
152 
153     // 2. If isArray is false, return ? ArrayCreate(length).
154     if (!isArray) {
155         return ArrayCreate(thread, length).GetTaggedValue();
156     }
157 
158     JSMutableHandle<JSTaggedValue> constructor(thread, globalConst->GetHandledHole());
159     // 3. Let C be ? Get(originalArray, "constructor").
160     // 3. fastpath: if object has no custom constructor, the constructor may be on the proto.
161     if (originalArray->IsJSArray() && !originalArray->GetJSHClass()->HasConstructor()) {
162         JSTaggedValue proto = JSObject::GetPrototype(originalArray);
163         // fastpath: if the hclass of proto is the default Array Prototype hclass,
164         // the constructor must in the inline properties.
165         if LIKELY(proto.IsECMAObject()
166             && JSObject::Cast(proto)->GetJSHClass() == thread->GetBuiltinPrototypeHClass(BuiltinTypeId::ARRAY)) {
167             constructor.Update(GetConstructorOrSpeciesInlinedProp(proto, CONSTRUCTOR_INLINE_PROPERTY_INDEX));
168         }
169     }
170 
171     if (constructor->IsHole()) {
172         // 3. slowpath: Let C be ? Get(originalArray, "constructor").
173         JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString();
174         JSTaggedValue c = ObjectFastOperator::FastGetPropertyByValue(thread, originalValue.GetTaggedValue(),
175                                                                      constructorKey.GetTaggedValue());
176         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
177         constructor.Update(c);
178     }
179 
180     // fastpath: if constructor is the default constructor. don't need check the realm.
181     // check the species of it.
182     if (constructor == env->GetArrayFunction() && constructor->IsECMAObject()) {
183         JSTaggedValue taggedCtor = constructor.GetTaggedValue();
184         JSHClass *chc = JSObject::Cast(taggedCtor)->GetJSHClass();
185         // if the hclass of constructor is the default Array Function hclass,
186         // the species must in the inline properties.
187         if LIKELY(chc == thread->GetBuiltinHClass(BuiltinTypeId::ARRAY)) {
188             JSTaggedValue species = GetConstructorOrSpeciesInlinedProp(taggedCtor, ARRAY_FUNCTION_SPECIES_INDEX);
189             if (species == globalConst->GetArraySpeciesAccessor()) {
190                 // fast path: means using default constructor, do ArrayCreate directly.
191                 return ArrayCreate(thread, length).GetTaggedValue();
192             }
193         }
194     }
195 
196     // 4. If IsConstructor(C) is true, then
197     if (constructor->IsConstructor()) {
198         // a. Let thisRealm be the current Realm Record.
199         // b. Let realmC be ? GetFunctionRealm(C).
200         JSHandle<GlobalEnv> realmC = JSObject::GetFunctionRealm(thread, constructor);
201         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
202         // c. If thisRealm and realmC are not the same Realm Record, then
203         if (*realmC != *env) {
204             JSTaggedValue realmArrayConstructor = realmC->GetArrayFunction().GetTaggedValue();
205             // i. If SameValue(C, realmC.[[Intrinsics]].[[%Array%]]) is true, set C to undefined.
206             if (JSTaggedValue::SameValue(constructor.GetTaggedValue(), realmArrayConstructor)) {
207                 constructor.Update(globalConst->GetUndefined());
208             }
209         }
210     }
211 
212     // 5. slowpath: If Type(C) is Object, then
213     if (constructor->IsECMAObject()) {
214         // a. Set C to ? Get(C, @@species).
215         JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
216         JSTaggedValue speciesConstructor = ObjectFastOperator::FastGetPropertyByValue(
217             thread, constructor.GetTaggedValue(), speciesSymbol.GetTaggedValue());
218         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
219         // b. If C is null, set C to undefined.
220         if (speciesConstructor.IsNull()) {
221             // fast path: use ArrayCreate instead pf set C to undefined.
222             return ArrayCreate(thread, length).GetTaggedValue();
223         }
224         constructor.Update(speciesConstructor);
225     }
226 
227     // 6. If C is undefined, return ? ArrayCreate(length).
228     if (constructor->IsUndefined()) {
229         return JSArray::ArrayCreate(thread, length).GetTaggedValue();
230     }
231     // 7. If IsConstructor(C) is false, throw a TypeError exception.
232     if (!constructor->IsConstructor()) {
233         THROW_TYPE_ERROR_AND_RETURN(thread, "Not a constructor", JSTaggedValue::Exception());
234     }
235     // 8. Return ? Construct(C, « ��(length)»).
236     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
237     EcmaRuntimeCallInfo *info =
238         EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 1);
239     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
240     info->SetCallArg(JSTaggedValue(arrayLength));
241     JSTaggedValue result = JSFunction::Construct(info);
242     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
243 
244     // The abstract operation ArraySpeciesCreate takes arguments originalArray and length (a non-negative integer).
245     // It is used to specify the creation of a new Array object using a constructor function that is derived from
246     // originalArray. It performs the following steps when called:
247     // 1. Let isArray be ? IsArray(originalArray).
248     // 2. If isArray is false, return ? ArrayCreate(length).
249     // 3. Let C be ? Get(originalArray, "constructor").
250     // 4. If IsConstructor(C) is true, then
251     //      a. Let thisRealm be the current Realm Record.
252     //      b. Let realmC be ? GetFunctionRealm(C).
253     //      c. If thisRealm and realmC are not the same Realm Record, then
254     //          i. If SameValue(C, realmC.[[Intrinsics]].[[%Array%]]) is true, set C to undefined.
255     // 5. If Type(C) is Object, then
256     //      a. Set C to ? Get(C, @@species).
257     //      b. If C is null, set C to undefined.
258     // 6. If C is undefined, return ? ArrayCreate(length).
259     // 7. If IsConstructor(C) is false, throw a TypeError exception.
260     // 8. Return ? Construct(C, « ��(length) »).
261 
262     // NOTEIf originalArray was created using the standard built-in Array constructor for
263     // a Realm that is not the Realm of the running execution context, then a new Array is
264     // created using the Realm of the running execution context. This maintains compatibility
265     // with Web browsers that have historically had that behaviour for the Array.prototype methods
266     // that now are defined using ArraySpeciesCreate.
267     return result;
268 }
269 
SetCapacity(JSThread * thread,const JSHandle<JSObject> & array,uint32_t oldLen,uint32_t newLen,bool isNew)270 void JSArray::SetCapacity(JSThread *thread, const JSHandle<JSObject> &array,
271                           uint32_t oldLen, uint32_t newLen, bool isNew)
272 {
273     TaggedArray *element = TaggedArray::Cast(array->GetElements().GetTaggedObject());
274 
275     if (element->IsDictionaryMode()) {
276         ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
277         uint32_t numOfElements = array->GetNumberOfElements(thread);
278         uint32_t newNumOfElements = newLen;
279 
280         if (newLen < oldLen && numOfElements != 0U) {
281             JSHandle<NumberDictionary> dictHandle(thread, element);
282             JSHandle<TaggedArray> newArr = factory->NewTaggedArray(numOfElements);
283             GetAllElementKeys(thread, array, 0, newArr);
284             for (uint32_t i = numOfElements - 1; i >= newLen; i--) {
285                 JSTaggedValue value = newArr->Get(i);
286                 uint32_t output = 0;
287                 JSTaggedValue::StringToElementIndex(value, &output);
288                 JSTaggedValue key(static_cast<int>(output));
289                 int entry = dictHandle->FindEntry(key);
290                 auto attr = dictHandle->GetAttributes(entry).GetValue();
291                 PropertyAttributes propAttr(attr);
292                 if (propAttr.IsConfigurable()) {
293                     JSHandle<NumberDictionary> newDict = NumberDictionary::Remove(thread, dictHandle, entry);
294                     array->SetElements(thread, newDict);
295                     if (i == 0) {
296                         newNumOfElements = i;
297                         break;
298                     }
299                 } else {
300                     newNumOfElements = i + 1;
301                     break;
302                 }
303             }
304         }
305         JSArray::Cast(*array)->SetArrayLength(thread, newNumOfElements);
306         return;
307     }
308 
309     uint32_t capacity = element->GetLength();
310     if (newLen <= capacity) {
311         // judge if need to cut down the array size, else fill the unused tail with holes
312         CheckAndCopyArray(thread, JSHandle<JSArray>(array));
313         JSObject::FillElementsWithHoles(thread, array, newLen, oldLen < capacity ? oldLen : capacity);
314     }
315     if (JSObject::ShouldTransToDict(oldLen, newLen)) {
316         JSObject::ElementsToDictionary(thread, array);
317     } else if (newLen > capacity) {
318         JSObject::GrowElementsCapacity(thread, array, newLen, isNew);
319     }
320     JSArray::Cast(*array)->SetArrayLength(thread, newLen);
321     JSArray::TransformElementsKindAfterSetCapacity(thread, array, oldLen, newLen, isNew);
322 }
323 
TransformElementsKindAfterSetCapacity(JSThread * thread,const JSHandle<JSObject> & array,uint32_t oldLen,uint32_t newLen,bool isNew)324 void JSArray::TransformElementsKindAfterSetCapacity(JSThread *thread, const JSHandle<JSObject> &array,
325                                                     [[maybe_unused]] uint32_t oldLen, uint32_t newLen,
326                                                     [[maybe_unused]] bool isNew)
327 {
328     // Update ElementsKind after reset array length.
329     // Add this switch because we do not support ElementsKind for instance from new Array
330     if (!array->IsElementDict()) {
331         ElementsKind oldKind = array->GetClass()->GetElementsKind();
332         if (Elements::IsGeneric(oldKind)) {
333             return;
334         }
335 #if ECMASCRIPT_ENABLE_ELEMENTSKIND_ALWAY_GENERIC
336         ElementsKind newKind = ElementsKind::GENERIC;
337 #else
338         // 1.When elementsKind is NONE, means thisArray is empty,
339         // so we don't need to traverse the elements to transform elementskind.
340         // 2.Make sure array is already created.
341         // 3.Make sure newLen > 0 for avoid making empty array elementsKind to HOLE accidently.
342         // ASSERT: If an array's elementsKind is NONE, its length must be zero.
343         if (Elements::IsNone(oldKind) && !isNew && newLen > 0) {
344             ASSERT(oldLen == 0);
345             JSHClass::TransitToElementsKindUncheck(thread, array, ElementsKind::HOLE);
346             return;
347         }
348         ElementsKind newKind = ElementsKind::NONE;
349 #endif
350         for (uint32_t i = 0; i < newLen; ++i) {
351             JSTaggedValue val = ElementAccessor::Get(thread, array, i);
352             newKind = Elements::ToElementsKind(val, newKind);
353         }
354         // elements length might not be zero when newLen is zero
355         uint32_t oldElementsLength = ElementAccessor::GetElementsLength(array);
356         if (newKind == ElementsKind::NONE && oldElementsLength != 0) {
357             JSHandle<TaggedArray> newTaggedArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(oldElementsLength);
358             array->SetElements(thread, newTaggedArray);
359             if (!JSHClass::TransitToElementsKindUncheck(thread, array, newKind)) {
360                 ASSERT(array->GetClass()->GetElementsKind() == ElementsKind::GENERIC);
361             }
362         } else if (newKind != oldKind) {
363             if (JSHClass::TransitToElementsKindUncheck(thread, array, newKind)) {
364                 Elements::MigrateArrayWithKind(thread, array, oldKind, newKind);
365             } else {
366                 // For the case that array has property transition,
367                 // Its elementsKind should be GENERIC for now.
368                 ASSERT(array->GetClass()->GetElementsKind() == ElementsKind::GENERIC);
369             }
370         }
371     }
372 }
373 
ArraySetLength(JSThread * thread,const JSHandle<JSObject> & array,const PropertyDescriptor & desc)374 bool JSArray::ArraySetLength(JSThread *thread, const JSHandle<JSObject> &array, const PropertyDescriptor &desc)
375 {
376     JSHandle<JSTaggedValue> lengthKeyHandle(thread->GlobalConstants()->GetHandledLengthString());
377 
378     // 1. If the [[Value]] field of Desc is absent, then
379     if (!desc.HasValue()) {
380         // 1a. Return OrdinaryDefineOwnProperty(A, "length", Desc).
381         return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, desc);
382     }
383     // 2. Let newLenDesc be a copy of Desc.
384     // (Actual copying is not necessary.)
385     PropertyDescriptor newLenDesc = desc;
386     // 3. - 7. Convert Desc.[[Value]] to newLen.
387     uint32_t newLen = 0;
388     if (!JSTaggedValue::ToArrayLength(thread, desc.GetValue(), &newLen)) {
389         THROW_RANGE_ERROR_AND_RETURN(thread, "array length must equal or less than 2^32.", false);
390     }
391     // 8. Set newLenDesc.[[Value]] to newLen.
392     // (Done below, if needed.)
393     // 9. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
394     PropertyDescriptor oldLenDesc(thread);
395     [[maybe_unused]] bool success = GetOwnProperty(thread, array, lengthKeyHandle, oldLenDesc);
396     // 10. (Assert)
397     ASSERT(success);
398 
399     // 11. Let oldLen be oldLenDesc.[[Value]].
400     uint32_t oldLen = 0;
401     JSTaggedValue::ToArrayLength(thread, oldLenDesc.GetValue(), &oldLen);
402     // 12. If newLen >= oldLen, then
403     if (newLen >= oldLen) {
404         // 8. Set newLenDesc.[[Value]] to newLen.
405         // 12a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc).
406         newLenDesc.SetValue(JSHandle<JSTaggedValue>(thread, JSTaggedValue(newLen)));
407         return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, newLenDesc);
408     }
409     // 13. If oldLenDesc.[[Writable]] is false, return false.
410     if (!oldLenDesc.IsWritable() ||
411         // Also handle the {configurable: true} case since we later use
412         // JSArray::SetLength instead of OrdinaryDefineOwnProperty to change
413         // the length, and it doesn't have access to the descriptor anymore.
414         newLenDesc.IsConfigurable() ||
415         (newLenDesc.HasEnumerable() && (newLenDesc.IsEnumerable() != oldLenDesc.IsEnumerable()))) {
416         return false;
417     }
418     // 14. If newLenDesc.[[Writable]] is absent or has the value true,
419     // let newWritable be true.
420     bool newWritable = false;
421     if (!newLenDesc.HasWritable() || newLenDesc.IsWritable()) {
422         newWritable = true;
423     } else {
424     // 15. Else,
425     // 15a. Need to defer setting the [[Writable]] attribute to false in case
426     //      any elements cannot be deleted.
427     // 15b. Let newWritable be false. (It's initialized as "false" anyway.)
428     // 15c. Set newLenDesc.[[Writable]] to true.
429     // (Not needed.)
430     }
431 
432     // Most of steps 16 through 19 is implemented by JSArray::SetCapacity.
433     JSArray::SetCapacity(thread, array, oldLen, newLen);
434     // Steps 19d-ii, 20.
435     if (!newWritable) {
436         PropertyDescriptor readonly(thread);
437         readonly.SetWritable(false);
438         success = JSObject::DefineOwnProperty(thread, array, lengthKeyHandle, readonly);
439         ASSERT_PRINT(success, "DefineOwnProperty of length must be success here!");
440     }
441 
442     // Steps 19d-v, 21. Return false if there were non-deletable elements.
443     uint32_t arrayLength = JSArray::Cast(*array)->GetArrayLength();
444     return arrayLength == newLen;
445 }
446 
PropertyKeyToArrayIndex(JSThread * thread,const JSHandle<JSTaggedValue> & key,uint32_t * output)447 bool JSArray::PropertyKeyToArrayIndex(JSThread *thread, const JSHandle<JSTaggedValue> &key, uint32_t *output)
448 {
449     return JSTaggedValue::ToArrayLength(thread, key, output) && *output <= JSArray::MAX_ARRAY_INDEX;
450 }
451 
452 // 9.4.2.1 [[DefineOwnProperty]] ( P, Desc)
DefineOwnProperty(JSThread * thread,const JSHandle<JSObject> & array,const JSHandle<JSTaggedValue> & key,const PropertyDescriptor & desc)453 bool JSArray::DefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &array, const JSHandle<JSTaggedValue> &key,
454                                 const PropertyDescriptor &desc)
455 {
456     // 1. Assert: IsPropertyKey(P) is true.
457     ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key!");
458     // 2. If P is "length", then
459     if (IsLengthString(thread, key)) {
460         // a. Return ArraySetLength(A, Desc).
461         return ArraySetLength(thread, array, desc);
462     }
463     // 3. Else if P is an array index, then
464     // already do in step 4.
465     // 4. Return OrdinaryDefineOwnProperty(A, P, Desc).
466     return JSObject::OrdinaryDefineOwnProperty(thread, array, key, desc);
467 }
468 
DefineOwnProperty(JSThread * thread,const JSHandle<JSObject> & array,uint32_t index,const PropertyDescriptor & desc)469 bool JSArray::DefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &array, uint32_t index,
470                                 const PropertyDescriptor &desc)
471 {
472     return JSObject::OrdinaryDefineOwnProperty(thread, array, index, desc);
473 }
474 
IsLengthString(JSThread * thread,const JSHandle<JSTaggedValue> & key)475 bool JSArray::IsLengthString(JSThread *thread, const JSHandle<JSTaggedValue> &key)
476 {
477     return key.GetTaggedValue() == thread->GlobalConstants()->GetLengthString();
478 }
479 
480 // static
481 // Check whether the element of the array is dictionary element,
482 // proto of the array has not been modified
483 // the element of the array prototype has not been modified
484 // the attribute of the array has not been modified
IsProtoNotModifiedDictionaryJSArray(JSThread * thread,const JSHandle<JSObject> & obj)485 bool JSArray::IsProtoNotModifiedDictionaryJSArray(JSThread *thread, const JSHandle<JSObject> &obj)
486 {
487     return obj->GetJSHClass()->IsDictionaryElement() &&
488            !thread->IsArrayPrototypeChangedGuardiansInvalid() &&
489            !obj->GetClass()->IsJSArrayPrototypeModifiedFromBitField() &&
490            JSObject::AttributesUnchanged(thread, obj);
491 }
492 
493 #if ENABLE_NEXT_OPTIMIZATION
494 // ecma6 7.3 Operations on Objects
CreateArrayFromList(JSThread * thread,const JSHandle<TaggedArray> & elements)495 JSHandle<JSArray> JSArray::CreateArrayFromList(JSThread *thread, const JSHandle<TaggedArray> &elements)
496 {
497     // Assert: elements is a List whose elements are all ECMAScript language values.
498     uint32_t length = elements->GetLength();
499     auto env = thread->GetEcmaVM()->GetGlobalEnv();
500 
501     // New JSObject by Constructor.
502     JSTaggedValue protoOrHClass = JSHandle<JSFunction>::Cast(env->GetArrayFunction())->GetProtoOrHClass();
503     JSHandle<JSHClass> jsHClass = JSHandle<JSHClass>(thread,
504         reinterpret_cast<JSHClass *>(protoOrHClass.GetTaggedObject()));
505 
506     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
507     JSHandle<JSObject> obj = factory->NewJSObject(jsHClass);
508 
509     JSArray::Cast(*obj)->SetTrackInfo(thread, JSTaggedValue::Undefined());
510     auto accessor = thread->GlobalConstants()->GetArrayLengthAccessor();
511     JSArray::Cast(*obj)->SetPropertyInlinedPropsWithSize<JSArray::SIZE, JSArray::LENGTH_INLINE_PROPERTY_INDEX>(
512         thread, accessor);
513 
514     obj->GetJSHClass()->SetExtensible(true);
515     JSArray::Cast(*obj)->SetArrayLength(thread, length);
516     obj->SetElements(thread, elements);
517 
518     JSHandle<JSArray> arr(obj);
519     JSHClass::TransitToElementsKind(thread, arr, ElementsKind::GENERIC);
520     return arr;
521 }
522 # else
523 // ecma6 7.3 Operations on Objects
CreateArrayFromList(JSThread * thread,const JSHandle<TaggedArray> & elements)524 JSHandle<JSArray> JSArray::CreateArrayFromList(JSThread *thread, const JSHandle<TaggedArray> &elements)
525 {
526     // Assert: elements is a List whose elements are all ECMAScript language values.
527     // 2. Let array be ArrayCreate(0) (see 9.4.2.2).
528     uint32_t length = elements->GetLength();
529 
530     // 4. For each element e of elements
531     auto env = thread->GetEcmaVM()->GetGlobalEnv();
532     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
533     JSHandle<JSFunction> arrayFunc(env->GetArrayFunction());
534     JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(arrayFunc);
535     obj->GetJSHClass()->SetExtensible(true);
536     JSArray::Cast(*obj)->SetArrayLength(thread, length);
537 
538     obj->SetElements(thread, elements);
539     JSHandle<JSArray> arr(obj);
540     JSHClass::TransitToElementsKind(thread, arr, ElementsKind::GENERIC);
541 
542     return arr;
543 }
544 #endif
545 
546 // used for array contructor with (...items)
CreateArrayFromList(JSThread * thread,const JSHandle<JSTaggedValue> & newtarget,const JSHandle<TaggedArray> & elements)547 JSHandle<JSArray> JSArray::CreateArrayFromList(JSThread *thread, const JSHandle<JSTaggedValue> &newtarget,
548                                                const JSHandle<TaggedArray> &elements)
549 {
550     // Assert: elements is a List whose elements are all ECMAScript language values.
551     uint32_t length = elements->GetLength();
552 
553     // create arr object
554     auto env = thread->GetEcmaVM()->GetGlobalEnv();
555     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
556     JSHandle<JSFunction> arrayFunc(env->GetArrayFunction());
557     JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(arrayFunc, newtarget);
558     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
559     obj->GetJSHClass()->SetExtensible(true);
560 
561     // set elements with initItems
562     JSHandle<JSArray> arr(obj);
563     arr->SetArrayLength(thread, length);
564     obj->SetElements(thread, elements);
565 
566     CheckAndSetPrototypeModified(thread, obj);
567     return arr;
568 }
569 
FastGetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t index)570 JSHandle<JSTaggedValue> JSArray::FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
571                                                         uint32_t index)
572 {
573     auto result = ObjectFastOperator::FastGetPropertyByIndex(thread, obj.GetTaggedValue(), index);
574     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
575     return JSHandle<JSTaggedValue>(thread, result);
576 }
577 
FastGetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key)578 JSHandle<JSTaggedValue> JSArray::FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
579                                                         const JSHandle<JSTaggedValue> &key)
580 {
581     auto result = ObjectFastOperator::FastGetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue());
582     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
583     return JSHandle<JSTaggedValue>(thread, result);
584 }
585 
FastSetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,uint32_t index,const JSHandle<JSTaggedValue> & value)586 bool JSArray::FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, uint32_t index,
587                                      const JSHandle<JSTaggedValue> &value)
588 {
589     return ObjectFastOperator::FastSetPropertyByIndex(thread, obj.GetTaggedValue(), index, value.GetTaggedValue());
590 }
591 
FastSetPropertyByValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value)592 bool JSArray::FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
593                                      const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
594 {
595     return ObjectFastOperator::FastSetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(),
596                                                       value.GetTaggedValue());
597 }
598 
TryFastCreateDataProperty(JSThread * thread,const JSHandle<JSObject> & obj,uint32_t index,const JSHandle<JSTaggedValue> & value,SCheckMode sCheckMode)599 bool JSArray::TryFastCreateDataProperty(JSThread *thread, const JSHandle<JSObject> &obj, uint32_t index,
600                                         const JSHandle<JSTaggedValue> &value,  SCheckMode sCheckMode)
601 {
602 #if ENABLE_NEXT_OPTIMIZATION
603     JSHandle<JSTaggedValue> objVal(obj);
604     if (!objVal->IsStableJSArray(thread)) {
605         // if JSArray is DictionaryMode goto slowPath
606         return JSObject::CreateDataPropertyOrThrow(thread, obj, index, value, sCheckMode);
607     }
608 
609     uint32_t capacity = TaggedArray::Cast(obj->GetElements())->GetLength();
610     uint32_t len = JSHandle<JSArray>::Cast(obj)->GetArrayLength();
611     if UNLIKELY(index > len) {
612         // goto slowPath
613         return JSObject::CreateDataPropertyOrThrow(thread, obj, index, value, sCheckMode);
614     }
615 
616     if UNLIKELY(index == len) {
617         // append situation
618         if (!IsArrayLengthWritable(thread, obj)) {
619             THROW_TYPE_ERROR_AND_RETURN(thread, "UnWritable ArrayLength", false);
620         }
621 
622         uint32_t newLen = index + 1;
623         if (newLen > capacity) {
624             // needs to expand the capacity
625             return JSObject::CreateDataPropertyOrThrow(thread, obj, index, value, sCheckMode);
626         }
627         JSHandle<JSArray>::Cast(obj)->SetArrayLength(thread, newLen);
628     }
629     if LIKELY(!thread->IsEnableMutantArray()) {
630         TaggedArray::Cast(obj->GetElements())->Set(thread, index, value);
631         if LIKELY(thread->IsEnableElementsKind()) {
632             JSHClass::TransitToElementsKind(thread, obj, value, ElementsKind::NONE);
633         }
634     } else {
635         ElementAccessor::Set(thread, obj, index, value, true);
636     }
637     return true;
638 #else
639     return JSObject::CreateDataPropertyOrThrow(thread, obj, index, value, sCheckMode);
640 #endif
641 }
642 
CopySortedListToReceiver(JSThread * thread,const JSHandle<JSTaggedValue> & obj,JSHandle<TaggedArray> sortedList,uint32_t len)643 JSTaggedValue JSArray::CopySortedListToReceiver(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
644                                                 JSHandle<TaggedArray> sortedList, uint32_t len)
645 {
646     // 6. Let itemCount be the number of elements in sortedList.
647     uint32_t itemCount = sortedList->GetLength();
648     // 7. Let j be 0.
649     uint32_t j = 0;
650     // 8. Repeat, while j < itemCount,
651     //     a. Perform ! Set(obj, ! ToString((j)), sortedList[j], true).
652     //     b. Set j to j + 1.
653     JSMutableHandle<JSTaggedValue> item(thread, JSTaggedValue::Undefined());
654     while (j < itemCount) {
655         item.Update(sortedList->Get(j));
656         JSArray::FastSetPropertyByValue(thread, obj, j, item);
657         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
658         ++j;
659     }
660     // 9. NOTE: The call to SortIndexedProperties in step 5 uses SKIP-HOLES.The remaining indices are deleted to
661     // preserve the number of holes that were detected and excluded from the sort.
662     // 10. Repeat, while j < len,
663     //       a. Perform ? DeletePropertyOrThrow(obj, ! ToString((j))).
664     //       b. Set j to j + 1.
665     while (j < len) {
666         item.Update(JSTaggedValue(j));
667         JSTaggedValue::DeletePropertyOrThrow(thread, obj, item);
668         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
669         ++j;
670     }
671     return obj.GetTaggedValue();
672 }
673 
674 // ecma2024 23.1.3.20 Array.prototype.sort(comparefn)
Sort(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & fn)675 JSTaggedValue JSArray::Sort(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const JSHandle<JSTaggedValue> &fn)
676 {
677     ASSERT(fn->IsUndefined() || fn->IsCallable());
678     // 3. Let len be ?LengthOfArrayLike(obj).
679     int64_t len = ArrayHelper::GetArrayLength(thread, obj);
680     // ReturnIfAbrupt(len).
681     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
682     // If len is 0 or 1, no need to sort
683     if (len == 0 || len == 1) {
684         return obj.GetTaggedValue();
685     }
686 
687     // 4. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs
688     // the following steps when called:
689     //    a. Return ? CompareArrayElements(x, y, comparefn).
690     // 5. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, SKIP-HOLES).
691     JSHandle<TaggedArray> sortedList =
692         ArrayHelper::SortIndexedProperties(thread, obj, len, fn, base::HolesType::SKIP_HOLES);
693     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
694     JSArray::CopySortedListToReceiver(thread, obj, sortedList, len);
695     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
696     return obj.GetTaggedValue();
697 }
698 
SortElements(JSThread * thread,const JSHandle<TaggedArray> & elements,const JSHandle<JSTaggedValue> & fn)699 void JSArray::SortElements(JSThread *thread, const JSHandle<TaggedArray> &elements, const JSHandle<JSTaggedValue> &fn)
700 {
701     ASSERT(fn->IsUndefined() || fn->IsCallable());
702 
703     uint32_t len = elements->GetLength();
704     // 64: if the elements is more than 64, use merge-sort algorithm.
705     if (len < 64) {
706         SortElementsByInsertionSort(thread, elements, len, fn);
707     } else {
708         SortElementsByMergeSort(thread, elements, fn, 0, len - 1);
709     }
710 }
711 
SortElementsByMergeSort(JSThread * thread,const JSHandle<TaggedArray> & elements,const JSHandle<JSTaggedValue> & fn,int64_t startIdx,int64_t endIdx)712 void JSArray::SortElementsByMergeSort(JSThread *thread, const JSHandle<TaggedArray> &elements,
713                                       const JSHandle<JSTaggedValue> &fn, int64_t startIdx, int64_t endIdx)
714 {
715     if (startIdx >= endIdx)
716         return;
717 
718     int64_t middleIdx = startIdx + (endIdx - startIdx) / 2; // 2: half
719     SortElementsByMergeSort(thread, elements, fn, startIdx, middleIdx);
720     SortElementsByMergeSort(thread, elements, fn, middleIdx + 1, endIdx);
721     MergeSortedElements(thread, elements, fn, startIdx, middleIdx, endIdx);
722 }
723 
724 
FastConcatDictionaryArray(JSThread * thread,JSHandle<JSObject> obj,JSHandle<JSObject> & newArrayHandle,JSMutableHandle<JSTaggedValue> & fromValHandle,JSMutableHandle<JSTaggedValue> & toKey,int64_t & n)725 JSTaggedValue JSArray::FastConcatDictionaryArray(JSThread *thread, JSHandle<JSObject> obj,
726     JSHandle<JSObject> &newArrayHandle, JSMutableHandle<JSTaggedValue> &fromValHandle,
727     JSMutableHandle<JSTaggedValue> &toKey, int64_t &n)
728 {
729     bool isArrayHandleDictionary = newArrayHandle->GetJSHClass()->IsDictionaryElement();
730     if (!isArrayHandleDictionary) {
731         JSObject::ElementsToDictionary(thread, newArrayHandle);
732         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
733     }
734     ASSERT(newArrayHandle->GetJSHClass()->IsDictionaryElement());
735     JSHandle<JSTaggedValue> objVal(obj);
736     int64_t len = base::ArrayHelper::GetArrayLength(thread, objVal);
737     JSHandle<NumberDictionary> elements(thread, obj->GetElements());
738     uint32_t size = static_cast<uint32_t>(elements->Size());
739     JSMutableHandle<NumberDictionary> dict(thread, newArrayHandle->GetElements());
740     auto attr = PropertyAttributes(PropertyAttributes::GetDefaultAttributes());
741     for (uint32_t hashIndex = 0; hashIndex < size; hashIndex++) {
742         JSTaggedValue key = elements->GetKey(hashIndex);
743         if (key.IsUndefined() || key.IsHole()) {
744             continue;
745         }
746         ASSERT(key.IsInt());
747         uint32_t uintKey = static_cast<uint32_t>(key.GetInt());
748         if (uintKey < len) {
749             JSTaggedValue value = elements->GetValue(hashIndex);
750             toKey.Update(JSTaggedValue(static_cast<int32_t>(n + uintKey))); // guarantee the toKey IsInt
751             fromValHandle.Update(value);
752             JSHandle<NumberDictionary> newDict = \
753                 NumberDictionary::PutIfAbsent(thread, dict, toKey, fromValHandle, attr);
754             dict.Update(newDict);
755             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
756         }
757     }
758     newArrayHandle->SetElements(thread, dict);
759     n += len;
760     return JSTaggedValue(true);
761 }
762 
MergeSortedElements(JSThread * thread,const JSHandle<TaggedArray> & elements,const JSHandle<JSTaggedValue> & fn,int64_t startIdx,int64_t middleIdx,int64_t endIdx)763 void JSArray::MergeSortedElements(JSThread *thread, const JSHandle<TaggedArray> &elements,
764                                   const JSHandle<JSTaggedValue> &fn, int64_t startIdx,
765                                   int64_t middleIdx, int64_t endIdx)
766 {
767     int64_t leftLength = middleIdx - startIdx + 1;
768     int64_t rightLength = endIdx - middleIdx;
769 
770     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
771     JSHandle<TaggedArray> leftArray = factory->NewTaggedArray(leftLength);
772     JSHandle<TaggedArray> rightArray = factory->NewTaggedArray(rightLength);
773 
774     for (int64_t i = 0; i < leftLength; i++) {
775         leftArray->Set(thread, i, elements->Get(startIdx + i));
776     }
777     for (int64_t j = 0; j < rightLength; j++) {
778         rightArray->Set(thread, j, elements->Get(static_cast<int32_t>(middleIdx + 1 + j)));
779     }
780 
781     int64_t i = 0;
782     int64_t j = 0;
783     int64_t k = startIdx;
784     JSMutableHandle<JSTaggedValue> leftValue(thread, JSTaggedValue::Undefined());
785     JSMutableHandle<JSTaggedValue> rightValue(thread, JSTaggedValue::Undefined());
786     while (i < leftLength && j < rightLength) {
787         leftValue.Update(leftArray->Get(i));
788         rightValue.Update(rightArray->Get(j));
789         int64_t compareRet = base::ArrayHelper::SortCompare(thread, fn, leftValue, rightValue);
790         RETURN_IF_ABRUPT_COMPLETION(thread);
791         if (compareRet <= 0) {
792             elements->Set(thread, k, leftArray->Get(i));
793             i++;
794         } else {
795             elements->Set(thread, k, rightArray->Get(j));
796             j++;
797         }
798         k++;
799     }
800 
801     while (i < leftLength) {
802         elements->Set(thread, k, leftArray->Get(i));
803         i++;
804         k++;
805     }
806 
807     while (j < rightLength) {
808         elements->Set(thread, k, rightArray->Get(j));
809         j++;
810         k++;
811     }
812 }
813 
SortElementsByInsertionSort(JSThread * thread,const JSHandle<TaggedArray> & elements,uint32_t len,const JSHandle<JSTaggedValue> & fn)814 void JSArray::SortElementsByInsertionSort(JSThread *thread, const JSHandle<TaggedArray> &elements, uint32_t len,
815     const JSHandle<JSTaggedValue> &fn)
816 {
817     if (len <= 1)
818         return;
819 
820     JSMutableHandle<JSTaggedValue> presentValue(thread, JSTaggedValue::Undefined());
821     JSMutableHandle<JSTaggedValue> middleValue(thread, JSTaggedValue::Undefined());
822     JSMutableHandle<JSTaggedValue> previousValue(thread, JSTaggedValue::Undefined());
823     for (uint32_t i = 1; i < len; i++) {
824         uint32_t beginIndex = 0;
825         uint32_t endIndex = i;
826         presentValue.Update(elements->Get(i));
827         while (beginIndex < endIndex) {
828             uint32_t middleIndex = (beginIndex + endIndex) / 2; // 2 : half
829             middleValue.Update(elements->Get(middleIndex));
830             double compareResult = base::ArrayHelper::SortCompare(thread, fn, middleValue, presentValue);
831             RETURN_IF_ABRUPT_COMPLETION(thread);
832             if (compareResult > 0) {
833                 endIndex = middleIndex;
834             } else {
835                 beginIndex = middleIndex + 1;
836             }
837         }
838 
839         if (endIndex >= 0 && endIndex < i) {
840             for (uint32_t j = i; j > endIndex; j--) {
841                 previousValue.Update(elements->Get(j - 1));
842                 elements->Set(thread, j, previousValue);
843             }
844             elements->Set(thread, endIndex, presentValue);
845         }
846     }
847 }
848 
SortElementsByObject(JSThread * thread,const JSHandle<JSObject> & thisObjHandle,const JSHandle<JSTaggedValue> & fn)849 void JSArray::SortElementsByObject(JSThread *thread, const JSHandle<JSObject> &thisObjHandle,
850                                    const JSHandle<JSTaggedValue> &fn)
851 {
852     ASSERT(fn->IsUndefined() || fn->IsCallable());
853 
854     JSMutableHandle<JSTaggedValue> presentValue(thread, JSTaggedValue::Undefined());
855     JSMutableHandle<JSTaggedValue> middleValue(thread, JSTaggedValue::Undefined());
856     JSMutableHandle<JSTaggedValue> previousValue(thread, JSTaggedValue::Undefined());
857     uint32_t len = ElementAccessor::GetElementsLength(thisObjHandle);
858     for (uint32_t i = 1; i < len; i++) {
859         uint32_t beginIndex = 0;
860         uint32_t endIndex = i;
861         presentValue.Update(CheckStableArrayAndGet(thread, thisObjHandle, i));
862         while (beginIndex < endIndex) {
863             uint32_t middleIndex = (beginIndex + endIndex) / 2; // 2 : half
864             middleValue.Update(CheckStableArrayAndGet(thread, thisObjHandle, middleIndex));
865             int32_t compareResult = base::ArrayHelper::SortCompare(thread, fn, middleValue, presentValue);
866             RETURN_IF_ABRUPT_COMPLETION(thread);
867             if (compareResult > 0) {
868                 endIndex = middleIndex;
869             } else {
870                 beginIndex = middleIndex + 1;
871             }
872         }
873 
874         if (endIndex >= 0 && endIndex < i) {
875             for (uint32_t j = i; j > endIndex; j--) {
876                 previousValue.Update(CheckStableArrayAndGet(thread, thisObjHandle, j - 1));
877                 CheckStableArrayAndSet(thread, thisObjHandle, j, previousValue);
878             }
879             CheckStableArrayAndSet(thread, thisObjHandle, endIndex, presentValue);
880         }
881     }
882 }
883 
CheckStableArrayAndSet(JSThread * thread,const JSHandle<JSObject> & thisObjHandle,uint32_t index,JSMutableHandle<JSTaggedValue> & value)884 void JSArray::CheckStableArrayAndSet(JSThread *thread, const JSHandle<JSObject> &thisObjHandle, uint32_t index,
885                                      JSMutableHandle<JSTaggedValue> &value)
886 {
887     if (thisObjHandle.GetTaggedValue().IsStableJSArray(thread) &&
888         index < ElementAccessor::GetElementsLength(thisObjHandle)) {
889         return ElementAccessor::Set(thread, thisObjHandle, index, value, false);
890     } else {
891         ObjectFastOperator::FastSetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), index,
892             value.GetTaggedValue());
893     }
894 }
895 
CheckStableArrayAndGet(JSThread * thread,const JSHandle<JSObject> & thisObjHandle,uint32_t index)896 JSTaggedValue JSArray::CheckStableArrayAndGet(JSThread *thread, const JSHandle<JSObject> &thisObjHandle, uint32_t index)
897 {
898     if (thisObjHandle.GetTaggedValue().IsStableJSArray(thread) &&
899         index < ElementAccessor::GetElementsLength(thisObjHandle)) {
900         return ElementAccessor::Get(thread, thisObjHandle, index);
901     } else {
902         return ObjectFastOperator::FastGetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), index);
903     }
904 }
905 
IncludeInSortedValue(JSThread * thread,const JSHandle<JSTaggedValue> & obj,const JSHandle<JSTaggedValue> & value)906 bool JSArray::IncludeInSortedValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
907                                    const JSHandle<JSTaggedValue> &value)
908 {
909     ASSERT(obj->IsJSArray());
910     JSHandle<JSArray> arrayObj = JSHandle<JSArray>::Cast(obj);
911     int32_t length = static_cast<int32_t>(arrayObj->GetArrayLength());
912     if (length == 0) {
913         return false;
914     }
915     int32_t left = 0;
916     int32_t right = length - 1;
917     while (left <= right) {
918         int32_t middle = (left + right) / 2;
919         JSHandle<JSTaggedValue> vv = JSArray::FastGetPropertyByValue(thread, obj, middle);
920         ComparisonResult res = JSTaggedValue::Compare(thread, vv, value);
921         if (res == ComparisonResult::EQUAL) {
922             return true;
923         } else if (res == ComparisonResult::LESS) {
924             left = middle + 1;
925         } else {
926             right = middle - 1;
927         }
928     }
929     return false;
930 }
931 
ToTaggedArray(JSThread * thread,const JSHandle<JSTaggedValue> & obj)932 JSHandle<TaggedArray> JSArray::ToTaggedArray(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
933 {
934     ASSERT(obj->IsJSArray());
935     JSHandle<JSArray> arrayObj = JSHandle<JSArray>::Cast(obj);
936     uint32_t length = arrayObj->GetArrayLength();
937     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
938     JSHandle<TaggedArray> taggedArray = factory->NewTaggedArray(length);
939     for (uint32_t idx = 0; idx < length; idx++) {
940         JSHandle<JSTaggedValue> vv = JSArray::FastGetPropertyByValue(thread, obj, idx);
941         taggedArray->Set(thread, idx, vv);
942     }
943     return taggedArray;
944 }
945 
CheckAndCopyArray(const JSThread * thread,JSHandle<JSArray> obj)946 void JSArray::CheckAndCopyArray(const JSThread *thread, JSHandle<JSArray> obj)
947 {
948     JSHandle<TaggedArray> arr(thread, obj->GetElements());
949     // Check whether array is shared in the nonmovable space before set properties and elements.
950     // If true, then really copy array in the semi space.
951     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
952     if (arr.GetTaggedValue().IsCOWArray()) {
953         auto newArray = factory->CopyArray(arr, arr->GetLength(), arr->GetLength(),
954             JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
955         obj->SetElements(thread, newArray.GetTaggedValue());
956     }
957     JSHandle<TaggedArray> prop(thread, obj->GetProperties());
958     if (prop.GetTaggedValue().IsCOWArray()) {
959         auto newProps = factory->CopyArray(prop, prop->GetLength(), prop->GetLength(),
960             JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
961         obj->SetProperties(thread, newProps.GetTaggedValue());
962     }
963 }
964 
965 // static
IsProtoNotChangeJSArray(JSThread * thread,const JSHandle<JSObject> & obj)966 bool JSArray::IsProtoNotChangeJSArray(JSThread *thread, const JSHandle<JSObject> &obj)
967 {
968     if (obj->IsJSArray()) {
969         if (obj->GetJSHClass()->GetElementsKind() != ElementsKind::GENERIC) {
970             return true;
971         }
972         JSTaggedValue arrayProtoValue = JSObject::GetPrototype(obj);
973         JSTaggedValue genericArrayHClass = thread->GlobalConstants()->GetElementHoleTaggedClass();
974         JSTaggedValue genericArrayProtoValue = \
975             JSHClass::Cast(genericArrayHClass.GetTaggedObject())->GetProto();
976         return genericArrayProtoValue == arrayProtoValue;
977     }
978     return false;
979 }
980 
CreateJSArrayPrototypeClass(const JSThread * thread,ObjectFactory * factory,JSHandle<JSTaggedValue> proto,uint32_t inlinedProps)981 JSHandle<JSHClass> JSArray::CreateJSArrayPrototypeClass(const JSThread *thread, ObjectFactory *factory,
982                                                         JSHandle<JSTaggedValue> proto, uint32_t inlinedProps)
983 {
984     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
985     JSHandle<JSHClass> arrayClass = factory->NewEcmaHClass(JSArray::SIZE, inlinedProps, JSType::JS_ARRAY, proto);
986 
987     uint32_t fieldOrder = 0;
988     ASSERT(JSArray::LENGTH_INLINE_PROPERTY_INDEX == fieldOrder);
989     JSHandle<LayoutInfo> layoutInfoHandle = factory->CreateLayoutInfo(inlinedProps);
990     {
991         PropertyAttributes attributes = PropertyAttributes::DefaultAccessor(true, false, false);
992         attributes.SetIsInlinedProps(true);
993         attributes.SetRepresentation(Representation::TAGGED);
994         attributes.SetOffset(fieldOrder++);
995         JSTaggedValue key = globalConst->GetLengthString();
996         layoutInfoHandle->AddKey(thread, JSArray::LENGTH_INLINE_PROPERTY_INDEX, key, attributes);
997     }
998     ASSERT(JSArray::CONSTRUCTOR_INLINE_PROPERTY_INDEX == fieldOrder);
999     {
1000         PropertyAttributes attributes = PropertyAttributes::Default(true, false, true);
1001         attributes.SetIsInlinedProps(true);
1002         attributes.SetRepresentation(Representation::TAGGED);
1003         attributes.SetOffset(fieldOrder++);
1004         JSTaggedValue key = globalConst->GetConstructorString();
1005         layoutInfoHandle->AddKey(thread, JSArray::CONSTRUCTOR_INLINE_PROPERTY_INDEX, key, attributes);
1006     }
1007     {
1008         arrayClass->SetLayout(thread, layoutInfoHandle);
1009         arrayClass->SetNumberOfProps(fieldOrder);
1010     }
1011     arrayClass->SetIsStableElements(true);
1012     arrayClass->SetHasConstructor(false);
1013 
1014     return arrayClass;
1015 }
1016 
CreateJSArrayFunctionClass(const JSThread * thread,ObjectFactory * factory,const JSHandle<GlobalEnv> & env)1017 JSHandle<JSHClass> JSArray::CreateJSArrayFunctionClass(const JSThread *thread, ObjectFactory *factory,
1018                                                        const JSHandle<GlobalEnv> &env)
1019 {
1020     JSHandle<JSHClass> arrayFunctionClass =
1021         factory->NewEcmaHClass(JSFunction::SIZE, JSArray::ARRAY_FUNCTION_INLINE_PROPERTY_NUM, JSType::JS_FUNCTION,
1022                                env->GetFunctionPrototype());
1023     arrayFunctionClass->SetConstructor(true);
1024 
1025     uint32_t fieldOrder = 0;
1026     JSHandle<LayoutInfo> layoutInfoHandle = factory->CreateLayoutInfo(1);
1027     {
1028         PropertyAttributes attributes = PropertyAttributes::DefaultAccessor(false, false, true);
1029         attributes.SetIsInlinedProps(true);
1030         attributes.SetRepresentation(Representation::TAGGED);
1031         attributes.SetOffset(fieldOrder++);
1032         JSTaggedValue key = env->GetSpeciesSymbol().GetTaggedValue();
1033         layoutInfoHandle->AddKey(thread, JSArray::ARRAY_FUNCTION_SPECIES_INDEX, key, attributes);
1034     }
1035     {
1036         arrayFunctionClass->SetLayout(thread, layoutInfoHandle);
1037         arrayFunctionClass->SetNumberOfProps(fieldOrder);
1038     }
1039     return arrayFunctionClass;
1040 }
1041 
UpdateTrackInfo(const JSThread * thread)1042 void JSArray::UpdateTrackInfo(const JSThread *thread)
1043 {
1044     JSTaggedValue trackInfoVal = GetTrackInfo();
1045     if (trackInfoVal.IsHeapObject() && trackInfoVal.IsWeak()) {
1046         TrackInfo *trackInfo = TrackInfo::Cast(trackInfoVal.GetWeakReferentUnChecked());
1047         ElementsKind oldKind = trackInfo->GetElementsKind();
1048         if (Elements::IsGeneric(oldKind)) {
1049             return;
1050         }
1051 
1052         JSHClass *hclass = GetJSHClass();
1053         ElementsKind newKind = hclass->GetElementsKind();
1054         trackInfo->SetElementsKind(newKind);
1055         // Since trackInfo is only used at define point,
1056         // we update cachedHClass with initial array hclass which does not have IsPrototype set.
1057         JSTaggedValue cachedHClass = JSTaggedValue(thread->GetArrayInstanceHClass(newKind, false));
1058         trackInfo->SetCachedHClass(thread, cachedHClass);
1059     }
1060 }
1061 }  // namespace panda::ecmascript
1062