Optimize StableArrayJoin

Issue: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/IAYSJT

Signed-off-by: hewei <hewei215@huawei.com>
Change-Id: I66a095bc5886b9894256699174fdf69f2ef09b2e
This commit is contained in:
hewei 2024-10-30 14:31:34 +08:00
parent dca45d344f
commit 2c6bd650fa
4 changed files with 422 additions and 18 deletions

View File

@ -35,6 +35,8 @@ namespace panda::ecmascript {
#define ECMASCRIPT_ENABLE_ELEMENTSKIND_ALWAY_GENERIC 0
#define ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT 0
#define ENABLE_NEXT_OPTIMIZATION 1
#ifndef NDEBUG
#define ECMASCRIPT_ENABLE_INTERPRETER_LOG 1
#define ECMASCRIPT_ENABLE_RUNTIME_STAT 1

View File

@ -417,6 +417,199 @@ JSTaggedValue JSStableArray::Shift(JSHandle<JSArray> receiver, EcmaRuntimeCallIn
return result->IsHole() ? JSTaggedValue::Undefined() : result.GetTaggedValue();
}
#if ENABLE_NEXT_OPTIMIZATION
bool JSStableArray::WorthUseTreeString(uint32_t sepLength, size_t allocateLength, uint32_t len)
{
if (allocateLength >= TREE_STRING_THRESHOLD) {
// if sepLength is 0, means all the elements in treeString is len -1;
// otherwise, the num of elements is (len-1)(string in vector) + (len -1)(num of seps)
size_t treeStringElementNum = (sepLength == 0) ? (len - 1) : (2 * (len - 1));
if (treeStringElementNum * TreeEcmaString::SIZE <= allocateLength) {
// heuristic: if tree string uses less memory than linestring, it is worth.
// In other words, we hope tree string can work for the large strings join.
return true;
}
}
return false;
}
template <typename Container>
JSTaggedValue JSStableArray::JoinUseTreeString(const JSThread *thread, const JSHandle<JSTaggedValue> receiverValue,
const JSHandle<EcmaString> sepStringHandle, uint32_t sepLength,
Container &arrElements, int elemNum)
{
// Do not concat the elements one by one, it will make the tree string unbalanced. Concat each element with its
// right neighbor first level by level, then the tree string will be balanced as possible.
if (sepLength != 0) {
for (int k = 0; k < elemNum - 1; k++) {
arrElements[k] = JSHandle<EcmaString>(
thread, EcmaStringAccessor::Concat(thread->GetEcmaVM(), arrElements[k], sepStringHandle));
RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
}
}
while (elemNum > 1) {
size_t newNum = (elemNum + 1) / NUM_2;
for (size_t i = 0; i < elemNum / NUM_2; ++i) {
arrElements[i] = JSHandle<EcmaString>(
thread,
EcmaStringAccessor::Concat(thread->GetEcmaVM(), arrElements[NUM_2 * i], arrElements[NUM_2 * i + 1])
);
RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
}
if (elemNum % NUM_2 == 1) {
arrElements[newNum - 1] = arrElements[elemNum - 1];
}
elemNum = newNum;
}
thread->GetCurrentEcmaContext()->JoinStackPopFastPath(receiverValue);
return arrElements[0].GetTaggedValue();
}
template <typename Container>
void JSStableArray::ProcessElements(JSThread *thread, JSHandle<JSTaggedValue> receiverValue, uint32_t len,
Container &arrElements, bool &isOneByte, uint64_t &allocateLength)
{
JSMutableHandle<JSTaggedValue> elementHandle(thread, JSTaggedValue::Undefined());
JSHandle<JSObject> obj(thread, receiverValue.GetTaggedValue());
JSTaggedValue element = JSTaggedValue::Undefined();
for (uint32_t k = 0; k < len; k++) {
element = ElementAccessor::Get(obj, k);
if (element.IsHole() && JSTaggedValue::HasProperty(thread, receiverValue, k)) {
element = JSArray::FastGetPropertyByValue(thread, receiverValue, k).GetTaggedValue();
RETURN_IF_ABRUPT_COMPLETION(thread);
}
if (!element.IsUndefinedOrNull() && !element.IsHole()) {
if (!element.IsString()) {
elementHandle.Update(element);
JSHandle<EcmaString> strElement = JSTaggedValue::ToString(thread, elementHandle);
RETURN_IF_ABRUPT_COMPLETION(thread);
element = strElement.GetTaggedValue();
}
auto nextStr = EcmaString::Cast(element.GetTaggedObject());
arrElements[k] = JSHandle<EcmaString>(thread, nextStr);
isOneByte = isOneByte & EcmaStringAccessor(nextStr).IsUtf8();
allocateLength += EcmaStringAccessor(nextStr).GetLength();
} else {
arrElements[k] = JSHandle<EcmaString>(thread->GlobalConstants()->GetHandledEmptyString());
}
}
}
template <typename Container>
JSTaggedValue JSStableArray::DoStableArrayJoin(JSThread *thread, JSHandle<JSTaggedValue> receiverValue, uint32_t len,
Container &arrElements, bool &isOneByte, uint32_t sep,
uint32_t sepLength, JSHandle<EcmaString> sepStringHandle)
{
auto context = thread->GetCurrentEcmaContext();
uint64_t allocateLength = 0;
ProcessElements(thread, receiverValue, len, arrElements, isOneByte, allocateLength);
RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
if (len > 0) {
allocateLength += static_cast<uint64_t>(sepLength) * (len - 1);
}
if (allocateLength > EcmaString::MAX_STRING_LENGTH) {
context->JoinStackPopFastPath(receiverValue);
THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
}
if (WorthUseTreeString(sepLength, allocateLength, len)) {
return JoinUseTreeString(thread, receiverValue, sepStringHandle, sepLength, arrElements, len);
}
// 5. Let R be the empty String.
auto newString =
EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), static_cast<size_t>(allocateLength), isOneByte);
int current = 0;
DISALLOW_GARBAGE_COLLECTION;
// 6. Repeat, while k < len
for (uint32_t k = 0; k < len; k++) {
// a. If k > 0, set R to the string-concatenation of R and sep.
if (k > 0) {
if (sepLength == 1) {
EcmaStringAccessor(newString).Set(current, static_cast<uint16_t>(sep));
} else if (sepLength > 1) {
EcmaStringAccessor::ReadData(newString, *sepStringHandle, current,
allocateLength - static_cast<uint32_t>(current), sepLength);
}
current += static_cast<int>(sepLength);
}
// b. Let element be ? Get(O, ToString(𝔽(k))).
JSHandle<EcmaString> nextStr = arrElements[k];
// c. Set R to the string-concatenation of R and S
int nextLength = static_cast<int>(EcmaStringAccessor(nextStr).GetLength());
EcmaStringAccessor::ReadData(newString, *nextStr, current, allocateLength - static_cast<uint32_t>(current),
nextLength);
current += nextLength;
}
ASSERT_PRINT(isOneByte == EcmaStringAccessor::CanBeCompressed(newString),
"isOneByte does not match the real value!");
context->JoinStackPopFastPath(receiverValue);
// return R
return JSTaggedValue(newString);
}
JSTaggedValue JSStableArray::Join(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv)
{
JSThread *thread = argv->GetThread();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
auto context = thread->GetCurrentEcmaContext();
// 1. Let O be ToObject(this.value)
JSHandle<JSTaggedValue> receiverValue = JSHandle<JSTaggedValue>::Cast(receiver);
JSHandle<JSObject> obj(thread, receiverValue.GetTaggedValue());
// 2. Let len be ToLength(Get(O, "length"))
uint32_t len = receiver->GetArrayLength();
int sep = ',';
uint32_t sepLength = 1;
JSHandle<JSTaggedValue> sepHandle = base::BuiltinsBase::GetCallArg(argv, 0);
JSHandle<EcmaString> sepStringHandle;
if (sepHandle->IsUndefined()) {
// 3. If separator is undefined, let sep be ",".
sepHandle = globalConst->GetHandledCommaString();
sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
} else {
// 4. Else, let sep be ? ToString(separator).
sepStringHandle = JSTaggedValue::ToString(thread, sepHandle);
RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
sepLength = EcmaStringAccessor(sepStringHandle).GetLength();
if (sepLength == 1) {
sep = EcmaStringAccessor(sepStringHandle).Get(0);
}
}
bool isOneByte = EcmaStringAccessor(sepStringHandle).IsUtf8();
// Fastpath should put after parsing "sep". Error may occur if sep cannout be transformed to string,
// which should be handled before fastpath return.
if (len == 0) {
context->JoinStackPopFastPath(receiverValue);
return globalConst->GetEmptyString();
}
if (len == 1) {
// sep unused, set isOneByte to default(true)
isOneByte = true;
}
// Use stack memory if the number of elements is less than USE_STACK_MEMORY_THRESHOLD.
// arr can be faster then vector.
if (len <= USE_STACK_MEMORY_THRESHOLD) {
std::array<JSHandle<EcmaString>, USE_STACK_MEMORY_THRESHOLD> arr;
return DoStableArrayJoin(thread, receiverValue, len, arr, isOneByte, sep, sepLength, sepStringHandle);
} else {
CVector<JSHandle<EcmaString>> vec(len);
return DoStableArrayJoin(thread, receiverValue, len, vec, isOneByte, sep, sepLength, sepStringHandle);
}
}
#endif
#if !ENABLE_NEXT_OPTIMIZATION
void JSStableArray::SetSepValue(JSHandle<EcmaString> sepStringHandle, int &sep, uint32_t &sepLength)
{
if (EcmaStringAccessor(sepStringHandle).IsUtf8() && EcmaStringAccessor(sepStringHandle).GetLength() == 1) {
@ -446,17 +639,18 @@ bool JSStableArray::WorthUseTreeString(int sep, size_t allocateLength, uint32_t
return false;
}
JSTaggedValue JSStableArray::JoinUseTreeString(const JSThread* thread, const JSHandle<JSTaggedValue> receiverValue,
JSTaggedValue JSStableArray::JoinUseTreeString(const JSThread *thread,
const JSHandle<JSTaggedValue> receiverValue,
const JSHandle<EcmaString> sepStringHandle, const int sep,
CVector<JSHandle<EcmaString>>& vec)
CVector<JSHandle<EcmaString>> &vec)
{
// Do not concat the elements one by one, it will make the tree string unbalanced. Concat each element with its
// right neighbor first level by level, then the tree string will be balanced as possible.
if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
auto last = std::prev(vec.end());
for (auto iter = vec.begin(); iter != last; ++iter) {
*iter = JSHandle<EcmaString>(
thread, EcmaStringAccessor::Concat(thread->GetEcmaVM(), *iter, sepStringHandle));
*iter =
JSHandle<EcmaString>(thread, EcmaStringAccessor::Concat(thread->GetEcmaVM(), *iter, sepStringHandle));
RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue);
}
}
@ -551,7 +745,7 @@ JSTaggedValue JSStableArray::Join(JSHandle<JSArray> receiver, EcmaRuntimeCallInf
return JoinUseTreeString(thread, receiverValue, sepStringHandle, sep, vec);
}
auto newString =
EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), static_cast<size_t>(allocateLength), isOneByte);
EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), static_cast<size_t>(allocateLength), isOneByte);
int current = 0;
DISALLOW_GARBAGE_COLLECTION;
for (uint32_t k = 0; k < len; k++) {
@ -560,21 +754,22 @@ JSTaggedValue JSStableArray::Join(JSHandle<JSArray> receiver, EcmaRuntimeCallInf
EcmaStringAccessor(newString).Set(current, static_cast<uint16_t>(sep));
} else if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) {
EcmaStringAccessor::ReadData(newString, *sepStringHandle, current,
allocateLength - static_cast<uint32_t>(current), sepLength);
allocateLength - static_cast<uint32_t>(current), sepLength);
}
current += static_cast<int>(sepLength);
}
JSHandle<EcmaString> nextStr = vec[k];
int nextLength = static_cast<int>(EcmaStringAccessor(nextStr).GetLength());
EcmaStringAccessor::ReadData(newString, *nextStr, current,
allocateLength - static_cast<uint32_t>(current), nextLength);
EcmaStringAccessor::ReadData(newString, *nextStr, current, allocateLength - static_cast<uint32_t>(current),
nextLength);
current += nextLength;
}
ASSERT_PRINT(
isOneByte == EcmaStringAccessor::CanBeCompressed(newString), "isOneByte does not match the real value!");
ASSERT_PRINT(isOneByte == EcmaStringAccessor::CanBeCompressed(newString),
"isOneByte does not match the real value!");
context->JoinStackPopFastPath(receiverValue);
return JSTaggedValue(newString);
}
#endif
JSTaggedValue JSStableArray::HandleFindIndexOfStable(JSThread *thread, JSHandle<JSObject> thisObjHandle,
JSHandle<JSTaggedValue> callbackFnHandle,

View File

@ -26,7 +26,9 @@
namespace panda::ecmascript {
class JSStableArray {
public:
#if !ENABLE_NEXT_OPTIMIZATION
enum SeparatorFlag : int { MINUS_ONE = -1, MINUS_TWO = -2 };
#endif
static JSTaggedValue Push(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv);
static JSTaggedValue Push(JSHandle<JSSharedArray> receiver, EcmaRuntimeCallInfo *argv);
static JSTaggedValue Pop(JSHandle<JSArray> receiver, EcmaRuntimeCallInfo *argv);
@ -100,7 +102,6 @@ public:
JSHandle<JSTaggedValue> thisArgHandle, int64_t &k);
private:
static void SetSepValue(JSHandle<EcmaString> sepStringHandle, int &sep, uint32_t &sepLength);
enum class IndexOfType {
IndexOf,
LastIndexOf
@ -138,15 +139,37 @@ private:
static void HandleArray(JSHandle<JSObject> &newArrayHandle, uint32_t &actualDeleteCount,
JSThread *thread, uint32_t &start, JSHandle<JSObject> &thisObjHandle,
JSHandle<JSTaggedValue> &holeHandle);
static JSTaggedValue JoinUseTreeString(const JSThread* thread, JSHandle<JSTaggedValue> receiverValue,
JSHandle<EcmaString> sepStringHandle, int sep,
CVector<JSHandle<EcmaString>>& vec);
#if !ENABLE_NEXT_OPTIMIZATION
static void SetSepValue(JSHandle<EcmaString> sepStringHandle, int &sep, uint32_t &sepLength);
static JSTaggedValue JoinUseTreeString(const JSThread *thread,
const JSHandle<JSTaggedValue> receiverValue,
const JSHandle<EcmaString> sepStringHandle, const int sep,
CVector<JSHandle<EcmaString>> &vec);
inline static bool WorthUseTreeString(int sep, size_t allocateLength, uint32_t len);
#endif
// Allocate object larger than 256 need liner search in the free object list,
// so try to use tree string when the join result is larger than 256.
static constexpr size_t TREE_STRING_THRESHOLD = 256;
static constexpr size_t NUM_2 = 2;
#if ENABLE_NEXT_OPTIMIZATION
// When Array length is no more than 64, use array (stack memory) instead of vector to store the elements.
static constexpr size_t USE_STACK_MEMORY_THRESHOLD = 64;
inline static bool WorthUseTreeString(uint32_t sepLength, size_t allocateLength, uint32_t len);
template <typename Container>
static void ProcessElements(JSThread *thread, JSHandle<JSTaggedValue> receiverValue, uint32_t len,
Container &arrElements, bool &isOneByte, uint64_t &allocateLength);
template <typename Container>
static JSTaggedValue DoStableArrayJoin(JSThread *thread, JSHandle<JSTaggedValue> receiverValue, uint32_t len,
Container &arrElements, bool &isOneByte, uint32_t sep,
uint32_t sepLength, JSHandle<EcmaString> sepStringHandle);
template <typename Container>
static JSTaggedValue JoinUseTreeString(const JSThread *thread, JSHandle<JSTaggedValue> receiverValue,
JSHandle<EcmaString> sepStringHandle, uint32_t sepLength,
Container &arrElements, int elemNum);
#endif
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_JS_STABLE_ARRAY_H

View File

@ -37,20 +37,32 @@ enum class StableArrayIndex {
namespace panda::test {
class JSStableArrayTest : public BaseTestWithScope<false> {
public:
JSHandle<JSTaggedValue> CallJoin(JSHandle<TaggedArray> handleTagArr, std::string& sep, int64_t lengthArr) const
JSHandle<JSTaggedValue> CallJoin(JSHandle<TaggedArray> handleTagArr, JSTaggedValue sepValue) const
{
ObjectFactory* objFactory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSArray> handleArr(JSArray::CreateArrayFromList(thread, handleTagArr));
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetCallArg(0,
JSHandle<JSTaggedValue>::Cast(objFactory->NewFromStdString(sep)).GetTaggedValue());
ecmaRuntimeCallInfo->SetCallArg(0, sepValue);
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
JSHandle<JSTaggedValue> handleTagValEcmaStrRet(thread, JSStableArray::Join(handleArr, ecmaRuntimeCallInfo));
TestHelper::TearDownFrame(thread, prev);
return handleTagValEcmaStrRet;
}
// tests for sep is Undefined
JSHandle<JSTaggedValue> CallJoin(JSHandle<TaggedArray> handleTagArr, int64_t lengthArr) const
{
JSTaggedValue sepValue = JSTaggedValue::Undefined();
return CallJoin(handleTagArr, sepValue);
}
JSHandle<JSTaggedValue> CallJoin(JSHandle<TaggedArray> handleTagArr, std::string& sep, int64_t lengthArr) const
{
ObjectFactory* objFactory = thread->GetEcmaVM()->GetFactory();
JSTaggedValue sepValue = JSHandle<JSTaggedValue>::Cast(objFactory->NewFromStdString(sep)).GetTaggedValue();
return CallJoin(handleTagArr, sepValue);
}
};
/**
@ -501,6 +513,178 @@ HWTEST_F_L0(JSStableArrayTest, Join_StringElements_LargeString3)
EXPECT_TRUE(EcmaStringAccessor(handleEcmaStrRet).IsTreeString());
}
/**
* @tc.name: Join_StringElements_Stack
* @tc.desc: Use stack to store the preprocessed elements of the source Array.
* @tc.type: FUNC
* @tc.require:
*/
HWTEST_F_L0(JSStableArrayTest, Join_StringElements_Stack)
{
int32_t lengthArr = 32;
// tiny string join should not use tree string.
ObjectFactory *objFactory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> handleTagArr(objFactory->NewTaggedArray(lengthArr));
JSHandle<JSTaggedValue> handleTagValElementEcmaStr(objFactory->NewFromStdString("a"));
for (int i = 0; i < lengthArr; i++) {
handleTagArr->Set(thread, i, handleTagValElementEcmaStr.GetTaggedValue());
}
// sep is Undefined
JSHandle<JSTaggedValue> handleTagValEcmaStrRet = CallJoin(handleTagArr, lengthArr);
JSHandle<EcmaString> handleEcmaStrRet(handleTagValEcmaStrRet);
// 32 x a
EXPECT_STREQ(EcmaStringAccessor(handleEcmaStrRet).ToCString().c_str(),
"a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a");
EXPECT_FALSE(EcmaStringAccessor(handleEcmaStrRet).IsTreeString());
}
/**
* @tc.name: Join_StringElements_Stack1
* @tc.desc: Use stack to store the preprocessed elements of the source Array.
* @tc.type: FUNC
* @tc.require:
*/
HWTEST_F_L0(JSStableArrayTest, Join_StringElements_Stack1)
{
int32_t lengthArr = 4;
// large string should use tree string.
ObjectFactory *objFactory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> handleTagArr(objFactory->NewTaggedArray(lengthArr));
// 64 x a
JSHandle<JSTaggedValue> handleTagValElementEcmaStr(
objFactory->NewFromStdString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
for (int i = 0; i < lengthArr; i++) {
handleTagArr->Set(thread, i, handleTagValElementEcmaStr.GetTaggedValue());
}
// sep is Undefined
JSHandle<JSTaggedValue> handleTagValEcmaStrRet = CallJoin(handleTagArr, lengthArr);
JSHandle<EcmaString> handleEcmaStrRet(handleTagValEcmaStrRet);
EXPECT_STREQ(EcmaStringAccessor(handleEcmaStrRet).ToCString().c_str(),
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
EXPECT_TRUE(EcmaStringAccessor(handleEcmaStrRet).IsTreeString());
}
/**
* @tc.name: Join_StringElements_Vector
* @tc.desc: Use vector to store the preprocessed elements of the source Array.
* @tc.type: FUNC
* @tc.require:
*/
HWTEST_F_L0(JSStableArrayTest, Join_StringElements_Vector)
{
int32_t lengthArr = 128;
// tiny string join should not use tree string.
ObjectFactory *objFactory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> handleTagArr(objFactory->NewTaggedArray(lengthArr));
JSHandle<JSTaggedValue> handleTagValElementEcmaStr(objFactory->NewFromStdString("a"));
for (int i = 0; i < lengthArr; i++) {
handleTagArr->Set(thread, i, handleTagValElementEcmaStr.GetTaggedValue());
}
// sep is Undefined
JSHandle<JSTaggedValue> handleTagValEcmaStrRet = CallJoin(handleTagArr, lengthArr);
JSHandle<EcmaString> handleEcmaStrRet(handleTagValEcmaStrRet);
// 128 x a
EXPECT_STREQ(EcmaStringAccessor(handleEcmaStrRet).ToCString().c_str(),
"a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,"
"a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,"
"a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,"
"a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a");
EXPECT_FALSE(EcmaStringAccessor(handleEcmaStrRet).IsTreeString());
}
/**
* @tc.name: Join_StringElements_Vector1
* @tc.desc: Use vector to store the preprocessed elements of the source Array.
* @tc.type: FUNC
* @tc.require:
*/
HWTEST_F_L0(JSStableArrayTest, Join_StringElements_Vector1)
{
int32_t lengthArr = 65;
std::string sep = "";
// large string should use tree string.
ObjectFactory *objFactory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> handleTagArr(objFactory->NewTaggedArray(lengthArr));
// 40 x a
JSHandle<JSTaggedValue> handleTagValElementEcmaStr(
objFactory->NewFromStdString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
for (int i = 0; i < lengthArr; i++) {
handleTagArr->Set(thread, i, handleTagValElementEcmaStr.GetTaggedValue());
}
JSHandle<JSTaggedValue> handleTagValEcmaStrRet = CallJoin(handleTagArr, sep, lengthArr);
JSHandle<EcmaString> handleEcmaStrRet(handleTagValEcmaStrRet);
EXPECT_STREQ(EcmaStringAccessor(handleEcmaStrRet).ToCString().c_str(),
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
EXPECT_TRUE(EcmaStringAccessor(handleEcmaStrRet).IsTreeString());
}
/**
* @tc.name: At
* @tc.desc: Create a source Array whose elements are Numbers and an EcmaRuntimeCallInfo, define the first arg of the