• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 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 "assignAnalyzer.h"
17 #include <cstddef>
18 
19 #include "ir/base/classDefinition.h"
20 #include "ir/base/classProperty.h"
21 #include "ir/base/classStaticBlock.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/expressions/arrowFunctionExpression.h"
45 #include "ir/expressions/assignmentExpression.h"
46 #include "ir/expressions/binaryExpression.h"
47 #include "ir/expressions/conditionalExpression.h"
48 #include "ir/expressions/memberExpression.h"
49 #include "ir/expressions/objectExpression.h"
50 #include "ir/expressions/unaryExpression.h"
51 #include "ir/expressions/updateExpression.h"
52 #include "ir/expressions/typeofExpression.h"
53 #include "ir/ets/etsNewClassInstanceExpression.h"
54 #include "ir/ets/etsStructDeclaration.h"
55 #include "ir/ts/tsInterfaceDeclaration.h"
56 #include "varbinder/ETSBinder.h"
57 #include "varbinder/variable.h"
58 #include "varbinder/scope.h"
59 #include "varbinder/declaration.h"
60 #include "checker/ETSchecker.h"
61 #include "ir/base/catchClause.h"
62 #include "parser/program/program.h"
63 #include "checker/types/ts/objectType.h"
64 
65 namespace ark::es2panda::checker {
66 
67 static constexpr NodeId INVALID_ID = -1;
68 static constexpr bool CHECK_ALL_PROPERTIES = true;
69 // NOTE(pantos) generic field initialization issue, skip them for now
70 static constexpr bool CHECK_GENERIC_NON_READONLY_PROPERTIES = false;
71 static constexpr bool WARN_NO_INIT_ONCE_PER_VARIABLE = false;
72 static constexpr int LOOP_PHASES = 2;
73 
74 template <typename... Ts>
75 struct ScopeGuard {
76     // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
77     std::tuple<Ts...> values;
78     std::tuple<Ts &...> refs;
79     // NOLINTEND(misc-non-private-member-variables-in-classes)
80 
ScopeGuardark::es2panda::checker::ScopeGuard81     explicit ScopeGuard(Ts &...ts) : values(ts...), refs(ts...) {}
~ScopeGuardark::es2panda::checker::ScopeGuard82     ~ScopeGuard()
83     {
84         refs = values;
85     }
86 
87     DEFAULT_COPY_SEMANTIC(ScopeGuard);
88     DEFAULT_MOVE_SEMANTIC(ScopeGuard);
89 };
90 
Capitalize(const util::StringView & str)91 static std::string Capitalize(const util::StringView &str)
92 {
93     if (str.Empty()) {
94         return "";
95     }
96     std::string ret(str.Utf8());
97     ret[0] = std::toupper(ret[0]);
98     return ret;
99 }
100 
Reset()101 void Set::Reset()
102 {
103     reset_ = true;
104 }
105 
IsReset()106 bool Set::IsReset()
107 {
108     return reset_;
109 }
110 
Incl(const int id)111 void Set::Incl(const int id)
112 {
113     nodes_.insert(id);
114 }
115 
InclRange(const int start,const int limit)116 void Set::InclRange(const int start, const int limit)
117 {
118     for (int x = start; x < limit; x++) {
119         nodes_.insert(x);
120     }
121 }
122 
Excl(const int id)123 void Set::Excl(const int id)
124 {
125     nodes_.erase(id);
126 }
127 
ExcludeFrom(const int start)128 void Set::ExcludeFrom(const int start)
129 {
130     auto it = nodes_.lower_bound(start);
131     nodes_.erase(nodes_.begin(), it);
132 }
133 
IsMember(const int id) const134 bool Set::IsMember(const int id) const
135 {
136     return nodes_.find(id) != nodes_.end();
137 }
138 
AndSet(const Set & xs)139 Set &Set::AndSet(const Set &xs)
140 {
141     std::set<int> res;
142     std::set_intersection(nodes_.begin(), nodes_.end(), xs.nodes_.begin(), xs.nodes_.end(),
143                           std::inserter(res, res.begin()));
144     nodes_ = std::move(res);
145     return *this;
146 }
147 
OrSet(const Set & xs)148 Set &Set::OrSet(const Set &xs)
149 {
150     std::set<int> res;
151     std::set_union(nodes_.begin(), nodes_.end(), xs.nodes_.begin(), xs.nodes_.end(), std::inserter(res, res.begin()));
152     nodes_ = std::move(res);
153     return *this;
154 }
155 
DiffSet(const Set & xs)156 Set &Set::DiffSet(const Set &xs)
157 {
158     std::set<int> res;
159     std::set_difference(nodes_.begin(), nodes_.end(), xs.nodes_.begin(), xs.nodes_.end(),
160                         std::inserter(res, res.begin()));
161     nodes_ = std::move(res);
162     return *this;
163 }
164 
Next(const int id)165 int Set::Next(const int id)
166 {
167     auto it = nodes_.upper_bound(id);
168     if (it != nodes_.end()) {
169         return *it;
170     }
171     return -1;
172 }
173 
AssignAnalyzer(ETSChecker * checker)174 AssignAnalyzer::AssignAnalyzer(ETSChecker *checker)
175     : checker_(checker),
176       varDecls_(checker->Allocator()->Adapter()),
177       nodeIdMap_(checker->Allocator()->Adapter()),
178       foundErrors_(checker->Allocator()->Adapter())
179 {
180 }
181 
Analyze(const ir::AstNode * node)182 void AssignAnalyzer::Analyze(const ir::AstNode *node)
183 {
184     const auto program = checker_->VarBinder()->Program();
185     globalClass_ = program->GlobalClass();
186 
187     AnalyzeClassDef(globalClass_);
188 
189     firstNonGlobalAdr_ = nextAdr_;
190     AnalyzeNodes(node);
191 }
192 
Warning(const diagnostic::DiagnosticKind & kind,const util::DiagnosticMessageParams & list,const lexer::SourcePosition & pos)193 void AssignAnalyzer::Warning(const diagnostic::DiagnosticKind &kind, const util::DiagnosticMessageParams &list,
194                              const lexer::SourcePosition &pos)
195 {
196     ++numErrors_;
197     checker_->LogDiagnostic(kind, list, pos);
198 }
199 
AnalyzeNodes(const ir::AstNode * node)200 void AssignAnalyzer::AnalyzeNodes(const ir::AstNode *node)
201 {
202     node->Iterate([this](auto *childNode) { AnalyzeNode(childNode); });
203 }
204 
AnalyzeNode(const ir::AstNode * node)205 void AssignAnalyzer::AnalyzeNode(const ir::AstNode *node)
206 {
207     ES2PANDA_ASSERT(node != nullptr);
208 
209     // NOTE(pantos) these are dummy methods to conform the CI's method size and complexity requirements
210     if (AnalyzeStmtNode1(node) || AnalyzeStmtNode2(node) || AnalyzeExprNode1(node) || AnalyzeExprNode2(node)) {
211         return;
212     }
213 
214     switch (node->Type()) {
215         case ir::AstNodeType::STRUCT_DECLARATION: {
216             AnalyzeStructDecl(node->AsETSStructDeclaration());
217             break;
218         }
219         case ir::AstNodeType::CLASS_DECLARATION: {
220             AnalyzeClassDecl(node->AsClassDeclaration());
221             break;
222         }
223         case ir::AstNodeType::CLASS_DEFINITION: {
224             if (node->AsClassDefinition() != globalClass_) {
225                 AnalyzeClassDef(node->AsClassDefinition());
226             }
227             break;
228         }
229         case ir::AstNodeType::METHOD_DEFINITION: {
230             AnalyzeMethodDef(node->AsMethodDefinition());
231             break;
232         }
233         case ir::AstNodeType::VARIABLE_DECLARATION: {
234             AnalyzeVarDef(node->AsVariableDeclaration());
235             break;
236         }
237         default: {
238             AnalyzeNodes(node);
239             if (node->IsExpression()) {
240                 if (inits_.IsReset()) {
241                     Merge();
242                 }
243             }
244             break;
245         }
246     }
247 }
248 
AnalyzeStmtNode1(const ir::AstNode * node)249 bool AssignAnalyzer::AnalyzeStmtNode1(const ir::AstNode *node)
250 {
251     switch (node->Type()) {
252         case ir::AstNodeType::EXPRESSION_STATEMENT: {
253             AnalyzeNode(node->AsExpressionStatement()->GetExpression());
254             break;
255         }
256         case ir::AstNodeType::BLOCK_STATEMENT: {
257             AnalyzeBlock(node->AsBlockStatement());
258             break;
259         }
260         case ir::AstNodeType::DO_WHILE_STATEMENT: {
261             AnalyzeDoLoop(node->AsDoWhileStatement());
262             break;
263         }
264         case ir::AstNodeType::WHILE_STATEMENT: {
265             AnalyzeWhileLoop(node->AsWhileStatement());
266             break;
267         }
268         case ir::AstNodeType::FOR_UPDATE_STATEMENT: {
269             AnalyzeForLoop(node->AsForUpdateStatement());
270             break;
271         }
272         case ir::AstNodeType::FOR_OF_STATEMENT: {
273             AnalyzeForOfLoop(node->AsForOfStatement());
274             break;
275         }
276         case ir::AstNodeType::IF_STATEMENT: {
277             AnalyzeIf(node->AsIfStatement());
278             break;
279         }
280         default:
281             return false;
282     }
283 
284     return true;
285 }
286 
AnalyzeStmtNode2(const ir::AstNode * node)287 bool AssignAnalyzer::AnalyzeStmtNode2(const ir::AstNode *node)
288 {
289     switch (node->Type()) {
290         case ir::AstNodeType::LABELLED_STATEMENT: {
291             AnalyzeLabelled(node->AsLabelledStatement());
292             break;
293         }
294         case ir::AstNodeType::SWITCH_STATEMENT: {
295             AnalyzeSwitch(node->AsSwitchStatement());
296             break;
297         }
298         case ir::AstNodeType::TRY_STATEMENT: {
299             AnalyzeTry(node->AsTryStatement());
300             break;
301         }
302         case ir::AstNodeType::BREAK_STATEMENT: {
303             AnalyzeBreak(node->AsBreakStatement());
304             break;
305         }
306         case ir::AstNodeType::CONTINUE_STATEMENT: {
307             AnalyzeContinue(node->AsContinueStatement());
308             break;
309         }
310         case ir::AstNodeType::RETURN_STATEMENT: {
311             AnalyzeReturn(node->AsReturnStatement());
312             break;
313         }
314         case ir::AstNodeType::THROW_STATEMENT: {
315             AnalyzeThrow(node->AsThrowStatement());
316             break;
317         }
318         default:
319             return false;
320     }
321 
322     return true;
323 }
324 
AnalyzeExprNode1(const ir::AstNode * node)325 bool AssignAnalyzer::AnalyzeExprNode1(const ir::AstNode *node)
326 {
327     switch (node->Type()) {
328         case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: {
329             AnalyzeNewClass(node->AsETSNewClassInstanceExpression());
330             break;
331         }
332         case ir::AstNodeType::CALL_EXPRESSION: {
333             AnalyzeCallExpr(node->AsCallExpression());
334             break;
335         }
336         case ir::AstNodeType::IDENTIFIER: {
337             AnalyzeId(node->AsIdentifier());
338             break;
339         }
340         case ir::AstNodeType::ASSIGNMENT_EXPRESSION: {
341             AnalyzeAssignExpr(node->AsAssignmentExpression());
342             break;
343         }
344         case ir::AstNodeType::CONDITIONAL_EXPRESSION: {
345             AnalyzeCondExpr(node->AsConditionalExpression());
346             break;
347         }
348         case ir::AstNodeType::MEMBER_EXPRESSION: {
349             AnalyzeMemberExpr(node->AsMemberExpression());
350             break;
351         }
352         default:
353             return false;
354     }
355 
356     return true;
357 }
358 
AnalyzeExprNode2(const ir::AstNode * node)359 bool AssignAnalyzer::AnalyzeExprNode2(const ir::AstNode *node)
360 {
361     switch (node->Type()) {
362         case ir::AstNodeType::BINARY_EXPRESSION: {
363             AnalyzeBinaryExpr(node->AsBinaryExpression());
364             break;
365         }
366         case ir::AstNodeType::UNARY_EXPRESSION: {
367             AnalyzeUnaryExpr(node->AsUnaryExpression());
368             break;
369         }
370         case ir::AstNodeType::UPDATE_EXPRESSION: {
371             AnalyzeUpdateExpr(node->AsUpdateExpression());
372             break;
373         }
374         case ir::AstNodeType::ARROW_FUNCTION_EXPRESSION: {
375             AnalyzeArrowFunctionExpr(node->AsArrowFunctionExpression());
376             break;
377         }
378         default:
379             return false;
380     }
381 
382     return true;
383 }
384 
AnalyzeStat(const ir::AstNode * node)385 void AssignAnalyzer::AnalyzeStat(const ir::AstNode *node)
386 {
387     ES2PANDA_ASSERT(node != nullptr);
388     AnalyzeNode(node);
389 }
390 
AnalyzeStats(const ArenaVector<ir::Statement * > & stats)391 void AssignAnalyzer::AnalyzeStats(const ArenaVector<ir::Statement *> &stats)
392 {
393     for (const auto it : stats) {
394         AnalyzeStat(it);
395     }
396 }
397 
AnalyzeBlock(const ir::BlockStatement * blockStmt)398 void AssignAnalyzer::AnalyzeBlock(const ir::BlockStatement *blockStmt)
399 {
400     ScopeGuard save(nextAdr_);
401 
402     AnalyzeStats(blockStmt->Statements());
403 }
404 
AnalyzeStructDecl(const ir::ETSStructDeclaration * structDecl)405 void AssignAnalyzer::AnalyzeStructDecl(const ir::ETSStructDeclaration *structDecl)
406 {
407     AnalyzeNode(structDecl->Definition());
408 }
409 
AnalyzeClassDecl(const ir::ClassDeclaration * classDecl)410 void AssignAnalyzer::AnalyzeClassDecl(const ir::ClassDeclaration *classDecl)
411 {
412     AnalyzeNode(classDecl->Definition());
413 }
414 
AnalyzeClassDef(const ir::ClassDefinition * classDef)415 void AssignAnalyzer::AnalyzeClassDef(const ir::ClassDefinition *classDef)
416 {
417     SetOldPendingExits(PendingExits());
418 
419     ScopeGuard save(firstAdr_, nextAdr_, classDef_, classFirstAdr_);
420 
421     classDef_ = classDef;
422     firstAdr_ = nextAdr_;
423     classFirstAdr_ = nextAdr_;
424 
425     ProcessClassDefStaticFields(classDef_);
426 
427     // define all the instance fields
428     for (const auto it : classDef->Body()) {
429         if (it->IsClassProperty() && !it->IsStatic()) {
430             const auto prop = it->AsClassProperty();
431             NewVar(prop);
432             if (prop->Value() != nullptr) {
433                 LetInit(prop);
434             }
435         }
436     }
437 
438     CheckAnonymousClassCtor(classDef_);
439 
440     // process all the methods
441     std::vector<const ir::AstNode *> methods;
442     for (const auto it : classDef->Body()) {
443         if (it->IsMethodDefinition()) {
444             const auto methodDef = it->AsMethodDefinition();
445             if (methodDef->Key()->AsIdentifier()->Name().Is(compiler::Signatures::INIT_METHOD)) {
446                 // skip the special init method as we have already checked it
447                 continue;
448             }
449 
450             methods.push_back(methodDef);
451 
452             for (const auto it2 : methodDef->Overloads()) {
453                 methods.push_back(it2);
454             }
455         }
456     }
457 
458     for (const auto it : methods) {
459         AnalyzeNode(it);
460     }
461 
462     SetPendingExits(OldPendingExits());
463 }
464 
465 // NOTE (pantos) awkward methods to conform method length/complexity requirements of CI...
ProcessClassDefStaticFields(const ir::ClassDefinition * classDef)466 void AssignAnalyzer::ProcessClassDefStaticFields(const ir::ClassDefinition *classDef)
467 {
468     // define all the static fields
469     for (const auto it : classDef->Body()) {
470         if (it->IsClassProperty() && it->IsStatic()) {
471             const auto prop = it->AsClassProperty();
472             NewVar(prop);
473             if (prop->Value() != nullptr) {
474                 LetInit(prop);
475             }
476         }
477     }
478 
479     // process all the static initializers
480     for (const auto it : classDef->Body()) {
481         if (it->IsClassStaticBlock() ||
482             (it->IsStatic() && it->IsMethodDefinition() &&
483              (it->AsMethodDefinition()->Key()->AsIdentifier()->Name().Is(compiler::Signatures::INIT_METHOD) ||
484               it->AsMethodDefinition()->Key()->AsIdentifier()->Name().StartsWith(
485                   compiler::Signatures::INITIALIZER_BLOCK_INIT)))) {
486             AnalyzeNodes(it);
487             ClearPendingExits();
488         }
489     }
490 
491     // verify all static const fields got initailized
492     if (!classDef->IsModule()) {
493         for (int i = firstAdr_; i < nextAdr_; i++) {
494             const ir::AstNode *var = varDecls_[i];
495             if (var->IsStatic() && (var->IsConst() || CHECK_ALL_PROPERTIES)) {
496                 CheckInit(var);
497             }
498         }
499     }
500 }
501 
CheckAnonymousClassCtor(const ir::ClassDefinition * classDef)502 void AssignAnalyzer::CheckAnonymousClassCtor(const ir::ClassDefinition *classDef)
503 {
504     if (classDef == globalClass_) {
505         return;
506     }
507 
508     // NOTE(pantos) anonymous classes of new expressions has no default ctor right now
509     // but this feature might be completely removed from the spec...
510     bool hasCtor = false;
511     for (const auto it : classDef->Body()) {
512         if (it->IsMethodDefinition() && it->AsMethodDefinition()->IsConstructor()) {
513             hasCtor = true;
514             break;
515         }
516     }
517     if (!hasCtor) {
518         for (int i = firstAdr_; i < nextAdr_; i++) {
519             const ir::AstNode *var = varDecls_[i];
520             if (!var->IsStatic() && (var->IsConst() || CHECK_ALL_PROPERTIES)) {
521                 CheckInit(var);
522             }
523         }
524     }
525 }
526 
527 // NOTE(pantos) modified version of ETSChecker::CheckCyclicConstructorCall
IsInitialConstructor(const ir::AstNode * node)528 static bool IsInitialConstructor(const ir::AstNode *node)
529 {
530     if (!node->IsMethodDefinition() || !node->AsMethodDefinition()->IsConstructor()) {
531         return false;
532     }
533 
534     const auto methodDef = node->AsMethodDefinition();
535     ES2PANDA_ASSERT(methodDef != nullptr);
536     if (methodDef->Function()->Body() == nullptr || methodDef->Function()->IsExternal()) {
537         return false;
538     }
539 
540     const auto *func = node->AsMethodDefinition()->Function();
541     ES2PANDA_ASSERT(func != nullptr);
542     const auto funcBody = func->Body()->AsBlockStatement();
543 
544     return !(!funcBody->Statements().empty() && funcBody->Statements()[0]->IsExpressionStatement() &&
545              funcBody->Statements()[0]->AsExpressionStatement()->GetExpression()->IsCallExpression() &&
546              funcBody->Statements()[0]
547                  ->AsExpressionStatement()
548                  ->GetExpression()
549                  ->AsCallExpression()
550                  ->Callee()
551                  ->IsThisExpression());
552 }
553 
AnalyzeMethodDef(const ir::MethodDefinition * methodDef)554 void AssignAnalyzer::AnalyzeMethodDef(const ir::MethodDefinition *methodDef)
555 {
556     auto *func = methodDef->Function();
557     ES2PANDA_ASSERT(func != nullptr);
558     if (func->Body() == nullptr || func->IsProxy()) {
559         return;
560     }
561 
562     Set initsPrev = inits_;
563     Set uninitsPrev = uninits_;
564 
565     ScopeGuard save(firstAdr_, nextAdr_, returnAdr_, isInitialConstructor_);
566 
567     hasTryFinallyBlock_ = func->IsAnyChild([](ir::AstNode *ast) {
568         return (ast->Type() == ir::AstNodeType::TRY_STATEMENT && ast->AsTryStatement()->FinallyBlock() != nullptr);
569     });
570     isInitialConstructor_ = IsInitialConstructor(methodDef);
571     if (!isInitialConstructor_) {
572         firstAdr_ = nextAdr_;
573     }
574 
575     AnalyzeStat(func->Body());
576 
577     if (isInitialConstructor_) {
578         for (int i = firstAdr_; i < nextAdr_; i++) {
579             const ir::AstNode *var = varDecls_[i];
580             if (!var->IsStatic() && (var->IsConst() || CHECK_ALL_PROPERTIES)) {
581                 CheckInit(var);
582             }
583         }
584     }
585 
586     CheckPendingExits();
587 
588     inits_ = std::move(initsPrev);
589     uninits_ = std::move(uninitsPrev);
590 }
591 
AnalyzeVarDef(const ir::VariableDeclaration * varDef)592 void AssignAnalyzer::AnalyzeVarDef(const ir::VariableDeclaration *varDef)
593 {
594     for (auto *var : varDef->Declarators()) {
595         NewVar(var);
596 
597         if (var->Init() != nullptr) {
598             AnalyzeExpr(var->Init());
599             LetInit(var);
600         }
601     }
602 }
603 
AnalyzeDoLoop(const ir::DoWhileStatement * doWhileStmt)604 void AssignAnalyzer::AnalyzeDoLoop(const ir::DoWhileStatement *doWhileStmt)
605 {
606     SetOldPendingExits(PendingExits());
607 
608     Set initsSkip {};
609     Set uninitsSkip {};
610     int prevErrors = numErrors_;
611 
612     for (int phase = 1; phase <= LOOP_PHASES; phase++) {
613         Set uninitsEntry = uninits_;
614         uninitsEntry.ExcludeFrom(nextAdr_);
615 
616         AnalyzeStat(doWhileStmt->Body());
617 
618         ResolveContinues(doWhileStmt);
619 
620         AnalyzeCond(doWhileStmt->Test());
621 
622         if (phase == 1) {
623             initsSkip = initsWhenFalse_;
624             uninitsSkip = uninitsWhenFalse_;
625         }
626 
627         if (prevErrors != numErrors_ || phase == LOOP_PHASES ||
628             uninitsEntry.DiffSet(uninitsWhenTrue_).Next(firstAdr_) == -1) {
629             break;
630         }
631 
632         inits_ = initsWhenTrue_;
633         uninits_ = uninitsEntry.AndSet(uninitsWhenTrue_);
634     }
635 
636     inits_ = std::move(initsSkip);
637     uninits_ = std::move(uninitsSkip);
638 
639     ResolveBreaks(doWhileStmt);
640 }
641 
AnalyzeWhileLoop(const ir::WhileStatement * whileStmt)642 void AssignAnalyzer::AnalyzeWhileLoop(const ir::WhileStatement *whileStmt)
643 {
644     SetOldPendingExits(PendingExits());
645 
646     Set initsSkip {};
647     Set uninitsSkip {};
648     int prevErrors = numErrors_;
649 
650     Set uninitsEntry = uninits_;
651     uninitsEntry.ExcludeFrom(nextAdr_);
652 
653     for (int phase = 1; phase <= LOOP_PHASES; phase++) {
654         AnalyzeCond(whileStmt->Test());
655 
656         if (phase == 1) {
657             initsSkip = initsWhenFalse_;
658             uninitsSkip = uninitsWhenFalse_;
659         }
660 
661         inits_ = initsWhenTrue_;
662         uninits_ = uninitsWhenTrue_;
663 
664         AnalyzeStat(whileStmt->Body());
665 
666         ResolveContinues(whileStmt);
667 
668         if (prevErrors != numErrors_ || phase == LOOP_PHASES || uninitsEntry.DiffSet(uninits_).Next(firstAdr_) == -1) {
669             break;
670         }
671 
672         uninits_ = uninitsEntry.AndSet(uninits_);
673     }
674 
675     inits_ = std::move(initsSkip);
676     uninits_ = std::move(uninitsSkip);
677 
678     ResolveBreaks(whileStmt);
679 }
680 
AnalyzeForLoop(const ir::ForUpdateStatement * forStmt)681 void AssignAnalyzer::AnalyzeForLoop(const ir::ForUpdateStatement *forStmt)
682 {
683     ScopeGuard save(nextAdr_);
684 
685     if (forStmt->Init() != nullptr) {
686         AnalyzeNode(forStmt->Init());
687     }
688 
689     Set initsSkip {};
690     Set uninitsSkip {};
691     int prevErrors = numErrors_;
692 
693     SetOldPendingExits(PendingExits());
694 
695     for (int phase = 1; phase <= LOOP_PHASES; phase++) {
696         Set uninitsEntry = uninits_;
697         uninitsEntry.ExcludeFrom(nextAdr_);
698 
699         if (forStmt->Test() != nullptr) {
700             AnalyzeCond(forStmt->Test());
701 
702             if (phase == 1) {
703                 initsSkip = initsWhenFalse_;
704                 uninitsSkip = uninitsWhenFalse_;
705             }
706 
707             inits_ = initsWhenTrue_;
708             uninits_ = uninitsWhenTrue_;
709         } else if (phase == 1) {
710             initsSkip = inits_;
711             initsSkip.InclRange(firstAdr_, nextAdr_);
712             uninitsSkip = uninits_;
713             uninitsSkip.InclRange(firstAdr_, nextAdr_);
714         }
715 
716         AnalyzeStat(forStmt->Body());
717 
718         ResolveContinues(forStmt);
719 
720         if (forStmt->Update() != nullptr) {
721             AnalyzeNode(forStmt->Update());
722         }
723 
724         if (prevErrors != numErrors_ || phase == LOOP_PHASES || uninitsEntry.DiffSet(uninits_).Next(firstAdr_) == -1) {
725             break;
726         }
727 
728         uninits_ = uninitsEntry.AndSet(uninits_);
729     }
730 
731     inits_ = std::move(initsSkip);
732     uninits_ = std::move(uninitsSkip);
733 
734     ResolveBreaks(forStmt);
735 }
736 
AnalyzeForOfLoop(const ir::ForOfStatement * forOfStmt)737 void AssignAnalyzer::AnalyzeForOfLoop(const ir::ForOfStatement *forOfStmt)
738 {
739     ScopeGuard save(nextAdr_);
740 
741     if (forOfStmt->Left()->IsVariableDeclaration()) {
742         AnalyzeVarDef(forOfStmt->Left()->AsVariableDeclaration());
743         for (auto *var : forOfStmt->Left()->AsVariableDeclaration()->Declarators()) {
744             LetInit(var);
745         }
746     } else {
747         LetInit(forOfStmt->Left());
748     }
749 
750     AnalyzeNode(forOfStmt->Right());
751 
752     Set initsStart = inits_;
753     Set uninitsStart = uninits_;
754     int prevErrors = numErrors_;
755 
756     SetOldPendingExits(PendingExits());
757 
758     for (int phase = 1; phase <= LOOP_PHASES; phase++) {
759         Set uninitsEntry = uninits_;
760         uninitsEntry.ExcludeFrom(nextAdr_);
761 
762         AnalyzeStat(forOfStmt->Body());
763 
764         ResolveContinues(forOfStmt);
765 
766         if (prevErrors != numErrors_ || phase == LOOP_PHASES || uninitsEntry.DiffSet(uninits_).Next(firstAdr_) == -1) {
767             break;
768         }
769 
770         uninits_ = uninitsEntry.AndSet(uninits_);
771     }
772 
773     inits_ = initsStart;
774     uninits_ = std::move(uninitsStart.AndSet(uninits_));
775 
776     ResolveBreaks(forOfStmt);
777 }
778 
AnalyzeIf(const ir::IfStatement * ifStmt)779 void AssignAnalyzer::AnalyzeIf(const ir::IfStatement *ifStmt)
780 {
781     AnalyzeCond(ifStmt->Test());
782 
783     Set initsBeforeElse = initsWhenFalse_;
784     Set uninitsBeforeElse = uninitsWhenFalse_;
785     inits_ = initsWhenTrue_;
786     uninits_ = uninitsWhenTrue_;
787 
788     AnalyzeStat(ifStmt->Consequent());
789 
790     if (ifStmt->Alternate() != nullptr) {
791         Set initsAfterThen = std::move(inits_);
792         Set uninitsAfterThen = std::move(uninits_);
793         inits_ = std::move(initsBeforeElse);
794         uninits_ = std::move(uninitsBeforeElse);
795 
796         AnalyzeStat(ifStmt->Alternate());
797 
798         inits_.AndSet(initsAfterThen);
799         uninits_.AndSet(uninitsAfterThen);
800     } else {
801         inits_.AndSet(initsBeforeElse);
802         uninits_.AndSet(uninitsBeforeElse);
803     }
804 }
805 
AnalyzeLabelled(const ir::LabelledStatement * labelledStmt)806 void AssignAnalyzer::AnalyzeLabelled(const ir::LabelledStatement *labelledStmt)
807 {
808     SetOldPendingExits(PendingExits());
809 
810     AnalyzeStat(labelledStmt->Body());
811 
812     ResolveBreaks(labelledStmt);
813 }
814 
AnalyzeSwitch(const ir::SwitchStatement * switchStmt)815 void AssignAnalyzer::AnalyzeSwitch(const ir::SwitchStatement *switchStmt)
816 {
817     SetOldPendingExits(PendingExits());
818 
819     ScopeGuard save(nextAdr_);
820 
821     AnalyzeNode(switchStmt->Discriminant());
822 
823     Set initsSwitch = inits_;
824     Set uninitsSwitch = uninits_;
825 
826     bool hasDefault = false;
827 
828     for (const auto caseClause : switchStmt->Cases()) {
829         inits_ = initsSwitch;
830         uninits_ = uninits_.AndSet(uninitsSwitch);
831 
832         if (caseClause->Test() == nullptr) {
833             hasDefault = true;
834         } else {
835             AnalyzeNode(caseClause->Test());
836         }
837 
838         if (hasDefault) {
839             inits_ = initsSwitch;
840             uninits_ = uninits_.AndSet(uninitsSwitch);
841         }
842 
843         AnalyzeStats(caseClause->Consequent());
844 
845         for (const auto stmt : caseClause->Consequent()) {
846             if (!stmt->IsVariableDeclaration()) {
847                 continue;
848             }
849             for (auto *var : stmt->AsVariableDeclaration()->Declarators()) {
850                 NodeId adr = GetNodeId(var);
851                 ES2PANDA_ASSERT(adr >= 0);
852                 initsSwitch.Excl(adr);
853                 uninitsSwitch.Incl(adr);
854             }
855         }
856 
857         if (!hasDefault) {
858             inits_ = initsSwitch;
859             uninits_ = uninits_.AndSet(uninitsSwitch);
860         }
861     }
862 
863     if (!hasDefault) {
864         inits_.AndSet(initsSwitch);
865     }
866 
867     ResolveBreaks(switchStmt);
868 }
869 
AnalyzeTry(const ir::TryStatement * tryStmt)870 void AssignAnalyzer::AnalyzeTry(const ir::TryStatement *tryStmt)
871 {
872     Set uninitsTryPrev = uninitsTry_;
873 
874     PendingExitsVector prevPendingExits = PendingExits();
875     SetOldPendingExits(prevPendingExits);
876 
877     Set initsTry = inits_;
878     uninitsTry_ = uninits_;
879 
880     AnalyzeNode(tryStmt->Block());
881 
882     uninitsTry_.AndSet(uninits_);
883 
884     Set initsEnd = inits_;
885     Set uninitsEnd = uninits_;
886     int nextAdrCatch = nextAdr_;
887 
888     Set initsCatchPrev = initsTry;  // NOLINT(performance-unnecessary-copy-initialization)
889     Set uninitsCatchPrev = uninitsTry_;
890 
891     for (const auto catchClause : tryStmt->CatchClauses()) {
892         inits_ = initsCatchPrev;
893         uninits_ = uninitsCatchPrev;
894 
895         AnalyzeNode(catchClause->Body());
896 
897         initsEnd.AndSet(inits_);
898         uninitsEnd.AndSet(uninits_);
899         nextAdr_ = nextAdrCatch;
900     }
901 
902     if (tryStmt->FinallyBlock() != nullptr) {
903         inits_ = std::move(initsTry);
904         uninits_ = uninitsTry_;
905 
906         PendingExitsVector exits = PendingExits();
907         SetPendingExits(prevPendingExits);
908 
909         AnalyzeNode(tryStmt->FinallyBlock());
910 
911         if (tryStmt->FinallyCanCompleteNormally()) {
912             uninits_.AndSet(uninitsEnd);
913             for (auto exit : exits) {
914                 exit.exitInits_.OrSet(inits_);
915                 exit.exitUninits_.AndSet(uninits_);
916                 PendingExits().push_back(exit);
917             }
918             inits_.OrSet(initsEnd);
919         }
920     } else {
921         inits_ = std::move(initsEnd);
922         uninits_ = std::move(uninitsEnd);
923 
924         PendingExitsVector exits = PendingExits();
925         SetPendingExits(prevPendingExits);
926 
927         for (const auto &exit : exits) {
928             PendingExits().push_back(exit);
929         }
930     }
931 
932     uninitsTry_.AndSet(uninitsTryPrev).AndSet(uninits_);
933 }
934 
AnalyzeBreak(const ir::BreakStatement * breakStmt)935 void AssignAnalyzer::AnalyzeBreak(const ir::BreakStatement *breakStmt)
936 {
937     RecordExit(AssignPendingExit(breakStmt, inits_, uninits_, isInitialConstructor_, hasTryFinallyBlock_));
938 }
939 
AnalyzeContinue(const ir::ContinueStatement * contStmt)940 void AssignAnalyzer::AnalyzeContinue(const ir::ContinueStatement *contStmt)
941 {
942     RecordExit(AssignPendingExit(contStmt, inits_, uninits_, isInitialConstructor_, hasTryFinallyBlock_));
943 }
944 
AnalyzeReturn(const ir::ReturnStatement * retStmt)945 void AssignAnalyzer::AnalyzeReturn(const ir::ReturnStatement *retStmt)
946 {
947     if (retStmt->Argument() != nullptr) {
948         AnalyzeNode(retStmt->Argument());
949     }
950     RecordExit(AssignPendingExit(retStmt, inits_, uninits_, isInitialConstructor_, hasTryFinallyBlock_));
951 }
952 
AnalyzeThrow(const ir::ThrowStatement * throwStmt)953 void AssignAnalyzer::AnalyzeThrow(const ir::ThrowStatement *throwStmt)
954 {
955     AnalyzeNode(throwStmt->Argument());
956     MarkDead();
957 }
958 
AnalyzeExpr(const ir::AstNode * node)959 void AssignAnalyzer::AnalyzeExpr(const ir::AstNode *node)
960 {
961     ES2PANDA_ASSERT(node != nullptr);
962     AnalyzeNode(node);
963     if (inits_.IsReset()) {
964         Merge();
965     }
966 }
967 
AnalyzeExprs(const ArenaVector<ir::Expression * > & exprs)968 void AssignAnalyzer::AnalyzeExprs(const ArenaVector<ir::Expression *> &exprs)
969 {
970     for (const auto it : exprs) {
971         AnalyzeExpr(it);
972     }
973 }
974 
AnalyzeCond(const ir::AstNode * node)975 void AssignAnalyzer::AnalyzeCond(const ir::AstNode *node)
976 {
977     ES2PANDA_ASSERT(node->IsExpression());
978     const ir::Expression *expr = node->AsExpression();
979 
980     if (auto etype = expr->TsType();
981         etype != nullptr && etype->IsETSBooleanType() && etype->HasTypeFlag(TypeFlag::CONSTANT)) {
982         const ETSBooleanType *condType = etype->AsETSBooleanType();
983         if (inits_.IsReset()) {
984             Merge();
985         }
986         if (condType->GetValue()) {
987             initsWhenFalse_ = inits_;
988             initsWhenFalse_.InclRange(firstAdr_, nextAdr_);
989             uninitsWhenFalse_ = uninits_;
990             uninitsWhenFalse_.InclRange(firstAdr_, nextAdr_);
991             initsWhenTrue_ = inits_;
992             uninitsWhenTrue_ = uninits_;
993         } else {
994             initsWhenTrue_ = inits_;
995             initsWhenTrue_.InclRange(firstAdr_, nextAdr_);
996             uninitsWhenTrue_ = uninits_;
997             uninitsWhenTrue_.InclRange(firstAdr_, nextAdr_);
998             initsWhenFalse_ = inits_;
999             uninitsWhenFalse_ = uninits_;
1000         }
1001     } else {
1002         AnalyzeNode(node);
1003         if (!inits_.IsReset()) {
1004             Split(true);
1005         }
1006     }
1007 
1008     inits_.Reset();
1009     uninits_.Reset();
1010 }
1011 
AnalyzeId(const ir::Identifier * id)1012 void AssignAnalyzer::AnalyzeId(const ir::Identifier *id)
1013 {
1014     if (id->Parent()->IsProperty() && id->Parent()->AsProperty()->Key() == id &&
1015         id->Parent()->Parent()->IsObjectExpression()) {
1016         return;  // inside ObjectExpression
1017     }
1018 
1019     if (id->Parent()->IsBinaryExpression()) {
1020         const ir::BinaryExpression *binExpr = id->Parent()->AsBinaryExpression();
1021         if ((binExpr->OperatorType() == lexer::TokenType::PUNCTUATOR_EQUAL ||
1022              binExpr->OperatorType() == lexer::TokenType::PUNCTUATOR_NOT_EQUAL) &&
1023             (binExpr->Left()->IsNullLiteral() || binExpr->Right()->IsNullLiteral() ||
1024              binExpr->Left()->IsUndefinedLiteral() || binExpr->Right()->IsUndefinedLiteral())) {
1025             return;  // null/undefined comparison with == or != operators (e.g. in assert statement)
1026         }
1027     }
1028 
1029     if (id->Parent()->IsMemberExpression()) {
1030         const ir::MemberExpression *membExpr = id->Parent()->AsMemberExpression();
1031         if (id == membExpr->Property() && !membExpr->Object()->IsThisExpression() &&
1032             membExpr->HasMemberKind(ir::MemberExpressionKind::PROPERTY_ACCESS)) {
1033             return;  // something.property
1034         }
1035     }
1036 
1037     CheckInit(id);
1038 }
1039 
IsIdentOrThisDotIdent(const ir::AstNode * node)1040 static bool IsIdentOrThisDotIdent(const ir::AstNode *node)
1041 {
1042     return node->IsIdentifier() ||
1043            (node->IsMemberExpression() && node->AsMemberExpression()->Object()->IsThisExpression());
1044 }
1045 
AnalyzeAssignExpr(const ir::AssignmentExpression * assignExpr)1046 void AssignAnalyzer::AnalyzeAssignExpr(const ir::AssignmentExpression *assignExpr)
1047 {
1048     if (!IsIdentOrThisDotIdent(assignExpr->Left())) {
1049         AnalyzeExpr(assignExpr->Left());
1050     }
1051 
1052     AnalyzeExpr(assignExpr->Right());
1053 
1054     if (assignExpr->OperatorType() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) {
1055         LetInit(assignExpr->Left());
1056     } else {
1057         CheckInit(assignExpr->Left());
1058     }
1059 }
1060 
AnalyzeCondExpr(const ir::ConditionalExpression * condExpr)1061 void AssignAnalyzer::AnalyzeCondExpr(const ir::ConditionalExpression *condExpr)
1062 {
1063     AnalyzeCond(condExpr->Test());
1064 
1065     Set initsBeforeElse = initsWhenFalse_;
1066     Set uninitsBeforeElse = uninitsWhenFalse_;
1067     inits_ = initsWhenTrue_;
1068     uninits_ = uninitsWhenTrue_;
1069 
1070     ES2PANDA_ASSERT(condExpr->Consequent()->TsType() && condExpr->Alternate()->TsType());
1071 
1072     if (condExpr->Consequent()->TsType()->IsETSBooleanType() && condExpr->Alternate()->TsType()->IsETSBooleanType()) {
1073         AnalyzeCond(condExpr->Consequent());
1074 
1075         Set initsAfterThenWhenTrue = initsWhenTrue_;
1076         Set initsAfterThenWhenFalse = initsWhenFalse_;
1077         Set uninitsAfterThenWhenTrue = uninitsWhenTrue_;
1078         Set uninitsAfterThenWhenFalse = uninitsWhenFalse_;
1079         inits_ = std::move(initsBeforeElse);
1080         uninits_ = std::move(uninitsBeforeElse);
1081 
1082         AnalyzeCond(condExpr->Alternate());
1083 
1084         initsWhenTrue_.AndSet(initsAfterThenWhenTrue);
1085         initsWhenFalse_.AndSet(initsAfterThenWhenFalse);
1086         uninitsWhenTrue_.AndSet(uninitsAfterThenWhenTrue);
1087         uninitsWhenFalse_.AndSet(uninitsAfterThenWhenFalse);
1088     } else {
1089         AnalyzeExpr(condExpr->Consequent());
1090 
1091         Set initsAfterThen = inits_;
1092         Set uninitsAfterThen = uninits_;
1093         inits_ = std::move(initsBeforeElse);
1094         uninits_ = std::move(uninitsBeforeElse);
1095 
1096         AnalyzeExpr(condExpr->Alternate());
1097 
1098         inits_.AndSet(initsAfterThen);
1099         uninits_.AndSet(uninitsAfterThen);
1100     }
1101 }
1102 
AnalyzeCallExpr(const ir::CallExpression * callExpr)1103 void AssignAnalyzer::AnalyzeCallExpr(const ir::CallExpression *callExpr)
1104 {
1105     AnalyzeExpr(callExpr->Callee());
1106     AnalyzeExprs(callExpr->Arguments());
1107 }
1108 
AnalyzeMemberExpr(const ir::MemberExpression * membExpr)1109 void AssignAnalyzer::AnalyzeMemberExpr(const ir::MemberExpression *membExpr)
1110 {
1111     if (membExpr->Object()->IsThisExpression() && membExpr->HasMemberKind(ir::MemberExpressionKind::PROPERTY_ACCESS)) {
1112         CheckInit(membExpr);
1113     } else {
1114         AnalyzeNode(membExpr->Object());
1115         AnalyzeNode(membExpr->Property());
1116     }
1117 }
1118 
AnalyzeNewClass(const ir::ETSNewClassInstanceExpression * newClass)1119 void AssignAnalyzer::AnalyzeNewClass(const ir::ETSNewClassInstanceExpression *newClass)
1120 {
1121     AnalyzeExpr(newClass->GetTypeRef());
1122     AnalyzeExprs(newClass->GetArguments());
1123 }
1124 
AnalyzeUnaryExpr(const ir::UnaryExpression * unaryExpr)1125 void AssignAnalyzer::AnalyzeUnaryExpr(const ir::UnaryExpression *unaryExpr)
1126 {
1127     AnalyzeCond(unaryExpr->Argument());
1128 
1129     switch (unaryExpr->OperatorType()) {
1130         case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: {
1131             Set ti = std::move(initsWhenFalse_);
1132             initsWhenFalse_ = std::move(initsWhenTrue_);
1133             initsWhenTrue_ = std::move(ti);
1134             Set tu = std::move(uninitsWhenFalse_);
1135             uninitsWhenFalse_ = std::move(uninitsWhenTrue_);
1136             uninitsWhenTrue_ = std::move(tu);
1137             break;
1138         }
1139         default: {
1140             AnalyzeExpr(unaryExpr->Argument());
1141             break;
1142         }
1143     }
1144 }
1145 
AnalyzeBinaryExpr(const ir::BinaryExpression * binExpr)1146 void AssignAnalyzer::AnalyzeBinaryExpr(const ir::BinaryExpression *binExpr)
1147 {
1148     switch (binExpr->OperatorType()) {
1149         case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: {
1150             AnalyzeCond(binExpr->Left());
1151             Set initsWhenFalseLeft = initsWhenFalse_;
1152             Set uninitsWhenFalseLeft = uninitsWhenFalse_;
1153             inits_ = initsWhenTrue_;
1154             uninits_ = uninitsWhenTrue_;
1155             AnalyzeCond(binExpr->Right());
1156             initsWhenFalse_.AndSet(initsWhenFalseLeft);
1157             uninitsWhenFalse_.AndSet(uninitsWhenFalseLeft);
1158             break;
1159         }
1160         case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
1161             AnalyzeCond(binExpr->Left());
1162             Set initsWhenTrueLeft = initsWhenTrue_;
1163             Set uninitsWhenTrueLeft = uninitsWhenTrue_;
1164             inits_ = initsWhenFalse_;
1165             uninits_ = uninitsWhenFalse_;
1166             AnalyzeCond(binExpr->Right());
1167             initsWhenTrue_.AndSet(initsWhenTrueLeft);
1168             uninitsWhenTrue_.AndSet(uninitsWhenTrueLeft);
1169             break;
1170         }
1171         default: {
1172             AnalyzeExpr(binExpr->Left());
1173             AnalyzeExpr(binExpr->Right());
1174             break;
1175         }
1176     }
1177 }
1178 
AnalyzeUpdateExpr(const ir::UpdateExpression * updateExpr)1179 void AssignAnalyzer::AnalyzeUpdateExpr(const ir::UpdateExpression *updateExpr)
1180 {
1181     AnalyzeExpr(updateExpr->Argument());
1182     LetInit(updateExpr->Argument());
1183 }
1184 
AnalyzeArrowFunctionExpr(const ir::ArrowFunctionExpression * arrowFuncExpr)1185 void AssignAnalyzer::AnalyzeArrowFunctionExpr(const ir::ArrowFunctionExpression *arrowFuncExpr)
1186 {
1187     // NOTE (pantos) handle lamdas correctly
1188     (void)arrowFuncExpr;
1189 }
1190 
GetVariableType(const ir::AstNode * node) const1191 util::StringView AssignAnalyzer::GetVariableType(const ir::AstNode *node) const
1192 {
1193     switch (node->Type()) {
1194         case ir::AstNodeType::CLASS_PROPERTY:
1195             if (node->AsClassProperty()->Parent() == globalClass_) {
1196                 return "variable";
1197             } else {
1198                 return "property";
1199             }
1200         case ir::AstNodeType::VARIABLE_DECLARATOR:
1201             return "variable";
1202         default:
1203             ES2PANDA_UNREACHABLE();
1204     }
1205 }
1206 
GetVariableName(const ir::AstNode * node) const1207 util::StringView AssignAnalyzer::GetVariableName(const ir::AstNode *node) const
1208 {
1209     switch (node->Type()) {
1210         case ir::AstNodeType::CLASS_PROPERTY: {
1211             const ir::Identifier *identifier = node->AsClassProperty()->Id();
1212             ES2PANDA_ASSERT(identifier != nullptr);
1213             return identifier->Name();
1214         }
1215         case ir::AstNodeType::VARIABLE_DECLARATOR:
1216             return node->AsVariableDeclarator()->Id()->AsIdentifier()->Name();
1217         default:
1218             ES2PANDA_UNREACHABLE();
1219     }
1220 }
1221 
GetVariablePosition(const ir::AstNode * node) const1222 const lexer::SourcePosition &AssignAnalyzer::GetVariablePosition(const ir::AstNode *node) const
1223 {
1224     switch (node->Type()) {
1225         case ir::AstNodeType::CLASS_PROPERTY:
1226             return node->AsClassProperty()->Key()->Start();
1227         case ir::AstNodeType::VARIABLE_DECLARATOR:
1228         default:
1229             return node->Start();
1230     }
1231 }
1232 
GetNodeId(const ir::AstNode * node) const1233 NodeId AssignAnalyzer::GetNodeId(const ir::AstNode *node) const
1234 {
1235     auto res = nodeIdMap_.find(node);
1236     if (res != nodeIdMap_.end()) {
1237         return res->second;
1238     }
1239     return INVALID_ID;
1240 }
1241 
Trackable(const ir::AstNode * node) const1242 bool AssignAnalyzer::Trackable(const ir::AstNode *node) const
1243 {
1244     switch (node->Type()) {
1245         case ir::AstNodeType::CLASS_PROPERTY:
1246         case ir::AstNodeType::VARIABLE_DECLARATOR:
1247             return true;
1248         default:
1249             return false;
1250     }
1251 }
1252 
IsConstUninitializedField(const ir::AstNode * node) const1253 bool AssignAnalyzer::IsConstUninitializedField(const ir::AstNode *node) const
1254 {
1255     return node->IsClassProperty() && node->IsConst();
1256 }
1257 
IsConstUninitializedStaticField(const ir::AstNode * node) const1258 bool AssignAnalyzer::IsConstUninitializedStaticField(const ir::AstNode *node) const
1259 {
1260     return IsConstUninitializedField(node) && node->IsStatic();
1261 }
1262 
NewVar(const ir::AstNode * node)1263 void AssignAnalyzer::NewVar(const ir::AstNode *node)
1264 {
1265     if (!Trackable(node)) {
1266         return;
1267     }
1268 
1269     if (GetNodeId(node) != INVALID_ID) {
1270         return;
1271     }
1272 
1273     nodeIdMap_[node] = nextAdr_;
1274     varDecls_.reserve(nextAdr_ + 1);
1275     varDecls_.insert(varDecls_.begin() + nextAdr_, node);
1276     inits_.Excl(nextAdr_);
1277     uninits_.Incl(nextAdr_);
1278     ++nextAdr_;
1279 }
1280 
GetBoundVariable(const ir::AstNode * node)1281 varbinder::Variable *AssignAnalyzer::GetBoundVariable(const ir::AstNode *node)
1282 {
1283     varbinder::Variable *ret = nullptr;
1284 
1285     if (node->IsClassProperty()) {
1286         const ir::Identifier *identifier = node->AsClassProperty()->Id();
1287         ES2PANDA_ASSERT(identifier != nullptr);
1288         ret = identifier->Variable();
1289     } else if (node->IsVariableDeclarator()) {
1290         ret = node->AsVariableDeclarator()->Id()->AsIdentifier()->Variable();
1291     } else {
1292         ES2PANDA_UNREACHABLE();
1293     }
1294 
1295     return ret;
1296 }
1297 
CheckInterfaceProp(const ark::es2panda::ir::AstNode * const node,const ir::ClassDefinition * classDef)1298 static const ir::AstNode *CheckInterfaceProp(const ark::es2panda::ir::AstNode *const node,
1299                                              const ir::ClassDefinition *classDef)
1300 {
1301     util::StringView methodName = node->AsMethodDefinition()->Key()->AsIdentifier()->Name();
1302     // the property from interface should start with <property> to distinguish from its getter/setter.
1303     std::string interfaceProp = std::string("<property>") + std::string(methodName.Utf8());
1304     for (const auto it : classDef->Body()) {
1305         // Check if there is corresponding class property in the same class.
1306         if (it->IsClassProperty() && !it->IsStatic()) {
1307             const auto *prop = it->AsClassProperty();
1308             auto *propIdentifier = prop->Key()->AsIdentifier();
1309             if (propIdentifier->Name().Is(interfaceProp)) {
1310                 // Use property node as declNode to ensure obtaining NodeId and add it to inits.
1311                 return prop;
1312             }
1313         }
1314     }
1315     return nullptr;
1316 }
1317 
GetDeclaringNode(const ir::AstNode * node)1318 const ir::AstNode *AssignAnalyzer::GetDeclaringNode(const ir::AstNode *node)
1319 {
1320     if (node->IsClassProperty() || node->IsVariableDeclarator()) {
1321         return node;
1322     }
1323 
1324     const ir::AstNode *ret = nullptr;
1325 
1326     if (node->IsMemberExpression()) {
1327         const ir::MemberExpression *membExpr = node->AsMemberExpression();
1328         if (membExpr->PropVar() != nullptr) {
1329             if (membExpr->PropVar()->Declaration() != nullptr) {
1330                 ret = membExpr->PropVar()->Declaration()->Node();
1331             }
1332         }
1333     } else if (node->IsIdentifier()) {
1334         if (auto *const variable = node->Variable(); variable != nullptr) {
1335             ret = variable->Declaration()->Node();
1336         }
1337     }
1338 
1339     if (ret != nullptr) {
1340         if (ret->IsIdentifier() && ret->Parent()->IsVariableDeclarator() &&
1341             ret == ret->Parent()->AsVariableDeclarator()->Id()) {
1342             ret = ret->Parent();
1343         }
1344     }
1345 
1346     if (ret != nullptr) {
1347         // if declNode is a getter/setter method, actual node initialized should be a class proterty node.
1348         if ((ret->Modifiers() & ir::ModifierFlags::GETTER_SETTER) != 0U) {
1349             if (const auto *interfaceProp = CheckInterfaceProp(ret, classDef_); interfaceProp != nullptr) {
1350                 ret = interfaceProp;
1351             }
1352         }
1353     }
1354 
1355     return ret;
1356 }
1357 
IsDefaultValueType(const Type * type,bool isNonReadonlyField)1358 static bool IsDefaultValueType(const Type *type, bool isNonReadonlyField)
1359 {
1360     if (type == nullptr) {
1361         return false;
1362     }
1363     return (type->IsETSPrimitiveType() || type->IsETSNeverType() || type->IsETSUndefinedType() ||
1364             type->IsETSNullType() ||
1365             (type->PossiblyETSUndefined() && (!type->HasTypeFlag(checker::TypeFlag::GENERIC) ||
1366                                               (isNonReadonlyField && !CHECK_GENERIC_NON_READONLY_PROPERTIES))));
1367 }
1368 
VariableHasDefaultValue(const ir::AstNode * node)1369 bool AssignAnalyzer::VariableHasDefaultValue(const ir::AstNode *node)
1370 {
1371     ES2PANDA_ASSERT(node != nullptr);
1372 
1373     const checker::Type *type = nullptr;
1374     bool isNonReadonlyField = false;
1375 
1376     if (node->IsClassProperty()) {
1377         type = node->AsClassProperty()->TsType();
1378         isNonReadonlyField = !node->IsReadonly();  // NOTE(pantos) readonly is true, const is not set?
1379     } else if (node->IsVariableDeclarator()) {
1380         varbinder::Variable *variable = GetBoundVariable(node);
1381         ES2PANDA_ASSERT(variable != nullptr);
1382         type = variable->TsType();
1383     } else {
1384         ES2PANDA_UNREACHABLE();
1385     }
1386     return IsDefaultValueType(type, isNonReadonlyField);
1387 }
1388 
LetInit(const ir::AstNode * node)1389 void AssignAnalyzer::LetInit(const ir::AstNode *node)
1390 {
1391     const ir::AstNode *declNode = GetDeclaringNode(node);
1392 
1393     if (declNode == nullptr || declNode->IsDeclare()) {
1394         return;
1395     }
1396 
1397     NodeId adr = GetNodeId(declNode);
1398     if (adr == INVALID_ID) {
1399         return;
1400     }
1401 
1402     if (node != declNode && declNode->IsConst()) {
1403         // check reassignment of readonly properties
1404         util::StringView type = GetVariableType(declNode);
1405         util::StringView name = GetVariableName(declNode);
1406         const lexer::SourcePosition &pos = GetVariablePosition(node);
1407 
1408         auto uninit = [this](NodeId a) {
1409             uninits_.Excl(a);
1410             if (!inits_.IsMember(a)) {
1411                 uninitsTry_.Excl(a);
1412             }
1413         };
1414 
1415         if (classDef_ == globalClass_ || (adr < classFirstAdr_ || adr >= firstAdr_)) {
1416             if (declNode->IsClassProperty() && classDef_ != declNode->Parent()) {
1417                 Warning(diagnostic::ASSIGN_TO_READONLY, {name}, pos);
1418             } else if (!uninits_.IsMember(adr)) {
1419                 Warning(diagnostic::MAYBE_REASSIGNED, {Capitalize(type).c_str(), name}, pos);
1420             } else {
1421                 uninit(adr);
1422             }
1423         }
1424     }
1425 
1426     inits_.Incl(adr);
1427 }
1428 
CheckInit(const ir::AstNode * node)1429 void AssignAnalyzer::CheckInit(const ir::AstNode *node)
1430 {
1431     const ir::AstNode *declNode = GetDeclaringNode(node);
1432 
1433     if (declNode == nullptr || declNode->IsDeclare()) {
1434         return;
1435     }
1436 
1437     NodeId adr = GetNodeId(declNode);
1438     if (adr == INVALID_ID) {
1439         return;
1440     }
1441 
1442     if (VariableHasDefaultValue(declNode)) {
1443         // no explicit init is required (primitive, nullish)
1444         return;
1445     }
1446 
1447     if (declNode->IsClassProperty()) {
1448         if (!CHECK_ALL_PROPERTIES && !declNode->IsConst()) {
1449             // non readonly property
1450             return;
1451         }
1452 
1453         if (declNode->Parent() != classDef_) {
1454             // property of an other class
1455             return;
1456         }
1457 
1458         if (node->IsDefinite()) {
1459             return;
1460         }
1461     }
1462 
1463     if (classDef_ == globalClass_ || (adr < classFirstAdr_ || adr >= firstAdr_)) {
1464         if (!inits_.IsMember(adr)) {
1465             if (WARN_NO_INIT_ONCE_PER_VARIABLE && !foundErrors_.insert(declNode).second) {
1466                 return;
1467             }
1468 
1469             util::StringView type = GetVariableType(declNode);
1470             util::StringView name = GetVariableName(declNode);
1471             const lexer::SourcePosition &pos = GetVariablePosition(node);
1472 
1473             std::stringstream ss;
1474             if (node->IsClassProperty()) {
1475                 checker_->LogError(diagnostic::PROPERTY_MAYBE_MISSING_INIT, {name}, pos);
1476             } else {
1477                 checker_->LogError(diagnostic::USE_BEFORE_INIT, {Capitalize(type), name}, pos);
1478             }
1479         }
1480     }
1481 }
1482 
Split(const bool setToNull)1483 void AssignAnalyzer::Split(const bool setToNull)
1484 {
1485     initsWhenFalse_ = inits_;
1486     uninitsWhenFalse_ = uninits_;
1487     initsWhenTrue_ = inits_;
1488     uninitsWhenTrue_ = uninits_;
1489     if (setToNull) {
1490         inits_.Reset();
1491         uninits_.Reset();
1492     }
1493 }
1494 
Merge()1495 void AssignAnalyzer::Merge()
1496 {
1497     inits_ = initsWhenFalse_.AndSet(initsWhenTrue_);
1498     uninits_ = uninitsWhenFalse_.AndSet(uninitsWhenTrue_);
1499 }
1500 
CheckPendingExits()1501 void AssignAnalyzer::CheckPendingExits()
1502 {
1503     for (const auto &it : PendingExits()) {
1504         if (!it.Node()->IsReturnStatement()) {
1505             continue;
1506         }
1507         if (isInitialConstructor_) {
1508             inits_ = it.exitInits_;
1509 
1510             for (int i = firstAdr_; i < nextAdr_; i++) {
1511                 CheckInit(varDecls_[i]);
1512             }
1513         }
1514     }
1515     ClearPendingExits();
1516 }
1517 
MarkDead()1518 void AssignAnalyzer::MarkDead()
1519 {
1520     if (!isInitialConstructor_) {
1521         inits_.InclRange(returnAdr_, nextAdr_);
1522     } else {
1523         for (int address = returnAdr_; address < nextAdr_; address++) {
1524             if (!IsConstUninitializedStaticField(varDecls_[address])) {
1525                 inits_.Incl(address);
1526             }
1527         }
1528     }
1529     uninits_.InclRange(returnAdr_, nextAdr_);
1530 }
1531 
1532 }  // namespace ark::es2panda::checker
1533