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