• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-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 #include "helpers.h"
17 
18 #include "varbinder/privateBinding.h"
19 #include "checker/types/ets/types.h"
20 #include "ir/astNode.h"
21 #include "ir/base/classDefinition.h"
22 #include "ir/base/classProperty.h"
23 #include "ir/base/methodDefinition.h"
24 #include "ir/base/property.h"
25 #include "ir/base/scriptFunction.h"
26 #include "ir/base/spreadElement.h"
27 #include "ir/expressions/arrayExpression.h"
28 #include "ir/expressions/assignmentExpression.h"
29 #include "ir/expressions/callExpression.h"
30 #include "ir/expressions/functionExpression.h"
31 #include "ir/expressions/identifier.h"
32 #include "ir/expressions/literals/numberLiteral.h"
33 #include "ir/expressions/literals/stringLiteral.h"
34 #include "ir/expressions/literals/booleanLiteral.h"
35 #include "ir/expressions/literals/nullLiteral.h"
36 #include "ir/expressions/objectExpression.h"
37 #include "ir/statements/returnStatement.h"
38 #include "ir/statements/variableDeclaration.h"
39 #include "ir/statements/variableDeclarator.h"
40 #include "ir/module/importSpecifier.h"
41 #include "ir/ets/etsImportDeclaration.h"
42 #include "ir/ts/tsParameterProperty.h"
43 #include "ir/ts/tsInterfaceDeclaration.h"
44 #include "ir/ts/tsEnumDeclaration.h"
45 #include "ir/ets/etsParameterExpression.h"
46 #include "ir/module/importDeclaration.h"
47 #include "lexer/token/letters.h"
48 #include "libpandabase/utils/utf.h"
49 #include "libpandabase/os/filesystem.h"
50 
51 namespace panda::es2panda::util {
52 // Helpers
53 
IsGlobalIdentifier(const util::StringView & str)54 bool Helpers::IsGlobalIdentifier(const util::StringView &str)
55 {
56     return (str.Is("NaN") || str.Is("undefined") || str.Is("Infinity"));
57 }
58 
ContainSpreadElement(const ArenaVector<ir::Expression * > & args)59 bool Helpers::ContainSpreadElement(const ArenaVector<ir::Expression *> &args)
60 {
61     return std::any_of(args.begin(), args.end(), [](const auto *it) { return it->IsSpreadElement(); });
62 }
63 
LiteralToPropName(const ir::Expression * lit)64 util::StringView Helpers::LiteralToPropName(const ir::Expression *lit)
65 {
66     switch (lit->Type()) {
67         case ir::AstNodeType::IDENTIFIER: {
68             return lit->AsIdentifier()->Name();
69         }
70         case ir::AstNodeType::STRING_LITERAL: {
71             return lit->AsStringLiteral()->Str();
72         }
73         case ir::AstNodeType::NUMBER_LITERAL: {
74             return lit->AsNumberLiteral()->Str();
75         }
76         case ir::AstNodeType::NULL_LITERAL: {
77             return "null";
78         }
79         case ir::AstNodeType::UNDEFINED_LITERAL: {
80             return "undefined";
81         }
82         default: {
83             UNREACHABLE();
84         }
85     }
86 }
87 
IsIndex(double number)88 bool Helpers::IsIndex(double number)
89 {
90     if (number >= 0 && number < static_cast<double>(INVALID_INDEX)) {
91         auto intNum = static_cast<uint32_t>(number);
92 
93         if (static_cast<double>(intNum) == number) {
94             return true;
95         }
96     }
97 
98     return false;
99 }
100 
IsDigit(char c)101 static bool IsDigit(char c)
102 {
103     return (c >= '0' && c <= '9');
104 }
105 
GetIndex(const util::StringView & str)106 int64_t Helpers::GetIndex(const util::StringView &str)
107 {
108     const auto &s = str.Utf8();
109 
110     if (s.empty() || (*s.begin() == '0' && s.length() > 1)) {
111         return INVALID_INDEX;
112     }
113 
114     int64_t value = 0;
115     for (const auto c : s) {
116         if (!IsDigit(c)) {
117             return INVALID_INDEX;
118         }
119 
120         constexpr auto MULTIPLIER = 10;
121         value *= MULTIPLIER;
122         value += (c - '0');
123 
124         if (value >= INVALID_INDEX) {
125             return INVALID_INDEX;
126         }
127     }
128 
129     return value;
130 }
131 
ToString(double number)132 std::string Helpers::ToString(double number)
133 {
134     std::string str;
135 
136     if (Helpers::IsInteger<int32_t>(number)) {
137         str = std::to_string(static_cast<int32_t>(number));
138     } else {
139         str = std::to_string(number);
140     }
141 
142     return str;
143 }
144 
ToStringView(ArenaAllocator * allocator,double number)145 util::StringView Helpers::ToStringView(ArenaAllocator *allocator, double number)
146 {
147     util::UString str(ToString(number), allocator);
148     return str.View();
149 }
150 
ToStringView(ArenaAllocator * allocator,uint32_t number)151 util::StringView Helpers::ToStringView(ArenaAllocator *allocator, uint32_t number)
152 {
153     ASSERT(number <= static_cast<uint32_t>(std::numeric_limits<int32_t>::max()));
154     return ToStringView(allocator, static_cast<int32_t>(number));
155 }
156 
ToStringView(ArenaAllocator * allocator,int32_t number)157 util::StringView Helpers::ToStringView(ArenaAllocator *allocator, int32_t number)
158 {
159     util::UString str(ToString(number), allocator);
160     return str.View();
161 }
162 
IsRelativePath(const std::string & path)163 bool Helpers::IsRelativePath(const std::string &path)
164 {
165     auto pathDelimiter = panda::os::file::File::GetPathDelim();
166 
167     std::string currentDirReference = ".";
168     std::string parentDirReference = "..";
169 
170     currentDirReference.append(pathDelimiter);
171     parentDirReference.append(pathDelimiter);
172 
173     return ((path.find(currentDirReference) == 0) || (path.find(parentDirReference) == 0));
174 }
175 
GetAbsPath(const std::string & path)176 std::string Helpers::GetAbsPath(const std::string &path)
177 {
178     std::string fullFilePath = path;
179     std::string importExtension;
180     if (!panda::os::file::File::IsRegularFile(path) && (panda::os::GetAbsolutePath(path).empty())) {
181         importExtension = ".ets";
182         fullFilePath = path + importExtension;
183         if (!panda::os::file::File::IsRegularFile(fullFilePath)) {
184             importExtension = ".ts";
185             fullFilePath = path + importExtension;
186             if (!panda::os::file::File::IsRegularFile(fullFilePath)) {
187                 return path;
188             }
189         }
190     }
191     std::string absFilePath = panda::os::GetAbsolutePath(fullFilePath);
192     absFilePath.erase(absFilePath.find(importExtension), importExtension.size());
193     return absFilePath;
194 }
195 
IsRealPath(const std::string & path)196 bool Helpers::IsRealPath(const std::string &path)
197 {
198     if (!panda::os::file::File::IsRegularFile(path) && (panda::os::GetAbsolutePath(path).empty())) {
199         auto importExtension = ".ets";
200         if (!panda::os::file::File::IsRegularFile(path + importExtension)) {
201             importExtension = ".ts";
202             if (!panda::os::file::File::IsRegularFile(path + importExtension)) {
203                 return false;
204             }
205         }
206     }
207     return true;
208 }
209 
GetContainingConstructor(const ir::AstNode * node)210 const ir::ScriptFunction *Helpers::GetContainingConstructor(const ir::AstNode *node)
211 {
212     const ir::ScriptFunction *iter = GetContainingFunction(node);
213 
214     while (iter != nullptr) {
215         if (iter->IsConstructor()) {
216             return iter;
217         }
218 
219         if (!iter->IsArrow()) {
220             return nullptr;
221         }
222 
223         iter = GetContainingFunction(iter);
224     }
225 
226     return iter;
227 }
228 
GetContainingEnumDeclaration(const ir::AstNode * node)229 const ir::TSEnumDeclaration *Helpers::GetContainingEnumDeclaration(const ir::AstNode *node)
230 {
231     auto *iter = node;
232 
233     while (iter != nullptr) {
234         if (iter->IsTSEnumDeclaration()) {
235             return iter->AsTSEnumDeclaration();
236         }
237 
238         iter = iter->Parent();
239     }
240 
241     return nullptr;
242 }
243 
GetContainingObjectType(const ir::AstNode * node)244 const checker::ETSObjectType *Helpers::GetContainingObjectType(const ir::AstNode *node)
245 {
246     const auto *iter = node;
247 
248     while (iter != nullptr) {
249         if (iter->IsClassDefinition()) {
250             auto *ret = iter->AsClassDefinition()->TsType();
251             return ret != nullptr ? ret->AsETSObjectType() : nullptr;
252         }
253 
254         if (iter->IsTSInterfaceDeclaration()) {
255             auto *ret = iter->AsTSInterfaceDeclaration()->TsType();
256             return ret != nullptr ? ret->AsETSObjectType() : nullptr;
257         }
258 
259         if (iter->IsTSEnumDeclaration()) {
260             auto *ret = iter->AsTSEnumDeclaration()->TsType();
261             return ret != nullptr ? ret->AsETSObjectType() : nullptr;
262         }
263 
264         if (iter->IsImportDeclaration()) {
265             // here must be `return iter->AsImportDeclaration();`
266         }
267 
268         iter = iter->Parent();
269     }
270 
271     return nullptr;
272 }
273 
GetContainingClassDefinition(const ir::AstNode * node)274 const ir::ClassDefinition *Helpers::GetContainingClassDefinition(const ir::AstNode *node)
275 {
276     const auto *iter = node;
277 
278     while (iter != nullptr) {
279         if (iter->IsClassDefinition()) {
280             return iter->AsClassDefinition();
281         }
282 
283         iter = iter->Parent();
284     }
285 
286     return nullptr;
287 }
288 
GetContainingInterfaceDeclaration(const ir::AstNode * node)289 const ir::TSInterfaceDeclaration *Helpers::GetContainingInterfaceDeclaration(const ir::AstNode *node)
290 {
291     const auto *iter = node;
292 
293     while (iter != nullptr) {
294         if (iter->IsTSInterfaceDeclaration()) {
295             return iter->AsTSInterfaceDeclaration();
296         }
297 
298         iter = iter->Parent();
299     }
300 
301     return nullptr;
302 }
303 
GetContainingClassMethodDefinition(const ir::AstNode * node)304 const ir::MethodDefinition *Helpers::GetContainingClassMethodDefinition(const ir::AstNode *node)
305 {
306     const auto *iter = node;
307 
308     while (iter != nullptr) {
309         if (iter->IsMethodDefinition()) {
310             return iter->AsMethodDefinition();
311         }
312 
313         if (iter->IsClassDefinition()) {
314             break;
315         }
316 
317         iter = iter->Parent();
318     }
319 
320     return nullptr;
321 }
322 
GetContainingClassStaticBlock(const ir::AstNode * node)323 const ir::ClassStaticBlock *Helpers::GetContainingClassStaticBlock(const ir::AstNode *node)
324 {
325     const auto *iter = node;
326 
327     while (iter != nullptr) {
328         if (iter->IsClassStaticBlock()) {
329             return iter->AsClassStaticBlock();
330         }
331 
332         if (iter->IsClassDefinition()) {
333             break;
334         }
335 
336         iter = iter->Parent();
337     }
338 
339     return nullptr;
340 }
341 
GetContainingConstructor(const ir::ClassProperty * node)342 const ir::ScriptFunction *Helpers::GetContainingConstructor(const ir::ClassProperty *node)
343 {
344     for (const auto *parent = node->Parent(); parent != nullptr; parent = parent->Parent()) {
345         if (parent->IsClassDefinition()) {
346             return parent->AsClassDefinition()->Ctor()->Function();
347         }
348     }
349 
350     return nullptr;
351 }
352 
GetContainingFunction(const ir::AstNode * node)353 const ir::ScriptFunction *Helpers::GetContainingFunction(const ir::AstNode *node)
354 {
355     for (const auto *parent = node->Parent(); parent != nullptr; parent = parent->Parent()) {
356         if (parent->IsScriptFunction()) {
357             return parent->AsScriptFunction();
358         }
359     }
360 
361     return nullptr;
362 }
363 
GetClassDefiniton(const ir::ScriptFunction * node)364 const ir::ClassDefinition *Helpers::GetClassDefiniton(const ir::ScriptFunction *node)
365 {
366     ASSERT(node->IsConstructor());
367     ASSERT(node->Parent()->IsFunctionExpression());
368     ASSERT(node->Parent()->Parent()->IsMethodDefinition());
369     ASSERT(node->Parent()->Parent()->Parent()->IsClassDefinition());
370 
371     return node->Parent()->Parent()->Parent()->AsClassDefinition();
372 }
373 
IsSpecialPropertyKey(const ir::Expression * expr)374 bool Helpers::IsSpecialPropertyKey(const ir::Expression *expr)
375 {
376     if (!expr->IsStringLiteral()) {
377         return false;
378     }
379 
380     auto *lit = expr->AsStringLiteral();
381     return lit->Str().Is("prototype") || lit->Str().Is("constructor");
382 }
383 
IsConstantPropertyKey(const ir::Expression * expr,bool isComputed)384 bool Helpers::IsConstantPropertyKey(const ir::Expression *expr, bool isComputed)
385 {
386     switch (expr->Type()) {
387         case ir::AstNodeType::IDENTIFIER: {
388             return !isComputed;
389         }
390         case ir::AstNodeType::NUMBER_LITERAL:
391         case ir::AstNodeType::STRING_LITERAL:
392         case ir::AstNodeType::BOOLEAN_LITERAL:
393         case ir::AstNodeType::NULL_LITERAL: {
394             return true;
395         }
396         default:
397             break;
398     }
399 
400     return false;
401 }
402 
ToConstantLiteral(const ir::Expression * expr)403 compiler::Literal Helpers::ToConstantLiteral(const ir::Expression *expr)
404 {
405     switch (expr->Type()) {
406         case ir::AstNodeType::NUMBER_LITERAL: {
407             auto *lit = expr->AsNumberLiteral();
408             if (util::Helpers::IsInteger<uint32_t>(lit->Number().GetDouble())) {
409                 return compiler::Literal(static_cast<uint32_t>(lit->Number().GetDouble()));
410             }
411             return compiler::Literal(lit->Number().GetDouble());
412         }
413         case ir::AstNodeType::STRING_LITERAL: {
414             auto *lit = expr->AsStringLiteral();
415             return compiler::Literal(lit->Str());
416         }
417         case ir::AstNodeType::BOOLEAN_LITERAL: {
418             auto *lit = expr->AsBooleanLiteral();
419             return compiler::Literal(lit->Value());
420         }
421         case ir::AstNodeType::NULL_LITERAL: {
422             return compiler::Literal::NullLiteral();
423         }
424         case ir::AstNodeType::UNDEFINED_LITERAL: {
425             return compiler::Literal::UndefinedLiteral();
426         }
427         default:
428             break;
429     }
430 
431     return compiler::Literal();
432 }
433 
IsBindingPattern(const ir::AstNode * node)434 bool Helpers::IsBindingPattern(const ir::AstNode *node)
435 {
436     return node->IsArrayPattern() || node->IsObjectPattern();
437 }
438 
IsPattern(const ir::AstNode * node)439 bool Helpers::IsPattern(const ir::AstNode *node)
440 {
441     return node->IsArrayPattern() || node->IsObjectPattern() || node->IsAssignmentPattern();
442 }
443 
CollectBindingName(ir::AstNode * node,std::vector<ir::Identifier * > * bindings)444 static void CollectBindingName(ir::AstNode *node, std::vector<ir::Identifier *> *bindings)
445 {
446     switch (node->Type()) {
447         case ir::AstNodeType::IDENTIFIER: {
448             if (!Helpers::IsGlobalIdentifier(node->AsIdentifier()->Name())) {
449                 bindings->push_back(node->AsIdentifier());
450             }
451 
452             break;
453         }
454         case ir::AstNodeType::OBJECT_PATTERN: {
455             for (auto *prop : node->AsObjectPattern()->Properties()) {
456                 CollectBindingName(prop, bindings);
457             }
458             break;
459         }
460         case ir::AstNodeType::ARRAY_PATTERN: {
461             for (auto *element : node->AsArrayPattern()->Elements()) {
462                 CollectBindingName(element, bindings);
463             }
464             break;
465         }
466         case ir::AstNodeType::ASSIGNMENT_PATTERN: {
467             CollectBindingName(node->AsAssignmentPattern()->Left(), bindings);
468             break;
469         }
470         case ir::AstNodeType::PROPERTY: {
471             CollectBindingName(node->AsProperty()->Value(), bindings);
472             break;
473         }
474         case ir::AstNodeType::REST_ELEMENT: {
475             CollectBindingName(node->AsRestElement()->Argument(), bindings);
476             break;
477         }
478         default:
479             break;
480     }
481 }
482 
CollectBindingNames(ir::AstNode * node)483 std::vector<ir::Identifier *> Helpers::CollectBindingNames(ir::AstNode *node)
484 {
485     std::vector<ir::Identifier *> bindings;
486     CollectBindingName(node, &bindings);
487     return bindings;
488 }
489 
CheckImportedName(ArenaVector<ir::AstNode * > * specifiers,const ir::ImportSpecifier * specifier,const std::string & fileName)490 void Helpers::CheckImportedName(ArenaVector<ir::AstNode *> *specifiers, const ir::ImportSpecifier *specifier,
491                                 const std::string &fileName)
492 {
493     auto newIdentName = specifier->Imported()->Name();
494     auto newAliasName = specifier->Local()->Name();
495     std::stringstream message {};
496 
497     for (auto *it : *specifiers) {
498         if (!it->IsImportSpecifier()) {
499             continue;
500         }
501 
502         auto savedIdentName = it->AsImportSpecifier()->Imported()->Name();
503         auto savedAliasName = it->AsImportSpecifier()->Local()->Name();
504         if (savedIdentName == savedAliasName && savedAliasName == newIdentName) {
505             message << "Warning: '" << newIdentName << "' has already imported ";
506             break;
507         }
508         if (savedIdentName == newIdentName && newAliasName != savedAliasName) {
509             message << "Warning: '" << newIdentName << "' is explicitly used with alias several times ";
510             break;
511         }
512     }
513 
514     if (message.rdbuf()->in_avail() > 0) {
515         std::cerr << message.str() << "[" << fileName.c_str() << ":" << specifier->Start().line << ":"
516                   << specifier->Start().index << "]" << std::endl;
517     }
518 }
519 
FunctionName(ArenaAllocator * allocator,const ir::ScriptFunction * func)520 util::StringView Helpers::FunctionName(ArenaAllocator *allocator, const ir::ScriptFunction *func)
521 {
522     if (func->Id() != nullptr) {
523         return func->Id()->Name();
524     }
525 
526     if (func->Parent()->IsFunctionDeclaration()) {
527         return "*default*";
528     }
529 
530     const ir::AstNode *parent = func->Parent()->Parent();
531 
532     if (func->IsConstructor()) {
533         parent = parent->Parent();
534         if (parent->AsClassDefinition()->Ident() != nullptr) {
535             return parent->AsClassDefinition()->Ident()->Name();
536         }
537 
538         parent = parent->Parent()->Parent();
539     }
540 
541     switch (parent->Type()) {
542         case ir::AstNodeType::VARIABLE_DECLARATOR: {
543             const ir::VariableDeclarator *varDecl = parent->AsVariableDeclarator();
544 
545             if (varDecl->Id()->IsIdentifier()) {
546                 return varDecl->Id()->AsIdentifier()->Name();
547             }
548 
549             break;
550         }
551         case ir::AstNodeType::METHOD_DEFINITION: {
552             const ir::MethodDefinition *methodDef = parent->AsMethodDefinition();
553 
554             if (methodDef->Key()->IsIdentifier()) {
555                 auto *ident = methodDef->Id();
556 
557                 if (!ident->IsPrivateIdent()) {
558                     return ident->Name();
559                 }
560 
561                 return util::UString(varbinder::PrivateBinding::ToPrivateBinding(ident->Name()), allocator).View();
562             }
563 
564             break;
565         }
566         case ir::AstNodeType::ASSIGNMENT_EXPRESSION: {
567             const ir::AssignmentExpression *assignment = parent->AsAssignmentExpression();
568 
569             if (assignment->Left()->IsIdentifier()) {
570                 return assignment->Left()->AsIdentifier()->Name();
571             }
572 
573             break;
574         }
575         case ir::AstNodeType::ASSIGNMENT_PATTERN: {
576             const ir::AssignmentExpression *assignment = parent->AsAssignmentPattern();
577 
578             if (assignment->Left()->IsIdentifier()) {
579                 return assignment->Left()->AsIdentifier()->Name();
580             }
581 
582             break;
583         }
584         case ir::AstNodeType::PROPERTY: {
585             const ir::Property *prop = parent->AsProperty();
586 
587             if (prop->Kind() != ir::PropertyKind::PROTO &&
588                 Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed())) {
589                 return Helpers::LiteralToPropName(prop->Key());
590             }
591 
592             break;
593         }
594         default:
595             break;
596     }
597 
598     return util::StringView();
599 }
600 
ParamName(ArenaAllocator * allocator,const ir::AstNode * param,uint32_t index)601 std::tuple<util::StringView, bool> Helpers::ParamName(ArenaAllocator *allocator, const ir::AstNode *param,
602                                                       uint32_t index)
603 {
604     switch (param->Type()) {
605         case ir::AstNodeType::IDENTIFIER: {
606             return {param->AsIdentifier()->Name(), false};
607         }
608         case ir::AstNodeType::ASSIGNMENT_PATTERN: {
609             const auto *lhs = param->AsAssignmentPattern()->Left();
610             if (lhs->IsIdentifier()) {
611                 return {param->AsAssignmentPattern()->Left()->AsIdentifier()->Name(), false};
612             }
613             break;
614         }
615         case ir::AstNodeType::REST_ELEMENT: {
616             if (param->AsRestElement()->Argument()->IsIdentifier()) {
617                 return {param->AsRestElement()->Argument()->AsIdentifier()->Name(), false};
618             }
619             break;
620         }
621         case ir::AstNodeType::TS_PARAMETER_PROPERTY: {
622             return ParamName(allocator, param->AsTSParameterProperty()->Parameter(), index);
623         }
624         case ir::AstNodeType::ETS_PARAMETER_EXPRESSION: {
625             return {param->AsETSParameterExpression()->Ident()->Name(), false};
626         }
627         default:
628             break;
629     }
630 
631     return {Helpers::ToStringView(allocator, index), true};
632 }
633 
CreateEscapedString(const std::string & str)634 std::string Helpers::CreateEscapedString(const std::string &str)
635 {
636     std::string ret {};
637     ret.reserve(str.size());
638 
639     for (std::string::value_type c : str) {
640         switch (c) {
641             case lexer::LEX_CHAR_BS: {
642                 ret.append("\\b");
643                 break;
644             }
645             case lexer::LEX_CHAR_TAB: {
646                 ret.append("\\t");
647                 break;
648             }
649             case lexer::LEX_CHAR_LF: {
650                 ret.append("\\n");
651                 break;
652             }
653             case lexer::LEX_CHAR_VT: {
654                 ret.append("\\v");
655                 break;
656             }
657             case lexer::LEX_CHAR_FF: {
658                 ret.append("\\f");
659                 break;
660             }
661             case lexer::LEX_CHAR_CR: {
662                 ret.append("\\r");
663                 break;
664             }
665             default: {
666                 ret += c;
667                 break;
668             }
669         }
670     }
671 
672     return ret;
673 }
674 
UTF16toUTF8(const char16_t c)675 std::string Helpers::UTF16toUTF8(const char16_t c)
676 {
677     const utf::Utf8Char utf8Ch = utf::ConvertUtf16ToUtf8(c, 0, false);
678     return std::string(reinterpret_cast<const char *>(utf8Ch.ch.data()), utf8Ch.n);
679 }
680 
SplitSignature(std::string_view signature)681 std::pair<std::string_view, std::string_view> Helpers::SplitSignature(std::string_view signature)
682 {
683     auto idx = signature.find_last_of(':');
684     auto stripped = signature.substr(0, idx);
685     idx = stripped.find_last_of('.');
686     auto fullClassName = stripped.substr(0, idx);
687     auto methodName = stripped.substr(idx + 1);
688     idx = fullClassName.find_last_of('.');
689     auto className = fullClassName.substr(idx + 1);
690     return {className, methodName};
691 }
692 
693 }  // namespace panda::es2panda::util
694