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#ifndef ES2PANDA_COMPILER_SCOPES_SCOPE_H
17#define ES2PANDA_COMPILER_SCOPES_SCOPE_H
18
19#include "varbinder/declaration.h"
20#include "varbinder/variable.h"
21#include "es2panda.h"
22#include "util/enumbitops.h"
23#include "util/ustring.h"
24
25#include <map>
26#include <unordered_map>
27#include <vector>
28
29namespace ark::es2panda::public_lib {
30struct Context;
31}  // namespace ark::es2panda::public_lib
32
33namespace ark::es2panda::compiler {
34class IRNode;
35}  // namespace ark::es2panda::compiler
36
37namespace ark::es2panda::varbinder {
38// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
39#define DECLARE_CLASSES(type, className) class className;
40SCOPE_TYPES(DECLARE_CLASSES)
41#undef DECLARE_CLASSES
42
43class Scope;
44class VariableScope;
45class Variable;
46
47template <typename ScopeT,
48          std::enable_if_t<std::is_pointer_v<ScopeT> && std::is_base_of_v<Scope, std::remove_pointer_t<ScopeT>>, bool> =
49              true>
50class ScopeFindResultT {
51public:
52    ScopeFindResultT() = default;
53    ScopeFindResultT(util::StringView n, ScopeT s, uint32_t l, Variable *v) : ScopeFindResultT(n, s, l, l, v) {}
54    ScopeFindResultT(ScopeT s, uint32_t l, uint32_t ll, Variable *v) : scope(s), level(l), lexLevel(ll), variable(v) {}
55    ScopeFindResultT(util::StringView n, ScopeT s, uint32_t l, uint32_t ll, Variable *v)
56        : name(n), scope(s), level(l), lexLevel(ll), variable(v)
57    {
58    }
59
60    // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
61    util::StringView name {};
62    ScopeT scope {};
63    uint32_t level {};
64    uint32_t lexLevel {};
65    Variable *variable {};
66    // NOLINTEND(misc-non-private-member-variables-in-classes)
67};
68
69using ConstScopeFindResult = ScopeFindResultT<const Scope *>;
70using ScopeFindResult = ScopeFindResultT<Scope *>;
71
72class Scope {
73public:
74    virtual ~Scope() = default;
75    NO_COPY_SEMANTIC(Scope);
76    NO_MOVE_SEMANTIC(Scope);
77
78    using VariableMap = ArenaUnorderedMap<util::StringView, Variable *>;
79    using InsertResult = std::pair<VariableMap::const_iterator, bool>;
80
81    virtual ScopeType Type() const = 0;
82
83// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
84#define DECLARE_CHECKS_CASTS(scopeType, className)        \
85    bool Is##className() const                            \
86    {                                                     \
87        return Type() == ScopeType::scopeType;            \
88    }                                                     \
89    className *As##className()                            \
90    {                                                     \
91        ASSERT(Is##className());                          \
92        return reinterpret_cast<className *>(this);       \
93    }                                                     \
94    const className *As##className() const                \
95    {                                                     \
96        ASSERT(Is##className());                          \
97        return reinterpret_cast<const className *>(this); \
98    }
99    SCOPE_TYPES(DECLARE_CHECKS_CASTS)
100#undef DECLARE_CHECKS_CASTS
101
102    bool IsVariableScope() const
103    {
104        return Type() > ScopeType::LOCAL;
105    }
106
107    bool IsFunctionVariableScope() const
108    {
109        return Type() >= ScopeType::FUNCTION;
110    }
111
112    FunctionScope *AsFunctionVariableScope()
113    {
114        ASSERT(IsFunctionVariableScope());
115        return reinterpret_cast<FunctionScope *>(this);
116    }
117
118    const FunctionScope *AsFunctionVariableScope() const
119    {
120        ASSERT(IsFunctionVariableScope());
121        return reinterpret_cast<const FunctionScope *>(this);
122    }
123
124    VariableScope *AsVariableScope()
125    {
126        ASSERT(IsVariableScope());
127        return reinterpret_cast<VariableScope *>(this);
128    }
129
130    const VariableScope *AsVariableScope() const
131    {
132        ASSERT(IsVariableScope());
133        return reinterpret_cast<const VariableScope *>(this);
134    }
135
136    VariableScope *EnclosingVariableScope();
137
138    const VariableScope *EnclosingVariableScope() const;
139
140    ClassScope *EnclosingClassScope();
141    const ClassScope *EnclosingClassScope() const;
142
143    void AddFlag(ScopeFlags flag)
144    {
145        flags_ |= flag;
146    }
147
148    void ClearFlag(ScopeFlags flag)
149    {
150        flags_ &= ~flag;
151    }
152
153    bool HasFlag(ScopeFlags flag) const
154    {
155        return (flags_ & flag) != 0;
156    }
157
158    ArenaVector<Decl *> &Decls()
159    {
160        return decls_;
161    }
162
163    const ArenaVector<Decl *> &Decls() const
164    {
165        return decls_;
166    }
167
168    void SetParent(Scope *parent)
169    {
170        parent_ = parent;
171    }
172
173    Scope *Parent()
174    {
175        return parent_;
176    }
177
178    const Scope *Parent() const
179    {
180        return parent_;
181    }
182
183    const compiler::IRNode *ScopeStart() const
184    {
185        return startIns_;
186    }
187
188    const compiler::IRNode *ScopeEnd() const
189    {
190        return endIns_;
191    }
192
193    void SetScopeStart(const compiler::IRNode *ins)
194    {
195        startIns_ = ins;
196    }
197
198    void SetScopeEnd(const compiler::IRNode *ins)
199    {
200        endIns_ = ins;
201    }
202
203    ir::AstNode *Node()
204    {
205        return node_;
206    }
207
208    const ir::AstNode *Node() const
209    {
210        return node_;
211    }
212
213    void BindNode(ir::AstNode *node)
214    {
215        node_ = node;
216    }
217
218    Variable *AddDecl(ArenaAllocator *allocator, Decl *decl, [[maybe_unused]] ScriptExtension extension)
219    {
220        decls_.push_back(decl);
221        auto options = decl->IsTypeAliasDecl() ? varbinder::ResolveBindingOptions::TYPE_ALIASES
222                                               : varbinder::ResolveBindingOptions::BINDINGS;
223        return AddBinding(allocator, FindLocal(decl->Name(), options), decl, extension);
224    }
225
226    Variable *AddTsDecl(ArenaAllocator *allocator, Decl *decl, [[maybe_unused]] ScriptExtension extension)
227    {
228        decls_.push_back(decl);
229        return AddBinding(allocator, FindLocal(decl->Name(), ResolveBindingOptions::ALL), decl, extension);
230    }
231
232    template <typename T, typename... Args>
233    T *NewDecl(ArenaAllocator *allocator, Args &&...args);
234
235    template <typename DeclType, typename VariableType>
236    VariableType *AddDecl(ArenaAllocator *allocator, util::StringView name, VariableFlags flags);
237
238    template <typename DeclType = varbinder::LetDecl, typename VariableType = varbinder::LocalVariable>
239    static VariableType *CreateVar(ArenaAllocator *allocator, util::StringView name, VariableFlags flags,
240                                   ir::AstNode *node);
241
242    template <typename T, typename... Args>
243    Variable *PropagateBinding(ArenaAllocator *allocator, util::StringView name, Args &&...args);
244
245    virtual InsertResult InsertBinding(const util::StringView &name, Variable *var);
246    virtual InsertResult TryInsertBinding(const util::StringView &name, Variable *var);
247    virtual void MergeBindings(VariableMap const &bindings);
248    virtual VariableMap::size_type EraseBinding(const util::StringView &name);
249
250    const VariableMap &Bindings() const
251    {
252        return bindings_;
253    }
254
255    ArenaMap<util::StringView, Variable *> OrderedBindings(ArenaAllocator *allocator) const
256    {
257        ArenaMap<util::StringView, Variable *> result(allocator->Adapter());
258        result.insert(bindings_.begin(), bindings_.end());
259        return result;
260    }
261
262    virtual Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
263                                 [[maybe_unused]] ScriptExtension extension) = 0;
264
265    virtual Variable *FindLocal(const util::StringView &name, ResolveBindingOptions options) const;
266
267    bool IsSuperscopeOf(const varbinder::Scope *subscope) const;
268
269    ConstScopeFindResult Find(const util::StringView &name,
270                              ResolveBindingOptions options = ResolveBindingOptions::BINDINGS) const;
271
272    ScopeFindResult Find(const util::StringView &name, ResolveBindingOptions options = ResolveBindingOptions::BINDINGS);
273
274    ConstScopeFindResult FindInGlobal(const util::StringView &name,
275                                      ResolveBindingOptions options = ResolveBindingOptions::BINDINGS) const;
276
277    ConstScopeFindResult FindInFunctionScope(const util::StringView &name,
278                                             ResolveBindingOptions options = ResolveBindingOptions::BINDINGS) const;
279
280    Decl *FindDecl(const util::StringView &name) const;
281
282protected:
283    explicit Scope(ArenaAllocator *allocator, Scope *parent)
284        : parent_(parent), decls_(allocator->Adapter()), bindings_(allocator->Adapter())
285    {
286    }
287
288    explicit Scope(ArenaAllocator *allocator, Scope *parent, ScopeFlags flags)
289        : parent_(parent), decls_(allocator->Adapter()), bindings_(allocator->Adapter()), flags_(flags)
290    {
291    }
292
293    /**
294     * @return true - if the variable is shadowed
295     *         false - otherwise
296     */
297    using VariableVisitor = std::function<bool(const Variable *)>;
298
299    /**
300     * @return true - if the variable is shadowed
301     *         false - otherwise
302     */
303    std::tuple<Scope *, bool> IterateShadowedVariables(const util::StringView &name, const VariableVisitor &visitor);
304
305    Variable *AddLocal(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
306                       [[maybe_unused]] ScriptExtension extension);
307
308    Variable *AddLocalVar(ArenaAllocator *allocator, Decl *newDecl);
309
310private:
311    template <
312        typename ResultT, typename ScopeT,
313        std::enable_if_t<std::is_same_v<ResultT, ConstScopeFindResult> || std::is_same_v<ResultT, ScopeFindResult>,
314                         bool> = true,
315        std::enable_if_t<std::is_pointer_v<ScopeT> && std::is_base_of_v<Scope, std::remove_pointer_t<ScopeT>>, bool> =
316            true>
317    static ResultT FindImpl(ScopeT &&scope, const util::StringView &name, const ResolveBindingOptions options)
318    {
319        uint32_t level = 0;
320        uint32_t lexLevel = 0;
321        // iter will be the EXACT type of scope with cv-qualifiers
322        auto &&iter = scope;
323
324        if (iter->IsFunctionParamScope()) {
325            auto *const v = iter->FindLocal(name, options);
326
327            if (v != nullptr) {
328                return {name, iter, level, lexLevel, v};
329            }
330
331            level++;
332            const auto *const funcVariableScope = iter->AsFunctionParamScope()->GetFunctionScope();
333
334            if (funcVariableScope != nullptr && funcVariableScope->NeedLexEnv()) {
335                lexLevel++;
336            }
337
338            iter = iter->Parent();
339        }
340
341        while (iter != nullptr) {
342            auto *const v = iter->FindLocal(name, options);
343
344            if (v != nullptr) {
345                return {name, iter, level, lexLevel, v};
346            }
347
348            if (iter->IsVariableScope()) {
349                level++;
350
351                if (iter->AsVariableScope()->NeedLexEnv()) {
352                    lexLevel++;
353                }
354            }
355
356            iter = iter->Parent();
357        }
358
359        return {name, nullptr, 0, 0, nullptr};
360    }
361
362    Scope *parent_ {};
363    ArenaVector<Decl *> decls_;
364    VariableMap bindings_;
365    ir::AstNode *node_ {};
366    ScopeFlags flags_ {};
367    const compiler::IRNode *startIns_ {};
368    const compiler::IRNode *endIns_ {};
369};
370
371class VariableScope : public Scope {
372public:
373    ~VariableScope() override = default;
374    NO_COPY_SEMANTIC(VariableScope);
375    NO_MOVE_SEMANTIC(VariableScope);
376
377    uint32_t NextSlot()
378    {
379        return slotIndex_++;
380    }
381
382    uint32_t LexicalSlots() const
383    {
384        return slotIndex_;
385    }
386
387    bool NeedLexEnv() const
388    {
389        return slotIndex_ != 0;
390    }
391
392    uint32_t EvalBindings() const
393    {
394        return evalBindings_;
395    }
396
397    void CheckDirectEval(public_lib::Context *context);
398
399protected:
400    explicit VariableScope(ArenaAllocator *allocator, Scope *parent) : Scope(allocator, parent) {}
401
402    template <typename T>
403    Variable *AddVar(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl);
404
405    template <typename T>
406    Variable *AddFunction(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
407                          [[maybe_unused]] ScriptExtension extension);
408
409    template <typename T>
410    Variable *AddTSBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags);
411
412    template <typename T>
413    Variable *AddLexical(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl);
414
415    // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
416    uint32_t evalBindings_ {};
417    uint32_t slotIndex_ {};
418    // NOLINTEND(misc-non-private-member-variables-in-classes)
419};
420
421class ParamScope : public Scope {
422public:
423    ScopeType Type() const override
424    {
425        return ScopeType::PARAM;
426    }
427
428    ArenaVector<LocalVariable *> &Params()
429    {
430        return params_;
431    }
432
433    const ArenaVector<LocalVariable *> &Params() const
434    {
435        return params_;
436    }
437
438    std::tuple<ParameterDecl *, ir::AstNode *, Variable *> AddParamDecl(ArenaAllocator *allocator, ir::AstNode *param);
439
440protected:
441    explicit ParamScope(ArenaAllocator *allocator, Scope *parent)
442        : Scope(allocator, parent), params_(allocator->Adapter())
443    {
444    }
445
446    Variable *AddParam(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags);
447
448    // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
449    ArenaVector<LocalVariable *> params_;
450};
451
452class FunctionScope;
453
454class FunctionParamScope : public ParamScope {
455public:
456    explicit FunctionParamScope(ArenaAllocator *allocator, Scope *parent) : ParamScope(allocator, parent) {}
457
458    FunctionScope *GetFunctionScope() const
459    {
460        return functionScope_;
461    }
462
463    void BindFunctionScope(FunctionScope *funcScope)
464    {
465        functionScope_ = funcScope;
466    }
467
468    LocalVariable *NameVar() const
469    {
470        return nameVar_;
471    }
472
473    void BindName(ArenaAllocator *allocator, util::StringView name);
474
475    ScopeType Type() const override
476    {
477        return ScopeType::FUNCTION_PARAM;
478    }
479
480    Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
481                         [[maybe_unused]] ScriptExtension extension) override;
482
483    friend class FunctionScope;
484    template <typename E, typename T>
485    friend class ScopeWithParamScope;
486
487private:
488    FunctionScope *functionScope_ {};
489    LocalVariable *nameVar_ {};
490};
491
492template <typename E, typename T>
493class ScopeWithParamScope : public E {
494public:
495    explicit ScopeWithParamScope(ArenaAllocator *allocator, Scope *parent) : E(allocator, parent), paramScope_(nullptr)
496    {
497    }
498
499    void BindParamScope(T *paramScope)
500    {
501        AssignParamScope(paramScope);
502        this->MergeBindings(paramScope->Bindings());
503    }
504
505    void AssignParamScope(T *paramScope)
506    {
507        ASSERT(this->Parent() == paramScope);
508        paramScope_ = paramScope;
509    }
510
511    T *ParamScope()
512    {
513        return paramScope_;
514    }
515
516    const T *ParamScope() const
517    {
518        return paramScope_;
519    }
520
521protected:
522    // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
523    T *paramScope_;
524};
525
526class LocalScope : public Scope {
527public:
528    explicit LocalScope(ArenaAllocator *allocator, Scope *parent) : Scope(allocator, parent) {}
529    explicit LocalScope(ArenaAllocator *allocator, Scope *parent, ScopeFlags flags) : Scope(allocator, parent, flags) {}
530
531    ScopeType Type() const override
532    {
533        return ScopeType::LOCAL;
534    }
535
536    Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
537                         [[maybe_unused]] ScriptExtension extension) override;
538};
539
540class LocalScopeWithTypeAlias : public LocalScope {
541public:
542    explicit LocalScopeWithTypeAlias(ArenaAllocator *allocator, Scope *parent)
543        : LocalScope(allocator, parent),
544          typeAliasScope_(allocator->New<LocalScope>(allocator, this, ScopeFlags::TYPE_ALIAS))
545    {
546    }
547    explicit LocalScopeWithTypeAlias(ArenaAllocator *allocator, Scope *parent, ScopeFlags flags)
548        : LocalScope(allocator, parent, flags),
549          typeAliasScope_(allocator->New<LocalScope>(allocator, this, ScopeFlags::TYPE_ALIAS))
550    {
551    }
552
553    Variable *FindLocal(const util::StringView &name, ResolveBindingOptions options) const override;
554
555    Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
556                         [[maybe_unused]] ScriptExtension extension) override;
557
558    const LocalScope *TypeAliasScope() const
559    {
560        return typeAliasScope_;
561    }
562
563    LocalScope *TypeAliasScope()
564    {
565        return typeAliasScope_;
566    }
567
568private:
569    LocalScope *typeAliasScope_;
570};
571
572class FunctionScope : public ScopeWithParamScope<VariableScope, FunctionParamScope> {
573public:
574    explicit FunctionScope(ArenaAllocator *allocator, Scope *parent)
575        : ScopeWithParamScope(allocator, parent),
576          typeAliasScope_(allocator->New<LocalScope>(allocator, this, ScopeFlags::TYPE_ALIAS))
577    {
578    }
579
580    ScopeType Type() const override
581    {
582        return ScopeType::FUNCTION;
583    }
584
585    void BindName(util::StringView name)
586    {
587        name_ = name;
588    }
589
590    void BindInternalName(util::StringView internalName)
591    {
592        internalName_ = internalName;
593    }
594
595    const util::StringView &Name() const
596    {
597        return name_;
598    }
599
600    const util::StringView &InternalName() const
601    {
602        return internalName_;
603    }
604
605    const LocalScope *TypeAliasScope() const
606    {
607        return typeAliasScope_;
608    }
609
610    Variable *FindLocal(const util::StringView &name, ResolveBindingOptions options) const override;
611
612    Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
613                         [[maybe_unused]] ScriptExtension extension) override;
614
615private:
616    util::StringView name_ {};
617    util::StringView internalName_ {};
618    LocalScope *typeAliasScope_;
619};
620
621class ClassScope : public LocalScopeWithTypeAlias {
622public:
623    explicit ClassScope(ArenaAllocator *allocator, Scope *parent)
624        : LocalScopeWithTypeAlias(allocator, parent),
625          staticDeclScope_(allocator->New<LocalScope>(allocator, this, ScopeFlags::STATIC_DECL_SCOPE)),
626          staticFieldScope_(allocator->New<LocalScope>(allocator, staticDeclScope_, ScopeFlags::STATIC_FIELD_SCOPE)),
627          staticMethodScope_(allocator->New<LocalScope>(allocator, staticFieldScope_, ScopeFlags::STATIC_METHOD_SCOPE)),
628          instanceDeclScope_(allocator->New<LocalScope>(allocator, staticMethodScope_, ScopeFlags::DECL_SCOPE)),
629          instanceFieldScope_(allocator->New<LocalScope>(allocator, instanceDeclScope_, ScopeFlags::FIELD_SCOPE)),
630          instanceMethodScope_(allocator->New<LocalScope>(allocator, instanceFieldScope_, ScopeFlags::METHOD_SCOPE))
631    {
632    }
633
634    ScopeType Type() const override
635    {
636        return ScopeType::CLASS;
637    }
638
639    LocalScope *StaticDeclScope()
640    {
641        return staticDeclScope_;
642    }
643
644    const LocalScope *StaticDeclScope() const
645    {
646        return staticDeclScope_;
647    }
648
649    LocalScope *StaticFieldScope()
650    {
651        return staticFieldScope_;
652    }
653
654    const LocalScope *StaticFieldScope() const
655    {
656        return staticFieldScope_;
657    }
658
659    LocalScope *StaticMethodScope()
660    {
661        return staticMethodScope_;
662    }
663
664    const LocalScope *StaticMethodScope() const
665    {
666        return staticMethodScope_;
667    }
668
669    LocalScope *InstanceFieldScope()
670    {
671        return instanceFieldScope_;
672    }
673
674    const LocalScope *InstanceFieldScope() const
675    {
676        return instanceFieldScope_;
677    }
678
679    LocalScope *InstanceMethodScope()
680    {
681        return instanceMethodScope_;
682    }
683
684    const LocalScope *InstanceMethodScope() const
685    {
686        return instanceMethodScope_;
687    }
688
689    LocalScope *InstanceDeclScope()
690    {
691        return instanceDeclScope_;
692    }
693
694    const LocalScope *InstanceDeclScope() const
695    {
696        return instanceDeclScope_;
697    }
698
699    uint32_t GetAndIncrementAnonymousClassIdx() const
700    {
701        return anonymousClassIdx_++;
702    }
703
704    Variable *FindLocal(const util::StringView &name, ResolveBindingOptions options) const override;
705
706    Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
707                         [[maybe_unused]] ScriptExtension extension) override;
708
709    class BindingProps {
710    public:
711        BindingProps() = default;
712
713        void SetFlagsType(VariableFlags flagsType)
714        {
715            flags_ |= flagsType;
716        }
717        void SetBindingProps(VariableFlags flags, ir::Identifier *ident, LocalScope *targetScope)
718        {
719            flags_ |= flags;
720            ident_ = ident;
721            targetScope_ = targetScope;
722        }
723        VariableFlags GetFlags() const
724        {
725            return flags_;
726        }
727        ir::Identifier *GetIdent()
728        {
729            return ident_;
730        }
731        LocalScope *GetTargetScope()
732        {
733            return targetScope_;
734        }
735
736    private:
737        VariableFlags flags_ = VariableFlags::NONE;
738        ir::Identifier *ident_ {};
739        LocalScope *targetScope_ {};
740    };
741
742    void SetBindingProps(Decl *newDecl, BindingProps *props, bool isStatic);
743
744private:
745    LocalScope *staticDeclScope_;
746    LocalScope *staticFieldScope_;
747    LocalScope *staticMethodScope_;
748    LocalScope *instanceDeclScope_;
749    LocalScope *instanceFieldScope_;
750    LocalScope *instanceMethodScope_;
751    mutable uint32_t anonymousClassIdx_ {1};
752};
753
754class CatchParamScope : public ParamScope {
755public:
756    explicit CatchParamScope(ArenaAllocator *allocator, Scope *parent) : ParamScope(allocator, parent) {}
757
758    ScopeType Type() const override
759    {
760        return ScopeType::CATCH_PARAM;
761    }
762
763    Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
764                         [[maybe_unused]] ScriptExtension extension) override;
765
766    friend class CatchScope;
767};
768
769class CatchScope : public ScopeWithParamScope<LocalScopeWithTypeAlias, CatchParamScope> {
770public:
771    explicit CatchScope(ArenaAllocator *allocator, Scope *parent) : ScopeWithParamScope(allocator, parent) {}
772
773    ScopeType Type() const override
774    {
775        return ScopeType::CATCH;
776    }
777
778    Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
779                         [[maybe_unused]] ScriptExtension extension) override;
780};
781
782class LoopScope;
783
784class LoopDeclarationScope : public VariableScope {
785public:
786    explicit LoopDeclarationScope(ArenaAllocator *allocator, Scope *parent) : VariableScope(allocator, parent) {}
787
788    ScopeType Type() const override
789    {
790        return loopType_;
791    }
792
793    Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
794                         [[maybe_unused]] ScriptExtension extension) override
795    {
796        return AddLocal(allocator, currentVariable, newDecl, extension);
797    }
798
799    Scope *InitScope()
800    {
801        if (NeedLexEnv()) {
802            return initScope_;
803        }
804
805        return this;
806    }
807
808    void ConvertToVariableScope(ArenaAllocator *allocator);
809
810private:
811    friend class LoopScope;
812    LoopScope *loopScope_ {};
813    LocalScope *initScope_ {};
814    ScopeType loopType_ {ScopeType::LOCAL};
815};
816
817class LoopScope : public VariableScope {
818public:
819    explicit LoopScope(ArenaAllocator *allocator, Scope *parent) : VariableScope(allocator, parent)
820    {
821        // NOTE(kkonkuznetsov): currently LoopScope type has ScopeType::LOCAL
822        // therefore it does not respond to IsLoopScope() because it checks for type.
823        // This LOOP_SCOPE flag can be used to check that scope is actually a loop scope.
824        AddFlag(ScopeFlags::LOOP_SCOPE);
825    }
826
827    LoopDeclarationScope *DeclScope()
828    {
829        return declScope_;
830    }
831
832    void BindDecls(LoopDeclarationScope *declScope)
833    {
834        declScope_ = declScope;
835        declScope_->loopScope_ = this;
836    }
837
838    ScopeType Type() const override
839    {
840        return loopType_;
841    }
842
843    void ConvertToVariableScope(ArenaAllocator *allocator);
844
845    Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
846                         [[maybe_unused]] ScriptExtension extension) override
847    {
848        return AddLocal(allocator, currentVariable, newDecl, extension);
849    }
850
851protected:
852    // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
853    LoopDeclarationScope *declScope_ {};
854    ScopeType loopType_ {ScopeType::LOCAL};
855    // NOLINTEND(misc-non-private-member-variables-in-classes)
856};
857
858class GlobalScope : public FunctionScope {
859public:
860    explicit GlobalScope(ArenaAllocator *allocator)
861        : FunctionScope(allocator, nullptr), foreignBindings_(allocator->Adapter())
862    {
863        auto *paramScope = allocator->New<FunctionParamScope>(allocator, this);
864        paramScope_ = paramScope;
865        paramScope_->BindFunctionScope(this);
866    }
867
868    ScopeType Type() const override
869    {
870        return ScopeType::GLOBAL;
871    }
872
873    Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
874                         [[maybe_unused]] ScriptExtension extension) override;
875
876    InsertResult InsertBinding(const util::StringView &name, Variable *var) override;
877    InsertResult TryInsertBinding(const util::StringView &name, Variable *var) override;
878    void MergeBindings(VariableMap const &bindings) override;
879    VariableMap::size_type EraseBinding(const util::StringView &name) override;
880
881    InsertResult InsertForeignBinding(const util::StringView &name, Variable *var);
882    [[nodiscard]] bool IsForeignBinding(const util::StringView &name) const;
883
884    InsertResult InsertDynamicBinding(const util::StringView &name, Variable *var);
885
886private:
887    InsertResult InsertImpl(const util::StringView &name, Variable *var, bool isForeign, bool isDynamic);
888
889    ArenaUnorderedMap<util::StringView, bool> foreignBindings_;
890};
891
892class ModuleScope : public GlobalScope {
893public:
894    template <typename K, typename V>
895    using ModuleEntry = ArenaVector<std::pair<K, V>>;
896    using ImportDeclList = ArenaVector<ImportDecl *>;
897    using ExportDeclList = ArenaVector<ExportDecl *>;
898    using LocalExportNameMap = ArenaMultiMap<varbinder::Variable *, util::StringView>;
899
900    explicit ModuleScope(ArenaAllocator *allocator)
901        : GlobalScope(allocator),
902          allocator_(allocator),
903          imports_(allocator_->Adapter()),
904          exports_(allocator_->Adapter()),
905          localExports_(allocator_->Adapter())
906    {
907    }
908
909    ScopeType Type() const override
910    {
911        return ScopeType::MODULE;
912    }
913
914    const ModuleEntry<ir::ImportDeclaration *, ImportDeclList> &Imports() const
915    {
916        return imports_;
917    }
918
919    const ModuleEntry<ir::AstNode *, ExportDeclList> &Exports() const
920    {
921        return exports_;
922    }
923
924    const LocalExportNameMap &LocalExports() const
925    {
926        return localExports_;
927    }
928
929    void AddImportDecl(ir::ImportDeclaration *importDecl, ImportDeclList &&decls);
930
931    void AddExportDecl(ir::AstNode *exportDecl, ExportDecl *decl);
932
933    void AddExportDecl(ir::AstNode *exportDecl, ExportDeclList &&decls);
934
935    Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
936                         [[maybe_unused]] ScriptExtension extension) override;
937
938    bool ExportAnalysis();
939
940private:
941    Variable *AddImport(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl);
942
943    ArenaAllocator *allocator_;
944    ModuleEntry<ir::ImportDeclaration *, ImportDeclList> imports_;
945    ModuleEntry<ir::AstNode *, ExportDeclList> exports_;
946    LocalExportNameMap localExports_;
947};
948
949template <typename T>
950Variable *VariableScope::AddVar(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl)
951{
952    if (!currentVariable) {
953        return InsertBinding(newDecl->Name(), allocator->New<T>(newDecl, VariableFlags::HOIST_VAR)).first->second;
954    }
955
956    switch (currentVariable->Declaration()->Type()) {
957        case DeclType::VAR: {
958            currentVariable->Reset(newDecl, VariableFlags::HOIST_VAR);
959            [[fallthrough]];
960        }
961        case DeclType::PARAM:
962        case DeclType::FUNC: {
963            return currentVariable;
964        }
965        default: {
966            return nullptr;
967        }
968    }
969}
970
971template <typename T>
972Variable *VariableScope::AddFunction(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
973                                     [[maybe_unused]] ScriptExtension extension)
974{
975    VariableFlags flags = (extension == ScriptExtension::JS) ? VariableFlags::HOIST_VAR : VariableFlags::HOIST;
976
977    if (!currentVariable) {
978        return InsertBinding(newDecl->Name(), allocator->New<T>(newDecl, flags)).first->second;
979    }
980
981    if (extension != ScriptExtension::JS || IsModuleScope()) {
982        return nullptr;
983    }
984
985    switch (currentVariable->Declaration()->Type()) {
986        case DeclType::VAR:
987        case DeclType::FUNC: {
988            currentVariable->Reset(newDecl, VariableFlags::HOIST_VAR);
989            return currentVariable;
990        }
991        default: {
992            return nullptr;
993        }
994    }
995}
996
997template <typename T>
998Variable *VariableScope::AddTSBinding(ArenaAllocator *allocator, [[maybe_unused]] Variable *currentVariable,
999                                      Decl *newDecl, VariableFlags flags)
1000{
1001    ASSERT(!currentVariable);
1002    return InsertBinding(newDecl->Name(), allocator->New<T>(newDecl, flags)).first->second;
1003}
1004
1005template <typename T>
1006Variable *VariableScope::AddLexical(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl)
1007{
1008    if (currentVariable) {
1009        return nullptr;
1010    }
1011
1012    return InsertBinding(newDecl->Name(), allocator->New<T>(newDecl, VariableFlags::NONE)).first->second;
1013}
1014
1015template <typename T, typename... Args>
1016T *Scope::NewDecl(ArenaAllocator *allocator, Args &&...args)
1017{
1018    T *decl = allocator->New<T>(std::forward<Args>(args)...);
1019    decls_.push_back(decl);
1020
1021    return decl;
1022}
1023
1024template <typename DeclType, typename VariableType>
1025VariableType *Scope::AddDecl(ArenaAllocator *allocator, util::StringView name, VariableFlags flags)
1026{
1027    if (FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS)) {
1028        return nullptr;
1029    }
1030
1031    auto *decl = allocator->New<DeclType>(name);
1032    auto *variable = allocator->New<VariableType>(decl, flags);
1033
1034    decls_.push_back(decl);
1035    bindings_.insert({decl->Name(), variable});
1036    variable->SetScope(this);
1037
1038    return variable;
1039}
1040
1041template <typename DeclType, typename VariableType>
1042VariableType *Scope::CreateVar(ArenaAllocator *allocator, util::StringView name, VariableFlags flags, ir::AstNode *node)
1043{
1044    auto *decl = allocator->New<DeclType>(name);
1045    auto *variable = allocator->New<VariableType>(decl, flags);
1046    decl->BindNode(node);
1047    return variable;
1048}
1049
1050template <typename T, typename... Args>
1051Variable *Scope::PropagateBinding(ArenaAllocator *allocator, util::StringView name, Args &&...args)
1052{
1053    auto res = bindings_.find(name);
1054    if (res == bindings_.end()) {
1055        return bindings_.insert({name, allocator->New<T>(std::forward<Args>(args)...)}).first->second;
1056    }
1057
1058    res->second->Reset(std::forward<Args>(args)...);
1059    return res->second;
1060}
1061}  // namespace ark::es2panda::varbinder
1062
1063#endif
1064