* Copyright (c) 2023 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_SERIALIZER_SERIALIZE_DATA_H
#define ECMASCRIPT_SERIALIZER_SERIALIZE_DATA_H
#include <limits>
#include "common_components/heap/heap.h"
#include "common_components/serialize/serialize_utils.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/mem/dyn_chunk.h"
#include "ecmascript/runtime.h"
#include "ecmascript/shared_mm/shared_mm.h"
#include "ecmascript/snapshot/mem/snapshot_env.h"
namespace panda::ecmascript {
constexpr size_t INITIAL_CAPACITY = 64;
constexpr int CAPACITY_INCREASE_RATE = 2;
constexpr uint32_t RESERVED_INDEX = 0;
static constexpr int SERIALIZE_SPACE_NUM = 12;
typedef void* (*DetachFunc)(void *enginePointer, void *objPointer, void *hint, void *detachData);
typedef Local<JSValueRef> (*AttachFunc)(void *enginePointer, void *buffer, void *hint, void *attachData);
typedef Local<JSValueRef> (*AttachXRefFunc)(void *enginePointer, void *attachXRefData);
typedef void (*DetachFinalizer)(void *detachedObject, void *finalizerHint);
struct NativeBindingDetachInfo {
DetachFinalizer detachedFinalizer = nullptr;
void *detachedObject = nullptr;
void *detachedHint = nullptr;
NativeBindingDetachInfo(void *df, void *dObj, void *hint)
: detachedFinalizer(reinterpret_cast<DetachFinalizer>(df)), detachedObject(dObj), detachedHint(hint)
{
}
};
enum class EncodeFlag : uint8_t {
NEW_OBJECT = 0x00,
REFERENCE = 0x10,
WEAK,
PRIMITIVE,
MULTI_RAW_DATA,
ROOT_OBJECT,
OBJECT_PROTO,
ARRAY_BUFFER,
TRANSFER_ARRAY_BUFFER,
SHARED_ARRAY_BUFFER,
SENDABLE_ARRAY_BUFFER,
NATIVE_BINDING_OBJECT,
XREF_BINDING_OBJECT,
JS_ERROR,
JS_REG_EXP,
SHARED_OBJECT,
GLOBAL_ENV,
MODULE_FILE_NAME,
MODULE_RECORD_NAME,
MODULE_LAZY_ARRAY,
SEQUENCE_HOLE,
LAST
};
enum class SerializedObjectSpace : uint8_t {
REGULAR_SPACE,
PIN_SPACE,
LARGE_SPACE,
READ_ONLY_SPACE,
OTHER,
OLD_SPACE,
SLOT_SPACE,
NON_MOVABLE_SPACE,
MACHINE_CODE_SPACE,
HUGE_SPACE,
SHARED_OLD_SPACE,
SHARED_NON_MOVABLE_SPACE,
SHARED_HUGE_SPACE,
};
using common::SerializedBaseObjectSpace;
enum class SerializeType : uint8_t {
VALUE_SERIALIZE,
PGO_SERIALIZE
};
enum class SerializeErrorInfo : uint8_t {
ONLY_MSG = 1,
MSG_AND_STACK = 2
};
class SerializeData {
public:
explicit SerializeData(JSThread *thread) : thread_(thread) {}
~SerializeData()
{
if (sharedArrayBufferSet_.size() > 0) {
DecreaseSharedArrayBufferReference();
}
for (const auto &info: nativeBindingDetachInfos_) {
auto finalizer = reinterpret_cast<DetachFinalizer>(info->detachedFinalizer);
if (finalizer != nullptr) {
finalizer(info->detachedObject, info->detachedHint);
}
delete info;
}
nativeBindingDetachInfos_.clear();
free(buffer_);
if (!incompleteData_ && dataIndex_ != RESERVED_INDEX) {
Runtime::GetInstance()->RemoveSerializationRoot(thread_, dataIndex_);
}
}
NO_COPY_SEMANTIC(SerializeData);
NO_MOVE_SEMANTIC(SerializeData);
static uint8_t EncodeNewObject(SerializedObjectSpace space)
{
return static_cast<uint8_t>(space) | static_cast<uint8_t>(EncodeFlag::NEW_OBJECT);
}
static SerializedObjectSpace DecodeSpace(uint8_t type)
{
ASSERT(type < static_cast<uint8_t>(EncodeFlag::REFERENCE));
return static_cast<SerializedObjectSpace>(type);
}
static size_t AlignUpRegionAvailableSize(size_t size)
{
if (g_isEnableCMCGC) {
constexpr size_t REGION_SIZE = common::Heap::GetNormalRegionAvailableSize();
if (size == 0) {
return REGION_SIZE;
}
ASSERT(REGION_SIZE != 0);
return ((size - 1) / REGION_SIZE + 1) * REGION_SIZE;
} else {
if (size == 0) {
return DefaultRegion::GetRegionAvailableSize();
}
size_t regionAvailableSize = DefaultRegion::GetRegionAvailableSize();
return ((size - 1) / regionAvailableSize + 1) * regionAvailableSize;
}
}
bool ExpandBuffer(size_t requestedSize)
{
size_t newCapacity = bufferCapacity_ * CAPACITY_INCREASE_RATE;
newCapacity = std::max(newCapacity, requestedSize);
if (newCapacity > sizeLimit_) {
return false;
}
uint8_t *newBuffer = reinterpret_cast<uint8_t *>(malloc(newCapacity));
if (newBuffer == nullptr) {
return false;
}
if (memcpy_s(newBuffer, newCapacity, buffer_, bufferSize_) != EOK) {
LOG_FULL(ERROR) << "Failed to memcpy_s Data";
free(newBuffer);
return false;
}
free(buffer_);
buffer_ = newBuffer;
bufferCapacity_ = newCapacity;
return true;
}
bool AllocateBuffer(size_t bytes)
{
if (sizeLimit_ == 0) {
uint64_t heapSize = thread_->GetEcmaVM()->GetJSOptions().GetSerializerBufferSizeLimit();
sizeLimit_ = heapSize;
}
size_t oldSize = bufferSize_;
size_t newSize = oldSize + bytes;
if (newSize > sizeLimit_) {
return false;
}
if (bufferCapacity_ == 0) {
if (bytes < INITIAL_CAPACITY) {
buffer_ = reinterpret_cast<uint8_t *>(malloc(INITIAL_CAPACITY));
if (buffer_ != nullptr) {
bufferCapacity_ = INITIAL_CAPACITY;
return true;
} else {
return false;
}
} else {
buffer_ = reinterpret_cast<uint8_t *>(malloc(bytes));
if (buffer_ != nullptr) {
bufferCapacity_ = bytes;
return true;
} else {
return false;
}
}
}
if (newSize > bufferCapacity_) {
if (!ExpandBuffer(newSize)) {
return false;
}
}
return true;
}
ssize_t RawDataEmit(const void *data, size_t length)
{
return RawDataEmit(data, length, bufferSize_);
}
ssize_t RawDataEmit(const void *data, size_t length, size_t offset)
{
if (length <= 0) {
return -1;
}
if ((offset + length) > bufferCapacity_) {
if (!AllocateBuffer(length)) {
return -1;
}
}
if (memcpy_s(buffer_ + offset, bufferCapacity_ - offset, data, length) != EOK) {
LOG_FULL(ERROR) << "Failed to memcpy_s Data";
return -1;
}
if (UNLIKELY(offset > std::numeric_limits<ssize_t>::max())) {
return -1;
}
ssize_t res = static_cast<ssize_t>(offset);
if (bufferSize_ == offset) {
bufferSize_ += length;
}
return res;
}
void EmitChar(uint8_t c)
{
RawDataEmit(&c, U8_SIZE);
}
ssize_t EmitU64(uint64_t c)
{
return RawDataEmit(reinterpret_cast<uint8_t *>(&c), U64_SIZE);
}
ssize_t EmitU64(uint64_t c, size_t offset)
{
return RawDataEmit(reinterpret_cast<uint8_t *>(&c), U64_SIZE, offset);
}
void WriteUint8(uint8_t data)
{
RawDataEmit(&data, 1);
}
uint8_t ReadUint8(size_t &position)
{
ASSERT(position < Size());
return *(buffer_ + (position++));
}
void WriteEncodeFlag(EncodeFlag flag)
{
EmitChar(static_cast<uint8_t>(flag));
}
void WriteUint32(uint32_t data)
{
RawDataEmit(reinterpret_cast<uint8_t *>(&data), U32_SIZE);
}
uint32_t ReadUint32(size_t &position)
{
ASSERT(position < Size());
uint32_t value = *reinterpret_cast<uint32_t *>(buffer_ + position);
position += sizeof(uint32_t);
return value;
}
void WriteRawData(uint8_t *data, size_t length)
{
RawDataEmit(data, length);
}
void WriteJSTaggedValue(JSTaggedValue value)
{
EmitU64(value.GetRawData());
}
ssize_t WriteJSTaggedType(JSTaggedType value)
{
return EmitU64(value);
}
JSTaggedType ReadJSTaggedType(size_t &position)
{
ASSERT(position < Size());
JSTaggedType value = *reinterpret_cast<uint64_t *>(buffer_ + position);
position += sizeof(JSTaggedType);
return value;
}
void ReadRawData(uintptr_t addr, size_t len, size_t &position)
{
ASSERT(position + len <= Size());
if (memcpy_s(reinterpret_cast<void *>(addr), len, buffer_ + position, len) != EOK) {
LOG_ECMA(FATAL) << "this branch is unreachable";
UNREACHABLE();
}
position += len;
}
uint8_t* Data() const
{
return buffer_;
}
size_t Size() const
{
return bufferSize_;
}
void SetIncompleteData(bool incomplete)
{
incompleteData_ = incomplete;
}
bool IsIncompleteData() const
{
return incompleteData_;
}
const std::vector<size_t>& GetRegularRemainSizeVector() const
{
return regularRemainSizeVector_;
}
const std::vector<size_t>& GetPinRemainSizeVector() const
{
return pinRemainSizeVector_;
}
const std::array<std::vector<size_t>, SERIALIZE_SPACE_NUM>& GetRegionRemainSizeVectors() const
{
return regionRemainSizeVectors_;
}
size_t GetRegularSpaceSize() const
{
return regularSpaceSize_;
}
size_t GetPinSpaceSize() const
{
return pinSpaceSize_;
}
size_t GetOldSpaceSize() const
{
return oldSpaceSize_;
}
size_t GetHugeSpaceSize() const
{
return hugeSpaceSize_;
}
size_t GetNonMovableSpaceSize() const
{
return nonMovableSpaceSize_;
}
size_t GetMachineCodeSpaceSize() const
{
return machineCodeSpaceSize_;
}
size_t GetSharedOldSpaceSize() const
{
return sharedOldSpaceSize_;
}
size_t GetSharedNonMovableSpaceSize() const
{
return sharedNonMovableSpaceSize_;
}
void CalculateSerializedObjectSize(SerializedObjectSpace space, size_t objectSize)
{
if (g_isEnableCMCGC) {
switch (space) {
case SerializedObjectSpace::REGULAR_SPACE:
AlignSpaceObjectSize(regularRemainSizeVector_, regularSpaceSize_, objectSize);
break;
case SerializedObjectSpace::PIN_SPACE:
AlignSpaceObjectSize(pinRemainSizeVector_, pinSpaceSize_, objectSize);
break;
default:
break;
}
} else {
switch (space) {
case SerializedObjectSpace::OLD_SPACE:
AlignSpaceObjectSize(oldSpaceSize_, objectSize, SerializedObjectSpace::OLD_SPACE);
break;
case SerializedObjectSpace::SLOT_SPACE:
break;
case SerializedObjectSpace::NON_MOVABLE_SPACE:
AlignSpaceObjectSize(nonMovableSpaceSize_, objectSize, SerializedObjectSpace::NON_MOVABLE_SPACE);
break;
case SerializedObjectSpace::MACHINE_CODE_SPACE:
AlignSpaceObjectSize(machineCodeSpaceSize_, objectSize, SerializedObjectSpace::MACHINE_CODE_SPACE);
break;
case SerializedObjectSpace::HUGE_SPACE:
AlignHugeSpaceObjectSize(objectSize);
break;
case SerializedObjectSpace::SHARED_OLD_SPACE:
AlignSpaceObjectSize(sharedOldSpaceSize_, objectSize, SerializedObjectSpace::SHARED_OLD_SPACE);
break;
case SerializedObjectSpace::SHARED_NON_MOVABLE_SPACE:
AlignSpaceObjectSize(sharedNonMovableSpaceSize_, objectSize,
SerializedObjectSpace::SHARED_NON_MOVABLE_SPACE);
break;
default:
break;
}
}
}
void AlignSpaceObjectSize(size_t &spaceSize, size_t objectSize, SerializedObjectSpace spaceType)
{
size_t alignRegionSize = AlignUpRegionAvailableSize(spaceSize);
if (UNLIKELY(spaceSize + objectSize > alignRegionSize)) {
regionRemainSizeVectors_.at(static_cast<uint8_t>(spaceType)).push_back(alignRegionSize - spaceSize);
spaceSize = alignRegionSize;
}
spaceSize += objectSize;
ASSERT(spaceSize <= SnapshotEnv::MAX_UINT_32);
}
void AlignSpaceObjectSize(std::vector<size_t> &remainSizeVec, size_t &spaceSize, size_t objectSize)
{
size_t alignRegionSize = AlignUpRegionAvailableSize(spaceSize);
if (UNLIKELY(spaceSize + objectSize > alignRegionSize)) {
remainSizeVec.push_back(alignRegionSize - spaceSize);
spaceSize = alignRegionSize;
}
spaceSize += objectSize;
ASSERT(spaceSize <= SnapshotEnv::MAX_UINT_32);
}
void AlignHugeSpaceObjectSize(size_t objectSize)
{
size_t alignedSize = HugeObjectSpace::AlignUpHugeObjectSize(objectSize);
hugeSpaceSize_ += alignedSize;
}
void DecreaseSharedArrayBufferReference()
{
auto manager = JSSharedMemoryManager::GetInstance();
for (auto iter = sharedArrayBufferSet_.begin(); iter != sharedArrayBufferSet_.end(); iter++) {
JSSharedMemoryManager::RemoveSharedMemory(thread_->GetEnv(), reinterpret_cast<void *>(*iter), manager);
}
sharedArrayBufferSet_.clear();
}
void insertSharedArrayBuffer(uintptr_t ptr)
{
sharedArrayBufferSet_.insert(ptr);
}
void SetDataIndex(uint32_t dataIndex)
{
dataIndex_ = dataIndex;
}
uint32_t GetDataIndex() const
{
return dataIndex_;
}
void AddNativeBindingDetachInfo(panda::JSNApi::NativeBindingInfo *info, void *dObj)
{
auto *detachInfo = new NativeBindingDetachInfo(info->detachedFinalizer, dObj, info->detachedHint);
nativeBindingDetachInfos_.insert(detachInfo);
}
void SetErrorMessage(const std::string &errorMessage)
{
errorMessage_ = errorMessage;
}
const std::string &GetErrorMessage() const
{
return errorMessage_;
}
void SetBuffer(uint8_t *buffer)
{
buffer_ = buffer;
}
private:
static constexpr size_t U8_SIZE = 1;
static constexpr size_t U16_SIZE = 2;
static constexpr size_t U32_SIZE = 4;
static constexpr size_t U64_SIZE = 8;
JSThread *thread_;
uint32_t dataIndex_ {RESERVED_INDEX};
uint8_t *buffer_ {nullptr};
uint64_t sizeLimit_ {0};
size_t bufferSize_ {0};
size_t bufferCapacity_ {0};
size_t regularSpaceSize_ {0};
size_t pinSpaceSize_ {0};
size_t oldSpaceSize_ {0};
size_t hugeSpaceSize_ {0};
size_t nonMovableSpaceSize_ {0};
size_t machineCodeSpaceSize_ {0};
size_t sharedOldSpaceSize_ {0};
size_t sharedNonMovableSpaceSize_ {0};
bool incompleteData_ {false};
std::string errorMessage_;
std::vector<size_t> regularRemainSizeVector_ {};
std::vector<size_t> pinRemainSizeVector_ {};
std::array<std::vector<size_t>, SERIALIZE_SPACE_NUM> regionRemainSizeVectors_ {};
std::set<uintptr_t> sharedArrayBufferSet_ {};
std::set<NativeBindingDetachInfo *> nativeBindingDetachInfos_ {};
friend class ModulesSnapshotHelper;
};
}
#endif