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 "ETSBinder.h"
17
18#include "ir/expressions/blockExpression.h"
19#include "ir/expressions/identifier.h"
20#include "ir/expressions/thisExpression.h"
21#include "ir/expressions/typeofExpression.h"
22#include "ir/expressions/memberExpression.h"
23#include "ir/expressions/callExpression.h"
24#include "ir/expressions/functionExpression.h"
25#include "ir/base/methodDefinition.h"
26#include "ir/base/scriptFunction.h"
27#include "ir/base/classElement.h"
28#include "ir/base/classDefinition.h"
29#include "ir/base/classProperty.h"
30#include "ir/base/classStaticBlock.h"
31#include "ir/statements/blockStatement.h"
32#include "ir/statements/classDeclaration.h"
33#include "ir/statements/variableDeclarator.h"
34#include "ir/statements/functionDeclaration.h"
35#include "ir/statements/returnStatement.h"
36#include "ir/ets/etsPrimitiveType.h"
37#include "ir/ets/etsTypeReferencePart.h"
38#include "ir/ets/etsNewClassInstanceExpression.h"
39#include "ir/ets/etsTypeReference.h"
40#include "ir/ets/etsFunctionType.h"
41#include "ir/ets/etsScript.h"
42#include "ir/ets/etsImportDeclaration.h"
43#include "ir/ts/tsInterfaceDeclaration.h"
44#include "ir/ts/tsTypeParameterDeclaration.h"
45#include "ir/ts/tsTypeParameterInstantiation.h"
46#include "ir/ts/tsClassImplements.h"
47#include "ir/ts/tsEnumDeclaration.h"
48#include "ir/ts/tsEnumMember.h"
49#include "ir/ts/tsInterfaceHeritage.h"
50#include "ir/ts/tsInterfaceBody.h"
51#include "ir/ts/tsFunctionType.h"
52#include "ir/ts/tsQualifiedName.h"
53#include "ir/module/importDefaultSpecifier.h"
54#include "ir/module/importNamespaceSpecifier.h"
55#include "ir/module/importDeclaration.h"
56#include "ir/module/importSpecifier.h"
57#include "ir/expressions/literals/stringLiteral.h"
58#include "mem/arena_allocator.h"
59#include "util/helpers.h"
60#include "util/ustring.h"
61#include "checker/ETSchecker.h"
62#include "checker/types/type.h"
63#include "checker/types/ets/types.h"
64#include "evaluate/scopedDebugInfoPlugin.h"
65#include "public/public.h"
66
67namespace ark::es2panda::varbinder {
68
69void ETSBinder::IdentifierAnalysis()
70{
71    ASSERT(Program()->Ast());
72    ASSERT(GetScope() == TopScope());
73    ASSERT(VarScope() == TopScope());
74
75    recordTable_->SetProgram(Program());
76    globalRecordTable_.SetClassDefinition(Program()->GlobalClass());
77    externalRecordTable_.insert({Program(), &globalRecordTable_});
78
79    BuildProgram();
80
81    ASSERT(globalRecordTable_.ClassDefinition() == Program()->GlobalClass());
82}
83
84void ETSBinder::LookupTypeArgumentReferences(ir::ETSTypeReference *typeRef)
85{
86    auto *iter = typeRef->Part();
87
88    while (iter != nullptr) {
89        if (iter->TypeParams() == nullptr) {
90            iter = iter->Previous();
91            continue;
92        }
93
94        ResolveReferences(iter->TypeParams());
95        iter = iter->Previous();
96    }
97}
98
99void ETSBinder::LookupTypeReference(ir::Identifier *ident, bool allowDynamicNamespaces)
100{
101    const auto &name = ident->Name();
102    if (name == compiler::Signatures::UNDEFINED || name == compiler::Signatures::NULL_LITERAL ||
103        name == compiler::Signatures::READONLY_TYPE_NAME || name == compiler::Signatures::PARTIAL_TYPE_NAME ||
104        name == compiler::Signatures::REQUIRED_TYPE_NAME) {
105        return;
106    }
107    auto *iter = GetScope();
108
109    while (iter != nullptr) {
110        auto res = iter->Find(name, ResolveBindingOptions::DECLARATION | ResolveBindingOptions::TYPE_ALIASES);
111        if (res.variable == nullptr) {
112            break;
113        }
114
115        if (IsDynamicModuleVariable(res.variable)) {
116            ident->SetVariable(res.variable);
117            return;
118        }
119
120        if (allowDynamicNamespaces && IsDynamicNamespaceVariable(res.variable)) {
121            ident->SetVariable(res.variable);
122            return;
123        }
124
125        switch (res.variable->Declaration()->Node()->Type()) {
126            case ir::AstNodeType::CLASS_DECLARATION:
127            case ir::AstNodeType::CLASS_DEFINITION:
128            case ir::AstNodeType::STRUCT_DECLARATION:
129            case ir::AstNodeType::TS_ENUM_DECLARATION:
130            case ir::AstNodeType::TS_INTERFACE_DECLARATION:
131            case ir::AstNodeType::TS_TYPE_PARAMETER:
132            case ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION:
133            case ir::AstNodeType::IMPORT_NAMESPACE_SPECIFIER: {
134                ident->SetVariable(res.variable);
135                return;
136            }
137            default: {
138                iter = iter->Parent();
139            }
140        }
141    }
142
143    auto *checker = GetContext()->checker->AsETSChecker();
144    auto *debugInfoPlugin = checker->GetDebugInfoPlugin();
145    if (UNLIKELY(debugInfoPlugin)) {
146        auto *var = debugInfoPlugin->FindClass(ident);
147        if (var != nullptr) {
148            ident->SetVariable(var);
149            return;
150        }
151        // NOTE: search an imported module's name in case of 'import "file" as xxx'.
152    }
153
154    ThrowUnresolvableType(ident->Start(), name);
155}
156
157void ETSBinder::ResolveReferencesForScope(ir::AstNode const *const parent, Scope *const scope)
158{
159    parent->Iterate([this, scope](auto *node) { ResolveReferenceForScope(node, scope); });
160}
161
162void ETSBinder::ResolveReferenceForScope(ir::AstNode *const node, Scope *const scope)
163{
164    switch (node->Type()) {
165        case ir::AstNodeType::IDENTIFIER: {
166            auto *ident = node->AsIdentifier();
167            if (ident->Variable() != nullptr) {
168                break;
169            }
170            if (auto const res = scope->Find(ident->Name(), ResolveBindingOptions::ALL); res.variable != nullptr) {
171                ident->SetVariable(res.variable);
172            }
173            break;
174        }
175        case ir::AstNodeType::VARIABLE_DECLARATOR: {
176            auto scopeCtx = LexicalScope<Scope>::Enter(this, scope);
177            BuildVarDeclarator(node->AsVariableDeclarator());
178            break;
179        }
180        /* Maybe will be used
181        case ir::AstNodeType::BLOCK_STATEMENT: {
182            auto scope_ctx = LexicalScope<Scope>::Enter(this, node->AsBlockStatement()->Scope());
183            ResolveReferences(node);
184            break;
185        }
186        */
187        case ir::AstNodeType::BLOCK_EXPRESSION: {
188            auto scopeCtx = LexicalScope<Scope>::Enter(this, node->AsBlockExpression()->Scope());
189            ResolveReferences(node);
190            break;
191        }
192        default: {
193            ResolveReferencesForScope(node, scope);
194            break;
195        }
196    }
197}
198
199void ETSBinder::ResolveReferencesForScopeWithContext(ir::AstNode *node, Scope *scope)
200{
201    auto lexScope = LexicalScope<Scope>::Enter(this, scope);
202    ResolveReference(node);
203}
204
205void ETSBinder::LookupIdentReference(ir::Identifier *ident)
206{
207    const auto &name = ident->Name();
208    auto res = GetScope()->Find(name, ResolveBindingOptions::ALL);
209    if (res.level != 0) {
210        ASSERT(res.variable != nullptr);
211
212        auto *outerFunction = GetScope()->EnclosingVariableScope()->Node();
213
214        if ((!outerFunction->IsScriptFunction() || !outerFunction->AsScriptFunction()->IsArrow()) &&
215            !res.variable->IsGlobalVariable() && res.variable->HasFlag(VariableFlags::LOCAL) && res.level > 1) {
216            ThrowInvalidCapture(ident->Start(), name);
217        }
218    }
219
220    if (res.variable == nullptr) {
221        return;
222    }
223
224    if (ident->IsReference() && res.variable->Declaration()->IsLetOrConstDecl() &&
225        !res.variable->HasFlag(VariableFlags::INITIALIZED)) {
226        ThrowTDZ(ident->Start(), name);
227    }
228}
229
230void ETSBinder::BuildClassProperty(const ir::ClassProperty *prop)
231{
232    ResolveReferences(prop);
233}
234
235void ETSBinder::InitializeInterfaceIdent(ir::TSInterfaceDeclaration *decl)
236{
237    auto res = GetScope()->Find(decl->Id()->Name());
238
239    ASSERT(res.variable && res.variable->Declaration()->IsInterfaceDecl());
240    res.variable->AddFlag(VariableFlags::INITIALIZED);
241    decl->Id()->SetVariable(res.variable);
242}
243
244void ETSBinder::ResolveEnumDeclaration(ir::TSEnumDeclaration *enumDecl)
245{
246    auto enumScopeCtx = LexicalScope<LocalScope>::Enter(this, enumDecl->Scope());
247
248    for (auto *member : enumDecl->Members()) {
249        ResolveReference(member);
250    }
251}
252
253void ETSBinder::ResolveInterfaceDeclaration(ir::TSInterfaceDeclaration *decl)
254{
255    auto boundCtx = BoundContext(recordTable_, decl);
256
257    for (auto *extend : decl->Extends()) {
258        ResolveReference(extend);
259    }
260
261    auto scopeCtx = LexicalScope<ClassScope>::Enter(this, decl->Scope()->AsClassScope());
262
263    for (auto *stmt : decl->Body()->Body()) {
264        if (!stmt->IsClassProperty()) {
265            continue;
266        }
267
268        ResolveReference(stmt);
269
270        auto fieldVar =
271            ResolvePropertyReference(stmt->AsClassProperty(), decl->Scope()->AsClassScope())
272                ->FindLocal(stmt->AsClassProperty()->Id()->Name(), varbinder::ResolveBindingOptions::BINDINGS);
273        fieldVar->AddFlag(VariableFlags::INITIALIZED);
274    }
275
276    for (auto *stmt : decl->Body()->Body()) {
277        if (stmt->IsClassProperty()) {
278            continue;
279        }
280        ResolveReference(stmt);
281    }
282}
283
284void ETSBinder::BuildInterfaceDeclaration(ir::TSInterfaceDeclaration *decl)
285{
286    if (decl->TypeParams() != nullptr) {
287        auto typeParamScopeCtx = LexicalScope<LocalScope>::Enter(this, decl->TypeParams()->Scope());
288        ResolveReferences(decl->TypeParams());
289        ResolveInterfaceDeclaration(decl);
290        return;
291    }
292
293    ResolveInterfaceDeclaration(decl);
294}
295
296void ETSBinder::BuildMethodDefinition(ir::MethodDefinition *methodDef)
297{
298    if (methodDef->Function()->TypeParams() != nullptr) {
299        auto scopeCtx = LexicalScope<LocalScope>::Enter(this, methodDef->Function()->TypeParams()->Scope());
300        ResolveReferences(methodDef->Function()->TypeParams());
301    }
302    ResolveMethodDefinition(methodDef);
303}
304
305void ETSBinder::ResolveMethodDefinition(ir::MethodDefinition *methodDef)
306{
307    methodDef->ResolveReferences([this](auto *childNode) { ResolveReference(childNode); });
308
309    auto *func = methodDef->Function();
310    if (methodDef->IsStatic() || func->IsStaticBlock()) {
311        return;
312    }
313
314    auto paramScopeCtx = LexicalScope<FunctionParamScope>::Enter(this, func->Scope()->ParamScope());
315
316    auto params = func->Scope()->ParamScope()->Params();
317    if (!params.empty() && params.front()->Name() == MANDATORY_PARAM_THIS) {
318        return;  // Implicit this parameter is already inserted by ResolveReferences(), don't insert it twice.
319    }
320
321    auto *thisParam = AddMandatoryParam(MANDATORY_PARAM_THIS);
322    thisParam->Declaration()->BindNode(thisParam_);
323}
324
325void ETSBinder::BuildMemberExpression(ir::MemberExpression *memberExpr)
326{
327    ResolveReference(memberExpr->Object());
328
329    if (memberExpr->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) {
330        ResolveReference(memberExpr->Property());
331    }
332}
333
334void ETSBinder::BuildClassDefinition(ir::ClassDefinition *classDef)
335{
336    auto boundCtx = BoundContext(recordTable_, classDef);
337
338    if (classDef->TypeParams() != nullptr) {
339        auto scopeCtx = LexicalScope<LocalScope>::Enter(this, classDef->TypeParams()->Scope());
340        ResolveReferences(classDef->TypeParams());
341        BuildClassDefinitionImpl(classDef);
342        return;
343    }
344
345    BuildClassDefinitionImpl(classDef);
346}
347
348LocalScope *ETSBinder::ResolvePropertyReference(ir::ClassProperty *prop, ClassScope *scope)
349{
350    ResolveReferences(prop);
351
352    if (prop->IsStatic()) {
353        return scope->StaticFieldScope();
354    }
355
356    return scope->InstanceFieldScope();
357}
358
359void ETSBinder::BuildClassDefinitionImpl(ir::ClassDefinition *classDef)
360{
361    auto classCtx = LexicalScope<ClassScope>::Enter(this, classDef->Scope()->AsClassScope());
362
363    if (classDef->Super() != nullptr) {
364        ResolveReference(classDef->Super());
365    }
366
367    for (auto *impl : classDef->Implements()) {
368        ResolveReference(impl);
369    }
370
371    for (auto *stmt : classDef->Body()) {
372        if (!stmt->IsClassProperty()) {
373            continue;
374        }
375
376        auto fieldScope = ResolvePropertyReference(stmt->AsClassProperty(), classDef->Scope()->AsClassScope());
377        auto fieldName = stmt->AsClassProperty()->Id()->Name();
378        auto fieldVar = fieldScope->FindLocal(fieldName, varbinder::ResolveBindingOptions::BINDINGS);
379        fieldVar->AddFlag(VariableFlags::INITIALIZED);
380        if ((fieldVar->Declaration()->IsConstDecl() || fieldVar->Declaration()->IsReadonlyDecl()) &&
381            stmt->AsClassProperty()->Value() == nullptr) {
382            fieldVar->AddFlag(VariableFlags::EXPLICIT_INIT_REQUIRED);
383        }
384    }
385
386    for (auto *stmt : classDef->Body()) {
387        if (stmt->IsClassProperty()) {
388            continue;
389        }
390        ResolveReference(stmt);
391    }
392}
393
394void ETSBinder::AddFunctionThisParam(ir::ScriptFunction *func)
395{
396    auto paramScopeCtx = LexicalScope<FunctionParamScope>::Enter(this, func->Scope()->ParamScope());
397    auto *thisParam = AddMandatoryParam(MANDATORY_PARAM_THIS);
398    thisParam->Declaration()->BindNode(thisParam_);
399}
400
401void ETSBinder::BuildProxyMethod(ir::ScriptFunction *func, const util::StringView &containingClassName, bool isStatic,
402                                 bool isExternal)
403{
404    ASSERT(!containingClassName.Empty());
405    func->Scope()->BindName(containingClassName);
406
407    if (!isStatic) {
408        auto paramScopeCtx = LexicalScope<FunctionParamScope>::Enter(this, func->Scope()->ParamScope());
409        auto *thisParam = AddMandatoryParam(MANDATORY_PARAM_THIS);
410        thisParam->Declaration()->BindNode(thisParam_);
411    }
412
413    if (!func->IsAsyncFunc() && !isExternal) {
414        Functions().push_back(func->Scope());
415    }
416}
417
418void ETSBinder::AddDynamicSpecifiersToTopBindings(ir::AstNode *const specifier,
419                                                  const ir::ETSImportDeclaration *const import)
420{
421    const auto name = [specifier]() {
422        if (specifier->IsImportNamespaceSpecifier()) {
423            return specifier->AsImportNamespaceSpecifier()->Local()->Name();
424        }
425
426        return specifier->AsImportSpecifier()->Local()->Name();
427    }();
428
429    ASSERT(GetScope()->Find(name, ResolveBindingOptions::DECLARATION).variable != nullptr);
430    auto specDecl = GetScope()->Find(name, ResolveBindingOptions::DECLARATION);
431    dynamicImportVars_.emplace(specDecl.variable, DynamicImportData {import, specifier, specDecl.variable});
432
433    if (specifier->IsImportSpecifier()) {
434        auto importSpecifier = specifier->AsImportSpecifier();
435        importSpecifier->Imported()->SetVariable(specDecl.variable);
436        importSpecifier->Local()->SetVariable(specDecl.variable);
437    }
438}
439
440void ETSBinder::InsertForeignBinding(ir::AstNode *const specifier, const ir::ETSImportDeclaration *const import,
441                                     const util::StringView &name, Variable *var)
442{
443    if (import->Language().IsDynamic()) {
444        dynamicImportVars_.emplace(var, DynamicImportData {import, specifier, var});
445    }
446
447    TopScope()->InsertForeignBinding(name, var);
448}
449
450std::string RedeclarationErrorMessageAssembler(const Variable *const var, const Variable *const variable,
451                                               util::StringView localName)
452{
453    auto type = var->Declaration()->Node()->IsClassDefinition() ? "Class '"
454                : var->Declaration()->IsFunctionDecl()          ? "Function '"
455                                                                : "Variable '";
456    auto str = util::Helpers::AppendAll(type, localName.Utf8(), "'");
457    str += variable->Declaration()->Type() == var->Declaration()->Type() ? " is already defined."
458                                                                         : " is already defined with different type.";
459
460    return str;
461}
462
463static const util::StringView &GetPackageName(varbinder::Variable *var)
464{
465    Scope *scope = var->GetScope();
466
467    while (scope->Parent() != nullptr) {
468        scope = scope->Parent();
469    }
470
471    ASSERT(scope->Node()->IsETSScript());
472
473    return scope->Node()->AsETSScript()->Program()->ModuleName();
474}
475
476void AddOverloadFlag(ArenaAllocator *allocator, bool isStdLib, varbinder::Variable *importedVar,
477                     varbinder::Variable *variable)
478{
479    auto *const currentNode = variable->Declaration()->Node()->AsMethodDefinition();
480    auto *const method = importedVar->Declaration()->Node()->AsMethodDefinition();
481
482    // Necessary because stdlib and escompat handled as same package, it can be removed after fixing package handling
483    if (isStdLib && (GetPackageName(importedVar) != GetPackageName(variable))) {
484        return;
485    }
486
487    if (!method->Overloads().empty() && !method->HasOverload(currentNode)) {
488        method->AddOverload(currentNode);
489        currentNode->Function()->Id()->SetVariable(importedVar);
490        currentNode->Function()->AddFlag(ir::ScriptFunctionFlags::OVERLOAD);
491        currentNode->Function()->AddFlag(ir::ScriptFunctionFlags::EXTERNAL_OVERLOAD);
492        util::UString newInternalName(currentNode->Function()->Scope()->Name(), allocator);
493        currentNode->Function()->Scope()->BindInternalName(newInternalName.View());
494        return;
495    }
496
497    if (!currentNode->HasOverload(method)) {
498        currentNode->AddOverload(method);
499        method->Function()->Id()->SetVariable(variable);
500        method->Function()->AddFlag(ir::ScriptFunctionFlags::OVERLOAD);
501        method->Function()->AddFlag(ir::ScriptFunctionFlags::EXTERNAL_OVERLOAD);
502        util::UString newInternalName(method->Function()->Scope()->Name(), allocator);
503        method->Function()->Scope()->BindInternalName(newInternalName.View());
504    }
505}
506
507void ETSBinder::ImportAllForeignBindings(ir::AstNode *const specifier,
508                                         const varbinder::Scope::VariableMap &globalBindings,
509                                         const parser::Program *const importProgram,
510                                         const varbinder::GlobalScope *const importGlobalScope,
511                                         const ir::ETSImportDeclaration *const import)
512{
513    for (const auto [bindingName, var] : globalBindings) {
514        if (bindingName.Is(compiler::Signatures::ETS_GLOBAL)) {
515            const auto *const classDef = var->Declaration()->Node()->AsClassDeclaration()->Definition();
516            ImportGlobalProperties(classDef);
517            continue;
518        }
519
520        if (!importGlobalScope->IsForeignBinding(bindingName) && !var->Declaration()->Node()->IsDefaultExported() &&
521            (var->AsLocalVariable()->Declaration()->Node()->IsExported() ||
522             var->AsLocalVariable()->Declaration()->Node()->IsExportedType())) {
523            auto variable = Program()->GlobalClassScope()->FindLocal(bindingName, ResolveBindingOptions::ALL);
524            if (variable != nullptr && var != variable && variable->Declaration()->IsFunctionDecl() &&
525                var->Declaration()->IsFunctionDecl()) {
526                bool isStdLib = util::Helpers::IsStdLib(Program());
527                AddOverloadFlag(Allocator(), isStdLib, var, variable);
528                continue;
529            }
530            if (variable != nullptr && var != variable) {
531                ThrowError(import->Source()->Start(), RedeclarationErrorMessageAssembler(var, variable, bindingName));
532            }
533            InsertForeignBinding(specifier, import, bindingName, var);
534        }
535    }
536
537    for (const auto [bindingName, var] : importProgram->GlobalClassScope()->StaticMethodScope()->Bindings()) {
538        if (!var->Declaration()->Node()->IsDefaultExported()) {
539            InsertForeignBinding(specifier, import, bindingName, var);
540        }
541    }
542
543    for (const auto [bindingName, var] : importProgram->GlobalClassScope()->StaticFieldScope()->Bindings()) {
544        if (!var->Declaration()->Node()->IsDefaultExported()) {
545            InsertForeignBinding(specifier, import, bindingName, var);
546        }
547    }
548}
549
550bool ETSBinder::AddImportNamespaceSpecifiersToTopBindings(ir::AstNode *const specifier,
551                                                          const varbinder::Scope::VariableMap &globalBindings,
552                                                          const parser::Program *const importProgram,
553                                                          const varbinder::GlobalScope *const importGlobalScope,
554                                                          const ir::ETSImportDeclaration *const import)
555{
556    if (!specifier->IsImportNamespaceSpecifier()) {
557        return false;
558    }
559    const auto *const namespaceSpecifier = specifier->AsImportNamespaceSpecifier();
560
561    if (namespaceSpecifier->Local()->Name().Empty()) {
562        ImportAllForeignBindings(specifier, globalBindings, importProgram, importGlobalScope, import);
563    }
564
565    std::unordered_set<std::string> exportedNames;
566    for (auto item : ReExportImports()) {
567        // NOTE(rsipka): this should be refactored or eliminated
568        if (auto source = import->ResolvedSource()->Str(), program = item->GetProgramPath();
569            !source.Is(program.Mutf8())) {
570            continue;
571        }
572
573        for (auto it : item->GetETSImportDeclarations()->Specifiers()) {
574            if (it->IsImportNamespaceSpecifier() && !namespaceSpecifier->Local()->Name().Empty()) {
575                continue;
576            }
577
578            AddSpecifiersToTopBindings(it, item->GetETSImportDeclarations(),
579                                       item->GetETSImportDeclarations()->Source());
580
581            if (it->IsImportSpecifier() &&
582                !exportedNames.insert(it->AsImportSpecifier()->Local()->Name().Mutf8()).second) {
583                ThrowError(import->Start(), "Ambiguous import \"" + it->AsImportSpecifier()->Local()->Name().Mutf8() +
584                                                "\" has multiple matching exports");
585            }
586        }
587    }
588
589    return true;
590}
591
592Variable *ETSBinder::FindImportSpecifiersVariable(const util::StringView &imported,
593                                                  const varbinder::Scope::VariableMap &globalBindings,
594                                                  const ArenaVector<parser::Program *> &recordRes)
595{
596    auto foundVar = globalBindings.find(imported);
597    if (foundVar == globalBindings.end()) {
598        const auto &staticMethodBindings = recordRes.front()->GlobalClassScope()->StaticMethodScope()->Bindings();
599        foundVar = staticMethodBindings.find(imported);
600        if (foundVar != staticMethodBindings.end()) {
601            return foundVar->second;
602        }
603        bool found = false;
604        for (auto res : recordRes) {
605            const auto &staticFieldBindings = res->GlobalClassScope()->StaticFieldScope()->Bindings();
606            foundVar = staticFieldBindings.find(imported);
607            if (foundVar != staticFieldBindings.end()) {
608                found = true;
609                foundVar->second->AsLocalVariable()->AddFlag(VariableFlags::INITIALIZED);
610                break;
611            }
612        }
613        if (!found) {
614            return nullptr;
615        }
616    }
617
618    return foundVar->second;
619}
620
621ir::ETSImportDeclaration *ETSBinder::FindImportDeclInReExports(const ir::ETSImportDeclaration *const import,
622                                                               std::vector<ir::ETSImportDeclaration *> &viewedReExport,
623                                                               const util::StringView &imported,
624                                                               const ir::StringLiteral *const importPath)
625{
626    ir::ETSImportDeclaration *implDecl = nullptr;
627    for (auto item : ReExportImports()) {
628        if (auto source = import->ResolvedSource()->Str(), program = item->GetProgramPath();
629            !source.Is(program.Mutf8())) {
630            continue;
631        }
632
633        viewedReExport.push_back(item->GetETSImportDeclarations());
634
635        auto specifiers = item->GetETSImportDeclarations()->Specifiers();
636        if (specifiers[0]->IsImportSpecifier()) {
637            if (!std::any_of(specifiers.begin(), specifiers.end(), [&imported](auto it) {
638                    return it->AsImportSpecifier()->Local()->Name().Is(imported.Mutf8());
639                })) {
640                continue;
641            }
642        } else {
643            ArenaVector<parser::Program *> record =
644                GetExternalProgram(item->GetETSImportDeclarations()->ResolvedSource()->Str(), importPath);
645            if (FindImportSpecifiersVariable(imported, record.front()->GlobalScope()->Bindings(), record) == nullptr) {
646                continue;
647            }
648        }
649
650        // NOTE: ttamas - Duplication check created error
651        implDecl = item->GetETSImportDeclarations();
652    }
653    return implDecl;
654}
655
656void ETSBinder::ValidateImportVariable(varbinder::Variable *const var, const ir::ETSImportDeclaration *const import,
657                                       const util::StringView &imported, const ir::StringLiteral *const importPath)
658{
659    if (var->Declaration()->Node()->IsDefaultExported()) {
660        ThrowError(importPath->Start(), "Use the default import syntax to import a default exported element");
661    }
662
663    if (import->IsTypeKind() && !var->Declaration()->Node()->IsExportedType()) {
664        ThrowError(importPath->Start(),
665                   "Cannot import '" + imported.Mutf8() + "', imported type imports only exported types.");
666    }
667
668    if (!var->Declaration()->Node()->IsExported() && !var->Declaration()->Node()->IsExportedType()) {
669        ThrowError(importPath->Start(), "Imported element not exported '" + var->Declaration()->Name().Mutf8() + "'");
670    }
671}
672
673static util::StringView ImportLocalName(const ir::ImportSpecifier *importSpecifier, const ir::StringLiteral *importPath,
674                                        util::StringView imported,
675                                        ArenaVector<std::pair<util::StringView, util::StringView>> &importSpecifiers,
676                                        GlobalScope *topScope)
677{
678    if (importSpecifier->Local() != nullptr) {
679        auto fnc = [&importPath, &imported](const auto &savedSpecifier) {
680            return importPath->Str() != savedSpecifier.first && imported == savedSpecifier.second;
681        };
682        if (!std::any_of(importSpecifiers.begin(), importSpecifiers.end(), fnc)) {
683            topScope->EraseBinding(imported);
684        }
685
686        importSpecifiers.push_back(std::make_pair(importPath->Str(), imported));
687
688        return importSpecifier->Local()->Name();
689    }
690
691    return imported;
692}
693
694bool ETSBinder::DetectNameConflict(const util::StringView localName, Variable *const var, Variable *const otherVar,
695                                   const ir::StringLiteral *const importPath, bool overloadAllowed)
696{
697    if (otherVar == nullptr || var == otherVar) {
698        return false;
699    }
700
701    if (overloadAllowed && var->Declaration()->IsFunctionDecl() && otherVar->Declaration()->IsFunctionDecl()) {
702        AddOverloadFlag(Allocator(), util::Helpers::IsStdLib(Program()), var, otherVar);
703        return true;
704    }
705
706    ThrowError(importPath->Start(), RedeclarationErrorMessageAssembler(var, otherVar, localName));
707}
708
709bool ETSBinder::AddImportSpecifiersToTopBindings(ir::AstNode *const specifier,
710                                                 const varbinder::Scope::VariableMap &globalBindings,
711                                                 const ir::ETSImportDeclaration *const import,
712                                                 const ArenaVector<parser::Program *> &recordRes,
713                                                 std::vector<ir::ETSImportDeclaration *> viewedReExport)
714{
715    if (!specifier->IsImportSpecifier()) {
716        return false;
717    }
718    const ir::StringLiteral *const importPath = import->Source();
719
720    auto importSpecifier = specifier->AsImportSpecifier();
721    if (!importSpecifier->Imported()->IsIdentifier()) {
722        return true;
723    }
724
725    auto imported = importSpecifier->Imported()->Name();
726
727    for (auto const item : import->Specifiers()) {
728        if (item->IsImportSpecifier() && item->AsImportSpecifier()->Local()->Name().Is(imported.Mutf8()) &&
729            !item->AsImportSpecifier()->Local()->Name().Is(item->AsImportSpecifier()->Imported()->Name().Mutf8())) {
730            imported = item->AsImportSpecifier()->Imported()->Name();
731        }
732    }
733
734    util::StringView nameToSearchFor = FindNameInAliasMap(import->ResolvedSource()->Str(), imported);
735    if (nameToSearchFor.Empty()) {
736        nameToSearchFor = imported;
737    }
738
739    auto *const var = FindImportSpecifiersVariable(nameToSearchFor, globalBindings, recordRes);
740    importSpecifier->Imported()->SetVariable(var);
741    importSpecifier->Local()->SetVariable(var);
742
743    const auto localName = ImportLocalName(importSpecifier, importPath, imported, importSpecifiers_, TopScope());
744
745    if (var == nullptr) {
746        ir::ETSImportDeclaration *implDecl = FindImportDeclInReExports(import, viewedReExport, imported, importPath);
747        if (implDecl != nullptr) {
748            AddSpecifiersToTopBindings(specifier, implDecl, implDecl->Source(), viewedReExport);
749            return true;
750        }
751
752        ThrowError(importPath->Start(), "Cannot find imported element '" + imported.Mutf8() + "'");
753    }
754
755    ValidateImportVariable(var, import, imported, importPath);
756
757    auto varInGlobalClassScope = Program()->GlobalClassScope()->FindLocal(localName, ResolveBindingOptions::ALL);
758    auto previouslyImportedVariable = TopScope()->FindLocal(localName, ResolveBindingOptions::ALL);
759    if (DetectNameConflict(localName, var, varInGlobalClassScope, importPath, true) ||
760        DetectNameConflict(localName, var, previouslyImportedVariable, importPath, false)) {
761        return true;
762    }
763
764    // The first part of the condition will be true, if something was given an alias when exported, but we try
765    // to import it using its original name.
766    if (nameToSearchFor == imported && var->Declaration()->Node()->HasExportAlias()) {
767        ThrowError(specifier->Start(), "Cannot find imported element '" + imported.Mutf8() + "'");
768    }
769
770    InsertForeignBinding(specifier, import, localName, var);
771    return true;
772}
773
774varbinder::Variable *ETSBinder::FindStaticBinding(const ArenaVector<parser::Program *> &recordRes,
775                                                  const ir::StringLiteral *const importPath)
776{
777    auto predicateFunc = [](const auto &item) { return item.second->Declaration()->Node()->IsDefaultExported(); };
778    const auto &staticMethodBindings = recordRes.front()->GlobalClassScope()->StaticMethodScope()->Bindings();
779    auto result = std::find_if(staticMethodBindings.begin(), staticMethodBindings.end(), predicateFunc);
780    if (result == staticMethodBindings.end()) {
781        const auto &staticFieldBindings = recordRes.front()->GlobalClassScope()->StaticFieldScope()->Bindings();
782        result = std::find_if(staticFieldBindings.begin(), staticFieldBindings.end(), predicateFunc);
783        if (result == staticFieldBindings.end()) {
784            ThrowError(importPath->Start(), "Cannot find default imported element in the target");
785        }
786    }
787    return result->second;
788}
789
790ArenaVector<parser::Program *> ETSBinder::GetExternalProgram(const util::StringView &sourceName,
791                                                             const ir::StringLiteral *importPath)
792{
793    // NOTE: quick fix to make sure not to look for the global program among the external sources
794    if (sourceName.Compare(globalRecordTable_.Program()->AbsoluteName()) == 0) {
795        ArenaVector<parser::Program *> mainModule(Allocator()->Adapter());
796        mainModule.emplace_back(globalRecordTable_.Program());
797        return mainModule;
798    }
799
800    auto programList = GetProgramList(sourceName);
801    if (programList.empty()) {
802        // NOTE(rsipka): it is not clear that an error should be thrown in these cases
803        if (ark::os::file::File::IsDirectory(sourceName.Mutf8())) {
804            ThrowError(importPath->Start(),
805                       "Cannot find index.[sts|ts] or package module in folder: " + importPath->Str().Mutf8());
806        }
807
808        ThrowError(importPath->Start(), "Cannot find import: " + importPath->Str().Mutf8());
809    }
810
811    return programList;
812}
813
814void ETSBinder::AddSpecifiersToTopBindings(ir::AstNode *const specifier, const ir::ETSImportDeclaration *const import,
815                                           ir::StringLiteral *path,
816                                           std::vector<ir::ETSImportDeclaration *> viewedReExport)
817{
818    const ir::StringLiteral *const importPath = path;
819
820    if (import->IsPureDynamic()) {
821        AddDynamicSpecifiersToTopBindings(specifier, import);
822        return;
823    }
824
825    const util::StringView sourceName = import->ResolvedSource()->Str();
826
827    auto record = GetExternalProgram(sourceName, importPath);
828    const auto *const importProgram = record.front();
829    const auto *const importGlobalScope = importProgram->GlobalScope();
830    const auto &globalBindings = importGlobalScope->Bindings();
831
832    if (AddImportNamespaceSpecifiersToTopBindings(specifier, globalBindings, importProgram, importGlobalScope,
833                                                  import) ||
834        AddImportSpecifiersToTopBindings(specifier, globalBindings, import, record, std::move(viewedReExport))) {
835        return;
836    }
837
838    ASSERT(specifier->IsImportDefaultSpecifier());
839    auto predicateFunc = [](const auto &item) { return item.second->Declaration()->Node()->IsDefaultExported(); };
840
841    auto item = std::find_if(globalBindings.begin(), globalBindings.end(), predicateFunc);
842    if (item == globalBindings.end()) {
843        auto var = FindStaticBinding(record, importPath);
844        specifier->AsImportDefaultSpecifier()->Local()->SetVariable(var);
845        InsertForeignBinding(specifier, import, specifier->AsImportDefaultSpecifier()->Local()->Name(), var);
846        return;
847    }
848
849    specifier->AsImportDefaultSpecifier()->Local()->SetVariable(item->second);
850    InsertForeignBinding(specifier, import, specifier->AsImportDefaultSpecifier()->Local()->Name(), item->second);
851}
852
853void ETSBinder::HandleCustomNodes(ir::AstNode *childNode)
854{
855    switch (childNode->Type()) {
856        case ir::AstNodeType::ETS_TYPE_REFERENCE: {
857            auto *typeRef = childNode->AsETSTypeReference();
858            auto *baseName = typeRef->BaseName();
859            ASSERT(baseName->IsReference());
860            // We allow to resolve following types in pure dynamic mode:
861            // import * as I from "@dynamic"
862            // let x : I.X.Y
863            bool allowDynamicNamespaces = typeRef->Part()->Name() != baseName;
864            LookupTypeReference(baseName, allowDynamicNamespaces);
865            LookupTypeArgumentReferences(typeRef);
866            break;
867        }
868        case ir::AstNodeType::TS_INTERFACE_DECLARATION: {
869            BuildInterfaceDeclaration(childNode->AsTSInterfaceDeclaration());
870            break;
871        }
872        case ir::AstNodeType::TS_ENUM_DECLARATION: {
873            ResolveEnumDeclaration(childNode->AsTSEnumDeclaration());
874            break;
875        }
876        case ir::AstNodeType::EXPORT_NAMED_DECLARATION: {
877            break;
878        }
879        case ir::AstNodeType::ETS_IMPORT_DECLARATION: {
880            BuildImportDeclaration(childNode->AsETSImportDeclaration());
881            break;
882        }
883        case ir::AstNodeType::MEMBER_EXPRESSION: {
884            BuildMemberExpression(childNode->AsMemberExpression());
885            break;
886        }
887        case ir::AstNodeType::METHOD_DEFINITION: {
888            BuildMethodDefinition(childNode->AsMethodDefinition());
889            break;
890        }
891        case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: {
892            BuildETSNewClassInstanceExpression(childNode->AsETSNewClassInstanceExpression());
893            break;
894        }
895        case ir::AstNodeType::ETS_FUNCTION_TYPE: {
896            BuildSignatureDeclarationBaseParams(childNode);
897            break;
898        }
899        default: {
900            ResolveReferences(childNode);
901            break;
902        }
903    }
904}
905
906bool ETSBinder::BuildInternalName(ir::ScriptFunction *scriptFunc)
907{
908    const bool isExternal = recordTable_->IsExternal();
909    if (isExternal) {
910        scriptFunc->AddFlag(ir::ScriptFunctionFlags::EXTERNAL);
911    }
912
913    if (scriptFunc->IsArrow()) {
914        return true;
915    }
916
917    auto *funcScope = scriptFunc->Scope();
918    funcScope->BindName(recordTable_->RecordName());
919
920    bool compilable = scriptFunc->Body() != nullptr && !isExternal;
921    if (!compilable) {
922        recordTable_->Signatures().push_back(funcScope);
923    }
924
925    return compilable;
926}
927
928bool ETSBinder::BuildInternalNameWithCustomRecordTable(ir::ScriptFunction *const scriptFunc,
929                                                       RecordTable *const recordTable)
930{
931    const bool isExternal = recordTable->IsExternal();
932    if (isExternal) {
933        scriptFunc->AddFlag(ir::ScriptFunctionFlags::EXTERNAL);
934    }
935
936    if (scriptFunc->IsArrow()) {
937        return true;
938    }
939
940    auto *const funcScope = scriptFunc->Scope();
941    funcScope->BindName(recordTable->RecordName());
942
943    const bool compilable = scriptFunc->Body() != nullptr && !isExternal;
944    if (!compilable) {
945        recordTable->Signatures().push_back(funcScope);
946    }
947
948    return compilable;
949}
950
951void ETSBinder::AddCompilableFunction(ir::ScriptFunction *func)
952{
953    if (func->IsArrow() || func->IsAsyncFunc()) {
954        return;
955    }
956
957    AddCompilableFunctionScope(func->Scope());
958}
959
960void ETSBinder::BuildFunctionName(const ir::ScriptFunction *func) const
961{
962    auto *funcScope = func->Scope();
963
964    std::stringstream ss;
965    ASSERT(func->IsArrow() || !funcScope->Name().Empty());
966    ss << (func->IsExternalOverload() ? funcScope->InternalName() : funcScope->Name())
967       << compiler::Signatures::METHOD_SEPARATOR;
968
969    const auto *signature = func->Signature();
970
971    if (func->IsStaticBlock()) {
972        ss << compiler::Signatures::CCTOR;
973    } else if (func->IsConstructor()) {
974        ss << compiler::Signatures::CTOR;
975    } else {
976        if (func->IsGetter()) {
977            ss << compiler::Signatures::GETTER_BEGIN;
978        } else if (func->IsSetter()) {
979            ss << compiler::Signatures::SETTER_BEGIN;
980        }
981        ss << util::Helpers::FunctionName(Allocator(), func);
982    }
983
984    signature->ToAssemblerType(ss);
985
986    util::UString internalName(ss.str(), Allocator());
987    funcScope->BindInternalName(internalName.View());
988}
989
990void ETSBinder::InitImplicitThisParam()
991{
992    thisParam_ = Allocator()->New<ir::Identifier>("this", Allocator());
993}
994
995void ETSBinder::BuildProgram()
996{
997    for (auto &[_, extPrograms] : Program()->ExternalSources()) {
998        (void)_;
999        for (auto *extProg : extPrograms) {
1000            BuildExternalProgram(extProg);
1001        }
1002    }
1003
1004    for (auto *defaultImport : defaultImports_) {
1005        BuildImportDeclaration(defaultImport);
1006    }
1007
1008    auto &stmts = Program()->Ast()->Statements();
1009    const auto etsGlobal = std::find_if(stmts.begin(), stmts.end(), [](const ir::Statement *stmt) {
1010        return stmt->IsClassDeclaration() &&
1011               stmt->AsClassDeclaration()->Definition()->Ident()->Name().Is(compiler::Signatures::ETS_GLOBAL);
1012    });
1013    if (etsGlobal != stmts.end()) {
1014        const auto begin = std::find_if(stmts.rbegin(), stmts.rend(), [](const ir::Statement *stmt) {
1015                               return stmt->IsETSImportDeclaration() || stmt->IsETSPackageDeclaration();
1016                           }).base();
1017
1018        const auto index = std::distance(begin, etsGlobal);
1019        std::rotate(begin, begin + index, begin + index + 1);
1020    }
1021
1022    for (auto *stmt : stmts) {
1023        ResolveReference(stmt);
1024    }
1025}
1026
1027void ETSBinder::BuildExternalProgram(parser::Program *extProgram)
1028{
1029    auto *savedProgram = Program();
1030    auto *savedRecordTable = recordTable_;
1031    auto *savedTopScope = TopScope();
1032
1033    auto flags = Program()->VarBinder()->IsGenStdLib() ? RecordTableFlags::NONE : RecordTableFlags::EXTERNAL;
1034    auto *extRecordTable = Allocator()->New<RecordTable>(Allocator(), extProgram, flags);
1035    externalRecordTable_.insert({extProgram, extRecordTable});
1036
1037    ResetTopScope(extProgram->GlobalScope());
1038    recordTable_ = extRecordTable;
1039    SetProgram(extProgram);
1040
1041    BuildProgram();
1042
1043    SetProgram(savedProgram);
1044    recordTable_ = savedRecordTable;
1045    ResetTopScope(savedTopScope);
1046}
1047
1048void ETSBinder::BuildETSNewClassInstanceExpression(ir::ETSNewClassInstanceExpression *classInstance)
1049{
1050    BoundContext boundCtx(recordTable_, classInstance->ClassDefinition());
1051    ResolveReference(classInstance->GetTypeRef());
1052
1053    for (auto *arg : classInstance->GetArguments()) {
1054        ResolveReference(arg);
1055    }
1056
1057    if (classInstance->ClassDefinition() == nullptr) {
1058        return;
1059    }
1060
1061    ResolveReference(classInstance->ClassDefinition());
1062}
1063
1064void ETSBinder::BuildImportDeclaration(ir::ETSImportDeclaration *decl)
1065{
1066    if (decl->Source()->Str() == Program()->SourceFile().GetAbsolutePath()) {
1067        return;
1068    }
1069
1070    const auto &specifiers = decl->Specifiers();
1071
1072    for (auto specifier : specifiers) {
1073        AddSpecifiersToTopBindings(specifier, decl, decl->Source());
1074    }
1075}
1076
1077bool ETSBinder::ImportGlobalPropertiesForNotDefaultedExports(varbinder::Variable *var, const util::StringView &name,
1078                                                             const ir::ClassElement *classElement)
1079{
1080    if (var->Declaration()->Node()->IsDefaultExported()) {
1081        return false;
1082    }
1083
1084    auto variable = Program()->GlobalClassScope()->FindLocal(name, ResolveBindingOptions::ALL);
1085
1086    bool isStdLib = util::Helpers::IsStdLib(Program());
1087    if (variable != nullptr && var != variable) {
1088        if (variable->Declaration()->IsFunctionDecl() && var->Declaration()->IsFunctionDecl()) {
1089            AddOverloadFlag(Allocator(), isStdLib, var, variable);
1090            return true;
1091        }
1092
1093        ThrowError(classElement->Id()->Start(), RedeclarationErrorMessageAssembler(var, variable, name.Utf8()));
1094    }
1095
1096    const auto insRes = TopScope()->InsertForeignBinding(name, var);
1097    if (!(!insRes.second && insRes.first != TopScope()->Bindings().end()) || !(insRes.first->second != var)) {
1098        return true;
1099    }
1100    if (insRes.first->second->Declaration()->IsFunctionDecl() && var->Declaration()->IsFunctionDecl()) {
1101        AddOverloadFlag(Allocator(), isStdLib, var, insRes.first->second);
1102        return true;
1103    }
1104
1105    ThrowError(classElement->Id()->Start(), RedeclarationErrorMessageAssembler(var, insRes.first->second, name.Utf8()));
1106}
1107
1108void ETSBinder::ImportGlobalProperties(const ir::ClassDefinition *const classDef)
1109{
1110    const auto scopeCtx = LexicalScope<ClassScope>::Enter(this, classDef->Scope()->AsClassScope());
1111
1112    for (const auto *const prop : classDef->Body()) {
1113        const auto *const classElement = prop->AsClassElement();
1114
1115        if (classElement->IsClassStaticBlock()) {
1116            continue;
1117        }
1118
1119        ASSERT(classElement->IsStatic());
1120        const auto &name = classElement->Id()->Name();
1121        auto *const var = scopeCtx.GetScope()->FindLocal(name, ResolveBindingOptions::ALL);
1122        ASSERT(var != nullptr);
1123
1124        if (ImportGlobalPropertiesForNotDefaultedExports(var, name, classElement)) {
1125            return;
1126        }
1127    }
1128}
1129
1130const DynamicImportData *ETSBinder::DynamicImportDataForVar(const Variable *var) const
1131{
1132    auto it = dynamicImportVars_.find(var);
1133    if (it == dynamicImportVars_.cend()) {
1134        return nullptr;
1135    }
1136
1137    return &it->second;
1138}
1139
1140bool ETSBinder::IsDynamicModuleVariable(const Variable *var) const
1141{
1142    auto *data = DynamicImportDataForVar(var);
1143    if (data == nullptr) {
1144        return false;
1145    }
1146
1147    return data->specifier->IsImportSpecifier();
1148}
1149
1150bool ETSBinder::IsDynamicNamespaceVariable(const Variable *var) const
1151{
1152    auto *data = DynamicImportDataForVar(var);
1153    if (data == nullptr) {
1154        return false;
1155    }
1156
1157    return data->specifier->IsImportNamespaceSpecifier();
1158}
1159
1160}  // namespace ark::es2panda::varbinder
1161