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 
25 namespace panda::ecmascript {
26 using PatchErrorCode = panda::JSNApi::PatchErrorCode;
27 using JSRecordInfo = JSPandaFile::JSRecordInfo;
28 using LiteralDataAccessor = panda_file::LiteralDataAccessor;
29 using LiteralValue = panda_file::LiteralDataAccessor::LiteralValue;
30 using LiteralTag = panda_file::LiteralTag;
31 class ConstantPool;
32 class JSThread;
33 
34 struct BaseMethodIndex {
35     uint32_t constpoolNum {UINT32_MAX};
36     uint32_t constpoolIndex {UINT32_MAX};
37     uint32_t literalIndex {UINT32_MAX};
38     struct Hash {
operator ()panda::ecmascript::BaseMethodIndex::Hash39         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 
operator ==panda::ecmascript::BaseMethodIndex46     bool operator==(const BaseMethodIndex &baseMethodIndex) const
47     {
48         return constpoolNum == baseMethodIndex.constpoolNum && constpoolIndex == baseMethodIndex.constpoolIndex &&
49             literalIndex == baseMethodIndex.literalIndex;
50     }
51 };
52 
53 struct PatchMethodIndex {
54     CString recordName;
55     CString className;
56     CString methodName;
57     struct Hash {
operator ()panda::ecmascript::PatchMethodIndex::Hash58         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 
operator ==panda::ecmascript::PatchMethodIndex65     bool operator==(const PatchMethodIndex &patchMethodIndex) const
66     {
67         return recordName == patchMethodIndex.recordName && className == patchMethodIndex.className &&
68             methodName == patchMethodIndex.methodName;
69     }
70 };
71 
72 struct ReplacedMethod {
73     EntityId methodId;
74     CString fileName;
75     struct Hash {
operator ()panda::ecmascript::ReplacedMethod::Hash76         std::size_t operator()(const ReplacedMethod &replacedMethod) const
77         {
78             return std::hash<EntityId>{}(replacedMethod.methodId) ^ std::hash<CString>{}(replacedMethod.fileName);
79         }
80     };
81 
operator ==panda::ecmascript::ReplacedMethod82     bool operator==(const ReplacedMethod &replacedMethod) const
83     {
84         return methodId == replacedMethod.methodId && fileName == replacedMethod.fileName;
85     }
86 };
87 
88 
89 struct 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 
104 enum 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 
111 enum class StageOfColdReload : int32_t {
112     NOT_COLD_RELOAD,
113     IS_COLD_RELOAD
114 };
115 
116 class PatchLoader {
117 public:
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 
138 private:
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