* Copyright (c) 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.
*/
#include "common_components/serialize/serialize_utils.h"
#include "ecmascript/module/js_module_source_text.h"
#include "ecmascript/serializer/base_serializer-inl.h"
namespace panda::ecmascript {
SerializedObjectSpace BaseSerializer::GetSerializedObjectSpace(TaggedObject *object) const
{
if (g_isEnableCMCGC) {
SerializedObjectSpace spaceType =
SerializedObjectSpace(static_cast<int>(common::SerializeUtils::GetSerializeObjectSpace(ToUintPtr(object))));
if (spaceType == SerializedObjectSpace::OTHER) {
LOG_ECMA(FATAL) << "unsupported space type";
}
return spaceType;
} else {
auto region = Region::ObjectAddressToRange(object);
auto flag = region->GetRegionSpaceFlag();
switch (flag) {
case RegionSpaceFlag::IN_OLD_SPACE:
case RegionSpaceFlag::IN_YOUNG_SPACE:
ASSERT(!G_USE_CMS_GC);
return SerializedObjectSpace::OLD_SPACE;
case RegionSpaceFlag::IN_APPSPAWN_SPACE:
if constexpr (G_USE_CMS_GC) {
return SerializedObjectSpace::SLOT_SPACE;
} else {
return SerializedObjectSpace::OLD_SPACE;
}
case RegionSpaceFlag::IN_SLOT_SPACE:
return SerializedObjectSpace::SLOT_SPACE;
case RegionSpaceFlag::IN_NON_MOVABLE_SPACE:
case RegionSpaceFlag::IN_READ_ONLY_SPACE:
return SerializedObjectSpace::NON_MOVABLE_SPACE;
case RegionSpaceFlag::IN_MACHINE_CODE_SPACE:
return SerializedObjectSpace::MACHINE_CODE_SPACE;
case RegionSpaceFlag::IN_HUGE_OBJECT_SPACE:
return SerializedObjectSpace::HUGE_SPACE;
case RegionSpaceFlag::IN_SHARED_APPSPAWN_SPACE:
case RegionSpaceFlag::IN_SHARED_OLD_SPACE:
case RegionSpaceFlag::IN_SHARED_READ_ONLY_SPACE:
return SerializedObjectSpace::SHARED_OLD_SPACE;
case RegionSpaceFlag::IN_SHARED_NON_MOVABLE:
return SerializedObjectSpace::SHARED_NON_MOVABLE_SPACE;
case RegionSpaceFlag::IN_SHARED_HUGE_OBJECT_SPACE:
return SerializedObjectSpace::SHARED_HUGE_SPACE;
default:
LOG_ECMA(FATAL) << "this branch is unreachable, the fault flag is: " << static_cast<uint32_t>(flag);
UNREACHABLE();
}
}
}
void BaseSerializer::WriteMultiRawData(uintptr_t beginAddr, size_t fieldSize)
{
if (fieldSize > 0) {
data_->WriteEncodeFlag(EncodeFlag::MULTI_RAW_DATA);
data_->WriteUint32(fieldSize);
data_->WriteRawData(reinterpret_cast<uint8_t *>(beginAddr), fieldSize);
}
}
void BaseSerializer::SerializeJSTaggedValue(JSTaggedValue value)
{
DISALLOW_GARBAGE_COLLECTION;
if (!value.IsHeapObject()) {
data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
data_->WriteJSTaggedValue(value);
} else {
TaggedObject *object = nullptr;
bool isWeak = value.IsWeak();
object = isWeak ? value.GetWeakReferent() : value.GetTaggedObject();
SerializeObjectImpl(object, isWeak);
}
}
bool BaseSerializer::SerializeReference(TaggedObject *object)
{
if (referenceMap_.find(object) != referenceMap_.end()) {
uint32_t objectIndex = referenceMap_.find(object)->second;
data_->WriteEncodeFlag(EncodeFlag::REFERENCE);
data_->WriteUint32(objectIndex);
return true;
}
return false;
}
bool BaseSerializer::SerializeRootObject(TaggedObject *object)
{
size_t index = vm_->GetSnapshotEnv()->FindEnvObjectIndex(ToUintPtr(object));
if (index != SnapshotEnv::MAX_UINT_32) {
data_->WriteEncodeFlag(EncodeFlag::ROOT_OBJECT);
data_->WriteUint32(index);
return true;
}
return false;
}
void BaseSerializer::SerializeSharedObject(TaggedObject *object)
{
data_->WriteEncodeFlag(EncodeFlag::SHARED_OBJECT);
data_->WriteUint32(sharedObjChunk_->Size());
referenceMap_.emplace(object, objectIndex_++);
sharedObjChunk_->Emplace(static_cast<JSTaggedType>(ToUintPtr(object)));
}
bool BaseSerializer::SerializeSpecialObjIndividually(JSType objectType, TaggedObject *root,
ObjectSlot start, ObjectSlot end)
{
switch (objectType) {
case JSType::HCLASS:
SerializeHClassFieldIndividually(root, start, end);
return true;
case JSType::LEXICAL_ENV:
SerializeLexicalEnvFieldIndividually(root, start, end);
return true;
case JSType::SFUNCTION_ENV:
SerializeSFunctionEnvFieldIndividually(root, start, end);
return true;
case JSType::SENDABLE_ENV:
SerializeSendableEnvFieldIndividually(root, start, end);
return true;
case JSType::JS_SHARED_FUNCTION:
case JSType::JS_SHARED_ASYNC_FUNCTION:
SerializeSFunctionFieldIndividually(root, start, end);
return true;
case JSType::JS_ASYNC_FUNCTION:
SerializeAsyncFunctionFieldIndividually(root, start, end);
return true;
case JSType::SOURCE_TEXT_MODULE_RECORD:
SerializeSourceTextModuleFieldIndividually(root, start, end);
return true;
case JSType::CONSTANT_POOL:
SerializeConstantPoolFieldIndividually(root, start, end);
return true;
default:
return false;
}
}
void BaseSerializer::SerializeHClassFieldIndividually(TaggedObject *root, ObjectSlot start, ObjectSlot end)
{
ASSERT(root->GetClass()->IsHClass());
ObjectSlot slot = start;
while (slot < end) {
size_t fieldOffset = slot.SlotAddress() - ToUintPtr(root);
switch (fieldOffset) {
case JSHClass::PROTOTYPE_OFFSET: {
JSHClass *kclass = reinterpret_cast<JSHClass *>(root);
JSTaggedValue proto = kclass->GetPrototype(thread_);
JSType type = kclass->GetObjectType();
if ((serializeSharedEvent_ > 0) &&
(type == JSType::JS_SHARED_OBJECT || type == JSType::JS_SHARED_FUNCTION)) {
SerializeJSTaggedValue(JSTaggedValue(Barriers::GetTaggedValue(thread_, slot.SlotAddress())));
} else {
SerializeObjectProto(kclass, proto);
}
slot++;
break;
}
case JSHClass::TRANSTIONS_OFFSET:
case JSHClass::PARENT_OFFSET:
case JSHClass::DEPENDENT_INFOS_OFFSET: {
data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
data_->WriteJSTaggedValue(JSTaggedValue::Undefined());
slot++;
break;
}
case JSHClass::PROTO_CHANGE_MARKER_OFFSET:
case JSHClass::PROTO_CHANGE_DETAILS_OFFSET:
case JSHClass::ENUM_CACHE_OFFSET: {
data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
data_->WriteJSTaggedValue(JSTaggedValue::Null());
slot++;
break;
}
default: {
SerializeJSTaggedValue(JSTaggedValue(Barriers::GetTaggedValue(thread_, slot.SlotAddress())));
slot++;
break;
}
}
}
}
void BaseSerializer::SerializeSFunctionFieldIndividually(TaggedObject *root, ObjectSlot start, ObjectSlot end)
{
ASSERT(root->GetClass()->GetObjectType() == JSType::JS_SHARED_FUNCTION ||
root->GetClass()->GetObjectType() == JSType::JS_SHARED_ASYNC_FUNCTION);
ObjectSlot slot = start;
while (slot < end) {
size_t fieldOffset = slot.SlotAddress() - ToUintPtr(root);
switch (fieldOffset) {
case JSFunction::MACHINECODE_OFFSET:
case JSFunction::BASELINECODE_OFFSET:
case JSFunction::RAW_PROFILE_TYPE_INFO_OFFSET: {
data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
data_->WriteJSTaggedValue(JSTaggedValue::Undefined());
slot++;
break;
}
case JSFunction::ECMA_MODULE_OFFSET: {
SerializeSFunctionModule(JSFunction::Cast(root));
slot++;
break;
}
#if !ENABLE_MEMORY_OPTIMIZATION
case JSFunction::WORK_NODE_POINTER_OFFSET: {
data_->WriteEncodeFlag(EncodeFlag::MULTI_RAW_DATA);
data_->WriteUint32(sizeof(uintptr_t));
data_->WriteRawData(reinterpret_cast<uint8_t *>(slot.SlotAddress()), sizeof(uintptr_t));
break;
}
#endif
default: {
SerializeJSTaggedValue(JSTaggedValue(Barriers::GetTaggedValue(thread_, slot.SlotAddress())));
slot++;
break;
}
}
}
}
void BaseSerializer::SerializeSFunctionModule(JSFunction *func)
{
JSTaggedValue moduleValue = func->GetModule(thread_);
if (moduleValue.IsHeapObject()) {
if (!moduleValue.IsInSharedHeap()) {
LOG_ECMA(ERROR) << "Shared function reference to local module";
}
if (!SerializeReference(moduleValue.GetTaggedObject())) {
SerializeSharedObject(moduleValue.GetTaggedObject());
}
} else {
data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
data_->WriteJSTaggedValue(moduleValue);
}
}
void BaseSerializer::SerializeLexicalEnvFieldIndividually(TaggedObject *root, ObjectSlot start, ObjectSlot end)
{
ASSERT(root->GetClass()->GetObjectType() == JSType::LEXICAL_ENV);
ObjectSlot slot = start;
while (slot < end) {
size_t fieldOffset = slot.SlotAddress() - ToUintPtr(root);
switch (fieldOffset) {
case LEXICALENV_GLOBAL_ENV_SLOT:
case LEXICALENV_PARENT_ENV_SLOT:
case LEXICALENV_SCOPE_INFO_SLOT: {
data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
data_->WriteJSTaggedValue(JSTaggedValue::Hole());
slot++;
break;
}
default: {
SerializeJSTaggedValue(JSTaggedValue(slot.GetTaggedType()));
slot++;
break;
}
}
}
}
void BaseSerializer::SerializeSFunctionEnvFieldIndividually(TaggedObject *root, ObjectSlot start, ObjectSlot end)
{
ASSERT(root->GetClass()->GetObjectType() == JSType::SFUNCTION_ENV);
ObjectSlot slot = start;
while (slot < end) {
size_t fieldOffset = slot.SlotAddress() - ToUintPtr(root);
switch (fieldOffset) {
case SFUNCTIONENV_GLOBAL_ENV_SLOT:
case SFUNCTIONENV_CONSTRUCTOR_SLOT: {
data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
data_->WriteJSTaggedValue(JSTaggedValue::Hole());
slot++;
break;
}
default: {
SerializeJSTaggedValue(JSTaggedValue(Barriers::GetTaggedValue(thread_, slot.SlotAddress())));
slot++;
break;
}
}
}
}
void BaseSerializer::SerializeSendableEnvFieldIndividually(TaggedObject *root, ObjectSlot start, ObjectSlot end)
{
ASSERT(root->GetClass()->GetObjectType() == JSType::SENDABLE_ENV);
ObjectSlot slot = start;
while (slot < end) {
size_t fieldOffset = slot.SlotAddress() - ToUintPtr(root);
switch (fieldOffset) {
case SENDABLEENV_PARENT_ENV_SLOT:
case SENDABLEENV_SCOPE_INFO_SLOT: {
data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
data_->WriteJSTaggedValue(JSTaggedValue::Hole());
slot++;
break;
}
default: {
SerializeJSTaggedValue(JSTaggedValue(Barriers::GetTaggedValue(thread_, slot.SlotAddress())));
slot++;
break;
}
}
}
}
void BaseSerializer::SerializeAsyncFunctionFieldIndividually(TaggedObject *root, ObjectSlot start, ObjectSlot end)
{
ASSERT(root->GetClass()->GetObjectType() == JSType::JS_ASYNC_FUNCTION);
ObjectSlot slot = start;
while (slot < end) {
size_t fieldOffset = slot.SlotAddress() - ToUintPtr(root);
switch (fieldOffset) {
case sizeof(TaggedObject): {
data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
data_->WriteJSTaggedValue(JSTaggedValue(0));
slot++;
break;
}
case JSFunction::PROTO_OR_DYNCLASS_OFFSET:
case JSFunction::MACHINECODE_OFFSET:
case JSFunction::BASELINECODE_OFFSET:
case JSFunction::RAW_PROFILE_TYPE_INFO_OFFSET:
case JSFunction::HOME_OBJECT_OFFSET:
case JSFunction::ECMA_MODULE_OFFSET: {
data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
data_->WriteJSTaggedValue(JSTaggedValue::Undefined());
slot++;
break;
}
case JSFunction::LEXICAL_ENV_OFFSET:
data_->WriteEncodeFlag(EncodeFlag::GLOBAL_ENV);
slot++;
break;
#if !ENABLE_MEMORY_OPTIMIZATION
case JSFunction::WORK_NODE_POINTER_OFFSET: {
data_->WriteEncodeFlag(EncodeFlag::MULTI_RAW_DATA);
data_->WriteUint32(sizeof(uintptr_t));
data_->WriteRawData(reinterpret_cast<uint8_t *>(slot.SlotAddress()), sizeof(uintptr_t));
slot++;
break;
}
#endif
default: {
SerializeJSTaggedValue(JSTaggedValue(Barriers::GetTaggedValue(thread_, slot.SlotAddress())));
slot++;
break;
}
}
}
}
void BaseSerializer::SerializeObjectProto(JSHClass *kclass, JSTaggedValue proto)
{
if (!proto.IsHeapObject()) {
data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
data_->WriteJSTaggedValue(proto);
} else if (!SerializeReference(proto.GetTaggedObject()) && !SerializeRootObject(proto.GetTaggedObject())) {
data_->WriteEncodeFlag(EncodeFlag::OBJECT_PROTO);
data_->WriteUint8(static_cast<uint8_t>(kclass->GetObjectType()));
}
}
void BaseSerializer::SerializeTaggedObjField(SerializeType serializeType, TaggedObject *root,
ObjectSlot start, ObjectSlot end)
{
JSType objectType = root->GetClass()->GetObjectType();
if (serializeType != SerializeType::VALUE_SERIALIZE
|| !SerializeSpecialObjIndividually(objectType, root, start, end)) {
for (ObjectSlot slot = start; slot < end; slot++) {
SerializeJSTaggedValue(JSTaggedValue(Barriers::GetTaggedValue(thread_, slot.SlotAddress())));
}
}
}
void BaseSerializer::SerializeInObjField(TaggedObject *object, ObjectSlot start, ObjectSlot end)
{
auto hclass = object->GetClass();
auto layout = LayoutInfo::Cast(hclass->GetLayout(thread_).GetTaggedObject());
size_t index = 0;
for (ObjectSlot slot = start; slot < end; slot++) {
auto attr = layout->GetAttr(thread_, index++);
if (attr.GetRepresentation() == Representation::DOUBLE || attr.GetRepresentation() == Representation::INT) {
auto fieldAddr = slot.SlotAddress();
data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
data_->WriteRawData(reinterpret_cast<uint8_t *>(fieldAddr), sizeof(JSTaggedType));
} else {
SerializeJSTaggedValue(JSTaggedValue(Barriers::GetTaggedValue(thread_, slot.SlotAddress())));
}
}
}
void BaseSerializer::SerializeSourceTextModuleFieldIndividually(TaggedObject *root, ObjectSlot start, ObjectSlot end)
{
ASSERT(root->GetClass()->GetObjectType() == JSType::SOURCE_TEXT_MODULE_RECORD);
ObjectSlot slot = start;
while (slot < end) {
size_t fieldOffset = slot.SlotAddress() - ToUintPtr(root);
switch (fieldOffset) {
case SourceTextModule::NAMESPACE_OFFSET:
case SourceTextModule::NAME_DICTIONARY_OFFSET:
case SourceTextModule::CYCLE_ROOT_OFFSET:
case SourceTextModule::TOP_LEVEL_CAPABILITY_OFFSET:
case SourceTextModule::ASYNC_PARENT_MODULES_OFFSET:
case SourceTextModule::SENDABLE_ENV_OFFSET:
case SourceTextModule::EXCEPTION_OFFSET: {
data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
data_->WriteJSTaggedValue(JSTaggedValue::Undefined());
slot++;
break;
}
default: {
SerializeJSTaggedValue(JSTaggedValue(Barriers::GetTaggedValue(thread_, slot.SlotAddress())));
slot++;
break;
}
}
}
}
void BaseSerializer::SerializeConstantPoolFieldIndividually(TaggedObject* root, ObjectSlot start, ObjectSlot end)
{
ASSERT(root->GetClass()->GetObjectType() == JSType::CONSTANT_POOL);
constexpr const auto SHARED_CONSTPOOL_ID_OFFSET =
ConstantPool::SHARED_CONSTPOOL_ID - ConstantPool::RESERVED_POOL_LENGTH;
uint64_t seqHole = 0;
auto writeHoles = [&seqHole, this]() {
if (seqHole == 0) {
return;
}
ASSERT(seqHole <= UINT16_MAX);
data_->WriteEncodeFlag(EncodeFlag::SEQUENCE_HOLE);
data_->WriteUint32(static_cast<uint32_t>(seqHole));
seqHole = 0;
};
for (auto slot = start; slot < end; slot++) {
if (!slot.GetTaggedValue().IsHeapObject()) {
const auto toEnd = (end.SlotAddress() - slot.SlotAddress()) / JSTaggedValue::TaggedTypeSize();
if (toEnd == SHARED_CONSTPOOL_ID_OFFSET) {
writeHoles();
SerializeJSTaggedValue(JSTaggedValue(Barriers::GetTaggedValue(thread_, slot.SlotAddress())));
continue;
}
seqHole++;
continue;
}
auto val = slot.GetTaggedObject();
switch (val->GetClass()->GetObjectType()) {
case JSType::LINE_STRING:
writeHoles();
SerializeJSTaggedValue(JSTaggedValue(Barriers::GetTaggedValue(thread_, slot.SlotAddress())));
break;
default:
seqHole++;
break;
}
}
writeHoles();
}
}