!4493 ECMA Script 2023-支持Array.prototype.toSorted方法

Merge pull request !4493 from 查维/master
This commit is contained in:
openharmony_ci 2023-08-10 13:21:29 +00:00 committed by Gitee
commit f27f8bceae
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
10 changed files with 172 additions and 2 deletions

View File

@ -24,6 +24,7 @@
#include "ecmascript/js_hclass.h"
#include "ecmascript/js_tagged_number.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/object_fast_operator-inl.h"
namespace panda::ecmascript::base {
bool ArrayHelper::IsConcatSpreadable(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
@ -248,4 +249,54 @@ JSTaggedValue ArrayHelper::FlattenIntoArray(JSThread *thread, const JSHandle<JSO
// 7. Return targetIndex.
return BuiltinsBase::GetTaggedDouble(tempArgs.start);
}
JSTaggedValue ArrayHelper::SortIndexedProperties(JSThread *thread, const JSHandle<JSObject> &thisObj,
int64_t len, const JSHandle<JSTaggedValue> &callbackFnHandle,
HolesType holes)
{
// 1. Let items be a new empty List.
JSHandle<TaggedArray> items(thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len));
// 2. Let k be 0.
int64_t k = 0;
// 3. Repeat, while k < len,
// a. Let Pk be ! ToString(𝔽(k)).
// b. If holes is skip-holes, then
// i. Let kRead be ? HasProperty(obj, Pk).
// c. Else,
// i. Assert: holes is read-through-holes.
// ii. Let kRead be true.
// d. If kRead is true, then
// i. Let kValue be ? Get(obj, Pk).
// ii. Append kValue to items.
// e. Set k to k + 1.
bool kRead = false;
JSHandle<JSTaggedValue> thisObjVal(thisObj);
JSMutableHandle<JSTaggedValue> pk(thread, JSTaggedValue::Undefined());
while(k < len) {
if (holes == HolesType::SKIP_HOLES) {
pk.Update(JSTaggedValue(k));
kRead = JSTaggedValue::HasProperty(thread, thisObjVal, pk);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
ASSERT(holes == HolesType::READ_THROUGH_HOLES);
kRead = true;
}
if (kRead) {
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
items->Set(thread, k, kValue.GetTaggedValue());
}
++k;
}
JSHandle<JSArray> array(JSArray::CreateArrayFromList(thread, items));
JSHandle<JSObject> arrayObj = JSHandle<JSObject>::Cast(array);
// 4. Sort items using an implementation-defined sequence of calls to SortCompare.
// If any such call returns an abrupt completion,
// stop before performing any further calls to SortCompare and return that Completion Record.
JSArray::Sort(thread, arrayObj, callbackFnHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 5. Return items.
return arrayObj.GetTaggedValue();
}
} // namespace panda::ecmascript::base

View File

@ -26,6 +26,11 @@ struct FlattenArgs {
int64_t start = 0;
double depth = 0;
};
enum class HolesType {
SKIP_HOLES,
READ_THROUGH_HOLES,
};
class ArrayHelper {
public:
static bool IsConcatSpreadable(JSThread *thread, const JSHandle<JSTaggedValue> &obj);
@ -37,6 +42,9 @@ public:
const JSHandle<JSTaggedValue> &thisObjVal, const FlattenArgs &args,
const JSHandle<JSTaggedValue> &mapperFunctionHandle,
const JSHandle<JSTaggedValue> &thisArg);
static JSTaggedValue SortIndexedProperties(JSThread *thread, const JSHandle<JSObject> &thisObj,
int64_t len, const JSHandle<JSTaggedValue> &callbackFnHandle,
HolesType holes);
};
} // namespace panda::ecmascript::base

View File

@ -2129,6 +2129,7 @@ void Builtins::InitializeArray(const JSHandle<GlobalEnv> &env, const JSHandle<JS
SetFunction(env, arrFuncPrototype, "flat", BuiltinsArray::Flat, FunctionLength::ZERO);
SetFunction(env, arrFuncPrototype, "flatMap", BuiltinsArray::FlatMap, FunctionLength::ONE);
SetFunction(env, arrFuncPrototype, "at", BuiltinsArray::At, FunctionLength::ONE);
SetFunction(env, arrFuncPrototype, "toSorted", BuiltinsArray::ToSorted, FunctionLength::ONE);
SetFunction(env, arrFuncPrototype, "toSpliced", BuiltinsArray::ToSpliced, FunctionLength::TWO);
// %ArrayPrototype% [ @@iterator ]

View File

@ -2980,6 +2980,60 @@ JSTaggedValue BuiltinsArray::At(EcmaRuntimeCallInfo *argv)
return element.GetTaggedValue();
}
// 23.1.3.34 Array.prototype.toSorted ( comparefn )
JSTaggedValue BuiltinsArray::ToSorted(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Array, ToSorted);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
// 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception.
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Callable is false", JSTaggedValue::Exception());
}
// 2. Let obj be ToObject(this value).
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 3. Let len be ToLength(Get(obj, "length")).
int64_t len = ArrayHelper::GetArrayLength(thread, JSHandle<JSTaggedValue>(thisObjHandle));
// ReturnIfAbrupt(len).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 4. Let A be ? ArrayCreate(len).
JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast<double>(len))).GetTaggedValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSObject> newArrayHandle(thread, newArray);
// 5. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs
// the following steps when called:
// a. Return ? CompareArrayElements(x, y, comparefn).
// 6. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, read-through-holes).
JSTaggedValue sortedList = ArrayHelper::SortIndexedProperties(thread, thisObjHandle, len, callbackFnHandle,
base::HolesType::READ_THROUGH_HOLES);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> sortedArray(thread, sortedList);
//7. Let j be 0.
int64_t j = 0;
// 8. Repeat, while j < len,
// a. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(j)), sortedList[j]).
// b. Set j to j + 1.
while (j < len) {
JSHandle<JSTaggedValue> item = JSArray::FastGetPropertyByValue(thread, sortedArray, j);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, j, item);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
++j;
}
// 9. Return A.
return newArrayHandle.GetTaggedValue();
}
// 23.1.3.35 Array.prototype.toSpliced ( start, skipCount, ...items )
JSTaggedValue BuiltinsArray::ToSpliced(EcmaRuntimeCallInfo *argv)
{

View File

@ -103,6 +103,8 @@ public:
static JSTaggedValue FlatMap(EcmaRuntimeCallInfo *argv);
// 23.1.3.1 Array.prototype.at ( index )
static JSTaggedValue At(EcmaRuntimeCallInfo *argv);
// 23.1.3.34 Array.prototype.toSorted ( comparefn )
static JSTaggedValue ToSorted(EcmaRuntimeCallInfo *argv);
// 23.1.3.11
static JSTaggedValue FindLast(EcmaRuntimeCallInfo *argv);
// 23.1.3.12
@ -112,4 +114,4 @@ public:
};
} // namespace panda::ecmascript::builtins
#endif // ECMASCRIPT_BUILTINS_BUILTINS_ARRAY_H
#endif // ECMASCRIPT_BUILTINS_BUILTINS_ARRAY_H

View File

@ -38,6 +38,8 @@ using namespace panda::ecmascript::base;
constexpr int32_t INT_VALUE_0 = 0;
constexpr int32_t INT_VALUE_1 = 1;
constexpr int32_t INT_VALUE_2 = 2;
constexpr int32_t INT_VALUE_3 = 3;
constexpr uint32_t RUNTIME_CALL_INFO_PARA_NUM_4 = 4;
constexpr int32_t INT_VALUE_666 = 666;
constexpr uint32_t RUNTIME_CALL_INFO_PARA_NUM_10 = 10;
@ -1736,6 +1738,42 @@ HWTEST_F_L0(BuiltinsArrayTest, At)
ASSERT_EQ(result, JSTaggedValue::Undefined());
}
HWTEST_F_L0(BuiltinsArrayTest, ToSorted)
{
JSHandle<JSTaggedValue> lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString();
JSArray *arr =
JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_0)).GetTaggedValue().GetTaggedObject());
EXPECT_TRUE(arr != nullptr);
JSHandle<JSObject> obj(thread, arr);
EXPECT_EQ(JSArray::GetProperty(thread, JSHandle<JSTaggedValue>(obj),
lengthKeyHandle).GetValue()->GetInt(), INT_VALUE_0);
JSHandle<JSTaggedValue> key0(thread, JSTaggedValue(INT_VALUE_0));
PropertyDescriptor desc0(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue(INT_VALUE_3)), true, true, true);
JSArray::DefineOwnProperty(thread, obj, key0, desc0);
JSHandle<JSTaggedValue> key1(thread, JSTaggedValue(INT_VALUE_1));
PropertyDescriptor desc1(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue(INT_VALUE_2)), true, true, true);
JSArray::DefineOwnProperty(thread, obj, key1, desc1);
JSHandle<JSTaggedValue> key2(thread, JSTaggedValue(INT_VALUE_2));
PropertyDescriptor desc2(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue(INT_VALUE_1)), true, true, true);
JSArray::DefineOwnProperty(thread, obj, key2, desc2);
auto ecmaRuntimeCallInfo1 =
TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), RUNTIME_CALL_INFO_PARA_NUM_4);
ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
JSTaggedValue result2 = Array::ToSorted(ecmaRuntimeCallInfo1);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result2.IsECMAObject());
JSHandle<JSTaggedValue> resultArr =
JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<JSTaggedType>(result2.GetRawData())));
EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key0).GetValue()->GetInt(), INT_VALUE_1);
EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key1).GetValue()->GetInt(), INT_VALUE_2);
EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key2).GetValue()->GetInt(), INT_VALUE_3);
}
HWTEST_F_L0(BuiltinsArrayTest, ToSpliced)
{
JSHandle<JSTaggedValue> lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString();

View File

@ -272,6 +272,7 @@ namespace panda::ecmascript {
V(Array, Flat) \
V(Array, FlatMap) \
V(Array, At) \
V(Array, ToSorted) \
V(Array, ToSpliced) \
V(ArrayBuffer, Constructor) \
V(ArrayBuffer, Slice) \

View File

@ -420,6 +420,7 @@ static uintptr_t g_nativeTable[] = {
reinterpret_cast<uintptr_t>(BuiltinsArray::Flat),
reinterpret_cast<uintptr_t>(BuiltinsArray::FlatMap),
reinterpret_cast<uintptr_t>(BuiltinsArray::At),
reinterpret_cast<uintptr_t>(BuiltinsArray::ToSorted),
reinterpret_cast<uintptr_t>(BuiltinsArray::ToSpliced),
reinterpret_cast<uintptr_t>(BuiltinsTypedArray::TypedArrayBaseConstructor),
reinterpret_cast<uintptr_t>(BuiltinsTypedArray::CopyWithin),

View File

@ -142,4 +142,14 @@ print(arr21.at(5));
print(arr21.at(-1));
print(arr21.at(6));
print(arr21.at('1.9'));
print(arr21.at(true));
print(arr21.at(true));
const months = ["Mar", "Jan", "Feb", "Dec"];
const sortedMonths = months.toSorted();
print(sortedMonths); // ['Dec', 'Feb', 'Jan', 'Mar']
print(months); // ['Mar', 'Jan', 'Feb', 'Dec']
const values = [1, 10, 21, 2];
const sortedValues = values.toSorted((a, b) => {return a- b});
print(sortedValues); // [1, 2, 10, 21]
print(values); // [1, 10, 21, 2]

View File

@ -75,3 +75,7 @@ undefined
undefined
2
2
Dec,Feb,Jan,Mar
Mar,Jan,Feb,Dec
1,2,10,21
1,10,21,2