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