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