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