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