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