1/*
2 * Copyright (c) 2021-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 "aliveAnalyzer.h"
17#include <cstddef>
18
19#include "checker/types/ets/etsAsyncFuncReturnType.h"
20#include "ir/base/classDefinition.h"
21#include "ir/base/classProperty.h"
22#include "ir/base/methodDefinition.h"
23#include "ir/base/scriptFunction.h"
24#include "ir/statements/classDeclaration.h"
25#include "ir/statements/variableDeclaration.h"
26#include "ir/statements/doWhileStatement.h"
27#include "ir/statements/expressionStatement.h"
28#include "ir/statements/whileStatement.h"
29#include "ir/statements/forUpdateStatement.h"
30#include "ir/statements/labelledStatement.h"
31#include "ir/statements/forOfStatement.h"
32#include "ir/statements/blockStatement.h"
33#include "ir/statements/ifStatement.h"
34#include "ir/statements/switchStatement.h"
35#include "ir/statements/variableDeclarator.h"
36#include "ir/statements/throwStatement.h"
37#include "ir/statements/switchCaseStatement.h"
38#include "ir/statements/breakStatement.h"
39#include "ir/statements/continueStatement.h"
40#include "ir/statements/returnStatement.h"
41#include "ir/statements/tryStatement.h"
42#include "ir/expressions/callExpression.h"
43#include "ir/expressions/identifier.h"
44#include "ir/ets/etsNewClassInstanceExpression.h"
45#include "ir/ets/etsStructDeclaration.h"
46#include "ir/ts/tsInterfaceDeclaration.h"
47#include "checker/types/globalTypesHolder.h"
48#include "varbinder/variable.h"
49#include "varbinder/declaration.h"
50#include "checker/ETSchecker.h"
51#include "ir/base/catchClause.h"
52
53namespace ark::es2panda::checker {
54
55void AliveAnalyzer::AnalyzeNodes(const ir::AstNode *node)
56{
57    node->Iterate([this](auto *childNode) { AnalyzeNode(childNode); });
58}
59
60void AliveAnalyzer::AnalyzeNode(const ir::AstNode *node)
61{
62    if (node == nullptr) {
63        return;
64    }
65
66    switch (node->Type()) {
67        case ir::AstNodeType::EXPRESSION_STATEMENT: {
68            AnalyzeNode(node->AsExpressionStatement()->GetExpression());
69            break;
70        }
71        case ir::AstNodeType::STRUCT_DECLARATION: {
72            AnalyzeStructDecl(node->AsETSStructDeclaration());
73            break;
74        }
75        case ir::AstNodeType::CLASS_DECLARATION: {
76            AnalyzeClassDecl(node->AsClassDeclaration());
77            break;
78        }
79        case ir::AstNodeType::METHOD_DEFINITION: {
80            AnalyzeMethodDef(node->AsMethodDefinition());
81            break;
82        }
83        case ir::AstNodeType::VARIABLE_DECLARATION: {
84            AnalyzeVarDef(node->AsVariableDeclaration());
85            break;
86        }
87        case ir::AstNodeType::BLOCK_STATEMENT: {
88            AnalyzeStats(node->AsBlockStatement()->Statements());
89            break;
90        }
91        case ir::AstNodeType::DO_WHILE_STATEMENT: {
92            AnalyzeDoLoop(node->AsDoWhileStatement());
93            break;
94        }
95        default: {
96            break;
97        }
98    }
99
100    // Helpers to reduce function size and pass code checker
101    AnalyzeNodeHelper1(node);
102    AnalyzeNodeHelper2(node);
103}
104
105// Helper function to reduce AnalyzeNode size and pass code checker
106void AliveAnalyzer::AnalyzeNodeHelper1(const ir::AstNode *node)
107{
108    switch (node->Type()) {
109        case ir::AstNodeType::WHILE_STATEMENT: {
110            AnalyzeWhileLoop(node->AsWhileStatement());
111            break;
112        }
113        case ir::AstNodeType::FOR_UPDATE_STATEMENT: {
114            AnalyzeForLoop(node->AsForUpdateStatement());
115            break;
116        }
117        case ir::AstNodeType::FOR_OF_STATEMENT: {
118            AnalyzeForOfLoop(node->AsForOfStatement());
119            break;
120        }
121        case ir::AstNodeType::IF_STATEMENT: {
122            AnalyzeIf(node->AsIfStatement());
123            break;
124        }
125        case ir::AstNodeType::LABELLED_STATEMENT: {
126            AnalyzeLabelled(node->AsLabelledStatement());
127            break;
128        }
129        case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: {
130            AnalyzeNewClass(node->AsETSNewClassInstanceExpression());
131            break;
132        }
133        case ir::AstNodeType::CALL_EXPRESSION: {
134            AnalyzeCall(node->AsCallExpression());
135            break;
136        }
137        case ir::AstNodeType::THROW_STATEMENT: {
138            AnalyzeThrow(node->AsThrowStatement());
139            break;
140        }
141        case ir::AstNodeType::SWITCH_STATEMENT: {
142            AnalyzeSwitch(node->AsSwitchStatement());
143            break;
144        }
145        default: {
146            break;
147        }
148    }
149}
150
151// Helper function to reduce AnalyzeNode size and pass code checker
152void AliveAnalyzer::AnalyzeNodeHelper2(const ir::AstNode *node)
153{
154    switch (node->Type()) {
155        case ir::AstNodeType::TRY_STATEMENT: {
156            AnalyzeTry(node->AsTryStatement());
157            break;
158        }
159        case ir::AstNodeType::BREAK_STATEMENT: {
160            AnalyzeBreak(node->AsBreakStatement());
161            break;
162        }
163        case ir::AstNodeType::CONTINUE_STATEMENT: {
164            AnalyzeContinue(node->AsContinueStatement());
165            break;
166        }
167        case ir::AstNodeType::RETURN_STATEMENT: {
168            AnalyzeReturn(node->AsReturnStatement());
169            break;
170        }
171        default: {
172            break;
173        }
174    }
175}
176
177void AliveAnalyzer::AnalyzeDef(const ir::AstNode *node)
178{
179    AnalyzeStat(node);
180    if (node != nullptr && node->IsClassStaticBlock() && status_ == LivenessStatus::DEAD) {
181        checker_->LogTypeError("Initializer must be able to complete normally.", node->Start());
182    }
183}
184
185void AliveAnalyzer::AnalyzeStat(const ir::AstNode *node)
186{
187    if (node == nullptr) {
188        return;
189    }
190
191    if (status_ == LivenessStatus::DEAD) {
192        checker_->LogTypeError("Unreachable statement.", node->Start());
193        return;
194    }
195
196    if (node->IsClassStaticBlock()) {
197        AnalyzeNodes(node);
198        return;
199    }
200
201    AnalyzeNode(node);
202}
203
204void AliveAnalyzer::AnalyzeStats(const ArenaVector<ir::Statement *> &stats)
205{
206    for (const auto *it : stats) {
207        AnalyzeStat(it);
208    }
209}
210
211void AliveAnalyzer::AnalyzeStructDecl(const ir::ETSStructDeclaration *structDecl)
212{
213    for (const auto *it : structDecl->Definition()->Body()) {
214        AnalyzeNode(it);
215    }
216}
217
218void AliveAnalyzer::AnalyzeClassDecl(const ir::ClassDeclaration *classDecl)
219{
220    LivenessStatus prevStatus = status_;
221
222    for (const auto *it : classDecl->Definition()->Body()) {
223        AnalyzeNode(it);
224    }
225
226    status_ = prevStatus;
227}
228
229void AliveAnalyzer::AnalyzeMethodDef(const ir::MethodDefinition *methodDef)
230{
231    auto *func = methodDef->Function();
232
233    if (func->Body() == nullptr || func->IsProxy()) {
234        return;
235    }
236
237    status_ = LivenessStatus::ALIVE;
238    AnalyzeStat(func->Body());
239    ASSERT(methodDef->TsType() && methodDef->TsType()->IsETSFunctionType());
240    const auto *returnType = methodDef->TsType()->AsETSFunctionType()->FindSignature(func)->ReturnType();
241    const auto isVoid = returnType->IsETSVoidType() || returnType == checker_->GlobalVoidType();
242
243    auto isPromiseVoid = false;
244
245    if (returnType->IsETSAsyncFuncReturnType()) {
246        const auto *asAsync = returnType->AsETSAsyncFuncReturnType();
247        isPromiseVoid = asAsync->GetPromiseTypeArg() == checker_->GlobalETSUndefinedType();
248    }
249
250    if (status_ == LivenessStatus::ALIVE && !isVoid && !isPromiseVoid && !checker_->IsAsyncImplMethod(methodDef)) {
251        if (!methodDef->Function()->HasReturnStatement()) {
252            checker_->LogTypeError("Function with a non void return type must return a value.", func->Id()->Start());
253            ClearPendingExits();
254            return;
255        }
256
257        checker_->LogTypeError("Not all code paths return a value.", func->Id()->Start());
258    }
259
260    ClearPendingExits();
261}
262
263void AliveAnalyzer::AnalyzeVarDef(const ir::VariableDeclaration *varDef)
264{
265    for (auto *it : varDef->Declarators()) {
266        if (it->Init() == nullptr) {
267            continue;
268        }
269
270        AnalyzeNode(it->Init());
271    }
272}
273
274void AliveAnalyzer::AnalyzeDoLoop(const ir::DoWhileStatement *doWhile)
275{
276    SetOldPendingExits(PendingExits());
277    AnalyzeStat(doWhile->Body());
278    status_ = Or(status_, ResolveContinues(doWhile));
279    AnalyzeNode(doWhile->Test());
280    ASSERT(doWhile->Test()->TsType() && doWhile->Test()->TsType()->IsConditionalExprType());
281    const auto exprRes = doWhile->Test()->TsType()->ResolveConditionExpr();
282    status_ = And(status_, static_cast<LivenessStatus>(!std::get<0>(exprRes) || !std::get<1>(exprRes)));
283    status_ = Or(status_, ResolveBreaks(doWhile));
284}
285
286void AliveAnalyzer::AnalyzeWhileLoop(const ir::WhileStatement *whileStmt)
287{
288    SetOldPendingExits(PendingExits());
289    AnalyzeNode(whileStmt->Test());
290    ASSERT(whileStmt->Test()->TsType() && whileStmt->Test()->TsType()->IsConditionalExprType());
291    const auto exprRes = whileStmt->Test()->TsType()->ResolveConditionExpr();
292    status_ = And(status_, static_cast<LivenessStatus>(!std::get<0>(exprRes) || std::get<1>(exprRes)));
293    AnalyzeStat(whileStmt->Body());
294    status_ = Or(status_, ResolveContinues(whileStmt));
295    status_ = Or(ResolveBreaks(whileStmt), From(!std::get<0>(exprRes) || !std::get<1>(exprRes)));
296}
297
298void AliveAnalyzer::AnalyzeForLoop(const ir::ForUpdateStatement *forStmt)
299{
300    AnalyzeNode(forStmt->Init());
301    SetOldPendingExits(PendingExits());
302    const Type *condType {};
303    bool resolveType = false;
304    bool res = false;
305
306    if (forStmt->Test() != nullptr) {
307        AnalyzeNode(forStmt->Test());
308        ASSERT(forStmt->Test()->TsType() && forStmt->Test()->TsType()->IsConditionalExprType());
309        condType = forStmt->Test()->TsType();
310        std::tie(resolveType, res) = forStmt->Test()->TsType()->ResolveConditionExpr();
311        status_ = From(!resolveType || res);
312    } else {
313        status_ = LivenessStatus::ALIVE;
314    }
315
316    AnalyzeStat(forStmt->Body());
317    status_ = Or(status_, ResolveContinues(forStmt));
318    AnalyzeNode(forStmt->Update());
319    status_ = Or(ResolveBreaks(forStmt), From(condType != nullptr && (!resolveType || !res)));
320}
321
322void AliveAnalyzer::AnalyzeForOfLoop(const ir::ForOfStatement *forOfStmt)
323{
324    //  Note: iterator definition can be a reference to variable defined in outer scope!
325    if (forOfStmt->Left()->IsVariableDeclaration()) {
326        AnalyzeVarDef(forOfStmt->Left()->AsVariableDeclaration());
327    } else {
328        AnalyzeNode(forOfStmt->Left());
329    }
330    AnalyzeNode(forOfStmt->Right());
331    SetOldPendingExits(PendingExits());
332
333    AnalyzeStat(forOfStmt->Body());
334    status_ = Or(status_, ResolveContinues(forOfStmt));
335    ResolveBreaks(forOfStmt);
336    status_ = LivenessStatus::ALIVE;
337}
338
339void AliveAnalyzer::AnalyzeIf(const ir::IfStatement *ifStmt)
340{
341    AnalyzeNode(ifStmt->Test());
342    AnalyzeStat(ifStmt->Consequent());
343    if (ifStmt->Alternate() != nullptr) {
344        LivenessStatus prevStatus = status_;
345        status_ = LivenessStatus::ALIVE;
346        AnalyzeStat(ifStmt->Alternate());
347        status_ = Or(status_, prevStatus);
348    } else {
349        status_ = LivenessStatus::ALIVE;
350    }
351}
352
353void AliveAnalyzer::AnalyzeLabelled(const ir::LabelledStatement *labelledStmt)
354{
355    SetOldPendingExits(PendingExits());
356    AnalyzeStat(labelledStmt->Body());
357    status_ = Or(status_, ResolveBreaks(labelledStmt));
358}
359
360void AliveAnalyzer::AnalyzeNewClass(const ir::ETSNewClassInstanceExpression *newClass)
361{
362    for (const auto *it : newClass->GetArguments()) {
363        AnalyzeNode(it);
364    }
365
366    if (newClass->ClassDefinition() != nullptr) {
367        AnalyzeNode(newClass->ClassDefinition());
368    }
369}
370
371void AliveAnalyzer::AnalyzeCall(const ir::CallExpression *callExpr)
372{
373    AnalyzeNode(callExpr->Callee());
374    for (const auto *it : callExpr->Arguments()) {
375        AnalyzeNode(it);
376    }
377    if (callExpr->Signature()->ReturnType() == checker_->GetGlobalTypesHolder()->GlobalBuiltinNeverType()) {
378        MarkDead();
379    }
380}
381
382void AliveAnalyzer::AnalyzeThrow(const ir::ThrowStatement *throwStmt)
383{
384    AnalyzeNode(throwStmt->Argument());
385    MarkDead();
386}
387
388void AliveAnalyzer::AnalyzeSwitch(const ir::SwitchStatement *switchStmt)
389{
390    SetOldPendingExits(PendingExits());
391
392    AnalyzeNode(switchStmt->Discriminant());
393
394    bool hasDefault = false;
395    for (std::size_t i = 0, size = switchStmt->Cases().size(); i < size; i++) {
396        const auto *caseClause = switchStmt->Cases()[i];
397        status_ = LivenessStatus::ALIVE;
398
399        if (caseClause->Test() == nullptr) {
400            hasDefault = true;
401        } else {
402            AnalyzeNode(caseClause->Test());
403        }
404
405        AnalyzeStats(caseClause->Consequent());
406
407        if (status_ == LivenessStatus::ALIVE && !caseClause->Consequent().empty() && i < size - 1) {
408            // NOTE(user) Add lint categories and option to enable/disable compiler warnings
409            checker_->Warning("Possible fall-through into case", caseClause->Start());
410        }
411    }
412
413    if (!hasDefault) {
414        status_ = LivenessStatus::ALIVE;
415    }
416
417    status_ = Or(status_, ResolveBreaks(switchStmt));
418}
419
420void AliveAnalyzer::AnalyzeBreak(const ir::BreakStatement *breakStmt)
421{
422    RecordExit(PendingExit(breakStmt));
423}
424
425void AliveAnalyzer::AnalyzeContinue(const ir::ContinueStatement *contStmt)
426{
427    RecordExit(PendingExit(contStmt));
428}
429
430void AliveAnalyzer::AnalyzeReturn(const ir::ReturnStatement *retStmt)
431{
432    AnalyzeNode(retStmt->Argument());
433    RecordExit(PendingExit(retStmt));
434}
435
436void AliveAnalyzer::AnalyzeTry(const ir::TryStatement *tryStmt)
437{
438    status_ = LivenessStatus::ALIVE;
439    bool isAlive = false;
440    AnalyzeStats(tryStmt->Block()->Statements());
441
442    if (status_ != LivenessStatus::DEAD) {
443        isAlive = true;
444    }
445
446    for (const auto &it : tryStmt->CatchClauses()) {
447        status_ = LivenessStatus::ALIVE;
448        AnalyzeStats(it->Body()->Statements());
449        if (status_ == LivenessStatus::ALIVE) {
450            isAlive = true;
451        }
452    }
453
454    if (tryStmt->FinallyBlock() != nullptr) {
455        status_ = LivenessStatus::ALIVE;
456        AnalyzeStats(tryStmt->FinallyBlock()->Statements());
457        const_cast<ir::TryStatement *>(tryStmt)->SetFinallyCanCompleteNormally(status_ == LivenessStatus::ALIVE);
458        if (status_ == LivenessStatus::DEAD) {
459            isAlive = false;
460            // NOTE(user) Add lint categories and option to enable/disable compiler warnings
461            checker_->Warning("Finally clause cannot complete normally", tryStmt->FinallyBlock()->Start());
462        }
463    }
464
465    status_ = isAlive ? LivenessStatus::ALIVE : LivenessStatus::DEAD;
466}
467}  // namespace ark::es2panda::checker
468