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