1 /*
2 * Copyright (c) 2021 - 2023 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 "varbinder/variable.h"
48 #include "varbinder/scope.h"
49 #include "varbinder/declaration.h"
50 #include "checker/ETSchecker.h"
51 #include "ir/base/catchClause.h"
52
53 namespace panda::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::CLASS_DEFINITION: {
80 AnalyzeClassDef(node->AsClassDefinition());
81 break;
82 }
83 case ir::AstNodeType::METHOD_DEFINITION: {
84 AnalyzeMethodDef(node->AsMethodDefinition());
85 break;
86 }
87 case ir::AstNodeType::VARIABLE_DECLARATION: {
88 AnalyzeVarDef(node->AsVariableDeclaration());
89 break;
90 }
91 case ir::AstNodeType::BLOCK_STATEMENT: {
92 AnalyzeStats(node->AsBlockStatement()->Statements());
93 break;
94 }
95 case ir::AstNodeType::DO_WHILE_STATEMENT: {
96 AnalyzeDoLoop(node->AsDoWhileStatement());
97 break;
98 }
99 case ir::AstNodeType::WHILE_STATEMENT: {
100 AnalyzeWhileLoop(node->AsWhileStatement());
101 break;
102 }
103 case ir::AstNodeType::FOR_UPDATE_STATEMENT: {
104 AnalyzeForLoop(node->AsForUpdateStatement());
105 break;
106 }
107 case ir::AstNodeType::FOR_OF_STATEMENT: {
108 AnalyzeForOfLoop(node->AsForOfStatement());
109 break;
110 }
111 case ir::AstNodeType::IF_STATEMENT: {
112 AnalyzeIf(node->AsIfStatement());
113 break;
114 }
115 case ir::AstNodeType::LABELLED_STATEMENT: {
116 AnalyzeLabelled(node->AsLabelledStatement());
117 break;
118 }
119 case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: {
120 AnalyzeNewClass(node->AsETSNewClassInstanceExpression());
121 break;
122 }
123 case ir::AstNodeType::CALL_EXPRESSION: {
124 AnalyzeCall(node->AsCallExpression());
125 break;
126 }
127 case ir::AstNodeType::THROW_STATEMENT: {
128 AnalyzeThrow(node->AsThrowStatement());
129 break;
130 }
131 case ir::AstNodeType::SWITCH_STATEMENT: {
132 AnalyzeSwitch(node->AsSwitchStatement());
133 break;
134 }
135 case ir::AstNodeType::TRY_STATEMENT: {
136 AnalyzeTry(node->AsTryStatement());
137 break;
138 }
139 case ir::AstNodeType::BREAK_STATEMENT: {
140 AnalyzeBreak(node->AsBreakStatement());
141 break;
142 }
143 case ir::AstNodeType::CONTINUE_STATEMENT: {
144 AnalyzeContinue(node->AsContinueStatement());
145 break;
146 }
147 case ir::AstNodeType::RETURN_STATEMENT: {
148 AnalyzeReturn(node->AsReturnStatement());
149 break;
150 }
151 default: {
152 break;
153 }
154 }
155 }
156
AnalyzeDef(const ir::AstNode * node)157 void AliveAnalyzer::AnalyzeDef(const ir::AstNode *node)
158 {
159 AnalyzeStat(node);
160 if (node != nullptr && node->IsClassStaticBlock() && status_ == LivenessStatus::DEAD) {
161 checker_->ThrowTypeError("Initializer must be able to complete normally.", node->Start());
162 }
163 }
164
AnalyzeStat(const ir::AstNode * node)165 void AliveAnalyzer::AnalyzeStat(const ir::AstNode *node)
166 {
167 if (node == nullptr) {
168 return;
169 }
170
171 if (status_ == LivenessStatus::DEAD) {
172 checker_->ThrowTypeError("Unreachable statement.", node->Start());
173 }
174
175 if (node->IsClassStaticBlock()) {
176 AnalyzeNodes(node);
177 return;
178 }
179
180 AnalyzeNode(node);
181 }
182
AnalyzeStats(const ArenaVector<ir::Statement * > & stats)183 void AliveAnalyzer::AnalyzeStats(const ArenaVector<ir::Statement *> &stats)
184 {
185 for (const auto *it : stats) {
186 AnalyzeStat(it);
187 }
188 }
189
IsStaticMember(const ir::AstNode * node)190 static bool IsStaticMember(const ir::AstNode *node)
191 {
192 switch (node->Type()) {
193 case ir::AstNodeType::CLASS_PROPERTY: {
194 return node->IsStatic();
195 }
196 case ir::AstNodeType::STRUCT_DECLARATION: {
197 return node->AsETSStructDeclaration()->Definition()->IsStatic();
198 }
199 case ir::AstNodeType::CLASS_DECLARATION: {
200 return node->AsClassDeclaration()->Definition()->IsStatic();
201 }
202 case ir::AstNodeType::TS_INTERFACE_DECLARATION: {
203 return node->IsStatic();
204 }
205 default: {
206 return false;
207 }
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 for (const auto *it : classDecl->Definition()->Body()) {
221 AnalyzeNode(it);
222 }
223 }
224
AnalyzeClassDef(const ir::ClassDefinition * classDef)225 void AliveAnalyzer::AnalyzeClassDef(const ir::ClassDefinition *classDef)
226 {
227 if (classDef->Variable() == nullptr) {
228 return;
229 }
230
231 LivenessStatus prevStatus = status_;
232 SetOldPendingExits(PendingExits());
233
234 for (const auto *it : classDef->Body()) {
235 if (!it->IsMethodDefinition() && IsStaticMember(it)) {
236 AnalyzeDef(it);
237 ClearPendingExits();
238 }
239 }
240
241 for (const auto *it : classDef->Body()) {
242 if (!it->IsMethodDefinition() && !IsStaticMember(it)) {
243 AnalyzeDef(it);
244 ClearPendingExits();
245 }
246 }
247
248 for (const auto *it : classDef->Body()) {
249 if (it->IsClassStaticBlock()) {
250 AnalyzeDef(it);
251 break;
252 }
253 }
254
255 for (const auto *it : classDef->Body()) {
256 if (it->IsMethodDefinition()) {
257 AnalyzeNode(it);
258 }
259 }
260
261 SetPendingExits(OldPendingExits());
262 status_ = prevStatus;
263 }
264
AnalyzeMethodDef(const ir::MethodDefinition * methodDef)265 void AliveAnalyzer::AnalyzeMethodDef(const ir::MethodDefinition *methodDef)
266 {
267 auto *func = methodDef->Function();
268
269 if (func->Body() == nullptr || func->IsProxy()) {
270 return;
271 }
272
273 status_ = LivenessStatus::ALIVE;
274 AnalyzeStat(func->Body());
275 ASSERT(methodDef->TsType() && methodDef->TsType()->IsETSFunctionType());
276 const auto *returnType = methodDef->TsType()->AsETSFunctionType()->FindSignature(func)->ReturnType();
277 const auto isVoid = returnType->IsETSVoidType() || returnType == checker_->GlobalBuiltinVoidType();
278
279 auto isPromiseVoid = false;
280
281 if (returnType->IsETSAsyncFuncReturnType()) {
282 const auto *asAsync = returnType->AsETSAsyncFuncReturnType();
283 isPromiseVoid = asAsync->GetPromiseTypeArg() == checker_->GlobalBuiltinVoidType();
284 }
285
286 if (status_ == LivenessStatus::ALIVE && !isVoid && !isPromiseVoid) {
287 checker_->ThrowTypeError("Function with a non void return type must return a value.", func->Id()->Start());
288 }
289
290 ClearPendingExits();
291 }
292
AnalyzeVarDef(const ir::VariableDeclaration * varDef)293 void AliveAnalyzer::AnalyzeVarDef(const ir::VariableDeclaration *varDef)
294 {
295 for (auto *it : varDef->Declarators()) {
296 if (it->Init() == nullptr) {
297 continue;
298 }
299
300 AnalyzeNode(it->Init());
301 }
302 }
303
AnalyzeDoLoop(const ir::DoWhileStatement * doWhile)304 void AliveAnalyzer::AnalyzeDoLoop(const ir::DoWhileStatement *doWhile)
305 {
306 SetOldPendingExits(PendingExits());
307 AnalyzeStat(doWhile->Body());
308 status_ = Or(status_, ResolveContinues(doWhile));
309 AnalyzeNode(doWhile->Test());
310 ASSERT(doWhile->Test()->TsType() && doWhile->Test()->TsType()->IsConditionalExprType());
311 const auto exprRes = doWhile->Test()->TsType()->ResolveConditionExpr();
312 status_ = And(status_, static_cast<LivenessStatus>(!std::get<0>(exprRes) || !std::get<1>(exprRes)));
313 status_ = Or(status_, ResolveBreaks(doWhile));
314 }
315
AnalyzeWhileLoop(const ir::WhileStatement * whileStmt)316 void AliveAnalyzer::AnalyzeWhileLoop(const ir::WhileStatement *whileStmt)
317 {
318 SetOldPendingExits(PendingExits());
319 AnalyzeNode(whileStmt->Test());
320 ASSERT(whileStmt->Test()->TsType() && whileStmt->Test()->TsType()->IsConditionalExprType());
321 const auto exprRes = whileStmt->Test()->TsType()->ResolveConditionExpr();
322 status_ = And(status_, static_cast<LivenessStatus>(!std::get<0>(exprRes) || std::get<1>(exprRes)));
323 AnalyzeStat(whileStmt->Body());
324 status_ = Or(status_, ResolveContinues(whileStmt));
325 status_ = Or(ResolveBreaks(whileStmt), From(!std::get<0>(exprRes) || !std::get<1>(exprRes)));
326 }
327
AnalyzeForLoop(const ir::ForUpdateStatement * forStmt)328 void AliveAnalyzer::AnalyzeForLoop(const ir::ForUpdateStatement *forStmt)
329 {
330 AnalyzeNode(forStmt->Init());
331 SetOldPendingExits(PendingExits());
332 const Type *condType {};
333 bool resolveType = false;
334 bool res = false;
335
336 if (forStmt->Test() != nullptr) {
337 AnalyzeNode(forStmt->Test());
338 ASSERT(forStmt->Test()->TsType() && forStmt->Test()->TsType()->IsConditionalExprType());
339 condType = forStmt->Test()->TsType();
340 std::tie(resolveType, res) = forStmt->Test()->TsType()->ResolveConditionExpr();
341 status_ = From(!resolveType || res);
342 } else {
343 status_ = LivenessStatus::ALIVE;
344 }
345
346 AnalyzeStat(forStmt->Body());
347 status_ = Or(status_, ResolveContinues(forStmt));
348 AnalyzeNode(forStmt->Update());
349 status_ = Or(ResolveBreaks(forStmt), From(condType != nullptr && (!resolveType || !res)));
350 }
351
AnalyzeForOfLoop(const ir::ForOfStatement * forOfStmt)352 void AliveAnalyzer::AnalyzeForOfLoop(const ir::ForOfStatement *forOfStmt)
353 {
354 // Note: iterator definition can be a reference to variable defined in outer scope!
355 if (forOfStmt->Left()->IsVariableDeclaration()) {
356 AnalyzeVarDef(forOfStmt->Left()->AsVariableDeclaration());
357 } else {
358 AnalyzeNode(forOfStmt->Left());
359 }
360 AnalyzeNode(forOfStmt->Right());
361 SetOldPendingExits(PendingExits());
362
363 AnalyzeStat(forOfStmt->Body());
364 status_ = Or(status_, ResolveContinues(forOfStmt));
365 ResolveBreaks(forOfStmt);
366 status_ = LivenessStatus::ALIVE;
367 }
368
AnalyzeIf(const ir::IfStatement * ifStmt)369 void AliveAnalyzer::AnalyzeIf(const ir::IfStatement *ifStmt)
370 {
371 AnalyzeNode(ifStmt->Test());
372 AnalyzeStat(ifStmt->Consequent());
373 if (ifStmt->Alternate() != nullptr) {
374 LivenessStatus prevStatus = status_;
375 status_ = LivenessStatus::ALIVE;
376 AnalyzeStat(ifStmt->Alternate());
377 status_ = Or(status_, prevStatus);
378 } else {
379 status_ = LivenessStatus::ALIVE;
380 }
381 }
382
AnalyzeLabelled(const ir::LabelledStatement * labelledStmt)383 void AliveAnalyzer::AnalyzeLabelled(const ir::LabelledStatement *labelledStmt)
384 {
385 SetOldPendingExits(PendingExits());
386 AnalyzeStat(labelledStmt->Body());
387 status_ = Or(status_, ResolveBreaks(labelledStmt));
388 }
389
AnalyzeNewClass(const ir::ETSNewClassInstanceExpression * newClass)390 void AliveAnalyzer::AnalyzeNewClass(const ir::ETSNewClassInstanceExpression *newClass)
391 {
392 for (const auto *it : newClass->GetArguments()) {
393 AnalyzeNode(it);
394 }
395
396 if (newClass->ClassDefinition() != nullptr) {
397 AnalyzeNode(newClass->ClassDefinition());
398 }
399 }
400
AnalyzeCall(const ir::CallExpression * callExpr)401 void AliveAnalyzer::AnalyzeCall(const ir::CallExpression *callExpr)
402 {
403 AnalyzeNode(callExpr->Callee());
404 for (const auto *it : callExpr->Arguments()) {
405 AnalyzeNode(it);
406 }
407 if (callExpr->Signature()->ReturnType() == checker_->GetGlobalTypesHolder()->GlobalBuiltinNeverType()) {
408 MarkDead();
409 }
410 }
411
AnalyzeThrow(const ir::ThrowStatement * throwStmt)412 void AliveAnalyzer::AnalyzeThrow(const ir::ThrowStatement *throwStmt)
413 {
414 AnalyzeNode(throwStmt->Argument());
415 MarkDead();
416 }
417
AnalyzeSwitch(const ir::SwitchStatement * switchStmt)418 void AliveAnalyzer::AnalyzeSwitch(const ir::SwitchStatement *switchStmt)
419 {
420 SetOldPendingExits(PendingExits());
421
422 AnalyzeNode(switchStmt->Discriminant());
423
424 bool hasDefault = false;
425 for (std::size_t i = 0, size = switchStmt->Cases().size(); i < size; i++) {
426 const auto *caseClause = switchStmt->Cases()[i];
427 status_ = LivenessStatus::ALIVE;
428
429 if (caseClause->Test() == nullptr) {
430 hasDefault = true;
431 } else {
432 AnalyzeNode(caseClause->Test());
433 }
434
435 AnalyzeStats(caseClause->Consequent());
436
437 if (status_ == LivenessStatus::ALIVE && !caseClause->Consequent().empty() && i < size - 1) {
438 // NOTE(user) Add lint categories and option to enable/disable compiler warnings
439 checker_->Warning("Possible fall-through into case", caseClause->Start());
440 }
441 }
442
443 if (!hasDefault) {
444 status_ = LivenessStatus::ALIVE;
445 }
446
447 status_ = Or(status_, ResolveBreaks(switchStmt));
448 }
449
AnalyzeBreak(const ir::BreakStatement * breakStmt)450 void AliveAnalyzer::AnalyzeBreak(const ir::BreakStatement *breakStmt)
451 {
452 RecordExit(PendingExit(breakStmt));
453 }
454
AnalyzeContinue(const ir::ContinueStatement * contStmt)455 void AliveAnalyzer::AnalyzeContinue(const ir::ContinueStatement *contStmt)
456 {
457 RecordExit(PendingExit(contStmt));
458 }
459
AnalyzeReturn(const ir::ReturnStatement * retStmt)460 void AliveAnalyzer::AnalyzeReturn(const ir::ReturnStatement *retStmt)
461 {
462 AnalyzeNode(retStmt->Argument());
463 RecordExit(PendingExit(retStmt));
464 }
465
AnalyzeTry(const ir::TryStatement * tryStmt)466 void AliveAnalyzer::AnalyzeTry(const ir::TryStatement *tryStmt)
467 {
468 status_ = LivenessStatus::ALIVE;
469 bool isAlive = false;
470 AnalyzeStats(tryStmt->Block()->Statements());
471
472 if (status_ != LivenessStatus::DEAD) {
473 isAlive = true;
474 }
475
476 for (const auto &it : tryStmt->CatchClauses()) {
477 status_ = LivenessStatus::ALIVE;
478 AnalyzeStats(it->Body()->Statements());
479 if (status_ == LivenessStatus::ALIVE) {
480 isAlive = true;
481 }
482 }
483
484 if (tryStmt->FinallyBlock() != nullptr) {
485 status_ = LivenessStatus::ALIVE;
486 AnalyzeStats(tryStmt->FinallyBlock()->Statements());
487 if (status_ == LivenessStatus::DEAD) {
488 isAlive = false;
489 }
490 }
491
492 status_ = isAlive ? LivenessStatus::ALIVE : LivenessStatus::DEAD;
493 }
494 } // namespace panda::es2panda::checker
495