1/* 2 * Copyright (c) 2021 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 <binder/scope.h> 19#include <es2panda.h> 20#include <ir/base/classDefinition.h> 21#include <ir/base/classProperty.h> 22#include <ir/base/methodDefinition.h> 23#include <ir/base/property.h> 24#include <ir/base/scriptFunction.h> 25#include <ir/base/spreadElement.h> 26#include <ir/expressions/arrayExpression.h> 27#include <ir/expressions/assignmentExpression.h> 28#include <ir/expressions/functionExpression.h> 29#include <ir/expressions/identifier.h> 30#include <ir/expressions/literals/booleanLiteral.h> 31#include <ir/expressions/literals/numberLiteral.h> 32#include <ir/expressions/literals/stringLiteral.h> 33#include <ir/expressions/objectExpression.h> 34#include <ir/expressions/unaryExpression.h> 35#include <ir/statement.h> 36#include <ir/statements/blockStatement.h> 37#include <ir/statements/expressionStatement.h> 38#include <ir/statements/variableDeclaration.h> 39#include <ir/statements/variableDeclarator.h> 40#include <ir/ts/tsParameterProperty.h> 41#include <lexer/token/sourceLocation.h> 42#include <parser/module/sourceTextModuleRecord.h> 43#include <util/concurrent.h> 44 45#ifdef ENABLE_BYTECODE_OPT 46#include <bytecode_optimizer/bytecodeopt_options.h> 47#include <bytecode_optimizer/bytecode_analysis_results.h> 48#include <bytecode_optimizer/optimize_bytecode.h> 49#else 50#include <assembly-type.h> 51#include <assembly-program.h> 52#include <assembly-emitter.h> 53#endif 54 55#ifdef PANDA_TARGET_WINDOWS 56#include <windows.h> 57#undef ERROR 58#else 59#include <unistd.h> 60#endif 61#include <algorithm> 62#include <fstream> 63#include <iostream> 64 65namespace panda::es2panda::util { 66 67// Helpers 68 69bool Helpers::IsGlobalIdentifier(const util::StringView &str) 70{ 71 return (str.Is("NaN") || str.Is("undefined") || str.Is("Infinity")); 72} 73 74bool Helpers::ContainSpreadElement(const ArenaVector<ir::Expression *> &args) 75{ 76 return std::any_of(args.begin(), args.end(), [](const auto *it) { return it->IsSpreadElement(); }); 77} 78 79util::StringView Helpers::LiteralToPropName(ArenaAllocator *allocator, const ir::Expression *lit) 80{ 81 switch (lit->Type()) { 82 case ir::AstNodeType::IDENTIFIER: { 83 return lit->AsIdentifier()->Name(); 84 } 85 case ir::AstNodeType::STRING_LITERAL: { 86 return lit->AsStringLiteral()->Str(); 87 } 88 case ir::AstNodeType::NUMBER_LITERAL: { 89 return Helpers::ToStringView(allocator, lit->AsNumberLiteral()->Number()); 90 } 91 case ir::AstNodeType::NULL_LITERAL: { 92 return "null"; 93 } 94 case ir::AstNodeType::BOOLEAN_LITERAL: { 95 if (lit->AsBooleanLiteral()->Value()) { 96 return "true"; 97 } 98 return "false"; 99 } 100 default: { 101 UNREACHABLE(); 102 } 103 } 104} 105 106bool Helpers::IsIndex(double number) 107{ 108 if (number >= 0 && number < static_cast<double>(INVALID_INDEX)) { 109 auto intNum = static_cast<uint32_t>(number); 110 111 if (static_cast<double>(intNum) == number) { 112 return true; 113 } 114 } 115 116 return false; 117} 118 119static bool IsDigit(char c) 120{ 121 return (c >= '0' && c <= '9'); 122} 123 124int64_t Helpers::GetIndex(const util::StringView &str) 125{ 126 const auto &s = str.Utf8(); 127 128 if (s.empty() || (*s.begin() == '0' && s.length() > 1)) { 129 return INVALID_INDEX; 130 } 131 132 int64_t value = 0; 133 for (const auto c : s) { 134 if (!IsDigit(c)) { 135 return INVALID_INDEX; 136 } 137 138 constexpr auto MULTIPLIER = 10; 139 value *= MULTIPLIER; 140 value += (c - '0'); 141 142 if (value >= INVALID_INDEX) { 143 return INVALID_INDEX; 144 } 145 } 146 147 return value; 148} 149 150bool Helpers::FileExtensionIs(std::string_view filePath, std::string_view extension) 151{ 152 return filePath.length() > extension.length() && Helpers::EndsWith(filePath, extension); 153} 154 155bool Helpers::EndsWith(std::string_view str, std::string_view suffix) 156{ 157 if (str.length() < suffix.length()) { 158 return false; 159 } 160 size_t expectPos = str.length() - suffix.length(); 161 return str.find(suffix, expectPos) == expectPos; 162} 163 164void Helpers::GetScientificNotationForDouble(double number, uint32_t significandBitCount, int32_t &numberBitCount, 165 char *significandArray, char *sciNotationArray, uint32_t size) 166{ 167 if (size < MAX_DOUBLE_DIGIT) { 168 std::cerr << "Failed to set the size of buffer, the buffer size provided (" << size 169 << ") is less than the required minimum size (" << MAX_DOUBLE_DIGIT 170 << ") for formatting the number in scientific notation." << std::endl; 171 return; 172 } 173 if (snprintf_s(sciNotationArray, size, size - 1, "%.*e", significandBitCount - 1, number) == FAIL_SNPRINTF_S) { 174 std::cerr << "Failed to format the number " << number 175 << " into scientific notation using snprintf_s. Please check if the buffer size (" << size 176 << ") and significand bit count (" << significandBitCount << ") are appropriate." << std::endl; 177 return; 178 } 179 180 // sciNotationArray includes significand, '.' and 'e' 181 // If significandBitCount == 1, sciNotationArray does not contain '.' 182 int32_t exponent = atoi(sciNotationArray + significandBitCount + 1 + (significandBitCount > 1)); 183 numberBitCount = exponent + 1; 184 185 // Get the significand of the current sciNotationArray 186 if (significandBitCount > 1) { 187 for (uint32_t i = 0; i < significandBitCount + 1; i++) { 188 significandArray[i] = sciNotationArray[i]; 189 } 190 } 191 192 significandArray[significandBitCount + 1] = '\0'; 193} 194 195int32_t Helpers::GetIntegerSignificandBitCount(double number, int32_t &numberBitCount, char *significandArray) 196{ 197 uint32_t bitPos = 0; 198 uint32_t minBitPos = 1; 199 uint32_t maxBitPos = MAX_DOUBLE_PRECISION_DIGIT; 200 uint32_t integerAndPointBitCount = 2; 201 char sciNotationArray[MAX_DOUBLE_DIGIT] = {0}; 202 203 while (minBitPos < maxBitPos) { 204 bitPos = (minBitPos + maxBitPos) / 2; // 2: binary search 205 GetScientificNotationForDouble(number, bitPos, numberBitCount, significandArray, 206 sciNotationArray, sizeof(sciNotationArray)); 207 208 // Update bitPos 209 if (std::strtod(sciNotationArray, nullptr) == number) { 210 while (bitPos >= integerAndPointBitCount && significandArray[bitPos] == '0') { 211 bitPos--; 212 } 213 maxBitPos = bitPos; 214 } else { 215 minBitPos = bitPos + 1; 216 } 217 } 218 219 // minBitPos == maxBitPos 220 bitPos = maxBitPos; 221 GetScientificNotationForDouble(number, bitPos, numberBitCount, significandArray, 222 sciNotationArray, sizeof(sciNotationArray)); 223 224 return bitPos; 225} 226 227std::string Helpers::DoubleToString(double number) 228{ 229 // In Scientific notation, number is expressed in the form of significand multiplied by exponent-th power of 10. 230 // The range of significand is: 1 <= |significand| < 10 231 // Scientific notation of number: sciNotationArray = significand * (10 ** exponent) 232 // number 1.23e25 => sciNotationArray: 1.23e+25, significand: 1.23, exponent: 25, 233 234 // In the ECMAScript, integerSignificand, integerSignificandBitCount and numberBitCount are defined as an integer: 235 // 1. integerSignificand is an integer in the Decimal representation of Scientific notation. 236 // 2. integerSignificandBitCount is the number of bits in the Decimal representation of significand. 237 // 3. numberBitCount is the number of bits in the Decimal representation of number. 238 // Scientific notation of number in the ECMAScript of Number::toString (number): 239 // integerSciNotationArray = integerSignificand * (10 ** (numberBitCount - integerSignificandBitCount)) 240 // number 1.23e25 => integerSciNotationArray: 123e+23, integerSignificand: 123, integerExponent: 23, 241 // integerSignificandBitCount: 3, numberBitCount: 26 242 std::string result; 243 int32_t numberBitCount = 0; 244 char significandArray[MAX_DOUBLE_DIGIT] = {0}; 245 246 if (number < 0) { 247 result += "-"; 248 number = -number; 249 } 250 251 // The number of bits of significand in integer form 252 int32_t integerSignificandBitCount = GetIntegerSignificandBitCount(number, numberBitCount, significandArray); 253 254 std::string significand = significandArray; 255 std::string integerSignificand; 256 if (numberBitCount > 0 && numberBitCount <= MAX_DECIMAL_EXPONENT) { 257 integerSignificand = significand.erase(1, 1); 258 if (numberBitCount >= integerSignificandBitCount) { 259 // If integerSignificandBitCount ≤ numberBitCount ≤ 21, return the string represented by Decimal, 260 // integerSignificand followed by (numberBitCount - integerSignificandBitCount) digit zeros. 261 integerSignificand += std::string(numberBitCount - integerSignificandBitCount, '0'); 262 } else { 263 // If 0 < numberBitCount < integerSignificandBitCount, return the string represented by Decimal, 264 // integerSignificand followed by point on the (numberBitCount + 1) digit. 265 integerSignificand.insert(numberBitCount, 1, '.'); 266 } 267 } else if (numberBitCount <= 0 && numberBitCount > MIN_DECIMAL_EXPONENT) { 268 // If -6 < numberBitCount ≤ 0, return the string consisting of "0." and digit zeros represented by Decimal, 269 // string followed by integerSignificand. 270 integerSignificand = significand.erase(1, 1); 271 integerSignificand = std::string("0.") + std::string(-numberBitCount, '0') + integerSignificand; 272 } else { 273 // If integerSignificandBitCount == 1, return the string consisting of the single digit of significand. 274 if (integerSignificandBitCount == 1) { 275 significand = significand.erase(1, 1); 276 } 277 // If numberBitCount ≤ -6 or numberBitCount > 21, return the string represented by Scientific notation, 278 // integerSignificand followed by "e", symbol and (numberBitCount - 1) digit zeros. 279 significand += 'e' + (numberBitCount >= 1 ? std::string("+") : "") + std::to_string(numberBitCount - 1); 280 281 result += significand; 282 return result; 283 } 284 285 result += integerSignificand; 286 return result; 287} 288 289std::string Helpers::ToString(double number) 290{ 291 if (std::isnan(number)) { 292 return "NaN"; 293 } 294 if (number == 0.0) { 295 return "0"; 296 } 297 if (std::isinf(number)) { 298 return "Infinity"; 299 } 300 301 std::string str; 302 if (Helpers::IsInteger<int32_t>(number)) { 303 str = std::to_string(static_cast<int32_t>(number)); 304 } else { 305 str = DoubleToString(number); 306 } 307 308 return str; 309} 310 311util::StringView Helpers::ToStringView(ArenaAllocator *allocator, double number) 312{ 313 util::UString str(ToString(number), allocator); 314 return str.View(); 315} 316 317util::StringView Helpers::ToStringView(ArenaAllocator *allocator, uint32_t number) 318{ 319 ASSERT(number <= static_cast<uint32_t>(std::numeric_limits<int32_t>::max())); 320 return ToStringView(allocator, static_cast<int32_t>(number)); 321} 322 323util::StringView Helpers::ToStringView(ArenaAllocator *allocator, int32_t number) 324{ 325 util::UString str(ToString(number), allocator); 326 return str.View(); 327} 328 329const ir::ScriptFunction *Helpers::GetContainingConstructor(const ir::AstNode *node) 330{ 331 const ir::ScriptFunction *iter = GetContainingFunction(node); 332 333 while (iter != nullptr) { 334 if (iter->IsConstructor()) { 335 return iter; 336 } 337 338 if (!iter->IsArrow()) { 339 return nullptr; 340 } 341 342 iter = GetContainingFunction(iter); 343 } 344 345 return iter; 346} 347 348const ir::ScriptFunction *Helpers::GetContainingConstructor(const ir::ClassProperty *node) 349{ 350 for (const auto *parent = node->Parent(); parent != nullptr; parent = parent->Parent()) { 351 if (parent->IsClassDefinition()) { 352 return parent->AsClassDefinition()->Ctor()->Function(); 353 } 354 } 355 356 return nullptr; 357} 358 359const ir::ScriptFunction *Helpers::GetContainingFunction(const ir::AstNode *node) 360{ 361 for (const auto *parent = node->Parent(); parent != nullptr; parent = parent->Parent()) { 362 if (parent->IsScriptFunction()) { 363 return parent->AsScriptFunction(); 364 } 365 } 366 367 return nullptr; 368} 369 370const ir::ClassDefinition *Helpers::GetClassDefiniton(const ir::ScriptFunction *node) 371{ 372 CHECK_NOT_NULL(node); 373 ASSERT(node->IsConstructor() || node->IsMethod()); 374 ASSERT(node->Parent()->IsFunctionExpression()); 375 ASSERT(node->Parent()->Parent()->IsMethodDefinition()); 376 ASSERT(node->Parent()->Parent()->Parent()->IsClassDefinition()); 377 378 return node->Parent()->Parent()->Parent()->AsClassDefinition(); 379} 380 381bool Helpers::IsSpecialPropertyKey(const ir::Expression *expr) 382{ 383 if (!expr->IsStringLiteral()) { 384 return false; 385 } 386 387 auto *lit = expr->AsStringLiteral(); 388 return lit->Str().Is("prototype") || lit->Str().Is("constructor"); 389} 390 391bool Helpers::IsConstantPropertyKey(const ir::Expression *expr, bool isComputed) 392{ 393 switch (expr->Type()) { 394 case ir::AstNodeType::IDENTIFIER: { 395 return !isComputed; 396 } 397 case ir::AstNodeType::NUMBER_LITERAL: 398 case ir::AstNodeType::STRING_LITERAL: 399 case ir::AstNodeType::BOOLEAN_LITERAL: 400 case ir::AstNodeType::NULL_LITERAL: { 401 return true; 402 } 403 default: 404 break; 405 } 406 407 return false; 408} 409 410bool Helpers::IsConstantExpr(const ir::Expression *expr) 411{ 412 switch (expr->Type()) { 413 case ir::AstNodeType::NUMBER_LITERAL: 414 case ir::AstNodeType::STRING_LITERAL: 415 case ir::AstNodeType::BOOLEAN_LITERAL: 416 case ir::AstNodeType::NULL_LITERAL: { 417 return true; 418 } 419 default: 420 break; 421 } 422 423 return false; 424} 425 426bool Helpers::IsBindingPattern(const ir::AstNode *node) 427{ 428 return node->IsArrayPattern() || node->IsObjectPattern(); 429} 430 431bool Helpers::IsPattern(const ir::AstNode *node) 432{ 433 return node->IsArrayPattern() || node->IsObjectPattern() || node->IsAssignmentPattern(); 434} 435 436static void CollectBindingName(const ir::AstNode *node, std::vector<const ir::Identifier *> *bindings) 437{ 438 switch (node->Type()) { 439 case ir::AstNodeType::IDENTIFIER: { 440 if (!Helpers::IsGlobalIdentifier(node->AsIdentifier()->Name())) { 441 bindings->push_back(node->AsIdentifier()); 442 } 443 444 break; 445 } 446 case ir::AstNodeType::OBJECT_PATTERN: { 447 for (const auto *prop : node->AsObjectPattern()->Properties()) { 448 CollectBindingName(prop, bindings); 449 } 450 break; 451 } 452 case ir::AstNodeType::ARRAY_PATTERN: { 453 for (const auto *element : node->AsArrayPattern()->Elements()) { 454 CollectBindingName(element, bindings); 455 } 456 break; 457 } 458 case ir::AstNodeType::ASSIGNMENT_PATTERN: { 459 CollectBindingName(node->AsAssignmentPattern()->Left(), bindings); 460 break; 461 } 462 case ir::AstNodeType::PROPERTY: { 463 CollectBindingName(node->AsProperty()->Value(), bindings); 464 break; 465 } 466 case ir::AstNodeType::REST_ELEMENT: { 467 CollectBindingName(node->AsRestElement()->Argument(), bindings); 468 break; 469 } 470 default: 471 break; 472 } 473} 474 475std::vector<const ir::Identifier *> Helpers::CollectBindingNames(const ir::AstNode *node) 476{ 477 std::vector<const ir::Identifier *> bindings; 478 CollectBindingName(node, &bindings); 479 return bindings; 480} 481 482util::StringView Helpers::FunctionName(ArenaAllocator *allocator, const ir::ScriptFunction *func) 483{ 484 if (func->Id()) { 485 return func->Id()->Name(); 486 } 487 488 if (func->Parent()->IsFunctionDeclaration()) { 489 return parser::SourceTextModuleRecord::DEFAULT_EXTERNAL_NAME; 490 } 491 492 const ir::AstNode *parent = func->Parent()->Parent(); 493 494 if (func->IsConstructor()) { 495 parent = parent->Parent(); 496 if (parent->AsClassDefinition()->Ident()) { 497 return parent->AsClassDefinition()->Ident()->Name(); 498 } 499 500 parent = parent->Parent()->Parent(); 501 } 502 return GetName(allocator, parent); 503} 504 505util::StringView Helpers::GetName(ArenaAllocator *allocator, const ir::AstNode *node) 506{ 507 switch (node->Type()) { 508 case ir::AstNodeType::VARIABLE_DECLARATOR: { 509 const ir::VariableDeclarator *varDecl = node->AsVariableDeclarator(); 510 511 if (varDecl->Id()->IsIdentifier()) { 512 return varDecl->Id()->AsIdentifier()->Name(); 513 } 514 515 break; 516 } 517 case ir::AstNodeType::METHOD_DEFINITION: { 518 const ir::MethodDefinition *methodDef = node->AsMethodDefinition(); 519 520 if (methodDef->Key()->IsIdentifier()) { 521 return methodDef->Key()->AsIdentifier()->Name(); 522 } 523 524 break; 525 } 526 case ir::AstNodeType::ASSIGNMENT_EXPRESSION: { 527 const ir::AssignmentExpression *assignment = node->AsAssignmentExpression(); 528 529 if (assignment->Left()->IsIdentifier()) { 530 return assignment->Left()->AsIdentifier()->Name(); 531 } 532 533 break; 534 } 535 case ir::AstNodeType::ASSIGNMENT_PATTERN: { 536 const ir::AssignmentExpression *assignment = node->AsAssignmentPattern(); 537 538 if (assignment->Left()->IsIdentifier()) { 539 return assignment->Left()->AsIdentifier()->Name(); 540 } 541 542 break; 543 } 544 case ir::AstNodeType::PROPERTY: { 545 const ir::Property *prop = node->AsProperty(); 546 547 if (prop->Kind() != ir::PropertyKind::PROTO && 548 Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed())) { 549 return Helpers::LiteralToPropName(allocator, prop->Key()); 550 } 551 552 break; 553 } 554 case ir::AstNodeType::CLASS_PROPERTY: { 555 const ir::ClassProperty *prop = node->AsClassProperty(); 556 if (Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed())) { 557 return Helpers::LiteralToPropName(allocator, prop->Key()); 558 } 559 560 break; 561 } 562 case ir::AstNodeType::EXPORT_DEFAULT_DECLARATION: { 563 return parser::SourceTextModuleRecord::DEFAULT_EXTERNAL_NAME; 564 } 565 default: 566 break; 567 } 568 569 return util::StringView(); 570} 571 572std::tuple<util::StringView, bool> Helpers::ParamName(ArenaAllocator *allocator, const ir::AstNode *param, 573 uint32_t index) 574{ 575 switch (param->Type()) { 576 case ir::AstNodeType::IDENTIFIER: { 577 return {param->AsIdentifier()->Name(), false}; 578 } 579 case ir::AstNodeType::ASSIGNMENT_PATTERN: { 580 const auto *lhs = param->AsAssignmentPattern()->Left(); 581 if (lhs->IsIdentifier()) { 582 return {param->AsAssignmentPattern()->Left()->AsIdentifier()->Name(), false}; 583 } 584 break; 585 } 586 case ir::AstNodeType::REST_ELEMENT: { 587 if (param->AsRestElement()->Argument()->IsIdentifier()) { 588 return {param->AsRestElement()->Argument()->AsIdentifier()->Name(), false}; 589 } 590 break; 591 } 592 case ir::AstNodeType::TS_PARAMETER_PROPERTY: { 593 return ParamName(allocator, param->AsTSParameterProperty()->Parameter(), index); 594 } 595 default: 596 break; 597 } 598 599 return {Helpers::ToStringView(allocator, index), true}; 600} 601 602bool Helpers::IsChild(const ir::AstNode *parent, const ir::AstNode *child) 603{ 604 while (child) { 605 if (child == parent) { 606 return true; 607 } 608 609 child = child->Parent(); 610 } 611 612 return false; 613} 614 615bool Helpers::IsChildScope(const binder::Scope *parent, const binder::Scope *child) 616{ 617 while (child) { 618 if (child == parent) { 619 return true; 620 } 621 622 child = child->Parent(); 623 } 624 625 return false; 626} 627 628bool Helpers::IsObjectPropertyValue(const ArenaVector<ir::Expression *> &properties, const ir::AstNode *ident) 629{ 630 for (const auto *prop : properties) { 631 ASSERT(prop->IsProperty() || prop->IsSpreadElement()); 632 if (prop->IsProperty() && (prop->AsProperty()->Value() == ident)) { 633 return true; 634 } 635 636 if (prop->IsSpreadElement() && (prop->AsSpreadElement()->Argument() == ident)) { 637 return true; 638 } 639 } 640 641 return false; 642} 643 644SignedNumberLiteral Helpers::GetSignedNumberLiteral(const ir::Expression *expr) 645{ 646 if (!expr->IsUnaryExpression()) { 647 return SignedNumberLiteral::UNRECOGNIZED; 648 } 649 650 auto unaryExpression = expr->AsUnaryExpression(); 651 if (!unaryExpression->Argument()->IsNumberLiteral()) { 652 return SignedNumberLiteral::UNRECOGNIZED; 653 } 654 655 // TODO(hxw): Here we return different value for positive and nagative number literal in UnaryExpression. 656 // Because when we access a computed property by MemberExpression, the compiler should emit different instruction. 657 // Now es2abc always emits the instruction `loadObjByValue` whether the computed property is literal or not. 658 // It can be optimized. For positive integer literal, the instruction should be `loadObjByIndex`. 659 // While for negative number literal, the instruction should be `loadObjByName`. 660 // So I add this util api and return different value for future use. 661 if (unaryExpression->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS) { 662 return SignedNumberLiteral::POSITIVE; 663 } else if (unaryExpression->OperatorType() == lexer::TokenType::PUNCTUATOR_MINUS) { 664 return SignedNumberLiteral::NEGATIVE; 665 } 666 667 return SignedNumberLiteral::UNRECOGNIZED; 668} 669 670void Helpers::SetConstantLocalExportSlots(const std::string &record, const std::unordered_set<uint32_t> &slots) 671{ 672 bool ignored; 673 auto &result = panda::bytecodeopt::BytecodeAnalysisResults::GetOrCreateBytecodeAnalysisResult(record, ignored); 674 result.SetConstantLocalExportSlots(slots); 675} 676 677static std::string GetTempOutputName(const std::string &inputFile) 678{ 679 std::string pid; 680#ifdef PANDA_TARGET_WINDOWS 681 pid = std::to_string(GetCurrentProcessId()); 682#else 683 pid = std::to_string(getpid()); 684#endif 685 const std::string outputSuffix = ".unopt.abc"; 686 return panda::os::file::File::GetExtendedFilePath(inputFile + pid + outputSuffix); 687} 688 689void Helpers::AnalysisProgram(panda::pandasm::Program *prog, const std::string &inputFile) 690{ 691 std::map<std::string, size_t> stat; 692 std::map<std::string, size_t> *statp = &stat; 693 694#ifdef PANDA_WITH_BYTECODE_OPTIMIZER 695 auto tempOutput = GetTempOutputName(inputFile); 696 bool exists = false; 697 auto mapsp = &panda::bytecodeopt::BytecodeAnalysisResults::GetOrCreateBytecodeMaps(tempOutput, exists); 698 ASSERT(!exists); 699 700 const uint32_t COMPONENT_MASK = panda::Logger::Component::ASSEMBLER | 701 panda::Logger::Component::BYTECODE_OPTIMIZER | 702 panda::Logger::Component::COMPILER; 703 panda::Logger::InitializeStdLogging(panda::Logger::Level::ERROR, COMPONENT_MASK); 704 705 if (panda::pandasm::AsmEmitter::Emit(tempOutput, *prog, statp, mapsp, true)) { 706 panda::bytecodeopt::AnalysisBytecode(prog, mapsp, tempOutput, true, true); 707 } else { 708 panda::bytecodeopt::BytecodeAnalysisResults::DeleteBytecodeMaps(tempOutput); 709 } 710#endif 711} 712 713void Helpers::OptimizeProgram(panda::pandasm::Program *prog, const std::string &inputFile) 714{ 715 std::map<std::string, size_t> stat; 716 std::map<std::string, size_t> *statp = &stat; 717 auto tempOutput = GetTempOutputName(inputFile); 718 719#ifdef PANDA_WITH_BYTECODE_OPTIMIZER 720 const uint32_t COMPONENT_MASK = panda::Logger::Component::ASSEMBLER | 721 panda::Logger::Component::BYTECODE_OPTIMIZER | 722 panda::Logger::Component::COMPILER; 723 panda::Logger::InitializeStdLogging(panda::Logger::Level::ERROR, COMPONENT_MASK); 724 725 bool exists = false; 726 auto mapsp = &panda::bytecodeopt::BytecodeAnalysisResults::GetOrCreateBytecodeMaps(tempOutput, exists); 727 if (!exists) { 728 exists = panda::pandasm::AsmEmitter::Emit(tempOutput, *prog, statp, mapsp, true); 729 } 730 if (exists) { 731 panda::bytecodeopt::OptimizeBytecode(prog, mapsp, tempOutput, true, true); 732 std::remove(tempOutput.c_str()); 733 } 734 panda::bytecodeopt::BytecodeAnalysisResults::DeleteBytecodeMaps(tempOutput); 735 736#endif 737} 738 739bool Helpers::CheckAopTransformPath(const std::string &libPath) 740{ 741 std::string lowerLibPath = libPath; 742 std::transform(libPath.begin(), libPath.end(), lowerLibPath.begin(), ::tolower); 743 bool isValidSuffix = false; 744#ifdef PANDA_TARGET_WINDOWS 745 isValidSuffix = FileExtensionIs(lowerLibPath, FileSuffix::DLL); 746 std::string supportSuffix = std::string(FileSuffix::DLL); 747#else 748 isValidSuffix = FileExtensionIs(lowerLibPath, FileSuffix::SO) 749 || FileExtensionIs(lowerLibPath, FileSuffix::DYLIB); 750 std::string supportSuffix = std::string(FileSuffix::SO) + "|" + std::string(FileSuffix::DYLIB); 751#endif 752 //check file suffix(.so|.dll) 753 if (!isValidSuffix) { 754 std::string msg = "aop transform file suffix support " + supportSuffix + ", error file: " + libPath; 755 std::cout << msg << std::endl; 756 return false; 757 } 758 return true; 759} 760 761AopTransformFuncDef Helpers::LoadAopTransformLibFunc(const std::string &libPath, 762 const std::string &funcName, os::library_loader::LibraryHandle &handler) 763{ 764 auto loadRes = os::library_loader::Load(libPath); 765 if (!loadRes.HasValue()) { 766 std::string msg = "os::library_loader::Load error: " + loadRes.Error().ToString(); 767 std::cout << msg << std::endl; 768 return nullptr; 769 } 770 handler = std::move(loadRes.Value()); 771 772 auto initRes = os::library_loader::ResolveSymbol(handler, funcName); 773 if (!initRes.HasValue()) { 774 std::string msg = "os::library_loader::ResolveSymbol get func Transform error: " + initRes.Error().ToString(); 775 std::cout << msg << std::endl; 776 return nullptr; 777 } 778 779 return reinterpret_cast<AopTransformFuncDef>(initRes.Value()); 780} 781 782bool Helpers::AopTransform(const std::string &inputFile, const std::string &libPath) 783{ 784 if (!CheckAopTransformPath(libPath)) { 785 return false; 786 } 787 788 os::library_loader::LibraryHandle handler(nullptr); 789 AopTransformFuncDef transform = LoadAopTransformLibFunc(libPath, "Transform", handler); 790 if (transform == nullptr) { 791 return false; 792 } 793 794 //invoke Transform, untransformed ABC to transformed ABC, result define: 0:success, other:fail 795 int res = transform(inputFile.c_str()); 796 if (res != 0) { 797 std::string msg = "Transform exec fail: " + libPath; 798 std::cout << msg << std::endl; 799 return false; 800 } 801 return true; 802} 803 804bool Helpers::ReadFileToBuffer(const std::string &file, std::stringstream &ss) 805{ 806 std::ifstream inputStream = Helpers::FileStream<std::ifstream>( 807 panda::os::file::File::GetExtendedFilePath(file), std::ios::binary); 808 if (inputStream.fail()) { 809 std::cerr << "Failed to read file to buffer: " << file << std::endl << 810 "Solutions: > Check whether the above file exists." << 811 "> Check whether you have the correct access permissions for the file."; 812 return false; 813 } 814 ss << inputStream.rdbuf(); 815 return true; 816} 817 818void Helpers::ScanDirectives(ir::ScriptFunction *func, const lexer::LineIndex &lineIndex, bool enableSendableClass, 819 bool enableSendableFunc) 820{ 821 auto *body = func->Body(); 822 if (!body || body->IsExpression()) { 823 return; 824 } 825 826 auto &statements = body->AsBlockStatement()->Statements(); 827 if (statements.empty()) { 828 return; 829 } 830 831 bool keepScan = true; 832 auto iter = statements.begin(); 833 while (keepScan && (iter != statements.end())) { 834 auto *stmt = *iter++; 835 if (!stmt->IsExpressionStatement()) { 836 return; 837 } 838 839 auto *expr = stmt->AsExpressionStatement()->GetExpression(); 840 if (!expr->IsStringLiteral()) { 841 return; 842 } 843 844 keepScan = SetFuncFlagsForDirectives(expr->AsStringLiteral(), func, lineIndex, enableSendableClass, 845 enableSendableFunc); 846 } 847 848 return; 849} 850 851bool Helpers::SetFuncFlagsForDirectives(const ir::StringLiteral *strLit, ir::ScriptFunction *func, 852 const lexer::LineIndex &lineIndex, bool enableSendableClass, 853 bool enableSendableFunc) 854{ 855 if (strLit->Str().Is(USE_CONCURRENT)) { 856 util::Concurrent::SetConcurrent(func, strLit, lineIndex); 857 return true; 858 } 859 860 if (strLit->Str().Is(USE_SENDABLE)) { 861 if (func->IsConstructor()) { 862 if (enableSendableClass) { 863 auto *classDef = const_cast<ir::ClassDefinition*>(GetClassDefiniton(func)); 864 classDef->SetSendable(); 865 } 866 } else if (enableSendableFunc) { 867 func->AddFlag(ir::ScriptFunctionFlags::SENDABLE); 868 } 869 return true; 870 } 871 872 return false; 873} 874 875std::string Helpers::GetHashString(const std::string &str) 876{ 877 uint64_t result = FNV_OFFSET; 878 879 const uint8_t *input = reinterpret_cast<const uint8_t *>(str.c_str()); 880 // FNV-1a 64-bit Algorithm 881 for (size_t i = 0; i < str.size(); i++) { 882 result ^= input[i]; 883 result *= FNV_PRIME; 884 } 885 886 return std::to_string(result); 887} 888 889#ifdef PANDA_TARGET_WINDOWS 890std::wstring Helpers::Utf8ToUtf16(const std::string &utf8) 891{ 892 std::wstring utf16; 893 if (utf8.empty()) { 894 return utf16; 895 } 896 897 if (utf8.length() > static_cast<size_t>(std::numeric_limits<int>::max())) { 898 std::cerr << "Length of filename: " << utf8 << " is too long" << std::endl; 899 return utf16; 900 } 901 902 const int utf8Length = static_cast<int>(utf8.length()); 903 constexpr DWORD kFlags = MB_ERR_INVALID_CHARS; 904 const int utf16Length = MultiByteToWideChar(CP_UTF8, kFlags, utf8.data(), utf8Length, nullptr, 0); 905 if (utf16Length == 0) { 906 std::cerr << "The filename: " << utf8 << " is not a valid utf8 encoding string" << std::endl; 907 return utf16; 908 } 909 910 utf16.resize(utf16Length); 911 MultiByteToWideChar(CP_UTF8, kFlags, utf8.data(), utf8Length, &utf16[0], utf16Length); 912 return utf16; 913} 914#endif 915 916void Helpers::ThrowError(ErrorType type, const parser::Program *program, const lexer::SourcePosition &pos, 917 const std::string_view &msg) 918{ 919 lexer::LineIndex index(program->SourceCode()); 920 lexer::SourceLocation loc = index.GetLocation(pos); 921 922 throw Error {type, msg, loc.line, loc.col}; 923} 924 925bool Helpers::IsUseShared(const ir::Statement *statement) 926{ 927 if (!statement->IsExpressionStatement()) { 928 return false; 929 } 930 931 if (!statement->AsExpressionStatement()->GetExpression()->IsStringLiteral()) { 932 return false; 933 } 934 935 return statement->AsExpressionStatement()->GetExpression()->AsStringLiteral()->Str().Is(USE_SHARED); 936} 937 938const ir::ClassDefinition *Helpers::GetContainingSendableClass(const ir::AstNode *node) 939{ 940 while (node != nullptr) { 941 if (node->IsClassDefinition() && node->AsClassDefinition()->IsSendable()) { 942 return node->AsClassDefinition(); 943 } 944 945 node = node->Parent(); 946 } 947 948 return nullptr; 949} 950 951bool Helpers::IsSpecialScopeName(const util::StringView &name) 952{ 953 return name.Find(Helpers::DOT.data()) != std::string::npos || 954 name.Find(Helpers::BACKSLASH.data()) != std::string::npos; 955} 956 957bool Helpers::BelongingToRecords(const std::string &name, const std::unordered_set<std::string> &retainRecordSet, 958 const std::string &delimiter) 959{ 960 size_t pos = name.rfind(delimiter); 961 if (pos == std::string::npos) { 962 std::cerr << "The input name: " << name << " is illegal, it should contain the delimiter character '" << 963 delimiter << "'" << std::endl; 964 return false; 965 } 966 967 auto recordName = name.substr(0, pos); 968 return retainRecordSet.find(recordName) != retainRecordSet.end(); 969} 970 971void Helpers::RemoveProgramsRedundantData(std::map<std::string, panda::es2panda::util::ProgramCache*> &progsInfo, 972 const std::map<std::string, std::unordered_set<std::string>> &resolvedDepsRelation) 973{ 974 auto progInfoIter = progsInfo.begin(); 975 while (progInfoIter != progsInfo.end()) { 976 // remove redundant sourcefiles and bytecodefile data which are not dependant in compilation 977 if (resolvedDepsRelation.find(progInfoIter->first) == resolvedDepsRelation.end()) { 978 progInfoIter = progsInfo.erase(progInfoIter); 979 continue; 980 } 981 982 progInfoIter++; 983 } 984} 985 986bool Helpers::IsDefaultApiVersion(int apiVersion, std::string subApiVersion) 987{ 988 return apiVersion < DEFAULT_TARGET_API_VERSION || ((apiVersion == DEFAULT_TARGET_API_VERSION) && 989 (subApiVersion == SUB_API_VERSION_1 || subApiVersion == SUB_API_VERSION_2)); 990} 991 992bool Helpers::IsSupportLazyImportVersion(int apiVersion, std::string subApiVersion) 993{ 994 return !(apiVersion < LAZY_IMPORT_MIN_SUPPORTED_API_VERSION || 995 ((apiVersion == LAZY_IMPORT_MIN_SUPPORTED_API_VERSION) && 996 (subApiVersion == SUB_API_VERSION_1 || subApiVersion == SUB_API_VERSION_2))); 997} 998 999} // namespace panda::es2panda::util 1000