• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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 "es2panda.h"
20 #include "generated/tokenType.h"
21 #include "lexer/token/sourceLocation.h"
22 #include "util/enumbitops.h"
23 
24 namespace ark::es2panda::ir {
25 class ArrowFunctionExpression;
26 class AstNode;
27 class LoopStatement;
28 class BlockStatement;
29 class Identifier;
30 class UnaryExpression;
31 class BreakStatement;
32 class Statement;
33 class BinaryExpression;
34 }  // namespace ark::es2panda::ir
35 namespace ark::es2panda::varbinder {
36 class Variable;
37 }  // namespace ark::es2panda::varbinder
38 
39 namespace ark::es2panda::checker {
40 
41 class ETSObjectType;
42 class Signature;
43 class Type;
44 class Checker;
45 
46 using ENUMBITOPS_OPERATORS;
47 
48 enum class CheckerStatus : uint32_t {
49     NO_OPTS = 0U,
50     FORCE_TUPLE = 1U << 0U,
51     IN_CONST_CONTEXT = 1U << 1U,
52     KEEP_LITERAL_TYPE = 1U << 2U,
53     IN_PARAMETER = 1U << 3U,
54     IN_CLASS = 1U << 4U,
55     IN_INTERFACE = 1U << 5U,
56     IN_ABSTRACT = 1U << 6U,
57     IN_STATIC_CONTEXT = 1U << 7U,
58     IN_CONSTRUCTOR = 1U << 8U,
59     IN_STATIC_BLOCK = 1U << 9U,
60     INNER_CLASS = 1U << 10U,
61     IN_ENUM = 1U << 11U,
62     BUILTINS_INITIALIZED = 1U << 12U,
63     IN_LAMBDA = 1U << 13U,
64     IGNORE_VISIBILITY = 1U << 14U,
65     IN_EXTENSION_METHOD = 1U << 15U,
66     IN_LOCAL_CLASS = 1U << 16U,
67     IN_INSTANCEOF_CONTEXT = 1U << 17U,
68     IN_TEST_EXPRESSION = 1U << 18U,
69     IN_LOOP = 1U << 19U,
70     MEET_RETURN = 1U << 20U,
71     MEET_BREAK = 1U << 21U,
72     MEET_CONTINUE = 1U << 22U,
73     MEET_THROW = 1U << 23U,
74     IN_EXTERNAL = 1U << 24U,
75     IN_BRIDGE_TEST = 1U << 25U,
76     IN_GETTER = 1U << 26U,
77     IN_SETTER = 1U << 27U,
78     IN_EXTENSION_ACCESSOR_CHECK = 1U << 28U,
79     IN_TYPE_INFER = 1U << 29U,
80 };
81 
82 }  // namespace ark::es2panda::checker
83 
84 template <>
85 struct enumbitops::IsAllowedType<ark::es2panda::checker::CheckerStatus> : std::true_type {
86 };
87 
88 namespace ark::es2panda::checker {
89 
90 using CapturedVarsMap = ArenaUnorderedMap<varbinder::Variable *, lexer::SourcePosition>;
91 using SmartCastMap = ArenaMap<varbinder::Variable const *, checker::Type *>;
92 using SmartCastArray = std::vector<std::pair<varbinder::Variable const *, checker::Type *>>;
93 using SmartCastTestMap = ArenaMap<varbinder::Variable const *, std::pair<checker::Type *, checker::Type *>>;
94 using SmartCastTuple = std::tuple<varbinder::Variable const *, checker::Type *, checker::Type *>;
95 using SmartCastTestArray = std::vector<SmartCastTuple>;
96 using PreservedSmartCastsMap = ArenaMultiMap<ir::AstNode const *, SmartCastArray>;
97 
98 // ReassignedVariableMap
99 //  - key: a variable that is on the left side of an assignment
100 //  - value: a boolean that marks, if the variable was accessed after it has been reassigned
101 using ReassignedVariableMap = std::unordered_map<varbinder::Variable const *, bool>;
102 
103 struct SmartCastCondition final {
104     SmartCastCondition() = default;
105     ~SmartCastCondition() = default;
106 
107     DEFAULT_COPY_SEMANTIC(SmartCastCondition);
108     DEFAULT_MOVE_SEMANTIC(SmartCastCondition);
109 
110     // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
111     varbinder::Variable const *variable = nullptr;
112     checker::Type *testedType = nullptr;
113     bool negate = false;
114     bool strict = true;
115     // NOLINTEND(misc-non-private-member-variables-in-classes)
116 };
117 
118 using SmartCastTypes = std::optional<SmartCastTestArray>;
119 
120 class CheckerContext final {
121 public:
122     explicit CheckerContext(Checker *checker, CheckerStatus newStatus) : CheckerContext(checker, newStatus, nullptr) {}
123 
124     explicit CheckerContext(Checker *checker, CheckerStatus newStatus, const ETSObjectType *containingClass)
125         : CheckerContext(checker, newStatus, containingClass, nullptr)
126     {
127     }
128 
129     explicit CheckerContext(Checker *checker, CheckerStatus newStatus, const ETSObjectType *containingClass,
130                             Signature *containingSignature);
131 
132     CheckerContext() = delete;
133     ~CheckerContext() = default;
134 
135     DEFAULT_COPY_SEMANTIC(CheckerContext);
136     DEFAULT_MOVE_SEMANTIC(CheckerContext);
137 
138     [[nodiscard]] const CapturedVarsMap &CapturedVars() const noexcept
139     {
140         return capturedVars_;
141     }
142 
143     [[nodiscard]] CapturedVarsMap &CapturedVars() noexcept
144     {
145         return capturedVars_;
146     }
147 
148     [[nodiscard]] const CheckerStatus &Status() const noexcept
149     {
150         return status_;
151     }
152 
153     [[nodiscard]] ETSObjectType *ContainingClass() const noexcept
154     {
155         return const_cast<ETSObjectType *>(containingClass_);
156     }
157 
158     [[nodiscard]] Signature *ContainingSignature() const noexcept
159     {
160         return containingSignature_;
161     }
162 
163     [[nodiscard]] CheckerStatus &Status() noexcept
164     {
165         return status_;
166     }
167 
168     void SetContainingSignature(Signature *containingSignature) noexcept
169     {
170         containingSignature_ = containingSignature;
171     }
172 
173     void SetContainingClass(ETSObjectType *containingClass) noexcept
174     {
175         containingClass_ = containingClass;
176     }
177 
178     void AddCapturedVar(varbinder::Variable *var, const lexer::SourcePosition &pos)
179     {
180         capturedVars_.emplace(var, pos);
181     }
182     [[nodiscard]] ir::ArrowFunctionExpression *ContainingLambda() const noexcept
183     {
184         return containingLambda_;
185     }
186 
187     void SetContainingLambda(ir::ArrowFunctionExpression *containingLambda) noexcept
188     {
189         containingLambda_ = containingLambda;
190     }
191 
192     void ClearSmartCasts() noexcept
193     {
194         smartCasts_.clear();
195     }
196 
197     void RemoveSmartCast(varbinder::Variable const *const variable) noexcept
198     {
199         smartCasts_.erase(variable);
200     }
201 
202     void SetSmartCast(varbinder::Variable const *const variable, checker::Type *const smartType) noexcept;
203 
204     [[nodiscard]] checker::Type *GetSmartCast(varbinder::Variable const *const variable) const noexcept;
205     [[nodiscard]] SmartCastArray CloneSmartCasts(bool clearData = false) noexcept;
206     void RestoreSmartCasts(SmartCastArray const &otherSmartCasts);
207     void CombineSmartCasts(SmartCastArray const &otherSmartCasts);
208 
209     [[nodiscard]] SmartCastArray EnterTestExpression() noexcept
210     {
211         status_ |= CheckerStatus::IN_TEST_EXPRESSION;
212         ClearTestSmartCasts();
213         return CloneSmartCasts(false);
214     }
215 
216     [[nodiscard]] bool IsInTestExpression() const noexcept
217     {
218         return (status_ & CheckerStatus::IN_TEST_EXPRESSION) != 0;
219     }
220 
221     SmartCastTypes ExitTestExpression()
222     {
223         status_ &= ~CheckerStatus::IN_TEST_EXPRESSION;
224         CheckTestSmartCastCondition(lexer::TokenType::EOS);
225         return CloneTestSmartCasts(true);
226     }
227 
228     [[nodiscard]] std::pair<SmartCastArray, bool> EnterLoop(const ir::LoopStatement &loop,
229                                                             SmartCastTypes loopConditionSmartCasts) noexcept;
230 
231     [[nodiscard]] bool IsInLoop() const noexcept
232     {
233         return (status_ & CheckerStatus::IN_LOOP) != 0;
234     }
235 
236     void ExitLoop(SmartCastArray &prevSmartCasts, bool clearFlag, ir::LoopStatement *loopStatement) noexcept;
237 
238     void EnterPath() noexcept
239     {
240         status_ &= ~(CheckerStatus::MEET_RETURN | CheckerStatus::MEET_BREAK | CheckerStatus::MEET_CONTINUE |
241                      CheckerStatus::MEET_THROW);
242     }
243 
244     [[nodiscard]] bool ExitPath() noexcept
245     {
246         auto const rc = (status_ & (CheckerStatus::MEET_RETURN | CheckerStatus::MEET_BREAK |
247                                     CheckerStatus::MEET_CONTINUE | CheckerStatus::MEET_THROW)) != 0;
248         status_ &= ~(CheckerStatus::MEET_RETURN | CheckerStatus::MEET_BREAK | CheckerStatus::MEET_CONTINUE |
249                      CheckerStatus::MEET_THROW);
250         return rc;
251     }
252 
253     [[nodiscard]] SmartCastArray CheckTryBlock(ir::BlockStatement const &tryBlock) noexcept;
254 
255     checker::Type *GetIntersectionOfTypes(checker::Type *type1, checker::Type *type2) const noexcept;
256     checker::Type *GetUnionOfTypes(checker::Type *type1, checker::Type *type2) const noexcept;
257     void MergeSmartTypesForLogicalAnd(SmartCastTuple &newSmartCastTypes);
258     void InvalidateNecessarySmartCastsInLogicalAnd(std::optional<SmartCastTuple> &newSmartCastTypes);
259     ReassignedVariableMap GetReassignedVariablesInNode(const ir::AstNode *node) const;
260     void CheckTestSmartCastCondition(lexer::TokenType operatorType);
261     void CheckIdentifierSmartCastCondition(ir::Identifier const *identifier) noexcept;
262     void CheckUnarySmartCastCondition(ir::UnaryExpression const *unaryExpression) noexcept;
263     void CheckBinarySmartCastCondition(ir::BinaryExpression *binaryExpression) noexcept;
264 
265     void OnBreakStatement(ir::BreakStatement const *breakStatement);
266     void AddBreakSmartCasts(ir::Statement const *targetStatement, SmartCastArray &&smartCasts);
267     void CombineBreakSmartCasts(ir::Statement const *targetStatement);
268     friend class SavedCheckerContextStatus;
269 
270 private:
271     Checker *parent_;
272     CheckerStatus status_;
273     CapturedVarsMap capturedVars_;
274     SmartCastMap smartCasts_;
275     const ETSObjectType *containingClass_ {nullptr};
276     ir::ArrowFunctionExpression *containingLambda_ {nullptr};
277     Signature *containingSignature_ {nullptr};
278 
279     lexer::TokenType operatorType_ = lexer::TokenType::EOS;
280     SmartCastCondition testCondition_ {};
281     SmartCastTestMap testSmartCasts_;
282 
283     PreservedSmartCastsMap breakSmartCasts_;
284 
285     void RemoveSmartCasts(SmartCastArray const &otherSmartCasts) noexcept;
286     [[nodiscard]] checker::Type *CombineTypes(checker::Type *typeOne, checker::Type *typeTwo) const noexcept;
287     [[nodiscard]] static bool IsInValidChain(ir::AstNode const *parent) noexcept;
288     void CheckSmartCastEqualityCondition(ir::BinaryExpression *binaryExpression) noexcept;
289     [[nodiscard]] SmartCastTypes CloneTestSmartCasts(bool clearData = true) noexcept;
290     void ClearTestSmartCasts() noexcept;
291     [[nodiscard]] std::optional<SmartCastTuple> ResolveSmartCastTypes();
292     [[nodiscard]] bool CheckTestOrSmartCastCondition(SmartCastTuple const &types);
293     void CheckAssignments(ir::AstNode const *node, ReassignedVariableMap &changedVariables) const noexcept;
294 };
295 
296 class SavedCheckerContextStatus final {
297 public:
298     explicit SavedCheckerContextStatus(CheckerContext *context, CheckerStatus status)
299         : context_(context), storedStatus_(status), preStatus_(context_->status_)
300     {
301         context_->status_ = context_->status_ | storedStatus_;
302     }
303 
304     ~SavedCheckerContextStatus()
305     {
306         context_->status_ = preStatus_;
307     }
308 
309     NO_COPY_SEMANTIC(SavedCheckerContextStatus);
310     DEFAULT_MOVE_SEMANTIC(SavedCheckerContextStatus);
311 
312 private:
313     CheckerContext *context_;
314     CheckerStatus storedStatus_;
315     CheckerStatus preStatus_;
316 };
317 }  // namespace ark::es2panda::checker
318 
319 #endif
320