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