* Copyright (c) 2021-2025 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.
*/
#ifndef ECMASCRIPT_STRING_H
#define ECMASCRIPT_STRING_H
#include <cstddef>
#include <cstdint>
#include <cstring>
#include "common_components/base/utf_helper.h"
#include "ecmascript/common.h"
#include "ecmascript/ecma_macros.h"
#include "ecmascript/js_hclass.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/mem/barriers.h"
#include "ecmascript/mem/space.h"
#include "ecmascript/mem/tagged_object.h"
#include "ecmascript/string/base_string.h"
#include "ecmascript/string/base_string-inl.h"
#include "ecmascript/string/external_string.h"
#include "ecmascript/string/line_string.h"
#include "ecmascript/string/sliced_string.h"
#include "ecmascript/string/tree_string.h"
#include "libpandabase/macros.h"
#include "securec.h"
#include "unicode/locid.h"
namespace panda {
namespace test {
class EcmaStringEqualsTest;
class EcmaStringHashTest;
}
namespace ecmascript {
template<typename T>
class JSHandle;
class JSPandaFile;
class EcmaVM;
class LineEcmaString;
class TreeEcmaString;
class SlicedEcmaString;
class CachedExternalEcmaString;
class ExternalNonMovableStringResource;
class FlatStringInfo;
#define ECMA_STRING_CHECK_LENGTH_AND_TRHOW(vm, length) \
if ((length) >= BaseString::MAX_STRING_LENGTH) { \
THROW_RANGE_ERROR_AND_RETURN((vm)->GetJSThread(), "Invalid string length", nullptr); \
}
class EcmaString : public TaggedObject {
private:
static constexpr size_t SIZE = BaseString::SIZE;
public:
CAST_CHECK(EcmaString, IsString);
BaseString* ToBaseString()
{
return BaseString::Cast(this);
}
const BaseString* ToBaseString() const
{
return BaseString::ConstCast(this);
}
static EcmaString* FromBaseString(BaseString* str)
{
return reinterpret_cast<EcmaString*>(str);
}
enum TrimMode : uint8_t {
TRIM,
TRIM_START,
TRIM_END,
};
void SetMixHashcode(uint32_t mixHashCode)
{
ToBaseString()->SetMixHashcode(mixHashCode);
}
private:
friend class EcmaStringAccessor;
friend class LineEcmaString;
friend class TreeEcmaString;
friend class SlicedEcmaString;
friend class CachedExternalEcmaString;
friend class FlatStringInfo;
friend class NameDictionary;
friend class panda::test::EcmaStringEqualsTest;
friend class panda::test::EcmaStringHashTest;
static constexpr size_t ALIGNMENT_8_BYTES = 8;
static EcmaString *CreateEmptyString(const EcmaVM *vm);
static EcmaString *CreateFromUtf8(const EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len,
bool canBeCompress, MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE);
static EcmaString *CreateFromUtf8CompressedSubString(const EcmaVM *vm, const JSHandle<EcmaString> &string,
uint32_t offset, uint32_t utf8Len, MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE);
static EcmaString *CreateUtf16StringFromUtf8(const EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len,
MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE);
static EcmaString *CreateFromUtf16(const EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len,
bool canBeCompress, MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE);
static EcmaString *CreateFromExternalResource(const EcmaVM *vm, void *data, uint32_t length,
bool canBeCompress, ExternalStringResourceCallback callback, void *hint);
static SlicedEcmaString* CreateSlicedString(const EcmaVM* vm, JSHandle<EcmaString> parent,
MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE);
static EcmaString *CreateLineString(const EcmaVM *vm, size_t length, bool compressed);
static EcmaString *CreateLineStringNoGC(const EcmaVM *vm, size_t length, bool compressed);
static EcmaString *AllocLineString(const EcmaVM* vm, size_t size, MemSpaceType type);
static EcmaString *CreateLineStringWithSpaceType(const EcmaVM *vm,
size_t length, bool compressed, MemSpaceType type);
static EcmaString *CreateTreeString(const EcmaVM *vm,
const JSHandle<EcmaString> &left, const JSHandle<EcmaString> &right, uint32_t length, bool compressed);
static EcmaString *Concat(const EcmaVM *vm, const JSHandle<EcmaString> &left,
const JSHandle<EcmaString> &right, MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE);
template<typename T1, typename T2>
static uint32_t CalculateDataConcatHashCode(const T1 *dataFirst, size_t sizeFirst,
const T2 *dataSecond, size_t sizeSecond);
static uint32_t CalculateConcatHashCode(const JSThread *thread,
const JSHandle<EcmaString> &firstString,
const JSHandle<EcmaString> &secondString);
bool HashIntegerString(uint32_t length, uint32_t *hash, uint32_t hashSeed) const;
inline bool IsInteger(const JSThread* thread)
{
auto readBarrier = [thread](const void* obj, size_t offset)-> TaggedObject* {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToBaseString()->IsInteger(std::move(readBarrier));
}
static EcmaString *CopyStringToOldSpace(const EcmaVM *vm, const JSHandle<EcmaString> &original,
uint32_t length, bool compressed);
static EcmaString *FastSubString(const EcmaVM *vm,
const JSHandle<EcmaString> &src, uint32_t start, uint32_t length);
static bool SubStringIsUtf8(const EcmaVM *vm,
const JSHandle<EcmaString> &src, uint32_t start, uint32_t length);
static EcmaString *GetSlicedString(const EcmaVM *vm,
const JSHandle<EcmaString> &src, uint32_t start, uint32_t length);
static EcmaString *GetSubString(const EcmaVM *vm,
const JSHandle<EcmaString> &src, uint32_t start, uint32_t length);
static inline EcmaString *FastSubUtf8String(const EcmaVM *vm,
const JSHandle<EcmaString> &src, uint32_t start, uint32_t length);
static inline EcmaString *FastSubUtf16String(const EcmaVM *vm,
const JSHandle<EcmaString> &src, uint32_t start, uint32_t length);
inline void TrimLineString(uint32_t newLength);
inline bool IsUtf8() const
{
return ToBaseString()->IsUtf8();
}
inline bool IsUtf16() const
{
return ToBaseString()->IsUtf16();
}
inline const uint8_t *GetDataUtf8() const;
inline const uint16_t *GetDataUtf16() const;
inline common::Span<const uint8_t> FastToUtf8Span() const;
inline uint8_t *GetDataUtf8Writable();
inline uint16_t *GetDataUtf16Writable();
inline uint32_t GetLength() const
{
return ToBaseString()->GetLength();
}
inline void InitLengthAndFlags(uint32_t length, bool compressed = false, bool isIntern = false)
{
ToBaseString()->InitLengthAndFlags(length, compressed, isIntern);
}
inline size_t GetUtf8Length(const JSThread *thread, bool modify = true, bool isGetBufferSize = false) const;
inline void SetIsInternString()
{
ToBaseString()->SetIsInternString();
}
inline bool IsInternString() const
{
return ToBaseString()->IsInternString();
}
inline void ClearInternStringFlag()
{
ToBaseString()->ClearInternStringFlag();
}
inline bool TryGetHashCode(uint32_t *hash)
{
return ToBaseString()->TryGetHashCode(hash);
}
uint32_t PUBLIC_API GetHashcode(const JSThread *thread)
{
auto readBarrier = [thread](const void* obj, size_t offset)-> TaggedObject* {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToBaseString()->GetHashcode(std::move(readBarrier));
}
uint32_t PUBLIC_API ComputeHashcode(const JSThread *thread) const
{
auto readBarrier = [thread](const void* obj, size_t offset)-> TaggedObject* {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToBaseString()->ComputeHashcode(std::move(readBarrier));
}
static uint32_t ComputeHashcodeUtf8(const uint8_t *utf8Data, size_t utf8Len, bool canBeCompress);
static uint32_t ComputeHashcodeUtf16(const uint16_t *utf16Data, uint32_t length);
template<bool verify = true>
uint16_t At(const JSThread *thread, int32_t index) const;
void WriteData(uint32_t index, uint16_t src);
static int32_t Compare(const EcmaVM *vm, const JSHandle<EcmaString> &left, const JSHandle<EcmaString> &right);
static bool IsSubStringAt(const EcmaVM *vm, const JSHandle<EcmaString>& left,
const JSHandle<EcmaString>& right, uint32_t offset);
template<typename T, typename T1>
static bool StringsAreEquals(common::Span<const T> &str1, common::Span<const T1> &str2);
bool EqualToSplicedString(const JSThread *thread, const EcmaString *str1, const EcmaString *str2);
static PUBLIC_API bool StringsAreEqual(const EcmaVM *vm, const JSHandle<EcmaString> &str1,
const JSHandle<EcmaString> &str2);
template <RBMode mode = RBMode::DEFAULT_RB>
static PUBLIC_API bool StringsAreEqual(const JSThread *thread, EcmaString *str1, EcmaString *str2)
{
auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
return Barriers::GetTaggedObject<mode>(thread, obj, offset);
};
return BaseString::StringsAreEqual(std::move(readBarrier), str1->ToBaseString(), str2->ToBaseString());
}
static bool StringsAreEqualDiffUtfEncoding(const JSThread *thread, EcmaString *str1, EcmaString *str2);
static bool StringsAreEqualDiffUtfEncoding(const FlatStringInfo &str1, const FlatStringInfo &str2);
static bool StringIsEqualUint8Data(const JSThread *thread,
const EcmaString *str1, const uint8_t *dataAddr, uint32_t dataLen,
bool canBeCompress);
static bool StringsAreEqualUtf16(const JSThread *thread,
const EcmaString *str1, const uint16_t *utf16Data, uint32_t utf16Len);
static int32_t IndexOf(const EcmaVM *vm,
const JSHandle<EcmaString> &receiver, const JSHandle<EcmaString> &search, int pos = 0);
static int32_t LastIndexOf(const EcmaVM *vm,
const JSHandle<EcmaString> &receiver, const JSHandle<EcmaString> &search, int pos = 0);
size_t WriteUtf8(const JSThread *thread, uint8_t *buf, size_t maxLength, bool isWriteBuffer = false) const;
size_t WriteUtf16(const JSThread *thread, uint16_t *buf, uint32_t targetLength, uint32_t bufLength) const;
size_t WriteOneByte(const JSThread *thread, uint8_t *buf, size_t maxLength) const;
uint32_t CopyDataUtf16(const JSThread *thread, uint16_t *buf, uint32_t maxLength) const;
std::u16string ToU16String(const JSThread *thread, uint32_t len = 0);
common::Span<const uint8_t> ToUtf8Span(const JSThread *thread, CVector<uint8_t> &buf, bool modify = true,
bool cesu8 = false);
common::Span<const uint8_t> DebuggerToUtf8Span(const JSThread *thread, CVector<uint8_t> &buf, bool modify = true);
Span<const uint16_t> ToUtf16Span(const JSThread *thread, CVector<uint16_t> &buf)
{
Span<const uint16_t> str;
uint32_t strLen = GetLength();
if (UNLIKELY(IsUtf16())) {
const uint16_t *data = EcmaString::GetUtf16DataFlat(thread, this, buf);
str = Span<const uint16_t>(data, strLen);
} else {
CVector<uint8_t> tmpBuf;
const uint8_t *data = EcmaString::GetUtf8DataFlat(thread, this, tmpBuf);
buf.reserve(strLen);
auto utf16Len = common::utf_helper::ConvertRegionUtf8ToUtf16(data, buf.data(), strLen, strLen);
#if !defined(NDEBUG)
auto calculatedLen = common::utf_helper::Utf8ToUtf16Size(data, strLen);
ASSERT_PRINT(utf16Len == calculatedLen, "Bad utf8 to utf16 conversion!");
#endif
str = Span<const uint16_t>(buf.data(), utf16Len);
}
return str;
}
template <RBMode mode = RBMode::DEFAULT_RB>
void WriteData(const JSThread *thread, EcmaString *src, uint32_t start, uint32_t destSize, uint32_t length)
{
auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
return Barriers::GetTaggedObject<mode>(thread, obj, offset);
};
LineString::Cast(ToBaseString())->WriteData(std::move(readBarrier), src->ToBaseString(), start, destSize,
length);
}
static bool CanBeCompressed(const uint8_t *utf8Data, uint32_t utf8Len);
static bool CanBeCompressed(const uint16_t *utf16Data, uint32_t utf16Len);
static bool CanBeCompressed(const EcmaString *string);
bool PUBLIC_API ToElementIndex(const JSThread *thread, uint32_t *index);
bool ToInt(const JSThread *thread, int32_t *index, bool *negative);
bool ToUInt64FromLoopStart(uint64_t *index, uint32_t loopStart, const uint8_t *data);
bool PUBLIC_API ToTypedArrayIndex(const JSThread *thread, uint32_t *index);
template <typename T>
static EcmaString* TrimBody(const JSThread* thread, const JSHandle<EcmaString>& src, common::Span<T>& data,
TrimMode mode);
static EcmaString* Trim(const JSThread* thread, const JSHandle<EcmaString>& src, TrimMode mode = TrimMode::TRIM);
template<typename T>
static bool MemCopyChars(common::Span<T> &dst, size_t dstMax, common::Span<const T> &src, size_t count);
template<typename T1, typename T2>
static int32_t IndexOf(common::Span<const T1> &lhsSp, common::Span<const T2> &rhsSp, int32_t pos, int32_t max);
template<typename T1, typename T2>
static int32_t LastIndexOf(common::Span<const T1> &lhsSp, common::Span<const T2> &rhsSp, int32_t pos);
bool IsFlat(const JSThread *thread) const;
bool IsLineString() const
{
return ToBaseString()->IsLineString();
}
bool IsSlicedString() const
{
return ToBaseString()->IsSlicedString();
}
bool IsTreeString() const
{
return ToBaseString()->IsTreeString();
}
bool NotTreeString() const
{
return !IsTreeString();
}
bool IsCachedExternalString() const
{
return ToBaseString()->IsCachedExternalString();
}
bool IsLineOrCachedExternalString() const
{
return IsLineString() || IsCachedExternalString();
}
template <typename Char>
static void WriteToFlat(const JSThread *thread, EcmaString *src, Char *buf, uint32_t maxLength);
template <typename Char>
static void WriteToFlatWithPos(const JSThread *thread, EcmaString *src, Char *buf, uint32_t length, uint32_t pos);
static const uint8_t *PUBLIC_API GetUtf8DataFlat(const JSThread *thread, const EcmaString *src,
CVector<uint8_t> &buf);
static const uint8_t *PUBLIC_API GetNonTreeUtf8Data(const JSThread *thread, const EcmaString *src);
static const uint16_t *PUBLIC_API GetUtf16DataFlat(const JSThread *thread, const EcmaString *src,
CVector<uint16_t> &buf);
static const uint16_t *PUBLIC_API GetNonTreeUtf16Data(const JSThread *thread, const EcmaString *src);
static EcmaString *SlowFlatten(const EcmaVM *vm, const JSHandle<EcmaString> &string, MemSpaceType type);
PUBLIC_API static EcmaString *Flatten(const EcmaVM *vm, const JSHandle<EcmaString> &string,
MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE);
static FlatStringInfo FlattenAllString(const EcmaVM *vm, const JSHandle<EcmaString> &string,
MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE);
static EcmaString *FlattenNoGCForSnapshot(const EcmaVM *vm, EcmaString *string);
static EcmaString *ToLower(const EcmaVM *vm, const JSHandle<EcmaString> &src);
static EcmaString *ToUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src);
static EcmaString *ToLocaleLower(const EcmaVM *vm, const JSHandle<EcmaString> &src, const icu::Locale &locale);
static EcmaString *ToLocaleUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src, const icu::Locale &locale);
static EcmaString *TryToLower(const EcmaVM *vm, const JSHandle<EcmaString> &src);
static EcmaString *TryToUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src);
static EcmaString *ConvertUtf8ToLowerOrUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src,
bool toLower, uint32_t startIndex = 0);
};
class LineEcmaString : public EcmaString {
private:
static constexpr size_t SIZE = LineString::SIZE;
public:
LineString* ToLineString()
{
return LineString::Cast(this);
}
const LineString* ToLineString() const
{
return LineString::ConstCast(this);
}
CAST_CHECK(LineEcmaString, IsLineString);
DECL_VISIT_ARRAY(LineString::DATA_OFFSET, 0, GetPointerLength());
static LineEcmaString* Cast(EcmaString* str)
{
return static_cast<LineEcmaString *>(str);
}
static LineEcmaString *Cast(const EcmaString *str)
{
return LineEcmaString::Cast(const_cast<EcmaString *>(str));
}
static size_t ComputeSizeUtf8(uint32_t utf8Len)
{
return LineString::ComputeSizeUtf8(utf8Len);
}
static size_t ComputeSizeUtf16(uint32_t utf16Len)
{
return LineString::ComputeSizeUtf16(utf16Len);
}
static size_t ObjectSize(EcmaString *str)
{
return LineString::ObjectSize(str->ToBaseString());
}
static size_t DataSize(EcmaString *str)
{
return LineString::DataSize(str->ToBaseString());
}
size_t GetPointerLength()
{
size_t byteSize = DataSize(this);
return AlignUp(byteSize, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT)) / sizeof(JSTaggedType);
}
template<bool verify = true>
uint16_t Get(int32_t index) const
{
return ToLineString()->Get<verify>(index);
}
void Set(uint32_t index, uint16_t src)
{
return ToLineString()->Set(index, src);
}
};
class SlicedEcmaString : public EcmaString {
private:
static constexpr size_t SIZE = SlicedString::SIZE;
public:
DECL_VISIT_OBJECT(SlicedString::PARENT_OFFSET, SlicedString::STARTINDEX_AND_FLAGS_OFFSET);
CAST_CHECK(SlicedEcmaString, IsSlicedString);
SlicedString* ToSlicedString()
{
return SlicedString::Cast(this);
}
const SlicedString* ToSlicedString() const
{
return SlicedString::ConstCast(this);
}
static SlicedEcmaString* FromBaseString(SlicedString* str)
{
return reinterpret_cast<SlicedEcmaString*>(str);
}
uint32_t GetStartIndex() const
{
return ToSlicedString()->GetStartIndex();
}
void SetStartIndex(uint32_t startIndex)
{
ToSlicedString()->SetStartIndex(startIndex);
}
bool GetHasBackingStore() const
{
return ToSlicedString()->GetHasBackingStore();
}
void SetHasBackingStore(bool hasBackingStore)
{
return ToSlicedString()->SetHasBackingStore(hasBackingStore);
}
JSTaggedValue GetParent(const JSThread* thread) const
{
auto readBarrier = [thread](const void* obj, size_t offset)-> TaggedObject* {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return JSTaggedValue(ToSlicedString()->GetParent<TaggedObject*>(std::move(readBarrier)));
}
template <typename T>
void SetParent(const JSThread* thread, JSHandle<T> value, BarrierMode mode = WRITE_BARRIER)
{
auto writeBarrier = [thread, mode](void* obj, size_t offset, BaseObject* str) {
if (mode == WRITE_BARRIER) {
Barriers::SetObject<true>(thread, obj, offset, reinterpret_cast<JSTaggedType>(str));
} else { Barriers::SetPrimitive<JSTaggedType>(obj, offset, reinterpret_cast<JSTaggedType>(str)); }
};
ToSlicedString()->SetParent(std::move(writeBarrier), value.GetTaggedValue().GetTaggedObject());
}
void SetParent(const JSThread* thread, JSTaggedValue value, BarrierMode mode = WRITE_BARRIER)
{
auto writeBarrier = [thread, mode](void* obj, size_t offset, BaseObject* str) {
if (mode == WRITE_BARRIER) {
Barriers::SetObject<true>(thread, obj, offset, reinterpret_cast<JSTaggedType>(str));
} else { Barriers::SetPrimitive<JSTaggedType>(obj, offset, reinterpret_cast<JSTaggedType>(str)); }
};
ToSlicedString()->SetParent(std::move(writeBarrier), value.GetTaggedObject());
};
private:
friend class EcmaString;
static SlicedEcmaString *Cast(EcmaString *str)
{
return static_cast<SlicedEcmaString *>(str);
}
static SlicedEcmaString *Cast(const EcmaString *str)
{
return SlicedEcmaString::Cast(const_cast<EcmaString *>(str));
}
static size_t ObjectSize()
{
return SlicedString::SIZE;
}
template<bool verify = true>
uint16_t Get(const JSThread *thread, int32_t index) const
{
auto readBarrier = [thread](const void* obj, size_t offset)-> TaggedObject* {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToSlicedString()->Get<verify>(std::move(readBarrier), index);
}
};
class TreeEcmaString : public EcmaString {
private:
static constexpr size_t SIZE = TreeString::SIZE;
public:
DECL_VISIT_OBJECT(TreeString::LEFT_OFFSET, TreeString::SIZE);
CAST_CHECK(TreeEcmaString, IsTreeString);
static TreeEcmaString* Cast(EcmaString* str)
{
return static_cast<TreeEcmaString*>(str);
}
static TreeEcmaString* Cast(const EcmaString* str)
{
return TreeEcmaString::Cast(const_cast<EcmaString*>(str));
}
TreeString* ToTreeString()
{
return TreeString::Cast(this);
}
const TreeString* ToTreeString() const
{
return TreeString::ConstCast(this);
}
static TreeEcmaString* FromBaseString(TreeString* str)
{
return reinterpret_cast<TreeEcmaString*>(str);
}
JSTaggedValue GetFirst(const JSThread *thread) const
{
auto readBarrier = [thread](const void* obj, size_t offset)-> TaggedObject* {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return JSTaggedValue(ToTreeString()->GetLeftSubString<TaggedObject*>(std::move(readBarrier)));
}
template <typename T>
void SetFirst(const JSThread* thread, JSHandle<T> value, BarrierMode mode = WRITE_BARRIER)
{
auto writeBarrier = [thread, mode](void* obj, size_t offset, BaseObject* str) {
if (mode == WRITE_BARRIER) {
Barriers::SetObject<true>(thread, obj, offset, reinterpret_cast<JSTaggedType>(str));
} else { Barriers::SetPrimitive<JSTaggedType>(obj, offset, reinterpret_cast<JSTaggedType>(str)); }
};
ToTreeString()->SetLeftSubString(std::move(writeBarrier), value->GetTaggedObject());
}
void SetFirst(const JSThread* thread, JSTaggedValue value, BarrierMode mode = WRITE_BARRIER)
{
auto writeBarrier = [thread, mode](void* obj, size_t offset, BaseObject* str) {
if (mode == WRITE_BARRIER) {
Barriers::SetObject<true>(thread, obj, offset, reinterpret_cast<JSTaggedType>(str));
} else { Barriers::SetPrimitive<JSTaggedType>(obj, offset, reinterpret_cast<JSTaggedType>(str)); }
};
ToTreeString()->SetLeftSubString(std::move(writeBarrier), value.GetTaggedObject());
};
JSTaggedValue GetSecond(const JSThread *thread) const
{
auto readBarrier = [thread](const void* obj, size_t offset)-> TaggedObject* {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return JSTaggedValue(ToTreeString()->GetRightSubString<TaggedObject*>(std::move(readBarrier)));
}
template <typename T>
void SetSecond(const JSThread* thread, JSHandle<T> value, BarrierMode mode = WRITE_BARRIER)
{
auto writeBarrier = [thread, mode](void* obj, size_t offset, BaseObject* str) {
if (mode == WRITE_BARRIER) {
Barriers::SetObject<true>(thread, obj, offset, reinterpret_cast<JSTaggedType>(str));
} else { Barriers::SetPrimitive<JSTaggedType>(obj, offset, reinterpret_cast<JSTaggedType>(str)); }
};
ToTreeString()->SetRightSubString(std::move(writeBarrier), value->GetTaggedObject());
}
void SetSecond(const JSThread* thread, JSTaggedValue value, BarrierMode mode = WRITE_BARRIER)
{
auto writeBarrier = [thread, mode](void* obj, size_t offset, BaseObject* str) {
if (mode == WRITE_BARRIER) {
Barriers::SetObject<true>(thread, obj, offset, reinterpret_cast<JSTaggedType>(str));
} else { Barriers::SetPrimitive<JSTaggedType>(obj, offset, reinterpret_cast<JSTaggedType>(str)); }
};
ToTreeString()->SetRightSubString(std::move(writeBarrier), value.GetTaggedObject());
};
bool IsFlat(const JSThread *thread) const
{
auto readBarrier = [thread](const void* obj, size_t offset)-> TaggedObject* {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToTreeString()->IsFlat(std::move(readBarrier));
}
template<bool verify = true>
uint16_t Get(const JSThread *thread, int32_t index) const
{
auto readBarrier = [thread](const void* obj, size_t offset)-> TaggedObject* {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToTreeString()->Get<verify>(std::move(readBarrier), index);
}
};
class CachedExternalEcmaString : public EcmaString {
private:
static constexpr size_t SIZE = CachedExternalString::SIZE;
public:
DECL_VISIT_PRIMITIVE_OBJECT();
CAST_CHECK(CachedExternalEcmaString, IsCachedExternalString);
static CachedExternalEcmaString* Cast(EcmaString* str)
{
return static_cast<CachedExternalEcmaString*>(str);
}
static CachedExternalEcmaString* Cast(const EcmaString* str)
{
return CachedExternalEcmaString::Cast(const_cast<EcmaString*>(str));
}
CachedExternalString* ToCachedExternalString()
{
return CachedExternalString::Cast(this);
}
const CachedExternalString* ToCachedExternalString() const
{
return CachedExternalString::ConstCast(this);
}
static CachedExternalEcmaString* FromBaseString(CachedExternalString* str)
{
return reinterpret_cast<CachedExternalEcmaString*>(str);
}
ExternalNonMovableStringResource* GetResource() const
{
return ToCachedExternalString()->GetResource();
}
void SetResource(ExternalNonMovableStringResource* resource)
{
ToCachedExternalString()->SetResource(resource);
}
void SetCachedResourceData(uint8_t* data)
{
ToCachedExternalString()->SetCachedResourceData(reinterpret_cast<void*>(data));
}
void SetCachedResourceData(uint16_t* data)
{
ToCachedExternalString()->SetCachedResourceData(reinterpret_cast<void*>(data));
}
template<bool verify = true>
uint16_t Get(int32_t index) const
{
return ToCachedExternalString()->Get<verify>(index);
}
};
class FlatStringInfo {
public:
FlatStringInfo(EcmaString *string, uint32_t startIndex, uint32_t length) : string_(string),
startIndex_(startIndex),
length_(length) {}
bool IsUtf8() const
{
return string_->IsUtf8();
}
bool IsUtf16() const
{
return string_->IsUtf16();
}
EcmaString *GetString() const
{
return string_;
}
void SetString(EcmaString *string)
{
string_ = string;
}
uint32_t GetStartIndex() const
{
return startIndex_;
}
void SetStartIndex(uint32_t index)
{
startIndex_ = index;
}
uint32_t GetLength() const
{
return length_;
}
const uint8_t *GetDataUtf8() const;
const uint16_t *GetDataUtf16() const;
uint8_t *GetDataUtf8Writable() const;
uint16_t *GetDataUtf16Writable() const;
std::u16string ToU16String(uint32_t len = 0);
private:
EcmaString *string_ {nullptr};
uint32_t startIndex_ {0};
uint32_t length_ {0};
};
class PUBLIC_API EcmaStringAccessor {
public:
explicit inline EcmaStringAccessor(EcmaString *string)
{
ASSERT(string != nullptr);
string_ = string;
}
explicit EcmaStringAccessor(TaggedObject *obj);
explicit EcmaStringAccessor(JSTaggedValue value);
explicit EcmaStringAccessor(const JSHandle<EcmaString> &strHandle);
static uint32_t CalculateAllConcatHashCode(const JSThread *thread,
const JSHandle<EcmaString> &firstString,
const JSHandle<EcmaString> &secondString)
{
return EcmaString::CalculateConcatHashCode(thread, firstString, secondString);
}
static EcmaString *CreateLineString(const EcmaVM *vm, size_t length, bool compressed);
static EcmaString *CreateEmptyString(const EcmaVM *vm)
{
return EcmaString::CreateEmptyString(vm);
}
static EcmaString *CreateFromUtf8(const EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress,
MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE)
{
return EcmaString::CreateFromUtf8(vm, utf8Data, utf8Len, canBeCompress, type);
}
static EcmaString *CreateFromUtf8CompressedSubString(const EcmaVM *vm, const JSHandle<EcmaString> &string,
uint32_t offset, uint32_t utf8Len,
MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE)
{
return EcmaString::CreateFromUtf8CompressedSubString(vm, string, offset, utf8Len, type);
}
static EcmaString *CreateUtf16StringFromUtf8(const EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len,
MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE)
{
return EcmaString::CreateUtf16StringFromUtf8(vm, utf8Data, utf16Len, type);
}
static EcmaString *CreateFromUtf16(const EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len,
bool canBeCompress, MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE)
{
return EcmaString::CreateFromUtf16(vm, utf16Data, utf16Len, canBeCompress, type);
}
static EcmaString *CreateFromExternalResource(const EcmaVM *vm, void *data, uint32_t length, bool canBeCompress,
ExternalStringResourceCallback callback, void *hint)
{
return EcmaString::CreateFromExternalResource(vm, data, length, canBeCompress, callback, hint);
}
static EcmaString *Concat(const EcmaVM *vm, const JSHandle<EcmaString> &str1Handle,
const JSHandle<EcmaString> &str2Handle, MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE)
{
return EcmaString::Concat(vm, str1Handle, str2Handle, type);
}
static EcmaString *CopyStringToOldSpace(const EcmaVM *vm, const JSHandle<EcmaString> &original,
uint32_t length, bool compressed)
{
return EcmaString::CopyStringToOldSpace(vm, original, length, compressed);
}
static EcmaString *FastSubString(const EcmaVM *vm,
const JSHandle<EcmaString> &src, uint32_t start, uint32_t length)
{
return EcmaString::FastSubString(vm, src, start, length);
}
static bool SubStringIsUtf8(const EcmaVM *vm,
const JSHandle<EcmaString> &src, uint32_t start, uint32_t length)
{
return EcmaString::SubStringIsUtf8(vm, src, start, length);
}
static EcmaString *GetSubString(const EcmaVM *vm,
const JSHandle<EcmaString> &src, uint32_t start, uint32_t length)
{
return EcmaString::GetSubString(vm, src, start, length);
}
bool IsUtf8() const
{
return string_->IsUtf8();
}
bool IsUtf16() const
{
return string_->IsUtf16();
}
uint32_t GetLength() const
{
return string_->GetLength();
}
inline size_t GetUtf8Length(const JSThread *thread, bool isGetBufferSize = false) const;
size_t ObjectSize() const
{
if (string_->IsLineString()) {
return LineEcmaString::ObjectSize(string_);
} else {
return TreeString::SIZE;
}
}
size_t GetFlatStringSize() const
{
return LineEcmaString::ObjectSize(string_);
}
bool IsInternString() const
{
return string_->IsInternString();
}
void SetInternString()
{
string_->SetIsInternString();
}
void ClearInternString()
{
string_->ClearInternStringFlag();
}
inline const uint8_t *GetDataUtf8();
inline const uint16_t *GetDataUtf16();
std::u16string ToU16String(const JSThread *thread, uint32_t len = 0)
{
return string_->ToU16String(thread, len);
}
common::Span<const uint8_t> ToUtf8Span(const JSThread *thread, CVector<uint8_t> &buf)
{
return string_->ToUtf8Span(thread, buf);
}
std::string ToStdString(const JSThread *thread, StringConvertedUsage usage = StringConvertedUsage::PRINT);
CString Utf8ConvertToString(const JSThread *thread);
std::string DebuggerToStdString(const JSThread *thread, StringConvertedUsage usage = StringConvertedUsage::PRINT);
CString ToCString(const JSThread *thread, StringConvertedUsage usage = StringConvertedUsage::LOGICOPERATION,
bool cesu8 = false);
void AppendToCString(const JSThread *thread, CString &str);
void AppendToC16String(const JSThread *thread, C16String &str);
uint32_t WriteToFlatUtf8(const JSThread *thread, uint8_t *buf, uint32_t maxLength, bool isWriteBuffer = false)
{
return string_->WriteUtf8(thread, buf, maxLength, isWriteBuffer);
}
uint32_t WriteToUtf16(const JSThread *thread, uint16_t *buf, uint32_t bufLength)
{
return string_->WriteUtf16(thread, buf, GetLength(), bufLength);
}
uint32_t WriteToOneByte(const JSThread *thread, uint8_t *buf, uint32_t maxLength)
{
return string_->WriteOneByte(thread, buf, maxLength);
}
uint32_t WriteToFlatUtf16(const JSThread *thread, uint16_t *buf, uint32_t maxLength) const
{
return string_->CopyDataUtf16(thread, buf, maxLength);
}
template <typename Char>
static void WriteToFlatWithPos(const JSThread *thread, EcmaString *src, Char *buf, uint32_t length, uint32_t pos)
{
src->WriteToFlatWithPos(thread, src, buf, length, pos);
}
template <typename Char>
static void WriteToFlat(const JSThread *thread, EcmaString *src, Char *buf, uint32_t maxLength)
{
src->WriteToFlat(thread, src, buf, maxLength);
}
template <RBMode mode = RBMode::DEFAULT_RB>
inline static void ReadData(const JSThread *thread, EcmaString *dst, EcmaString *src, uint32_t start,
uint32_t destSize, uint32_t length);
template<bool verify = true>
uint16_t Get(const JSThread *thread, uint32_t index) const
{
return string_->At<verify>(thread, index);
}
void Set(uint32_t index, uint16_t src)
{
return string_->WriteData(index, src);
}
uint32_t GetHashcode(const JSThread *thread)
{
return string_->GetHashcode(thread);
}
uint32_t ComputeHashcode(const JSThread *thread)
{
return string_->ComputeHashcode(thread);
}
static uint32_t ComputeHashcodeUtf8(const uint8_t *utf8Data, size_t utf8Len, bool canBeCompress)
{
return EcmaString::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress);
}
static uint32_t ComputeHashcodeUtf16(const uint16_t *utf16Data, uint32_t length)
{
return EcmaString::ComputeHashcodeUtf16(utf16Data, length);
}
static int32_t IndexOf(const EcmaVM *vm,
const JSHandle<EcmaString> &receiver, const JSHandle<EcmaString> &search, int pos = 0)
{
return EcmaString::IndexOf(vm, receiver, search, pos);
}
static int32_t LastIndexOf(const EcmaVM *vm,
const JSHandle<EcmaString> &receiver, const JSHandle<EcmaString> &search, int pos = 0)
{
return EcmaString::LastIndexOf(vm, receiver, search, pos);
}
static int32_t Compare(const EcmaVM *vm, const JSHandle<EcmaString>& left, const JSHandle<EcmaString>& right)
{
return EcmaString::Compare(vm, left, right);
}
static bool IsSubStringAt(const EcmaVM *vm, const JSHandle<EcmaString>& left,
const JSHandle<EcmaString>& right, uint32_t offset = 0)
{
return EcmaString::IsSubStringAt(vm, left, right, offset);
}
static bool StringsAreEqual(const EcmaVM *vm, const JSHandle<EcmaString> &str1, const JSHandle<EcmaString> &str2)
{
return EcmaString::StringsAreEqual(vm, str1, str2);
}
template <RBMode mode = RBMode::DEFAULT_RB>
static bool StringsAreEqual(const JSThread *thread, EcmaString *str1, EcmaString *str2)
{
return EcmaString::StringsAreEqual<mode>(thread, str1, str2);
}
static bool StringsAreEqualDiffUtfEncoding(const JSThread *thread, EcmaString *str1, EcmaString *str2)
{
return EcmaString::StringsAreEqualDiffUtfEncoding(thread, str1, str2);
}
static bool StringIsEqualUint8Data(const JSThread *thread,
const EcmaString *str1, const uint8_t *dataAddr, uint32_t dataLen,
bool canBeCompress)
{
return EcmaString::StringIsEqualUint8Data(thread, str1, dataAddr, dataLen, canBeCompress);
}
static bool StringsAreEqualUtf16(const JSThread *thread, const EcmaString *str1, const uint16_t *utf16Data,
uint32_t utf16Len)
{
return EcmaString::StringsAreEqualUtf16(thread, str1, utf16Data, utf16Len);
}
bool EqualToSplicedString(const JSThread *thread, const EcmaString *str1, const EcmaString *str2)
{
return string_->EqualToSplicedString(thread, str1, str2);
}
static bool CanBeCompressed(const uint8_t *utf8Data, uint32_t utf8Len)
{
return EcmaString::CanBeCompressed(utf8Data, utf8Len);
}
static bool CanBeCompressed(const uint16_t *utf16Data, uint32_t utf16Len)
{
return EcmaString::CanBeCompressed(utf16Data, utf16Len);
}
static bool CanBeCompressed(const EcmaString *string)
{
return EcmaString::CanBeCompressed(string);
}
bool ToElementIndex(const JSThread *thread, uint32_t *index)
{
return string_->ToElementIndex(thread, index);
}
bool ToInt(const JSThread *thread, int32_t *index, bool *negative)
{
return string_->ToInt(thread, index, negative);
}
bool PUBLIC_API ToTypedArrayIndex(const JSThread *thread, uint32_t *index)
{
return string_->ToTypedArrayIndex(thread, index);
}
static EcmaString *ToLower(const EcmaVM *vm, const JSHandle<EcmaString> &src)
{
return EcmaString::ToLower(vm, src);
}
static EcmaString *TryToLower(const EcmaVM *vm, const JSHandle<EcmaString> &src)
{
return EcmaString::TryToLower(vm, src);
}
static EcmaString *TryToUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src)
{
return EcmaString::TryToUpper(vm, src);
}
static EcmaString *ToUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src)
{
return EcmaString::ToUpper(vm, src);
}
static EcmaString *ToLocaleLower(const EcmaVM *vm, const JSHandle<EcmaString> &src, const icu::Locale &locale)
{
return EcmaString::ToLocaleLower(vm, src, locale);
}
static EcmaString *ToLocaleUpper(const EcmaVM *vm, const JSHandle<EcmaString> &src, const icu::Locale &locale)
{
return EcmaString::ToLocaleUpper(vm, src, locale);
}
static EcmaString *Trim(const JSThread *thread,
const JSHandle<EcmaString> &src, EcmaString::TrimMode mode = EcmaString::TrimMode::TRIM)
{
return EcmaString::Trim(thread, src, mode);
}
static bool IsASCIICharacter(uint16_t data);
bool IsFlat(const JSThread *thread) const
{
return string_->IsFlat(thread);
}
bool IsLineString() const
{
return string_->IsLineString();
}
bool IsSlicedString() const
{
return string_->IsSlicedString();
}
bool IsCachedExternalString() const
{
return string_->IsCachedExternalString();
}
bool IsLineOrCachedExternalString() const
{
return string_->IsLineOrCachedExternalString();
}
bool IsTreeString() const
{
return string_->IsTreeString();
}
bool NotTreeString() const
{
return string_->NotTreeString();
}
inline common::Span<const uint8_t> FastToUtf8Span() const;
PUBLIC_API static EcmaString *Flatten(const EcmaVM *vm, const JSHandle<EcmaString> &string,
MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE)
{
return EcmaString::Flatten(vm, string, type);
}
static FlatStringInfo FlattenAllString(const EcmaVM *vm, const JSHandle<EcmaString> &string,
MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE)
{
return EcmaString::FlattenAllString(vm, string, type);
}
static EcmaString *SlowFlatten(const EcmaVM *vm, const JSHandle<EcmaString> &string,
MemSpaceType type = MemSpaceType::SHARED_OLD_SPACE)
{
return EcmaString::SlowFlatten(vm, string, type);
}
static EcmaString *FlattenNoGCForSnapshot(const EcmaVM *vm, EcmaString *string)
{
return EcmaString::FlattenNoGCForSnapshot(vm, string);
}
static const uint8_t *GetUtf8DataFlat(const JSThread *thread, const EcmaString *src, CVector<uint8_t> &buf)
{
return EcmaString::GetUtf8DataFlat(thread, src, buf);
}
static const uint8_t *GetNonTreeUtf8Data(const JSThread *thread, const EcmaString *src)
{
return EcmaString::GetNonTreeUtf8Data(thread, src);
}
static const uint16_t *GetUtf16DataFlat(const JSThread *thread, const EcmaString *src, CVector<uint16_t> &buf)
{
return EcmaString::GetUtf16DataFlat(thread, src, buf);
}
static const uint16_t *GetNonTreeUtf16Data(const JSThread *thread, const EcmaString *src)
{
return EcmaString::GetNonTreeUtf16Data(thread, src);
}
static JSTaggedValue StringToList(JSThread *thread, JSHandle<JSTaggedValue> &str);
private:
EcmaString *string_ {nullptr};
};
}
}
#endif