13af6ab5fSopenharmony_ci/** 23af6ab5fSopenharmony_ci * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 33af6ab5fSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 43af6ab5fSopenharmony_ci * you may not use this file except in compliance with the License. 53af6ab5fSopenharmony_ci * You may obtain a copy of the License at 63af6ab5fSopenharmony_ci * 73af6ab5fSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 83af6ab5fSopenharmony_ci * 93af6ab5fSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 103af6ab5fSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 113af6ab5fSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 123af6ab5fSopenharmony_ci * See the License for the specific language governing permissions and 133af6ab5fSopenharmony_ci * limitations under the License. 143af6ab5fSopenharmony_ci */ 153af6ab5fSopenharmony_ci 163af6ab5fSopenharmony_ci#include "ETSchecker.h" 173af6ab5fSopenharmony_ci 183af6ab5fSopenharmony_cinamespace ark::es2panda::checker { 193af6ab5fSopenharmony_ci 203af6ab5fSopenharmony_ciCheckerContext::CheckerContext(Checker *checker, CheckerStatus newStatus, ETSObjectType const *containingClass, 213af6ab5fSopenharmony_ci Signature *containingSignature) 223af6ab5fSopenharmony_ci : parent_(checker), 233af6ab5fSopenharmony_ci status_(newStatus), 243af6ab5fSopenharmony_ci capturedVars_(parent_->Allocator()->Adapter()), 253af6ab5fSopenharmony_ci smartCasts_(parent_->Allocator()->Adapter()), 263af6ab5fSopenharmony_ci containingClass_(containingClass), 273af6ab5fSopenharmony_ci containingSignature_(containingSignature), 283af6ab5fSopenharmony_ci testSmartCasts_(parent_->Allocator()->Adapter()), 293af6ab5fSopenharmony_ci breakSmartCasts_(parent_->Allocator()->Adapter()) 303af6ab5fSopenharmony_ci{ 313af6ab5fSopenharmony_ci} 323af6ab5fSopenharmony_ci 333af6ab5fSopenharmony_civoid CheckerContext::SetSmartCast(varbinder::Variable const *const variable, checker::Type *const smartType) noexcept 343af6ab5fSopenharmony_ci{ 353af6ab5fSopenharmony_ci // Just block captured and modified variables here instead of finding all their usage occurrences. 363af6ab5fSopenharmony_ci if (!variable->HasFlag(varbinder::VariableFlags::CAPTURED_MODIFIED)) { 373af6ab5fSopenharmony_ci smartCasts_.insert_or_assign(variable, smartType); 383af6ab5fSopenharmony_ci } 393af6ab5fSopenharmony_ci} 403af6ab5fSopenharmony_ci 413af6ab5fSopenharmony_ciSmartCastTypes CheckerContext::CloneTestSmartCasts(bool const clearData) noexcept 423af6ab5fSopenharmony_ci{ 433af6ab5fSopenharmony_ci if (testSmartCasts_.empty()) { 443af6ab5fSopenharmony_ci return std::nullopt; 453af6ab5fSopenharmony_ci } 463af6ab5fSopenharmony_ci 473af6ab5fSopenharmony_ci SmartCastTestArray smartCasts {}; 483af6ab5fSopenharmony_ci smartCasts.reserve(testSmartCasts_.size()); 493af6ab5fSopenharmony_ci 503af6ab5fSopenharmony_ci for (auto [variable, types] : testSmartCasts_) { 513af6ab5fSopenharmony_ci if (types.first != nullptr || types.second != nullptr) { 523af6ab5fSopenharmony_ci smartCasts.emplace_back(variable, types.first, types.second); 533af6ab5fSopenharmony_ci } 543af6ab5fSopenharmony_ci } 553af6ab5fSopenharmony_ci 563af6ab5fSopenharmony_ci if (clearData) { 573af6ab5fSopenharmony_ci ClearTestSmartCasts(); 583af6ab5fSopenharmony_ci } 593af6ab5fSopenharmony_ci 603af6ab5fSopenharmony_ci return std::make_optional(smartCasts); 613af6ab5fSopenharmony_ci} 623af6ab5fSopenharmony_ci 633af6ab5fSopenharmony_ciSmartCastArray CheckerContext::CloneSmartCasts(bool const clearData) noexcept 643af6ab5fSopenharmony_ci{ 653af6ab5fSopenharmony_ci SmartCastArray smartCasts {}; 663af6ab5fSopenharmony_ci 673af6ab5fSopenharmony_ci if (!smartCasts_.empty()) { 683af6ab5fSopenharmony_ci smartCasts.reserve(smartCasts_.size()); 693af6ab5fSopenharmony_ci 703af6ab5fSopenharmony_ci for (auto const [variable, type] : smartCasts_) { 713af6ab5fSopenharmony_ci smartCasts.emplace_back(variable, type); 723af6ab5fSopenharmony_ci } 733af6ab5fSopenharmony_ci } 743af6ab5fSopenharmony_ci 753af6ab5fSopenharmony_ci if (clearData) { 763af6ab5fSopenharmony_ci ClearSmartCasts(); 773af6ab5fSopenharmony_ci } 783af6ab5fSopenharmony_ci 793af6ab5fSopenharmony_ci return smartCasts; 803af6ab5fSopenharmony_ci} 813af6ab5fSopenharmony_ci 823af6ab5fSopenharmony_civoid CheckerContext::RestoreSmartCasts(SmartCastArray const &otherSmartCasts) 833af6ab5fSopenharmony_ci{ 843af6ab5fSopenharmony_ci smartCasts_.clear(); 853af6ab5fSopenharmony_ci if (!otherSmartCasts.empty()) { 863af6ab5fSopenharmony_ci for (auto [variable, type] : otherSmartCasts) { 873af6ab5fSopenharmony_ci smartCasts_.emplace(variable, type); 883af6ab5fSopenharmony_ci } 893af6ab5fSopenharmony_ci } 903af6ab5fSopenharmony_ci} 913af6ab5fSopenharmony_ci 923af6ab5fSopenharmony_civoid CheckerContext::RemoveSmartCasts(SmartCastArray const &otherSmartCasts) noexcept 933af6ab5fSopenharmony_ci{ 943af6ab5fSopenharmony_ci if (!smartCasts_.empty()) { 953af6ab5fSopenharmony_ci auto it = smartCasts_.begin(); 963af6ab5fSopenharmony_ci while (it != smartCasts_.end()) { 973af6ab5fSopenharmony_ci if (std::find_if(otherSmartCasts.begin(), otherSmartCasts.end(), [&it](auto const &item) -> bool { 983af6ab5fSopenharmony_ci return item.first == it->first; 993af6ab5fSopenharmony_ci }) == otherSmartCasts.end()) { 1003af6ab5fSopenharmony_ci it = smartCasts_.erase(it); 1013af6ab5fSopenharmony_ci } else { 1023af6ab5fSopenharmony_ci ++it; 1033af6ab5fSopenharmony_ci } 1043af6ab5fSopenharmony_ci } 1053af6ab5fSopenharmony_ci } 1063af6ab5fSopenharmony_ci} 1073af6ab5fSopenharmony_ci 1083af6ab5fSopenharmony_ci// Auxiliary private method returning combined type (if types differ) or 'nullptr' if types are identical 1093af6ab5fSopenharmony_ci// and no smart cast change is required. 1103af6ab5fSopenharmony_cichecker::Type *CheckerContext::CombineTypes(checker::Type *const typeOne, checker::Type *const typeTwo) const noexcept 1113af6ab5fSopenharmony_ci{ 1123af6ab5fSopenharmony_ci ASSERT(typeOne != nullptr && typeTwo != nullptr); 1133af6ab5fSopenharmony_ci auto *const checker = parent_->AsETSChecker(); 1143af6ab5fSopenharmony_ci 1153af6ab5fSopenharmony_ci if (checker->Relation()->IsIdenticalTo(typeOne, typeTwo)) { 1163af6ab5fSopenharmony_ci return nullptr; 1173af6ab5fSopenharmony_ci } 1183af6ab5fSopenharmony_ci 1193af6ab5fSopenharmony_ci return checker->CreateETSUnionType({typeOne, typeTwo}); 1203af6ab5fSopenharmony_ci} 1213af6ab5fSopenharmony_ci 1223af6ab5fSopenharmony_civoid CheckerContext::CombineSmartCasts(SmartCastArray const &otherSmartCasts) 1233af6ab5fSopenharmony_ci{ 1243af6ab5fSopenharmony_ci auto *const checker = parent_->AsETSChecker(); 1253af6ab5fSopenharmony_ci 1263af6ab5fSopenharmony_ci for (auto [variable, type] : otherSmartCasts) { 1273af6ab5fSopenharmony_ci auto const it = smartCasts_.find(variable); 1283af6ab5fSopenharmony_ci if (it == smartCasts_.end()) { 1293af6ab5fSopenharmony_ci continue; 1303af6ab5fSopenharmony_ci } 1313af6ab5fSopenharmony_ci // Smart cast presents in both sets 1323af6ab5fSopenharmony_ci if (auto *const smartType = CombineTypes(it->second, type); smartType != nullptr) { 1333af6ab5fSopenharmony_ci // Remove it or set to new combined value 1343af6ab5fSopenharmony_ci if (checker->Relation()->IsIdenticalTo(it->first->TsType(), smartType)) { 1353af6ab5fSopenharmony_ci smartCasts_.erase(it); 1363af6ab5fSopenharmony_ci } else { 1373af6ab5fSopenharmony_ci it->second = smartType; 1383af6ab5fSopenharmony_ci } 1393af6ab5fSopenharmony_ci } 1403af6ab5fSopenharmony_ci } 1413af6ab5fSopenharmony_ci 1423af6ab5fSopenharmony_ci // Remove smart casts that don't present in the other set. 1433af6ab5fSopenharmony_ci RemoveSmartCasts(otherSmartCasts); 1443af6ab5fSopenharmony_ci} 1453af6ab5fSopenharmony_ci 1463af6ab5fSopenharmony_ci// Second return value shows if the 'IN_LOOP' flag should be cleared on exit from the loop (case of nested loops). 1473af6ab5fSopenharmony_cistd::pair<SmartCastArray, bool> CheckerContext::EnterLoop(ir::LoopStatement const &loop) noexcept 1483af6ab5fSopenharmony_ci{ 1493af6ab5fSopenharmony_ci bool const clearFlag = !IsInLoop(); 1503af6ab5fSopenharmony_ci if (clearFlag) { 1513af6ab5fSopenharmony_ci status_ |= CheckerStatus::IN_LOOP; 1523af6ab5fSopenharmony_ci } 1533af6ab5fSopenharmony_ci 1543af6ab5fSopenharmony_ci auto smartCasts = CloneSmartCasts(); 1553af6ab5fSopenharmony_ci 1563af6ab5fSopenharmony_ci SmartVariables changedVariables {}; 1573af6ab5fSopenharmony_ci loop.Iterate([this, &changedVariables](ir::AstNode *childNode) { CheckAssignments(childNode, changedVariables); }); 1583af6ab5fSopenharmony_ci 1593af6ab5fSopenharmony_ci if (!changedVariables.empty()) { 1603af6ab5fSopenharmony_ci for (auto const *variable : changedVariables) { 1613af6ab5fSopenharmony_ci smartCasts_.erase(variable); 1623af6ab5fSopenharmony_ci } 1633af6ab5fSopenharmony_ci } 1643af6ab5fSopenharmony_ci 1653af6ab5fSopenharmony_ci return {std::move(smartCasts), clearFlag}; 1663af6ab5fSopenharmony_ci} 1673af6ab5fSopenharmony_ci 1683af6ab5fSopenharmony_civoid CheckerContext::ExitLoop(SmartCastArray &prevSmartCasts, bool const clearFlag, 1693af6ab5fSopenharmony_ci ir::LoopStatement *loopStatement) noexcept 1703af6ab5fSopenharmony_ci{ 1713af6ab5fSopenharmony_ci if (clearFlag) { 1723af6ab5fSopenharmony_ci status_ &= ~CheckerStatus::IN_LOOP; 1733af6ab5fSopenharmony_ci } 1743af6ab5fSopenharmony_ci 1753af6ab5fSopenharmony_ci if (!breakSmartCasts_.empty()) { 1763af6ab5fSopenharmony_ci auto it = breakSmartCasts_.begin(); 1773af6ab5fSopenharmony_ci 1783af6ab5fSopenharmony_ci while (it != breakSmartCasts_.end()) { 1793af6ab5fSopenharmony_ci if (it->first != loopStatement) { 1803af6ab5fSopenharmony_ci ++it; 1813af6ab5fSopenharmony_ci } else { 1823af6ab5fSopenharmony_ci CombineSmartCasts(it->second); 1833af6ab5fSopenharmony_ci it = breakSmartCasts_.erase(it); 1843af6ab5fSopenharmony_ci } 1853af6ab5fSopenharmony_ci } 1863af6ab5fSopenharmony_ci } 1873af6ab5fSopenharmony_ci 1883af6ab5fSopenharmony_ci // Now we don't process smart casts inside the loops correctly, thus just combine them on exit from the loop. 1893af6ab5fSopenharmony_ci CombineSmartCasts(prevSmartCasts); 1903af6ab5fSopenharmony_ci} 1913af6ab5fSopenharmony_ci 1923af6ab5fSopenharmony_civoid CheckerContext::CheckAssignments(ir::AstNode const *node, SmartVariables &changedVariables) noexcept 1933af6ab5fSopenharmony_ci{ 1943af6ab5fSopenharmony_ci if (node == nullptr) { // Just in case! 1953af6ab5fSopenharmony_ci return; 1963af6ab5fSopenharmony_ci } 1973af6ab5fSopenharmony_ci 1983af6ab5fSopenharmony_ci if (!node->IsAssignmentExpression()) { 1993af6ab5fSopenharmony_ci node->Iterate( 2003af6ab5fSopenharmony_ci [this, &changedVariables](ir::AstNode *childNode) { CheckAssignments(childNode, changedVariables); }); 2013af6ab5fSopenharmony_ci return; 2023af6ab5fSopenharmony_ci } 2033af6ab5fSopenharmony_ci 2043af6ab5fSopenharmony_ci auto const *assignment = node->AsAssignmentExpression(); 2053af6ab5fSopenharmony_ci if (assignment->Left()->IsIdentifier()) { 2063af6ab5fSopenharmony_ci auto const *const ident = assignment->Left()->AsIdentifier(); 2073af6ab5fSopenharmony_ci 2083af6ab5fSopenharmony_ci auto const *variable = ident->Variable(); 2093af6ab5fSopenharmony_ci if (variable == nullptr) { 2103af6ab5fSopenharmony_ci // NOTE: we're interesting in the local variables ONLY! 2113af6ab5fSopenharmony_ci variable = parent_->AsETSChecker()->FindVariableInFunctionScope( 2123af6ab5fSopenharmony_ci ident->Name(), varbinder::ResolveBindingOptions::ALL_NON_TYPE); 2133af6ab5fSopenharmony_ci } 2143af6ab5fSopenharmony_ci 2153af6ab5fSopenharmony_ci if (variable != nullptr) { 2163af6ab5fSopenharmony_ci changedVariables.insert(variable); 2173af6ab5fSopenharmony_ci } 2183af6ab5fSopenharmony_ci } 2193af6ab5fSopenharmony_ci 2203af6ab5fSopenharmony_ci assignment->Right()->Iterate( 2213af6ab5fSopenharmony_ci [this, &changedVariables](ir::AstNode *childNode) { CheckAssignments(childNode, changedVariables); }); 2223af6ab5fSopenharmony_ci} 2233af6ab5fSopenharmony_ci 2243af6ab5fSopenharmony_ciSmartCastArray CheckerContext::CheckTryBlock(ir::BlockStatement const &tryBlock) noexcept 2253af6ab5fSopenharmony_ci{ 2263af6ab5fSopenharmony_ci SmartVariables changedVariables {}; 2273af6ab5fSopenharmony_ci tryBlock.Iterate( 2283af6ab5fSopenharmony_ci [this, &changedVariables](ir::AstNode *childNode) { CheckAssignments(childNode, changedVariables); }); 2293af6ab5fSopenharmony_ci 2303af6ab5fSopenharmony_ci SmartCastArray smartCasts {}; 2313af6ab5fSopenharmony_ci if (!smartCasts_.empty()) { 2323af6ab5fSopenharmony_ci smartCasts.reserve(smartCasts_.size()); 2333af6ab5fSopenharmony_ci 2343af6ab5fSopenharmony_ci for (auto const [variable, type] : smartCasts_) { 2353af6ab5fSopenharmony_ci if (changedVariables.find(variable) == changedVariables.end()) { 2363af6ab5fSopenharmony_ci smartCasts.emplace_back(variable, type); 2373af6ab5fSopenharmony_ci } 2383af6ab5fSopenharmony_ci } 2393af6ab5fSopenharmony_ci } 2403af6ab5fSopenharmony_ci 2413af6ab5fSopenharmony_ci return smartCasts; 2423af6ab5fSopenharmony_ci} 2433af6ab5fSopenharmony_ci 2443af6ab5fSopenharmony_ci// Check that the expression is a part of logical OR/AND or unary negation operators chain 2453af6ab5fSopenharmony_ci// (other cases are not interested) 2463af6ab5fSopenharmony_cibool CheckerContext::IsInValidChain(ir::AstNode const *parent) noexcept 2473af6ab5fSopenharmony_ci{ 2483af6ab5fSopenharmony_ci while (parent != nullptr && !parent->IsIfStatement() && !parent->IsConditionalExpression()) { 2493af6ab5fSopenharmony_ci if (parent->IsBinaryExpression()) { 2503af6ab5fSopenharmony_ci auto const operation = parent->AsBinaryExpression()->OperatorType(); 2513af6ab5fSopenharmony_ci if (operation != lexer::TokenType::PUNCTUATOR_LOGICAL_OR && 2523af6ab5fSopenharmony_ci operation != lexer::TokenType::PUNCTUATOR_LOGICAL_AND) { 2533af6ab5fSopenharmony_ci return false; 2543af6ab5fSopenharmony_ci } 2553af6ab5fSopenharmony_ci } else if (parent->IsUnaryExpression()) { 2563af6ab5fSopenharmony_ci if (parent->AsUnaryExpression()->OperatorType() != lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK) { 2573af6ab5fSopenharmony_ci return false; 2583af6ab5fSopenharmony_ci } 2593af6ab5fSopenharmony_ci } else { 2603af6ab5fSopenharmony_ci return false; 2613af6ab5fSopenharmony_ci } 2623af6ab5fSopenharmony_ci parent = parent->Parent(); 2633af6ab5fSopenharmony_ci } 2643af6ab5fSopenharmony_ci return parent != nullptr; 2653af6ab5fSopenharmony_ci} 2663af6ab5fSopenharmony_ci 2673af6ab5fSopenharmony_civoid CheckerContext::CheckIdentifierSmartCastCondition(ir::Identifier const *const identifier) noexcept 2683af6ab5fSopenharmony_ci{ 2693af6ab5fSopenharmony_ci if (!IsInTestExpression()) { 2703af6ab5fSopenharmony_ci return; 2713af6ab5fSopenharmony_ci } 2723af6ab5fSopenharmony_ci 2733af6ab5fSopenharmony_ci auto const *const variable = identifier->Variable(); 2743af6ab5fSopenharmony_ci ASSERT(variable != nullptr); 2753af6ab5fSopenharmony_ci 2763af6ab5fSopenharmony_ci // Smart cast for extended conditional check can be applied only to the variables of reference types. 2773af6ab5fSopenharmony_ci if (auto const *const variableType = variable->TsType(); !variableType->IsETSReferenceType()) { 2783af6ab5fSopenharmony_ci return; 2793af6ab5fSopenharmony_ci } 2803af6ab5fSopenharmony_ci 2813af6ab5fSopenharmony_ci if (!IsInValidChain(identifier->Parent())) { 2823af6ab5fSopenharmony_ci return; 2833af6ab5fSopenharmony_ci } 2843af6ab5fSopenharmony_ci 2853af6ab5fSopenharmony_ci ASSERT(testCondition_.variable == nullptr); 2863af6ab5fSopenharmony_ci if (identifier->TsType()->PossiblyETSNullish()) { 2873af6ab5fSopenharmony_ci testCondition_ = {variable, parent_->AsETSChecker()->GlobalETSNullType(), true, false}; 2883af6ab5fSopenharmony_ci } 2893af6ab5fSopenharmony_ci} 2903af6ab5fSopenharmony_ci 2913af6ab5fSopenharmony_civoid CheckerContext::CheckUnarySmartCastCondition(ir::UnaryExpression const *const unaryExpression) noexcept 2923af6ab5fSopenharmony_ci{ 2933af6ab5fSopenharmony_ci if (!IsInTestExpression() || unaryExpression->OperatorType() != lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK) { 2943af6ab5fSopenharmony_ci return; 2953af6ab5fSopenharmony_ci } 2963af6ab5fSopenharmony_ci 2973af6ab5fSopenharmony_ci auto const *const argument = unaryExpression->Argument(); 2983af6ab5fSopenharmony_ci if (argument == nullptr || (!argument->IsIdentifier() && !argument->IsBinaryExpression())) { 2993af6ab5fSopenharmony_ci return; 3003af6ab5fSopenharmony_ci } 3013af6ab5fSopenharmony_ci 3023af6ab5fSopenharmony_ci if (!IsInValidChain(unaryExpression->Parent())) { 3033af6ab5fSopenharmony_ci return; 3043af6ab5fSopenharmony_ci } 3053af6ab5fSopenharmony_ci 3063af6ab5fSopenharmony_ci if (testCondition_.variable != nullptr) { 3073af6ab5fSopenharmony_ci testCondition_.negate = !testCondition_.negate; 3083af6ab5fSopenharmony_ci } 3093af6ab5fSopenharmony_ci} 3103af6ab5fSopenharmony_ci 3113af6ab5fSopenharmony_civoid CheckerContext::CheckBinarySmartCastCondition(ir::BinaryExpression *const binaryExpression) noexcept 3123af6ab5fSopenharmony_ci{ 3133af6ab5fSopenharmony_ci if (!IsInTestExpression() || !IsInValidChain(binaryExpression->Parent())) { 3143af6ab5fSopenharmony_ci return; 3153af6ab5fSopenharmony_ci } 3163af6ab5fSopenharmony_ci 3173af6ab5fSopenharmony_ci if (auto const operatorType = binaryExpression->OperatorType(); operatorType == lexer::TokenType::KEYW_INSTANCEOF) { 3183af6ab5fSopenharmony_ci ASSERT(testCondition_.variable == nullptr); 3193af6ab5fSopenharmony_ci if (binaryExpression->Left()->IsIdentifier()) { 3203af6ab5fSopenharmony_ci testCondition_ = {binaryExpression->Left()->AsIdentifier()->Variable(), 3213af6ab5fSopenharmony_ci binaryExpression->Right()->TsType()}; 3223af6ab5fSopenharmony_ci } 3233af6ab5fSopenharmony_ci } else if (operatorType == lexer::TokenType::PUNCTUATOR_STRICT_EQUAL || 3243af6ab5fSopenharmony_ci operatorType == lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL || 3253af6ab5fSopenharmony_ci operatorType == lexer::TokenType::PUNCTUATOR_EQUAL || 3263af6ab5fSopenharmony_ci operatorType == lexer::TokenType::PUNCTUATOR_NOT_EQUAL) { 3273af6ab5fSopenharmony_ci ASSERT(testCondition_.variable == nullptr); 3283af6ab5fSopenharmony_ci CheckSmartCastEqualityCondition(binaryExpression); 3293af6ab5fSopenharmony_ci } 3303af6ab5fSopenharmony_ci} 3313af6ab5fSopenharmony_ci 3323af6ab5fSopenharmony_ci// Extracted just to avoid large length and depth of method 'CheckBinarySmartCastCondition()'. 3333af6ab5fSopenharmony_civoid CheckerContext::CheckSmartCastEqualityCondition(ir::BinaryExpression *const binaryExpression) noexcept 3343af6ab5fSopenharmony_ci{ 3353af6ab5fSopenharmony_ci varbinder::Variable const *variable = nullptr; 3363af6ab5fSopenharmony_ci checker::Type *testedType = nullptr; 3373af6ab5fSopenharmony_ci auto const operatorType = binaryExpression->OperatorType(); 3383af6ab5fSopenharmony_ci 3393af6ab5fSopenharmony_ci bool strict = operatorType == lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL || 3403af6ab5fSopenharmony_ci operatorType == lexer::TokenType::PUNCTUATOR_STRICT_EQUAL; 3413af6ab5fSopenharmony_ci 3423af6ab5fSopenharmony_ci // extracted just to avoid extra nested level 3433af6ab5fSopenharmony_ci auto const getTestedType = [&variable, &testedType, &strict](ir::Identifier const *const identifier, 3443af6ab5fSopenharmony_ci ir::Expression *const expression) -> void { 3453af6ab5fSopenharmony_ci ASSERT(identifier != nullptr && expression != nullptr); 3463af6ab5fSopenharmony_ci variable = identifier->Variable(); 3473af6ab5fSopenharmony_ci if (expression->IsLiteral()) { 3483af6ab5fSopenharmony_ci testedType = expression->TsType(); 3493af6ab5fSopenharmony_ci if (!expression->IsNullLiteral() && !expression->IsUndefinedLiteral()) { 3503af6ab5fSopenharmony_ci strict = false; 3513af6ab5fSopenharmony_ci } 3523af6ab5fSopenharmony_ci } 3533af6ab5fSopenharmony_ci }; 3543af6ab5fSopenharmony_ci 3553af6ab5fSopenharmony_ci if (binaryExpression->Left()->IsIdentifier()) { 3563af6ab5fSopenharmony_ci getTestedType(binaryExpression->Left()->AsIdentifier(), binaryExpression->Right()); 3573af6ab5fSopenharmony_ci } 3583af6ab5fSopenharmony_ci 3593af6ab5fSopenharmony_ci if (testedType == nullptr && binaryExpression->Right()->IsIdentifier()) { 3603af6ab5fSopenharmony_ci getTestedType(binaryExpression->Right()->AsIdentifier(), binaryExpression->Left()); 3613af6ab5fSopenharmony_ci } 3623af6ab5fSopenharmony_ci 3633af6ab5fSopenharmony_ci if (testedType != nullptr) { 3643af6ab5fSopenharmony_ci bool const negate = operatorType == lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL || 3653af6ab5fSopenharmony_ci operatorType == lexer::TokenType::PUNCTUATOR_NOT_EQUAL; 3663af6ab5fSopenharmony_ci 3673af6ab5fSopenharmony_ci if (testedType->DefinitelyETSNullish()) { 3683af6ab5fSopenharmony_ci testCondition_ = {variable, testedType, negate, strict}; 3693af6ab5fSopenharmony_ci } 3703af6ab5fSopenharmony_ci } 3713af6ab5fSopenharmony_ci} 3723af6ab5fSopenharmony_ci 3733af6ab5fSopenharmony_civoid CheckerContext::ClearTestSmartCasts() noexcept 3743af6ab5fSopenharmony_ci{ 3753af6ab5fSopenharmony_ci testCondition_ = {}; 3763af6ab5fSopenharmony_ci testSmartCasts_.clear(); 3773af6ab5fSopenharmony_ci operatorType_ = lexer::TokenType::EOS; 3783af6ab5fSopenharmony_ci} 3793af6ab5fSopenharmony_ci 3803af6ab5fSopenharmony_cichecker::Type *CheckerContext::GetSmartCast(varbinder::Variable const *const variable) const noexcept 3813af6ab5fSopenharmony_ci{ 3823af6ab5fSopenharmony_ci if (IsInTestExpression()) { 3833af6ab5fSopenharmony_ci if (operatorType_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) { 3843af6ab5fSopenharmony_ci if (auto const it = testSmartCasts_.find(variable); 3853af6ab5fSopenharmony_ci it != testSmartCasts_.end() && it->second.first != nullptr) { 3863af6ab5fSopenharmony_ci return it->second.first; 3873af6ab5fSopenharmony_ci } 3883af6ab5fSopenharmony_ci } else if (operatorType_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR) { 3893af6ab5fSopenharmony_ci if (auto const it = testSmartCasts_.find(variable); 3903af6ab5fSopenharmony_ci it != testSmartCasts_.end() && it->second.second != nullptr) { 3913af6ab5fSopenharmony_ci return it->second.second; 3923af6ab5fSopenharmony_ci } 3933af6ab5fSopenharmony_ci } 3943af6ab5fSopenharmony_ci } 3953af6ab5fSopenharmony_ci 3963af6ab5fSopenharmony_ci auto const it = smartCasts_.find(variable); 3973af6ab5fSopenharmony_ci return it == smartCasts_.end() ? nullptr : it->second; 3983af6ab5fSopenharmony_ci} 3993af6ab5fSopenharmony_ci 4003af6ab5fSopenharmony_civoid CheckerContext::OnBreakStatement(ir::BreakStatement const *breakStatement) 4013af6ab5fSopenharmony_ci{ 4023af6ab5fSopenharmony_ci ir::Statement const *targetStatement = breakStatement->Target()->AsStatement(); 4033af6ab5fSopenharmony_ci ASSERT(targetStatement != nullptr); 4043af6ab5fSopenharmony_ci if (targetStatement->IsLabelledStatement()) { 4053af6ab5fSopenharmony_ci targetStatement = targetStatement->AsLabelledStatement()->Body(); 4063af6ab5fSopenharmony_ci } 4073af6ab5fSopenharmony_ci ASSERT(targetStatement != nullptr); 4083af6ab5fSopenharmony_ci 4093af6ab5fSopenharmony_ci auto const inInnerScope = [targetStatement](varbinder::Scope const *scope, ir::AstNode const *parent) -> bool { 4103af6ab5fSopenharmony_ci do { 4113af6ab5fSopenharmony_ci parent = parent->Parent(); 4123af6ab5fSopenharmony_ci if (parent->IsScopeBearer() && parent->Scope() == scope) { 4133af6ab5fSopenharmony_ci return true; 4143af6ab5fSopenharmony_ci } 4153af6ab5fSopenharmony_ci } while (parent != targetStatement); 4163af6ab5fSopenharmony_ci return false; 4173af6ab5fSopenharmony_ci }; 4183af6ab5fSopenharmony_ci 4193af6ab5fSopenharmony_ci status_ |= CheckerStatus::MEET_BREAK; 4203af6ab5fSopenharmony_ci 4213af6ab5fSopenharmony_ci if (smartCasts_.empty()) { 4223af6ab5fSopenharmony_ci return; 4233af6ab5fSopenharmony_ci } 4243af6ab5fSopenharmony_ci 4253af6ab5fSopenharmony_ci SmartCastArray smartCasts {}; 4263af6ab5fSopenharmony_ci smartCasts.reserve(smartCasts_.size()); 4273af6ab5fSopenharmony_ci 4283af6ab5fSopenharmony_ci for (auto const [variable, type] : smartCasts_) { 4293af6ab5fSopenharmony_ci if (!inInnerScope(variable->AsLocalVariable()->GetScope(), breakStatement)) { 4303af6ab5fSopenharmony_ci smartCasts.emplace_back(variable, type); 4313af6ab5fSopenharmony_ci } 4323af6ab5fSopenharmony_ci } 4333af6ab5fSopenharmony_ci 4343af6ab5fSopenharmony_ci if (!smartCasts.empty()) { 4353af6ab5fSopenharmony_ci AddBreakSmartCasts(targetStatement, std::move(smartCasts)); 4363af6ab5fSopenharmony_ci } 4373af6ab5fSopenharmony_ci 4383af6ab5fSopenharmony_ci ClearSmartCasts(); 4393af6ab5fSopenharmony_ci} 4403af6ab5fSopenharmony_ci 4413af6ab5fSopenharmony_civoid CheckerContext::AddBreakSmartCasts(ir::Statement const *targetStatement, SmartCastArray &&smartCasts) 4423af6ab5fSopenharmony_ci{ 4433af6ab5fSopenharmony_ci breakSmartCasts_.emplace(targetStatement, std::move(smartCasts)); 4443af6ab5fSopenharmony_ci} 4453af6ab5fSopenharmony_ci 4463af6ab5fSopenharmony_civoid CheckerContext::CombineBreakSmartCasts(ir::Statement const *targetStatement) 4473af6ab5fSopenharmony_ci{ 4483af6ab5fSopenharmony_ci ASSERT(smartCasts_.empty()); 4493af6ab5fSopenharmony_ci 4503af6ab5fSopenharmony_ci if (!breakSmartCasts_.empty()) { 4513af6ab5fSopenharmony_ci bool firstCase = true; 4523af6ab5fSopenharmony_ci auto it = breakSmartCasts_.begin(); 4533af6ab5fSopenharmony_ci 4543af6ab5fSopenharmony_ci while (it != breakSmartCasts_.end()) { 4553af6ab5fSopenharmony_ci if (it->first != targetStatement) { 4563af6ab5fSopenharmony_ci ++it; 4573af6ab5fSopenharmony_ci continue; 4583af6ab5fSopenharmony_ci } 4593af6ab5fSopenharmony_ci 4603af6ab5fSopenharmony_ci if (firstCase) { 4613af6ab5fSopenharmony_ci firstCase = false; 4623af6ab5fSopenharmony_ci RestoreSmartCasts(it->second); 4633af6ab5fSopenharmony_ci } else { 4643af6ab5fSopenharmony_ci CombineSmartCasts(it->second); 4653af6ab5fSopenharmony_ci } 4663af6ab5fSopenharmony_ci 4673af6ab5fSopenharmony_ci it = breakSmartCasts_.erase(it); 4683af6ab5fSopenharmony_ci } 4693af6ab5fSopenharmony_ci } 4703af6ab5fSopenharmony_ci} 4713af6ab5fSopenharmony_ci} // namespace ark::es2panda::checker 472