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