/* * Copyright (c) 2022-2024 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. */ #ifndef ECMASCRIPT_PATCH_PATCH_LOADER_H #define ECMASCRIPT_PATCH_PATCH_LOADER_H #include "ecmascript/jspandafile/js_pandafile.h" #include "ecmascript/js_tagged_value.h" #include "ecmascript/method.h" #include "ecmascript/mem/c_containers.h" #include "ecmascript/napi/include/jsnapi.h" namespace panda::ecmascript { using PatchErrorCode = panda::JSNApi::PatchErrorCode; using JSRecordInfo = JSPandaFile::JSRecordInfo; using LiteralDataAccessor = panda_file::LiteralDataAccessor; using LiteralValue = panda_file::LiteralDataAccessor::LiteralValue; using LiteralTag = panda_file::LiteralTag; class ConstantPool; class JSThread; struct BaseMethodIndex { uint32_t constpoolNum {UINT32_MAX}; uint32_t constpoolIndex {UINT32_MAX}; uint32_t literalIndex {UINT32_MAX}; struct Hash { std::size_t operator()(const BaseMethodIndex &baseMethodIndex) const { return std::hash{}(baseMethodIndex.constpoolNum) ^ (std::hash{}( baseMethodIndex.constpoolIndex) << 1) ^ std::hash{}(baseMethodIndex.literalIndex); } }; bool operator==(const BaseMethodIndex &baseMethodIndex) const { return constpoolNum == baseMethodIndex.constpoolNum && constpoolIndex == baseMethodIndex.constpoolIndex && literalIndex == baseMethodIndex.literalIndex; } }; struct PatchMethodIndex { CString recordName; CString className; CString methodName; struct Hash { std::size_t operator()(const PatchMethodIndex &patchMethodIndex) const { return std::hash{}(patchMethodIndex.recordName) ^ std::hash{}(patchMethodIndex.className) ^ std::hash{}(patchMethodIndex.methodName); } }; bool operator==(const PatchMethodIndex &patchMethodIndex) const { return recordName == patchMethodIndex.recordName && className == patchMethodIndex.className && methodName == patchMethodIndex.methodName; } }; struct ReplacedMethod { EntityId methodId; CString fileName; struct Hash { std::size_t operator()(const ReplacedMethod &replacedMethod) const { return std::hash{}(replacedMethod.methodId) ^ std::hash{}(replacedMethod.fileName); } }; bool operator==(const ReplacedMethod &replacedMethod) const { return methodId == replacedMethod.methodId && fileName == replacedMethod.fileName; } }; struct PatchInfo { // patch file name. CString patchFileName; // patch methodLiterals for load patch, > CUnorderedMap patchMethodLiterals; // base method info for unload patch, CUnorderedMap baseMethodInfo; // save base constpool in global for avoid gc. CVector> baseConstpools; // patch replaced recordNames. CUnorderedSet replacedRecordNames; // patch replaced methods. CUnorderedMap replacedPatchMethods; }; enum class StageOfHotReload : int32_t { BEGIN_EXECUTE_PATCHMAIN = -1, // -1: For intercepting Evaluate() INITIALIZE_STAGE_OF_HOTRELOAD, // 0 : initialize stageOfHotreload_ in ecma_context.h LOAD_END_EXECUTE_PATCHMAIN, // 1: for Interceptint get module var UNLOAD_END_EXECUTE_PATCHMAIN // 2 :for execute abc normally }; enum class StageOfColdReload : int32_t { NOT_COLD_RELOAD, IS_COLD_RELOAD }; class PatchLoader { public: PatchLoader() = default; ~PatchLoader() = default; NO_COPY_SEMANTIC(PatchLoader); NO_MOVE_SEMANTIC(PatchLoader); static PatchErrorCode LoadPatchInternal(JSThread *thread, const JSPandaFile *baseFile, const JSPandaFile *patchFile, PatchInfo &patchInfo, const CMap &baseClassInfo); static PatchErrorCode UnloadPatchInternal(JSThread *thread, const CString &patchFileName, const CString &baseFileName, PatchInfo &patchInfo); static MethodLiteral *FindSameMethod(PatchInfo &patchInfo, const JSPandaFile *baseFile, EntityId baseMethodId, const CMap &baseClassInfo); static void ExecuteFuncOrPatchMain( JSThread *thread, const JSPandaFile *jsPandaFile, const PatchInfo &patchInfo, bool loadPatch = true); static CMap CollectClassInfo(const JSPandaFile *jsPandaFile); static void UpdateModuleForColdPatch( JSThread *thread, EntityId methodId, CString &recordName, bool hasModule = true); static void UpdateJSFunction(JSThread *thread, PatchInfo &patchInfo); private: static PatchInfo GeneratePatchInfo(const JSPandaFile *patchFile); static CString GetRealName(const JSPandaFile *jsPandaFile, EntityId entityId, CString &className); static void FindAndReplaceSameMethod(JSThread *thread, const JSPandaFile *baseFile, const JSPandaFile *patchFile, PatchInfo &patchInfo, const CMap &baseClassInfo); static void SaveBaseMethodInfo(PatchInfo &patchInfo, const JSPandaFile *baseFile, EntityId baseMethodId, const BaseMethodIndex &indexs); static void ReplaceMethod(JSThread *thread, Method *destMethod, MethodLiteral *srcMethodLiteral, JSTaggedValue srcConstpool); static void ClearPatchInfo(JSThread *thread, const CString &patchFileName); static Method *GetPatchMethod(JSThread *thread, const BaseMethodIndex &methodIndex, const JSTaggedValue baseConstpool); static void FindAndReplaceClassLiteral(JSThread *thread, const JSPandaFile *baseFile, const JSPandaFile *patchFile, JSTaggedValue constpoolValue, PatchInfo &patchInfo, uint32_t constpoolIndex, uint32_t constpoolNum, const CMap &baseClassInfo); }; } // namespace panda::ecmascript #endif // ECMASCRIPT_PATCH_PATCH_LOADER_H