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