• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/base/array_helper.h"
17 
18 #include "ecmascript/base/sort_helper.h"
19 #include "ecmascript/interpreter/interpreter.h"
20 #include "ecmascript/object_fast_operator-inl.h"
21 
22 namespace panda::ecmascript::base {
GetStartIndex(JSThread * thread,const JSHandle<JSTaggedValue> & startIndexHandle,int64_t length)23 int64_t ArrayHelper::GetStartIndex(JSThread *thread, const JSHandle<JSTaggedValue> &startIndexHandle,
24                                    int64_t length)
25 {
26     // Common procedure to clamp fromIndexValue to the range [0, length].
27     // For integer case, conditional selection instructions (csel in ARM, cmov in x86, etc.)
28     // may be utilized by the compiler to minimize branching.
29     auto doClamp = [length](auto fromIndexValue) -> int64_t {
30         if (LIKELY(fromIndexValue >= 0)) {
31             // Including the case where fromIndexValue == Infinity
32             return (fromIndexValue >= length) ? length : static_cast<int64_t>(fromIndexValue);
33         }
34         auto plusLength = fromIndexValue + length;
35         if (plusLength >= 0) {
36             return static_cast<int64_t>(plusLength);
37         }
38         return 0; // Including the case where fromIndexValue == -Infinity
39     };
40     if (LIKELY(startIndexHandle->IsInt())) {
41         // Fast path: startIndexHandle is tagged int32.
42         return doClamp(startIndexHandle->GetInt());
43     }
44     // Slow path: startIndexHandle is targged double, or type conversion is involved.
45     JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, startIndexHandle);
46     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, length);
47 
48     double fromIndexValue = base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber()); // NaN -> 0
49     return doClamp(fromIndexValue);
50 }
51 
GetStartIndexFromArgs(JSThread * thread,EcmaRuntimeCallInfo * argv,uint32_t argIndex,int64_t length)52 int64_t ArrayHelper::GetStartIndexFromArgs(JSThread *thread, EcmaRuntimeCallInfo *argv,
53                                            uint32_t argIndex, int64_t length)
54 {
55     uint32_t argc = argv->GetArgsNumber();
56     if (argc <= argIndex) {
57         return 0;
58     }
59     JSHandle<JSTaggedValue> arg = base::BuiltinsBase::GetCallArg(argv, argIndex);
60     return GetStartIndex(thread, arg, length);
61 }
62 
GetLastStartIndex(JSThread * thread,const JSHandle<JSTaggedValue> & startIndexHandle,int64_t length)63 int64_t ArrayHelper::GetLastStartIndex(JSThread *thread, const JSHandle<JSTaggedValue> &startIndexHandle,
64                                        int64_t length)
65 {
66     // Common procedure to clamp fromIndexValue to the range [-1, length-1].
67     auto doClamp = [length](auto fromIndexValue) -> int64_t {
68         if (LIKELY(fromIndexValue >= 0)) {
69             // Including the case where fromIndexValue == Infinity
70             return (length - 1 < fromIndexValue) ? (length - 1) : static_cast<int64_t>(fromIndexValue);
71         }
72         auto plusLength = fromIndexValue + length;
73         if (plusLength >= 0) {
74             return static_cast<int64_t>(plusLength);
75         }
76         return -1; // Including the case where fromIndexValue == -Infinity
77     };
78     if (LIKELY(startIndexHandle->IsInt())) {
79         // Fast path: startIndexHandle is tagged int32.
80         return doClamp(startIndexHandle->GetInt());
81     }
82     // Slow path: startIndexHandle is targged double, or type conversion is involved.
83     JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, startIndexHandle);
84     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, -1);
85 
86     double fromIndexValue = base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber()); // NaN -> 0
87     return doClamp(fromIndexValue);
88 }
89 
GetLastStartIndexFromArgs(JSThread * thread,EcmaRuntimeCallInfo * argv,uint32_t argIndex,int64_t length)90 int64_t ArrayHelper::GetLastStartIndexFromArgs(JSThread *thread, EcmaRuntimeCallInfo *argv,
91                                                uint32_t argIndex, int64_t length)
92 {
93     uint32_t argc = argv->GetArgsNumber();
94     if (argc <= argIndex) {
95         return length - 1;
96     }
97     JSHandle<JSTaggedValue> arg = base::BuiltinsBase::GetCallArg(argv, argIndex);
98     return GetLastStartIndex(thread, arg, length);
99 }
100 
ElementIsStrictEqualTo(JSThread * thread,const JSHandle<JSTaggedValue> & thisObjVal,const JSHandle<JSTaggedValue> & keyHandle,const JSHandle<JSTaggedValue> & target)101 bool ArrayHelper::ElementIsStrictEqualTo(JSThread *thread, const JSHandle<JSTaggedValue> &thisObjVal,
102                                          const JSHandle<JSTaggedValue> &keyHandle,
103                                          const JSHandle<JSTaggedValue> &target)
104 {
105     bool exists = thisObjVal->IsTypedArray() || thisObjVal->IsSharedTypedArray() ||
106         JSTaggedValue::HasProperty(thread, thisObjVal, keyHandle);
107     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
108     if (thread->HasPendingException() || !exists) {
109         return false;
110     }
111     JSHandle<JSTaggedValue> valueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, keyHandle);
112     if (thread->HasPendingException()) {
113         return false;
114     }
115     return JSTaggedValue::StrictEqual(thread, target, valueHandle);
116 }
117 
IsConcatSpreadable(JSThread * thread,const JSHandle<JSTaggedValue> & obj)118 bool ArrayHelper::IsConcatSpreadable(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
119 {
120     // 1. If Type(O) is not Object, return false.
121     if (!obj->IsECMAObject()) {
122         return false;
123     }
124 
125     // 2. Let spreadable be Get(O, @@isConcatSpreadable).
126     auto ecmaVm = thread->GetEcmaVM();
127     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
128     JSHandle<JSTaggedValue> isConcatsprKey = env->GetIsConcatSpreadableSymbol();
129     JSTaggedValue spreadable = ObjectFastOperator::FastGetPropertyByValue(thread, obj.GetTaggedValue(),
130                                                                           isConcatsprKey.GetTaggedValue());
131     // 3. ReturnIfAbrupt(spreadable).
132     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
133 
134     // 4. If spreadable is not undefined, return ToBoolean(spreadable).
135     if (!spreadable.IsUndefined()) {
136         return spreadable.ToBoolean();
137     }
138 
139     // 5. Return IsArray(O).
140     return obj->IsArray(thread) || obj->IsJSSharedArray();
141 }
142 
143 // must use 'double' as return type, for sort result may double.
144 // let arr = [1,2,3,4,5,6]; arr.sort(() => Math.random() - 0.5);
SortCompare(JSThread * thread,const JSHandle<JSTaggedValue> & callbackfnHandle,const JSHandle<JSTaggedValue> & valueX,const JSHandle<JSTaggedValue> & valueY)145 double ArrayHelper::SortCompare(JSThread *thread, const JSHandle<JSTaggedValue> &callbackfnHandle,
146                                 const JSHandle<JSTaggedValue> &valueX, const JSHandle<JSTaggedValue> &valueY)
147 {
148     // 1. If x and y are both undefined, return +0.
149     if (valueX->IsHole()) {
150         if (valueY->IsHole()) {
151             return 0;
152         }
153         return 1;
154     }
155     if (valueY->IsHole()) {
156         return -1;
157     }
158     if (valueX->IsUndefined()) {
159         if (valueY->IsUndefined()) {
160             return 0;
161         }
162         // 2. If x is undefined, return 1.
163         return 1;
164     }
165     // 3. If y is undefined, return -1.
166     if (valueY->IsUndefined()) {
167         return -1;
168     }
169     // 4. If the argument comparefn is not undefined, then
170     // a. Let v be ToNumber(Call(comparefn, undefined, «x, y»)).
171     // b. ReturnIfAbrupt(v).
172     // c. If v is NaN, return +0.
173     // d. Return v.
174     if (!callbackfnHandle->IsUndefined() && !callbackfnHandle->IsNull()) {
175         JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
176         EcmaRuntimeCallInfo *info =
177             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackfnHandle, undefined, undefined, 2); // 2: «x, y»
178         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
179         info->SetCallArg(valueX.GetTaggedValue(), valueY.GetTaggedValue());
180         JSTaggedValue callResult = JSFunction::Call(info);
181         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
182         if (callResult.IsInt()) {
183             return callResult.GetInt();
184         }
185         JSHandle<JSTaggedValue> testResult(thread, callResult);
186         JSTaggedNumber v = JSTaggedValue::ToNumber(thread, testResult);
187         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
188         double value = v.GetNumber();
189         if (std::isnan(value)) {
190             return +0;
191         }
192         return value;
193     }
194     // 5. Let xString be ToString(x).
195     // 6. ReturnIfAbrupt(xString).
196     // 7. Let yString be ToString(y).
197     // 8. ReturnIfAbrupt(yString).
198     // 9. If xString < yString, return -1.
199     // 10. If xString > yString, return 1.
200     // 11. Return +0.
201     if (valueX->IsInt() && valueY->IsInt()) {
202         return JSTaggedValue::IntLexicographicCompare(valueX.GetTaggedValue(), valueY.GetTaggedValue());
203     }
204     if (valueX->IsString() && valueY->IsString()) {
205         return EcmaStringAccessor::Compare(thread->GetEcmaVM(),
206             JSHandle<EcmaString>(valueX), JSHandle<EcmaString>(valueY));
207     }
208     JSHandle<JSTaggedValue> xValueHandle(JSTaggedValue::ToString(thread, valueX));
209     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
210     JSHandle<JSTaggedValue> yValueHandle(JSTaggedValue::ToString(thread, valueY));
211     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
212     ComparisonResult compareResult = JSTaggedValue::Compare(thread, xValueHandle, yValueHandle);
213     if (compareResult == ComparisonResult::GREAT) {
214         return 1;
215     }
216     if (compareResult == ComparisonResult::LESS) {
217         return -1;
218     }
219     return 0;
220 }
221 
StringSortCompare(JSThread * thread,const JSHandle<JSTaggedValue> & valueX,const JSHandle<JSTaggedValue> & valueY)222 double ArrayHelper::StringSortCompare(JSThread *thread, const JSHandle<JSTaggedValue> &valueX,
223                                       const JSHandle<JSTaggedValue> &valueY)
224 {
225     ASSERT(valueX->IsString());
226     ASSERT(valueY->IsString());
227     // 9. If xString < yString, return -1.
228     // 10. If xString > yString, return 1.
229     // 11. Return +0.
230     auto xHandle = JSHandle<EcmaString>(valueX);
231     auto yHandle = JSHandle<EcmaString>(valueY);
232     int result = EcmaStringAccessor::Compare(thread->GetEcmaVM(), xHandle, yHandle);
233     if (result < 0) {
234         return -1;
235     }
236     if (result > 0) {
237         return 1;
238     }
239     return 0;
240 }
241 
GetLength(JSThread * thread,const JSHandle<JSTaggedValue> & thisHandle)242 int64_t ArrayHelper::GetLength(JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle)
243 {
244     if (thisHandle->IsJSArray()) {
245         return JSArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength();
246     }
247     if (thisHandle->IsJSSharedArray()) {
248         return JSSharedArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength();
249     }
250     if (thisHandle->IsTypedArray() || thisHandle->IsSharedTypedArray()) {
251         return JSHandle<JSTypedArray>::Cast(thisHandle)->GetArrayLength();
252     }
253     JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
254     JSHandle<JSTaggedValue> lenResult = JSTaggedValue::GetProperty(thread, thisHandle, lengthKey).GetValue();
255     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
256     JSTaggedNumber len = JSTaggedValue::ToLength(thread, lenResult);
257     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
258     return len.GetNumber();
259 }
260 
GetArrayLength(JSThread * thread,const JSHandle<JSTaggedValue> & thisHandle)261 int64_t ArrayHelper::GetArrayLength(JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle)
262 {
263     if (thisHandle->IsJSArray()) {
264         return JSArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength();
265     }
266     if (thisHandle->IsJSSharedArray()) {
267         return JSSharedArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength();
268     }
269     JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
270     JSHandle<JSTaggedValue> lenResult = JSTaggedValue::GetProperty(thread, thisHandle, lengthKey).GetValue();
271     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
272     JSTaggedNumber len = JSTaggedValue::ToLength(thread, lenResult);
273     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
274     return len.GetNumber();
275 }
276 
FlattenIntoArray(JSThread * thread,const JSHandle<JSObject> & newArrayHandle,const JSHandle<JSTaggedValue> & thisObjVal,const FlattenArgs & args,const JSHandle<JSTaggedValue> & mapperFunctionHandle,const JSHandle<JSTaggedValue> & thisArg)277 JSTaggedValue ArrayHelper::FlattenIntoArray(JSThread *thread, const JSHandle<JSObject> &newArrayHandle,
278                                             const JSHandle<JSTaggedValue> &thisObjVal, const FlattenArgs &args,
279                                             const JSHandle<JSTaggedValue> &mapperFunctionHandle,
280                                             const JSHandle<JSTaggedValue> &thisArg)
281 {
282     if (thread->DoStackLimitCheck()) {
283         return JSTaggedValue::Exception();
284     }
285     // 1. Assert: Type(target) is Object.
286     // 2. Assert: Type(source) is Object.
287     // 3. Assert: If mapperFunction is present, then ! IsCallable(mapperFunction) is true,
288     //    thisArg is present, and depth is 1.
289     ASSERT(mapperFunctionHandle->IsUndefined() || mapperFunctionHandle->IsCallable() ||
290            (!thisArg->IsUndefined() && args.depth == 1));
291     // 4. Let targetIndex be start.
292     // 5. Let sourceIndex be +0!.
293     FlattenArgs tempArgs;
294     tempArgs.start = args.start;
295     int64_t sourceIndex = 0;
296     JSMutableHandle<JSTaggedValue> p(thread, JSTaggedValue::Undefined());
297     JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Undefined());
298     JSMutableHandle<JSTaggedValue> targetIndexHandle(thread, JSTaggedValue::Undefined());
299     JSMutableHandle<JSTaggedValue> sourceIndexHandle(thread, JSTaggedValue::Undefined());
300     JSHandle<EcmaString> sourceIndexStr;
301     // 6. Repeat, while (sourceIndex) < sourceLen,
302     //     a. Let P be ! ToString(sourceIndex).
303     //     b. Let exists be ? HasProperty(source, P).
304     //     c. If exists is true, then
305     //         i. Let element be ? Get(source, P).
306     //     ii. If mapperFunction is present, then
307     //             1. Set element to ? Call(mapperFunction, thisArg, « element, sourceIndex, source »).
308     //     iii. Let shouldFlatten be false.
309     //     iv. If depth > 0, then
310     //             1. Set shouldFlatten to ? IsArray(element).
311     //         v. If shouldFlatten is true, then
312     //             1. If depth is +∞, let newDepth be +∞.
313     //             2. Else, let newDepth be depth - 1.
314     //             3. Let elementLen be ? LengthOfArrayLike(element).
315     //             4. Set targetIndex to ? FlattenIntoArray(target, element, elementLen, targetIndex, newDepth).
316     //     vi. Else,
317     //             1. If targetIndex ≥ 2^53 - 1, throw a TypeError exception.
318     //             2. Perform ? CreateDataPropertyOrThrow(target, ! ToString(!(targetIndex)), element).
319     //             3. Set targetIndex to targetIndex + 1.
320     //     d. Set sourceIndex to sourceIndex + 1!.
321     while (sourceIndex < args.sourceLen) {
322         sourceIndexHandle.Update(JSTaggedValue(sourceIndex));
323         sourceIndexStr = JSTaggedValue::ToString(thread, sourceIndexHandle);
324         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
325         p.Update(sourceIndexStr.GetTaggedValue());
326         bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, p);
327         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
328         if (exists) {
329             element.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, p).GetTaggedValue());
330             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
331             if (!mapperFunctionHandle->IsUndefined()) {
332                 const int32_t argsLength = 3; // 3: « element, sourceIndex, source »
333                 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
334                 EcmaRuntimeCallInfo *info =
335                     EcmaInterpreter::NewRuntimeCallInfo(thread, mapperFunctionHandle, thisArg, undefined, argsLength);
336                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
337                 info->SetCallArg(element.GetTaggedValue(), p.GetTaggedValue(), thisObjVal.GetTaggedValue());
338                 JSTaggedValue obj = JSFunction::Call(info);
339                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
340                 element.Update(obj);
341             }
342             bool shouldFlatten = false;
343             if (args.depth > 0) {
344                 shouldFlatten = element->IsArray(thread);
345                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
346             }
347             if (shouldFlatten) {
348                 tempArgs.depth = args.depth > POSITIVE_INFINITY ? POSITIVE_INFINITY : args.depth - 1;
349                 tempArgs.sourceLen = ArrayHelper::GetLength(thread, element);
350                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
351                 JSTaggedValue TargetIndexObj = FlattenIntoArray(thread, newArrayHandle, element, tempArgs,
352                                                                 thread->GlobalConstants()->GetHandledUndefined(),
353                                                                 thread->GlobalConstants()->GetHandledUndefined());
354                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
355                 targetIndexHandle.Update(TargetIndexObj);
356                 JSTaggedNumber targetIndexTemp = JSTaggedValue::ToNumber(thread, targetIndexHandle);
357                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
358                 tempArgs.start = base::NumberHelper::TruncateDouble(targetIndexTemp.GetNumber());
359             } else {
360                 if (tempArgs.start > base::MAX_SAFE_INTEGER) {
361                     THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
362                 }
363                 sourceIndexHandle.Update(JSTaggedValue(tempArgs.start));
364                 sourceIndexStr = JSTaggedValue::ToString(thread, sourceIndexHandle);
365                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
366                 targetIndexHandle.Update(sourceIndexStr.GetTaggedValue());
367                 JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, targetIndexHandle, element);
368                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
369                 tempArgs.start++;
370             }
371         }
372         sourceIndex++;
373     }
374     // 7. Return targetIndex.
375     return BuiltinsBase::GetTaggedDouble(tempArgs.start);
376 }
377 
SortIndexedProperties(JSThread * thread,const JSHandle<JSTaggedValue> & thisObj,int64_t len,const JSHandle<JSTaggedValue> & callbackFnHandle,HolesType holes)378 JSHandle<TaggedArray> ArrayHelper::SortIndexedProperties(JSThread *thread, const JSHandle<JSTaggedValue> &thisObj,
379                                                          int64_t len, const JSHandle<JSTaggedValue> &callbackFnHandle,
380                                                          HolesType holes)
381 {
382     // 1. Let items be a new empty List.
383     JSHandle<TaggedArray> items(thread->GetEcmaVM()->GetFactory()->EmptyArray());
384     CVector<JSHandle<JSTaggedValue>> itemsVector;
385     // 2. Let k be 0.
386     int64_t k = 0;
387     // 3. Repeat, while k < len,
388     //     a. Let Pk be ! ToString(��(k)).
389     //     b. If holes is skip-holes, then
390     //         i. Let kRead be ? HasProperty(obj, Pk).
391     //     c. Else,
392     //         i. Assert: holes is read-through-holes.
393     //         ii. Let kRead be true.
394     //     d. If kRead is true, then
395     //         i. Let kValue be ? Get(obj, Pk).
396     //         ii. Append kValue to items.
397     //     e. Set k to k + 1.
398     bool kRead = false;
399     JSMutableHandle<JSTaggedValue> pk(thread, JSTaggedValue::Undefined());
400 
401     while (k < len) {
402         if (holes == HolesType::SKIP_HOLES) {
403             pk.Update(JSTaggedValue(k));
404             kRead = JSTaggedValue::HasProperty(thread, thisObj, pk);
405             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, items);
406         } else {
407             ASSERT(holes == HolesType::READ_THROUGH_HOLES);
408             kRead = true;
409         }
410         if (kRead) {
411             JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObj, k);
412             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, items);
413             itemsVector.push_back(kValue);
414         }
415         ++k;
416     }
417     items = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(itemsVector.size());
418     for (size_t i = 0; i < itemsVector.size(); ++i) {
419         items->Set(thread, i, itemsVector[i].GetTaggedValue());
420     }
421     // 4. Sort items using an implementation-defined sequence of calls to SortCompare.
422     // If any such call returns an abrupt completion,
423     // stop before performing any further calls to SortCompare and return that Completion Record.
424     TimSort::Sort(thread, items, callbackFnHandle);
425     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, items);
426     // 5. Return items.
427     return items;
428 }
429 }  // namespace panda::ecmascript::base
430