* Copyright (c) 2021-2024 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/builtins/builtins_array.h"
#include "ecmascript/base/typed_array_helper-inl.h"
#include "ecmascript/interpreter/interpreter.h"
#include "ecmascript/js_map_iterator.h"
#include "ecmascript/js_stable_array.h"
#include "ecmascript/object_fast_operator-inl.h"
#include "ecmascript/builtins/builtins_string.h"
namespace panda::ecmascript::builtins {
using ArrayHelper = base::ArrayHelper;
using TypedArrayHelper = base::TypedArrayHelper;
const CString STRING_SEPERATOR = ",";
JSTaggedValue BuiltinsArray::ArrayConstructor(EcmaRuntimeCallInfo *argv)
{
BUILTINS_ENTRY_DEBUG_LOG();
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Constructor);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
uint32_t argc = argv->GetArgsNumber();
JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
if (newTarget->IsUndefined()) {
newTarget = constructor;
}
if (argc == 0) {
auto arrayHandle = JSArray::ArrayCreate(thread, JSTaggedNumber(0), newTarget);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
auto newArrayHandle = JSHandle<JSObject>::Cast(arrayHandle);
return newArrayHandle.GetTaggedValue();
}
if (argc == 1) {
JSHandle<JSObject> newArrayHandle(JSArray::ArrayCreate(thread, JSTaggedNumber(0), newTarget));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> len = GetCallArg(argv, 0);
uint32_t newLen = 0;
if (!len->IsNumber()) {
JSHandle<JSTaggedValue> key0 = thread->GlobalConstants()->GetHandledZeroString();
JSObject::CreateDataProperty(thread, newArrayHandle, key0, len);
newLen = 1;
} else {
newLen = JSTaggedValue::ToUint32(thread, len);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (JSTaggedNumber(len.GetTaggedValue()).GetNumber() != newLen) {
THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid array length", JSTaggedValue::Exception());
}
}
JSArray::SetCapacity(thread, newArrayHandle, 0, newLen, true);
return newArrayHandle.GetTaggedValue();
}
if (argc < JSObject::MAX_GAP) {
#if ECMASCRIPT_ENABLE_ELEMENTSKIND_ALWAY_GENERIC
ElementsKind newKind = ElementsKind::GENERIC;
#else
auto arrayFunc = thread->GetEcmaVM()->GetGlobalEnv()->GetArrayFunction();
ElementsKind newKind = newTarget.GetTaggedValue() == arrayFunc.GetTaggedValue() ?
ElementsKind::HOLE : ElementsKind::NONE;
#endif
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
auto elements = factory->NewTaggedArray(argc, JSTaggedValue::Undefined());
for (uint32_t k = 0; k < argc; k++) {
auto value = GetCallArg(argv, k);
newKind = Elements::ToElementsKind(value.GetTaggedValue(), newKind);
if (value->IsHole()) {
continue;
}
elements->Set(thread, k, value);
}
auto newArray = JSArray::CreateArrayFromList(thread, newTarget, elements);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (thread->IsEnableElementsKind()) {
JSHClass::TransitToElementsKind(thread, newArray, newKind);
}
return newArray.GetTaggedValue();
}
JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(argc), newTarget).GetTaggedValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!newArray.IsArray(thread)) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to create array.", JSTaggedValue::Exception());
}
JSHandle<JSObject> newArrayHandle(thread, newArray);
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> itemK(thread, JSTaggedValue::Undefined());
for (uint32_t k = 0; k < argc; k++) {
key.Update(JSTaggedValue(k));
itemK.Update(GetCallArg(argv, k));
if (itemK.GetTaggedValue().IsHole()) {
itemK.Update(JSTaggedValue::Undefined());
}
JSObject::CreateDataProperty(thread, newArrayHandle, key, itemK);
}
JSArray::Cast(*newArrayHandle)->SetArrayLength(thread, argc);
return newArrayHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::From(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, From);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
bool mapping = false;
JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, INDEX_TWO);
JSHandle<JSTaggedValue> mapfn = GetCallArg(argv, 1);
if (!mapfn->IsUndefined()) {
if (!mapfn->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the mapfn is not callable.", JSTaggedValue::Exception());
}
mapping = true;
}
JSHandle<JSTaggedValue> items = GetCallArg(argv, 0);
if (items->IsNull()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "The items is null.", JSTaggedValue::Exception());
}
if (!mapping && items->IsString()) {
JSHandle<EcmaString> strItems(items);
return BuiltinsString::StringToList(thread, strItems);
}
if (!mapping && items->IsTypedArray()) {
JSHandle<JSTypedArray> arrayItems(items);
return BuiltinsArrayBuffer::TypedArrayToList(thread, arrayItems);
}
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> iteratorSymbol = thread->GlobalConstants()->GetHandledIteratorSymbol();
JSHandle<JSTaggedValue> usingIterator = JSObject::GetMethod(thread, items, iteratorSymbol);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
if (!usingIterator->IsUndefined()) {
JSHandle<JSTaggedValue> iterator(thread, JSTaggedValue::Hole());
if (!mapping && items->IsJSMapIterator()) {
iterator = JSIterator::GetIterator(thread, items, usingIterator);
if (iterator->IsJSMapIterator()) {
return JSMapIterator::MapIteratorToList(thread, iterator);
}
}
JSTaggedValue newArray;
if (thisHandle == env->GetArrayFunction() || !thisHandle->IsConstructor()) {
newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast<double>(0))).GetTaggedValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, thisHandle, undefined, undefined, 0);
newArray = JSFunction::Construct(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
if (!newArray.IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to construct the array.", JSTaggedValue::Exception());
}
JSHandle<JSObject> newArrayHandle(thread, newArray);
if (iterator->IsHole()) {
iterator = JSIterator::GetIterator(thread, items, usingIterator);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
int k = 0;
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> mapValue(thread, JSTaggedValue::Undefined());
if (newArrayHandle->IsJSArray() && items->IsJSArray() && iterator->IsJSArrayIterator()) {
JSHandle<JSObject> arrayLikeObj = JSTaggedValue::ToObject(thread, items);
JSHandle<JSTaggedValue> arrayLike(arrayLikeObj) ;
int64_t len = ArrayHelper::GetArrayLength(thread, arrayLike);
while (k < len) {
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, arrayLike, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (mapping) {
const uint32_t argsLength = 2;
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, mapfn, thisArgHandle, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k));
JSTaggedValue callResult = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
mapValue.Update(callResult);
} else {
mapValue.Update(kValue.GetTaggedValue());
}
JSArray::TryFastCreateDataProperty(thread, newArrayHandle, k, mapValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
k++;
len = ArrayHelper::GetArrayLength(thread, arrayLike);
thread->CheckSafepointIfSuspended();
}
return newArrayHandle.GetTaggedValue();
}
while (true) {
key.Update(JSTaggedValue(k));
JSHandle<JSTaggedValue> next = JSIterator::IteratorStep(thread, iterator);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (next->IsFalse()) {
JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, key, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return newArrayHandle.GetTaggedValue();
}
JSHandle<JSTaggedValue> nextValue = JSIterator::IteratorValue(thread, next);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (mapping) {
const uint32_t argsLength = 2;
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, mapfn, thisArgHandle, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(nextValue.GetTaggedValue(), key.GetTaggedValue());
JSTaggedValue callResult = JSFunction::Call(info);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
JSIterator::IteratorClose(thread, iterator, mapValue).GetTaggedValue());
mapValue.Update(callResult);
} else {
mapValue.Update(nextValue.GetTaggedValue());
}
bool createRes = newArrayHandle->IsJSArray() ?
JSArray::TryFastCreateDataProperty(thread, newArrayHandle, k, mapValue) :
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, key, mapValue);
JSHandle<JSTaggedValue> defineStatus(thread, JSTaggedValue(createRes));
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
JSIterator::IteratorClose(thread, iterator, defineStatus).GetTaggedValue());
k++;
}
}
JSHandle<JSObject> arrayLikeObj = JSTaggedValue::ToObject(thread, items);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> arrayLike(arrayLikeObj);
int64_t len = ArrayHelper::GetArrayLength(thread, arrayLike);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue newArray;
if (thisHandle == env->GetArrayFunction() || !thisHandle->IsConstructor()) {
newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast<double>(len))).GetTaggedValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, thisHandle, undefined, undefined, 1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(JSTaggedValue(len));
newArray = JSFunction::Construct(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
if (!newArray.IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to construct the array.", JSTaggedValue::Exception());
}
JSHandle<JSObject> newArrayHandle(thread, newArray);
JSMutableHandle<JSTaggedValue> mapValue(thread, JSTaggedValue::Undefined());
int64_t k = 0;
while (k < len) {
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, arrayLike, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (mapping) {
const uint32_t argsLength = 2;
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, mapfn, thisArgHandle, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k));
JSTaggedValue callResult = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
mapValue.Update(callResult);
} else {
mapValue.Update(kValue.GetTaggedValue());
}
JSArray::TryFastCreateDataProperty(thread, newArrayHandle, k, mapValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
k++;
thread->CheckSafepointIfSuspended();
}
JSHandle<JSTaggedValue> lenHandle(thread, JSTaggedValue(len));
JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, lenHandle, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return newArrayHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::IsArray(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, IsArray);
if (GetCallArg(argv, 0)->IsArray(argv->GetThread())) {
return GetTaggedBoolean(true);
}
return GetTaggedBoolean(false);
}
JSTaggedValue BuiltinsArray::Of(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Of);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> lengthKey = globalConst->GetHandledLengthString();
uint32_t argc = argv->GetArgsNumber();
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSTaggedValue> newArray;
if (thisHandle->IsConstructor()) {
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, thisHandle, undefined, undefined, 1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(JSTaggedValue(argc));
JSTaggedValue taggedArray = JSFunction::Construct(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
newArray = JSHandle<JSTaggedValue>(thread, taggedArray);
} else {
newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(argc));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
if (!newArray->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to create Object.", JSTaggedValue::Exception());
}
JSHandle<JSObject> newArrayHandle(newArray);
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
for (uint32_t k = 0; k < argc; k++) {
key.Update(JSTaggedValue(k));
JSHandle<JSTaggedValue> kValue = GetCallArg(argv, k);
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, key, kValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
JSHandle<JSTaggedValue> lenHandle(thread, JSTaggedValue(argc));
JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, lenHandle, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return newArrayHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Species(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Species);
return GetThis(argv).GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Concat(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Concat);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
int argc = static_cast<int>(argv->GetArgsNumber());
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
uint32_t arrayLen = 0;
JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(arrayLen));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!(newArray.IsECMAObject() || newArray.IsUndefined())) {
THROW_TYPE_ERROR_AND_RETURN(thread, "array must be object or undefined.", JSTaggedValue::Exception());
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSObject> newArrayHandle(thread, newArray);
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
int64_t n = 0;
JSMutableHandle<JSTaggedValue> ele(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> valHandle(thread, JSTaggedValue::Undefined());
for (int i = -1; i < argc; i++) {
if (i < 0) {
ele.Update(thisObjHandle.GetTaggedValue());
} else {
ele.Update(GetCallArg(argv, i));
}
bool isSpreadable = ArrayHelper::IsConcatSpreadable(thread, ele);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (isSpreadable) {
int64_t len = ArrayHelper::GetArrayLength(thread, ele);
int64_t k = 0;
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (n + len > base::MAX_SAFE_INTEGER) {
THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
}
if (ele->IsStableJSArray(thread)) {
JSStableArray::Concat(thread, newArrayHandle, JSHandle<JSObject>::Cast(ele), k, n);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else if (JSArray::IsProtoNotModifiedDictionaryJSArray(thread, JSHandle<JSObject>::Cast(ele))) {
JSArray::FastConcatDictionaryArray(thread, JSHandle<JSObject>::Cast(ele), newArrayHandle,
valHandle, toKey, n);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
continue;
}
while (k < len) {
fromKey.Update(JSTaggedValue::ToString(thread, JSTaggedValue(k)));
toKey.Update(JSTaggedValue(n));
bool exists = JSTaggedValue::HasProperty(thread, ele, fromKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
JSHandle<JSTaggedValue> fromValHandle =
JSArray::FastGetPropertyByValue(thread, ele, fromKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
n++;
k++;
thread->CheckSafepointIfSuspended();
}
} else {
if (n >= base::MAX_SAFE_INTEGER) {
THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
}
toKey.Update(JSTaggedValue(n));
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, ele);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
n++;
}
}
JSHandle<JSTaggedValue> lenHandle(thread, JSTaggedValue(n));
JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, lenHandle, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return newArrayHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::CopyWithin(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, CopyWithin);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, GetThis(argv));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int64_t copyTo = 0;
int64_t copyFrom = 0;
int64_t copyEnd = len;
JSTaggedNumber targetTemp = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
double target = targetTemp.GetNumber();
if (target < 0) {
copyTo = target + len > 0 ? target + len : 0;
} else {
copyTo = target < len ? target : len;
}
JSTaggedNumber start_t = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 1));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
double start = start_t.GetNumber();
if (start < 0) {
copyFrom = start + len > 0 ? start + len : 0;
} else {
copyFrom = start < len ? start : len;
}
double end = len;
JSHandle<JSTaggedValue> msg3 = GetCallArg(argv, INDEX_TWO);
if (!msg3->IsUndefined()) {
JSTaggedNumber temp = JSTaggedValue::ToInteger(thread, msg3);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
end = temp.GetNumber();
}
if (end < 0) {
copyEnd = end + len > 0 ? end + len : 0;
} else {
copyEnd = end < len ? end : len;
}
int64_t count = (copyEnd - copyFrom < len - copyTo) ? (copyEnd - copyFrom) : (len - copyTo);
int64_t direction = 1;
if (copyFrom < copyTo && copyTo < copyFrom + count) {
direction = -1;
copyFrom = copyFrom + count - 1;
copyTo = copyTo + count - 1;
}
JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
while (count > 0) {
fromKey.Update(JSTaggedValue(copyFrom));
toKey.Update(JSTaggedValue(copyTo));
bool exists = (thisObjVal->IsTypedArray() || thisObjVal->IsSharedTypedArray() ||
JSTaggedValue::HasProperty(thread, thisObjVal, fromKey));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
JSHandle<JSTaggedValue> fromValHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
if (thisObjVal->IsJSProxy()) {
toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
copyFrom = copyFrom + direction;
copyTo = copyTo + direction;
count--;
}
return thisObjHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Entries(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Entries);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSObject> self = JSTaggedValue::ToObject(thread, GetThis(argv));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSArrayIterator> iter(factory->NewJSArrayIterator(self, IterationKind::KEY_AND_VALUE));
return iter.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Every(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Array, Every);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
uint64_t len = static_cast<uint64_t>(ArrayHelper::GetArrayLength(thread, thisObjVal));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
uint32_t k = 0;
JSTaggedValue callResult = GetTaggedBoolean(true);
if (thisObjVal->IsStableJSArray(thread)) {
callResult = JSStableArray::HandleEveryOfStable(thread, thisObjHandle, callbackFnHandle, thisArgHandle, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!callResult.ToBoolean()) {
return GetTaggedBoolean(false);
}
}
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
const uint32_t argsLength = 3;
while (k < len) {
bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
key.Update(JSTaggedValue(k));
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
callResult = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!callResult.ToBoolean()) {
return GetTaggedBoolean(false);
}
}
k++;
thread->CheckSafepointIfSuspended();
}
return GetTaggedBoolean(true);
}
JSTaggedValue BuiltinsArray::Fill(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Fill);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
if (thisObjVal->IsJSArray()) {
bool isDictionary = thisObjHandle->GetJSHClass()->IsDictionaryElement();
if (isDictionary) {
uint32_t length = JSArray::Cast(*thisObjHandle)->GetLength();
uint32_t size = thisObjHandle->GetNumberOfElements(thread);
if (length - size > JSObject::MAX_GAP) {
JSObject::TryOptimizeAsFastElements(thread, thisObjHandle);
}
}
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> startArg = GetCallArg(argv, 1);
JSTaggedNumber argStartTemp = JSTaggedValue::ToInteger(thread, startArg);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
double argStart = argStartTemp.GetNumber();
int64_t start = 0;
if (argStart < 0) {
double tempStart = argStart + len;
start = tempStart > 0 ? tempStart : 0;
} else {
start = argStart < len ? argStart : len;
}
double argEnd = len;
JSHandle<JSTaggedValue> endArg = GetCallArg(argv, INDEX_TWO);
if (!endArg->IsUndefined()) {
JSTaggedNumber argEndTemp = JSTaggedValue::ToInteger(thread, endArg);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
argEnd = argEndTemp.GetNumber();
}
int64_t end = len;
if (argEnd < 0) {
double tempEnd = argEnd + len;
end = tempEnd > 0 ? tempEnd : 0;
} else {
end = argEnd < len ? argEnd : len;
}
if (start < end) {
thread->GetEcmaVM()->GetGlobalEnv()->NotifyArrayPrototypeChangedGuardians(thread, thisObjHandle);
}
if (thisObjVal->IsStableJSArray(thread)) {
return JSStableArray::Fill(thread, thisObjHandle, value, start, end);
}
int64_t k = start;
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
while (k < end) {
key.Update(JSTaggedValue(k));
JSArray::FastSetPropertyByValue(thread, thisObjVal, key, value);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
k++;
}
return thisObjHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::FilterUnStableJSArray(JSThread *thread, JSHandle<JSTaggedValue> &thisArgHandle,
JSHandle<JSTaggedValue> &thisObjVal, int64_t k, int64_t len, uint32_t toIndex, JSHandle<JSObject> newArrayHandle,
JSHandle<JSTaggedValue> &callbackFnHandle)
{
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
const uint32_t argsLength = 3;
JSTaggedValue callResult = GetTaggedBoolean(true);
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> toIndexHandle(thread, JSTaggedValue::Undefined());
while (k < len) {
bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
key.Update(JSTaggedValue(k));
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
callResult = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (callResult.ToBoolean()) {
toIndexHandle.Update(JSTaggedValue(toIndex));
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toIndexHandle, kValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
toIndex++;
}
}
k++;
thread->CheckSafepointIfSuspended();
}
return newArrayHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Filter(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Array, Filter);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
uint64_t len = static_cast<uint64_t>(ArrayHelper::GetArrayLength(thread, thisObjVal));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
int32_t arrayLen = 0;
JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(arrayLen));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSObject> newArrayHandle(thread, newArray);
uint32_t toIndex = 0;
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> toIndexHandle(thread, JSTaggedValue::Undefined());
uint32_t k = 0;
if (thisObjVal->IsStableJSArray(thread)) {
JSStableArray::Filter(newArrayHandle, thisObjHandle, argv, k, toIndex);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
return FilterUnStableJSArray(thread, thisArgHandle, thisObjVal, k, len, toIndex, newArrayHandle, callbackFnHandle);
}
JSTaggedValue BuiltinsArray::Find(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Find);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the predicate is not callable.", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
int64_t k = 0;
while (k < len) {
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
key.Update(JSTaggedValue(k));
const uint32_t argsLength = 3;
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
JSTaggedValue callResult = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (callResult.ToBoolean()) {
return kValue.GetTaggedValue();
}
k++;
}
return JSTaggedValue::Undefined();
}
JSTaggedValue BuiltinsArray::FindIndex(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, FindIndex);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
uint64_t len = static_cast<uint64_t>(ArrayHelper::GetLength(thread, thisObjVal));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the predicate is not callable.", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
uint32_t k = 0;
JSTaggedValue callResult = GetTaggedBoolean(true);
if (thisObjVal->IsStableJSArray(thread)) {
callResult = JSStableArray::HandleFindIndexOfStable(thread, thisObjHandle, callbackFnHandle, thisArgHandle, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (callResult.ToBoolean()) {
return GetTaggedDouble(k);
}
}
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
const uint32_t argsLength = 3;
while (k < len) {
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
key.Update(JSTaggedValue(k));
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
callResult = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (callResult.ToBoolean()) {
return GetTaggedDouble(k);
}
k++;
thread->CheckSafepointIfSuspended();
}
return GetTaggedDouble(-1);
}
JSTaggedValue BuiltinsArray::ForEach(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Array, ForEach);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
uint64_t len = static_cast<uint64_t>(ArrayHelper::GetArrayLength(thread, thisObjVal));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
uint32_t k = 0;
if (thisObjVal->IsStableJSArray(thread)) {
JSStableArray::HandleforEachOfStable(thread, thisObjHandle, callbackFnHandle, thisArgHandle, len, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
const uint32_t argsLength = 3;
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
while (k < len) {
bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
key.Update(JSTaggedValue(k));
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
JSTaggedValue funcResult = JSFunction::Call(info);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
}
k++;
thread->CheckSafepointIfSuspended();
}
return JSTaggedValue::Undefined();
}
JSTaggedValue BuiltinsArray::IndexOfStable(EcmaRuntimeCallInfo *argv, const JSHandle<JSTaggedValue> &thisHandle)
{
int64_t length = JSHandle<JSArray>::Cast(thisHandle)->GetArrayLength();
if (length == 0) {
return JSTaggedValue(-1);
}
JSThread* thread = argv->GetThread();
int64_t fromIndex = 0;
uint32_t argc = argv->GetArgsNumber();
if (UNLIKELY(argc >= 2)) {
JSHandle<JSTaggedValue> fromIndexHandle = argv->GetCallArg(1);
fromIndex = ArrayHelper::GetStartIndex(thread, fromIndexHandle, length);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (UNLIKELY(fromIndexHandle->IsECMAObject())) {
return IndexOfSlowPath(argv, thisHandle, length, fromIndex);
}
}
if (fromIndex >= length) {
return JSTaggedValue(-1);
}
if (UNLIKELY(!thisHandle->IsECMAObject())) {
return IndexOfSlowPath(argv, thisHandle, length, fromIndex);
}
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
return JSStableArray::IndexOf(
thread, thisHandle, target, static_cast<uint32_t>(fromIndex), static_cast<uint32_t>(length));
}
JSTaggedValue BuiltinsArray::IndexOfSlowPath(EcmaRuntimeCallInfo *argv, const JSHandle<JSTaggedValue> &thisHandle)
{
JSThread* thread = argv->GetThread();
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t length = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (length == 0) {
return JSTaggedValue(-1);
}
int64_t fromIndex = ArrayHelper::GetStartIndexFromArgs(thread, argv, 1, length);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return IndexOfSlowPath(argv, thisObjVal, length, fromIndex);
}
JSTaggedValue BuiltinsArray::IndexOfSlowPath(
EcmaRuntimeCallInfo *argv, const JSHandle<JSTaggedValue> &thisObjVal, int64_t length, int64_t fromIndex)
{
if (fromIndex >= length) {
return JSTaggedValue(-1);
}
JSThread* thread = argv->GetThread();
JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
for (int64_t curIndex = fromIndex; curIndex < length; ++curIndex) {
keyHandle.Update(JSTaggedValue(curIndex));
bool found = ArrayHelper::ElementIsStrictEqualTo(thread, thisObjVal, keyHandle, target);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (UNLIKELY(found)) {
return JSTaggedValue(curIndex);
}
thread->CheckSafepointIfSuspended();
}
return JSTaggedValue(-1);
}
JSTaggedValue BuiltinsArray::IndexOf(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Array, IndexOf);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
if (thisHandle->IsStableJSArray(thread) && !thisHandle->IsMutantTaggedArray()) {
return IndexOfStable(argv, thisHandle);
}
return IndexOfSlowPath(argv, thisHandle);
}
JSTaggedValue BuiltinsArray::Join(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Join);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
if (thisHandle->IsStableJSArray(thread)) {
return JSStableArray::Join(thisHandle, argv);
}
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
if (len > UINT32_MAX) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Invalid array length", JSTaggedValue::Exception());
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> sepHandle;
if ((GetCallArg(argv, 0)->IsUndefined())) {
sepHandle = thread->GlobalConstants()->GetHandledCommaString();
} else {
sepHandle = GetCallArg(argv, 0);
}
JSHandle<EcmaString> sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
uint32_t allocateLength = 0;
uint32_t sepLength = EcmaStringAccessor(sepStringHandle).GetLength();
if (len > 0) {
allocateLength = sepLength * (len - 1) + len;
}
if (allocateLength > BaseString::MAX_STRING_LENGTH) {
THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
std::u16string sepStr = EcmaStringAccessor(sepStringHandle).ToU16String(thread);
if (len == 0 || !ArrayJoinStack::Push(thread, thisObjVal)) {
const auto* globalConst = thread->GlobalConstants();
return globalConst->GetEmptyString();
}
std::u16string concatStr;
uint32_t length = static_cast<uint32_t>(len);
for (uint32_t k = 0; k < length; k++) {
if (k > 0) {
concatStr.append(sepStr);
}
JSHandle<JSTaggedValue> element = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
if (!element->IsUndefined() && !element->IsNull()) {
JSHandle<EcmaString> nextStringHandle = JSTaggedValue::ToString(thread, element);
RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
concatStr.append(EcmaStringAccessor(nextStringHandle).ToU16String(thread));
}
if (concatStr.size() > BaseString::MAX_STRING_LENGTH) {
ArrayJoinStack::Pop(thread, thisHandle);
THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
}
thread->CheckSafepointIfSuspended();
}
auto *uint16tData = reinterpret_cast<uint16_t *>(concatStr.data());
uint32_t u16strSize = concatStr.size();
ArrayJoinStack::Pop(thread, thisHandle);
auto* factory = thread->GetEcmaVM()->GetFactory();
return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Keys(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Keys);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSObject> self = JSTaggedValue::ToObject(thread, GetThis(argv));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSArrayIterator> iter(factory->NewJSArrayIterator(self, IterationKind::KEY));
return iter.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::LastIndexOfStable(EcmaRuntimeCallInfo *argv, const JSHandle<JSTaggedValue> &thisHandle)
{
int64_t length = JSHandle<JSArray>::Cast(thisHandle)->GetArrayLength();
if (length == 0) {
return JSTaggedValue(-1);
}
JSThread* thread = argv->GetThread();
int64_t fromIndex = length - 1;
uint32_t argc = argv->GetArgsNumber();
if (UNLIKELY(argc >= 2)) {
JSHandle<JSTaggedValue> fromIndexHandle = argv->GetCallArg(1);
fromIndex = ArrayHelper::GetLastStartIndex(thread, fromIndexHandle, length);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (UNLIKELY(fromIndexHandle->IsECMAObject())) {
return LastIndexOfSlowPath(argv, thisHandle, fromIndex);
}
}
if (fromIndex < 0) {
return JSTaggedValue(-1);
}
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
return JSStableArray::LastIndexOf(
thread, thisHandle, target, static_cast<uint32_t>(fromIndex), static_cast<uint32_t>(length));
}
JSTaggedValue BuiltinsArray::LastIndexOfSlowPath(EcmaRuntimeCallInfo *argv, const JSHandle<JSTaggedValue> &thisHandle)
{
JSThread* thread = argv->GetThread();
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t length = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (length == 0) {
return JSTaggedValue(-1);
}
int64_t fromIndex = ArrayHelper::GetLastStartIndexFromArgs(thread, argv, 1, length);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return LastIndexOfSlowPath(argv, thisObjVal, fromIndex);
}
JSTaggedValue BuiltinsArray::LastIndexOfSlowPath(
EcmaRuntimeCallInfo *argv, const JSHandle<JSTaggedValue> &thisObjVal, int64_t fromIndex)
{
if (fromIndex < 0) {
return JSTaggedValue(-1);
}
JSThread* thread = argv->GetThread();
JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
JSHandle<JSTaggedValue> target = base::BuiltinsBase::GetCallArg(argv, 0);
for (int64_t curIndex = fromIndex; curIndex >= 0; --curIndex) {
keyHandle.Update(JSTaggedValue(curIndex));
bool found = ArrayHelper::ElementIsStrictEqualTo(thread, thisObjVal, keyHandle, target);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (UNLIKELY(found)) {
return JSTaggedValue(curIndex);
}
}
return JSTaggedValue(-1);
}
JSTaggedValue BuiltinsArray::LastIndexOf(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, IndexOf);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
if (thisHandle->IsStableJSArray(thread) && !thisHandle->IsMutantTaggedArray()) {
return LastIndexOfStable(argv, thisHandle);
}
return LastIndexOfSlowPath(argv, thisHandle);
}
JSTaggedValue BuiltinsArray::Map(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Map);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t rawLen = ArrayHelper::GetArrayLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
JSTaggedValue newArray =
JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(static_cast<double>(rawLen)));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!newArray.IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to create Object.", JSTaggedValue::Exception());
}
JSHandle<JSObject> newArrayHandle(thread, newArray);
uint32_t k = 0;
uint32_t len = static_cast<uint32_t>(rawLen);
if (thisObjVal->IsStableJSArray(thread)) {
JSStableArray::Map(newArrayHandle, thisObjHandle, argv, k, len);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
return MapUnStableJSArray(thread, thisArgHandle, thisObjVal, k, len, newArrayHandle, callbackFnHandle);
}
JSTaggedValue BuiltinsArray::MapUnStableJSArray(JSThread *thread, JSHandle<JSTaggedValue> &thisArgHandle,
JSHandle<JSTaggedValue> &thisObjVal, int64_t k, int64_t len, JSHandle<JSObject> newArrayHandle,
JSHandle<JSTaggedValue> &callbackFnHandle)
{
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> mapResultHandle(thread, JSTaggedValue::Undefined());
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
const uint32_t argsLength = 3;
while (k < len) {
bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
key.Update(JSTaggedValue(k));
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
JSTaggedValue mapResult = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
mapResultHandle.Update(mapResult);
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, mapResultHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
k++;
thread->CheckSafepointIfSuspended();
}
return newArrayHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Pop(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Pop);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
if (thisHandle->IsStableJSArray(thread) && JSObject::IsArrayLengthWritable(thread, thisObjHandle)) {
return JSStableArray::Pop(JSHandle<JSArray>::Cast(thisHandle), argv);
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
if (len == 0) {
JSHandle<JSTaggedValue> lengthValue(thread, JSTaggedValue(0));
JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, lengthValue, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue::Undefined();
}
int64_t newLen = len - 1;
JSHandle<JSTaggedValue> indexHandle(thread, JSTaggedValue(newLen));
JSHandle<JSTaggedValue> element = JSTaggedValue::GetProperty(thread, thisObjVal, indexHandle).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, indexHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, indexHandle, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return element.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Push(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Push);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
uint32_t argc = argv->GetArgsNumber();
if (argc > 0) {
thread->GetEcmaVM()->GetGlobalEnv()->NotifyArrayPrototypeChangedGuardians(thread, thisObjHandle);
}
if (thisHandle->IsStableJSArray(thread) && JSObject::IsArrayLengthWritable(thread, thisObjHandle)) {
return JSStableArray::Push(JSHandle<JSArray>::Cast(thisHandle), argv);
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if ((len + static_cast<int64_t>(argc)) > base::MAX_SAFE_INTEGER) {
THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
}
uint32_t k = 0;
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
while (k < argc) {
key.Update(JSTaggedValue(len));
JSHandle<JSTaggedValue> kValue = GetCallArg(argv, k);
JSArray::FastSetPropertyByValue(thread, thisObjVal, key, kValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
k++;
len++;
}
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
key.Update(JSTaggedValue(len));
JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, key, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return GetTaggedDouble(len);
}
JSTaggedValue BuiltinsArray::ReduceUnStableJSArray(JSThread *thread, JSHandle<JSTaggedValue> &thisHandle,
JSHandle<JSTaggedValue> &thisObjVal, int64_t k, int64_t len, JSMutableHandle<JSTaggedValue> &accumulator,
JSHandle<JSTaggedValue> &callbackFnHandle)
{
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSTaggedValue callResult = JSTaggedValue::Undefined();
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
while (k < len) {
bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
key.Update(JSTaggedValue(k));
JSHandle<JSTaggedValue> thisArgHandle = globalConst->GetHandledUndefined();
const uint32_t argsLength = 4;
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(), key.GetTaggedValue(),
thisObjVal.GetTaggedValue());
callResult = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
accumulator.Update(callResult);
}
k++;
thread->CheckSafepointIfSuspended();
}
return accumulator.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Reduce(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Reduce);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
uint32_t argc = argv->GetArgsNumber();
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
}
if (len == 0 && argc < 2) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reduce of empty array with no initial value", JSTaggedValue::Exception());
}
int64_t k = 0;
JSMutableHandle<JSTaggedValue> accumulator(thread, JSTaggedValue::Undefined());
if (argc >= 2) {
accumulator.Update(GetCallArg(argv, 1).GetTaggedValue());
} else {
bool kPresent = false;
while (!kPresent && k < len) {
kPresent = (thisHandle->IsTypedArray() || thisHandle->IsSharedTypedArray() ||
JSTaggedValue::HasProperty(thread, thisObjVal, k));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (kPresent) {
accumulator.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
k++;
}
if (!kPresent) {
THROW_TYPE_ERROR_AND_RETURN(thread, "accumulator can't be initialized.", JSTaggedValue::Exception());
}
}
if (thisObjVal->IsStableJSArray(thread)) {
JSStableArray::Reduce(thread, thisObjHandle, callbackFnHandle, accumulator, k, len);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
return ReduceUnStableJSArray(thread, thisHandle, thisObjVal, k, len, accumulator, callbackFnHandle);
}
JSTaggedValue BuiltinsArray::ReduceRight(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, ReduceRight);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
uint32_t argc = argv->GetArgsNumber();
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
}
if (len == 0 && argc < 2) {
THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
}
int64_t k = len - 1;
JSMutableHandle<JSTaggedValue> accumulator(thread, JSTaggedValue::Undefined());
if (argc >= 2) {
accumulator.Update(GetCallArg(argv, 1).GetTaggedValue());
} else {
bool kPresent = false;
while (!kPresent && k >= 0) {
kPresent = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (kPresent) {
accumulator.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
k--;
}
if (!kPresent) {
THROW_TYPE_ERROR_AND_RETURN(thread, "accumulator can't be initialized.", JSTaggedValue::Exception());
}
}
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSTaggedValue callResult = JSTaggedValue::Undefined();
JSHandle<JSTaggedValue> thisArgHandle = globalConst->GetHandledUndefined();
if (thisObjVal->IsStableJSArray(thread)) {
JSTaggedValue ret = JSStableArray::HandleReduceRightOfStable(thread, thisObjHandle,
callbackFnHandle, accumulator, thisArgHandle, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (ret.ToBoolean()) {
return accumulator.GetTaggedValue();
}
}
while (k >= 0) {
key.Update(JSTaggedValue(k));
bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, key));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, key);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
const uint32_t argsLength = 4;
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(), key.GetTaggedValue(),
thisObjVal.GetTaggedValue());
callResult = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
accumulator.Update(callResult);
}
k--;
}
return accumulator.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Reverse(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Reverse);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = 0;
if (thisHandle->IsJSArray()) {
len = JSArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength();
} else {
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
JSHandle<JSTaggedValue> lenResult = JSTaggedValue::GetProperty(thread, thisHandle, lengthKey).GetValue();
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread, lenResult);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
len = lenNumber.GetNumber();
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int64_t middle = std::floor(len / 2);
int64_t lower = 0;
JSMutableHandle<JSTaggedValue> lowerP(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> upperP(thread, JSTaggedValue::Undefined());
JSHandle<JSTaggedValue> lowerValueHandle(thread, JSTaggedValue::Undefined());
JSHandle<JSTaggedValue> upperValueHandle(thread, JSTaggedValue::Undefined());
if (thisObjVal->IsStableJSArray(thread)) {
JSStableArray::Reverse(thread, thisObjHandle, lower, len);
}
while (lower != middle) {
int64_t upper = len - lower - 1;
lowerP.Update(JSTaggedValue(lower));
upperP.Update(JSTaggedValue(upper));
bool lowerExists = (JSTaggedValue::HasProperty(thread, thisObjVal, lowerP));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (lowerExists) {
lowerValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, lowerP);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
bool upperExists = (JSTaggedValue::HasProperty(thread, thisObjVal, upperP));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (upperExists) {
upperValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, upperP);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
if (lowerExists && upperExists) {
JSArray::FastSetPropertyByValue(thread, thisObjVal, lowerP, upperValueHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSArray::FastSetPropertyByValue(thread, thisObjVal, upperP, lowerValueHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else if (upperExists) {
JSArray::FastSetPropertyByValue(thread, thisObjVal, lowerP, upperValueHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, upperP);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else if (lowerExists) {
JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, lowerP);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSArray::FastSetPropertyByValue(thread, thisObjVal, upperP, lowerValueHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
}
lower++;
}
return thisObjHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Shift(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Shift);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (thisHandle->IsStableJSArray(thread) && JSObject::IsArrayLengthWritable(thread, thisObjHandle)) {
return JSStableArray::Shift(JSHandle<JSArray>::Cast(thisHandle), argv);
}
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
if (len == 0) {
JSHandle<JSTaggedValue> zeroLenHandle(thread, JSTaggedValue(len));
JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, zeroLenHandle, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return JSTaggedValue::Undefined();
}
JSHandle<JSTaggedValue> firstKey(thread, JSTaggedValue(0));
JSHandle<JSTaggedValue> firstValue = JSTaggedValue::GetProperty(thread, thisObjVal, firstKey).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
int64_t k = 1;
while (k < len) {
bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSArray::FastSetPropertyByValue(thread, thisObjVal, k - 1, fromValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
toKey.Update(JSTaggedValue(k - 1));
JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
k++;
thread->CheckSafepointIfSuspended();
}
JSHandle<JSTaggedValue> deleteKey(thread, JSTaggedValue(len - 1));
JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, deleteKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(len - 1));
JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return firstValue.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Slice(EcmaRuntimeCallInfo *argv)
{
BUILTINS_API_TRACE(argv->GetThread(), Array, Slice);
ASSERT(argv);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> msg0 = GetCallArg(argv, 0);
double argStart;
if (msg0->IsInt()) {
argStart = msg0->GetInt();
} else {
JSTaggedNumber argStartTemp = JSTaggedValue::ToInteger(thread, msg0);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
argStart = argStartTemp.GetNumber();
}
int64_t k = 0;
if (argStart < 0) {
double tempStart = len + argStart;
k = tempStart > 0 ? tempStart : 0;
} else {
k = argStart < len ? argStart : len;
}
JSHandle<JSTaggedValue> msg1 = GetCallArg(argv, 1);
double argEnd = len;
if (!msg1->IsUndefined()) {
if (msg1->IsInt()) {
argEnd = msg1->GetInt();
} else {
JSTaggedNumber argEndTemp = JSTaggedValue::ToInteger(thread, msg1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
argEnd = argEndTemp.GetNumber();
}
}
int64_t final = 0;
if (argEnd < 0) {
double tempFinal = len + argEnd;
final = tempFinal > 0 ? tempFinal : 0;
} else {
final = argEnd < len ? argEnd : len;
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int64_t count = final > k ? (final - k) : 0;
if (thisHandle->IsStableJSArray(thread) && !thisObjHandle->GetJSHClass()->HasConstructor()
&& JSObject::GetPrototype(thread, thisObjHandle).IsJSArray()) {
return JSStableArray::Slice(thread, thisObjHandle, k, count);
}
JSTaggedValue newArray =
JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(static_cast<double>(count)));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (count == 0) {
return newArray;
}
JSHandle<JSObject> newArrayHandle(thread, newArray);
int64_t n = 0;
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> nKey(thread, JSTaggedValue::Undefined());
while (k < final) {
key.Update(JSTaggedValue(k));
bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, key);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
nKey.Update(JSTaggedValue(n));
JSHandle<JSTaggedValue> kValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, key);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, nKey, kValueHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
k++;
n++;
}
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(n));
JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, newLenHandle, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return newArrayHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Some(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Some);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
uint32_t k = 0;
JSTaggedValue callResult = GetTaggedBoolean(false);
if (thisObjVal->IsStableJSArray(thread)) {
callResult = JSStableArray::HandleSomeOfStable(thread, thisObjHandle, callbackFnHandle, thisArgHandle, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (callResult.ToBoolean()) {
return GetTaggedBoolean(true);
}
}
while (k < len) {
bool exists = (thisHandle->IsTypedArray() || thisHandle->IsSharedTypedArray() ||
JSTaggedValue::HasProperty(thread, thisObjVal, k));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
key.Update(JSTaggedValue(k));
kValue.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, key));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
const uint32_t argsLength = 3;
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
callResult = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (callResult.ToBoolean()) {
return GetTaggedBoolean(true);
}
}
k++;
thread->CheckSafepointIfSuspended();
}
return GetTaggedBoolean(false);
}
JSTaggedValue BuiltinsArray::Sort(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Array, Sort);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Callable is false", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (thisHandle->IsStableJSArray(thread)) {
JSStableArray::Sort(thread, thisHandle, callbackFnHandle);
} else {
JSArray::Sort(thread, JSHandle<JSTaggedValue>::Cast(thisObjHandle), callbackFnHandle);
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return thisObjHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Splice(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Splice);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
uint32_t argc = argv->GetArgsNumber();
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int64_t start = 0;
int64_t insertCount = 0;
int64_t actualDeleteCount = 0;
int64_t end = len;
double argStart = 0;
if (argc > 0) {
JSHandle<JSTaggedValue> msg0 = GetCallArg(argv, 0);
JSTaggedNumber argStartTemp = JSTaggedValue::ToInteger(thread, msg0);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
argStart = argStartTemp.GetNumber();
if (argStart < 0) {
double tempStart = argStart + len;
start = tempStart > 0 ? tempStart : 0;
} else {
start = argStart < end ? argStart : end;
}
actualDeleteCount = len - start;
}
if (argc > 1) {
thread->GetEcmaVM()->GetGlobalEnv()->NotifyArrayPrototypeChangedGuardians(thread, thisObjHandle);
insertCount = argc - 2;
JSHandle<JSTaggedValue> msg1 = GetCallArg(argv, 1);
JSTaggedNumber argDeleteCount = JSTaggedValue::ToInteger(thread, msg1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
double deleteCount = argDeleteCount.GetNumber();
deleteCount = deleteCount > 0 ? deleteCount : 0;
actualDeleteCount = deleteCount < (len - start) ? deleteCount : len - start;
}
if (len + insertCount - actualDeleteCount > base::MAX_SAFE_INTEGER) {
THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
}
JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle,
JSTaggedNumber(static_cast<double>(actualDeleteCount)));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSObject> newArrayHandle(thread, newArray);
if (thisHandle->IsStableJSArray(thread) && JSObject::IsArrayLengthWritable(thread, thisObjHandle)) {
return JSStableArray::Splice(JSHandle<JSArray>::Cast(thisHandle), argv, start, insertCount,
actualDeleteCount, newArrayHandle, len);
}
JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
int64_t k = 0;
while (k < actualDeleteCount) {
int64_t from = start + k;
fromKey.Update(JSTaggedValue(from));
bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
toKey.Update(JSTaggedValue(k));
if (newArrayHandle->IsJSProxy()) {
toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
k++;
}
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
JSHandle<JSTaggedValue> deleteCountHandle(thread, JSTaggedValue(actualDeleteCount));
JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(newArrayHandle), lengthKey, deleteCountHandle,
true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (insertCount < actualDeleteCount) {
k = start;
while (k < len - actualDeleteCount) {
fromKey.Update(JSTaggedValue(k + actualDeleteCount));
toKey.Update(JSTaggedValue(k + insertCount));
bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
k++;
}
k = len;
JSMutableHandle<JSTaggedValue> deleteKey(thread, JSTaggedValue::Undefined());
while (k > len - actualDeleteCount + insertCount) {
deleteKey.Update(JSTaggedValue(k - 1));
JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, deleteKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
k--;
}
} else if (insertCount > actualDeleteCount) {
k = len - actualDeleteCount;
while (k > start) {
fromKey.Update(JSTaggedValue(k + actualDeleteCount - 1));
toKey.Update(JSTaggedValue(k + insertCount - 1));
bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
k--;
}
}
k = start;
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
for (uint32_t i = 2; i < argc; i++) {
JSHandle<JSTaggedValue> itemValue = GetCallArg(argv, i);
key.Update(JSTaggedValue(k));
JSArray::FastSetPropertyByValue(thread, thisObjVal, key, itemValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
k++;
}
int64_t newLen = len - actualDeleteCount + insertCount;
JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newLen));
JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return newArrayHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::ToLocaleString(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, ToLocaleString);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (len == 0 || !ArrayJoinStack::Push(thread, thisObjVal)) {
const auto* globalConst = thread->GlobalConstants();
return globalConst->GetEmptyString();
}
JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> options = GetCallArg(argv, 1);
CString concatStr;
auto globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
for (int64_t k = 0; k < len; k++) {
thread->CheckSafepointIfSuspended();
JSTaggedValue next = globalConst->GetEmptyString();
JSHandle<JSTaggedValue> nextElement = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
if (!nextElement->IsUndefined() && !nextElement->IsNull()) {
JSHandle<JSTaggedValue> nextValueHandle = nextElement;
JSHandle<JSTaggedValue> key = globalConst->GetHandledToLocaleStringString();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, nextValueHandle, undefined, 2);
RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
info->SetCallArg(locales.GetTaggedValue(), options.GetTaggedValue());
JSTaggedValue callResult = JSFunction::Invoke(info, key);
RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
next = callResult;
}
JSHandle<JSTaggedValue> nextHandle(thread, next);
JSHandle<EcmaString> nextStringHandle = JSTaggedValue::ToString(thread, nextHandle);
RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle);
CString nextString = ConvertToString(thread, *nextStringHandle);
if (k > 0) {
concatStr += STRING_SEPERATOR;
concatStr += nextString;
continue;
}
concatStr += nextString;
}
ArrayJoinStack::Pop(thread, thisHandle);
auto* factory = thread->GetEcmaVM()->GetFactory();
return factory->NewFromUtf8(concatStr).GetTaggedValue();
}
JSTaggedValue BuiltinsArray::ToString(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, ToString);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
auto ecmaVm = thread->GetEcmaVM();
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
JSHandle<JSTaggedValue> joinKey = thread->GlobalConstants()->GetHandledJoinString();
JSHandle<JSTaggedValue> callbackFnHandle = JSTaggedValue::GetProperty(thread, thisObjVal, joinKey).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!callbackFnHandle->IsCallable()) {
JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
JSHandle<JSTaggedValue> objectPrototype = env->GetObjectFunctionPrototype();
JSHandle<JSTaggedValue> toStringKey = thread->GlobalConstants()->GetHandledToStringString();
callbackFnHandle = JSTaggedValue::GetProperty(thread, objectPrototype, toStringKey).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisObjVal, undefined, 0);
return JSFunction::Call(info);
}
JSTaggedValue BuiltinsArray::Unshift(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Unshift);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
int64_t argc = argv->GetArgsNumber();
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (argc > 0) {
if (len + argc > base::MAX_SAFE_INTEGER) {
THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
}
thread->GetEcmaVM()->GetGlobalEnv()->NotifyArrayPrototypeChangedGuardians(thread, thisObjHandle);
JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
int64_t k = len;
while (k > 0) {
fromKey.Update(JSTaggedValue(k - 1));
toKey.Update(JSTaggedValue(k + argc - 1));
bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (exists) {
JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
k--;
}
int64_t j = 0;
while (j < argc) {
toKey.Update(JSTaggedValue(j));
JSHandle<JSTaggedValue> toValue = GetCallArg(argv, j);
JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, toValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
j++;
}
}
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
int64_t newLen = len + argc;
JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newLen));
JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return GetTaggedDouble(newLen);
}
JSTaggedValue BuiltinsArray::Values(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Values);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSObject> self = JSTaggedValue::ToObject(thread, GetThis(argv));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSArrayIterator> iter(factory->NewJSArrayIterator(self, IterationKind::VALUE));
return iter.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::Flat(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Flat);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
uint32_t argc = argv->GetArgsNumber();
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t sourceLen = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
double depthNum = 1;
if (argc > 0) {
JSHandle<JSTaggedValue> msg1 = GetCallArg(argv, 0);
if (!msg1->IsUndefined()) {
JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, msg1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
depthNum = base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber());
depthNum = depthNum < 0 ? 0 : depthNum;
}
}
uint32_t arrayLen = 0;
JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(arrayLen));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
base::FlattenArgs args = { sourceLen, 0, depthNum };
JSHandle<JSObject> newArrayHandle(thread, newArray);
ArrayHelper::FlattenIntoArray(thread, newArrayHandle, thisObjVal, args,
thread->GlobalConstants()->GetHandledUndefined(),
thread->GlobalConstants()->GetHandledUndefined());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return newArrayHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::FlatMap(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, FlatMap);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t sourceLen = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> mapperFunctionHandle = GetCallArg(argv, 0);
if (!mapperFunctionHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the mapperFunction is not callable.", JSTaggedValue::Exception());
}
JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(0U));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
base::FlattenArgs args = { sourceLen, 0, 1 };
JSHandle<JSObject> newArrayHandle(thread, newArray);
int64_t targetIdx = 0;
ArrayHelper::FlatMapFromIndex(thread, thisObjVal, newArrayHandle, mapperFunctionHandle,
GetCallArg(argv, 1), targetIdx, 0, sourceLen);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return newArrayHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::IncludesStable(EcmaRuntimeCallInfo *argv, const JSHandle<JSTaggedValue> &thisHandle)
{
int64_t length = JSHandle<JSArray>::Cast(thisHandle)->GetArrayLength();
if (length == 0) {
return GetTaggedBoolean(false);
}
JSThread* thread = argv->GetThread();
int64_t fromIndex = 0;
uint32_t argc = argv->GetArgsNumber();
if (UNLIKELY(argc >= 2)) {
JSHandle<JSTaggedValue> fromIndexHandle = argv->GetCallArg(1);
fromIndex = ArrayHelper::GetStartIndex(thread, fromIndexHandle, length);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (UNLIKELY(fromIndexHandle->IsECMAObject())) {
return IncludesSlowPath(argv, thisHandle, length, fromIndex);
}
}
if (fromIndex >= length) {
return GetTaggedBoolean(false);
}
if (UNLIKELY(!thisHandle->IsECMAObject())) {
return IncludesSlowPath(argv, thisHandle, length, fromIndex);
}
JSHandle<JSTaggedValue> target = GetCallArg(argv, 0);
return JSStableArray::Includes(
thread, thisHandle, target, static_cast<uint32_t>(fromIndex), static_cast<uint32_t>(length));
}
JSTaggedValue BuiltinsArray::IncludesSlowPath(EcmaRuntimeCallInfo *argv, const JSHandle<JSTaggedValue> &thisHandle)
{
JSThread* thread = argv->GetThread();
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t length = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (length == 0) {
return GetTaggedBoolean(false);
}
int64_t fromIndex = ArrayHelper::GetStartIndexFromArgs(thread, argv, 1, length);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return IncludesSlowPath(argv, thisObjVal, length, fromIndex);
}
JSTaggedValue BuiltinsArray::IncludesSlowPath(
EcmaRuntimeCallInfo *argv, const JSHandle<JSTaggedValue> &thisObjVal, int64_t length, int64_t fromIndex)
{
if (fromIndex >= length) {
return GetTaggedBoolean(false);
}
JSThread* thread = argv->GetThread();
JSHandle<JSTaggedValue> searchElement = GetCallArg(argv, 0);
JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> valueHandle(thread, JSTaggedValue::Undefined());
while (fromIndex < length) {
JSHandle<JSTaggedValue> handledFromIndex(thread, JSTaggedValue(fromIndex));
keyHandle.Update(JSTaggedValue::ToString(thread, handledFromIndex));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
valueHandle.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, keyHandle).GetTaggedValue());
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (JSTaggedValue::SameValueZero(thread, searchElement.GetTaggedValue(), valueHandle.GetTaggedValue())) {
return GetTaggedBoolean(true);
}
fromIndex++;
}
return GetTaggedBoolean(false);
}
JSTaggedValue BuiltinsArray::Includes(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, Includes);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
if (thisHandle->IsStableJSArray(thread) && !thisHandle->IsMutantTaggedArray()) {
return IncludesStable(argv, thisHandle);
}
return IncludesSlowPath(argv, thisHandle);
}
JSTaggedValue BuiltinsArray::At(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Array, At);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
if (thisHandle->IsStableJSArray(thread)) {
return JSStableArray::At(JSHandle<JSArray>::Cast(thisHandle), argv);
}
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedNumber index = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int64_t relativeIndex = index.GetNumber();
int64_t k = 0;
if (relativeIndex >= 0) {
k = relativeIndex;
} else {
k = len + relativeIndex;
}
if (k < 0 || k >= len) {
return JSTaggedValue::Undefined();
}
JSHandle<JSTaggedValue> element = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return element.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::With(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Array, With);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedNumber index = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int64_t relativeIndex = index.GetNumber();
int64_t actualIndex = 0;
JSHandle<JSTaggedValue> value = GetCallArg(argv, 1);
if (relativeIndex >= 0) {
actualIndex = relativeIndex;
} else {
actualIndex = len + relativeIndex;
}
if (actualIndex >= len || actualIndex < 0) {
THROW_RANGE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
}
JSTaggedValue newArray =
JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast<double>(len))).GetTaggedValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSObject> newArrayHandle(thread, newArray);
if (thisHandle->IsStableJSArray(thread)) {
return JSStableArray::With(thread, JSHandle<JSArray>::Cast(thisHandle), len, actualIndex, value);
}
int64_t k = 0;
JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
JSHandle<JSTaggedValue> fromValue;
while (k < len) {
fromKey.Update(JSTaggedValue(k));
if (k == actualIndex) {
fromValue = value;
} else {
fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, fromKey, fromValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
++k;
thread->CheckSafepointIfSuspended();
}
return newArrayHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::ToSorted(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Array, ToSorted);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Callable is false", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int64_t len = ArrayHelper::GetArrayLength(thread, JSHandle<JSTaggedValue>(thisObjHandle));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast<double>(len))).GetTaggedValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSObject> newArrayHandle(thread, newArray);
JSHandle<TaggedArray> sortedList =
ArrayHelper::SortIndexedProperties(thread, JSHandle<JSTaggedValue>::Cast(thisObjHandle), len, callbackFnHandle,
base::HolesType::READ_THROUGH_HOLES);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int64_t j = 0;
JSMutableHandle<JSTaggedValue> itemValue(thread, JSTaggedValue::Undefined());
while (j < len) {
itemValue.Update(sortedList->Get(thread, j));
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, j, itemValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
++j;
}
return newArrayHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::ToSpliced(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Array, ToSpliced);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
uint32_t argc = argv->GetArgsNumber();
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
int64_t actualStart = 0;
int64_t actualSkipCount = 0;
int64_t newLen = 0;
int64_t insertCount = 0;
if (argc > 0) {
JSTaggedNumber argStart = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
double relativeStart = argStart.GetNumber();
if (relativeStart < 0) {
double tempStart = relativeStart + len;
actualStart = tempStart > 0 ? tempStart : 0;
} else {
actualStart = relativeStart < len ? relativeStart : len;
}
actualSkipCount = len - actualStart;
}
if (argc > 1) {
insertCount = argc - 2;
JSTaggedNumber argSkipCount = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 1));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
double skipCount = argSkipCount.GetNumber();
skipCount = skipCount > 0 ? skipCount : 0;
actualSkipCount = skipCount < (len - actualStart) ? skipCount : len - actualStart;
}
newLen = len + insertCount - actualSkipCount;
if (newLen > base::MAX_SAFE_INTEGER) {
THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception());
}
if (thisHandle->IsStableJSArray(thread)) {
return JSStableArray::ToSpliced(JSHandle<JSArray>::Cast(thisHandle), argv, argc, actualStart,
actualSkipCount, newLen);
}
JSHandle<JSTaggedValue> newJsTaggedArray =
JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast<double>(newLen)));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSObject> newArrayHandle(thread, newJsTaggedArray.GetTaggedValue());
int64_t i = 0;
int64_t r = actualStart + actualSkipCount;
while (i < actualStart) {
JSHandle<JSTaggedValue> iValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, i);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, i, iValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
++i;
}
JSMutableHandle<JSTaggedValue> pi(thread, JSTaggedValue::Undefined());
for (int64_t pos = 2; pos < argc; ++pos) {
pi.Update(JSTaggedValue(i));
JSHandle<JSTaggedValue> element = GetCallArg(argv, pos);
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, pi, element);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
++i;
}
JSMutableHandle<JSTaggedValue> from(thread, JSTaggedValue::Undefined());
while (i < newLen) {
pi.Update(JSTaggedValue(i));
from.Update(JSTaggedValue(r));
JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, from);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, pi, fromValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
++i;
++r;
}
JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
JSHandle<JSTaggedValue> newLenHandle(thread, JSTaggedValue(newLen));
JSTaggedValue::SetProperty(thread, newJsTaggedArray, lengthKey, newLenHandle, true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return newArrayHandle.GetTaggedValue();
}
JSTaggedValue BuiltinsArray::FindLast(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Array, FindLast);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the predicate is not callable.", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
int64_t k = len - 1;
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
const uint32_t argsLength = 3;
JSTaggedValue callResult = GetTaggedBoolean(false);
if (thisObjVal->IsStableJSArray(thread)) {
JSMutableHandle<JSTaggedValue> kValue(thread, JSTaggedValue::Undefined());
callResult = JSStableArray::HandleFindLastOfStable(thread, thisObjHandle,
callbackFnHandle, thisArgHandle, kValue, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (callResult.ToBoolean()) {
return kValue.GetTaggedValue();
}
}
while (k >= 0) {
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
key.Update(JSTaggedValue(k));
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
callResult = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (callResult.ToBoolean()) {
return kValue.GetTaggedValue();
}
k--;
}
return JSTaggedValue::Undefined();
}
JSTaggedValue BuiltinsArray::FindLastIndex(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Array, FindLastIndex);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0);
if (!callbackFnHandle->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "the predicate is not callable.", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1);
int64_t k = len - 1;
JSTaggedValue callResult = GetTaggedBoolean(true);
if (thisObjVal->IsStableJSArray(thread)) {
callResult =
JSStableArray::HandleFindLastIndexOfStable(thread, thisObjHandle, callbackFnHandle, thisArgHandle, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (callResult.ToBoolean()) {
return GetTaggedDouble(k);
}
}
JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
const uint32_t argsLength = 3;
while (k >= 0) {
JSHandle<JSTaggedValue> kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
key.Update(JSTaggedValue(k));
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue());
callResult = JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (callResult.ToBoolean()) {
return GetTaggedDouble(k);
}
k--;
}
return GetTaggedDouble(-1);
}
JSTaggedValue BuiltinsArray::ToReversed(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Array, ToReversed);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);
int64_t len = ArrayHelper::GetLength(thread, thisObjVal);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (thisHandle->IsStableJSArray(thread)) {
return JSStableArray::ToReversed(thread, JSHandle<JSArray>::Cast(thisHandle), len);
}
JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast<double>(len))).GetTaggedValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSObject> newArrayHandle(thread, newArray);
JSMutableHandle<JSTaggedValue> fromKey(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> toKey(thread, JSTaggedValue::Undefined());
int64_t k = 0;
while (k < len) {
int64_t from = len - k - 1;
fromKey.Update(JSTaggedValue(from));
toKey.Update(JSTaggedValue(k));
JSHandle<JSTaggedValue> fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
k++;
thread->CheckSafepointIfSuspended();
}
return newArrayHandle.GetTaggedValue();
}
}