/*
 * Copyright (c) 2021-2022 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/js_serializer.h"

#if defined(PANDA_TARGET_IOS)
#include <stdlib.h>
#elif defined(PANDA_TARGET_MACOS)
#include <sys/malloc.h>
#else
#include <malloc.h>
#endif

#include "ecmascript/base/array_helper.h"
#include "ecmascript/base/typed_array_helper-inl.h"
#include "ecmascript/base/typed_array_helper.h"
#include "ecmascript/global_env.h"
#include "ecmascript/js_array.h"
#include "ecmascript/js_arraybuffer.h"
#include "ecmascript/js_hclass.h"
#include "ecmascript/js_regexp.h"
#include "ecmascript/js_set.h"
#include "ecmascript/js_typed_array.h"
#include "ecmascript/linked_hash_table.h"
#include "ecmascript/shared_mm/shared_mm.h"

#include "securec.h"

namespace panda::ecmascript {
using TypedArrayHelper = base::TypedArrayHelper;
constexpr size_t INITIAL_CAPACITY = 64;
constexpr int CAPACITY_INCREASE_RATE = 2;

bool JSSerializer::WriteType(SerializationUID id)
{
    uint8_t rawId = static_cast<uint8_t>(id);
    return WriteRawData(&rawId, sizeof(rawId));
}

void JSSerializer::InitTransferSet(CUnorderedSet<uintptr_t> transferDataSet)
{
    transferDataSet_ = std::move(transferDataSet);
}

void JSSerializer::ClearTransferSet()
{
    transferDataSet_.clear();
}

// Write JSTaggedValue could be either a pointer to a HeapObject or a value
bool JSSerializer::SerializeJSTaggedValue(const JSHandle<JSTaggedValue> &value)
{
    [[maybe_unused]] EcmaHandleScope scope(thread_);
    DISALLOW_GARBAGE_COLLECTION;
    if (!value->IsHeapObject()) {
        if (!WritePrimitiveValue(value)) {
            return false;
        }
    } else {
        if (!WriteTaggedObject(value)) {
            return false;
        }
    }
    return true;
}

// Write JSTaggedValue that is pure value
bool JSSerializer::WritePrimitiveValue(const JSHandle<JSTaggedValue> &value)
{
    if (value->IsNull()) {
        return WriteType(SerializationUID::JS_NULL);
    }
    if (value->IsUndefined()) {
        return WriteType(SerializationUID::JS_UNDEFINED);
    }
    if (value->IsTrue()) {
        return WriteType(SerializationUID::JS_TRUE);
    }
    if (value->IsFalse()) {
        return WriteType(SerializationUID::JS_FALSE);
    }
    if (value->IsInt()) {
        return WriteInt(value->GetInt());
    }
    if (value->IsDouble()) {
        return WriteDouble(value->GetDouble());
    }
    if (value->IsHole()) {
        return WriteType(SerializationUID::HOLE);
    }
    return false;
}

bool JSSerializer::WriteInt(int32_t value)
{
    if (!WriteType(SerializationUID::INT32)) {
        return false;
    }
    if (!WriteRawData(&value, sizeof(value))) {
        return false;
    }
    return true;
}

bool JSSerializer::WriteDouble(double value)
{
    if (!WriteType(SerializationUID::DOUBLE)) {
        return false;
    }
    if (!WriteRawData(&value, sizeof(value))) {
        return false;
    }
    return true;
}

bool JSSerializer::WriteBoolean(bool value)
{
    if (value) {
        return WriteType(SerializationUID::C_TRUE);
    }
    return WriteType(SerializationUID::C_FALSE);
}

bool JSSerializer::WriteRawData(const void *data, size_t length)
{
    if (length <= 0) {
        return false;
    }
    if ((bufferSize_ + length) > bufferCapacity_) {
        if (!AllocateBuffer(length)) {
            return false;
        }
    }
    if (memcpy_s(buffer_ + bufferSize_, bufferCapacity_ - bufferSize_, data, length) != EOK) {
        LOG_FULL(ERROR) << "Failed to memcpy_s Data";
        return false;
    }
    bufferSize_ += length;
    return true;
}

bool JSSerializer::WriteString(const CString &str)
{
    if (!WriteType(SerializationUID::C_STRING)) {
        return false;
    }

    size_t length = str.length() + 1;  // 1: '\0'
    if ((bufferSize_ + length) > bufferCapacity_) {
        if (!AllocateBuffer(length)) {
            return false;
        }
    }
    if (memcpy_s(buffer_ + bufferSize_, bufferCapacity_ - bufferSize_, str.c_str(), length) != EOK) {
        LOG_FULL(ERROR) << "Failed to memcpy_s Data";
        return false;
    }
    bufferSize_ += length;
    return true;
}

bool JSSerializer::AllocateBuffer(size_t bytes)
{
    // Get internal heap size
    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;
}

bool JSSerializer::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;
}

// Transfer ownership of buffer, should not use this Serializer after release
std::pair<uint8_t *, size_t> JSSerializer::ReleaseBuffer()
{
    auto res = std::make_pair(buffer_, bufferSize_);
    buffer_ = nullptr;
    bufferSize_ = 0;
    bufferCapacity_ = 0;
    objectId_ = 0;
    return res;
}

bool JSSerializer::IsSerialized(uintptr_t addr) const
{
    if (referenceMap_.find(addr) != referenceMap_.end()) {
        return true;
    }
    return false;
}

bool JSSerializer::WriteIfSerialized(uintptr_t addr)
{
    auto iter = referenceMap_.find(addr);
    if (iter == referenceMap_.end()) {
        return false;
    }
    uint64_t id = iter->second;
    if (!WriteType(SerializationUID::TAGGED_OBJECT_REFERNCE)) {
        return false;
    }
    if (!WriteRawData(&id, sizeof(uint64_t))) {
        return false;
    }
    return true;
}

// Write HeapObject
bool JSSerializer::WriteTaggedObject(const JSHandle<JSTaggedValue> &value)
{
    uintptr_t addr = reinterpret_cast<uintptr_t>(value.GetTaggedValue().GetTaggedObject());
    bool serialized = IsSerialized(addr);
    if (serialized) {
        return WriteIfSerialized(addr);
    }
    referenceMap_.emplace(addr, objectId_);
    objectId_++;

    TaggedObject *taggedObject = value->GetTaggedObject();
    JSType type = taggedObject->GetClass()->GetObjectType();
    switch (type) {
        case JSType::JS_ERROR:
        case JSType::JS_EVAL_ERROR:
        case JSType::JS_RANGE_ERROR:
        case JSType::JS_REFERENCE_ERROR:
        case JSType::JS_TYPE_ERROR:
        case JSType::JS_AGGREGATE_ERROR:
        case JSType::JS_URI_ERROR:
        case JSType::JS_SYNTAX_ERROR:
        case JSType::JS_OOM_ERROR:
            return WriteJSError(value);
        case JSType::JS_DATE:
            return WriteJSDate(value);
        case JSType::JS_ARRAY:
            return WriteJSArray(value);
        case JSType::JS_MAP:
            return WriteJSMap(value);
        case JSType::JS_SET:
            return WriteJSSet(value);
        case JSType::JS_REG_EXP:
            return WriteJSRegExp(value);
        case JSType::JS_INT8_ARRAY:
            return WriteJSTypedArray(value, SerializationUID::JS_INT8_ARRAY);
        case JSType::JS_UINT8_ARRAY:
            return WriteJSTypedArray(value, SerializationUID::JS_UINT8_ARRAY);
        case JSType::JS_UINT8_CLAMPED_ARRAY:
            return WriteJSTypedArray(value, SerializationUID::JS_UINT8_CLAMPED_ARRAY);
        case JSType::JS_INT16_ARRAY:
            return WriteJSTypedArray(value, SerializationUID::JS_INT16_ARRAY);
        case JSType::JS_UINT16_ARRAY:
            return WriteJSTypedArray(value, SerializationUID::JS_UINT16_ARRAY);
        case JSType::JS_INT32_ARRAY:
            return WriteJSTypedArray(value, SerializationUID::JS_INT32_ARRAY);
        case JSType::JS_UINT32_ARRAY:
            return WriteJSTypedArray(value, SerializationUID::JS_UINT32_ARRAY);
        case JSType::JS_FLOAT32_ARRAY:
            return WriteJSTypedArray(value, SerializationUID::JS_FLOAT32_ARRAY);
        case JSType::JS_FLOAT64_ARRAY:
            return WriteJSTypedArray(value, SerializationUID::JS_FLOAT64_ARRAY);
        case JSType::JS_BIGINT64_ARRAY:
            return WriteJSTypedArray(value, SerializationUID::JS_BIGINT64_ARRAY);
        case JSType::JS_BIGUINT64_ARRAY:
            return WriteJSTypedArray(value, SerializationUID::JS_BIGUINT64_ARRAY);
        case JSType::JS_ARRAY_BUFFER:
        case JSType::JS_SHARED_ARRAY_BUFFER:
            return WriteJSArrayBuffer(value);
        case JSType::LINE_STRING:
        case JSType::CONSTANT_STRING:
        case JSType::TREE_STRING:
            return WriteEcmaString(value);
        case JSType::JS_OBJECT:
            return WritePlainObject(value);
        case JSType::JS_ASYNC_FUNCTION:  // means CONCURRENT_FUNCTION
            return WriteJSFunction(value);
        case JSType::METHOD:
            return WriteMethod(value);
        case JSType::TAGGED_ARRAY:
            return WriteTaggedArray(value);
        case JSType::BIGINT:
            return WriteBigInt(value);
        default:
            break;
    }
    return false;
}

bool JSSerializer::WriteBigInt(const JSHandle<JSTaggedValue> &value)
{
    JSHandle<BigInt> bigInt = JSHandle<BigInt>::Cast(value);
    if (!WriteType(SerializationUID::BIGINT)) {
        return false;
    }
    uint32_t len = bigInt->GetLength();
    if (!WriteInt(len)) {
        return false;
    }
    bool sign = bigInt->GetSign();
    if (!WriteBoolean(sign)) {
        return false;
    }
    for (uint32_t i = 0; i < len; i++) {
        uint32_t val = bigInt->GetDigit(i);
        if (!WriteInt(val)) {
            return false;
        }
    }
    return true;
}

bool JSSerializer::WriteTaggedArray(const JSHandle<JSTaggedValue> &value)
{
    JSHandle<TaggedArray> taggedArray = JSHandle<TaggedArray>::Cast(value);
    if (!WriteType(SerializationUID::TAGGED_ARRAY)) {
        return false;
    }
    uint32_t len = taggedArray->GetLength();
    if (!WriteInt(len)) {
        return false;
    }
    JSMutableHandle<JSTaggedValue> val(thread_, JSTaggedValue::Undefined());
    for (uint32_t i = 0; i < len; i++) {
        val.Update(taggedArray->Get(i));
        if (!SerializeJSTaggedValue(val)) {
            return false;
        }
    }
    return true;
}

bool JSSerializer::WriteByteArray(const JSHandle<JSTaggedValue> &value, DataViewType viewType)
{
    JSHandle<ByteArray> byteArray = JSHandle<ByteArray>::Cast(value);
    if (!WriteType(SerializationUID::BYTE_ARRAY)) {
        return false;
    }
    uint32_t arrayLength = byteArray->GetArrayLength();
    if (!WriteInt(arrayLength)) {
        return false;
    }
    uint32_t viewTypeIndex = GetDataViewTypeIndex(viewType);
    if (!WriteInt(viewTypeIndex)) {
        return false;
    }
    JSMutableHandle<JSTaggedValue> val(thread_, JSTaggedValue::Undefined());
    for (uint32_t i = 0; i < arrayLength; i++) {
        val.Update(byteArray->Get(thread_, i, viewType));
        if (!SerializeJSTaggedValue(val)) {
            return false;
        }
    }
    return true;
}

uint32_t JSSerializer::GetDataViewTypeIndex(const DataViewType viewType)
{
    uint32_t index = 0;
    switch (viewType) {
        case DataViewType::INT8:
            index = 1; // 1 : DataViewType::INT8
            break;
        case DataViewType::UINT8:
            index = 2; // 2 : DataViewType::UINT8
            break;
        case DataViewType::UINT8_CLAMPED:
            index = 3; // 3 : DataViewType::UINT8_CLAMPED
            break;
        case DataViewType::INT16:
            index = 4; // 4 : DataViewType::INT16
            break;
        case DataViewType::UINT16:
            index = 5; // 5 : DataViewType::UINT16
            break;
        case DataViewType::INT32:
            index = 6; // 6 : DataViewType::INT32
            break;
        case DataViewType::UINT32:
            index = 7; // 7 : DataViewType::UINT32
            break;
        case DataViewType::FLOAT32:
            index = 8; // 8 : DataViewType::FLOAT32
            break;
        case DataViewType::FLOAT64:
            index = 9; // 9 : DataViewType::FLOAT64
            break;
        case DataViewType::BIGINT64:
            index = 10; // 10 : DataViewType::BIGINT64
            break;
        case DataViewType::BIGUINT64:
            index = 11; // 11 : DataViewType::BIGUINT64
            break;
        default:
            LOG_ECMA(FATAL) << "this branch is unreachable";
            UNREACHABLE();
    }
    return index;
}

bool JSSerializer::WriteMethod(const JSHandle<JSTaggedValue> &value)
{
    JSHandle<Method> method = JSHandle<Method>::Cast(value);
    if (method->IsNativeWithCallField()) {
        if (!WriteType(SerializationUID::NATIVE_METHOD)) {
            return false;
        }
        const void *nativeFunc = method->GetNativePointer();
        if (!WriteRawData(&nativeFunc, sizeof(uintptr_t))) {
            return false;
        }
    } else {
        if (!WriteType(SerializationUID::METHOD)) {
            return false;
        }
        const MethodLiteral *methodLiteral = method->GetMethodLiteral();
        if (!WriteRawData(&methodLiteral, sizeof(uintptr_t))) {
            return false;
        }
        JSHandle<ConstantPool> constPool(thread_, method->GetConstantPool());
        const JSPandaFile *jsPandaFile = constPool->GetJSPandaFile();
        if (jsPandaFile == nullptr) {
            return false;
        }
        const CString &desc = jsPandaFile->GetJSPandaFileDesc();
        if (!WriteString(desc)) {
            return false;
        }
    }
    return true;
}

bool JSSerializer::WriteJSFunction(const JSHandle<JSTaggedValue> &value)
{
    if (!WriteType(SerializationUID::CONCURRENT_FUNCTION)) {
        return false;
    }
    JSHandle<JSFunction> func = JSHandle<JSFunction>::Cast(value);
    // check concurrent function
    if (func->GetFunctionKind() != ecmascript::FunctionKind::CONCURRENT_FUNCTION) {
        LOG_ECMA(ERROR) << "only support serialize concurrent function";
        return false;
    }
    JSHandle<JSTaggedValue> method(thread_, func->GetMethod());
    if (!SerializeJSTaggedValue(method)) {
        return false;
    }
    return true;
}

bool JSSerializer::WriteJSError(const JSHandle<JSTaggedValue> &value)
{
    TaggedObject *taggedObject = value->GetTaggedObject();
    JSType errorType = taggedObject->GetClass()->GetObjectType();
    if (!WriteJSErrorHeader(errorType)) {
        return false;
    }
    auto globalConst = thread_->GlobalConstants();
    JSHandle<JSTaggedValue> handleMsg = globalConst->GetHandledMessageString();
    JSHandle<JSTaggedValue> msg = JSObject::GetProperty(thread_, value, handleMsg).GetValue();
    // Write error message
    if (!SerializeJSTaggedValue(msg)) {
        return false;
    }
    return true;
}

bool JSSerializer::WriteJSErrorHeader(JSType type)
{
    switch (type) {
        case JSType::JS_ERROR:
            return WriteType(SerializationUID::JS_ERROR);
        case JSType::JS_EVAL_ERROR:
            return WriteType(SerializationUID::EVAL_ERROR);
        case JSType::JS_RANGE_ERROR:
            return WriteType(SerializationUID::RANGE_ERROR);
        case JSType::JS_REFERENCE_ERROR:
            return WriteType(SerializationUID::REFERENCE_ERROR);
        case JSType::JS_TYPE_ERROR:
            return WriteType(SerializationUID::TYPE_ERROR);
        case JSType::JS_AGGREGATE_ERROR:
            return WriteType(SerializationUID::AGGREGATE_ERROR);
        case JSType::JS_URI_ERROR:
            return WriteType(SerializationUID::URI_ERROR);
        case JSType::JS_SYNTAX_ERROR:
            return WriteType(SerializationUID::SYNTAX_ERROR);
        case JSType::JS_OOM_ERROR:
            return WriteType(SerializationUID::OOM_ERROR);
        default:
            LOG_ECMA(FATAL) << "this branch is unreachable";
            UNREACHABLE();
    }
    return false;
}

bool JSSerializer::WriteJSDate(const JSHandle<JSTaggedValue> &value)
{
    JSHandle<JSDate> date = JSHandle<JSDate>::Cast(value);
    if (!WriteType(SerializationUID::JS_DATE)) {
        return false;
    }
    if (!WritePlainObject(value)) {
        return false;
    }
    double timeValue = date->GetTimeValue().GetDouble();
    if (!WriteDouble(timeValue)) {
        return false;
    }
    double localOffset = date->GetLocalOffset().GetDouble();
    if (!WriteDouble(localOffset)) {
        return false;
    }
    return true;
}

bool JSSerializer::WriteJSArray(const JSHandle<JSTaggedValue> &value)
{
    JSHandle<JSArray> array = JSHandle<JSArray>::Cast(value);
    if (!WriteType(SerializationUID::JS_ARRAY)) {
        return false;
    }
    if (!WritePlainObject(value)) {
        return false;
    }
    uint32_t arrayLength = array->GetLength();
    if (!WriteInt(arrayLength)) {
        return false;
    }
    return true;
}

bool JSSerializer::WriteEcmaString(const JSHandle<JSTaggedValue> &value)
{
    JSHandle<EcmaString> strHandle = JSHandle<EcmaString>::Cast(value);
    auto string = JSHandle<EcmaString>(thread_, EcmaStringAccessor::Flatten(thread_->GetEcmaVM(), strHandle));
    if (!WriteType(SerializationUID::ECMASTRING)) {
        return false;
    }

    size_t length = EcmaStringAccessor(string).GetLength();
    if (!WriteInt(static_cast<int32_t>(length))) {
        return false;
    }
    // skip writeRawData for empty EcmaString
    if (length == 0) {
        return true;
    }

    bool isUtf8 = EcmaStringAccessor(string).IsUtf8();
    // write utf encode flag
    if (!WriteBoolean(isUtf8)) {
        return false;
    }
    if (isUtf8) {
        const uint8_t *data = EcmaStringAccessor(string).GetDataUtf8();
        const uint8_t strEnd = '\0';
        if (!WriteRawData(data, length) || !WriteRawData(&strEnd, sizeof(uint8_t))) {
            return false;
        }
    } else {
        const uint16_t *data = EcmaStringAccessor(string).GetDataUtf16();
        if (!WriteRawData(data, length * sizeof(uint16_t))) {
            return false;
        }
    }
    return true;
}

bool JSSerializer::WriteJSMap(const JSHandle<JSTaggedValue> &value)
{
    JSHandle<JSMap> map = JSHandle<JSMap>::Cast(value);
    if (!WriteType(SerializationUID::JS_MAP)) {
        return false;
    }
    if (!WritePlainObject(value)) {
        return false;
    }
    uint32_t size = map->GetSize();
    if (!WriteInt(static_cast<int32_t>(size))) {
        return false;
    }
    JSMutableHandle<JSTaggedValue> key(thread_, JSTaggedValue::Undefined());
    JSMutableHandle<JSTaggedValue> val(thread_, JSTaggedValue::Undefined());
    for (uint32_t i = 0; i < size; i++) {
        key.Update(map->GetKey(i));
        if (!SerializeJSTaggedValue(key)) {
            return false;
        }
        val.Update(map->GetValue(i));
        if (!SerializeJSTaggedValue(val)) {
            return false;
        }
    }
    return true;
}

bool JSSerializer::WriteJSSet(const JSHandle<JSTaggedValue> &value)
{
    JSHandle<JSSet> set = JSHandle<JSSet>::Cast(value);
    if (!WriteType(SerializationUID::JS_SET)) {
        return false;
    }
    if (!WritePlainObject(value)) {
        return false;
    }
    uint32_t size = set->GetSize();
    if (!WriteInt(static_cast<int32_t>(size))) {
        return false;
    }
    JSMutableHandle<JSTaggedValue> val(thread_, JSTaggedValue::Undefined());
    for (uint32_t i = 0; i < size; i++) {
        val.Update(set->GetValue(i));
        if (!SerializeJSTaggedValue(val)) {
            return false;
        }
    }
    return true;
}

bool JSSerializer::WriteJSRegExp(const JSHandle<JSTaggedValue> &value)
{
    JSHandle<JSRegExp> regExp = JSHandle<JSRegExp>::Cast(value);
    if (!WriteType(SerializationUID::JS_REG_EXP)) {
        return false;
    }
    if (!WritePlainObject(value)) {
        return false;
    }
    uint32_t bufferSize = regExp->GetLength();
    if (!WriteInt(static_cast<int32_t>(bufferSize))) {
        return false;
    }
    // Write Accessor(ByteCodeBuffer) which is a pointer to a dynamic buffer
    JSHandle<JSTaggedValue> bufferValue(thread_, regExp->GetByteCodeBuffer());
    JSHandle<JSNativePointer> np = JSHandle<JSNativePointer>::Cast(bufferValue);
    void *dynBuffer = np->GetExternalPointer();
    if (!WriteRawData(dynBuffer, bufferSize)) {
        return false;
    }
    // Write Accessor(OriginalSource)
    JSHandle<JSTaggedValue> originalSource(thread_, regExp->GetOriginalSource());
    if (!SerializeJSTaggedValue(originalSource)) {
        return false;
    }
    // Write Accessor(OriginalFlags)
    JSHandle<JSTaggedValue> originalFlags(thread_, regExp->GetOriginalFlags());
    if (!SerializeJSTaggedValue(originalFlags)) {
        return false;
    }
    return true;
}

bool JSSerializer::WriteJSTypedArray(const JSHandle<JSTaggedValue> &value, SerializationUID uId)
{
    JSHandle<JSTypedArray> typedArray = JSHandle<JSTypedArray>::Cast(value);
    if (!WriteType(uId)) {
        return false;
    }
    if (!WritePlainObject(value)) {
        return false;
    }
    [[maybe_unused]] DataViewType viewType = TypedArrayHelper::GetType(typedArray);
    // Write ACCESSORS(ViewedArrayBuffer) which is a pointer to an ArrayBuffer
    JSHandle<JSTaggedValue> viewedArrayBufferOrByteArray(thread_, typedArray->GetViewedArrayBufferOrByteArray());
    bool isViewedArrayBuffer = false;
    if (viewedArrayBufferOrByteArray->IsArrayBuffer() || viewedArrayBufferOrByteArray->IsSharedArrayBuffer()) {
        isViewedArrayBuffer = true;
        if (!WriteBoolean(isViewedArrayBuffer)) {
            return false;
        }
        if (!SerializeJSTaggedValue(viewedArrayBufferOrByteArray)) {
            return false;
        }
    } else {
        if (!WriteBoolean(isViewedArrayBuffer)) {
            return false;
        }
        if (!WriteByteArray(viewedArrayBufferOrByteArray, viewType)) {
            return false;
        }
    }

    // Write ACCESSORS(TypedArrayName)
    JSHandle<JSTaggedValue> typedArrayName(thread_, typedArray->GetTypedArrayName());
    if (!SerializeJSTaggedValue(typedArrayName)) {
        return false;
    }
    // Write ACCESSORS(ByteLength)
    JSTaggedValue byteLength(typedArray->GetByteLength());
    if (!WriteRawData(&byteLength, sizeof(JSTaggedValue))) {
        return false;
    }
    // Write ACCESSORS(ByteOffset)
    JSTaggedValue byteOffset(typedArray->GetByteOffset());
    if (!WriteRawData(&byteOffset, sizeof(JSTaggedValue))) {
        return false;
    }
    // Write ACCESSORS(ArrayLength)
    JSTaggedValue arrayLength(typedArray->GetArrayLength());
    if (!WriteRawData(&arrayLength, sizeof(JSTaggedValue))) {
        return false;
    }
    // Write ACCESSORS(ContentType)
    ContentType contentType = typedArray->GetContentType();
    if (!WriteRawData(&contentType, sizeof(ContentType))) {
        return false;
    }
    return true;
}

bool JSSerializer::WriteJSNativePointer(const JSHandle<JSNativePointer> &nativePtr)
{
    uintptr_t externalPtr = reinterpret_cast<uintptr_t>(nativePtr->GetExternalPointer());
    if (!WriteRawData(&externalPtr, sizeof(uintptr_t))) {
        return false;
    }
    uintptr_t deleter = reinterpret_cast<uintptr_t>(nativePtr->GetDeleter());
    if (!WriteRawData(&deleter, sizeof(uintptr_t))) {
        return false;
    }
    uintptr_t allocatorPtr = reinterpret_cast<uintptr_t>(nativePtr->GetData());
    if (!WriteRawData(&allocatorPtr, sizeof(uintptr_t))) {
        return false;
    }
    int32_t bindingSize = static_cast<int32_t>(nativePtr->GetBindingSize());
    if (!WriteInt(bindingSize)) {
        return false;
    }
    nativePtr->Detach();
    return true;
}

bool JSSerializer::WriteJSArrayBuffer(const JSHandle<JSTaggedValue> &value)
{
    JSHandle<JSArrayBuffer> arrayBuffer = JSHandle<JSArrayBuffer>::Cast(value);
    if (arrayBuffer->IsDetach()) {
        return false;
    }
    bool shared = arrayBuffer->GetShared();
    bool transfer = transferDataSet_.find(static_cast<uintptr_t>(value.GetTaggedType())) != transferDataSet_.end();
    if (shared && transfer) {
        LOG_ECMA(ERROR) << "Can't transfer a shared JSArrayBuffer";
        return false;
    }
    if (shared) {
        if (!WriteType(SerializationUID::JS_SHARED_ARRAY_BUFFER)) {
            return false;
        }
    } else if (transfer) {
        if (!WriteType(SerializationUID::JS_TRANSFER_ARRAY_BUFFER)) {
            return false;
        }
    } else {
        if (!WriteType(SerializationUID::JS_ARRAY_BUFFER)) {
            return false;
        }
    }

    bool withNativeAreaAllocator = arrayBuffer->GetWithNativeAreaAllocator();
    if (!WriteBoolean(withNativeAreaAllocator)) {
        return false;
    }

    // Write Accessors(ArrayBufferByteLength)
    uint32_t arrayLength = arrayBuffer->GetArrayBufferByteLength();
    if (!WriteInt(arrayLength)) {
        return false;
    }

    bool empty = arrayLength == 0;
    if (!empty) {
        JSHandle<JSNativePointer> np(thread_, arrayBuffer->GetArrayBufferData());
        if (shared) {
            void *buffer = np->GetExternalPointer();
            JSSharedMemoryManager::GetInstance()->CreateOrLoad(&buffer, arrayLength);
            uint64_t bufferAddr = reinterpret_cast<uint64_t>(buffer);
            if (!WriteRawData(&bufferAddr, sizeof(uint64_t))) {
                return false;
            }
        } else if (transfer) {
            // Write Accessors(ArrayBufferData) which is a pointer to a Buffer
            if (!WriteJSNativePointer(np)) {
                return false;
            }
            arrayBuffer->Detach(thread_, withNativeAreaAllocator);
        } else {
            // Write Accessors(ArrayBufferData) which is a pointer to a Buffer
            void *buffer = np->GetExternalPointer();
            if (!WriteRawData(buffer, arrayLength)) {
                return false;
            }
        }
    }

    // write obj properties
    if (!WritePlainObject(value)) {
        return false;
    }
    return true;
}

bool JSSerializer::IsNativeBindingObject(std::vector<JSTaggedValue> keyVector)
{
    if (keyVector.size() < 2) { // 2: detachSymbol, attachSymbol
        return false;
    }
    JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSTaggedValue> detach = env->GetDetachSymbol();
    JSHandle<JSTaggedValue> attach = env->GetAttachSymbol();
    JSMutableHandle<JSTaggedValue> detachKey(thread_, JSTaggedValue::Undefined());
    JSMutableHandle<JSTaggedValue> attachKey(thread_, JSTaggedValue::Undefined());
    uint32_t keyLength = keyVector.size();
    for (uint32_t i = 0; i < keyLength - 1; i++) {
        if (keyVector[i].IsSymbol() && keyVector[i + 1].IsSymbol()) {
            detachKey.Update(keyVector[i]);
            attachKey.Update(keyVector[i + 1]);
            if (JSTaggedValue::Equal(thread_, detach, detachKey) || JSTaggedValue::Equal(thread_, attach, attachKey)) {
                return true;
            }
        }
    }
    return false;
}

bool JSSerializer::WritePlainObject(const JSHandle<JSTaggedValue> &objValue)
{
    JSHandle<JSObject> obj = JSHandle<JSObject>::Cast(objValue);
    std::vector<JSTaggedValue> keyVector;
    uint32_t propertiesLength = obj->GetNumberOfKeys();
    JSObject::GetAllKeys(obj, keyVector);
    if (keyVector.size() != propertiesLength) {
        return false;
    }

    // Write custom JS obj that only used for carrying native binding functions
    if (IsNativeBindingObject(keyVector)) {
        return WriteNativeBindingObject(objValue);
    }

    // not support native object without detach and attach
    if (obj->GetNativePointerFieldCount() > 0) {
        return false;
    }

    if (!WriteType(SerializationUID::JS_PLAIN_OBJECT)) {
        return false;
    }
    if (!WriteInt(static_cast<int32_t>(propertiesLength))) {
        return false;
    }
    JSMutableHandle<JSTaggedValue> propertyKey(thread_, JSTaggedValue::Undefined());
    for (uint32_t i = 0; i < propertiesLength; i++) {
        if (keyVector.empty()) {
            return false;
        }
        propertyKey.Update(keyVector[i]);
        if (!SerializeJSTaggedValue(propertyKey)) {
            return false;
        }
        PropertyDescriptor desc(thread_);
        JSObject::OrdinaryGetOwnProperty(thread_, obj, propertyKey, desc);
        if (!WriteDesc(desc)) {
            return false;
        }
    }

    uint32_t elementsLength = obj->GetNumberOfElements();
    if (!WriteInt(static_cast<int32_t>(elementsLength))) {
        return false;
    }
    keyVector.clear();
    JSObject::GetALLElementKeysIntoVector(thread_, obj, keyVector);
    // Write elements' description attributes and value
    if (keyVector.size() != elementsLength) {
        return false;
    }
    JSMutableHandle<JSTaggedValue> elementKey(thread_, JSTaggedValue::Undefined());
    for (uint32_t i = 0; i < elementsLength; i++) {
        elementKey.Update(keyVector[i]);
        if (!SerializeJSTaggedValue(elementKey)) {
            return false;
        }
        PropertyDescriptor desc(thread_);
        JSObject::OrdinaryGetOwnProperty(thread_, obj, elementKey, desc);
        if (!WriteDesc(desc)) {
            return false;
        }
    }
    return true;
}

bool JSSerializer::WriteNativeBindingObject(const JSHandle<JSTaggedValue> &objValue)
{
    JSHandle<JSObject> obj = JSHandle<JSObject>::Cast(objValue);
    JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSTaggedValue> detach = env->GetDetachSymbol();
    JSHandle<JSTaggedValue> attach = env->GetAttachSymbol();
    if (!WriteType(SerializationUID::NATIVE_BINDING_OBJECT)) {
        return false;
    }
    int32_t paramCount = obj->GetNativePointerFieldCount();
    void *enginePointer = nullptr;
    void *objPointer = nullptr;
    void *hint = nullptr;
    void *detachData = nullptr;
    void *attachData = nullptr;
    if (paramCount == 5) { // 5 : enginePointer, objPointer, hint, detachData, attachData
        enginePointer = obj->GetNativePointerField(0);
        objPointer = obj->GetNativePointerField(1);
        hint = obj->GetNativePointerField(2); // 2 : hint
        detachData = obj->GetNativePointerField(3); // 3 : detachData
        attachData = obj->GetNativePointerField(4); // 4 : attachData
    }
    // Write custom object's values: AttachFunc*, buffer*
    JSHandle<JSTaggedValue> detachVal = JSObject::GetProperty(thread_, obj, detach).GetRawValue();
    JSHandle<JSTaggedValue> attackVal = JSObject::GetProperty(thread_, obj, attach).GetRawValue();
    DetachFunc detachNative = reinterpret_cast<DetachFunc>(JSNativePointer::Cast(
        detachVal.GetTaggedValue().GetTaggedObject())->GetExternalPointer());
    if (detachNative == nullptr) {
        return false;
    }
    void *buffer = detachNative(enginePointer, objPointer, hint, detachData);
    AttachFunc attachNative = reinterpret_cast<AttachFunc>(JSNativePointer::Cast(
        attackVal.GetTaggedValue().GetTaggedObject())->GetExternalPointer());
    if (!WriteRawData(&attachNative, sizeof(uintptr_t))) {
        return false;
    }
    if (!WriteRawData(&buffer, sizeof(uintptr_t))) {
        return false;
    }
    if (!WriteRawData(&hint, sizeof(uintptr_t))) {
        return false;
    }
    if (!WriteRawData(&attachData, sizeof(uintptr_t))) {
        return false;
    }
    return true;
}

bool JSSerializer::WriteDesc(const PropertyDescriptor &desc)
{
    bool isWritable = desc.IsWritable();
    if (!WriteBoolean(isWritable)) {
        return false;
    }
    bool isEnumerable = desc.IsEnumerable();
    if (!WriteBoolean(isEnumerable)) {
        return false;
    }
    bool isConfigurable = desc.IsConfigurable();
    if (!WriteBoolean(isConfigurable)) {
        return false;
    }
    bool hasWritable = desc.HasWritable();
    if (!WriteBoolean(hasWritable)) {
        return false;
    }
    bool hasEnumerable = desc.HasEnumerable();
    if (!WriteBoolean(hasEnumerable)) {
        return false;
    }
    bool hasConfigurable = desc.HasConfigurable();
    if (!WriteBoolean(hasConfigurable)) {
        return false;
    }
    JSHandle<JSTaggedValue> value = desc.GetValue();
    if (!SerializeJSTaggedValue(value)) {
        return false;
    }
    return true;
}

SerializationUID JSDeserializer::ReadType()
{
    SerializationUID uid;
    if (position_ >= end_) {
        return SerializationUID::UNKNOWN;
    }
    uid = static_cast<SerializationUID>(*position_);
    if (uid < SerializationUID::UID_BEGIN || uid > SerializationUID::UID_END) {
        return SerializationUID::UNKNOWN;
    }
    position_++;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
    return uid;
}

bool JSDeserializer::ReadInt(int32_t *value)
{
    size_t len = sizeof(int32_t);
    if (len > static_cast<size_t>(end_ - position_)) {
        return false;
    }
    if (memcpy_s(value, len, position_, len) != EOK) {
        LOG_ECMA(FATAL) << "this branch is unreachable";
        UNREACHABLE();
    }
    position_ += len;
    return true;
}

bool JSDeserializer::ReadObjectId(uint64_t *objectId)
{
    size_t len = sizeof(uint64_t);
    if (len > static_cast<size_t>(end_ - position_)) {
        return false;
    }
    if (memcpy_s(objectId, len, position_, len) != EOK) {
        LOG_ECMA(FATAL) << "this branch is unreachable";
        UNREACHABLE();
    }
    position_ += len;
    return true;
}

bool JSDeserializer::ReadDouble(double *value)
{
    size_t len = sizeof(double);
    if (len > static_cast<size_t>(end_ - position_)) {
        return false;
    }
    if (memcpy_s(value, len, position_, len) != EOK) {
        LOG_ECMA(FATAL) << "this branch is unreachable";
        UNREACHABLE();
    }
    position_ += len;
    return true;
}

JSDeserializer::~JSDeserializer()
{
    referenceMap_.clear();
}

JSHandle<JSTaggedValue> JSDeserializer::Deserialize()
{
    size_t maxSerializerSize = thread_->GetEcmaVM()->GetEcmaParamConfiguration().GetMaxJSSerializerSize();
    uint8_t dataSize = end_ - begin_;
    if (dataSize > maxSerializerSize) {
        LOG_ECMA(ERROR) << "The Serialization data size exceed limit Size";
        return JSHandle<JSTaggedValue>();
    }
    JSHandle<JSTaggedValue> res = DeserializeJSTaggedValue();
    return res;
}

JSHandle<JSTaggedValue> JSDeserializer::DeserializeJSTaggedValue()
{
    SerializationUID uid = ReadType();
    if (uid == SerializationUID::UNKNOWN) {
        return JSHandle<JSTaggedValue>();
    }
    switch (uid) {
        case SerializationUID::JS_NULL:
            return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Null());
        case SerializationUID::JS_UNDEFINED:
            return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined());
        case SerializationUID::JS_TRUE:
            return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True());
        case SerializationUID::JS_FALSE:
            return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False());
        case SerializationUID::HOLE:
            return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Hole());
        case SerializationUID::INT32: {
            int32_t value;
            if (!ReadInt(&value)) {
                return JSHandle<JSTaggedValue>();
            }
            return JSHandle<JSTaggedValue>(thread_, JSTaggedValue(value));
        }
        case SerializationUID::DOUBLE: {
            double value;
            if (!ReadDouble(&value)) {
                return JSHandle<JSTaggedValue>();
            }
            return JSHandle<JSTaggedValue>(thread_, JSTaggedValue(value));
        }
        case SerializationUID::JS_ERROR:
        case SerializationUID::EVAL_ERROR:
        case SerializationUID::RANGE_ERROR:
        case SerializationUID::REFERENCE_ERROR:
        case SerializationUID::TYPE_ERROR:
        case SerializationUID::AGGREGATE_ERROR:
        case SerializationUID::URI_ERROR:
        case SerializationUID::SYNTAX_ERROR:
        case SerializationUID::OOM_ERROR:
            return ReadJSError(uid);
        case SerializationUID::JS_DATE:
            return ReadJSDate();
        case SerializationUID::JS_PLAIN_OBJECT:
            return ReadPlainObject();
        case SerializationUID::NATIVE_BINDING_OBJECT:
            return ReadNativeBindingObject();
        case SerializationUID::JS_ARRAY:
            return ReadJSArray();
        case SerializationUID::ECMASTRING:
            return ReadEcmaString();
        case SerializationUID::JS_MAP:
            return ReadJSMap();
        case SerializationUID::JS_SET:
            return ReadJSSet();
        case SerializationUID::JS_REG_EXP:
            return ReadJSRegExp();
        case SerializationUID::JS_INT8_ARRAY:
            return ReadJSTypedArray(SerializationUID::JS_INT8_ARRAY);
        case SerializationUID::JS_UINT8_ARRAY:
            return ReadJSTypedArray(SerializationUID::JS_UINT8_ARRAY);
        case SerializationUID::JS_UINT8_CLAMPED_ARRAY:
            return ReadJSTypedArray(SerializationUID::JS_UINT8_CLAMPED_ARRAY);
        case SerializationUID::JS_INT16_ARRAY:
            return ReadJSTypedArray(SerializationUID::JS_INT16_ARRAY);
        case SerializationUID::JS_UINT16_ARRAY:
            return ReadJSTypedArray(SerializationUID::JS_UINT16_ARRAY);
        case SerializationUID::JS_INT32_ARRAY:
            return ReadJSTypedArray(SerializationUID::JS_INT32_ARRAY);
        case SerializationUID::JS_UINT32_ARRAY:
            return ReadJSTypedArray(SerializationUID::JS_UINT32_ARRAY);
        case SerializationUID::JS_FLOAT32_ARRAY:
            return ReadJSTypedArray(SerializationUID::JS_FLOAT32_ARRAY);
        case SerializationUID::JS_FLOAT64_ARRAY:
            return ReadJSTypedArray(SerializationUID::JS_FLOAT64_ARRAY);
        case SerializationUID::JS_BIGINT64_ARRAY:
            return ReadJSTypedArray(SerializationUID::JS_BIGINT64_ARRAY);
        case SerializationUID::JS_BIGUINT64_ARRAY:
            return ReadJSTypedArray(SerializationUID::JS_BIGUINT64_ARRAY);
        case SerializationUID::JS_ARRAY_BUFFER:
        case SerializationUID::JS_SHARED_ARRAY_BUFFER:
        case SerializationUID::JS_TRANSFER_ARRAY_BUFFER:
            return ReadJSArrayBuffer(uid);
        case SerializationUID::TAGGED_OBJECT_REFERNCE:
            return ReadReference();
        case SerializationUID::CONCURRENT_FUNCTION:
            return ReadJSFunction();
        case SerializationUID::TAGGED_ARRAY:
            return ReadTaggedArray();
        case SerializationUID::METHOD:
            return ReadMethod();
        case SerializationUID::NATIVE_METHOD:
            return ReadNativeMethod();
        case SerializationUID::BIGINT:
            return ReadBigInt();
        default:
            return JSHandle<JSTaggedValue>();
    }
}

JSHandle<JSTaggedValue> JSDeserializer::ReadBigInt()
{
    int32_t len = 0;
    if (!JudgeType(SerializationUID::INT32) || !ReadInt(&len)) {
        return JSHandle<JSTaggedValue>();
    }
    bool sign = false;
    if (!ReadBoolean(&sign)) {
        return JSHandle<JSTaggedValue>();
    }
    JSHandle<BigInt> bigInt = factory_->NewBigInt(len);
    bigInt->SetSign(sign);
    JSHandle<JSTaggedValue> bigIntVal(bigInt);
    referenceMap_.emplace(objectId_++, bigIntVal);
    for (int32_t i = 0; i < len; i++) {
        int32_t val = 0;
        if (!JudgeType(SerializationUID::INT32) || !ReadInt(&val)) {
            return JSHandle<JSTaggedValue>();
        }
        bigInt->SetDigit(i, val);
    }
    return bigIntVal;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadTaggedArray()
{
    int32_t len = 0;
    if (!JudgeType(SerializationUID::INT32) || !ReadInt(&len)) {
        return JSHandle<JSTaggedValue>();
    }
    JSHandle<TaggedArray> taggedArray = factory_->NewTaggedArray(len);
    JSHandle<JSTaggedValue> arrayTag(taggedArray);
    referenceMap_.emplace(objectId_++, arrayTag);
    for (int32_t i = 0; i < len; i++) {
        JSHandle<JSTaggedValue> val = DeserializeJSTaggedValue();
        taggedArray->Set(thread_, i, val.GetTaggedValue());
    }
    return arrayTag;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadByteArray()
{
    int32_t arrayLength = 0;
    if (!JudgeType(SerializationUID::INT32) || !ReadInt(&arrayLength)) {
        return JSHandle<JSTaggedValue>();
    }
    int32_t viewTypeIndex = 0;
    if (!JudgeType(SerializationUID::INT32) || !ReadInt(&viewTypeIndex)) {
        return JSHandle<JSTaggedValue>();
    }
    DataViewType viewType = GetDataViewTypeByIndex(viewTypeIndex);
    uint32_t arrayType = TypedArrayHelper::GetSizeFromType(viewType);
    JSHandle<ByteArray> byteArray = factory_->NewByteArray(arrayLength, arrayType);
    for (int32_t i = 0; i < arrayLength; i++) {
        JSHandle<JSTaggedValue> val = DeserializeJSTaggedValue();
        byteArray->Set(thread_, i, viewType, val.GetTaggedType());
    }
    return JSHandle<JSTaggedValue>(byteArray);
}

DataViewType JSDeserializer::GetDataViewTypeByIndex(uint32_t viewTypeIndex)
{
    DataViewType viewType;
    switch (viewTypeIndex) {
        case 1: // 1 : DataViewType::INT8
            viewType = DataViewType::INT8;
            break;
        case 2: // 2 : DataViewType::UINT8
            viewType = DataViewType::UINT8;
            break;
        case 3: // 3 : DataViewType::UINT8_CLAMPED
            viewType = DataViewType::UINT8_CLAMPED;
            break;
        case 4: // 4 : DataViewType::INT16
            viewType = DataViewType::INT16;
            break;
        case 5: // 5 : DataViewType::UINT16
            viewType = DataViewType::UINT16;
            break;
        case 6: // 6 : DataViewType::INT32
            viewType = DataViewType::INT32;
            break;
        case 7: // 7 : DataViewType::UINT32
            viewType = DataViewType::UINT32;
            break;
        case 8: // 8 : DataViewType::FLOAT32
            viewType = DataViewType::FLOAT32;
            break;
        case 9: // 9 : DataViewType::FLOAT64
            viewType = DataViewType::FLOAT64;
            break;
        case 10: // 10 : DataViewType::BIGINT64
            viewType = DataViewType::BIGINT64;
            break;
        case 11: // 11 : DataViewType::BIGUINT64
            viewType = DataViewType::BIGUINT64;
            break;
        default:
            LOG_ECMA(FATAL) << "this branch is unreachable";
            UNREACHABLE();
    }
    return viewType;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadMethod()
{
    uintptr_t methodLiteral;
    if (!ReadNativePointer(&methodLiteral)) {
        return JSHandle<JSTaggedValue>();
    }
    JSHandle<Method> method = factory_->NewMethod(reinterpret_cast<MethodLiteral *>(methodLiteral));
    JSHandle<JSTaggedValue> methodTag(method);
    referenceMap_.emplace(objectId_++, methodTag);

    CString desc;
    if (!ReadString(&desc)) {
        return JSHandle<JSTaggedValue>();
    }
    std::shared_ptr<JSPandaFile> jsPandaFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(desc);
    if (jsPandaFile == nullptr) {
        return JSHandle<JSTaggedValue>();
    }
    JSHandle<ConstantPool> constPool =
        thread_->GetCurrentEcmaContext()->FindOrCreateConstPool(jsPandaFile.get(), method->GetMethodId());
    method->SetConstantPool(thread_, constPool.GetTaggedValue());
    return methodTag;
}

bool JSDeserializer::ReadString(CString *value)
{
    if (!JudgeType(SerializationUID::C_STRING)) {
        return false;
    }

    *value = reinterpret_cast<char *>(const_cast<uint8_t *>(position_));
    size_t len = value->length() + 1; // 1: '\0'
    position_ += len;
    return true;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadNativeMethod()
{
    uintptr_t nativeFunc;
    if (!ReadNativePointer(&nativeFunc)) {
        return JSHandle<JSTaggedValue>();
    }
    JSHandle<Method> method = factory_->NewMethodForNativeFunction(reinterpret_cast<void *>(nativeFunc));
    JSHandle<JSTaggedValue> methodTag(method);
    referenceMap_.emplace(objectId_++, methodTag);
    return methodTag;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadJSFunction()
{
    JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSFunction> func = factory_->NewJSFunction(env, nullptr, FunctionKind::CONCURRENT_FUNCTION);
    JSHandle<JSTaggedValue> funcTag(func);
    referenceMap_.emplace(objectId_++, funcTag);
    JSHandle<JSTaggedValue> methodVal = DeserializeJSTaggedValue();
    JSHandle<Method> method = JSHandle<Method>::Cast(methodVal);
    func->SetMethod(thread_, method);
    return funcTag;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadJSError(SerializationUID uid)
{
    base::ErrorType errorType;
    switch (uid) {
        case SerializationUID::JS_ERROR:
            errorType = base::ErrorType::ERROR;
            break;
        case SerializationUID::EVAL_ERROR:
            errorType = base::ErrorType::EVAL_ERROR;
            break;
        case SerializationUID::RANGE_ERROR:
            errorType = base::ErrorType::RANGE_ERROR;
            break;
        case SerializationUID::REFERENCE_ERROR:
            errorType = base::ErrorType::REFERENCE_ERROR;
            break;
        case SerializationUID::TYPE_ERROR:
            errorType = base::ErrorType::TYPE_ERROR;
            break;
        case SerializationUID::AGGREGATE_ERROR:
            errorType = base::ErrorType::AGGREGATE_ERROR;
            break;
        case SerializationUID::URI_ERROR:
            errorType = base::ErrorType::URI_ERROR;
            break;
        case SerializationUID::SYNTAX_ERROR:
            errorType = base::ErrorType::SYNTAX_ERROR;
            break;
        case SerializationUID::OOM_ERROR:
            errorType = base::ErrorType::OOM_ERROR;
            break;
        default:
            LOG_ECMA(FATAL) << "this branch is unreachable";
            UNREACHABLE();
    }
    JSHandle<JSTaggedValue> msg = DeserializeJSTaggedValue();
    JSHandle<EcmaString> handleMsg(msg);
    JSHandle<JSTaggedValue> errorTag = JSHandle<JSTaggedValue>::Cast(factory_->NewJSError(errorType, handleMsg));
    referenceMap_.emplace(objectId_++, errorTag);
    return errorTag;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadJSDate()
{
    JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSFunction> dateFunction(env->GetDateFunction());
    JSHandle<JSDate> date = JSHandle<JSDate>::Cast(factory_->NewJSObjectByConstructor(dateFunction));
    JSHandle<JSTaggedValue> dateTag = JSHandle<JSTaggedValue>::Cast(date);
    referenceMap_.emplace(objectId_++, dateTag);
    if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(dateTag)) {
        return JSHandle<JSTaggedValue>();
    }
    double timeValue = 0.0;
    if (!JudgeType(SerializationUID::DOUBLE) || !ReadDouble(&timeValue)) {
        return JSHandle<JSTaggedValue>();
    }
    date->SetTimeValue(thread_, JSTaggedValue(timeValue));
    double localOffset = 0.0;
    if (!JudgeType(SerializationUID::DOUBLE) || !ReadDouble(&localOffset)) {
        return JSHandle<JSTaggedValue>();
    }
    date->SetLocalOffset(thread_, JSTaggedValue(localOffset));
    return dateTag;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadJSArray()
{
    JSHandle<JSArray> jsArray = thread_->GetEcmaVM()->GetFactory()->NewJSArray();
    JSHandle<JSTaggedValue> arrayTag = JSHandle<JSTaggedValue>::Cast(jsArray);
    referenceMap_.emplace(objectId_++, arrayTag);
    if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(arrayTag)) {
        return JSHandle<JSTaggedValue>();
    }
    int32_t arrLength;
    if (!JudgeType(SerializationUID::INT32) || !ReadInt(&arrLength)) {
        return JSHandle<JSTaggedValue>();
    }
    jsArray->SetLength(arrLength);
    return arrayTag;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadEcmaString()
{
    int32_t stringLength;
    if (!JudgeType(SerializationUID::INT32) || !ReadInt(&stringLength)) {
        return JSHandle<JSTaggedValue>();
    }
    if (stringLength == 0) {
        JSHandle<JSTaggedValue> emptyString = JSHandle<JSTaggedValue>::Cast(factory_->GetEmptyString());
        referenceMap_.emplace(objectId_++, emptyString);
        return emptyString;
    }

    bool isUtf8 = false;
    if (!ReadBoolean(&isUtf8)) {
        return JSHandle<JSTaggedValue>();
    }

    JSHandle<JSTaggedValue> stringTag;
    if (isUtf8) {
        uint8_t *string = reinterpret_cast<uint8_t*>(GetBuffer(stringLength + 1));
        if (string == nullptr) {
            return JSHandle<JSTaggedValue>();
        }

        JSHandle<EcmaString> ecmaString = factory_->NewFromUtf8(string, stringLength);
        stringTag = JSHandle<JSTaggedValue>(ecmaString);
        referenceMap_.emplace(objectId_++, stringTag);
    } else {
        uint16_t *string = reinterpret_cast<uint16_t*>(GetBuffer(stringLength * sizeof(uint16_t)));
        if (string == nullptr) {
            return JSHandle<JSTaggedValue>();
        }
        JSHandle<EcmaString> ecmaString = factory_->NewFromUtf16(string, stringLength);
        stringTag = JSHandle<JSTaggedValue>(ecmaString);
        referenceMap_.emplace(objectId_++, stringTag);
    }
    return stringTag;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadPlainObject()
{
    JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSFunction> objFunc(env->GetObjectFunction());
    JSHandle<JSObject> jsObject = thread_->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(objFunc);
    JSHandle<JSTaggedValue> objTag = JSHandle<JSTaggedValue>::Cast(jsObject);
    referenceMap_.emplace(objectId_++, objTag);
    if (!DefinePropertiesAndElements(objTag)) {
        return JSHandle<JSTaggedValue>();
    }
    return objTag;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadNativeBindingObject()
{
    uintptr_t funcPointer;
    if (!ReadNativePointer(&funcPointer)) {
        return JSHandle<JSTaggedValue>();
    }
    AttachFunc attachFunc = reinterpret_cast<AttachFunc>(funcPointer);
    if (attachFunc == nullptr) {
        return JSHandle<JSTaggedValue>();
    }
    uintptr_t bufferPointer;
    if (!ReadNativePointer(&bufferPointer)) {
        return JSHandle<JSTaggedValue>();
    }
    uintptr_t hint;
    if (!ReadNativePointer(&hint)) {
        return JSHandle<JSTaggedValue>();
    }
    uintptr_t attachData;
    if (!ReadNativePointer(&attachData)) {
        return JSHandle<JSTaggedValue>();
    }
    Local<JSValueRef> attachVal = attachFunc(engine_, reinterpret_cast<void *>(bufferPointer),
        reinterpret_cast<void *>(hint), reinterpret_cast<void *>(attachData));
    if (attachVal.IsEmpty()) {
        LOG_ECMA(ERROR) << "NativeBindingObject is empty";
        attachVal = JSValueRef::Undefined(thread_->GetEcmaVM());
    }
    objectId_++;
    return JSNApiHelper::ToJSHandle(attachVal);
}

JSHandle<JSTaggedValue> JSDeserializer::ReadJSMap()
{
    JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSFunction> mapFunction(env->GetBuiltinsMapFunction());
    JSHandle<JSMap> jsMap = JSHandle<JSMap>::Cast(factory_->NewJSObjectByConstructor(mapFunction));
    JSHandle<JSTaggedValue> mapTag = JSHandle<JSTaggedValue>::Cast(jsMap);
    referenceMap_.emplace(objectId_++, mapTag);
    if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(mapTag)) {
        return JSHandle<JSTaggedValue>();
    }
    int32_t size;
    if (!JudgeType(SerializationUID::INT32) || !ReadInt(&size)) {
        return JSHandle<JSTaggedValue>();
    }
    JSHandle<LinkedHashMap> linkedMap = LinkedHashMap::Create(thread_);
    jsMap->SetLinkedMap(thread_, linkedMap);
    for (int32_t i = 0; i < size; i++) {
        JSHandle<JSTaggedValue> key = DeserializeJSTaggedValue();
        if (key.IsEmpty()) {
            return JSHandle<JSTaggedValue>();
        }
        JSHandle<JSTaggedValue> value = DeserializeJSTaggedValue();
        if (value.IsEmpty()) {
            return JSHandle<JSTaggedValue>();
        }
        JSMap::Set(thread_, jsMap, key, value);
    }
    return mapTag;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadJSSet()
{
    JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSFunction> setFunction(env->GetBuiltinsSetFunction());
    JSHandle<JSSet> jsSet = JSHandle<JSSet>::Cast(factory_->NewJSObjectByConstructor(setFunction));
    JSHandle<JSTaggedValue> setTag = JSHandle<JSTaggedValue>::Cast(jsSet);
    referenceMap_.emplace(objectId_++, setTag);
    if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(setTag)) {
        return JSHandle<JSTaggedValue>();
    }
    int32_t size;
    if (!JudgeType(SerializationUID::INT32) || !ReadInt(&size)) {
        return JSHandle<JSTaggedValue>();
    }
    JSHandle<LinkedHashSet> linkedSet = LinkedHashSet::Create(thread_);
    jsSet->SetLinkedSet(thread_, linkedSet);
    for (int32_t i = 0; i < size; i++) {
        JSHandle<JSTaggedValue> key = DeserializeJSTaggedValue();
        if (key.IsEmpty()) {
            return JSHandle<JSTaggedValue>();
        }
        JSSet::Add(thread_, jsSet, key);
    }
    return setTag;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadJSRegExp()
{
    JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSFunction> regexpFunction(env->GetRegExpFunction());
    JSHandle<JSObject> obj = factory_->NewJSObjectByConstructor(regexpFunction);
    JSHandle<JSRegExp> regExp = JSHandle<JSRegExp>::Cast(obj);
    JSHandle<JSTaggedValue> regexpTag = JSHandle<JSTaggedValue>::Cast(regExp);
    referenceMap_.emplace(objectId_++, regexpTag);
    if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(regexpTag)) {
        return JSHandle<JSTaggedValue>();
    }
    int32_t bufferSize;
    if (!JudgeType(SerializationUID::INT32) || !ReadInt(&bufferSize)) {
        return JSHandle<JSTaggedValue>();
    }
    void *buffer = GetBuffer(bufferSize);
    if (buffer == nullptr) {
        return JSHandle<JSTaggedValue>();
    }
    factory_->NewJSRegExpByteCodeData(regExp, buffer, bufferSize);
    JSHandle<JSTaggedValue> originalSource = DeserializeJSTaggedValue();
    regExp->SetOriginalSource(thread_, originalSource);
    JSHandle<JSTaggedValue> originalFlags = DeserializeJSTaggedValue();
    regExp->SetOriginalFlags(thread_, originalFlags);
    return regexpTag;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadJSTypedArray(SerializationUID uid)
{
    JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSTaggedValue> target;
    JSHandle<JSObject> obj;
    JSHandle<JSTaggedValue> objTag;
    switch (uid) {
        case SerializationUID::JS_INT8_ARRAY: {
            target = env->GetInt8ArrayFunction();
            break;
        }
        case SerializationUID::JS_UINT8_ARRAY: {
            target = env->GetUint8ArrayFunction();
            break;
        }
        case SerializationUID::JS_UINT8_CLAMPED_ARRAY: {
            target = env->GetUint8ClampedArrayFunction();
            break;
        }
        case SerializationUID::JS_INT16_ARRAY: {
            target = env->GetInt16ArrayFunction();
            break;
        }
        case SerializationUID::JS_UINT16_ARRAY: {
            target = env->GetUint16ArrayFunction();
            break;
        }
        case SerializationUID::JS_INT32_ARRAY: {
            target = env->GetInt32ArrayFunction();
            break;
        }
        case SerializationUID::JS_UINT32_ARRAY: {
            target = env->GetUint32ArrayFunction();
            break;
        }
        case SerializationUID::JS_FLOAT32_ARRAY: {
            target = env->GetFloat32ArrayFunction();
            break;
        }
        case SerializationUID::JS_FLOAT64_ARRAY: {
            target = env->GetFloat64ArrayFunction();
            break;
        }
        case SerializationUID::JS_BIGINT64_ARRAY: {
            target = env->GetBigInt64ArrayFunction();
            break;
        }
        case SerializationUID::JS_BIGUINT64_ARRAY: {
            target = env->GetBigUint64ArrayFunction();
            break;
        }
        default:
            LOG_ECMA(FATAL) << "this branch is unreachable";
            UNREACHABLE();
    }
    JSHandle<JSTypedArray> typedArray =
        JSHandle<JSTypedArray>::Cast(factory_->NewJSObjectByConstructor(JSHandle<JSFunction>(target)));
    obj = JSHandle<JSObject>::Cast(typedArray);
    objTag = JSHandle<JSTaggedValue>::Cast(obj);
    referenceMap_.emplace(objectId_++, objTag);
    if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(objTag)) {
        return JSHandle<JSTaggedValue>();
    }

    bool isViewedArrayBuffer = false;
    if (!ReadBoolean(&isViewedArrayBuffer)) {
        return JSHandle<JSTaggedValue>();
    }
    JSHandle<JSTaggedValue> viewedArrayBufferOrByteArray;
    if (isViewedArrayBuffer) {
        viewedArrayBufferOrByteArray = DeserializeJSTaggedValue();
    } else {
        if (!JudgeType(SerializationUID::BYTE_ARRAY)) {
            return JSHandle<JSTaggedValue>();
        }
        viewedArrayBufferOrByteArray = ReadByteArray();
    }
    if (viewedArrayBufferOrByteArray.IsEmpty()) {
        return JSHandle<JSTaggedValue>();
    }
    typedArray->SetViewedArrayBufferOrByteArray(thread_, viewedArrayBufferOrByteArray);

    JSHandle<JSTaggedValue> typedArrayName = DeserializeJSTaggedValue();
    if (typedArrayName.IsEmpty()) {
        return JSHandle<JSTaggedValue>();
    }
    typedArray->SetTypedArrayName(thread_, typedArrayName);

    JSTaggedValue byteLength;
    if (!ReadJSTaggedValue(&byteLength) || !byteLength.IsNumber()) {
        return JSHandle<JSTaggedValue>();
    }
    typedArray->SetByteLength(byteLength.GetNumber());

    JSTaggedValue byteOffset;
    if (!ReadJSTaggedValue(&byteOffset) || !byteOffset.IsNumber()) {
        return JSHandle<JSTaggedValue>();
    }
    typedArray->SetByteOffset(byteOffset.GetNumber());

    JSTaggedValue arrayLength;
    if (!ReadJSTaggedValue(&arrayLength) || !byteOffset.IsNumber()) {
        return JSHandle<JSTaggedValue>();
    }
    typedArray->SetArrayLength(arrayLength.GetNumber());

    ContentType *contentType = reinterpret_cast<ContentType*>(GetBuffer(sizeof(ContentType)));
    if (contentType == nullptr) {
        return JSHandle<JSTaggedValue>();
    }
    typedArray->SetContentType(*contentType);
    return objTag;
}

JSHandle<JSTaggedValue> JSDeserializer::ReadJSNativePointer()
{
    uintptr_t externalPtr;
    if (!ReadNativePointer(&externalPtr)) {
        return JSHandle<JSTaggedValue>();
    }
    uintptr_t deleter;
    if (!ReadNativePointer(&deleter)) {
        return JSHandle<JSTaggedValue>();
    }
    uintptr_t allocatorPtr;
    if (!ReadNativePointer(&allocatorPtr)) {
        return JSHandle<JSTaggedValue>();
    }
    int32_t bindingSize;
    if (!JudgeType(SerializationUID::INT32) || !ReadInt(&bindingSize)) {
        return JSHandle<JSTaggedValue>();
    }
    JSHandle<JSNativePointer> np = factory_->NewJSNativePointer(ToVoidPtr(externalPtr),
                                                                reinterpret_cast<DeleteEntryPoint>(deleter),
                                                                ToVoidPtr(allocatorPtr),
                                                                false,
                                                                bindingSize);
    return JSHandle<JSTaggedValue>::Cast(np);
}

JSHandle<JSTaggedValue> JSDeserializer::ReadJSArrayBuffer(SerializationUID uid)
{
    bool withNativeAreaAllocator;
    if (!ReadBoolean(&withNativeAreaAllocator)) {
        return JSHandle<JSTaggedValue>();
    }
    // read access length
    int32_t arrayLength;
    if (!JudgeType(SerializationUID::INT32) || !ReadInt(&arrayLength)) {
        return JSHandle<JSTaggedValue>();
    }
    // read access shared
    bool shared = (uid == SerializationUID::JS_SHARED_ARRAY_BUFFER);

    JSHandle<JSArrayBuffer> arrayBuffer;
    if (arrayLength == 0) {
        // create an empty arrayBuffer
        arrayBuffer = factory_->NewJSArrayBuffer(0);
        arrayBuffer->SetShared(shared);
    } else {
        if (shared) {
            uint64_t *bufferAddr = reinterpret_cast<uint64_t*>(GetBuffer(sizeof(uint64_t)));
            void *bufferData = ToVoidPtr(*bufferAddr);
            arrayBuffer = factory_->NewJSSharedArrayBuffer(bufferData, arrayLength);
        } else if (uid == SerializationUID::JS_TRANSFER_ARRAY_BUFFER) {
            JSHandle<JSTaggedValue> np = ReadJSNativePointer();
            if (np.IsEmpty()) {
                return JSHandle<JSTaggedValue>();
            }
            arrayBuffer = factory_->NewJSArrayBuffer(0);
            arrayBuffer->Attach(thread_, arrayLength, np.GetTaggedValue(), withNativeAreaAllocator);
        } else {
            void *fromBuffer = GetBuffer(arrayLength);
            if (fromBuffer == nullptr) {
                return JSHandle<JSTaggedValue>();
            }
            arrayBuffer = factory_->NewJSArrayBuffer(arrayLength);
            JSNativePointer* np = JSNativePointer::Cast(arrayBuffer->GetArrayBufferData().GetTaggedObject());
            void *toBuffer = np->GetExternalPointer();
            if (memcpy_s(toBuffer, arrayLength, fromBuffer, arrayLength) != EOK) {
                LOG_ECMA(FATAL) << "this branch is unreachable";
                UNREACHABLE();
            }
        }
    }

    arrayBuffer->SetWithNativeAreaAllocator(withNativeAreaAllocator);
    JSHandle<JSTaggedValue> arrayBufferTag = JSHandle<JSTaggedValue>::Cast(arrayBuffer);
    referenceMap_.emplace(objectId_++, arrayBufferTag);
    // read jsarraybuffer properties
    if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(arrayBufferTag)) {
        return JSHandle<JSTaggedValue>();
    }

    return arrayBufferTag;
}

bool JSDeserializer::ReadJSTaggedValue(JSTaggedValue *value)
{
    size_t len = sizeof(JSTaggedValue);
    if (len > static_cast<size_t>(end_ - position_)) {
        return false;
    }
    if (memcpy_s(value, len, position_, len) != EOK) {
        LOG_ECMA(FATAL) << "this branch is unreachable";
        UNREACHABLE();
    }
    position_ += len;
    return true;
}

bool JSDeserializer::ReadNativePointer(uintptr_t *value)
{
    size_t len = sizeof(uintptr_t);
    if (len > static_cast<size_t>(end_ - position_)) {
        return false;
    }
    if (memcpy_s(value, len, position_, len) != EOK) {
        LOG_ECMA(FATAL) << "this branch is unreachable";
        UNREACHABLE();
    }
    position_ += len;
    return true;
}

void *JSDeserializer::GetBuffer(uint32_t bufferSize)
{
    const uint8_t *buffer = nullptr;
    if (bufferSize > static_cast<size_t>(end_ - position_)) {
        return nullptr;
    }
    buffer = position_;
    position_ += bufferSize;
    uint8_t *retBuffer = const_cast<uint8_t *>(buffer);
    return static_cast<void *>(retBuffer);
}

JSHandle<JSTaggedValue> JSDeserializer::ReadReference()
{
    uint64_t objId;
    if (!ReadObjectId(&objId)) {
        return JSHandle<JSTaggedValue>();
    }
    auto objIter = referenceMap_.find(objId);
    if (objIter == referenceMap_.end()) {
        return JSHandle<JSTaggedValue>();
    }
    return objIter->second;
}

bool JSDeserializer::JudgeType(SerializationUID targetUid)
{
    if (ReadType() != targetUid) {
        return false;
    }
    return true;
}

bool JSDeserializer::DefinePropertiesAndElements(const JSHandle<JSTaggedValue> &obj)
{
    int32_t propertyLength;
    if (!JudgeType(SerializationUID::INT32) || !ReadInt(&propertyLength)) {
        return false;
    }
    for (int32_t i = 0; i < propertyLength; i++) {
        JSHandle<JSTaggedValue> key = DeserializeJSTaggedValue();
        if (key.IsEmpty()) {
            return false;
        }
        PropertyDescriptor desc(thread_);
        if (!ReadDesc(&desc)) {
            return false;
        }
        if (!JSTaggedValue::DefineOwnProperty(thread_, obj, key, desc)) {
            return false;
        }
    }

    int32_t elementLength;
    if (!JudgeType(SerializationUID::INT32) || !ReadInt(&elementLength)) {
        return false;
    }
    for (int32_t i = 0; i < elementLength; i++) {
        JSHandle<JSTaggedValue> key = DeserializeJSTaggedValue();
        if (key.IsEmpty()) {
            return false;
        }
        PropertyDescriptor desc(thread_);
        if (!ReadDesc(&desc)) {
            return false;
        }
        if (!JSTaggedValue::DefineOwnProperty(thread_, obj, key, desc)) {
            return false;
        }
    }
    return true;
}

bool JSDeserializer::ReadDesc(PropertyDescriptor *desc)
{
    bool isWritable = false;
    if (!ReadBoolean(&isWritable)) {
        return false;
    }
    bool isEnumerable = false;
    if (!ReadBoolean(&isEnumerable)) {
        return false;
    }
    bool isConfigurable = false;
    if (!ReadBoolean(&isConfigurable)) {
        return false;
    }
    bool hasWritable = false;
    if (!ReadBoolean(&hasWritable)) {
        return false;
    }
    bool hasEnumerable = false;
    if (!ReadBoolean(&hasEnumerable)) {
        return false;
    }
    bool hasConfigurable = false;
    if (!ReadBoolean(&hasConfigurable)) {
        return false;
    }
    JSHandle<JSTaggedValue> value = DeserializeJSTaggedValue();
    if (value.IsEmpty()) {
        return false;
    }
    desc->SetValue(value);
    if (hasWritable) {
        desc->SetWritable(isWritable);
    }
    if (hasEnumerable) {
        desc->SetEnumerable(isEnumerable);
    }
    if (hasConfigurable) {
        desc->SetConfigurable(isConfigurable);
    }
    return true;
}

bool JSDeserializer::ReadBoolean(bool *value)
{
    SerializationUID uid = ReadType();
    if (uid == SerializationUID::C_TRUE) {
        *value = true;
        return true;
    }
    if (uid == SerializationUID::C_FALSE) {
        *value = false;
        return true;
    }
    return false;
}

bool Serializer::WriteValue(
    JSThread *thread, const JSHandle<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &transfer)
{
    if (data_ != nullptr) {
        return false;
    }
    data_.reset(new SerializationData);
    if (!PrepareTransfer(thread, transfer)) {
        return false;
    }
    if (!valueSerializer_.SerializeJSTaggedValue(value)) {
        return false;
    }
    // clear transfer obj set after serialization
    valueSerializer_.ClearTransferSet();
    std::pair<uint8_t*, size_t> pair = valueSerializer_.ReleaseBuffer();
    data_->value_.reset(pair.first);
    data_->dataSize_ = pair.second;
    return true;
}

std::unique_ptr<SerializationData> Serializer::Release()
{
    return std::move(data_);
}

bool Serializer::PrepareTransfer(JSThread *thread, const JSHandle<JSTaggedValue> &transfer)
{
    if (transfer->IsUndefined()) {
        return true;
    }
    if (!transfer->IsJSArray()) {
        return false;
    }
    int len = base::ArrayHelper::GetArrayLength(thread, transfer);
    int k = 0;
    CUnorderedSet<uintptr_t> transferDataSet;
    while (k < len) {
        bool exists = JSTaggedValue::HasProperty(thread, transfer, k);
        if (exists) {
            JSHandle<JSTaggedValue> element = JSArray::FastGetPropertyByValue(thread, transfer, k);
            if (!element->IsArrayBuffer()) {
                return false;
            }
            transferDataSet.insert(static_cast<uintptr_t>(element.GetTaggedType()));
        }
        k++;
    }
    valueSerializer_.InitTransferSet(std::move(transferDataSet));
    return true;
}

JSHandle<JSTaggedValue> Deserializer::ReadValue()
{
    return valueDeserializer_.Deserialize();
}
}  // namespace panda::ecmascript