arkcompiler_ets_runtime/ecmascript/base/array_helper.cpp
zha.wei 22ea93f87f description:合并冲突
Signed-off-by: zha.wei <zhawei@kaihong.com>
2023-08-14 11:59:07 +08:00

304 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/base/array_helper.h"
#include "ecmascript/base/typed_array_helper-inl.h"
#include "ecmascript/ecma_macros.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/global_env.h"
#include "ecmascript/interpreter/interpreter.h"
#include "ecmascript/js_array.h"
#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)
{
// 1. If Type(O) is not Object, return false.
if (!obj->IsECMAObject()) {
return false;
}
// 2. Let spreadable be Get(O, @@isConcatSpreadable).
auto ecmaVm = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
JSHandle<JSTaggedValue> isConcatsprKey = env->GetIsConcatSpreadableSymbol();
JSTaggedValue spreadable = ObjectFastOperator::FastGetPropertyByValue(thread, obj.GetTaggedValue(),
isConcatsprKey.GetTaggedValue());
// 3. ReturnIfAbrupt(spreadable).
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
// 4. If spreadable is not undefined, return ToBoolean(spreadable).
if (!spreadable.IsUndefined()) {
return spreadable.ToBoolean();
}
// 5. Return IsArray(O).
return obj->IsArray(thread);
}
// must use 'double' as return type, for sort result may double.
// let arr = [1,2,3,4,5,6]; arr.sort(() => Math.random() - 0.5);
double ArrayHelper::SortCompare(JSThread *thread, const JSHandle<JSTaggedValue> &callbackfnHandle,
const JSHandle<JSTaggedValue> &valueX, const JSHandle<JSTaggedValue> &valueY)
{
// 1. If x and y are both undefined, return +0.
if (valueX->IsHole()) {
if (valueY->IsHole()) {
return 0;
}
return 1;
}
if (valueY->IsHole()) {
return -1;
}
if (valueX->IsUndefined()) {
if (valueY->IsUndefined()) {
return 0;
}
// 2. If x is undefined, return 1.
return 1;
}
// 3. If y is undefined, return -1.
if (valueY->IsUndefined()) {
return -1;
}
// 4. If the argument comparefn is not undefined, then
// a. Let v be ToNumber(Call(comparefn, undefined, «x, y»)).
// b. ReturnIfAbrupt(v).
// c. If v is NaN, return +0.
// d. Return v.
if (!callbackfnHandle->IsUndefined() && !callbackfnHandle->IsNull()) {
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackfnHandle, undefined, undefined, 2); // 2: «x, y»
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
info->SetCallArg(valueX.GetTaggedValue(), valueY.GetTaggedValue());
JSTaggedValue callResult = JSFunction::Call(info);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
if (callResult.IsInt()) {
return callResult.GetInt();
}
JSHandle<JSTaggedValue> testResult(thread, callResult);
JSTaggedNumber v = JSTaggedValue::ToNumber(thread, testResult);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
double value = v.GetNumber();
if (std::isnan(value)) {
return +0;
}
return value;
}
// 5. Let xString be ToString(x).
// 6. ReturnIfAbrupt(xString).
// 7. Let yString be ToString(y).
// 8. ReturnIfAbrupt(yString).
// 9. If xString < yString, return -1.
// 10. If xString > yString, return 1.
// 11. Return +0.
JSHandle<JSTaggedValue> xValueHandle(JSTaggedValue::ToString(thread, valueX));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
JSHandle<JSTaggedValue> yValueHandle(JSTaggedValue::ToString(thread, valueY));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
ComparisonResult compareResult = JSTaggedValue::Compare(thread, xValueHandle, yValueHandle);
if (compareResult == ComparisonResult::GREAT) {
return 1;
}
if (compareResult == ComparisonResult::LESS) {
return -1;
}
return 0;
}
int64_t ArrayHelper::GetLength(JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle)
{
if (thisHandle->IsJSArray()) {
return JSArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength();
}
if (thisHandle->IsTypedArray()) {
return JSHandle<JSTypedArray>::Cast(thisHandle)->GetArrayLength();
}
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
JSHandle<JSTaggedValue> lenResult = JSTaggedValue::GetProperty(thread, thisHandle, lengthKey).GetValue();
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
JSTaggedNumber len = JSTaggedValue::ToLength(thread, lenResult);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
return len.GetNumber();
}
int64_t ArrayHelper::GetArrayLength(JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle)
{
if (thisHandle->IsJSArray()) {
return JSArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength();
}
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
JSHandle<JSTaggedValue> lenResult = JSTaggedValue::GetProperty(thread, thisHandle, lengthKey).GetValue();
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
JSTaggedNumber len = JSTaggedValue::ToLength(thread, lenResult);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
return len.GetNumber();
}
JSTaggedValue ArrayHelper::FlattenIntoArray(JSThread *thread, const JSHandle<JSObject> &newArrayHandle,
const JSHandle<JSTaggedValue> &thisObjVal, const FlattenArgs &args,
const JSHandle<JSTaggedValue> &mapperFunctionHandle,
const JSHandle<JSTaggedValue> &thisArg)
{
// 1. Assert: Type(target) is Object.
// 2. Assert: Type(source) is Object.
// 3. Assert: If mapperFunction is present, then ! IsCallable(mapperFunction) is true,
// thisArg is present, and depth is 1.
ASSERT(mapperFunctionHandle->IsUndefined() || mapperFunctionHandle->IsCallable() ||
(!thisArg->IsUndefined() && args.depth == 1));
// 4. Let targetIndex be start.
// 5. Let sourceIndex be +0!.
FlattenArgs tempArgs;
tempArgs.start = args.start;
int64_t sourceIndex = 0;
JSMutableHandle<JSTaggedValue> p(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> targetIndexHandle(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> sourceIndexHandle(thread, JSTaggedValue::Undefined());
JSHandle<EcmaString> sourceIndexStr;
// 6. Repeat, while (sourceIndex) < sourceLen,
// a. Let P be ! ToString(sourceIndex).
// b. Let exists be ? HasProperty(source, P).
// c. If exists is true, then
// i. Let element be ? Get(source, P).
// ii. If mapperFunction is present, then
// 1. Set element to ? Call(mapperFunction, thisArg, « element, sourceIndex, source »).
// iii. Let shouldFlatten be false.
// iv. If depth > 0, then
// 1. Set shouldFlatten to ? IsArray(element).
// v. If shouldFlatten is true, then
// 1. If depth is +∞, let newDepth be +∞.
// 2. Else, let newDepth be depth - 1.
// 3. Let elementLen be ? LengthOfArrayLike(element).
// 4. Set targetIndex to ? FlattenIntoArray(target, element, elementLen, targetIndex, newDepth).
// vi. Else,
// 1. If targetIndex ≥ 2^53 - 1, throw a TypeError exception.
// 2. Perform ? CreateDataPropertyOrThrow(target, ! ToString(!(targetIndex)), element).
// 3. Set targetIndex to targetIndex + 1.
// d. Set sourceIndex to sourceIndex + 1!.
while (sourceIndex < args.sourceLen) {
sourceIndexHandle.Update(JSTaggedValue(sourceIndex));
sourceIndexStr = JSTaggedValue::ToString(thread, sourceIndexHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
p.Update(sourceIndexStr.GetTaggedValue());
bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, p);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
element.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, p).GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!mapperFunctionHandle->IsUndefined()) {
const int32_t argsLength = 3; // 3: « element, sourceIndex, source »
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, mapperFunctionHandle, thisArg, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(element.GetTaggedValue(), p.GetTaggedValue(), thisObjVal.GetTaggedValue());
JSTaggedValue obj = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
element.Update(obj);
}
bool shouldFlatten = false;
if (args.depth > 0) {
shouldFlatten = element->IsArray(thread);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
if (shouldFlatten) {
tempArgs.depth = args.depth > POSITIVE_INFINITY ? POSITIVE_INFINITY : args.depth - 1;
tempArgs.sourceLen = ArrayHelper::GetLength(thread, element);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue TargetIndexObj = FlattenIntoArray(thread, newArrayHandle, element, tempArgs,
thread->GlobalConstants()->GetHandledUndefined(),
thread->GlobalConstants()->GetHandledUndefined());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
targetIndexHandle.Update(TargetIndexObj);
JSTaggedNumber targetIndexTemp = JSTaggedValue::ToNumber(thread, targetIndexHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
tempArgs.start = base::NumberHelper::TruncateDouble(targetIndexTemp.GetNumber());
} else {
if (tempArgs.start > base::MAX_SAFE_INTEGER) {
THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
}
sourceIndexHandle.Update(JSTaggedValue(tempArgs.start));
sourceIndexStr = JSTaggedValue::ToString(thread, sourceIndexHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
targetIndexHandle.Update(sourceIndexStr.GetTaggedValue());
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, targetIndexHandle, element);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
tempArgs.start++;
}
}
sourceIndex++;
}
// 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