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_CHECKER_CHECKER_CONTEXT_H
17#define ES2PANDA_CHECKER_CHECKER_CONTEXT_H
18
19#include "checker/types/type.h"
20#include "ir/statements/loopStatement.h"
21#include "varbinder/variable.h"
22
23namespace ark::es2panda::checker {
24
25class ETSObjectType;
26class Signature;
27
28using ENUMBITOPS_OPERATORS;
29
30enum class CheckerStatus : uint32_t {
31    NO_OPTS = 0U,
32    FORCE_TUPLE = 1U << 0U,
33    IN_CONST_CONTEXT = 1U << 1U,
34    KEEP_LITERAL_TYPE = 1U << 2U,
35    IN_PARAMETER = 1U << 3U,
36    IN_CLASS = 1U << 4U,
37    IN_INTERFACE = 1U << 5U,
38    IN_ABSTRACT = 1U << 6U,
39    IN_STATIC_CONTEXT = 1U << 7U,
40    IN_CONSTRUCTOR = 1U << 8U,
41    IN_STATIC_BLOCK = 1U << 9U,
42    INNER_CLASS = 1U << 10U,
43    IN_ENUM = 1U << 11U,
44    BUILTINS_INITIALIZED = 1U << 12U,
45    IN_LAMBDA = 1U << 13U,
46    IGNORE_VISIBILITY = 1U << 14U,
47    IN_INSTANCE_EXTENSION_METHOD = 1U << 15U,
48    IN_LOCAL_CLASS = 1U << 16U,
49    IN_INSTANCEOF_CONTEXT = 1U << 17U,
50    IN_TEST_EXPRESSION = 1U << 18U,
51    IN_LOOP = 1U << 19U,
52    MEET_RETURN = 1U << 20U,
53    MEET_BREAK = 1U << 21U,
54    MEET_CONTINUE = 1U << 22U,
55    MEET_THROW = 1U << 23U,
56    IN_EXTERNAL = 1U << 24U,
57    IN_BRIDGE_TEST = 1U << 25U,
58};
59
60}  // namespace ark::es2panda::checker
61
62template <>
63struct enumbitops::IsAllowedType<ark::es2panda::checker::CheckerStatus> : std::true_type {
64};
65
66namespace ark::es2panda::checker {
67
68using CapturedVarsMap = ArenaUnorderedMap<varbinder::Variable *, lexer::SourcePosition>;
69using SmartCastMap = ArenaMap<varbinder::Variable const *, checker::Type *>;
70using SmartCastArray = std::vector<std::pair<varbinder::Variable const *, checker::Type *>>;
71using SmartCastTestMap = ArenaMap<varbinder::Variable const *, std::pair<checker::Type *, checker::Type *>>;
72using SmartCastTuple = std::tuple<varbinder::Variable const *, checker::Type *, checker::Type *>;
73using SmartCastTestArray = std::vector<SmartCastTuple>;
74using PreservedSmartCastsMap = ArenaMultiMap<ir::AstNode const *, SmartCastArray>;
75using SmartVariables = std::unordered_set<varbinder::Variable const *>;
76
77struct SmartCastCondition final {
78    SmartCastCondition() = default;
79    ~SmartCastCondition() = default;
80
81    DEFAULT_COPY_SEMANTIC(SmartCastCondition);
82    DEFAULT_MOVE_SEMANTIC(SmartCastCondition);
83
84    // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
85    varbinder::Variable const *variable = nullptr;
86    checker::Type *testedType = nullptr;
87    bool negate = false;
88    bool strict = true;
89    // NOLINTEND(misc-non-private-member-variables-in-classes)
90};
91
92using SmartCastTypes = std::optional<SmartCastTestArray>;
93
94class CheckerContext final {
95public:
96    explicit CheckerContext(Checker *checker, CheckerStatus newStatus) : CheckerContext(checker, newStatus, nullptr) {}
97
98    explicit CheckerContext(Checker *checker, CheckerStatus newStatus, const ETSObjectType *containingClass)
99        : CheckerContext(checker, newStatus, containingClass, nullptr)
100    {
101    }
102
103    explicit CheckerContext(Checker *checker, CheckerStatus newStatus, const ETSObjectType *containingClass,
104                            Signature *containingSignature);
105
106    CheckerContext() = delete;
107    ~CheckerContext() = default;
108
109    DEFAULT_COPY_SEMANTIC(CheckerContext);
110    DEFAULT_MOVE_SEMANTIC(CheckerContext);
111
112    [[nodiscard]] const CapturedVarsMap &CapturedVars() const noexcept
113    {
114        return capturedVars_;
115    }
116
117    [[nodiscard]] CapturedVarsMap &CapturedVars() noexcept
118    {
119        return capturedVars_;
120    }
121
122    [[nodiscard]] const CheckerStatus &Status() const noexcept
123    {
124        return status_;
125    }
126
127    [[nodiscard]] ETSObjectType *ContainingClass() const noexcept
128    {
129        return const_cast<ETSObjectType *>(containingClass_);
130    }
131
132    [[nodiscard]] Signature *ContainingSignature() const noexcept
133    {
134        return containingSignature_;
135    }
136
137    [[nodiscard]] CheckerStatus &Status() noexcept
138    {
139        return status_;
140    }
141
142    void SetContainingSignature(Signature *containingSignature) noexcept
143    {
144        containingSignature_ = containingSignature;
145    }
146
147    void SetContainingClass(ETSObjectType *containingClass) noexcept
148    {
149        containingClass_ = containingClass;
150    }
151
152    void AddCapturedVar(varbinder::Variable *var, const lexer::SourcePosition &pos)
153    {
154        capturedVars_.emplace(var, pos);
155    }
156    [[nodiscard]] ir::ArrowFunctionExpression *ContainingLambda() const noexcept
157    {
158        return containingLambda_;
159    }
160
161    void SetContainingLambda(ir::ArrowFunctionExpression *containingLambda) noexcept
162    {
163        containingLambda_ = containingLambda;
164    }
165
166    void ClearSmartCasts() noexcept
167    {
168        smartCasts_.clear();
169    }
170
171    void RemoveSmartCast(varbinder::Variable const *const variable) noexcept
172    {
173        smartCasts_.erase(variable);
174    }
175
176    void SetSmartCast(varbinder::Variable const *const variable, checker::Type *const smartType) noexcept;
177
178    [[nodiscard]] checker::Type *GetSmartCast(varbinder::Variable const *const variable) const noexcept;
179    [[nodiscard]] SmartCastArray CloneSmartCasts(bool clearData = false) noexcept;
180    void RestoreSmartCasts(SmartCastArray const &otherSmartCasts);
181    void CombineSmartCasts(SmartCastArray const &otherSmartCasts);
182
183    [[nodiscard]] SmartCastArray EnterTestExpression() noexcept
184    {
185        status_ |= CheckerStatus::IN_TEST_EXPRESSION;
186        ClearTestSmartCasts();
187        return CloneSmartCasts(false);
188    }
189
190    [[nodiscard]] bool IsInTestExpression() const noexcept
191    {
192        return (status_ & CheckerStatus::IN_TEST_EXPRESSION) != 0;
193    }
194
195    SmartCastTypes ExitTestExpression()
196    {
197        status_ &= ~CheckerStatus::IN_TEST_EXPRESSION;
198        CheckTestSmartCastCondition(lexer::TokenType::EOS);
199        return CloneTestSmartCasts(true);
200    }
201
202    [[nodiscard]] std::pair<SmartCastArray, bool> EnterLoop(ir::LoopStatement const &loop) noexcept;
203
204    [[nodiscard]] bool IsInLoop() const noexcept
205    {
206        return (status_ & CheckerStatus::IN_LOOP) != 0;
207    }
208
209    void ExitLoop(SmartCastArray &prevSmartCasts, bool clearFlag, ir::LoopStatement *loopStatement) noexcept;
210
211    void EnterPath() noexcept
212    {
213        status_ &= ~(CheckerStatus::MEET_RETURN | CheckerStatus::MEET_BREAK | CheckerStatus::MEET_CONTINUE |
214                     CheckerStatus::MEET_THROW);
215    }
216
217    [[nodiscard]] bool ExitPath() noexcept
218    {
219        auto const rc = (status_ & (CheckerStatus::MEET_RETURN | CheckerStatus::MEET_BREAK |
220                                    CheckerStatus::MEET_CONTINUE | CheckerStatus::MEET_THROW)) != 0;
221        status_ &= ~(CheckerStatus::MEET_RETURN | CheckerStatus::MEET_BREAK | CheckerStatus::MEET_CONTINUE |
222                     CheckerStatus::MEET_THROW);
223        return rc;
224    }
225
226    [[nodiscard]] SmartCastArray CheckTryBlock(ir::BlockStatement const &tryBlock) noexcept;
227
228    void CheckTestSmartCastCondition(lexer::TokenType operatorType);
229    void CheckIdentifierSmartCastCondition(ir::Identifier const *identifier) noexcept;
230    void CheckUnarySmartCastCondition(ir::UnaryExpression const *unaryExpression) noexcept;
231    void CheckBinarySmartCastCondition(ir::BinaryExpression *binaryExpression) noexcept;
232
233    void OnBreakStatement(ir::BreakStatement const *breakStatement);
234    void AddBreakSmartCasts(ir::Statement const *targetStatement, SmartCastArray &&smartCasts);
235    void CombineBreakSmartCasts(ir::Statement const *targetStatement);
236
237private:
238    Checker *parent_;
239    CheckerStatus status_;
240    CapturedVarsMap capturedVars_;
241    SmartCastMap smartCasts_;
242    const ETSObjectType *containingClass_ {nullptr};
243    ir::ArrowFunctionExpression *containingLambda_ {nullptr};
244    Signature *containingSignature_ {nullptr};
245
246    lexer::TokenType operatorType_ = lexer::TokenType::EOS;
247    SmartCastCondition testCondition_ {};
248    SmartCastTestMap testSmartCasts_;
249
250    PreservedSmartCastsMap breakSmartCasts_;
251
252    void RemoveSmartCasts(SmartCastArray const &otherSmartCasts) noexcept;
253    [[nodiscard]] checker::Type *CombineTypes(checker::Type *typeOne, checker::Type *typeTwo) const noexcept;
254    [[nodiscard]] static bool IsInValidChain(ir::AstNode const *parent) noexcept;
255    void CheckSmartCastEqualityCondition(ir::BinaryExpression *binaryExpression) noexcept;
256    [[nodiscard]] SmartCastTypes CloneTestSmartCasts(bool clearData = true) noexcept;
257    void ClearTestSmartCasts() noexcept;
258    [[nodiscard]] std::optional<SmartCastTuple> ResolveSmartCastTypes();
259    [[nodiscard]] bool CheckTestOrSmartCastCondition(SmartCastTuple const &types);
260    void CheckAssignments(ir::AstNode const *node, SmartVariables &changedVariables) noexcept;
261};
262}  // namespace ark::es2panda::checker
263
264#endif
265