1/* 2 * Copyright (c) 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#include "checkInfiniteLoop.h" 17#include "ir/statements/forUpdateStatement.h" 18#include "checker/types/type.h" 19#include "ir/statements/doWhileStatement.h" 20#include "ir/statements/whileStatement.h" 21 22namespace ark::es2panda::compiler::ast_verifier { 23 24[[nodiscard]] CheckResult CheckInfiniteLoop::operator()(CheckContext &ctx, const ir::AstNode *ast) 25{ 26 if (ast->IsDoWhileStatement()) { 27 return HandleDoWhileStatement(ctx, ast->AsDoWhileStatement()); 28 } 29 30 if (ast->IsWhileStatement()) { 31 return HandleWhileStatement(ctx, ast->AsWhileStatement()); 32 } 33 34 if (ast->IsForUpdateStatement()) { 35 return HandleForUpdateStatement(ctx, ast->AsForUpdateStatement()); 36 } 37 38 return {CheckDecision::CORRECT, CheckAction::CONTINUE}; 39} 40 41bool CheckInfiniteLoop::ConditionIsAlwaysTrue(const ir::Expression *const test) const 42{ 43 ASSERT(test); 44 auto const *const type = test->TsType(); 45 if (type == nullptr) { 46 return false; 47 } 48 49 if (!type->IsConditionalExprType()) { 50 // Cannot be tested for truthiness 51 return false; 52 } 53 54 const auto [constant, truthy] = type->ResolveConditionExpr(); 55 return (constant && truthy); 56} 57 58bool CheckInfiniteLoop::HasBreakOrReturnStatement(const ir::Statement *const body) const 59{ 60 ASSERT(body); 61 bool hasExit = body->IsBreakStatement() || body->IsReturnStatement(); 62 body->IterateRecursively( 63 [&hasExit](ir::AstNode *child) { hasExit |= child->IsBreakStatement() || child->IsReturnStatement(); }); 64 65 return hasExit; 66} 67 68[[nodiscard]] CheckResult CheckInfiniteLoop::HandleWhileStatement(CheckContext &ctx, 69 const ir::WhileStatement *const stmt) const 70{ 71 auto const *body = stmt->Body(); 72 auto const *test = stmt->Test(); 73 if ((body == nullptr) || (test == nullptr)) { 74 return {CheckDecision::CORRECT, CheckAction::CONTINUE}; 75 } 76 77 if (ConditionIsAlwaysTrue(test)) { 78 if (!HasBreakOrReturnStatement(body)) { 79 ctx.AddCheckMessage("INFINITE LOOP", *stmt, stmt->Start()); 80 } 81 } 82 83 return {CheckDecision::CORRECT, CheckAction::CONTINUE}; 84} 85 86[[nodiscard]] CheckResult CheckInfiniteLoop::HandleDoWhileStatement(CheckContext &ctx, 87 const ir::DoWhileStatement *const stmt) const 88{ 89 auto const *body = stmt->Body(); 90 auto const *test = stmt->Test(); 91 if ((body == nullptr) || (test == nullptr)) { 92 return {CheckDecision::CORRECT, CheckAction::CONTINUE}; 93 } 94 95 if (ConditionIsAlwaysTrue(test)) { 96 if (!HasBreakOrReturnStatement(body)) { 97 ctx.AddCheckMessage("INFINITE LOOP", *stmt, stmt->Start()); 98 } 99 } 100 101 return {CheckDecision::CORRECT, CheckAction::CONTINUE}; 102} 103 104[[nodiscard]] CheckResult CheckInfiniteLoop::HandleForUpdateStatement(CheckContext &ctx, 105 const ir::ForUpdateStatement *const stmt) const 106{ 107 auto const *body = stmt->Body(); 108 if (body == nullptr) { 109 // Body existence is checked in ForLoopCorrectlyInitialized 110 return {CheckDecision::CORRECT, CheckAction::CONTINUE}; 111 } 112 113 // Test can be null for for-update statements 114 auto const *test = stmt->Test(); 115 if (test == nullptr || ConditionIsAlwaysTrue(test)) { 116 if (!HasBreakOrReturnStatement(body)) { 117 ctx.AddCheckMessage("INFINITE LOOP", *stmt, stmt->Start()); 118 return {CheckDecision::CORRECT, CheckAction::CONTINUE}; 119 } 120 } 121 122 return {CheckDecision::CORRECT, CheckAction::CONTINUE}; 123} 124} // namespace ark::es2panda::compiler::ast_verifier 125