• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 #include "hotfix.h"
17 #include <binder/binder.h>
18 #include <binder/scope.h>
19 #include <binder/variable.h>
20 #include <compiler/core/pandagen.h>
21 #include <ir/expressions/literal.h>
22 
23 #include <fstream>
24 #include <iostream>
25 #include <string>
26 #include <unistd.h>
27 
28 namespace panda::es2panda::util {
29 
30 constexpr std::string_view ANONYMOUS_OR_DUPLICATE_FUNCTION_SPECIFIER = "#";
31 const std::string EXTERNAL_ATTRIBUTE = "external";
32 const panda::panda_file::SourceLang SRC_LANG = panda::panda_file::SourceLang::ECMASCRIPT;
33 
ProcessFunction(const compiler::PandaGen * pg,panda::pandasm::Function * func,LiteralBuffers & literalBuffers)34 void Hotfix::ProcessFunction(const compiler::PandaGen *pg, panda::pandasm::Function *func,
35     LiteralBuffers &literalBuffers)
36 {
37     if (generateSymbolFile_) {
38         DumpFunctionInfo(pg, func, literalBuffers);
39         return;
40     }
41 
42     if (generatePatch_ || hotReload_) {
43         HandleFunction(pg, func, literalBuffers);
44         return;
45     }
46 }
47 
ProcessModule(const std::string & recordName,std::vector<panda::pandasm::LiteralArray::Literal> & moduleBuffer)48 void Hotfix::ProcessModule(const std::string &recordName,
49     std::vector<panda::pandasm::LiteralArray::Literal> &moduleBuffer)
50 {
51     if (generateSymbolFile_) {
52         DumpModuleInfo(recordName, moduleBuffer);
53         return;
54     }
55 
56     if (generatePatch_ || hotReload_) {
57         ValidateModuleInfo(recordName, moduleBuffer);
58         return;
59     }
60 }
61 
ProcessJsonContentRecord(const std::string & recordName,const std::string & jsonFileContent)62 void Hotfix::ProcessJsonContentRecord(const std::string &recordName, const std::string &jsonFileContent)
63 {
64     if (generateSymbolFile_) {
65         DumpJsonContentRecInfo(recordName, jsonFileContent);
66         return;
67     }
68 
69     if (generatePatch_ || hotReload_) {
70         ValidateJsonContentRecInfo(recordName, jsonFileContent);
71         return;
72     }
73 }
74 
DumpModuleInfo(const std::string & recordName,std::vector<panda::pandasm::LiteralArray::Literal> & moduleBuffer)75 void Hotfix::DumpModuleInfo(const std::string &recordName,
76     std::vector<panda::pandasm::LiteralArray::Literal> &moduleBuffer)
77 {
78     std::stringstream ss;
79     ss << recordName << SymbolTable::SECOND_LEVEL_SEPERATOR;
80     auto hash = std::hash<std::string>{}(ConvertLiteralToString(moduleBuffer));
81     ss << hash << std::endl;
82     symbolTable_->WriteSymbolTable(ss.str());
83 }
84 
ValidateModuleInfo(const std::string & recordName,std::vector<panda::pandasm::LiteralArray::Literal> & moduleBuffer)85 void Hotfix::ValidateModuleInfo(const std::string &recordName,
86     std::vector<panda::pandasm::LiteralArray::Literal> &moduleBuffer)
87 {
88     auto it = originModuleInfo_->find(recordName);
89     if (it == originModuleInfo_->end()) {
90         std::cerr << "[Patch] Found new import/export expression in " << recordName << ", not supported!" << std::endl;
91         patchError_ = true;
92         return;
93     }
94 
95     auto hash = std::hash<std::string>{}(ConvertLiteralToString(moduleBuffer));
96     if (std::to_string(hash) != it->second) {
97         std::cerr << "[Patch] Found import/export expression changed in " << recordName << ", not supported!" <<
98             std::endl;
99         patchError_ = true;
100         return;
101     }
102 }
103 
DumpJsonContentRecInfo(const std::string & recordName,const std::string & jsonFileContent)104 void Hotfix::DumpJsonContentRecInfo(const std::string &recordName, const std::string &jsonFileContent)
105 {
106     std::stringstream ss;
107     ss << recordName << SymbolTable::SECOND_LEVEL_SEPERATOR;
108     auto hash = std::hash<std::string>{}(jsonFileContent);
109     ss << hash << std::endl;
110     symbolTable_->WriteSymbolTable(ss.str());
111 }
112 
ValidateJsonContentRecInfo(const std::string & recordName,const std::string & jsonFileContent)113 void Hotfix::ValidateJsonContentRecInfo(const std::string &recordName, const std::string &jsonFileContent)
114 {
115     auto it = originModuleInfo_->find(recordName);
116     if (it == originModuleInfo_->end()) {
117         std::cerr << "[Patch] Found new import/require json file expression in " << recordName
118                   << ", not supported!" << std::endl;
119         patchError_ = true;
120         return;
121     }
122 
123     auto hash = std::hash<std::string>{}(jsonFileContent);
124     if (std::to_string(hash) != it->second) {
125         std::cerr << "[Patch] Found imported/required json file content changed in " << recordName
126                   << ", not supported!" << std::endl;
127         patchError_ = true;
128         return;
129     }
130 }
131 
IsAnonymousOrDuplicateNameFunction(const std::string & funcName)132 bool Hotfix::IsAnonymousOrDuplicateNameFunction(const std::string &funcName)
133 {
134     return funcName.find(ANONYMOUS_OR_DUPLICATE_FUNCTION_SPECIFIER) != std::string::npos;
135 }
136 
GetLiteralIdxFromStringId(const std::string & stringId)137 int64_t Hotfix::GetLiteralIdxFromStringId(const std::string &stringId)
138 {
139     auto recordPrefix = recordName_ + "_";
140     auto idxStr = stringId.substr(recordPrefix.size());
141     return std::atoi(idxStr.c_str());
142 }
143 
GenerateFunctionAndClassHash(panda::pandasm::Function * func,LiteralBuffers & literalBuffers)144 std::vector<std::pair<std::string, size_t>> Hotfix::GenerateFunctionAndClassHash(panda::pandasm::Function *func,
145     LiteralBuffers &literalBuffers)
146 {
147     std::stringstream ss;
148     std::vector<std::pair<std::string, size_t>> hashList;
149 
150     ss << ".function any " << func->name << '(';
151 
152     for (uint32_t i = 0; i < func->GetParamsNum(); i++) {
153         ss << "any a" << std::to_string(i);
154         if (i != func->GetParamsNum() - 1) {
155             ss << ", ";
156         }
157     }
158     ss << ") {" << std::endl;
159 
160     for (const auto &ins : func->ins) {
161         ss << (ins.set_label ? "" : "\t") << ins.ToString("", true, func->GetTotalRegs()) << " ";
162         if (ins.opcode == panda::pandasm::Opcode::CREATEARRAYWITHBUFFER ||
163             ins.opcode == panda::pandasm::Opcode::CREATEOBJECTWITHBUFFER) {
164             int64_t bufferIdx = GetLiteralIdxFromStringId(ins.ids[0]);
165             ss << ExpandLiteral(bufferIdx, literalBuffers) << " ";
166         } else if (ins.opcode == panda::pandasm::Opcode::DEFINECLASSWITHBUFFER) {
167             int64_t bufferIdx = GetLiteralIdxFromStringId(ins.ids[1]);
168             std::string literalStr = ExpandLiteral(bufferIdx, literalBuffers);
169             auto classHash = std::hash<std::string>{}(literalStr);
170             hashList.push_back(std::pair<std::string, size_t>(ins.ids[0], classHash));
171             CollectClassMemberFunctions(ins.ids[0], bufferIdx, literalBuffers);
172         }
173         ss << " ";
174     }
175 
176     ss << "}" << std::endl;
177 
178     for (const auto &ct : func->catch_blocks) {
179         ss << ".catchall " << ct.try_begin_label << ", " << ct.try_end_label << ", " << ct.catch_begin_label
180             << std::endl;
181     }
182 
183     auto funcHash = std::hash<std::string>{}(ss.str());
184     hashList.push_back(std::pair<std::string, size_t>(func->name, funcHash));
185     return hashList;
186 }
187 
ConvertLiteralToString(std::vector<panda::pandasm::LiteralArray::Literal> & literalBuffer)188 std::string Hotfix::ConvertLiteralToString(std::vector<panda::pandasm::LiteralArray::Literal> &literalBuffer)
189 {
190     std::stringstream ss;
191     int count = 0;
192     for (auto &literal : literalBuffer) {
193         ss << "{" << "index: " << count++ << " ";
194         ss << "tag: " << static_cast<std::underlying_type<panda::es2panda::ir::LiteralTag>::type>(literal.tag_);
195         ss << " ";
196         std::string val;
197         std::visit([&val](auto&& element) {
198             val += "val: ";
199             val += element;
200             val += " ";
201         }, literal.value_ );
202         ss << val;
203         ss << "},";
204 
205     }
206 
207     return ss.str();
208 }
209 
ExpandLiteral(int64_t bufferIdx,Hotfix::LiteralBuffers & literalBuffers)210 std::string Hotfix::ExpandLiteral(int64_t bufferIdx, Hotfix::LiteralBuffers &literalBuffers)
211 {
212     for (auto &litPair : literalBuffers) {
213         if (litPair.first == bufferIdx) {
214             return ConvertLiteralToString(litPair.second);
215         }
216     }
217 
218     return "";
219 }
220 
GetLiteralMethods(int64_t bufferIdx,Hotfix::LiteralBuffers & literalBuffers)221 std::vector<std::string> Hotfix::GetLiteralMethods(int64_t bufferIdx, Hotfix::LiteralBuffers &literalBuffers)
222 {
223     std::vector<std::string> methods;
224     for (auto &litPair : literalBuffers) {
225         if (litPair.first != bufferIdx) {
226             continue;
227         }
228         for (auto &literal : litPair.second) {
229             switch (literal.tag_) {
230                 case panda::panda_file::LiteralTag::METHOD:
231                 case panda::panda_file::LiteralTag::GENERATORMETHOD:
232                 case panda::panda_file::LiteralTag::ASYNCGENERATORMETHOD: {
233                     methods.push_back(std::get<std::string>(literal.value_));
234                     break;
235                 }
236                 default:
237                     break;
238             }
239         }
240     }
241 
242     return methods;
243 }
244 
CollectClassMemberFunctions(const std::string & className,int64_t bufferIdx,Hotfix::LiteralBuffers & literalBuffers)245 void Hotfix::CollectClassMemberFunctions(const std::string &className, int64_t bufferIdx,
246     Hotfix::LiteralBuffers &literalBuffers)
247 {
248     std::vector<std::string> classMemberFunctions = GetLiteralMethods(bufferIdx, literalBuffers);
249     classMemberFunctions.push_back(className);
250     classMemberFunctions_.insert({className, classMemberFunctions});
251 }
252 
IsScopeValidToPatchLexical(binder::VariableScope * scope) const253 bool Hotfix::IsScopeValidToPatchLexical(binder::VariableScope *scope) const
254 {
255     if (!generatePatch_ && !hotReload_) {
256         return false;
257     }
258 
259     if (!scope->IsFunctionVariableScope()) {
260         return false;
261     }
262 
263     auto funcName = scope->AsFunctionVariableScope()->InternalName();
264     if (std::string(funcName) != funcMain0_) {
265         return false;
266     }
267     return true;
268 }
269 
AllocSlotfromPatchEnv(const std::string & variableName)270 void Hotfix::AllocSlotfromPatchEnv(const std::string &variableName)
271 {
272     if (!topScopeLexEnvs_.count(variableName)) {
273         topScopeLexEnvs_[variableName] = topScopeIdx_++;
274     }
275 }
276 
GetSlotIdFromSymbolTable(const std::string & variableName)277 uint32_t Hotfix::GetSlotIdFromSymbolTable(const std::string &variableName)
278 {
279     auto functionIter = originFunctionInfo_->find(funcMain0_);
280     if (functionIter != originFunctionInfo_->end()) {
281         for (const auto &lexenv : functionIter->second.lexenv) {
282             if (lexenv.second.first == variableName) {
283                 return lexenv.first;
284             }
285         }
286     }
287     return UINT32_MAX;
288 }
289 
GetPatchLexicalIdx(const std::string & variableName)290 uint32_t Hotfix::GetPatchLexicalIdx(const std::string &variableName)
291 {
292     ASSERT(topScopeLexEnvs_.count(variableName));
293     return topScopeLexEnvs_[variableName];
294 }
295 
IsFunctionOrClassDefineIns(panda::pandasm::Ins & ins)296 bool IsFunctionOrClassDefineIns(panda::pandasm::Ins &ins)
297 {
298     if (ins.opcode == panda::pandasm::Opcode::DEFINEMETHOD ||
299         ins.opcode == panda::pandasm::Opcode::DEFINEFUNC ||
300         ins.opcode == panda::pandasm::Opcode::DEFINECLASSWITHBUFFER) {
301         return true;
302     }
303     return false;
304 }
305 
IsStPatchVarIns(panda::pandasm::Ins & ins)306 bool IsStPatchVarIns(panda::pandasm::Ins &ins)
307 {
308     return ins.opcode == panda::pandasm::Opcode::WIDE_STPATCHVAR;
309 }
310 
CollectFuncDefineIns(panda::pandasm::Function * func)311 void Hotfix::CollectFuncDefineIns(panda::pandasm::Function *func)
312 {
313     for (size_t i = 0; i < func->ins.size(); ++i) {
314         if (IsFunctionOrClassDefineIns(func->ins[i])) {
315             funcDefineIns_.push_back(func->ins[i]);  // push define ins
316             funcDefineIns_.push_back(func->ins[i + 1]);  // push store ins
317         }
318     }
319 }
320 
HandleModifiedClasses(panda::pandasm::Program * prog)321 void Hotfix::HandleModifiedClasses(panda::pandasm::Program *prog)
322 {
323     for (auto &cls: classMemberFunctions_) {
324         for (auto &func: cls.second) {
325             if (!prog->function_table.at(func).metadata->IsForeign()) {
326                 modifiedClassNames_.insert(cls.first);
327                 break;
328             }
329         }
330     }
331 
332     for (auto &cls: modifiedClassNames_) {
333         auto &memberFunctions = classMemberFunctions_[cls];
334         for (auto &func: memberFunctions) {
335             if (prog->function_table.at(func).metadata->IsForeign()) {
336                 prog->function_table.at(func).metadata->RemoveAttribute(EXTERNAL_ATTRIBUTE);
337             }
338         }
339     }
340 }
341 
AddHeadAndTailInsForPatchFuncMain0(std::vector<panda::pandasm::Ins> & ins)342 void Hotfix::AddHeadAndTailInsForPatchFuncMain0(std::vector<panda::pandasm::Ins> &ins)
343 {
344     panda::pandasm::Ins returnUndefine;
345     returnUndefine.opcode = pandasm::Opcode::RETURNUNDEFINED;
346 
347     if (ins.size() == 0) {
348         ins.push_back(returnUndefine);
349         return;
350     }
351 
352     panda::pandasm::Ins newLexenv;
353     newLexenv.opcode = pandasm::Opcode::NEWLEXENV;
354     newLexenv.imms.reserve(1);
355     auto newFuncNum = long(ins.size() / 2);  // each new function has 2 ins: define and store
356     newLexenv.imms.emplace_back(newFuncNum);
357 
358     ins.insert(ins.begin(), newLexenv);
359     ins.push_back(returnUndefine);
360 }
361 
AddTailInsForPatchFuncMain1(std::vector<panda::pandasm::Ins> & ins)362 void Hotfix::AddTailInsForPatchFuncMain1(std::vector<panda::pandasm::Ins> &ins)
363 {
364     panda::pandasm::Ins returnUndefined;
365     returnUndefined.opcode = pandasm::Opcode::RETURNUNDEFINED;
366     ins.push_back(returnUndefined);
367 }
368 
CreateFunctionPatchMain0AndMain1(panda::pandasm::Function & patchFuncMain0,panda::pandasm::Function & patchFuncMain1)369 void Hotfix::CreateFunctionPatchMain0AndMain1(panda::pandasm::Function &patchFuncMain0,
370     panda::pandasm::Function &patchFuncMain1)
371 {
372     const size_t defaultParamCount = 3;
373     patchFuncMain0.params.reserve(defaultParamCount);
374     patchFuncMain1.params.reserve(defaultParamCount);
375     for (uint32_t i = 0; i < defaultParamCount; ++i) {
376         patchFuncMain0.params.emplace_back(panda::pandasm::Type("any", 0), SRC_LANG);
377         patchFuncMain1.params.emplace_back(panda::pandasm::Type("any", 0), SRC_LANG);
378     }
379 
380     std::vector<panda::pandasm::Ins> patchMain0DefineIns;
381     std::vector<panda::pandasm::Ins> patchMain1DefineIns;
382 
383     for (size_t i = 0; i < funcDefineIns_.size(); ++i) {
384         if (IsFunctionOrClassDefineIns(funcDefineIns_[i])) {
385             auto &name = funcDefineIns_[i].ids[0];
386             if (newFuncNames_.count(name) && IsStPatchVarIns(funcDefineIns_[i + 1])) {
387                 patchMain0DefineIns.push_back(funcDefineIns_[i]);
388                 patchMain0DefineIns.push_back(funcDefineIns_[i + 1]);
389                 continue;
390             }
391             if (patchFuncNames_.count(name) || modifiedClassNames_.count(name)) {
392                 patchMain1DefineIns.push_back(funcDefineIns_[i]);
393                 continue;
394             }
395         }
396     }
397 
398     AddHeadAndTailInsForPatchFuncMain0(patchMain0DefineIns);
399     AddTailInsForPatchFuncMain1(patchMain1DefineIns);
400 
401     patchFuncMain0.ins = patchMain0DefineIns;
402     patchFuncMain1.ins = patchMain1DefineIns;
403 
404     patchFuncMain0.return_type = panda::pandasm::Type("any", 0);
405     patchFuncMain1.return_type = panda::pandasm::Type("any", 0);
406 }
407 
Finalize(panda::pandasm::Program ** prog)408 void Hotfix::Finalize(panda::pandasm::Program **prog)
409 {
410     if (!generatePatch_ && !hotReload_) {
411         return;
412     }
413 
414     HandleModifiedClasses(*prog);
415 
416     if (patchError_) {
417         *prog = nullptr;
418         std::cerr << "[Patch] Found unsupported change in file, will not generate patch!" << std::endl;
419         return;
420     }
421 
422     if (hotReload_) {
423         return;
424     }
425 
426     panda::pandasm::Function patchFuncMain0(patchMain0_, SRC_LANG);
427     panda::pandasm::Function patchFuncMain1(patchMain1_, SRC_LANG);
428     CreateFunctionPatchMain0AndMain1(patchFuncMain0, patchFuncMain1);
429 
430     (*prog)->function_table.emplace(patchFuncMain0.name, std::move(patchFuncMain0));
431     (*prog)->function_table.emplace(patchFuncMain1.name, std::move(patchFuncMain1));
432 }
433 
CompareLexenv(const std::string & funcName,const compiler::PandaGen * pg,SymbolTable::OriginFunctionInfo & bytecodeInfo)434 bool Hotfix::CompareLexenv(const std::string &funcName, const compiler::PandaGen *pg,
435     SymbolTable::OriginFunctionInfo &bytecodeInfo)
436 {
437     auto &lexicalVarNameAndTypes = pg->TopScope()->GetLexicalVarNameAndTypes();
438     auto &lexenv = bytecodeInfo.lexenv;
439     if (funcName != funcMain0_) {
440         if (lexenv.size() != lexicalVarNameAndTypes.size()) {
441             std::cerr << "[Patch] Found lexical variable added or removed in " << funcName << ", not supported!"
442                 << std::endl;
443             patchError_ = true;
444             return false;
445         }
446         for (auto &variable: lexicalVarNameAndTypes) {
447             auto varSlot = variable.first;
448             auto lexenvIter = lexenv.find(varSlot);
449             if (lexenvIter == lexenv.end()) {
450                 std::cerr << "[Patch] Found new lexical variable added in function " << funcName << ", not supported!"
451                     << std::endl;
452                 patchError_ = true;
453                 return false;
454             }
455 
456             auto &lexInfo = lexenvIter->second;
457             if (std::string(variable.second.first) != lexInfo.first || variable.second.second != lexInfo.second) {
458                 std::cerr << "[Patch] Found lexical variable changed in function " << funcName << ", not supported!"
459                     << std::endl;
460                 patchError_ = true;
461                 return false;
462             }
463         }
464     }
465     return true;
466 }
467 
CompareClassHash(std::vector<std::pair<std::string,size_t>> & hashList,SymbolTable::OriginFunctionInfo & bytecodeInfo)468 bool Hotfix::CompareClassHash(std::vector<std::pair<std::string, size_t>> &hashList,
469     SymbolTable::OriginFunctionInfo &bytecodeInfo)
470 {
471     auto &classInfo = bytecodeInfo.classHash;
472     for (size_t i = 0; i < hashList.size() - 1; ++i) {
473         auto &className = hashList[i].first;
474         auto classIter = classInfo.find(className);
475         if (classIter != classInfo.end()) {
476             if (classIter->second != std::to_string(hashList[i].second)) {
477                 if (hotReload_) {
478                     std::cerr << "[Patch] Found class " << hashList[i].first << " changed, not supported! If " <<
479                         hashList[i].first << " is not changed and you are changing UI Component, please only " <<
480                         "change one Component at a time and make sure the Component is placed at the bottom " <<
481                         "of the file." << std::endl;
482                 } else {
483                     std::cerr << "[Patch] Found class " << hashList[i].first << " changed, not supported!" << std::endl;
484                 }
485                 patchError_ = true;
486                 return false;
487             }
488         }
489     }
490     return true;
491 }
492 
HandleFunction(const compiler::PandaGen * pg,panda::pandasm::Function * func,LiteralBuffers & literalBuffers)493 void Hotfix::HandleFunction(const compiler::PandaGen *pg, panda::pandasm::Function *func,
494     LiteralBuffers &literalBuffers)
495 {
496     std::string funcName = func->name;
497     auto originFunction = originFunctionInfo_->find(funcName);
498     if (originFunction == originFunctionInfo_->end()) {
499         // Support adding anonymous funtion in hotreload mode.
500         if (hotReload_) {
501             return;
502         }
503         if (IsAnonymousOrDuplicateNameFunction(funcName)) {
504             std::cerr << "[Patch] Found new anonymous or duplicate name function " << funcName
505                       << " not supported!" << std::endl;
506             patchError_ = true;
507             return;
508         }
509         newFuncNames_.insert(funcName);
510         CollectFuncDefineIns(func);
511         return;
512     }
513 
514     auto &bytecodeInfo = originFunction->second;
515     if (!CompareLexenv(funcName, pg, bytecodeInfo)) {
516         return;
517     }
518 
519     auto hashList = GenerateFunctionAndClassHash(func, literalBuffers);
520     if (!CompareClassHash(hashList, bytecodeInfo)) {
521         return;
522     }
523 
524     if (hotReload_) {
525         return;
526     }
527 
528     auto funcHash = std::to_string(hashList.back().second);
529     if (funcHash == bytecodeInfo.funcHash || funcName == funcMain0_) {
530         func->metadata->SetAttribute(EXTERNAL_ATTRIBUTE);
531     } else {
532         patchFuncNames_.insert(funcName);
533     }
534 
535     CollectFuncDefineIns(func);
536 }
537 
DumpFunctionInfo(const compiler::PandaGen * pg,panda::pandasm::Function * func,Hotfix::LiteralBuffers & literalBuffers)538 void Hotfix::DumpFunctionInfo(const compiler::PandaGen *pg, panda::pandasm::Function *func,
539     Hotfix::LiteralBuffers &literalBuffers)
540 {
541     std::stringstream ss;
542 
543     ss << pg->InternalName();
544     ss << SymbolTable::SECOND_LEVEL_SEPERATOR << pg->InternalName() << SymbolTable::SECOND_LEVEL_SEPERATOR;
545 
546     std::vector<std::pair<std::string, size_t>> hashList = GenerateFunctionAndClassHash(func, literalBuffers);
547     ss << hashList.back().second << SymbolTable::SECOND_LEVEL_SEPERATOR;
548 
549     ss << SymbolTable::FIRST_LEVEL_SEPERATOR;
550     for (size_t i = 0; i < hashList.size() - 1; ++i) {
551         ss << hashList[i].first << SymbolTable::SECOND_LEVEL_SEPERATOR << hashList[i].second <<
552             SymbolTable::SECOND_LEVEL_SEPERATOR;
553     }
554     ss << SymbolTable::SECOND_LEVEL_SEPERATOR << SymbolTable::FIRST_LEVEL_SEPERATOR;
555 
556     for (auto &variable: pg->TopScope()->GetLexicalVarNameAndTypes()) {
557         ss << variable.second.first << SymbolTable::SECOND_LEVEL_SEPERATOR
558            << variable.first << SymbolTable::SECOND_LEVEL_SEPERATOR
559            << variable.second.second << SymbolTable::SECOND_LEVEL_SEPERATOR;
560     }
561     ss << SymbolTable::SECOND_LEVEL_SEPERATOR << std::endl;
562 
563     symbolTable_->WriteSymbolTable(ss.str());
564 }
565 
IsPatchVar(uint32_t slot)566 bool Hotfix::IsPatchVar(uint32_t slot)
567 {
568     return slot == UINT32_MAX;
569 }
570 
571 } // namespace panda::es2panda::util
572