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