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 
23 namespace ark::es2panda::checker {
24 
25 class ETSObjectType;
26 class Signature;
27 
28 using ENUMBITOPS_OPERATORS;
29 
30 enum 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 
62 template <>
63 struct enumbitops::IsAllowedType<ark::es2panda::checker::CheckerStatus> : std::true_type {
64 };
65 
66 namespace ark::es2panda::checker {
67 
68 using CapturedVarsMap = ArenaUnorderedMap<varbinder::Variable *, lexer::SourcePosition>;
69 using SmartCastMap = ArenaMap<varbinder::Variable const *, checker::Type *>;
70 using SmartCastArray = std::vector<std::pair<varbinder::Variable const *, checker::Type *>>;
71 using SmartCastTestMap = ArenaMap<varbinder::Variable const *, std::pair<checker::Type *, checker::Type *>>;
72 using SmartCastTuple = std::tuple<varbinder::Variable const *, checker::Type *, checker::Type *>;
73 using SmartCastTestArray = std::vector<SmartCastTuple>;
74 using PreservedSmartCastsMap = ArenaMultiMap<ir::AstNode const *, SmartCastArray>;
75 using SmartVariables = std::unordered_set<varbinder::Variable const *>;
76 
77 struct 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 
92 using SmartCastTypes = std::optional<SmartCastTestArray>;
93 
94 class CheckerContext final {
95 public:
CheckerContext(Checker *checker, CheckerStatus newStatus)96     explicit CheckerContext(Checker *checker, CheckerStatus newStatus) : CheckerContext(checker, newStatus, nullptr) {}
97 
CheckerContext(Checker *checker, CheckerStatus newStatus, const ETSObjectType *containingClass)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 
AddCapturedVar(varbinder::Variable *var, const lexer::SourcePosition &pos)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 
ExitTestExpression()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 
237 private:
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