1/*
2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef ECMASCRIPT_PATCH_PATCH_LOADER_H
17#define ECMASCRIPT_PATCH_PATCH_LOADER_H
18
19#include "ecmascript/jspandafile/js_pandafile.h"
20#include "ecmascript/js_tagged_value.h"
21#include "ecmascript/method.h"
22#include "ecmascript/mem/c_containers.h"
23#include "ecmascript/napi/include/jsnapi.h"
24
25namespace panda::ecmascript {
26using PatchErrorCode = panda::JSNApi::PatchErrorCode;
27using JSRecordInfo = JSPandaFile::JSRecordInfo;
28using LiteralDataAccessor = panda_file::LiteralDataAccessor;
29using LiteralValue = panda_file::LiteralDataAccessor::LiteralValue;
30using LiteralTag = panda_file::LiteralTag;
31class ConstantPool;
32class JSThread;
33
34struct BaseMethodIndex {
35    uint32_t constpoolNum {UINT32_MAX};
36    uint32_t constpoolIndex {UINT32_MAX};
37    uint32_t literalIndex {UINT32_MAX};
38    struct Hash {
39        std::size_t operator()(const BaseMethodIndex &baseMethodIndex) const
40        {
41            return std::hash<uint32_t>{}(baseMethodIndex.constpoolNum) ^ (std::hash<uint32_t>{}(
42                baseMethodIndex.constpoolIndex) << 1) ^ std::hash<uint32_t>{}(baseMethodIndex.literalIndex);
43        }
44    };
45
46    bool operator==(const BaseMethodIndex &baseMethodIndex) const
47    {
48        return constpoolNum == baseMethodIndex.constpoolNum && constpoolIndex == baseMethodIndex.constpoolIndex &&
49            literalIndex == baseMethodIndex.literalIndex;
50    }
51};
52
53struct PatchMethodIndex {
54    CString recordName;
55    CString className;
56    CString methodName;
57    struct Hash {
58        std::size_t operator()(const PatchMethodIndex &patchMethodIndex) const
59        {
60            return std::hash<CString>{}(patchMethodIndex.recordName) ^
61                std::hash<CString>{}(patchMethodIndex.className) ^ std::hash<CString>{}(patchMethodIndex.methodName);
62        }
63    };
64
65    bool operator==(const PatchMethodIndex &patchMethodIndex) const
66    {
67        return recordName == patchMethodIndex.recordName && className == patchMethodIndex.className &&
68            methodName == patchMethodIndex.methodName;
69    }
70};
71
72struct ReplacedMethod {
73    EntityId methodId;
74    CString fileName;
75    struct Hash {
76        std::size_t operator()(const ReplacedMethod &replacedMethod) const
77        {
78            return std::hash<EntityId>{}(replacedMethod.methodId) ^ std::hash<CString>{}(replacedMethod.fileName);
79        }
80    };
81
82    bool operator==(const ReplacedMethod &replacedMethod) const
83    {
84        return methodId == replacedMethod.methodId && fileName == replacedMethod.fileName;
85    }
86};
87
88
89struct PatchInfo {
90    // patch file name.
91    CString patchFileName;
92    // patch methodLiterals for load patch, <recordName, <methodName, MethodLiteral>>
93    CUnorderedMap<PatchMethodIndex, MethodLiteral*, PatchMethodIndex::Hash> patchMethodLiterals;
94    // base method info for unload patch, <BaseMethodIndex, base MethodLiteral>
95    CUnorderedMap<BaseMethodIndex, MethodLiteral *, BaseMethodIndex::Hash> baseMethodInfo;
96    // save base constpool in global for avoid gc.
97    CVector<JSHandle<JSTaggedValue>> baseConstpools;
98    // patch replaced recordNames.
99    CUnorderedSet<CString> replacedRecordNames;
100    // patch replaced methods.
101    CUnorderedMap<ReplacedMethod, CString, ReplacedMethod::Hash> replacedPatchMethods;
102};
103
104enum class StageOfHotReload : int32_t {
105    BEGIN_EXECUTE_PATCHMAIN = -1, // -1: For intercepting Evaluate()
106    INITIALIZE_STAGE_OF_HOTRELOAD, // 0 : initialize stageOfHotreload_ in ecma_context.h
107    LOAD_END_EXECUTE_PATCHMAIN, // 1: for Interceptint get module var
108    UNLOAD_END_EXECUTE_PATCHMAIN // 2 :for execute abc normally
109};
110
111enum class StageOfColdReload : int32_t {
112    NOT_COLD_RELOAD,
113    IS_COLD_RELOAD
114};
115
116class PatchLoader {
117public:
118    PatchLoader() = default;
119    ~PatchLoader() = default;
120    NO_COPY_SEMANTIC(PatchLoader);
121    NO_MOVE_SEMANTIC(PatchLoader);
122
123    static PatchErrorCode LoadPatchInternal(JSThread *thread, const JSPandaFile *baseFile,
124                                            const JSPandaFile *patchFile, PatchInfo &patchInfo,
125                                            const CMap<uint32_t, CString> &baseClassInfo);
126    static PatchErrorCode UnloadPatchInternal(JSThread *thread, const CString &patchFileName,
127                                              const CString &baseFileName, PatchInfo &patchInfo);
128
129    static MethodLiteral *FindSameMethod(PatchInfo &patchInfo, const JSPandaFile *baseFile,
130                                         EntityId baseMethodId, const CMap<uint32_t, CString> &baseClassInfo);
131    static void ExecuteFuncOrPatchMain(
132        JSThread *thread, const JSPandaFile *jsPandaFile, const PatchInfo &patchInfo, bool loadPatch = true);
133    static CMap<uint32_t, CString> CollectClassInfo(const JSPandaFile *jsPandaFile);
134    static void UpdateModuleForColdPatch(
135        JSThread *thread, EntityId methodId, CString &recordName, bool hasModule = true);
136    static void UpdateJSFunction(JSThread *thread, PatchInfo &patchInfo);
137
138private:
139    static PatchInfo GeneratePatchInfo(const JSPandaFile *patchFile);
140    static CString GetRealName(const JSPandaFile *jsPandaFile, EntityId entityId, CString &className);
141    static void FindAndReplaceSameMethod(JSThread *thread,
142                                         const JSPandaFile *baseFile,
143                                         const JSPandaFile *patchFile,
144                                         PatchInfo &patchInfo,
145                                         const CMap<uint32_t, CString> &baseClassInfo);
146    static void SaveBaseMethodInfo(PatchInfo &patchInfo, const JSPandaFile *baseFile,
147                                   EntityId baseMethodId, const BaseMethodIndex &indexs);
148    static void ReplaceMethod(JSThread *thread,
149                              Method *destMethod,
150                              MethodLiteral *srcMethodLiteral,
151                              JSTaggedValue srcConstpool);
152
153    static void ClearPatchInfo(JSThread *thread, const CString &patchFileName);
154
155    static Method *GetPatchMethod(JSThread *thread,
156        const BaseMethodIndex &methodIndex, const JSTaggedValue baseConstpool);
157    static void FindAndReplaceClassLiteral(JSThread *thread, const JSPandaFile *baseFile,
158                                           const JSPandaFile *patchFile, JSTaggedValue constpoolValue,
159                                           PatchInfo &patchInfo, uint32_t constpoolIndex,
160                                           uint32_t constpoolNum, const CMap<uint32_t, CString> &baseClassInfo);
161};
162}  // namespace panda::ecmascript
163#endif // ECMASCRIPT_PATCH_PATCH_LOADER_H
164