1/**
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "scope.h"
17
18#include <binder/declaration.h>
19#include <util/helpers.h>
20#include <binder/tsBinding.h>
21#include <binder/variable.h>
22#include <binder/variableFlags.h>
23#include <ir/astNode.h>
24#include <ir/base/scriptFunction.h>
25#include <ir/base/classDefinition.h>
26#include <ir/expressions/identifier.h>
27#include <ir/expressions/literals/stringLiteral.h>
28#include <ir/expressions/privateIdentifier.h>
29#include <ir/module/exportAllDeclaration.h>
30#include <ir/module/exportNamedDeclaration.h>
31#include <ir/module/exportSpecifier.h>
32#include <ir/module/importDeclaration.h>
33#include <ir/ts/tsModuleDeclaration.h>
34#include <ir/ts/tsEnumDeclaration.h>
35#include <macros.h>
36#include <util/concurrent.h>
37#include <util/ustring.h>
38
39#include <algorithm>
40#include <sstream>
41
42namespace panda::es2panda::binder {
43
44VariableScope *Scope::EnclosingVariableScope()
45{
46    Scope *iter = this;
47
48    while (iter) {
49        if (iter->IsVariableScope()) {
50            return iter->AsVariableScope();
51        }
52
53        iter = iter->Parent();
54    }
55
56    return nullptr;
57}
58
59FunctionScope *Scope::EnclosingFunctionVariableScope()
60{
61    Scope *iter = this;
62    while (iter) {
63        if (iter->IsFunctionVariableScope()) {
64            return iter->AsFunctionVariableScope();
65        }
66
67        iter = iter->Parent();
68    }
69
70    return nullptr;
71}
72
73Variable *Scope::FindLocal(const util::StringView &name, ResolveBindingOptions options) const
74{
75    if (options & ResolveBindingOptions::INTERFACES) {
76        const std::string &interfaceName = binder::TSBinding::ToTSBinding(name);
77
78        auto res = bindings_.find(util::StringView{interfaceName});
79        if (res != bindings_.end()) {
80            return res->second;
81        }
82
83        if (!(options & ResolveBindingOptions::BINDINGS)) {
84            return nullptr;
85        }
86    }
87
88    auto res = bindings_.find(name);
89    if (res == bindings_.end()) {
90        return nullptr;
91    }
92
93    return res->second;
94}
95
96void Scope::CalculateLevelInCorrespondingFunctionScope(const FunctionParamScope *scope, uint32_t &lexLevel,
97                                                       uint32_t &sendableLevel) const
98{
99    auto *funcVariableScope = scope->GetFunctionScope();
100    // we may only have function param scope without function scope in TS here
101    if (funcVariableScope == nullptr) {
102        return;
103    }
104
105    if (funcVariableScope->NeedLexEnv()) {
106        lexLevel++;
107    }
108
109    if (funcVariableScope->NeedSendableEnv()) {
110        sendableLevel++;
111    }
112}
113
114ScopeFindResult Scope::Find(const util::StringView &name, ResolveBindingOptions options) const
115{
116    uint32_t level = 0;
117    uint32_t lexLevel = 0;
118    uint32_t sendableLevel = 0;
119    const auto *iter = this;
120    ir::ScriptFunction *concurrentFunc = nullptr;
121    // If the first scope is functionParamScope, it means its corresponding functionScope is not
122    // iterated. so by default we set prevScopeNotFunctionScope as true so under such case,
123    // functionScopeNotIterated will be true.
124    bool prevScopeNotFunctionScope = true;
125    bool lexical = false;
126
127    while (iter != nullptr) {
128        bool functionScopeNotIterated = iter->IsFunctionParamScope() && prevScopeNotFunctionScope;
129        Variable *v = iter->FindLocal(name, options);
130
131        if (v != nullptr) {
132            return {name, const_cast<Scope *>(iter), level, lexLevel, sendableLevel, v, concurrentFunc};
133        }
134
135        if (iter->IsFunctionVariableScope() && !lexical) {
136            lexical = true;
137        }
138
139        if (iter->IsFunctionScope() && !concurrentFunc) {
140            if (iter->Node()->AsScriptFunction()->IsConcurrent()) {
141                concurrentFunc = const_cast<ir::ScriptFunction *>(iter->Node()->AsScriptFunction());
142            }
143        }
144
145        if (iter->IsVariableScope()) {
146            if (lexical) {
147                level++;
148            }
149
150            if (iter->AsVariableScope()->NeedLexEnv()) {
151                lexLevel++;
152            }
153
154            if (iter->AsVariableScope()->NeedSendableEnv()) {
155                sendableLevel++;
156            }
157        } else if (functionScopeNotIterated) {
158            level++;
159            CalculateLevelInCorrespondingFunctionScope(iter->AsFunctionParamScope(), lexLevel, sendableLevel);
160        }
161
162        prevScopeNotFunctionScope = !iter->IsFunctionVariableScope();
163        iter = iter->Parent();
164    }
165
166    return {name, nullptr, 0, 0, 0, nullptr, concurrentFunc};
167}
168
169std::pair<uint32_t, uint32_t> Scope::Find(const ir::Expression *expr, bool onlyLevel) const
170{
171    uint32_t lexLevel = 0;
172    const auto *iter = this;
173
174    while (iter != nullptr) {
175        if (iter->Type() == ScopeType::CLASS) {
176            if (onlyLevel) {
177                return {lexLevel, 0};
178            }
179            return {lexLevel, iter->AsClassScope()->GetSlot(expr)};
180        }
181
182        if (iter->IsVariableScope()) {
183            if (iter->AsVariableScope()->NeedLexEnv()) {
184                lexLevel++;
185            }
186        }
187        iter = iter->Parent();
188    }
189
190    UNREACHABLE();
191}
192
193bool ClassScope::IsVariableScope() const
194{
195    // sendable class does not need a lexical env, handle it's scope as a non-variable scope
196    return !node_->AsClassDefinition()->IsSendable();
197}
198
199Result ClassScope::GetPrivateProperty(const util::StringView &name, bool isSetter) const
200{
201    if (name.Is("#method")) {
202        return {instanceMethodValidation_, false, false, false, false, 0};
203    }
204
205    uint32_t slot{0};
206    bool setter{false};
207    bool getter{false};
208
209    if (privateNames_.find(name) != privateNames_.end()) {
210        slot = privateNames_.find(name)->second;
211    } else {
212        auto accessor = isSetter ? privateSetters_ : privateGetters_;
213        auto unexpectedAccessor = isSetter ? privateGetters_ : privateSetters_;
214
215        if (accessor.find(name) != accessor.end()) {
216            setter = isSetter;
217            getter = !setter;
218            slot = accessor.find(name)->second;
219        } else {
220            getter = isSetter;
221            setter = !getter;
222            slot = unexpectedAccessor.find(name)->second;
223        }
224    }
225
226    uint32_t validateMethodSlot{0};
227
228    if (IsMethod(slot)) {
229        validateMethodSlot = IsStaticMethod(slot) ? staticMethodValidation_ : instanceMethodValidation_;
230    }
231
232    return {slot, IsMethod(slot), IsStaticMethod(slot), getter, setter, validateMethodSlot};
233}
234
235void ClassScope::AddPrivateName(std::vector<const ir::Statement *> privateProperties, uint32_t privateFieldCnt,
236                                uint32_t instancePrivateMethodCnt, uint32_t staticPrivateMethodCnt)
237{
238    privateFieldCnt_ = privateFieldCnt;
239    instancePrivateMethodStartSlot_ = slotIndex_ + privateFieldCnt_;
240    staticPrivateMethodStartSlot_ = instancePrivateMethodStartSlot_ + instancePrivateMethodCnt;
241    uint32_t instancePrivateMethodSlot = instancePrivateMethodStartSlot_;
242    uint32_t staticPrivateMethodSlot = staticPrivateMethodStartSlot_;
243    for (const auto *stmt : privateProperties) {
244        if (stmt->IsClassProperty()) {
245            privateNames_[stmt->AsClassProperty()->Key()->AsPrivateIdentifier()->Name()] = slotIndex_++;
246            continue;
247        }
248        ASSERT(stmt->IsMethodDefinition());
249        auto *methodDef = stmt->AsMethodDefinition();
250        uint32_t *start = methodDef->IsStatic() ? &staticPrivateMethodSlot : &instancePrivateMethodSlot;
251        auto name = methodDef->Key()->AsPrivateIdentifier()->Name();
252        switch (methodDef->Kind()) {
253            case ir::MethodDefinitionKind::GET: {
254                privateGetters_[name] =  (*start)++;
255                continue;
256            }
257            case ir::MethodDefinitionKind::SET: {
258                privateSetters_[name] =  (*start)++;
259                continue;
260            }
261            default: {
262                privateNames_[name]=  (*start)++;
263                continue;
264            }
265        }
266    }
267    slotIndex_ = staticPrivateMethodSlot;
268    privateMethodEndSlot_ = slotIndex_;
269    if (instancePrivateMethodCnt != 0) {
270        instanceMethodValidation_ = slotIndex_++;
271    }
272
273    if (staticPrivateMethodCnt != 0) {
274        staticMethodValidation_ = slotIndex_++;
275    }
276}
277
278util::StringView ClassScope::GetSelfScopeName()
279{
280    if (hasSelfScopeNameSet_) {
281        return selfScopeName_;
282    }
283
284    std::stringstream scopeName;
285
286    if (node_ && node_->IsClassDefinition() && node_->AsClassDefinition()->Ident()) {
287        util::StringView selfName = node_->AsClassDefinition()->Ident()->Name();
288        scopeName << selfName;
289        return util::UString(scopeName.str(), allocator_).View();
290    }
291    // To get the name for anonymous class
292    if (node_ && node_->Parent() && node_->Parent()->Parent()) {
293        scopeName << util::Helpers::GetName(allocator_, node_->Parent()->Parent());
294        return util::UString(scopeName.str(), allocator_).View();
295    }
296
297    return util::UString(scopeName.str(), allocator_).View();
298}
299
300util::StringView ClassScope::GetScopeTag()
301{
302    return util::UString(util::Helpers::CLASS_SCOPE_TAG.data(), allocator_).View();
303}
304
305PrivateNameFindResult Scope::FindPrivateName(const util::StringView &name, bool isSetter) const
306{
307    uint32_t lexLevel = 0;
308    const auto *iter = this;
309
310    while (iter != nullptr) {
311        if (iter->Type() == ScopeType::CLASS) {
312            const auto *classScope = iter->AsClassScope();
313            if (name.Is("#method") || classScope->HasPrivateName(name)) {
314                return {lexLevel, classScope->GetPrivateProperty(name, isSetter)};
315            }
316        }
317
318        if (iter->IsVariableScope()) {
319            if (iter->AsVariableScope()->NeedLexEnv()) {
320                lexLevel++;
321            }
322        }
323        iter = iter->Parent();
324    }
325
326    UNREACHABLE();
327}
328
329Decl *Scope::FindDecl(const util::StringView &name) const
330{
331    for (auto *it : decls_) {
332        if (it->Name() == name) {
333            return it;
334        }
335    }
336
337    return nullptr;
338}
339
340bool Scope::HasVarDecl(const util::StringView &name) const
341{
342    for (auto *it : decls_) {
343        if (it->Name() == name && it->IsVarDecl()) {
344            return true;
345        }
346    }
347
348    return false;
349}
350
351std::tuple<Scope *, bool> Scope::IterateShadowedVariables(const util::StringView &name, const VariableVisitior &visitor)
352{
353    auto *iter = this;
354
355    while (true) {
356        auto *v = iter->FindLocal(name);
357
358        if (v && visitor(v)) {
359            return {iter, true};
360        }
361
362        if (iter->IsFunctionVariableScope()) {
363            break;
364        }
365
366        iter = iter->Parent();
367    }
368
369    return {iter, false};
370}
371
372void Scope::SetFullScopeNames()
373{
374    if (hasFullScopeNameSet_) {
375        return;
376    }
377    hasFullScopeNameSet_ = true;
378    if (!hasSelfScopeNameSet_) {
379        SetSelfScopeName(GetSelfScopeName());
380    }
381
382    std::stringstream selfScopeStream;
383    OptimizeSelfScopeName(selfScopeStream);
384    std::stringstream fullScopeName;
385    Scope *parent = GetParentWithScopeName();
386    if (parent) {
387        fullScopeName << parent->GetFullScopeName() <<
388                         GetScopeTag() <<
389                         selfScopeStream.str();
390        if (scopeDuplicateIndex_ > 0) {
391            fullScopeName << util::Helpers::DUPLICATED_SEPERATOR <<
392                             std::hex << scopeDuplicateIndex_;
393        }
394    }
395
396    fullScopeName_ = util::UString(fullScopeName.str(), allocator_).View();
397}
398
399void Scope::OptimizeSelfScopeName(std::stringstream &selfScopeStream)
400{
401    bool useIndex = false;
402    auto it = topScope_->scopeNames_.find(selfScopeName_);
403    if (it == topScope_->scopeNames_.end()) {
404        std::stringstream indexScopeName;
405        indexScopeName << util::Helpers::INDEX_NAME_SPICIFIER << std::hex << topScope_->scopeNames_.size();
406        if (selfScopeName_.Length() > indexScopeName.str().length()) {
407            topScope_->scopeNames_.insert(
408                {selfScopeName_, (int32_t)topScope_->scopeNames_.size()}
409            );
410            selfScopeStream << indexScopeName.str();
411            useIndex = true;
412        }
413    } else {
414        selfScopeStream << util::Helpers::INDEX_NAME_SPICIFIER << std::hex << it->second;
415        useIndex = true;
416    }
417
418    if (!useIndex) {
419        selfScopeStream << selfScopeName_;
420    }
421}
422
423bool Scope::AddLocal(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
424                     [[maybe_unused]] ScriptExtension extension)
425{
426    VariableFlags flags = VariableFlags::NONE;
427    switch (newDecl->Type()) {
428        case DeclType::VAR: {
429            auto [scope, shadowed] = IterateShadowedVariables(
430                newDecl->Name(), [](const Variable *v) { return !v->HasFlag(VariableFlags::VAR); });
431
432            if (shadowed) {
433                return false;
434            }
435
436            VariableFlags varFlags = VariableFlags::HOIST_VAR;
437            if (scope->IsGlobalScope()) {
438                scope->Bindings().insert({newDecl->Name(), allocator->New<GlobalVariable>(newDecl, varFlags)});
439            } else {
440                scope->PropagateBinding<LocalVariable>(allocator, newDecl->Name(), newDecl, varFlags);
441            }
442
443            return true;
444        }
445        case DeclType::ENUM_LITERAL: {
446            return tsBindings_.AddTSVariable<TSBindingType::ENUMLITERAL>(
447                newDecl->Name(), allocator->New<EnumLiteralVariable>(newDecl, VariableFlags::ENUM_LITERAL));
448        }
449        case DeclType::INTERFACE: {
450            bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, VariableFlags::INTERFACE)});
451            return true;
452        }
453        case DeclType::FUNC: {
454            flags = VariableFlags::HOIST;
455            [[fallthrough]];
456        }
457        default: {
458            if (currentVariable) {
459                return false;
460            }
461
462            if (HasVarDecl(newDecl->Name())) {
463                return false;
464            }
465
466            bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, flags)});
467            return true;
468        }
469    }
470}
471
472bool ParamScope::AddParam(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags)
473{
474    CHECK_NOT_NULL(newDecl);
475    ASSERT(newDecl->IsParameterDecl());
476
477    if (currentVariable) {
478        return false;
479    }
480
481    auto *param = allocator->New<LocalVariable>(newDecl, flags);
482
483    params_.push_back(param);
484    bindings_.insert({newDecl->Name(), param});
485    return true;
486}
487
488std::tuple<ParameterDecl *, const ir::AstNode *> ParamScope::AddParamDecl(ArenaAllocator *allocator,
489                                                                          const ir::AstNode *param)
490{
491    const auto [name, pattern] = util::Helpers::ParamName(allocator, param, params_.size());
492
493    auto *decl = NewDecl<ParameterDecl>(allocator, name);
494    CHECK_NOT_NULL(decl);
495
496    if (!AddParam(allocator, FindLocal(name), decl, VariableFlags::VAR)) {
497        return {decl, param};
498    }
499
500    if (!pattern) {
501        decl->BindNode(param);
502        return {decl, nullptr};
503    }
504
505    std::vector<const ir::Identifier *> bindings = util::Helpers::CollectBindingNames(param);
506
507    for (const auto *binding : bindings) {
508        auto *varDecl = NewDecl<VarDecl>(allocator, binding->Name());
509        CHECK_NOT_NULL(varDecl);
510        varDecl->BindNode(binding);
511
512        if (FindLocal(varDecl->Name())) {
513            return {decl, binding};
514        }
515
516        auto *paramVar = allocator->New<LocalVariable>(varDecl, VariableFlags::VAR);
517        bindings_.insert({varDecl->Name(), paramVar});
518    }
519
520    return {decl, nullptr};
521}
522
523void FunctionParamScope::BindName(ArenaAllocator *allocator, util::StringView name)
524{
525    nameVar_ = AddDecl<ConstDecl, LocalVariable>(allocator, name, VariableFlags::INITIALIZED);
526    functionScope_->Bindings().insert({name, nameVar_});
527}
528
529bool FunctionParamScope::AddBinding([[maybe_unused]] ArenaAllocator *allocator,
530                                    [[maybe_unused]] Variable *currentVariable, [[maybe_unused]] Decl *newDecl,
531                                    [[maybe_unused]] ScriptExtension extension)
532{
533    UNREACHABLE();
534}
535
536const util::StringView &FunctionParamScope::GetFullScopeName()
537{
538    if (functionScope_) {
539        return functionScope_->GetFullScopeName();
540    }
541
542    // FunctionParam should have the same name with FunctionScope
543    // Get scope name from parent in case the functionScope_ is nullptr
544    if (parent_) {
545        return parent_->GetFullScopeName();
546    }
547
548    return fullScopeName_;
549}
550
551uint32_t FunctionParamScope::GetDuplicateScopeIndex(const util::StringView &childScopeName)
552{
553    if (functionScope_) {
554        return functionScope_->GetDuplicateScopeIndex(childScopeName);
555    }
556
557    if (parent_) {
558        return parent_->GetDuplicateScopeIndex(childScopeName);
559    }
560
561    return 0;
562}
563
564void FunctionScope::BindNameWithScopeInfo(util::StringView name, util::StringView recordName)
565{
566    name_ = name;
567    std::stringstream internalName;
568    internalName << recordName << util::Helpers::FUNC_NAME_SEPARATOR;
569
570    Scope *parent = GetParentWithScopeName();
571    if (parent != nullptr) {
572        internalName << parent->GetFullScopeName();
573    }
574    internalName << GetScopeTag() << util::Helpers::FUNC_NAME_SEPARATOR << GetSelfScopeName();
575    if (scopeDuplicateIndex_ > 0) {
576        internalName << util::Helpers::DUPLICATED_SEPERATOR <<
577                        std::hex << scopeDuplicateIndex_;
578    }
579    internalName_ = util::UString(internalName.str(), allocator_).View();
580}
581
582bool FunctionScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
583                               [[maybe_unused]] ScriptExtension extension)
584{
585    switch (newDecl->Type()) {
586        case DeclType::VAR: {
587            return AddVar<LocalVariable>(allocator, currentVariable, newDecl);
588        }
589        case DeclType::FUNC: {
590            return AddFunction<LocalVariable>(allocator, currentVariable, newDecl, extension);
591        }
592        case DeclType::CLASS: {
593            return AddClass<LocalVariable>(allocator, currentVariable, newDecl);
594        }
595        case DeclType::ENUM_LITERAL: {
596            return AddTSBinding<EnumLiteralVariable>(allocator, newDecl, VariableFlags::ENUM_LITERAL);
597        }
598        case DeclType::NAMESPACE: {
599            return AddTSBinding<NamespaceVariable>(allocator, newDecl, VariableFlags::NAMESPACE);
600        }
601        case DeclType::IMPORT_EQUALS: {
602            return AddTSBinding<ImportEqualsVariable>(allocator, newDecl, VariableFlags::IMPORT_EQUALS);
603        }
604        case DeclType::INTERFACE: {
605            return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::INTERFACE);
606        }
607        default: {
608            return AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
609        }
610    }
611}
612
613void FunctionScope::SetSelfScopeName(const util::StringView &ident)
614{
615    Scope::SetSelfScopeName(ident);
616    paramScope_->selfScopeName_ = selfScopeName_;
617    paramScope_->hasSelfScopeNameSet_ = true;
618    hasSelfScopeNameSet_ = true;
619}
620
621util::StringView FunctionScope::GetScopeTag()
622{
623    if (IsFunctionScope() && (node_->IsScriptFunction() && node_->AsScriptFunction()->IsConstructor())) {
624        return util::UString(util::Helpers::CTOR_TAG.data(), allocator_).View();
625    }
626    if (parent_ && parent_->Parent() && parent_->Parent()->IsClassScope()) {
627        bool hasNodeParent = node_ && node_->Parent() && node_->Parent()->Parent();
628        const ir::AstNode *nodeParent = hasNodeParent ? node_->Parent()->Parent() : nullptr;
629        if (nodeParent && nodeParent->IsMethodDefinition() && nodeParent->AsMethodDefinition()->IsStatic()) {
630            return util::UString(util::Helpers::STATIC_METHOD_TAG.data(), allocator_).View();
631        }
632        return util::UString(util::Helpers::METHOD_TAG.data(), allocator_).View();
633    }
634    return util::UString(util::Helpers::FUNCTION_TAG.data(), allocator_).View();
635}
636
637util::StringView FunctionScope::GetSelfScopeName()
638{
639    if (hasSelfScopeNameSet_) {
640        return selfScopeName_;
641    }
642
643    if (node_ && node_->IsScriptFunction()) {
644        auto selfName = util::Helpers::FunctionName(allocator_, node_->AsScriptFunction());
645        if (!util::Helpers::IsSpecialScopeName(selfName)) {
646            return selfName;
647        }
648    }
649    return util::UString(util::Helpers::STRING_EMPTY.data(), allocator_).View();
650}
651
652util::StringView TSModuleScope::GetSelfScopeName()
653{
654    if (hasSelfScopeNameSet_) {
655        return selfScopeName_;
656    }
657    throw Error(ErrorType::GENERIC, "namespace or module name should be set in Binder::ResolveReference()");
658}
659
660util::StringView TSModuleScope::GetScopeTag()
661{
662    return util::UString(util::Helpers::NAMESPACE_TAG.data(), allocator_).View();
663}
664
665bool TSEnumScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
666                             [[maybe_unused]] ScriptExtension extension)
667{
668    ASSERT(newDecl->Type() == DeclType::ENUM);
669    return enumMemberBindings_->insert({newDecl->Name(), allocator->New<EnumVariable>(newDecl, false)}).second;
670}
671
672void TSEnumScope::SetSelfScopeName(const util::StringView &ident)
673{
674    if (!hasSelfScopeNameSet_) {
675        FunctionScope::SetSelfScopeName(GetSelfScopeName());
676    }
677}
678
679util::StringView TSEnumScope::GetSelfScopeName()
680{
681    if (hasSelfScopeNameSet_) {
682        return selfScopeName_;
683    }
684
685    std::stringstream scopeName;
686    if (node_ && node_->IsScriptFunction()) {
687        auto scriptFunction = node_->AsScriptFunction();
688        if (scriptFunction->Params().size() > 0 && scriptFunction->Params()[0]->IsIdentifier()) {
689            scopeName << scriptFunction->Params()[0]->AsIdentifier()->Name();
690        }
691    }
692    return util::UString(scopeName.str(), allocator_).View();
693}
694
695util::StringView TSEnumScope::GetScopeTag()
696{
697    return util::UString(util::Helpers::ENUM_TAG.data(), allocator_).View();
698}
699
700bool GlobalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
701                             [[maybe_unused]] ScriptExtension extension)
702{
703    switch (newDecl->Type()) {
704        case DeclType::VAR: {
705            return AddVar<GlobalVariable>(allocator, currentVariable, newDecl);
706        }
707        case DeclType::FUNC: {
708            return AddFunction<GlobalVariable>(allocator, currentVariable, newDecl, extension);
709        }
710        case DeclType::CLASS: {
711            return AddClass<LocalVariable>(allocator, currentVariable, newDecl);
712        }
713        case DeclType::ENUM_LITERAL: {
714            return AddTSBinding<EnumLiteralVariable>(allocator, newDecl, VariableFlags::ENUM_LITERAL);
715        }
716        case DeclType::NAMESPACE: {
717            return AddTSBinding<NamespaceVariable>(allocator, newDecl, VariableFlags::NAMESPACE);
718        }
719        case DeclType::IMPORT_EQUALS: {
720            return AddTSBinding<ImportEqualsVariable>(allocator, newDecl, VariableFlags::IMPORT_EQUALS);
721        }
722        case DeclType::INTERFACE: {
723            return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::INTERFACE);
724        }
725        default: {
726            return AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
727        }
728    }
729
730    return true;
731}
732
733void GlobalScope::SetSelfScopeName([[maybe_unused]] const util::StringView &ident)
734{
735    hasSelfScopeNameSet_ = true;
736}
737
738// ModuleScope
739
740void ModuleScope::ConvertLocalVariableToModuleVariable(ArenaAllocator *allocator, util::StringView localName)
741{
742    auto res = bindings_.find(localName);
743    // Since the module's exported [localName] has been validated before,
744    // [localName] must have a binding now.
745    ASSERT(res != bindings_.end());
746    if (!res->second->IsModuleVariable()) {
747        auto *decl = res->second->Declaration();
748        decl->AddFlag(DeclarationFlags::EXPORT);
749        VariableFlags flags = res->second->Flags();
750        res->second = allocator->New<ModuleVariable>(decl, flags | VariableFlags::LOCAL_EXPORT);
751    }
752}
753
754void ModuleScope::AssignIndexToModuleVariable(util::StringView name, uint32_t index)
755{
756    auto *moduleVar = FindLocal(name);
757    CHECK_NOT_NULL(moduleVar);
758    ASSERT(moduleVar->IsModuleVariable());
759    moduleVar->AsModuleVariable()->AssignIndex(index);
760}
761
762bool ModuleScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
763                             [[maybe_unused]] ScriptExtension extension)
764{
765    switch (newDecl->Type()) {
766        case DeclType::VAR: {
767            auto [scope, shadowed] = IterateShadowedVariables(
768                newDecl->Name(), [](const Variable *v) { return !v->HasFlag(VariableFlags::VAR); });
769
770            if (shadowed) {
771                return false;
772            }
773            return newDecl->IsImportOrExportDecl() ?
774                   AddVar<ModuleVariable>(allocator, currentVariable, newDecl) :
775                   AddVar<LocalVariable>(allocator, currentVariable, newDecl);
776        }
777        case DeclType::FUNC: {
778            if (currentVariable) {
779                auto decl = currentVariable->Declaration();
780                if (!decl->IsClassDecl() || !decl->AsClassDecl()->IsDeclare()) {
781                    return false;
782                }
783            }
784            return newDecl->IsImportOrExportDecl() ?
785                   AddFunction<ModuleVariable>(allocator, currentVariable, newDecl, extension) :
786                   AddFunction<LocalVariable>(allocator, currentVariable, newDecl, extension);
787        }
788        case DeclType::CLASS: {
789            return newDecl->IsImportOrExportDecl() ?
790                   AddClass<ModuleVariable>(allocator, currentVariable, newDecl) :
791                   AddClass<LocalVariable>(allocator, currentVariable, newDecl);
792        }
793        case DeclType::ENUM_LITERAL: {
794            return AddTSBinding<EnumLiteralVariable>(allocator, newDecl, VariableFlags::ENUM_LITERAL);
795        }
796        case DeclType::NAMESPACE: {
797            return AddTSBinding<NamespaceVariable>(allocator, newDecl, VariableFlags::NAMESPACE);
798        }
799        case DeclType::IMPORT_EQUALS: {
800            return AddTSBinding<ImportEqualsVariable>(allocator, newDecl, VariableFlags::IMPORT_EQUALS);
801        }
802        case DeclType::INTERFACE: {
803            return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::INTERFACE);
804        }
805        default: {
806            if (currentVariable) {
807                return false;
808            }
809            return newDecl->IsImportOrExportDecl() ?
810                   AddLexical<ModuleVariable>(allocator, currentVariable, newDecl) :
811                   AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
812        }
813    }
814}
815
816void ModuleScope::SetSelfScopeName([[maybe_unused]] const util::StringView &ident)
817{
818    hasSelfScopeNameSet_ = true;
819}
820
821// LocalScope
822
823bool LocalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
824                            [[maybe_unused]] ScriptExtension extension)
825{
826    return AddLocal(allocator, currentVariable, newDecl, extension);
827}
828
829void LoopScope::InitVariable()
830{
831    for (const auto &[name, var] : bindings_) {
832        if (!var->Declaration()->IsLetOrConstOrClassDecl()) {
833            continue;
834        }
835
836        var->AddFlag(VariableFlags::INITIALIZED);
837        if (var->LexicalBound()) {
838            var->AddFlag(VariableFlags::PER_ITERATION);
839        }
840    }
841}
842
843bool CatchParamScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
844                                 [[maybe_unused]] ScriptExtension extension)
845{
846    return AddParam(allocator, currentVariable, newDecl, VariableFlags::INITIALIZED);
847}
848
849bool CatchScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
850                            [[maybe_unused]] ScriptExtension extension)
851{
852    if (!newDecl->IsVarDecl() && paramScope_->FindLocal(newDecl->Name())) {
853        return false;
854    }
855
856    return AddLocal(allocator, currentVariable, newDecl, extension);
857}
858}  // namespace panda::es2panda::binder
859