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 
22 namespace ark::es2panda::compiler::ast_verifier {
23 
operator ()(CheckContext &ctx, const ir::AstNode *ast)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 
ConditionIsAlwaysTrue(const ir::Expression *const test) const41 bool 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 
HasBreakOrReturnStatement(const ir::Statement *const body) const58 bool 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 
HandleWhileStatement(CheckContext &ctx, const ir::WhileStatement *const stmt) const68 [[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 
HandleDoWhileStatement(CheckContext &ctx, const ir::DoWhileStatement *const stmt) const86 [[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 
HandleForUpdateStatement(CheckContext &ctx, const ir::ForUpdateStatement *const stmt) const104 [[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