14514f5e3Sopenharmony_ci/*
24514f5e3Sopenharmony_ci * Copyright (c) 2022 Huawei Device Co., Ltd.
34514f5e3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
44514f5e3Sopenharmony_ci * you may not use this file except in compliance with the License.
54514f5e3Sopenharmony_ci * You may obtain a copy of the License at
64514f5e3Sopenharmony_ci *
74514f5e3Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
84514f5e3Sopenharmony_ci *
94514f5e3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
104514f5e3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
114514f5e3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124514f5e3Sopenharmony_ci * See the License for the specific language governing permissions and
134514f5e3Sopenharmony_ci * limitations under the License.
144514f5e3Sopenharmony_ci */
154514f5e3Sopenharmony_ci
164514f5e3Sopenharmony_ci#include "ecmascript/snapshot/mem/snapshot.h"
174514f5e3Sopenharmony_ci
184514f5e3Sopenharmony_ci#include <cerrno>
194514f5e3Sopenharmony_ci
204514f5e3Sopenharmony_ci#include "ecmascript/compiler/pgo_type/pgo_type_manager.h"
214514f5e3Sopenharmony_ci
224514f5e3Sopenharmony_cinamespace panda::ecmascript {
234514f5e3Sopenharmony_civoid Snapshot::Serialize(const CString &fileName)
244514f5e3Sopenharmony_ci{
254514f5e3Sopenharmony_ci    kungfu::AOTSnapshot &aotSnapshot = vm_->GetJSThread()->GetCurrentEcmaContext()->GetPTManager()->GetAOTSnapshot();
264514f5e3Sopenharmony_ci    JSTaggedValue root = aotSnapshot.GetSnapshotData();
274514f5e3Sopenharmony_ci    if (root == JSTaggedValue::Hole()) {
284514f5e3Sopenharmony_ci        // root equals hole means no data stored.
294514f5e3Sopenharmony_ci        LOG_COMPILER(ERROR) << "error: no data for ai file generation!";
304514f5e3Sopenharmony_ci        return;
314514f5e3Sopenharmony_ci    }
324514f5e3Sopenharmony_ci    Serialize(root.GetTaggedObject(), nullptr, fileName);
334514f5e3Sopenharmony_ci}
344514f5e3Sopenharmony_ci
354514f5e3Sopenharmony_civoid Snapshot::Serialize(TaggedObject *objectHeader, const JSPandaFile *jsPandaFile, const CString &fileName)
364514f5e3Sopenharmony_ci{
374514f5e3Sopenharmony_ci    std::string realPath;
384514f5e3Sopenharmony_ci    if (!RealPath(std::string(fileName), realPath, false)) {
394514f5e3Sopenharmony_ci        LOG_FULL(FATAL) << "snapshot file path error";
404514f5e3Sopenharmony_ci    }
414514f5e3Sopenharmony_ci    std::fstream writer(realPath.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
424514f5e3Sopenharmony_ci    if (!writer.good()) {
434514f5e3Sopenharmony_ci        writer.close();
444514f5e3Sopenharmony_ci        LOG_FULL(ERROR) << "snapshot open file failed";
454514f5e3Sopenharmony_ci        return;
464514f5e3Sopenharmony_ci    }
474514f5e3Sopenharmony_ci
484514f5e3Sopenharmony_ci    SnapshotProcessor processor(vm_);
494514f5e3Sopenharmony_ci    processor.Initialize();
504514f5e3Sopenharmony_ci
514514f5e3Sopenharmony_ci    std::unordered_map<uint64_t, std::pair<uint64_t, EncodeBit>> data;
524514f5e3Sopenharmony_ci    CQueue<TaggedObject *> objectQueue;
534514f5e3Sopenharmony_ci
544514f5e3Sopenharmony_ci    if (objectHeader->GetClass()->GetObjectType() == JSType::PROGRAM) {
554514f5e3Sopenharmony_ci        processor.SetProgramSerializeStart();
564514f5e3Sopenharmony_ci    }
574514f5e3Sopenharmony_ci
584514f5e3Sopenharmony_ci    processor.EncodeTaggedObject(objectHeader, &objectQueue, &data);
594514f5e3Sopenharmony_ci    size_t rootObjSize = objectQueue.size();
604514f5e3Sopenharmony_ci    processor.ProcessObjectQueue(&objectQueue, &data);
614514f5e3Sopenharmony_ci    WriteToFile(writer, jsPandaFile, rootObjSize, processor);
624514f5e3Sopenharmony_ci}
634514f5e3Sopenharmony_ci
644514f5e3Sopenharmony_civoid Snapshot::Serialize(uintptr_t startAddr, size_t size, const CString &fileName)
654514f5e3Sopenharmony_ci{
664514f5e3Sopenharmony_ci    std::string realPath;
674514f5e3Sopenharmony_ci    if (!RealPath(std::string(fileName), realPath, false)) {
684514f5e3Sopenharmony_ci        LOG_FULL(FATAL) << "snapshot file path error";
694514f5e3Sopenharmony_ci    }
704514f5e3Sopenharmony_ci    std::fstream writer(realPath.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
714514f5e3Sopenharmony_ci    if (!writer.good()) {
724514f5e3Sopenharmony_ci        writer.close();
734514f5e3Sopenharmony_ci        LOG_FULL(ERROR) << "snapshot open file failed";
744514f5e3Sopenharmony_ci        return;
754514f5e3Sopenharmony_ci    }
764514f5e3Sopenharmony_ci
774514f5e3Sopenharmony_ci    SnapshotProcessor processor(vm_);
784514f5e3Sopenharmony_ci    processor.Initialize();
794514f5e3Sopenharmony_ci
804514f5e3Sopenharmony_ci    std::unordered_map<uint64_t, std::pair<uint64_t, EncodeBit>> data;
814514f5e3Sopenharmony_ci    CQueue<TaggedObject *> objectQueue;
824514f5e3Sopenharmony_ci
834514f5e3Sopenharmony_ci    ObjectSlot start(startAddr);
844514f5e3Sopenharmony_ci    ObjectSlot end(startAddr + size * sizeof(JSTaggedType));
854514f5e3Sopenharmony_ci    processor.EncodeTaggedObjectRange(start, end, &objectQueue, &data);
864514f5e3Sopenharmony_ci
874514f5e3Sopenharmony_ci    size_t rootObjSize = objectQueue.size();
884514f5e3Sopenharmony_ci    processor.ProcessObjectQueue(&objectQueue, &data);
894514f5e3Sopenharmony_ci    WriteToFile(writer, nullptr, rootObjSize, processor);
904514f5e3Sopenharmony_ci}
914514f5e3Sopenharmony_ci
924514f5e3Sopenharmony_civoid Snapshot::SerializeBuiltins(const CString &fileName)
934514f5e3Sopenharmony_ci{
944514f5e3Sopenharmony_ci    std::string realPath;
954514f5e3Sopenharmony_ci    if (!RealPath(std::string(fileName), realPath, false)) {
964514f5e3Sopenharmony_ci        LOG_FULL(FATAL) << "snapshot file path error";
974514f5e3Sopenharmony_ci    }
984514f5e3Sopenharmony_ci    std::fstream write(realPath.c_str(), std::ios::out | std::ios::binary | std::ios::app);
994514f5e3Sopenharmony_ci    if (!write.good()) {
1004514f5e3Sopenharmony_ci        write.close();
1014514f5e3Sopenharmony_ci        LOG_FULL(ERROR) << "snapshot open file failed";
1024514f5e3Sopenharmony_ci        return;
1034514f5e3Sopenharmony_ci    }
1044514f5e3Sopenharmony_ci    // if builtins.snapshot file has exist, return directly
1054514f5e3Sopenharmony_ci    if (write.tellg()) {
1064514f5e3Sopenharmony_ci        LOG_FULL(DEBUG) << "snapshot already exist";
1074514f5e3Sopenharmony_ci        write.close();
1084514f5e3Sopenharmony_ci        return;
1094514f5e3Sopenharmony_ci    }
1104514f5e3Sopenharmony_ci
1114514f5e3Sopenharmony_ci    SnapshotProcessor processor(vm_);
1124514f5e3Sopenharmony_ci    processor.Initialize();
1134514f5e3Sopenharmony_ci    processor.SetBuiltinsSerializeStart();
1144514f5e3Sopenharmony_ci
1154514f5e3Sopenharmony_ci    std::unordered_map<uint64_t, std::pair<uint64_t, EncodeBit>> data;
1164514f5e3Sopenharmony_ci    CQueue<TaggedObject *> objectQueue;
1174514f5e3Sopenharmony_ci
1184514f5e3Sopenharmony_ci    auto globalEnvHandle = vm_->GetGlobalEnv();
1194514f5e3Sopenharmony_ci    auto constant = const_cast<GlobalEnvConstants *>(vm_->GetJSThread()->GlobalConstants());
1204514f5e3Sopenharmony_ci    constant->VisitRangeSlot([&objectQueue, &data, &processor]([[maybe_unused]] Root type,
1214514f5e3Sopenharmony_ci                                                               ObjectSlot start, ObjectSlot end) {
1224514f5e3Sopenharmony_ci        processor.EncodeTaggedObjectRange(start, end, &objectQueue, &data);
1234514f5e3Sopenharmony_ci    });
1244514f5e3Sopenharmony_ci    processor.EncodeTaggedObject(*globalEnvHandle, &objectQueue, &data);
1254514f5e3Sopenharmony_ci    size_t rootObjSize = objectQueue.size();
1264514f5e3Sopenharmony_ci    processor.ProcessObjectQueue(&objectQueue, &data);
1274514f5e3Sopenharmony_ci    WriteToFile(write, nullptr, rootObjSize, processor);
1284514f5e3Sopenharmony_ci}
1294514f5e3Sopenharmony_ci
1304514f5e3Sopenharmony_cibool Snapshot::DeserializeInternal(SnapshotType type, const CString &snapshotFile, SnapshotProcessor &processor,
1314514f5e3Sopenharmony_ci                                   MemMap &fileMap)
1324514f5e3Sopenharmony_ci{
1334514f5e3Sopenharmony_ci    if (fileMap.GetOriginAddr() == nullptr) {
1344514f5e3Sopenharmony_ci        LOG_FULL(FATAL) << "file mmap failed";
1354514f5e3Sopenharmony_ci        UNREACHABLE();
1364514f5e3Sopenharmony_ci    }
1374514f5e3Sopenharmony_ci    auto readFile = ToUintPtr(fileMap.GetOriginAddr());
1384514f5e3Sopenharmony_ci    auto hdr = *ToNativePtr<const SnapShotHeader>(readFile);
1394514f5e3Sopenharmony_ci    if (!hdr.Verify(GetLastVersion())) {
1404514f5e3Sopenharmony_ci        FileUnMap(fileMap);
1414514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "file verify failed.";
1424514f5e3Sopenharmony_ci        return false;
1434514f5e3Sopenharmony_ci    }
1444514f5e3Sopenharmony_ci    uintptr_t oldSpaceBegin = readFile + sizeof(SnapShotHeader);
1454514f5e3Sopenharmony_ci    uintptr_t stringBegin = oldSpaceBegin + hdr.oldSpaceObjSize + hdr.nonMovableObjSize +
1464514f5e3Sopenharmony_ci        hdr.machineCodeObjSize + hdr.snapshotObjSize + hdr.hugeObjSize;
1474514f5e3Sopenharmony_ci    uintptr_t stringEnd = stringBegin + hdr.stringSize;
1484514f5e3Sopenharmony_ci    [[maybe_unused]] EcmaHandleScope stringHandleScope(vm_->GetJSThread());
1494514f5e3Sopenharmony_ci    processor.DeserializeString(stringBegin, stringEnd);
1504514f5e3Sopenharmony_ci
1514514f5e3Sopenharmony_ci    processor.DeserializeObjectExcludeString(oldSpaceBegin, hdr.oldSpaceObjSize, hdr.nonMovableObjSize,
1524514f5e3Sopenharmony_ci                                             hdr.machineCodeObjSize, hdr.snapshotObjSize, hdr.hugeObjSize);
1534514f5e3Sopenharmony_ci
1544514f5e3Sopenharmony_ci#if !defined(CROSS_PLATFORM)
1554514f5e3Sopenharmony_ci    FileUnMap(MemMap(fileMap.GetOriginAddr(), hdr.pandaFileBegin));
1564514f5e3Sopenharmony_ci#endif
1574514f5e3Sopenharmony_ci    std::shared_ptr<JSPandaFile> jsPandaFile;
1584514f5e3Sopenharmony_ci    if (static_cast<uint32_t>(fileMap.GetSize()) > hdr.pandaFileBegin) {
1594514f5e3Sopenharmony_ci        uintptr_t pandaFileMem = readFile + hdr.pandaFileBegin;
1604514f5e3Sopenharmony_ci        auto pf = panda_file::File::OpenFromMemory(os::mem::ConstBytePtr(ToNativePtr<std::byte>(pandaFileMem),
1614514f5e3Sopenharmony_ci            static_cast<uint32_t>(fileMap.GetSize()) - hdr.pandaFileBegin, os::mem::MmapDeleter));
1624514f5e3Sopenharmony_ci        jsPandaFile = JSPandaFileManager::GetInstance()->NewJSPandaFile(pf.release(), "");
1634514f5e3Sopenharmony_ci    }
1644514f5e3Sopenharmony_ci    // relocate object field
1654514f5e3Sopenharmony_ci    processor.Relocate(type, jsPandaFile.get(), hdr.rootObjectSize);
1664514f5e3Sopenharmony_ci    processor.AddRootObjectToAOTFileManager(type, snapshotFile);
1674514f5e3Sopenharmony_ci    LOG_COMPILER(INFO) << "loaded ai file: " << snapshotFile.c_str();
1684514f5e3Sopenharmony_ci    return true;
1694514f5e3Sopenharmony_ci}
1704514f5e3Sopenharmony_ci
1714514f5e3Sopenharmony_cibool Snapshot::Deserialize(SnapshotType type, const CString &snapshotFile, bool isBuiltins)
1724514f5e3Sopenharmony_ci{
1734514f5e3Sopenharmony_ci    std::string realPath;
1744514f5e3Sopenharmony_ci    if (!RealPath(std::string(snapshotFile), realPath, false)) {
1754514f5e3Sopenharmony_ci        LOG_FULL(FATAL) << "snapshot file path error";
1764514f5e3Sopenharmony_ci        UNREACHABLE();
1774514f5e3Sopenharmony_ci    }
1784514f5e3Sopenharmony_ci
1794514f5e3Sopenharmony_ci    std::ifstream file(realPath);
1804514f5e3Sopenharmony_ci    if (!file.good()) {
1814514f5e3Sopenharmony_ci        return false;
1824514f5e3Sopenharmony_ci    }
1834514f5e3Sopenharmony_ci    file.close();
1844514f5e3Sopenharmony_ci
1854514f5e3Sopenharmony_ci    SnapshotProcessor processor(vm_);
1864514f5e3Sopenharmony_ci    if (isBuiltins) {
1874514f5e3Sopenharmony_ci        processor.SetBuiltinsDeserializeStart();
1884514f5e3Sopenharmony_ci    }
1894514f5e3Sopenharmony_ci
1904514f5e3Sopenharmony_ci    MemMap fileMap = FileMap(realPath.c_str(), FILE_RDONLY, PAGE_PROT_READWRITE);
1914514f5e3Sopenharmony_ci    return DeserializeInternal(type, snapshotFile, processor, fileMap);
1924514f5e3Sopenharmony_ci}
1934514f5e3Sopenharmony_ci
1944514f5e3Sopenharmony_ci#if defined(CROSS_PLATFORM) && defined(ANDROID_PLATFORM)
1954514f5e3Sopenharmony_cibool Snapshot::Deserialize(SnapshotType type, const CString &snapshotFile, [[maybe_unused]] std::function<bool
1964514f5e3Sopenharmony_ci    (std::string fileName, uint8_t **buff, size_t *buffSize)> ReadAOTCallBack, bool isBuiltins)
1974514f5e3Sopenharmony_ci{
1984514f5e3Sopenharmony_ci    SnapshotProcessor processor(vm_);
1994514f5e3Sopenharmony_ci    if (isBuiltins) {
2004514f5e3Sopenharmony_ci        processor.SetBuiltinsDeserializeStart();
2014514f5e3Sopenharmony_ci    }
2024514f5e3Sopenharmony_ci
2034514f5e3Sopenharmony_ci    std::string fileName = std::string(snapshotFile);
2044514f5e3Sopenharmony_ci    uint8_t *buff = nullptr;
2054514f5e3Sopenharmony_ci    size_t buffSize = 0;
2064514f5e3Sopenharmony_ci    MemMap fileMap = {};
2074514f5e3Sopenharmony_ci    size_t found = fileName.find_last_of("/");
2084514f5e3Sopenharmony_ci    if (found != std::string::npos) {
2094514f5e3Sopenharmony_ci        fileName = fileName.substr(found + 1);
2104514f5e3Sopenharmony_ci    }
2114514f5e3Sopenharmony_ci
2124514f5e3Sopenharmony_ci    LOG_ECMA(INFO) << "Call JsAotReader to load: " << fileName;
2134514f5e3Sopenharmony_ci    if (ReadAOTCallBack(fileName, &buff, &buffSize)) {
2144514f5e3Sopenharmony_ci        fileMap = MemMap(buff, buffSize);
2154514f5e3Sopenharmony_ci    }
2164514f5e3Sopenharmony_ci
2174514f5e3Sopenharmony_ci    return DeserializeInternal(type, snapshotFile, processor, fileMap);
2184514f5e3Sopenharmony_ci}
2194514f5e3Sopenharmony_ci#endif
2204514f5e3Sopenharmony_ci
2214514f5e3Sopenharmony_cisize_t Snapshot::AlignUpPageSize(size_t spaceSize)
2224514f5e3Sopenharmony_ci{
2234514f5e3Sopenharmony_ci    if (spaceSize % Constants::PAGE_SIZE_ALIGN_UP == 0) {
2244514f5e3Sopenharmony_ci        return spaceSize;
2254514f5e3Sopenharmony_ci    }
2264514f5e3Sopenharmony_ci    return Constants::PAGE_SIZE_ALIGN_UP * (spaceSize / Constants::PAGE_SIZE_ALIGN_UP + 1);
2274514f5e3Sopenharmony_ci}
2284514f5e3Sopenharmony_ci
2294514f5e3Sopenharmony_civoid Snapshot::WriteToFile(std::fstream &writer, const JSPandaFile *jsPandaFile,
2304514f5e3Sopenharmony_ci                           size_t size, SnapshotProcessor &processor)
2314514f5e3Sopenharmony_ci{
2324514f5e3Sopenharmony_ci    uint32_t totalStringSize = 0U;
2334514f5e3Sopenharmony_ci    CVector<uintptr_t> stringVector = processor.GetStringVector();
2344514f5e3Sopenharmony_ci    for (size_t i = 0; i < stringVector.size(); ++i) {
2354514f5e3Sopenharmony_ci        auto str = reinterpret_cast<EcmaString *>(stringVector[i]);
2364514f5e3Sopenharmony_ci        size_t objectSize = AlignUp(EcmaStringAccessor(str).ObjectSize(),
2374514f5e3Sopenharmony_ci            static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
2384514f5e3Sopenharmony_ci        totalStringSize += objectSize;
2394514f5e3Sopenharmony_ci    }
2404514f5e3Sopenharmony_ci
2414514f5e3Sopenharmony_ci    std::vector<uint32_t> objSizeVector = processor.StatisticsObjectSize();
2424514f5e3Sopenharmony_ci    size_t totalObjSize = totalStringSize;
2434514f5e3Sopenharmony_ci    for (uint32_t objSize : objSizeVector) {
2444514f5e3Sopenharmony_ci        totalObjSize += objSize;
2454514f5e3Sopenharmony_ci    }
2464514f5e3Sopenharmony_ci    uint32_t pandaFileBegin = RoundUp(totalObjSize + sizeof(SnapShotHeader), Constants::PAGE_SIZE_ALIGN_UP);
2474514f5e3Sopenharmony_ci    SnapShotHeader hdr(GetLastVersion());
2484514f5e3Sopenharmony_ci    hdr.oldSpaceObjSize = objSizeVector[0]; // 0: oldSpaceObj
2494514f5e3Sopenharmony_ci    hdr.nonMovableObjSize = objSizeVector[1]; // 1: nonMovableObj
2504514f5e3Sopenharmony_ci    hdr.machineCodeObjSize = objSizeVector[2]; // 2: machineCodeObj
2514514f5e3Sopenharmony_ci    hdr.snapshotObjSize = objSizeVector[3]; // 3: snapshotObj
2524514f5e3Sopenharmony_ci    hdr.hugeObjSize = objSizeVector[4]; // 4: hugeObj
2534514f5e3Sopenharmony_ci    hdr.stringSize = totalStringSize;
2544514f5e3Sopenharmony_ci    hdr.pandaFileBegin = pandaFileBegin;
2554514f5e3Sopenharmony_ci    hdr.rootObjectSize = static_cast<uint32_t>(size);
2564514f5e3Sopenharmony_ci    writer.write(reinterpret_cast<char *>(&hdr), sizeof(hdr));
2574514f5e3Sopenharmony_ci    processor.WriteObjectToFile(writer);
2584514f5e3Sopenharmony_ci
2594514f5e3Sopenharmony_ci    for (size_t i = 0; i < stringVector.size(); ++i) {
2604514f5e3Sopenharmony_ci        auto str = reinterpret_cast<EcmaString *>(stringVector[i]);
2614514f5e3Sopenharmony_ci        size_t strSize = AlignUp(EcmaStringAccessor(str).ObjectSize(),
2624514f5e3Sopenharmony_ci            static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
2634514f5e3Sopenharmony_ci        int index = 0; // 0 represents the line string. Natural number 1 represents the constant string.
2644514f5e3Sopenharmony_ci        if (EcmaStringAccessor(str).IsConstantString()) {
2654514f5e3Sopenharmony_ci            index = 1;
2664514f5e3Sopenharmony_ci        }
2674514f5e3Sopenharmony_ci        // Write the index in the head of string.
2684514f5e3Sopenharmony_ci        uint8_t headerSize = JSTaggedValue::TaggedTypeSize();
2694514f5e3Sopenharmony_ci        JSTaggedType indexHeader = JSTaggedValue(index).GetRawData();
2704514f5e3Sopenharmony_ci        writer.write(reinterpret_cast<char *>(&indexHeader), headerSize);
2714514f5e3Sopenharmony_ci        writer.write(reinterpret_cast<char *>(str) + headerSize, strSize - headerSize);
2724514f5e3Sopenharmony_ci        writer.flush();
2734514f5e3Sopenharmony_ci    }
2744514f5e3Sopenharmony_ci
2754514f5e3Sopenharmony_ci    ASSERT(static_cast<size_t>(writer.tellp()) == totalObjSize + sizeof(SnapShotHeader));
2764514f5e3Sopenharmony_ci    if (jsPandaFile) {
2774514f5e3Sopenharmony_ci        writer.seekp(pandaFileBegin);
2784514f5e3Sopenharmony_ci        writer.write(static_cast<const char *>(jsPandaFile->GetHeader()), jsPandaFile->GetFileSize());
2794514f5e3Sopenharmony_ci    }
2804514f5e3Sopenharmony_ci    writer.close();
2814514f5e3Sopenharmony_ci}
2824514f5e3Sopenharmony_ci}  // namespace panda::ecmascript
283