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 "scope.h"
17
18#include "varbinder/declaration.h"
19#include "util/helpers.h"
20#include "varbinder/tsBinding.h"
21#include "varbinder/variable.h"
22#include "varbinder/variableFlags.h"
23#include "ir/astNode.h"
24#include "ir/expressions/identifier.h"
25#include "ir/statements/classDeclaration.h"
26#include "ir/base/classDefinition.h"
27#include "ir/base/scriptFunction.h"
28#include "ir/base/classProperty.h"
29#include "ir/base/methodDefinition.h"
30#include "ir/module/exportAllDeclaration.h"
31#include "ir/module/exportNamedDeclaration.h"
32#include "ir/module/exportSpecifier.h"
33#include "ir/module/importDeclaration.h"
34#include "ir/expressions/literals/stringLiteral.h"
35#include "ir/expressions/literals/booleanLiteral.h"
36#include "ir/ts/tsInterfaceDeclaration.h"
37#include "ir/ts/tsEnumDeclaration.h"
38#include "ir/ts/tsTypeAliasDeclaration.h"
39#include "compiler/base/literals.h"
40#include "macros.h"
41#include "util/ustring.h"
42#include "generated/signatures.h"
43#include "public/public.h"
44
45namespace ark::es2panda::varbinder {
46VariableScope *Scope::EnclosingVariableScope()
47{
48    Scope *iter = this;
49
50    while (iter != nullptr) {
51        if (iter->IsVariableScope()) {
52            return iter->AsVariableScope();
53        }
54
55        iter = iter->Parent();
56    }
57
58    return nullptr;
59}
60
61const VariableScope *Scope::EnclosingVariableScope() const
62{
63    const auto *iter = this;
64
65    while (iter != nullptr) {
66        if (iter->IsVariableScope()) {
67            return iter->AsVariableScope();
68        }
69
70        iter = iter->Parent();
71    }
72
73    return nullptr;
74}
75
76bool Scope::IsSuperscopeOf(const varbinder::Scope *subscope) const
77{
78    while (subscope != nullptr) {
79        if (subscope == this) {
80            return true;
81        }
82        subscope = ir::AstNode::EnclosingScope(subscope->Node()->Parent());
83    }
84    return false;
85}
86
87// NOTE(psiket): Duplication
88ClassScope *Scope::EnclosingClassScope()
89{
90    Scope *iter = this;
91
92    while (iter != nullptr) {
93        if (iter->IsClassScope()) {
94            return iter->AsClassScope();
95        }
96
97        iter = iter->Parent();
98    }
99
100    return nullptr;
101}
102
103const ClassScope *Scope::EnclosingClassScope() const
104{
105    const auto *iter = this;
106
107    while (iter != nullptr) {
108        if (iter->IsVariableScope()) {
109            return iter->AsClassScope();
110        }
111
112        iter = iter->Parent();
113    }
114
115    return nullptr;
116}
117
118Variable *Scope::FindLocal(const util::StringView &name, ResolveBindingOptions options) const
119{
120    if ((options & ResolveBindingOptions::INTERFACES) != 0) {
121        std::string tsBindingName = varbinder::TSBinding::ToTSBinding(name);
122        util::StringView interfaceNameView(tsBindingName);
123
124        auto res = bindings_.find(interfaceNameView);
125        if (res != bindings_.end()) {
126            return res->second;
127        }
128
129        if ((options & ResolveBindingOptions::BINDINGS) == 0) {
130            return nullptr;
131        }
132    }
133
134    auto res = bindings_.find(name);
135    if (res == bindings_.end()) {
136        return nullptr;
137    }
138
139    return res->second;
140}
141
142Scope::InsertResult Scope::InsertBinding(const util::StringView &name, Variable *const var)
143{
144    ASSERT(var != nullptr);
145    auto insertResult = bindings_.emplace(name, var);
146    if (insertResult.second) {
147        decls_.push_back(var->Declaration());
148    }
149
150    return insertResult;
151}
152
153Scope::InsertResult Scope::TryInsertBinding(const util::StringView &name, Variable *const var)
154{
155    ASSERT(var != nullptr);
156    return bindings_.try_emplace(name, var);
157}
158
159void Scope::MergeBindings(VariableMap const &bindings)
160{
161    for (auto &[k, v] : bindings) {
162        bindings_.try_emplace(k, v);
163    }
164}
165
166Scope::VariableMap::size_type Scope::EraseBinding(const util::StringView &name)
167{
168    if (auto toBeErased = bindings_.find(name);
169        toBeErased == bindings_.end() ||
170        (toBeErased->second->IsLocalVariable() &&
171         toBeErased->second->AsLocalVariable()->Declaration()->Node()->IsImportNamespaceSpecifier())) {
172        return 0;
173    }
174
175    return bindings_.erase(name);
176}
177
178ConstScopeFindResult Scope::FindInGlobal(const util::StringView &name, const ResolveBindingOptions options) const
179{
180    const auto *scopeIter = this;
181    const auto *scopeParent = this->Parent();
182    // One scope below true global is ETSGLOBAL
183    while (scopeParent != nullptr && !scopeParent->IsGlobalScope()) {
184        scopeIter = scopeParent;
185        scopeParent = scopeIter->Parent();
186    }
187
188    auto *resolved = scopeIter->FindLocal(name, options);
189    if (resolved == nullptr && scopeParent != nullptr) {
190        // If the variable cannot be found in the scope of the local ETSGLOBAL, than we still need to check the true
191        // global scope which contains all the imported ETSGLOBALs
192        resolved = scopeParent->FindLocal(name, options);
193    }
194
195    return {name, scopeIter, 0, 0, resolved};
196}
197
198ConstScopeFindResult Scope::FindInFunctionScope(const util::StringView &name, const ResolveBindingOptions options) const
199{
200    const auto *scopeIter = this;
201    while (scopeIter != nullptr && !scopeIter->IsGlobalScope()) {
202        if (!scopeIter->IsClassScope()) {
203            if (auto *const resolved = scopeIter->FindLocal(name, options); resolved != nullptr) {
204                return ConstScopeFindResult(name, scopeIter, 0, 0, resolved);
205            }
206        }
207        scopeIter = scopeIter->Parent();
208    }
209
210    return ConstScopeFindResult(name, scopeIter, 0, 0, nullptr);
211}
212
213ScopeFindResult Scope::Find(const util::StringView &name, const ResolveBindingOptions options)
214{
215    return FindImpl<ScopeFindResult>(this, name, options);
216}
217
218ConstScopeFindResult Scope::Find(const util::StringView &name, const ResolveBindingOptions options) const
219{
220    return FindImpl<ConstScopeFindResult>(this, name, options);
221}
222
223Decl *Scope::FindDecl(const util::StringView &name) const
224{
225    for (auto *it : decls_) {
226        if (it->Name() == name) {
227            return it;
228        }
229    }
230
231    return nullptr;
232}
233
234std::tuple<Scope *, bool> Scope::IterateShadowedVariables(const util::StringView &name, const VariableVisitor &visitor)
235{
236    auto *iter = this;
237
238    while (iter != nullptr) {
239        auto *v = iter->FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS);
240
241        if (v != nullptr && visitor(v)) {
242            return {iter, true};
243        }
244
245        if (iter->IsFunctionVariableScope()) {
246            break;
247        }
248
249        iter = iter->Parent();
250    }
251
252    return {iter, false};
253}
254
255Variable *Scope::AddLocalVar(ArenaAllocator *allocator, Decl *newDecl)
256{
257    auto [scope, shadowed] =
258        IterateShadowedVariables(newDecl->Name(), [](const Variable *v) { return !v->HasFlag(VariableFlags::VAR); });
259
260    if (shadowed) {
261        return nullptr;
262    }
263
264    VariableFlags varFlags = VariableFlags::HOIST_VAR | VariableFlags::LEXICAL_VAR;
265    if (scope->IsGlobalScope()) {
266        return scope->InsertBinding(newDecl->Name(), allocator->New<GlobalVariable>(newDecl, varFlags)).first->second;
267    }
268
269    return scope->PropagateBinding<LocalVariable>(allocator, newDecl->Name(), newDecl, varFlags);
270}
271
272Variable *Scope::AddLocal(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
273                          [[maybe_unused]] ScriptExtension extension)
274{
275    VariableFlags flags = VariableFlags::LEXICAL;
276    switch (newDecl->Type()) {
277        case DeclType::VAR: {
278            return AddLocalVar(allocator, newDecl);
279        }
280        case DeclType::ENUM: {
281            return bindings_.insert({newDecl->Name(), allocator->New<EnumVariable>(newDecl, false)}).first->second;
282        }
283        case DeclType::ENUM_LITERAL: {
284            return bindings_
285                .insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, VariableFlags::ENUM_LITERAL)})
286                .first->second;
287        }
288        case DeclType::INTERFACE: {
289            return bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, VariableFlags::INTERFACE)})
290                .first->second;
291        }
292        case DeclType::CLASS: {
293            return bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, VariableFlags::CLASS)})
294                .first->second;
295        }
296        case DeclType::TYPE_PARAMETER: {
297            return bindings_
298                .insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, VariableFlags::TYPE_PARAMETER)})
299                .first->second;
300        }
301        case DeclType::FUNC: {
302            flags = VariableFlags::HOIST;
303            [[fallthrough]];
304        }
305        default: {
306            if (currentVariable != nullptr) {
307                return nullptr;
308            }
309
310            auto [_, shadowed] = IterateShadowedVariables(
311                newDecl->Name(), [](const Variable *v) { return v->HasFlag(VariableFlags::LEXICAL_VAR); });
312            (void)_;
313
314            if (shadowed) {
315                return nullptr;
316            }
317
318            return bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, flags)}).first->second;
319        }
320    }
321}
322
323void VariableScope::CheckDirectEval(public_lib::Context *context)
324{
325    ASSERT(context);
326    const auto &varMap = Bindings();
327
328    if (!HasFlag(ScopeFlags::NO_REG_STORE) || varMap.empty()) {
329        evalBindings_ = compiler::INVALID_LITERAL_BUFFER_ID;
330        return;
331    }
332
333    size_t constBindings = 0;
334    for (const auto &[name, var] : varMap) {
335        (void)name;
336        var->SetLexical(this);
337
338        if (var->LexicalBound() && var->Declaration()->IsConstDecl()) {
339            constBindings++;
340        }
341    }
342
343    std::vector<compiler::Literal> literals(LexicalSlots() + constBindings, compiler::Literal(util::StringView()));
344
345    if (constBindings == 0U) {
346        for (const auto &[name, variable] : varMap) {
347            if (!variable->LexicalBound()) {
348                continue;
349            }
350
351            literals[variable->AsLocalVariable()->LexIdx()] = compiler::Literal(name);
352        }
353    } else {
354        std::vector<varbinder::Variable *> bindings(LexicalSlots());
355
356        for (const auto &[name, variable] : varMap) {
357            (void)name;
358            if (!variable->LexicalBound()) {
359                continue;
360            }
361
362            bindings[variable->AsLocalVariable()->LexIdx()] = variable;
363        }
364
365        uint32_t buffIndex = 0;
366        for (const auto *variable : bindings) {
367            if (variable == nullptr) {
368                ASSERT(literals[buffIndex].GetString().empty());
369                buffIndex++;
370                continue;
371            }
372            if (variable->Declaration()->IsConstDecl()) {
373                literals[buffIndex++] = compiler::Literal(true);
374            }
375            literals[buffIndex++] = compiler::Literal(variable->Name());
376        }
377    }
378    context->contextLiterals.emplace_back(literals);
379    evalBindings_ = context->contextLiterals.size() - 1;
380}
381
382Variable *ParamScope::AddParam(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags)
383{
384    ASSERT(newDecl->IsParameterDecl());
385
386    if (currentVariable != nullptr) {
387        return nullptr;
388    }
389
390    auto *param = allocator->New<LocalVariable>(newDecl, flags);
391    param->SetScope(this);
392
393    params_.push_back(param);
394    InsertBinding(newDecl->Name(), param);
395    return param;
396}
397
398std::tuple<ParameterDecl *, ir::AstNode *, Variable *> ParamScope::AddParamDecl(ArenaAllocator *allocator,
399                                                                                ir::AstNode *param)
400{
401    const auto [name, pattern] = util::Helpers::ParamName(allocator, param, params_.size());
402
403    auto *decl = NewDecl<ParameterDecl>(allocator, name);
404    auto *var = AddParam(allocator, FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS), decl,
405                         VariableFlags::VAR | VariableFlags::LOCAL);
406
407    if (var == nullptr) {
408        return {decl, param, nullptr};
409    }
410
411    if (!pattern) {
412        decl->BindNode(param);
413        return {decl, nullptr, var};
414    }
415
416    std::vector<ir::Identifier *> bindings = util::Helpers::CollectBindingNames(param);
417
418    for (auto *binding : bindings) {
419        auto *varDecl = NewDecl<VarDecl>(allocator, binding->Name());
420        varDecl->BindNode(binding);
421
422        if (FindLocal(varDecl->Name(), varbinder::ResolveBindingOptions::BINDINGS) != nullptr) {
423            return {decl, binding, nullptr};
424        }
425
426        auto *paramVar = allocator->New<LocalVariable>(varDecl, VariableFlags::VAR | VariableFlags::LOCAL);
427        TryInsertBinding(varDecl->Name(), paramVar);
428    }
429
430    return {decl, nullptr, var};
431}
432
433void FunctionParamScope::BindName(ArenaAllocator *allocator, util::StringView name)
434{
435    nameVar_ = AddDecl<ConstDecl, LocalVariable>(allocator, name, VariableFlags::INITIALIZED);
436    if (!functionScope_->InsertBinding(name, nameVar_).second) {
437        nameVar_ = nullptr;
438    }
439}
440
441Variable *FunctionParamScope::AddBinding([[maybe_unused]] ArenaAllocator *allocator,
442                                         [[maybe_unused]] Variable *currentVariable, [[maybe_unused]] Decl *newDecl,
443                                         [[maybe_unused]] ScriptExtension extension)
444{
445    UNREACHABLE();
446}
447
448Variable *FunctionScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
449                                    [[maybe_unused]] ScriptExtension extension)
450{
451    ir::Identifier *ident {};
452    Variable *var {};
453    switch (newDecl->Type()) {
454        case DeclType::VAR: {
455            return AddVar<LocalVariable>(allocator, currentVariable, newDecl);
456        }
457        case DeclType::FUNC: {
458            return AddFunction<LocalVariable>(allocator, currentVariable, newDecl, extension);
459        }
460        case DeclType::ENUM: {
461            return InsertBinding(newDecl->Name(), allocator->New<EnumVariable>(newDecl, false)).first->second;
462        }
463        case DeclType::ENUM_LITERAL: {
464            return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL);
465        }
466        // NOTE(psiket):Duplication
467        case DeclType::INTERFACE: {
468            ident = newDecl->Node()->AsTSInterfaceDeclaration()->Id();
469            auto interfaceVar = allocator->New<LocalVariable>(newDecl, VariableFlags::INTERFACE);
470            var = InsertBinding(newDecl->Name(), interfaceVar).first->second;
471            break;
472        }
473        case DeclType::CLASS: {
474            ident = newDecl->Node()->AsClassDefinition()->Ident();
475            auto classVar = allocator->New<LocalVariable>(newDecl, VariableFlags::CLASS);
476            var = InsertBinding(newDecl->Name(), classVar).first->second;
477            break;
478        }
479        case DeclType::TYPE_ALIAS: {
480            ident = newDecl->Node()->AsTSTypeAliasDeclaration()->Id();
481            var = typeAliasScope_->AddBinding(allocator, currentVariable, newDecl, extension);
482            break;
483        }
484        default: {
485            return AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
486        }
487    }
488    if (var != nullptr) {
489        var->SetScope(this);
490        if (ident != nullptr) {
491            ident->SetVariable(var);
492        }
493    }
494    return var;
495}
496
497Variable *GlobalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
498                                  [[maybe_unused]] ScriptExtension extension)
499{
500    switch (newDecl->Type()) {
501        case DeclType::VAR: {
502            return AddVar<GlobalVariable>(allocator, currentVariable, newDecl);
503        }
504        case DeclType::FUNC: {
505            return AddFunction<GlobalVariable>(allocator, currentVariable, newDecl, extension);
506        }
507        case DeclType::ENUM: {
508            return InsertBinding(newDecl->Name(), allocator->New<EnumVariable>(newDecl, false)).first->second;
509        }
510        case DeclType::ENUM_LITERAL: {
511            return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL);
512        }
513        case DeclType::INTERFACE: {
514            return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::INTERFACE);
515        }
516        default: {
517            return AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
518        }
519    }
520}
521
522Scope::InsertResult GlobalScope::InsertBinding(const util::StringView &name, Variable *const var)
523{
524    return GlobalScope::InsertImpl(name, var, false, false);
525}
526
527Scope::InsertResult GlobalScope::TryInsertBinding(const util::StringView &name, Variable *const var)
528{
529    const auto insRes = Scope::TryInsertBinding(name, var);
530    if (insRes.second) {
531        [[maybe_unused]] const bool insertSuccess = std::get<1>(foreignBindings_.try_emplace(name, var));
532        ASSERT(insertSuccess);
533    }
534
535    return insRes;
536}
537
538void GlobalScope::MergeBindings([[maybe_unused]] const VariableMap &bindings)
539{
540    UNREACHABLE();
541}
542
543Scope::VariableMap::size_type GlobalScope::EraseBinding(const util::StringView &name)
544{
545    const auto erased = Scope::EraseBinding(name);
546    if (erased != 0) {
547        [[maybe_unused]] const auto erasedForeign = foreignBindings_.erase(name);
548        ASSERT(erasedForeign != 0);
549    }
550
551    return erased;
552}
553
554Scope::InsertResult GlobalScope::InsertForeignBinding(const util::StringView &name, Variable *const var)
555{
556    return GlobalScope::InsertImpl(name, var, true, false);
557}
558
559Scope::InsertResult GlobalScope::InsertImpl(const util::StringView &name, Variable *const var, const bool isForeign,
560                                            const bool isDynamic)
561{
562    if (!isDynamic && isForeign && !var->Declaration()->Name().Is(compiler::Signatures::ETS_GLOBAL)) {
563        const auto *const node = var->Declaration()->Node();
564
565        if (!(node->IsExported() || node->IsDefaultExported() || node->IsExportedType())) {
566            return Scope::InsertResult {Bindings().end(), false};
567        }
568    }
569
570    const auto insRes = Scope::InsertBinding(name, var);
571    if (insRes.second) {
572        [[maybe_unused]] const bool insertSuccess = std::get<1>(foreignBindings_.emplace(name, isForeign));
573        ASSERT(insertSuccess);
574    }
575
576    return insRes;
577}
578
579bool GlobalScope::IsForeignBinding(const util::StringView &name) const
580{
581    // Asserts make sure that the passed in key comes from this scope
582    ASSERT(Bindings().find(name) != Bindings().end());
583    ASSERT(foreignBindings_.find(name) != foreignBindings_.end());
584
585    return foreignBindings_.at(name);
586}
587
588Scope::InsertResult GlobalScope::InsertDynamicBinding(const util::StringView &name, Variable *const var)
589{
590    return InsertImpl(name, var, true, true);
591}
592
593// ModuleScope
594
595Variable *ModuleScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
596                                  [[maybe_unused]] ScriptExtension extension)
597{
598    switch (newDecl->Type()) {
599        case DeclType::VAR: {
600            return AddVar<LocalVariable>(allocator, currentVariable, newDecl);
601        }
602        case DeclType::FUNC: {
603            return AddFunction<LocalVariable>(allocator, currentVariable, newDecl, extension);
604        }
605        case DeclType::ENUM: {
606            return InsertBinding(newDecl->Name(), allocator->New<EnumVariable>(newDecl, false)).first->second;
607        }
608        case DeclType::ENUM_LITERAL: {
609            return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL);
610        }
611        case DeclType::INTERFACE: {
612            return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::INTERFACE);
613        }
614        case DeclType::IMPORT: {
615            return AddImport(allocator, currentVariable, newDecl);
616        }
617        case DeclType::EXPORT: {
618            return allocator->New<LocalVariable>(newDecl, VariableFlags::NONE);
619        }
620        default: {
621            return AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
622        }
623    }
624}
625
626void ModuleScope::AddImportDecl(ir::ImportDeclaration *importDecl, ImportDeclList &&decls)
627{
628    auto res = imports_.emplace_back(importDecl, decls);
629
630    for (auto &decl : res.second) {
631        decl->BindNode(importDecl);
632    }
633}
634
635void ModuleScope::AddExportDecl(ir::AstNode *exportDecl, ExportDecl *decl)
636{
637    decl->BindNode(exportDecl);
638
639    ArenaVector<ExportDecl *> decls(allocator_->Adapter());
640    decls.push_back(decl);
641
642    AddExportDecl(exportDecl, std::move(decls));
643}
644
645void ModuleScope::AddExportDecl(ir::AstNode *exportDecl, ExportDeclList &&decls)
646{
647    auto res = exports_.emplace_back(exportDecl, decls);
648
649    for (auto &decl : res.second) {
650        decl->BindNode(exportDecl);
651    }
652}
653
654Variable *ModuleScope::AddImport(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl)
655{
656    if (currentVariable != nullptr && currentVariable->Declaration()->Type() != DeclType::VAR) {
657        return nullptr;
658    }
659
660    if (newDecl->Node()->IsImportNamespaceSpecifier()) {
661        return InsertBinding(newDecl->Name(), allocator->New<LocalVariable>(newDecl, VariableFlags::READONLY))
662            .first->second;
663    }
664
665    auto *variable = allocator->New<ModuleVariable>(newDecl, VariableFlags::NONE);
666    variable->ExoticName() = newDecl->AsImportDecl()->ImportName();
667    InsertBinding(newDecl->Name(), variable);
668    return variable;
669}
670
671bool ModuleScope::ExportAnalysis()
672{
673    std::set<util::StringView> exportedNames;
674
675    for (const auto &[exportDecl, decls] : exports_) {
676        if (exportDecl->IsExportAllDeclaration()) {
677            const auto *exportAllDecl = exportDecl->AsExportAllDeclaration();
678
679            if (exportAllDecl->Exported() == nullptr) {
680                continue;
681            }
682
683            auto result = exportedNames.insert(exportAllDecl->Exported()->Name());
684            if (!result.second) {
685                return false;
686            }
687
688            continue;
689        }
690
691        if (exportDecl->IsExportNamedDeclaration()) {
692            const auto *exportNamedDecl = exportDecl->AsExportNamedDeclaration();
693
694            if (exportNamedDecl->Source() != nullptr) {
695                continue;
696            }
697        }
698
699        for (const auto *decl : decls) {
700            varbinder::Variable *variable = FindLocal(decl->LocalName(), varbinder::ResolveBindingOptions::BINDINGS);
701
702            if (variable == nullptr) {
703                continue;
704            }
705
706            auto result = exportedNames.insert(decl->ExportName());
707            if (!result.second) {
708                return false;
709            }
710
711            if (!variable->IsModuleVariable()) {
712                variable->AddFlag(VariableFlags::LOCAL_EXPORT);
713                localExports_.insert({variable, decl->ExportName()});
714            }
715        }
716    }
717
718    return true;
719}
720
721Variable *FunctionScope::FindLocal(const util::StringView &name, ResolveBindingOptions options) const
722{
723    if ((options & ResolveBindingOptions::TYPE_ALIASES) != 0) {
724        auto found = typeAliasScope_->Bindings().find(name);
725        if (found != typeAliasScope_->Bindings().end()) {
726            return found->second;
727        }
728    }
729
730    if ((options & ResolveBindingOptions::ALL_NON_TYPE) == 0) {
731        return nullptr;
732    }
733
734    if ((options & ResolveBindingOptions::INTERFACES) != 0) {
735        std::string tsBindingName = varbinder::TSBinding::ToTSBinding(name);
736        util::StringView interfaceNameView(tsBindingName);
737
738        auto res = Bindings().find(interfaceNameView);
739        if (res != Bindings().end()) {
740            return res->second;
741        }
742
743        if ((options & ResolveBindingOptions::BINDINGS) == 0) {
744            return nullptr;
745        }
746    }
747
748    auto res = Bindings().find(name);
749    if (res == Bindings().end()) {
750        return nullptr;
751    }
752
753    return res->second;
754}
755
756// LocalScope
757
758Variable *LocalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
759                                 [[maybe_unused]] ScriptExtension extension)
760{
761    return AddLocal(allocator, currentVariable, newDecl, extension);
762}
763
764Variable *LocalScopeWithTypeAlias::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
765                                              [[maybe_unused]] ScriptExtension extension)
766{
767    if (newDecl->IsTypeAliasDecl()) {
768        auto *ident = newDecl->Node()->AsTSTypeAliasDeclaration()->Id();
769        auto *var = typeAliasScope_->AddBinding(allocator, currentVariable, newDecl, extension);
770        if (var != nullptr) {
771            var->SetScope(this);
772            if (ident != nullptr) {
773                ident->SetVariable(var);
774            }
775        }
776        return var;
777    }
778    return AddLocal(allocator, currentVariable, newDecl, extension);
779}
780
781Variable *LocalScopeWithTypeAlias::FindLocal(const util::StringView &name, ResolveBindingOptions options) const
782{
783    if ((options & ResolveBindingOptions::TYPE_ALIASES) != 0) {
784        auto found = typeAliasScope_->Bindings().find(name);
785        if (found != typeAliasScope_->Bindings().end()) {
786            return found->second;
787        }
788    }
789
790    if ((options & ResolveBindingOptions::ALL_NON_TYPE) == 0) {
791        return nullptr;
792    }
793
794    if ((options & ResolveBindingOptions::INTERFACES) != 0) {
795        std::string tsBindingName = varbinder::TSBinding::ToTSBinding(name);
796        util::StringView interfaceNameView(tsBindingName);
797
798        auto res = Bindings().find(interfaceNameView);
799        if (res != Bindings().end()) {
800            return res->second;
801        }
802
803        if ((options & ResolveBindingOptions::BINDINGS) == 0) {
804            return nullptr;
805        }
806    }
807
808    auto res = Bindings().find(name);
809    if (res == Bindings().end()) {
810        return nullptr;
811    }
812
813    return res->second;
814}
815
816Variable *ClassScope::FindLocal(const util::StringView &name, ResolveBindingOptions options) const
817{
818    if ((options & ResolveBindingOptions::TYPE_ALIASES) != 0) {
819        auto found = TypeAliasScope()->Bindings().find(name);
820        if (found != TypeAliasScope()->Bindings().end()) {
821            return found->second;
822        }
823    }
824
825    if ((options & ResolveBindingOptions::VARIABLES) != 0) {
826        auto found = instanceFieldScope_->Bindings().find(name);
827        if (found != instanceFieldScope_->Bindings().end()) {
828            return found->second;
829        }
830    }
831
832    if ((options & ResolveBindingOptions::STATIC_VARIABLES) != 0) {
833        auto found = staticFieldScope_->Bindings().find(name);
834        if (found != staticFieldScope_->Bindings().end()) {
835            return found->second;
836        }
837    }
838
839    if ((options & ResolveBindingOptions::DECLARATION) != 0) {
840        auto found = instanceDeclScope_->Bindings().find(name);
841        if (found != instanceDeclScope_->Bindings().end()) {
842            return found->second;
843        }
844    }
845
846    if ((options & ResolveBindingOptions::STATIC_DECLARATION) != 0) {
847        auto found = staticDeclScope_->Bindings().find(name);
848        if (found != staticDeclScope_->Bindings().end()) {
849            return found->second;
850        }
851    }
852
853    if ((options & ResolveBindingOptions::METHODS) != 0) {
854        auto found = instanceMethodScope_->Bindings().find(name);
855        if (found != instanceMethodScope_->Bindings().end()) {
856            return found->second;
857        }
858    }
859
860    if ((options & ResolveBindingOptions::STATIC_METHODS) != 0) {
861        auto found = staticMethodScope_->Bindings().find(name);
862        if (found != staticMethodScope_->Bindings().end()) {
863            return found->second;
864        }
865    }
866
867    return nullptr;
868}
869
870void ClassScope::SetBindingProps(Decl *newDecl, BindingProps *props, bool isStatic)
871{
872    switch (newDecl->Type()) {
873        case DeclType::CONST:
874        case DeclType::READONLY:
875        case DeclType::LET: {
876            props->SetBindingProps(VariableFlags::PROPERTY, newDecl->Node()->AsClassProperty()->Id(),
877                                   isStatic ? staticFieldScope_ : instanceFieldScope_);
878            break;
879        }
880        case DeclType::INTERFACE: {
881            props->SetBindingProps(VariableFlags::INTERFACE, newDecl->Node()->AsTSInterfaceDeclaration()->Id(),
882                                   isStatic ? staticDeclScope_ : instanceDeclScope_);
883            break;
884        }
885        case DeclType::CLASS: {
886            props->SetBindingProps(VariableFlags::CLASS, newDecl->Node()->AsClassDefinition()->Ident(),
887                                   isStatic ? staticDeclScope_ : instanceDeclScope_);
888            break;
889        }
890        case DeclType::ENUM_LITERAL: {
891            props->SetBindingProps(VariableFlags::ENUM_LITERAL, newDecl->Node()->AsTSEnumDeclaration()->Key(),
892                                   isStatic ? staticDeclScope_ : instanceDeclScope_);
893            break;
894        }
895        case DeclType::TYPE_ALIAS: {
896            props->SetBindingProps(VariableFlags::TYPE_ALIAS, newDecl->Node()->AsTSTypeAliasDeclaration()->Id(),
897                                   TypeAliasScope());
898            break;
899        }
900        default: {
901            UNREACHABLE();
902            break;
903        }
904    }
905}
906
907Variable *ClassScope::AddBinding(ArenaAllocator *allocator, [[maybe_unused]] Variable *currentVariable, Decl *newDecl,
908                                 [[maybe_unused]] ScriptExtension extension)
909{
910    bool isStatic = newDecl->Node()->IsStatic();
911    BindingProps props;
912
913    if (isStatic) {
914        props.SetFlagsType(VariableFlags::STATIC);
915    }
916
917    SetBindingProps(newDecl, &props, isStatic);
918
919    auto options = newDecl->Type() != DeclType::TYPE_ALIAS ? ResolveBindingOptions::ALL_NON_TYPE
920                                                           : ResolveBindingOptions::TYPE_ALIASES;
921
922    const auto *foundVar = FindLocal(newDecl->Name(), options);
923    if (foundVar != nullptr) {
924        if (!newDecl->IsLetOrConstDecl()) {
925            return nullptr;
926        }
927
928        foundVar = FindLocal(newDecl->Name(),
929                             ResolveBindingOptions::ALL ^ (isStatic ? ResolveBindingOptions::VARIABLES
930                                                                    : ResolveBindingOptions::STATIC_VARIABLES));
931        if (foundVar != nullptr) {
932            return nullptr;
933        }
934    }
935
936    auto *var = props.GetTargetScope()->AddBinding(allocator, nullptr, newDecl, extension);
937    if (var == nullptr) {
938        return nullptr;
939    }
940
941    if (auto node = newDecl->Node();
942        node->IsStatement() &&
943        (node->AsStatement()->IsMethodDefinition() || node->IsClassProperty() || node->IsClassStaticBlock()) &&
944        node->AsStatement()->AsClassElement()->Value() != nullptr) {
945        props.SetFlagsType(VariableFlags::INITIALIZED);
946    }
947
948    var->SetScope(this);
949    var->AddFlag(props.GetFlags());
950
951    if (props.GetIdent() != nullptr) {
952        props.GetIdent()->SetVariable(var);
953    }
954
955    return var;
956}
957
958void LoopDeclarationScope::ConvertToVariableScope(ArenaAllocator *allocator)
959{
960    if (NeedLexEnv()) {
961        return;
962    }
963
964    const auto &bindings = Bindings();
965    for (auto &[name, var] : bindings) {
966        if (!var->LexicalBound() || !var->Declaration()->IsLetOrConstDecl()) {
967            continue;
968        }
969
970        slotIndex_++;
971        loopType_ = ScopeType::LOOP_DECL;
972        auto *copiedVar = var->AsLocalVariable()->Copy(allocator, var->Declaration());
973        copiedVar->AddFlag(VariableFlags::INITIALIZED | VariableFlags::PER_ITERATION);
974        var->AddFlag(VariableFlags::LOOP_DECL);
975        loopScope_->InsertBinding(name, copiedVar);
976    }
977
978    if (loopType_ == ScopeType::LOOP_DECL) {
979        auto *parentVarScope = Parent()->EnclosingVariableScope();
980        slotIndex_ = std::max(slotIndex_, parentVarScope->LexicalSlots());
981        evalBindings_ = parentVarScope->EvalBindings();
982        initScope_ = allocator->New<LocalScope>(allocator, Parent());
983        initScope_->BindNode(Node());
984        initScope_->MergeBindings(bindings);
985    }
986}
987
988void LoopScope::ConvertToVariableScope(ArenaAllocator *allocator)
989{
990    declScope_->ConvertToVariableScope(allocator);
991
992    if (loopType_ != ScopeType::LOCAL) {
993        return;
994    }
995
996    for (const auto &[_, var] : Bindings()) {
997        (void)_;
998        if (var->LexicalBound() && var->Declaration()->IsLetDecl()) {
999            ASSERT(declScope_->NeedLexEnv());
1000            loopType_ = ScopeType::LOOP;
1001            break;
1002        }
1003    }
1004
1005    if (loopType_ == ScopeType::LOOP) {
1006        slotIndex_ = std::max(slotIndex_, declScope_->LexicalSlots());
1007        evalBindings_ = declScope_->EvalBindings();
1008    }
1009}
1010
1011Variable *CatchParamScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
1012                                      [[maybe_unused]] ScriptExtension extension)
1013{
1014    return AddParam(allocator, currentVariable, newDecl, VariableFlags::INITIALIZED);
1015}
1016
1017Variable *CatchScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
1018                                 [[maybe_unused]] ScriptExtension extension)
1019{
1020    if (!newDecl->IsVarDecl() &&
1021        (paramScope_->FindLocal(newDecl->Name(), varbinder::ResolveBindingOptions::BINDINGS) != nullptr)) {
1022        return nullptr;
1023    }
1024
1025    if (newDecl->IsTypeAliasDecl()) {
1026        auto *ident = newDecl->Node()->AsTSTypeAliasDeclaration()->Id();
1027        auto *var = TypeAliasScope()->AddBinding(allocator, currentVariable, newDecl, extension);
1028        if (var != nullptr) {
1029            var->SetScope(this);
1030            if (ident != nullptr) {
1031                ident->SetVariable(var);
1032            }
1033        }
1034        return var;
1035    }
1036
1037    return AddLocal(allocator, currentVariable, newDecl, extension);
1038}
1039}  // namespace ark::es2panda::varbinder
1040