arkcompiler_ets_runtime/ecmascript/shared_objects/js_shared_array.cpp
xwcai98 bb7b0fd181 Remove handle self
Issue: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/IB3F58

Signed-off-by: xwcai98 <caixinwei5@huawei.com>
Change-Id: Id7f26e8a0275d54d6f1c499e66322b1882bbed83
2024-11-11 14:26:02 +08:00

585 lines
27 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

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

/*
* Copyright (c) 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/shared_objects/js_shared_array.h"
#include "ecmascript/interpreter/interpreter.h"
#include "ecmascript/object_fast_operator-inl.h"
namespace panda::ecmascript {
using base::ArrayHelper;
JSTaggedValue JSSharedArray::LengthGetter([[maybe_unused]] JSThread *thread, const JSHandle<JSObject> &self,
SCheckMode checkMode)
{
[[maybe_unused]] ConcurrentApiScope<JSSharedArray> scope(thread, JSHandle<JSTaggedValue>::Cast(self),
checkMode);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
return JSTaggedValue(JSSharedArray::Cast(*self)->GetLength());
}
bool JSSharedArray::DummyLengthSetter([[maybe_unused]] JSThread *thread,
[[maybe_unused]] const JSHandle<JSObject> &self,
[[maybe_unused]] const JSHandle<JSTaggedValue> &value,
[[maybe_unused]] bool mayThrow)
{
// length is the read only property for Shared Array
return true;
}
bool JSSharedArray::LengthSetter(JSThread *thread, const JSHandle<JSObject> &self, const JSHandle<JSTaggedValue> &value,
bool mayThrow)
{
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
uint32_t newLen = 0;
if (!JSTaggedValue::ToArrayLength(thread, value, &newLen) && mayThrow) {
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
}
uint32_t oldLen = JSSharedArray::Cast(*self)->GetArrayLength();
if (oldLen == newLen) {
return true;
}
if (!IsArrayLengthWritable(thread, self)) {
if (mayThrow) {
THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetReadOnlyProperty), false);
}
return false;
}
JSSharedArray::SetCapacity(thread, self, oldLen, newLen);
uint32_t actualLen = JSSharedArray::Cast(*self)->GetArrayLength();
if (actualLen != newLen) { // LCOV_EXCL_START
if (mayThrow) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Not all array elements is configurable", false);
}
return false;
} // LCOV_EXCL_STOP
return true;
}
JSHandle<JSTaggedValue> JSSharedArray::ArrayCreate(JSThread *thread, JSTaggedNumber length, ArrayMode mode)
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> sharedArrayFunction = env->GetSharedArrayFunction();
return JSSharedArray::ArrayCreate(thread, length, sharedArrayFunction, mode);
}
// 9.4.2.2 ArrayCreate(length, proto)
JSHandle<JSTaggedValue> JSSharedArray::ArrayCreate(JSThread *thread, JSTaggedNumber length,
const JSHandle<JSTaggedValue> &newTarget, ArrayMode mode)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
// Assert: length is an integer Number ≥ 0.
ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer");
// 2. If length is 0, let length be +0.
double arrayLength = length.GetNumber();
if (arrayLength > MAX_ARRAY_INDEX) {
JSHandle<JSTaggedValue> exception(thread, JSTaggedValue::Exception());
auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::TYPE_ERROR,
"Parameter error.Array length must less than 2^32.");
THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, exception);
}
uint32_t normalArrayLength = length.ToUint32();
// 8. Set the [[Prototype]] internal slot of A to proto.
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSFunction> arrayFunc(env->GetSharedArrayFunction());
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(arrayFunc, newTarget);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
// 9. Set the [[Extensible]] internal slot of A to true.
// 10. Perform OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]:
// true, [[Enumerable]]: false, [[Configurable]]: false}).
if (mode == ArrayMode::LITERAL) {
JSSharedArray::Cast(*obj)->SetArrayLength(thread, normalArrayLength);
} else {
JSSharedArray::SetCapacity(thread, obj, 0, normalArrayLength, true);
}
return JSHandle<JSTaggedValue>(obj);
}
// 9.4.2.3 ArraySpeciesCreate(originalArray, length)
JSTaggedValue JSSharedArray::ArraySpeciesCreate(JSThread *thread, const JSHandle<JSObject> &originalArray,
JSTaggedNumber length)
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
// Assert: length is an integer Number ≥ 0.
ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer");
// If length is 0, let length be +0.
int64_t arrayLength = length.GetNumber();
if (arrayLength == -0) {
arrayLength = +0;
}
// Let C be undefined.
// Let isArray be IsArray(originalArray).
JSHandle<JSTaggedValue> originalValue(originalArray);
bool isSArray = originalValue->IsJSSharedArray();
// ReturnIfAbrupt(isArray).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// If isArray is true, then
JSHandle<JSTaggedValue> constructor(thread, JSTaggedValue::Undefined());
if (isSArray) {
// Let C be Get(originalArray, "constructor").
auto *hclass = originalArray->GetJSHClass();
JSTaggedValue proto = hclass->GetPrototype();
if (hclass->IsJSSharedArray() && !hclass->HasConstructor() && proto.IsJSSharedArray()) {
return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue();
}
JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString();
constructor = JSTaggedValue::GetProperty(thread, originalValue, constructorKey).GetValue();
// ReturnIfAbrupt(C).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// If IsConstructor(C) is true, then
if (constructor->IsConstructor()) {
// Let thisRealm be the running execution contexts Realm.
// Let realmC be GetFunctionRealm(C).
JSHandle<GlobalEnv> realmC = JSObject::GetFunctionRealm(thread, constructor);
// ReturnIfAbrupt(realmC).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// If thisRealm and realmC are not the same Realm Record, then
if (*realmC != *env) {
JSTaggedValue realmArrayConstructor = realmC->GetSharedArrayFunction().GetTaggedValue();
// If SameValue(C, realmC.[[intrinsics]].[[%Array%]]) is true, let C be undefined.
if (JSTaggedValue::SameValue(constructor.GetTaggedValue(), realmArrayConstructor)) {
return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue();
}
}
}
// If Type(C) is Object, then
if (constructor->IsECMAObject()) {
// Let C be Get(C, @@species).
JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
constructor = JSTaggedValue::GetProperty(thread, constructor, speciesSymbol).GetValue();
// ReturnIfAbrupt(C).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// If C is null, let C be undefined.
if (constructor->IsNull()) {
return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue();
}
}
}
// If C is undefined, return ArrayCreate(length).
if (constructor->IsUndefined()) {
return JSSharedArray::ArrayCreate(thread, length).GetTaggedValue();
}
// If IsConstructor(C) is false, throw a TypeError exception.
if (!constructor->IsConstructor()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Not a constructor", JSTaggedValue::Exception());
}
// Return Construct(C, «length»).
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 1);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
info->SetCallArg(JSTaggedValue(arrayLength));
JSTaggedValue result = JSFunction::Construct(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// NOTEIf originalArray was created using the standard built-in Array constructor for
// a Realm that is not the Realm of the running execution context, then a new Array is
// created using the Realm of the running execution context. This maintains compatibility
// with Web browsers that have historically had that behaviour for the Array.prototype methods
// that now are defined using ArraySpeciesCreate.
return result;
}
JSHandle<TaggedArray> JSSharedArray::SetCapacity(const JSThread *thread, const JSHandle<TaggedArray> &array,
uint32_t capa)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
uint32_t oldLength = array->GetLength();
JSHandle<TaggedArray> newArray = factory->CopySArray(array, oldLength, capa);
return newArray;
}
void JSSharedArray::SetCapacity(JSThread *thread, const JSHandle<JSObject> &array, uint32_t oldLen, uint32_t newLen,
bool isNew)
{
TaggedArray *element = TaggedArray::Cast(array->GetElements().GetTaggedObject());
if (element->IsDictionaryMode()) { // LCOV_EXCL_START
THROW_TYPE_ERROR(thread, "SendableArray don't support dictionary mode.");
} // LCOV_EXCL_STOP
uint32_t capacity = element->GetLength();
if (newLen <= capacity) {
// judge if need to cut down the array size, else fill the unused tail with holes
CheckAndCopyArray(thread, JSHandle<JSSharedArray>(array));
JSObject::FillElementsWithHoles(thread, array, newLen, oldLen < capacity ? oldLen : capacity);
}
if (newLen > capacity) {
JSObject::GrowElementsCapacity(thread, array, newLen, isNew);
}
JSSharedArray::Cast(*array)->SetArrayLength(thread, newLen);
}
bool JSSharedArray::ArraySetLength(JSThread *thread, const JSHandle<JSObject> &array, const PropertyDescriptor &desc)
{
JSHandle<JSTaggedValue> lengthKeyHandle(thread->GlobalConstants()->GetHandledLengthString());
// 1. If the [[Value]] field of Desc is absent, then
if (!desc.HasValue()) {
// 1a. Return OrdinaryDefineOwnProperty(A, "length", Desc).
return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, desc);
}
// 2. Let newLenDesc be a copy of Desc.
// (Actual copying is not necessary.)
PropertyDescriptor newLenDesc = desc;
// 3. - 7. Convert Desc.[[Value]] to newLen.
uint32_t newLen = 0;
if (!JSTaggedValue::ToArrayLength(thread, desc.GetValue(), &newLen)) {
THROW_RANGE_ERROR_AND_RETURN(thread, "array length must equal or less than 2^32.", false);
}
// 8. Set newLenDesc.[[Value]] to newLen.
// (Done below, if needed.)
// 9. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
PropertyDescriptor oldLenDesc(thread);
[[maybe_unused]] bool success = GetOwnProperty(thread, array, lengthKeyHandle, oldLenDesc);
// 10. (Assert)
ASSERT(success);
// 11. Let oldLen be oldLenDesc.[[Value]].
uint32_t oldLen = 0;
JSTaggedValue::ToArrayLength(thread, oldLenDesc.GetValue(), &oldLen);
// 12. If newLen >= oldLen, then
if (newLen >= oldLen) {
// 8. Set newLenDesc.[[Value]] to newLen.
// 12a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc).
newLenDesc.SetValue(JSHandle<JSTaggedValue>(thread, JSTaggedValue(newLen)));
return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, newLenDesc);
}
// 13. If oldLenDesc.[[Writable]] is false, return false.
if (!oldLenDesc.IsWritable() ||
// Also handle the {configurable: true} case since we later use
// JSSharedArray::SetLength instead of OrdinaryDefineOwnProperty to change
// the length, and it doesn't have access to the descriptor anymore.
newLenDesc.IsConfigurable() ||
(newLenDesc.HasEnumerable() && (newLenDesc.IsEnumerable() != oldLenDesc.IsEnumerable()))) {
return false;
}
// 14. If newLenDesc.[[Writable]] is absent or has the value true,
// let newWritable be true.
bool newWritable = false;
if (!newLenDesc.HasWritable() || newLenDesc.IsWritable()) {
newWritable = true;
} else {
// 15. Else,
// 15a. Need to defer setting the [[Writable]] attribute to false in case
// any elements cannot be deleted.
// 15b. Let newWritable be false. (It's initialized as "false" anyway.)
// 15c. Set newLenDesc.[[Writable]] to true.
// (Not needed.)
}
// Most of steps 16 through 19 is implemented by JSSharedArray::SetCapacity.
JSSharedArray::SetCapacity(thread, array, oldLen, newLen);
// Steps 19d-ii, 20.
if (!newWritable) { // LCOV_EXCL_START
PropertyDescriptor readonly(thread);
readonly.SetWritable(false);
success = JSObject::DefineOwnProperty(thread, array, lengthKeyHandle, readonly);
ASSERT_PRINT(success, "DefineOwnProperty of length must be success here!");
} // LCOV_EXCL_STOP
// Steps 19d-v, 21. Return false if there were non-deletable elements.
uint32_t arrayLength = JSSharedArray::Cast(*array)->GetArrayLength();
return arrayLength == newLen;
}
bool JSSharedArray::PropertyKeyToArrayIndex(JSThread *thread, const JSHandle<JSTaggedValue> &key, uint32_t *output)
{
return JSTaggedValue::ToArrayLength(thread, key, output) && *output <= JSSharedArray::MAX_ARRAY_INDEX;
}
// 9.4.2.1 [[DefineOwnProperty]] ( P, Desc)
bool JSSharedArray::DefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &array,
const JSHandle<JSTaggedValue> &key, const PropertyDescriptor &desc,
SCheckMode sCheckMode)
{
if (!desc.GetValue()->IsSharedType() || (desc.HasGetter() && !desc.GetGetter()->IsSharedType()) ||
(desc.HasSetter() && !desc.GetSetter()->IsSharedType())) {
auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::TYPE_ERROR,
"Parameter error. Only accept sendable value.");
THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
}
if (sCheckMode == SCheckMode::CHECK && !(JSSharedArray::Cast(*array)->IsKeyInRange(key))) {
auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::RANGE_ERROR,
"Key out of length.");
THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
}
// 1. Assert: IsPropertyKey(P) is true.
ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key!");
// 2. If P is "length", then
if (IsLengthString(thread, key)) {
// a. Return ArraySetLength(A, Desc).
return ArraySetLength(thread, array, desc);
}
// 3. Else if P is an array index, then
// already do in step 4.
// 4. Return OrdinaryDefineOwnProperty(A, P, Desc).
bool success = JSObject::OrdinaryDefineOwnProperty(thread, array, key, desc);
if (success) {
JSTaggedValue constructorKey = thread->GlobalConstants()->GetConstructorString();
if (key.GetTaggedValue() == constructorKey) {
array->GetJSHClass()->SetHasConstructor(true);
return true;
}
}
return success;
}
bool JSSharedArray::IsLengthString(JSThread *thread, const JSHandle<JSTaggedValue> &key)
{
return key.GetTaggedValue() == thread->GlobalConstants()->GetLengthString();
}
JSHandle<JSSharedArray> JSSharedArray::CreateArrayFromList(JSThread *thread, const JSHandle<TaggedArray> &elements)
{
// Assert: elements is a List whose elements are all ECMAScript language values.
// 2. Let array be ArrayCreate(0).
uint32_t length = elements->GetLength();
// 4. For each element e of elements
auto env = thread->GetEcmaVM()->GetGlobalEnv();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSFunction> arrayFunc(env->GetSharedArrayFunction());
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(arrayFunc);
JSSharedArray::Cast(*obj)->SetArrayLength(thread, length);
obj->SetElements(thread, elements);
obj->GetJSHClass()->SetExtensible(false);
JSHandle<JSSharedArray> arr(obj);
return arr;
}
JSHandle<JSTaggedValue> JSSharedArray::FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
uint32_t index)
{
auto result = ObjectFastOperator::FastGetPropertyByIndex(thread, obj.GetTaggedValue(), index);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
return JSHandle<JSTaggedValue>(thread, result);
}
JSHandle<JSTaggedValue> JSSharedArray::FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &key, SCheckMode sCheckMode)
{
auto result =
ObjectFastOperator::FastGetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(), sCheckMode);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
return JSHandle<JSTaggedValue>(thread, result);
}
bool JSSharedArray::FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, uint32_t index,
const JSHandle<JSTaggedValue> &value)
{
return ObjectFastOperator::FastSetPropertyByIndex(thread, obj.GetTaggedValue(), index, value.GetTaggedValue());
}
bool JSSharedArray::FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
{
return ObjectFastOperator::FastSetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(),
value.GetTaggedValue(), SCheckMode::SKIP);
}
OperationResult JSSharedArray::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &key, SCheckMode sCheckMode)
{
// Add Concurrent check for shared array
[[maybe_unused]] ConcurrentApiScope<JSSharedArray> scope(thread, obj,
sCheckMode);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
ObjectOperator op(thread, obj, key);
// Out of bounds check for shared array
if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) {
return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(false));
}
return OperationResult(thread, JSObject::GetProperty(thread, &op), PropertyMetaData(op.IsFound()));
}
bool JSSharedArray::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value, bool mayThrow,
SCheckMode sCheckMode)
{
// Concurrent check for shared array
[[maybe_unused]] ConcurrentApiScope<JSSharedArray, ModType::WRITE> scope(
thread, obj, sCheckMode);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
// 2 ~ 4 findProperty in Receiver, Obj and its parents
ObjectOperator op(thread, obj, key);
// Out of bounds check for shared array
if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) {
auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::RANGE_ERROR,
"The value of index is out of range.");
THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
}
return JSObject::SetProperty(&op, value, mayThrow);
}
bool JSSharedArray::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
uint32_t index, const JSHandle<JSTaggedValue> &value, bool mayThrow,
SCheckMode sCheckMode)
{
// Concurrent check for shared array
[[maybe_unused]] ConcurrentApiScope<JSSharedArray, ModType::WRITE> scope(
thread, obj, sCheckMode);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
// 2 ~ 4 findProperty in Receiver, Obj and its parents
ObjectOperator op(thread, obj, index);
// Out of bounds check for shared array
if ((obj->IsJSSharedArray() && sCheckMode == SCheckMode::CHECK) && op.IsElement() && !op.IsFound()) {
auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::RANGE_ERROR,
"The value of index is out of range.");
THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false);
}
return JSObject::SetProperty(&op, value, mayThrow);
}
// ecma2024 23.1.3.20 Array.prototype.sort(comparefn)
JSTaggedValue JSSharedArray::Sort(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &fn)
{
ASSERT(fn->IsUndefined() || fn->IsCallable());
// 3. Let len be ?LengthOfArrayLike(obj).
int64_t len = ArrayHelper::GetArrayLength(thread, obj);
// ReturnIfAbrupt(len).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// If len is 0 or 1, no need to sort
if (len == 0 || len == 1) {
return obj.GetTaggedValue();
}
// 4. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs
// the following steps when called:
// a. Return ? CompareArrayElements(x, y, comparefn).
// 5. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, SKIP-HOLES).
JSHandle<TaggedArray> sortedList =
ArrayHelper::SortIndexedProperties(thread, obj, len, fn, base::HolesType::SKIP_HOLES);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 6. Let itemCount be the number of elements in sortedList.
uint32_t itemCount = sortedList->GetLength();
// 7. Let j be 0.
uint32_t j = 0;
// 8. Repeat, while j < itemCount,
// a. Perform ! Set(obj, ! ToString((j)), sortedList[j], true).
// b. Set j to j + 1.
JSMutableHandle<JSTaggedValue> item(thread, JSTaggedValue::Undefined());
while (j < itemCount) {
item.Update(sortedList->Get(j));
JSSharedArray::FastSetPropertyByValue(thread, obj, j, item);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
++j;
}
// 9. NOTE: The call to SortIndexedProperties in step 5 uses SKIP-HOLES.The remaining indices are deleted to
// preserve the number of holes that were detected and excluded from the sort.
// 10. Repeat, while j < len,
// a. Perform ? DeletePropertyOrThrow(obj, ! ToString((j))).
// b. Set j to j + 1.
while (j < len) {
item.Update(JSTaggedValue(j));
JSTaggedValue::DeletePropertyOrThrow(thread, obj, item);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
++j;
}
return obj.GetTaggedValue();
}
bool JSSharedArray::IncludeInSortedValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> &value)
{
ASSERT(obj->IsJSSharedArray());
JSHandle<JSSharedArray> arrayObj = JSHandle<JSSharedArray>::Cast(obj);
int32_t length = static_cast<int32_t>(arrayObj->GetArrayLength());
if (length == 0) {
return false;
}
int32_t left = 0;
int32_t right = length - 1;
while (left <= right) {
int32_t middle = (left + right) / 2;
JSHandle<JSTaggedValue> vv = JSSharedArray::FastGetPropertyByValue(thread, obj, middle);
ComparisonResult res = JSTaggedValue::Compare(thread, vv, value);
if (res == ComparisonResult::EQUAL) {
return true;
} else if (res == ComparisonResult::LESS) {
left = middle + 1;
} else {
right = middle - 1;
}
}
return false;
}
void JSSharedArray::CheckAndCopyArray(const JSThread *thread, JSHandle<JSSharedArray> obj)
{
JSHandle<TaggedArray> arr(thread, obj->GetElements());
// Check whether array is shared in the nonmovable space before set properties and elements.
// If true, then really copy array in the semi space.
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
if (arr.GetTaggedValue().IsCOWArray()) {
auto newArray = factory->CopyArray(arr, arr->GetLength(), arr->GetLength(),
JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE);
obj->SetElements(thread, newArray.GetTaggedValue());
}
JSHandle<TaggedArray> prop(thread, obj->GetProperties());
if (prop.GetTaggedValue().IsCOWArray()) {
auto newProps = factory->CopyArray(prop, prop->GetLength(), prop->GetLength(),
JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE);
obj->SetProperties(thread, newProps.GetTaggedValue());
}
}
void JSSharedArray::DeleteInElementMode(const JSThread *thread, JSHandle<JSSharedArray> &obj)
{
JSHandle<TaggedArray> elements(thread, obj->GetElements());
ASSERT(!obj->GetJSHClass()->IsDictionaryElement());
uint32_t length = elements->GetLength();
// fixme(hzzhouzebin) Optimize Delete later.
uint32_t size = 0;
for (uint32_t i = 0; i < length; i++) {
JSTaggedValue value = ElementAccessor::Get(JSHandle<JSObject>(obj), i);
if (value.IsHole()) {
continue;
}
++size;
}
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> newElements(
factory->NewTaggedArray(length, JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE));
uint32_t newCurr = 0;
for (uint32_t i = 0; i < length; i++) {
JSTaggedValue value = ElementAccessor::Get(JSHandle<JSObject>(obj), i);
if (value.IsHole()) {
continue;
}
newElements->Set(thread, newCurr, value);
++newCurr;
}
obj->SetElements(thread, newElements);
}
} // namespace panda::ecmascript