* 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 "ecmascript/module/module_snapshot.h"
#include "ecmascript/base/config.h"
#include "ecmascript/module/js_module_source_text.h"
#include "ecmascript/platform/file.h"
#include "ecmascript/platform/signal_manager.h"
#include "ecmascript/serializer/file_deserializer.h"
#include "ecmascript/serializer/file_serializer.h"
#include "ecmascript/snapshot/common/modules_snapshot_helper.h"
#include "securec.h"
#include "zlib.h"
namespace panda::ecmascript {
void ModuleSnapshot::SerializeDataAndPostSavingJob(const EcmaVM *vm, const CString &path, const CString &version)
{
LOG_ECMA(DEBUG) << "ModuleSnapshot::SerializeDataAndPostSavingJob " << path;
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "ModuleSnapshot::SerializeDataAndPostSavingJob", "");
CString filePath = base::ConcatToCString(path, MODULE_SNAPSHOT_FILE_NAME);
if (FileExist(filePath.c_str())) {
LOG_ECMA(INFO) << "Module serialize file already exist";
return;
}
ModulesSnapshotHelper::MarkModuleSnapshotLoaded();
JSThread *thread = vm->GetJSThread();
std::unique_ptr<SerializeData> fileData = GetSerializeData(thread);
if (fileData == nullptr) {
return;
}
auto header = ModulesSnapshotHelper::SnapshotVersionInfo::New(vm->GetApplicationVersionCode(), version, "");
common::Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<SnapshotSaveTask>(
thread->GetThreadId(), std::move(fileData), filePath, std::move(header), "ModuleSnapshot"));
}
bool ModuleSnapshot::DeserializeData(const EcmaVM *vm, const CString &path, const CString &version)
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "ModuleSnapshot::DeserializeData", "");
LOG_ECMA(DEBUG) << "ModuleSnapshot::DeserializeData";
CString filePath = base::ConcatToCString(path, MODULE_SNAPSHOT_FILE_NAME);
if (!FileExist(filePath.c_str())) {
LOG_ECMA(INFO) << "ModuleSnapshot::DeserializeData Module serialize file doesn't exist: " << path;
return false;
}
ModulesSnapshotHelper::MarkModuleSnapshotLoaded();
JSThread *thread = vm->GetJSThread();
std::unique_ptr<SerializeData> fileData = std::make_unique<SerializeData>(thread);
auto header = ModulesSnapshotHelper::SnapshotVersionInfo::New(vm->GetApplicationVersionCode(), version, "");
if (!ModulesSnapshotHelper::ReadDataFromFile(fileData, filePath, header, "ModuleSnapshot")) {
LOG_ECMA(ERROR) << "ModuleSnapshot::DeserializeData failed: " << filePath;
ModulesSnapshotHelper::RemoveFile(filePath);
return false;
}
FileDeserializer deserializer(thread, fileData.release());
JSHandle<TaggedArray> deserializedModules = JSHandle<TaggedArray>::Cast(deserializer.ReadValue());
uint32_t length = deserializedModules->GetLength();
for (uint32_t i = 0; i < length; i++) {
JSTaggedValue module = deserializedModules->Get(thread, i);
JSHandle<SourceTextModule> moduleHdl(thread, SourceTextModule::Cast(module.GetTaggedObject()));
CString moduleName = SourceTextModule::GetModuleName(module);
if (SourceTextModule::IsSharedModule(moduleHdl)) {
SharedModuleManager::GetInstance()->AddToResolvedModulesAndCreateSharedModuleMutex(
thread, moduleName, module);
continue;
}
thread->GetModuleManager()->AddResolveImportedModule(moduleName, module);
}
LOG_ECMA(INFO) << "ModuleSnapshot::DeserializeData success";
return true;
}
JSHandle<TaggedArray> ModuleSnapshot::GetModuleSerializeArray(JSThread *thread)
{
ModuleManager *moduleManager = thread->GetModuleManager();
uint32_t normalModuleSize = moduleManager->GetResolvedModulesSize();
uint32_t sharedModuleSize = SharedModuleManager::GetInstance()->GetResolvedSharedModulesSize();
EcmaVM *vm = thread->GetEcmaVM();
ObjectFactory *factory = vm->GetFactory();
JSHandle<TaggedArray> serializerArray = factory->NewTaggedArray(normalModuleSize + sharedModuleSize);
moduleManager->AddNormalSerializeModule(thread, serializerArray, 0);
SharedModuleManager::GetInstance()->AddSharedSerializeModule(thread, serializerArray, normalModuleSize);
return serializerArray;
}
void ModuleSnapshot::RestoreUpdatedBinding(JSThread* thread, JSHandle<TaggedArray> serializeArray)
{
auto globalConstants = thread->GlobalConstants();
JSMutableHandle<SourceTextModule> module(thread, globalConstants->GetUndefined());
JSMutableHandle<ResolvedIndexBinding> indexBinding(thread, globalConstants->GetUndefined());
JSMutableHandle<TaggedArray> environment(thread, globalConstants->GetUndefined());
for (uint32_t moduleIdx = 0; moduleIdx < serializeArray->GetLength(); ++moduleIdx) {
module.Update(serializeArray->Get(thread, moduleIdx));
JSTaggedValue moduleEnvironment = module->GetEnvironment(thread);
if (moduleEnvironment.IsUndefined()) {
continue;
}
environment.Update(moduleEnvironment);
bool isShared = SourceTextModule::IsSharedModule(module);
for (uint32_t bindingIdx = 0; bindingIdx < environment->GetLength(); bindingIdx++) {
JSTaggedValue binding = environment->Get(thread, bindingIdx);
if (binding.IsResolvedIndexBinding() &&
ResolvedIndexBinding::Cast(binding)->GetIsUpdatedFromResolvedBinding()) {
indexBinding.Update(binding);
JSHandle<JSTaggedValue> nameBinding =
SourceTextModule::CreateBindingByIndexBinding(thread, indexBinding, isShared);
environment->Set(thread, bindingIdx, nameBinding);
}
}
}
}
std::unique_ptr<SerializeData> ModuleSnapshot::GetSerializeData(JSThread *thread)
{
FileSerializer serializer(thread, FileSerializer::SourceTextModuleFilter);
JSHandle<TaggedArray> serializeArray = GetModuleSerializeArray(thread);
RestoreUpdatedBinding(thread, serializeArray);
const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
if (!serializer.WriteValue(thread, JSHandle<JSTaggedValue>(serializeArray),
globalConstants->GetHandledUndefined(),
globalConstants->GetHandledUndefined())) {
LOG_ECMA(ERROR) << "ModuleSnapshot::GetSerializeData serialize failed";
return nullptr;
}
std::unique_ptr<SerializeData> fileData = serializer.Release();
return fileData;
}
}