* 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_INL_H
#define ECMASCRIPT_STRING_INL_H
#include "ecmascript/string/base_string-inl.h"
#include "ecmascript/ecma_string.h"
#include "ecmascript/base/string_helper.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/mem/space.h"
#include "ecmascript/object_factory-inl.h"
#include "ecmascript/debugger/js_debugger_manager.h"
#include "ecmascript/string/external_string_table.h"
namespace panda::ecmascript {
inline EcmaString *EcmaString::CreateEmptyString(const EcmaVM *vm)
{
auto string = vm->GetFactory()->AllocNonMovableLineStringObject(BaseString::SIZE);
string->InitLengthAndFlags(0, true);
string->SetMixHashcode(0);
return string;
}
inline EcmaString *EcmaString::CreateFromUtf8(const EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len,
bool canBeCompress, MemSpaceType type)
{
if (utf8Len == 0) {
return vm->GetFactory()->GetEmptyString().GetObject<EcmaString>();
}
EcmaString *string = nullptr;
if (canBeCompress) {
string = CreateLineStringWithSpaceType(vm, utf8Len, true, type);
ASSERT(string != nullptr);
std::copy(utf8Data, utf8Data + utf8Len, string->GetDataUtf8Writable());
} else {
auto utf16Len = common::utf_helper::Utf8ToUtf16Size(utf8Data, utf8Len);
string = CreateLineStringWithSpaceType(vm, utf16Len, false, type);
ASSERT(string != nullptr);
[[maybe_unused]] auto len =
common::utf_helper::ConvertRegionUtf8ToUtf16(utf8Data, string->GetDataUtf16Writable(), utf8Len, utf16Len);
ASSERT(len == utf16Len);
}
ASSERT_PRINT(canBeCompress == CanBeCompressed(string), "Bad input canBeCompress!");
return string;
}
inline EcmaString *EcmaString::CreateFromUtf8CompressedSubString(const EcmaVM *vm, const JSHandle<EcmaString> &string,
uint32_t offset, uint32_t utf8Len, MemSpaceType type)
{
if (UNLIKELY(utf8Len == 0)) {
return vm->GetFactory()->GetEmptyString().GetObject<EcmaString>();
}
auto allocator = [vm, type](size_t size, EcmaStringType stringType) -> BaseObject* {
ASSERT(stringType == EcmaStringType::LINE_STRING && "Can only allocate line string");
return EcmaString::AllocLineString(vm, size, type)->ToBaseString();
};
BaseString *str = LineString::CreateFromUtf8CompressedSubString(std::move(allocator), string, offset, utf8Len);
return EcmaString::FromBaseString(str);
}
inline EcmaString *EcmaString::CreateUtf16StringFromUtf8(const EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf16Len,
MemSpaceType type)
{
if (utf16Len == 0) {
return vm->GetFactory()->GetEmptyString().GetObject<EcmaString>();
}
auto string = CreateLineStringWithSpaceType(vm, utf16Len, false, type);
ASSERT(string != nullptr);
auto len = utf::ConvertRegionMUtf8ToUtf16(
utf8Data, string->GetDataUtf16Writable(), utf::Mutf8Size(utf8Data), utf16Len, 0);
if (len < utf16Len) {
string->TrimLineString(len);
}
ASSERT_PRINT(false == CanBeCompressed(string), "Bad input canBeCompress!");
return string;
}
inline void EcmaString::TrimLineString(uint32_t newLength)
{
BaseString* baseThis = reinterpret_cast<BaseString*>(this);
reinterpret_cast<LineString *>(baseThis)->LineString::Trim(newLength);
}
inline EcmaString *EcmaString::CreateFromUtf16(const EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len,
bool canBeCompress, MemSpaceType type)
{
if (utf16Len == 0) {
return vm->GetFactory()->GetEmptyString().GetObject<EcmaString>();
}
auto allocator = [vm, type](size_t size, EcmaStringType stringType) -> BaseObject* {
ASSERT(stringType == EcmaStringType::LINE_STRING && "Can only allocate line string");
return EcmaString::AllocLineString(vm, size, type)->ToBaseString();
};
BaseString *str = LineString::CreateFromUtf16(std::move(allocator), utf16Data, utf16Len, canBeCompress);
return EcmaString::FromBaseString(str);
}
inline EcmaString *EcmaString::CreateFromExternalResource(const EcmaVM *vm, void *data, uint32_t length,
bool canBeCompress, ExternalStringResourceCallback callback, void *hint)
{
auto *resource = new (std::nothrow) ExternalNonMovableStringResource(hint, callback);
if (resource == nullptr) {
LOG_ECMA(FATAL) << "create external string resource failed";
UNREACHABLE();
}
auto allocator = [vm](size_t size, EcmaStringType stringType) -> BaseObject* {
ASSERT(stringType == EcmaStringType::CACHED_EXTERNAL_STRING && "Can only allocate line string");
EcmaString* string = vm->GetFactory()->AllocCachedExternalStringObject();
return string;
};
BaseString *str = CachedExternalString::Create(std::move(allocator), resource, data, length, canBeCompress);
SharedHeap::GetInstance()->GetExternalStringTable()->AddString(reinterpret_cast<CachedExternalString*>(str));
return EcmaString::FromBaseString(str);
}
inline EcmaString *EcmaString::CreateLineString(const EcmaVM *vm, size_t length, bool compressed)
{
auto allocator = [vm](size_t size, EcmaStringType stringType) -> BaseObject* {
ASSERT(stringType == EcmaStringType::LINE_STRING && "Can only allocate line string");
EcmaString* string = vm->GetFactory()->AllocLineStringObject(size);
return string;
};
BaseString *str = LineString::Create(std::move(allocator), length, compressed);
return EcmaString::FromBaseString(str);
}
inline EcmaString *EcmaString::CreateLineStringNoGC(const EcmaVM *vm, size_t length, bool compressed)
{
auto allocator = [vm](size_t size, EcmaStringType stringType) -> BaseObject* {
ASSERT(stringType == EcmaStringType::LINE_STRING && "Can only allocate line string");
size = AlignUp(size, static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
EcmaString* string = vm->GetFactory()->AllocLineStringObjectNoGC(size);
return string;
};
BaseString *str = LineString::Create(std::move(allocator), length, compressed);
return EcmaString::FromBaseString(str);
}
inline EcmaString* EcmaString::AllocLineString(const EcmaVM* vm, size_t size, MemSpaceType type)
{
EcmaString* string = nullptr;
switch (type) {
case MemSpaceType::SHARED_OLD_SPACE:
string = vm->GetFactory()->AllocOldSpaceLineStringObject(size);
break;
case MemSpaceType::SHARED_NON_MOVABLE:
string = vm->GetFactory()->AllocNonMovableLineStringObject(size);
break;
case MemSpaceType::SHARED_READ_ONLY_SPACE:
string = vm->GetFactory()->AllocReadOnlyLineStringObject(size);
break;
default:
LOG_ECMA(FATAL) << "this branch is unreachable";
UNREACHABLE();
}
return string;
}
inline EcmaString *EcmaString::CreateLineStringWithSpaceType(const EcmaVM *vm, size_t length, bool compressed,
MemSpaceType type)
{
auto allocator = [vm, type](size_t size, EcmaStringType stringType) -> BaseObject* {
ASSERT(stringType == EcmaStringType::LINE_STRING && "Can only allocate line string");
ASSERT(IsSMemSpace(type));
return AllocLineString(vm, size, type);
};
BaseString *str = LineString::Create(std::move(allocator), length, compressed);
return EcmaString::FromBaseString(str);
}
inline SlicedEcmaString* EcmaString::CreateSlicedString(const EcmaVM* vm, JSHandle<EcmaString> parent,
MemSpaceType type)
{
auto allocator = [vm, type](size_t, EcmaStringType stringType) -> BaseObject* {
ASSERT(stringType == EcmaStringType::SLICED_STRING && "Can only allocate sliced string");
EcmaString* string = vm->GetFactory()->AllocSlicedStringObject(type);
return string;
};
auto writeBarrier = [vm](void* obj, size_t offset, BaseObject* str) {
Barriers::SetObject<true>(vm->GetJSThread(), obj, offset, reinterpret_cast<JSTaggedType>(str));
};
SlicedString* slicedString = SlicedString::Create(std::move(allocator), std::move(writeBarrier), parent);
return SlicedEcmaString::FromBaseString(slicedString);
}
* In the multi-thread optimization scenario, start the application.
* 1.The thread executes until CheckThread () acquires the lock.
* 2.At this time, the thread receives the SIGPROF signal, interrupts the current program execution,
* and enters the signal processing function.
* 3.When CreateTreeString()->GetJSThread()->CheckThread() is executed, the lock cannot be obtained
* and the system has to wait, causing a deadlock.
* Therefore, if the function is executed during signal processing, the thread ID is directly obtained and
* the thread detection is not performed, thereby avoiding deadlock.
*/
inline void GetDebuggerThread(const EcmaVM *vm, JSThread **thread)
{
if (vm->GetJsDebuggerManager()->GetSignalState()) {
*thread = vm->GetJSThreadNoCheck();
} else {
*thread = vm->GetJSThread();
}
}
inline EcmaString *EcmaString::CreateTreeString(const EcmaVM *vm,
const JSHandle<EcmaString> &left, const JSHandle<EcmaString> &right, uint32_t length, bool compressed)
{
ECMA_STRING_CHECK_LENGTH_AND_TRHOW(vm, length);
JSThread *thread = nullptr;
GetDebuggerThread(vm, &thread);
auto allocator = [vm](size_t, EcmaStringType stringType) -> BaseObject* {
ASSERT(stringType == EcmaStringType::TREE_STRING && "Can only allocate tree string");
EcmaString* string = vm->GetFactory()->AllocTreeStringObject();
return string;
};
auto writeBarrier = [thread](void* obj, size_t offset, BaseObject* str) {
Barriers::SetObject<true>(thread, obj, offset, reinterpret_cast<JSTaggedType>(str));
};
TreeString* treeString = TreeString::Create(std::move(allocator), std::move(writeBarrier), left, right,
length, compressed);
return TreeEcmaString::FromBaseString(treeString);
}
EcmaString *EcmaString::FastSubUtf8String(const EcmaVM *vm, const JSHandle<EcmaString> &src, uint32_t start,
uint32_t length)
{
JSHandle<EcmaString> string(vm->GetJSThread(), CreateLineString(vm, length, true));
FlatStringInfo srcFlat = FlattenAllString(vm, src);
common::Span<uint8_t> dst(string->GetDataUtf8Writable(), length);
common::Span<const uint8_t> source(srcFlat.GetDataUtf8() + start, length);
EcmaString::MemCopyChars(dst, length, source, length);
ASSERT_PRINT(CanBeCompressed(*string), "canBeCompresse does not match the real value!");
return *string;
}
EcmaString *EcmaString::FastSubUtf16String(const EcmaVM *vm, const JSHandle<EcmaString> &src, uint32_t start,
uint32_t length)
{
FlatStringInfo srcFlat = FlattenAllString(vm, src);
bool canBeCompressed = CanBeCompressed(srcFlat.GetDataUtf16() + start, length);
JSHandle<EcmaString> string(vm->GetJSThread(), CreateLineString(vm, length, canBeCompressed));
srcFlat = FlattenAllString(vm, src);
if (canBeCompressed) {
BaseString::CopyChars(string->GetDataUtf8Writable(), srcFlat.GetDataUtf16() + start, length);
} else {
uint32_t len = length * (sizeof(uint16_t) / sizeof(uint8_t));
common::Span<uint16_t> dst(string->GetDataUtf16Writable(), length);
common::Span<const uint16_t> source(srcFlat.GetDataUtf16() + start, length);
EcmaString::MemCopyChars(dst, len, source, len);
}
ASSERT_PRINT(canBeCompressed == CanBeCompressed(*string), "canBeCompresse does not match the real value!");
return *string;
}
inline const uint8_t* EcmaString::GetDataUtf8() const
{
if (IsLineString()) {
return LineString::ConstCast(this)->GetDataUtf8();
}
ASSERT(IsCachedExternalString());
return CachedExternalString::ConstCast(this)->GetDataUtf8();
}
inline const uint16_t* EcmaString::GetDataUtf16() const
{
if (IsLineString()) {
return LineString::ConstCast(this)->GetDataUtf16();
}
ASSERT(IsCachedExternalString());
return CachedExternalString::ConstCast(this)->GetDataUtf16();
}
inline uint8_t* EcmaString::GetDataUtf8Writable()
{
ASSERT(IsLineString());
return LineString::Cast(this)->GetDataUtf8Writable();
}
inline uint16_t* EcmaString::GetDataUtf16Writable()
{
ASSERT(IsLineString());
return LineString::Cast(this)->GetDataUtf16Writable();
}
inline size_t EcmaString::GetUtf8Length(const JSThread *thread, bool modify, bool isGetBufferSize) const
{
auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToBaseString()->GetUtf8Length(std::move(readBarrier), modify, isGetBufferSize);
}
template <bool verify>
uint16_t EcmaString::At(const JSThread *thread, int32_t index) const
{
auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToBaseString()->At<verify>(std::move(readBarrier), index);
}
template <typename T, typename T1>
bool EcmaString::StringsAreEquals(common::Span<const T>& str1, common::Span<const T1>& str2)
{
return BaseString::StringsAreEquals(str1, str2);
}
inline void EcmaString::WriteData(uint32_t index, uint16_t src)
{
return LineString::Cast(this)->Set(index, src);
}
inline common::Span<const uint8_t> EcmaString::FastToUtf8Span() const
{
uint32_t len = GetLength();
ASSERT(IsUtf8());
const uint8_t *data = GetDataUtf8();
return common::Span<const uint8_t>(data, len);
}
inline bool EcmaString::IsFlat(const JSThread *thread) const
{
auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToBaseString()->IsFlat(std::move(readBarrier));
}
template <typename Char>
void EcmaString::WriteToFlat(const JSThread *thread, EcmaString *src, Char *buf, uint32_t maxLength)
{
auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return BaseString::WriteToFlat(std::move(readBarrier), src->ToBaseString(), buf, maxLength);
}
template <typename Char>
void EcmaString::WriteToFlatWithPos(const JSThread *thread, EcmaString *src, Char *buf, uint32_t length, uint32_t pos)
{
auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return BaseString::WriteToFlatWithPos(std::move(readBarrier), src->ToBaseString(), buf, length, pos);
}
inline size_t EcmaString::WriteUtf8(const JSThread *thread, uint8_t *buf, size_t maxLength, bool isWriteBuffer) const
{
auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToBaseString()->WriteUtf8(std::move(readBarrier), buf, maxLength, isWriteBuffer);
}
inline size_t EcmaString::WriteUtf16(const JSThread *thread, uint16_t *buf, uint32_t targetLength,
uint32_t bufLength) const
{
auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToBaseString()->WriteUtf16(std::move(readBarrier), buf, targetLength, bufLength);
}
inline size_t EcmaString::WriteOneByte(const JSThread *thread, uint8_t *buf, size_t maxLength) const
{
auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToBaseString()->WriteOneByte(std::move(readBarrier), buf, maxLength);
}
inline uint32_t EcmaString::CopyDataUtf16(const JSThread *thread, uint16_t *buf, uint32_t maxLength) const
{
auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToBaseString()->CopyDataUtf16(std::move(readBarrier), buf, maxLength);
}
inline common::Span<const uint8_t> EcmaString::ToUtf8Span(const JSThread *thread, CVector<uint8_t> &buf, bool modify,
bool cesu8)
{
auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToBaseString()->ToUtf8Span(std::move(readBarrier), buf, modify, cesu8);
}
inline common::Span<const uint8_t> EcmaString::DebuggerToUtf8Span(const JSThread *thread, CVector<uint8_t> &buf,
bool modify)
{
auto readBarrier = [thread](const void *obj, size_t offset) -> TaggedObject * {
return Barriers::GetTaggedObject(thread, obj, offset);
};
return ToBaseString()->DebuggerToUtf8Span(std::move(readBarrier), buf, modify);
}
inline const uint8_t *FlatStringInfo::GetDataUtf8() const
{
return string_->GetDataUtf8() + startIndex_;
}
inline const uint16_t *FlatStringInfo::GetDataUtf16() const
{
return string_->GetDataUtf16() + startIndex_;
}
inline uint8_t *FlatStringInfo::GetDataUtf8Writable() const
{
return string_->GetDataUtf8Writable() + startIndex_;
}
inline uint16_t *FlatStringInfo::GetDataUtf16Writable() const
{
return string_->GetDataUtf16Writable() + startIndex_;
}
inline const uint8_t *EcmaStringAccessor::GetDataUtf8()
{
return string_->GetDataUtf8();
}
inline const uint16_t *EcmaStringAccessor::GetDataUtf16()
{
return string_->GetDataUtf16();
}
inline size_t EcmaStringAccessor::GetUtf8Length(const JSThread *thread, bool isGetBufferSize) const
{
return string_->GetUtf8Length(thread, true, isGetBufferSize);
}
template <RBMode mode>
inline void EcmaStringAccessor::ReadData(const JSThread *thread, EcmaString *dst, EcmaString *src, uint32_t start,
uint32_t destSize, uint32_t length)
{
dst->WriteData<mode>(thread, src, start, destSize, length);
}
inline common::Span<const uint8_t> EcmaStringAccessor::FastToUtf8Span() const
{
return string_->FastToUtf8Span();
}
inline bool EcmaStringAccessor::IsASCIICharacter(uint16_t data)
{
return BaseString::IsASCIICharacter(data);
}
}
#endif