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