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
53 namespace ark::es2panda::checker {
54
AnalyzeNodes(const ir::AstNode * node)55 void AliveAnalyzer::AnalyzeNodes(const ir::AstNode *node)
56 {
57 node->Iterate([this](auto *childNode) { AnalyzeNode(childNode); });
58 }
59
AnalyzeNode(const ir::AstNode * node)60 void 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
AnalyzeNodeHelper1(const ir::AstNode * node)106 void 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
AnalyzeNodeHelper2(const ir::AstNode * node)152 void 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
AnalyzeDef(const ir::AstNode * node)177 void 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
AnalyzeStat(const ir::AstNode * node)185 void 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
AnalyzeStats(const ArenaVector<ir::Statement * > & stats)204 void AliveAnalyzer::AnalyzeStats(const ArenaVector<ir::Statement *> &stats)
205 {
206 for (const auto *it : stats) {
207 AnalyzeStat(it);
208 }
209 }
210
AnalyzeStructDecl(const ir::ETSStructDeclaration * structDecl)211 void AliveAnalyzer::AnalyzeStructDecl(const ir::ETSStructDeclaration *structDecl)
212 {
213 for (const auto *it : structDecl->Definition()->Body()) {
214 AnalyzeNode(it);
215 }
216 }
217
AnalyzeClassDecl(const ir::ClassDeclaration * classDecl)218 void 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
AnalyzeMethodDef(const ir::MethodDefinition * methodDef)229 void AliveAnalyzer::AnalyzeMethodDef(const ir::MethodDefinition *methodDef)
230 {
231 for (ir::MethodDefinition *overload : methodDef->Overloads()) {
232 AnalyzeNode(overload);
233 }
234
235 auto *func = methodDef->Function();
236
237 if (func->Body() == nullptr || func->IsProxy()) {
238 return;
239 }
240
241 status_ = LivenessStatus::ALIVE;
242 AnalyzeStat(func->Body());
243 ASSERT(methodDef->TsType() && methodDef->TsType()->IsETSFunctionType());
244 const auto *returnType = methodDef->TsType()->AsETSFunctionType()->FindSignature(func)->ReturnType();
245 const auto isVoid = returnType->IsETSVoidType() || returnType == checker_->GlobalVoidType();
246
247 auto isPromiseVoid = false;
248
249 if (returnType->IsETSAsyncFuncReturnType()) {
250 const auto *asAsync = returnType->AsETSAsyncFuncReturnType();
251 isPromiseVoid = asAsync->GetPromiseTypeArg() == checker_->GlobalETSUndefinedType();
252 }
253
254 if (status_ == LivenessStatus::ALIVE && !isVoid && !isPromiseVoid && !checker_->IsAsyncImplMethod(methodDef)) {
255 if (!methodDef->Function()->HasReturnStatement()) {
256 checker_->LogTypeError("Function with a non void return type must return a value.", func->Start());
257 ClearPendingExits();
258 return;
259 }
260
261 checker_->LogTypeError("Not all code paths return a value.", func->Start());
262 }
263
264 ClearPendingExits();
265 }
266
AnalyzeVarDef(const ir::VariableDeclaration * varDef)267 void AliveAnalyzer::AnalyzeVarDef(const ir::VariableDeclaration *varDef)
268 {
269 for (auto *it : varDef->Declarators()) {
270 if (it->Init() == nullptr) {
271 continue;
272 }
273
274 AnalyzeNode(it->Init());
275 }
276 }
277
AnalyzeDoLoop(const ir::DoWhileStatement * doWhile)278 void AliveAnalyzer::AnalyzeDoLoop(const ir::DoWhileStatement *doWhile)
279 {
280 SetOldPendingExits(PendingExits());
281 AnalyzeStat(doWhile->Body());
282 status_ = Or(status_, ResolveContinues(doWhile));
283 AnalyzeNode(doWhile->Test());
284 ASSERT(doWhile->Test()->TsType() && doWhile->Test()->TsType()->IsConditionalExprType());
285 const auto exprRes = doWhile->Test()->TsType()->ResolveConditionExpr();
286 status_ = And(status_, static_cast<LivenessStatus>(!std::get<0>(exprRes) || !std::get<1>(exprRes)));
287 status_ = Or(status_, ResolveBreaks(doWhile));
288 }
289
AnalyzeWhileLoop(const ir::WhileStatement * whileStmt)290 void AliveAnalyzer::AnalyzeWhileLoop(const ir::WhileStatement *whileStmt)
291 {
292 SetOldPendingExits(PendingExits());
293 AnalyzeNode(whileStmt->Test());
294 ASSERT(whileStmt->Test()->TsType() && whileStmt->Test()->TsType()->IsConditionalExprType());
295 const auto exprRes = whileStmt->Test()->TsType()->ResolveConditionExpr();
296 status_ = And(status_, static_cast<LivenessStatus>(!std::get<0>(exprRes) || std::get<1>(exprRes)));
297 AnalyzeStat(whileStmt->Body());
298 status_ = Or(status_, ResolveContinues(whileStmt));
299 status_ = Or(ResolveBreaks(whileStmt), From(!std::get<0>(exprRes) || !std::get<1>(exprRes)));
300 }
301
AnalyzeForLoop(const ir::ForUpdateStatement * forStmt)302 void AliveAnalyzer::AnalyzeForLoop(const ir::ForUpdateStatement *forStmt)
303 {
304 AnalyzeNode(forStmt->Init());
305 SetOldPendingExits(PendingExits());
306 const Type *condType {};
307 bool resolveType = false;
308 bool res = false;
309
310 if (forStmt->Test() != nullptr) {
311 AnalyzeNode(forStmt->Test());
312 ASSERT(forStmt->Test()->TsType() && forStmt->Test()->TsType()->IsConditionalExprType());
313 condType = forStmt->Test()->TsType();
314 std::tie(resolveType, res) = forStmt->Test()->TsType()->ResolveConditionExpr();
315 status_ = From(!resolveType || res);
316 } else {
317 status_ = LivenessStatus::ALIVE;
318 }
319
320 AnalyzeStat(forStmt->Body());
321 status_ = Or(status_, ResolveContinues(forStmt));
322 AnalyzeNode(forStmt->Update());
323 status_ = Or(ResolveBreaks(forStmt), From(condType != nullptr && (!resolveType || !res)));
324 }
325
AnalyzeForOfLoop(const ir::ForOfStatement * forOfStmt)326 void AliveAnalyzer::AnalyzeForOfLoop(const ir::ForOfStatement *forOfStmt)
327 {
328 // Note: iterator definition can be a reference to variable defined in outer scope!
329 if (forOfStmt->Left()->IsVariableDeclaration()) {
330 AnalyzeVarDef(forOfStmt->Left()->AsVariableDeclaration());
331 } else {
332 AnalyzeNode(forOfStmt->Left());
333 }
334 AnalyzeNode(forOfStmt->Right());
335 SetOldPendingExits(PendingExits());
336
337 AnalyzeStat(forOfStmt->Body());
338 status_ = Or(status_, ResolveContinues(forOfStmt));
339 ResolveBreaks(forOfStmt);
340 status_ = LivenessStatus::ALIVE;
341 }
342
AnalyzeIf(const ir::IfStatement * ifStmt)343 void AliveAnalyzer::AnalyzeIf(const ir::IfStatement *ifStmt)
344 {
345 AnalyzeNode(ifStmt->Test());
346 AnalyzeStat(ifStmt->Consequent());
347 if (ifStmt->Alternate() != nullptr) {
348 LivenessStatus prevStatus = status_;
349 status_ = LivenessStatus::ALIVE;
350 AnalyzeStat(ifStmt->Alternate());
351 status_ = Or(status_, prevStatus);
352 } else {
353 status_ = LivenessStatus::ALIVE;
354 }
355 }
356
AnalyzeLabelled(const ir::LabelledStatement * labelledStmt)357 void AliveAnalyzer::AnalyzeLabelled(const ir::LabelledStatement *labelledStmt)
358 {
359 SetOldPendingExits(PendingExits());
360 AnalyzeStat(labelledStmt->Body());
361 status_ = Or(status_, ResolveBreaks(labelledStmt));
362 }
363
AnalyzeNewClass(const ir::ETSNewClassInstanceExpression * newClass)364 void AliveAnalyzer::AnalyzeNewClass(const ir::ETSNewClassInstanceExpression *newClass)
365 {
366 for (const auto *it : newClass->GetArguments()) {
367 AnalyzeNode(it);
368 }
369
370 if (newClass->ClassDefinition() != nullptr) {
371 AnalyzeNode(newClass->ClassDefinition());
372 }
373 }
374
AnalyzeCall(const ir::CallExpression * callExpr)375 void AliveAnalyzer::AnalyzeCall(const ir::CallExpression *callExpr)
376 {
377 AnalyzeNode(callExpr->Callee());
378 for (const auto *it : callExpr->Arguments()) {
379 AnalyzeNode(it);
380 }
381 if (callExpr->Signature()->ReturnType() == checker_->GetGlobalTypesHolder()->GlobalETSNeverType()) {
382 MarkDead();
383 }
384 }
385
AnalyzeThrow(const ir::ThrowStatement * throwStmt)386 void AliveAnalyzer::AnalyzeThrow(const ir::ThrowStatement *throwStmt)
387 {
388 AnalyzeNode(throwStmt->Argument());
389 MarkDead();
390 }
391
AnalyzeSwitch(const ir::SwitchStatement * switchStmt)392 void AliveAnalyzer::AnalyzeSwitch(const ir::SwitchStatement *switchStmt)
393 {
394 SetOldPendingExits(PendingExits());
395
396 AnalyzeNode(switchStmt->Discriminant());
397
398 bool hasDefault = false;
399 for (std::size_t i = 0, size = switchStmt->Cases().size(); i < size; i++) {
400 const auto *caseClause = switchStmt->Cases()[i];
401 status_ = LivenessStatus::ALIVE;
402
403 if (caseClause->Test() == nullptr) {
404 hasDefault = true;
405 } else {
406 AnalyzeNode(caseClause->Test());
407 }
408
409 AnalyzeStats(caseClause->Consequent());
410
411 if (status_ == LivenessStatus::ALIVE && !caseClause->Consequent().empty() && i < size - 1) {
412 // NOTE(user) Add lint categories and option to enable/disable compiler warnings
413 checker_->Warning("Possible fall-through into case", caseClause->Start());
414 }
415 }
416
417 if (!hasDefault) {
418 status_ = LivenessStatus::ALIVE;
419 }
420
421 status_ = Or(status_, ResolveBreaks(switchStmt));
422 }
423
AnalyzeBreak(const ir::BreakStatement * breakStmt)424 void AliveAnalyzer::AnalyzeBreak(const ir::BreakStatement *breakStmt)
425 {
426 RecordExit(PendingExit(breakStmt));
427 }
428
AnalyzeContinue(const ir::ContinueStatement * contStmt)429 void AliveAnalyzer::AnalyzeContinue(const ir::ContinueStatement *contStmt)
430 {
431 RecordExit(PendingExit(contStmt));
432 }
433
AnalyzeReturn(const ir::ReturnStatement * retStmt)434 void AliveAnalyzer::AnalyzeReturn(const ir::ReturnStatement *retStmt)
435 {
436 AnalyzeNode(retStmt->Argument());
437 RecordExit(PendingExit(retStmt));
438 }
439
AnalyzeTry(const ir::TryStatement * tryStmt)440 void AliveAnalyzer::AnalyzeTry(const ir::TryStatement *tryStmt)
441 {
442 status_ = LivenessStatus::ALIVE;
443 bool isAlive = false;
444 AnalyzeStats(tryStmt->Block()->Statements());
445
446 if (status_ != LivenessStatus::DEAD) {
447 isAlive = true;
448 }
449
450 for (const auto &it : tryStmt->CatchClauses()) {
451 status_ = LivenessStatus::ALIVE;
452 AnalyzeStats(it->Body()->Statements());
453 if (status_ == LivenessStatus::ALIVE) {
454 isAlive = true;
455 }
456 }
457
458 if (tryStmt->FinallyBlock() != nullptr) {
459 status_ = LivenessStatus::ALIVE;
460 AnalyzeStats(tryStmt->FinallyBlock()->Statements());
461 const_cast<ir::TryStatement *>(tryStmt)->SetFinallyCanCompleteNormally(status_ == LivenessStatus::ALIVE);
462 if (status_ == LivenessStatus::DEAD) {
463 isAlive = false;
464 // NOTE(user) Add lint categories and option to enable/disable compiler warnings
465 checker_->Warning("Finally clause cannot complete normally", tryStmt->FinallyBlock()->Start());
466 }
467 }
468
469 status_ = isAlive ? LivenessStatus::ALIVE : LivenessStatus::DEAD;
470 }
471 } // namespace ark::es2panda::checker
472