diff --git a/es2panda/aot/options.cpp b/es2panda/aot/options.cpp index e8d2e3ead687fb1d89491ce5bb3adfc151e7914b..82645cc4f5c7c5da4c49af52c69992e487e0ce51 100644 --- a/es2panda/aot/options.cpp +++ b/es2panda/aot/options.cpp @@ -164,10 +164,11 @@ bool Options::Parse(int argc, const char **argv) panda::PandArg opNpmModuleEntryList("npm-module-entry-list", "", "entry list file for module compile"); panda::PandArg opMergeAbc("merge-abc", false, "Compile as merge abc"); - // hotfix + // hotfix && hotreload panda::PandArg opDumpSymbolTable("dump-symbol-table", "", "dump symbol table to file"); panda::PandArg opInputSymbolTable("input-symbol-table", "", "input symbol table file"); panda::PandArg opGeneratePatch("generate-patch", false, "generate patch abc"); + panda::PandArg opHotReload("hot-reload", false, "compile as hot-reload mode"); // version panda::PandArg bcVersion("bc-version", false, "Print ark bytecode version"); @@ -209,6 +210,7 @@ bool Options::Parse(int argc, const char **argv) argparser_->Add(&opDumpSymbolTable); argparser_->Add(&opInputSymbolTable); argparser_->Add(&opGeneratePatch); + argparser_->Add(&opHotReload); argparser_->Add(&bcVersion); argparser_->Add(&bcMinVersion); @@ -388,6 +390,7 @@ bool Options::Parse(int argc, const char **argv) compilerOptions_.hotfixOptions.dumpSymbolTable = opDumpSymbolTable.GetValue(); compilerOptions_.hotfixOptions.symbolTable = opInputSymbolTable.GetValue(); compilerOptions_.hotfixOptions.generatePatch = opGeneratePatch.GetValue(); + compilerOptions_.hotfixOptions.hotReload = opHotReload.GetValue(); return true; } diff --git a/es2panda/compiler/core/compilerContext.cpp b/es2panda/compiler/core/compilerContext.cpp index 32d6a6e3d84b1fdde3fdae79b92264a62442bb45..c9e1f96468355a8b67af0380b6afebf8679c9cf9 100644 --- a/es2panda/compiler/core/compilerContext.cpp +++ b/es2panda/compiler/core/compilerContext.cpp @@ -22,10 +22,11 @@ namespace panda::es2panda::compiler { CompilerContext::CompilerContext(binder::Binder *binder, bool isDebug, bool isDebuggerEvaluateExpressionMode, bool isMergeAbc, bool isTypeExtractorEnabled, bool isJsonInputFile, - std::string sourceFile, std::string pkgName, util::StringView recordName) + std::string sourceFile, std::string pkgName, util::StringView recordName, + util::Hotfix *hotfixHelper) : binder_(binder), isDebug_(isDebug), isDebuggerEvaluateExpressionMode_(isDebuggerEvaluateExpressionMode), isMergeAbc_(isMergeAbc), isTypeExtractorEnabled_(isTypeExtractorEnabled), isJsonInputFile_(isJsonInputFile), - sourceFile_(sourceFile), pkgName_(pkgName), recordName_(recordName), + sourceFile_(sourceFile), pkgName_(pkgName), recordName_(recordName), hotfixHelper_(hotfixHelper), emitter_(std::make_unique(this)) { } diff --git a/es2panda/compiler/core/compilerContext.h b/es2panda/compiler/core/compilerContext.h index 2cd7829f8dd53109d2a261860ca2703efd442293..a6af7ed4b069e8f402798b83740b5a4ad5747039 100644 --- a/es2panda/compiler/core/compilerContext.h +++ b/es2panda/compiler/core/compilerContext.h @@ -44,7 +44,7 @@ class CompilerContext { public: CompilerContext(binder::Binder *binder, bool isDebug, bool isDebuggerEvaluateExpressionMode, bool isMergeAbc, bool isTypeExtractorEnabled, bool isJsonInputFile, std::string sourceFile, - std::string pkgName, util::StringView recordName); + std::string pkgName, util::StringView recordName, util::Hotfix *hotfixHelper); NO_COPY_SEMANTIC(CompilerContext); NO_MOVE_SEMANTIC(CompilerContext); ~CompilerContext() = default; @@ -100,12 +100,7 @@ public: return pkgName_; } - void AddHotfixHelper(util::Hotfix *hotfixHelper) - { - hotfixHelper_ = hotfixHelper; - } - - util::Hotfix *HotfixHelper() + util::Hotfix *HotfixHelper() const { return hotfixHelper_; } @@ -147,8 +142,8 @@ private: std::string sourceFile_; std::string pkgName_; util::StringView recordName_; - std::unique_ptr emitter_; util::Hotfix *hotfixHelper_ {nullptr}; + std::unique_ptr emitter_; }; } // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/compilerImpl.cpp b/es2panda/compiler/core/compilerImpl.cpp index 4245749bfacc7460bbca4ae3d73d2df943c0c847..fa66ae69616287bfcef61915b81788fdca9d7cf5 100644 --- a/es2panda/compiler/core/compilerImpl.cpp +++ b/es2panda/compiler/core/compilerImpl.cpp @@ -41,11 +41,7 @@ panda::pandasm::Program *CompilerImpl::Compile(parser::Program *program, const e { CompilerContext context(program->Binder(), options.isDebug, options.isDebuggerEvaluateExpressionMode, options.mergeAbc, options.typeExtractor, false, debugInfoSourceFile, pkgName, - program->RecordName()); - - if (hotfixHelper_ != nullptr) { - context.AddHotfixHelper(hotfixHelper_); - } + program->RecordName(), hotfixHelper_); ArenaAllocator localAllocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); diff --git a/es2panda/compiler/core/emitter/emitter.cpp b/es2panda/compiler/core/emitter/emitter.cpp index b4185b1f7aa944d8fb11f145bbd76bd595e012e3..d3229edb2cc9ee55c95a9c0531e718187eb50beb 100644 --- a/es2panda/compiler/core/emitter/emitter.cpp +++ b/es2panda/compiler/core/emitter/emitter.cpp @@ -349,6 +349,9 @@ void Emitter::GenJsonContentRecord(const CompilerContext *context) jsonContentField.metadata->SetValue(panda::pandasm::ScalarValue::Create( static_cast(context->SourceFile()))); rec_->field_list.emplace_back(std::move(jsonContentField)); + if (context->HotfixHelper()) { + context->HotfixHelper()->ProcessJsonContentRecord(rec_->name, context->SourceFile()); + } } void Emitter::AddFunction(FunctionEmitter *func, CompilerContext *context) diff --git a/es2panda/es2panda.cpp b/es2panda/es2panda.cpp index 48a97b78bc9c171347636a862a8e87f6f4b0df45..f593468d9c999f4bb974cf4ac8253d23a32a537d 100644 --- a/es2panda/es2panda.cpp +++ b/es2panda/es2panda.cpp @@ -51,10 +51,10 @@ Compiler::~Compiler() delete compiler_; } -panda::pandasm::Program *CreateJsonContentProgram(std::string src, std::string rname) +panda::pandasm::Program *CreateJsonContentProgram(std::string src, std::string rname, util::Hotfix *hotfixHelper) { panda::es2panda::compiler::CompilerContext context(nullptr, false, false, false, false, true, - src, "", util::StringView(rname)); + src, "", util::StringView(rname), hotfixHelper); return context.GetEmitter()->Finalize(false, nullptr); } @@ -69,12 +69,12 @@ panda::pandasm::Program *Compiler::Compile(const SourceFile &input, const Compil std::string pkgName(input.pkgName); parser::ScriptKind kind(input.scriptKind); + auto *hotfixHelper = InitHotfixHelper(input, options, symbolTable); + if (fname.substr(fname.find_last_of(".") + 1) == "json") { - return CreateJsonContentProgram(src, rname); + return CreateJsonContentProgram(src, rname, hotfixHelper); } - auto *hotfixHelper = InitHotfixHelper(input, options, symbolTable); - try { auto ast = parser_->Parse(fname, src, rname, kind); ast.Binder()->SetProgram(&ast); @@ -117,9 +117,11 @@ util::Hotfix *Compiler::InitHotfixHelper(const SourceFile &input, const Compiler { bool needDumpSymbolFile = !options.hotfixOptions.dumpSymbolTable.empty(); bool needGeneratePatch = options.hotfixOptions.generatePatch && !options.hotfixOptions.symbolTable.empty(); + bool isHotReload = options.hotfixOptions.hotReload; util::Hotfix *hotfixHelper = nullptr; - if (symbolTable && (needDumpSymbolFile || needGeneratePatch)) { - hotfixHelper = new util::Hotfix(needDumpSymbolFile, needGeneratePatch, input.recordName, symbolTable); + if (symbolTable && (needDumpSymbolFile || needGeneratePatch || isHotReload)) { + hotfixHelper = new util::Hotfix(needDumpSymbolFile, needGeneratePatch, isHotReload, + input.recordName, symbolTable); parser_->AddHotfixHelper(hotfixHelper); compiler_->AddHotfixHelper(hotfixHelper); } diff --git a/es2panda/es2panda.h b/es2panda/es2panda.h index efe53ae44dec1b2b8537415245827ad4ed88c158..d4d201855e8d2135a07a6160efabbb4cc4308471 100644 --- a/es2panda/es2panda.h +++ b/es2panda/es2panda.h @@ -65,6 +65,7 @@ struct HotfixOptions { std::string dumpSymbolTable {}; std::string symbolTable {}; bool generatePatch {false}; + bool hotReload {false}; }; struct CompilerOptions { diff --git a/es2panda/util/hotfix.cpp b/es2panda/util/hotfix.cpp index b7aefc3c1d3a3f64d126542abac9e5c3942980c5..ae0b0fc03662c9fd118b3eca21183f77c33bd97f 100644 --- a/es2panda/util/hotfix.cpp +++ b/es2panda/util/hotfix.cpp @@ -39,7 +39,7 @@ void Hotfix::ProcessFunction(const compiler::PandaGen *pg, panda::pandasm::Funct return; } - if (generatePatch_) { + if (generatePatch_ || hotReload_) { HandleFunction(pg, func, literalBuffers); return; } @@ -53,12 +53,25 @@ void Hotfix::ProcessModule(const std::string &recordName, return; } - if (generatePatch_) { + if (generatePatch_ || hotReload_) { ValidateModuleInfo(recordName, moduleBuffer); return; } } +void Hotfix::ProcessJsonContentRecord(const std::string &recordName, const std::string &jsonFileContent) +{ + if (generateSymbolFile_) { + DumpJsonContentRecInfo(recordName, jsonFileContent); + return; + } + + if (generatePatch_ || hotReload_) { + ValidateJsonContentRecInfo(recordName, jsonFileContent); + return; + } +} + void Hotfix::DumpModuleInfo(const std::string &recordName, std::vector &moduleBuffer) { @@ -88,6 +101,34 @@ void Hotfix::ValidateModuleInfo(const std::string &recordName, } } +void Hotfix::DumpJsonContentRecInfo(const std::string &recordName, const std::string &jsonFileContent) +{ + std::stringstream ss; + ss << recordName << SymbolTable::SECOND_LEVEL_SEPERATOR; + auto hash = std::hash{}(jsonFileContent); + ss << hash << std::endl; + symbolTable_->WriteSymbolTable(ss.str()); +} + +void Hotfix::ValidateJsonContentRecInfo(const std::string &recordName, const std::string &jsonFileContent) +{ + auto it = originModuleInfo_->find(recordName); + if (it == originModuleInfo_->end()) { + std::cerr << "[Patch] Found new import/require json file expression in " << recordName + << ", not supported!" << std::endl; + patchError_ = true; + return; + } + + auto hash = std::hash{}(jsonFileContent); + if (std::to_string(hash) != it->second) { + std::cerr << "[Patch] Found imported/required json file content changed in " << recordName + << ", not supported!" << std::endl; + patchError_ = true; + return; + } +} + bool Hotfix::IsAnonymousOrDuplicateNameFunction(const std::string &funcName) { return funcName.find(ANONYMOUS_OR_DUPLICATE_FUNCTION_SPECIFIER) != std::string::npos; @@ -211,7 +252,7 @@ void Hotfix::CollectClassMemberFunctions(const std::string &className, int64_t b bool Hotfix::IsScopeValidToPatchLexical(binder::VariableScope *scope) const { - if (!generatePatch_) { + if (!generatePatch_ && !hotReload_) { return false; } @@ -357,7 +398,7 @@ void Hotfix::CreateFunctionPatchMain0AndMain1(panda::pandasm::Function &patchFun void Hotfix::Finalize(panda::pandasm::Program **prog) { - if (!generatePatch_) { + if (!generatePatch_ && !hotReload_) { return; } @@ -369,6 +410,10 @@ void Hotfix::Finalize(panda::pandasm::Program **prog) return; } + if (hotReload_) { + return; + } + panda::pandasm::Function patchFuncMain0(patchMain0_, SRC_LANG); panda::pandasm::Function patchFuncMain1(patchMain1_, SRC_LANG); CreateFunctionPatchMain0AndMain1(patchFuncMain0, patchFuncMain1); @@ -392,14 +437,16 @@ bool Hotfix::CompareLexenv(const std::string &funcName, const compiler::PandaGen auto varName = std::string(variable.second.first); auto lexenvIter = lexenv.find(varName); if (lexenvIter == lexenv.end()) { - std::cerr << "[Patch] Found new lex env added, not supported!" << std::endl; + std::cerr << "[Patch] Found new lex env added in function " << funcName << ", not supported!" + << std::endl; patchError_ = true; return false; } auto &lexInfo = lexenvIter->second; if (variable.first != lexInfo.first || variable.second.second != lexInfo.second) { - std::cerr << "[Patch] Found new lex env changed(slot or type), not supported!" << std::endl; + std::cerr << "[Patch] Found new lex env changed(slot or type) in function " << funcName + << ", not supported!" << std::endl; patchError_ = true; return false; } @@ -453,6 +500,10 @@ void Hotfix::HandleFunction(const compiler::PandaGen *pg, panda::pandasm::Functi return; } + if (hotReload_) { + return; + } + auto funcHash = std::to_string(hashList.back().second); if (funcHash == bytecodeInfo.funcHash || funcName == funcMain0_) { func->metadata->SetAttribute(EXTERNAL_ATTRIBUTE); diff --git a/es2panda/util/hotfix.h b/es2panda/util/hotfix.h index 36e06ddb51843b832ac2c105c4423a90ed3aaa7a..0a76368a3d442068cb6d6dfb11ddc6747779e288 100644 --- a/es2panda/util/hotfix.h +++ b/es2panda/util/hotfix.h @@ -38,8 +38,10 @@ class Hotfix { using LiteralBuffers = ArenaVector>>; public: - Hotfix(bool generateSymbolFile, bool generatePatch, const std::string &recordName, util::SymbolTable *symbolTable) - : generateSymbolFile_(generateSymbolFile), generatePatch_(generatePatch), recordName_(recordName), + Hotfix(bool generateSymbolFile, bool generatePatch, bool hotReload, const std::string &recordName, + util::SymbolTable *symbolTable) + : generateSymbolFile_(generateSymbolFile), generatePatch_(generatePatch), hotReload_(hotReload), + recordName_(recordName), symbolTable_(symbolTable), allocator_(SpaceType::SPACE_TYPE_COMPILER, nullptr, true), topScopeLexEnvs_(allocator_.Adapter()), @@ -63,6 +65,7 @@ public: bool IsPatchVar(uint32_t slot); void ProcessFunction(const compiler::PandaGen *pg, panda::pandasm::Function *func, LiteralBuffers &literalBuffers); void ProcessModule(const std::string &recordName, std::vector &moduleBuffer); + void ProcessJsonContentRecord(const std::string &recordName, const std::string &jsonFileContent); private: void DumpFunctionInfo(const compiler::PandaGen *pg, panda::pandasm::Function *func, LiteralBuffers &literalBuffers); @@ -72,6 +75,8 @@ private: void DumpModuleInfo(const std::string &recordName, std::vector &moduleBuffer); void ValidateModuleInfo(const std::string &recordName, std::vector &moduleBuffer); + void DumpJsonContentRecInfo(const std::string &recordName, const std::string &jsonFileContent); + void ValidateJsonContentRecInfo(const std::string &recordName, const std::string &jsonFileContent); std::string ExpandLiteral(int64_t bufferIdx, LiteralBuffers &literalBuffers); std::string ConvertLiteralToString(std::vector &literalBuffer); @@ -94,6 +99,7 @@ private: bool patchError_ {false}; bool generateSymbolFile_ {false}; bool generatePatch_ {false}; + bool hotReload_ {false}; std::string recordName_; std::string funcMain0_; std::string patchMain0_; // stores newly added function define ins, runtime will execute