• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-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 "ASTVerifier.h"
17 
18 #include "checker/types/typeFlag.h"
19 #include "ir/astNode.h"
20 #include "ir/base/classDefinition.h"
21 #include "ir/base/classElement.h"
22 #include "ir/statement.h"
23 #include "ir/base/classStaticBlock.h"
24 #include "ir/base/methodDefinition.h"
25 #include "ir/base/scriptFunction.h"
26 #include "ir/ets/etsNewClassInstanceExpression.h"
27 #include "ir/ets/etsScript.h"
28 #include "ir/ets/etsImportDeclaration.h"
29 #include "ir/expressions/sequenceExpression.h"
30 #include "ir/module/importSpecifier.h"
31 #include "ir/module/importNamespaceSpecifier.h"
32 #include "ir/module/importDefaultSpecifier.h"
33 #include "ir/expressions/callExpression.h"
34 #include "ir/expressions/binaryExpression.h"
35 #include "ir/expressions/identifier.h"
36 #include "ir/expressions/memberExpression.h"
37 #include "ir/statements/forInStatement.h"
38 #include "ir/statements/forOfStatement.h"
39 #include "ir/statements/forUpdateStatement.h"
40 #include "ir/statements/variableDeclarator.h"
41 #include "ir/statements/expressionStatement.h"
42 #include "ir/statements/throwStatement.h"
43 #include "ir/ts/tsTypeParameter.h"
44 #include "lexer/token/tokenType.h"
45 #include "util/ustring.h"
46 #include "utils/arena_containers.h"
47 #include "varbinder/scope.h"
48 
49 constexpr auto RECURSIVE_SUFFIX = "ForAll";
50 
51 namespace panda::es2panda::compiler {
52 
IsNumericType(const ir::AstNode * ast)53 static bool IsNumericType(const ir::AstNode *ast)
54 {
55     if (ast == nullptr) {
56         return false;
57     }
58 
59     if (!ast->IsTyped()) {
60         return false;
61     }
62 
63     auto typedAst = static_cast<const ir::TypedAstNode *>(ast);
64 
65     if (typedAst->TsType() == nullptr) {
66         return false;
67     }
68 
69     return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC) ||
70            typedAst->TsType()->HasTypeFlag(checker::TypeFlag::NUMBER_LITERAL) ||
71            typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL);
72 }
73 
IsStringType(const ir::AstNode * ast)74 static bool IsStringType(const ir::AstNode *ast)
75 {
76     if (ast == nullptr) {
77         return false;
78     }
79 
80     if (!ast->IsTyped()) {
81         return false;
82     }
83 
84     auto typedAst = static_cast<const ir::TypedAstNode *>(ast);
85 
86     if (typedAst->TsType() == nullptr) {
87         return false;
88     }
89 
90     return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::STRING_LIKE);
91 }
92 
93 template <typename T>
IsContainedIn(const T * child,const T * parent)94 static bool IsContainedIn(const T *child, const T *parent)
95 {
96     if (child == nullptr || parent == nullptr) {
97         return false;
98     }
99 
100     std::unordered_set<const T *> savedNodes;
101     while (child != nullptr && child != parent) {
102         savedNodes.emplace(child);
103         child = child->Parent();
104         if (savedNodes.find(child) != savedNodes.end()) {
105             return false;
106         }
107     }
108     return child == parent;
109 }
IsVisibleInternalNode(const ir::AstNode * ast,const ir::AstNode * objTypeDeclNode)110 bool IsVisibleInternalNode(const ir::AstNode *ast, const ir::AstNode *objTypeDeclNode)
111 {
112     auto *currentTopStatement = (static_cast<const ir::ETSScript *>(ast->GetTopStatement()));
113     auto *currentProgram = currentTopStatement->Program();
114     if (currentProgram == nullptr) {
115         return false;
116     }
117     util::StringView packageNameCurrent = currentProgram->GetPackageName();
118     auto *objectTopStatement = (static_cast<const ir::ETSScript *>(objTypeDeclNode->GetTopStatement()));
119     auto *objectProgram = objectTopStatement->Program();
120     if (objectProgram == nullptr) {
121         return false;
122     }
123     util::StringView packageNameObject = objectProgram->GetPackageName();
124     return currentTopStatement == objectTopStatement ||
125            (packageNameCurrent == packageNameObject && !packageNameCurrent.Empty());
126 }
127 
ValidateVariableAccess(const varbinder::LocalVariable * propVar,const ir::MemberExpression * ast)128 static bool ValidateVariableAccess(const varbinder::LocalVariable *propVar, const ir::MemberExpression *ast)
129 {
130     const auto *propVarDecl = propVar->Declaration();
131     if (propVarDecl == nullptr) {
132         return false;
133     }
134     const auto *propVarDeclNode = propVarDecl->Node();
135     if (propVarDeclNode == nullptr) {
136         return false;
137     }
138     auto *objType = ast->ObjType();
139     if (objType == nullptr) {
140         return false;
141     }
142     const auto *objTypeDeclNode = objType->GetDeclNode();
143     if (objTypeDeclNode == nullptr) {
144         return false;
145     }
146     const auto *propVarDeclNodeParent = propVarDeclNode->Parent();
147     if (propVarDeclNodeParent != nullptr && propVarDeclNodeParent->IsClassDefinition() &&
148         objTypeDeclNode->IsClassDefinition()) {
149         // Check if the variable is used where it is declared
150         if (IsContainedIn<const ir::AstNode>(ast, propVarDeclNodeParent->AsClassDefinition())) {
151             return true;
152         }
153         if (propVarDeclNode->IsPrivate()) {
154             return false;
155         }
156         if (propVarDeclNode->IsProtected()) {
157             // Check if the variable is inherited and is used in class in which it is inherited
158             auto ret = objType->IsPropertyInherited(propVar);
159             return ret && IsContainedIn<const ir::AstNode>(ast, objTypeDeclNode->AsClassDefinition());
160         }
161         if (propVarDeclNode->IsInternal()) {
162             return IsVisibleInternalNode(ast, objTypeDeclNode);
163         }
164         return true;
165     }
166     return false;
167 }
168 
ValidateMethodAccess(const ir::MemberExpression * memberExpression,const ir::CallExpression * ast)169 static bool ValidateMethodAccess(const ir::MemberExpression *memberExpression, const ir::CallExpression *ast)
170 {
171     auto *memberObjType = memberExpression->ObjType();
172     if (memberObjType == nullptr) {
173         return false;
174     }
175     if (memberObjType->HasObjectFlag(checker::ETSObjectFlags::RESOLVED_SUPER) &&
176         memberObjType->SuperType() != nullptr &&
177         memberObjType->SuperType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE |
178                                                   checker::ETSObjectFlags::GLOBAL)) {
179         return true;
180     }
181     const auto *memberObjTypeDeclNode = memberObjType->GetDeclNode();
182     if (memberObjTypeDeclNode == nullptr) {
183         return false;
184     }
185     auto *signature = ast->Signature();
186     if (signature == nullptr) {
187         return false;
188     }
189     auto *ownerSign = signature->Owner();
190     if (ownerSign == nullptr) {
191         return false;
192     }
193     auto *ownerSignDeclNode = ownerSign->GetDeclNode();
194     if (ownerSignDeclNode != nullptr && ownerSignDeclNode->IsClassDefinition() &&
195         memberObjTypeDeclNode->IsClassDefinition()) {
196         // Check if the method is used where it is declared
197         if (IsContainedIn<const ir::AstNode>(ast, ownerSignDeclNode->AsClassDefinition())) {
198             return true;
199         }
200         if (signature->HasSignatureFlag(checker::SignatureFlags::PRIVATE)) {
201             return false;
202         }
203         if (signature->HasSignatureFlag(checker::SignatureFlags::PROTECTED)) {
204             // Check if the method is inherited and is used in class in which it is inherited
205             auto ret = memberObjType->IsSignatureInherited(signature);
206             return ret && IsContainedIn<const ir::AstNode>(ast, memberObjTypeDeclNode->AsClassDefinition());
207         }
208         if (signature->HasSignatureFlag(checker::SignatureFlags::INTERNAL)) {
209             return IsVisibleInternalNode(ast, memberObjTypeDeclNode);
210         }
211         return true;
212     }
213     return false;
214 }
215 
216 class NodeHasParent {
217 public:
NodeHasParent(ArenaAllocator & allocator)218     explicit NodeHasParent([[maybe_unused]] ArenaAllocator &allocator) {}
219 
operator ()(ASTVerifier::ErrorContext & ctx,const ir::AstNode * ast)220     ASTVerifier::CheckResult operator()(ASTVerifier::ErrorContext &ctx, const ir::AstNode *ast)
221     {
222         const auto isEtsScript = ast->IsETSScript();
223         const auto hasParent = ast->Parent() != nullptr;
224         if (!isEtsScript && !hasParent) {
225             ctx.AddInvariantError("NodeHasParent", "NULL_PARENT", *ast);
226             return ASTVerifier::CheckResult::FAILED;
227         }
228         if (ast->IsProgram()) {
229             return ASTVerifier::CheckResult::SUCCESS;
230         }
231         return ASTVerifier::CheckResult::SUCCESS;
232     }
233 };
234 
235 class IdentifierHasVariable {
236 public:
IdentifierHasVariable(ArenaAllocator & allocator)237     explicit IdentifierHasVariable([[maybe_unused]] ArenaAllocator &allocator) {}
238 
operator ()(ASTVerifier::ErrorContext & ctx,const ir::AstNode * ast)239     ASTVerifier::CheckResult operator()(ASTVerifier::ErrorContext &ctx, const ir::AstNode *ast)
240     {
241         if (!ast->IsIdentifier()) {
242             return ASTVerifier::CheckResult::SUCCESS;
243         }
244         if (ast->AsIdentifier()->Variable() != nullptr) {
245             return ASTVerifier::CheckResult::SUCCESS;
246         }
247 
248         const auto *id = ast->AsIdentifier();
249         ctx.AddInvariantError("IdentifierHasVariable", "NULL_VARIABLE", *id);
250         return ASTVerifier::CheckResult::FAILED;
251     }
252 
253 private:
254 };
255 
256 class NodeHasType {
257 public:
NodeHasType(ArenaAllocator & allocator)258     explicit NodeHasType([[maybe_unused]] ArenaAllocator &allocator) {}
259 
operator ()(ASTVerifier::ErrorContext & ctx,const ir::AstNode * ast)260     ASTVerifier::CheckResult operator()(ASTVerifier::ErrorContext &ctx, const ir::AstNode *ast)
261     {
262         if (ast->IsTyped()) {
263             if (ast->IsClassDefinition() && ast->AsClassDefinition()->Ident()->Name() == "ETSGLOBAL") {
264                 return ASTVerifier::CheckResult::SKIP_SUBTREE;
265             }
266             const auto *typed = static_cast<const ir::TypedAstNode *>(ast);
267             if (typed->TsType() == nullptr) {
268                 ctx.AddInvariantError("NodeHasType", "NULL_TS_TYPE", *ast);
269                 return ASTVerifier::CheckResult::FAILED;
270             }
271         }
272         return ASTVerifier::CheckResult::SUCCESS;
273     }
274 
275 private:
276 };
277 
278 class VariableHasScope {
279 public:
VariableHasScope(ArenaAllocator & allocator)280     explicit VariableHasScope(ArenaAllocator &allocator) : allocator_ {allocator} {}
281 
operator ()(ASTVerifier::ErrorContext & ctx,const ir::AstNode * ast)282     ASTVerifier::CheckResult operator()(ASTVerifier::ErrorContext &ctx, const ir::AstNode *ast)
283     {
284         if (!ast->IsIdentifier()) {
285             return ASTVerifier::CheckResult::SUCCESS;  // we will check invariant of Identifier only
286         }
287 
288         // we will check invariant for only local variables of identifiers
289         if (const auto maybeVar = GetLocalScopeVariable(allocator_, ctx, ast); maybeVar.has_value()) {
290             const auto var = *maybeVar;
291             const auto scope = var->GetScope();
292             if (scope == nullptr) {
293                 ctx.AddInvariantError("VariableHasScope", "NULL_SCOPE_LOCAL_VAR", *ast);
294                 return ASTVerifier::CheckResult::FAILED;
295             }
296             return ScopeEncloseVariable(ctx, var) ? ASTVerifier::CheckResult::SUCCESS
297                                                   : ASTVerifier::CheckResult::FAILED;
298         }
299         return ASTVerifier::CheckResult::SUCCESS;
300     }
301 
GetLocalScopeVariable(ArenaAllocator & allocator,ASTVerifier::ErrorContext & ctx,const ir::AstNode * ast)302     static std::optional<varbinder::LocalVariable *> GetLocalScopeVariable(ArenaAllocator &allocator,
303                                                                            ASTVerifier::ErrorContext &ctx,
304                                                                            const ir::AstNode *ast)
305     {
306         if (!ast->IsIdentifier()) {
307             return std::nullopt;
308         }
309 
310         auto invariantHasVariable = IdentifierHasVariable {allocator};
311         const auto variable = ast->AsIdentifier()->Variable();
312         if ((invariantHasVariable(ctx, ast) == ASTVerifier::CheckResult::SUCCESS) && variable->IsLocalVariable()) {
313             const auto localVar = variable->AsLocalVariable();
314             if (localVar->HasFlag(varbinder::VariableFlags::LOCAL)) {
315                 return localVar;
316             }
317         }
318         return std::nullopt;
319     }
320 
ScopeEncloseVariable(ASTVerifier::ErrorContext & ctx,const varbinder::LocalVariable * var)321     bool ScopeEncloseVariable(ASTVerifier::ErrorContext &ctx, const varbinder::LocalVariable *var)
322     {
323         ASSERT(var);
324 
325         const auto scope = var->GetScope();
326         if (scope == nullptr || var->Declaration() == nullptr) {
327             return true;
328         }
329         const auto node = var->Declaration()->Node();
330         if (node == nullptr) {
331             return true;
332         }
333         const auto name = "VariableHasScope";
334         bool isOk = true;
335         if (scope->Bindings().count(var->Name()) == 0) {
336             ctx.AddInvariantError(name, "SCOPE_DO_NOT_ENCLOSE_LOCAL_VAR", *node);
337             isOk = false;
338         }
339         const auto scopeNode = scope->Node();
340         auto varNode = node;
341         if (!IsContainedIn(varNode, scopeNode) || scopeNode == nullptr) {
342             ctx.AddInvariantError(name, "SCOPE_NODE_DONT_DOMINATE_VAR_NODE", *node);
343             isOk = false;
344         }
345         const auto &decls = scope->Decls();
346         const auto declDominate = std::count(decls.begin(), decls.end(), var->Declaration());
347         if (declDominate == 0) {
348             ctx.AddInvariantError(name, "SCOPE_DECL_DONT_DOMINATE_VAR_DECL", *node);
349             isOk = false;
350         }
351         return isOk;
352     }
353 
354 private:
355     ArenaAllocator &allocator_;
356 };
357 
358 class EveryChildHasValidParent {
359 public:
EveryChildHasValidParent(ArenaAllocator & allocator)360     explicit EveryChildHasValidParent([[maybe_unused]] ArenaAllocator &allocator) {}
361 
operator ()(ASTVerifier::ErrorContext & ctx,const ir::AstNode * ast)362     ASTVerifier::CheckResult operator()(ASTVerifier::ErrorContext &ctx, const ir::AstNode *ast)
363     {
364         auto result = ASTVerifier::CheckResult::SUCCESS;
365         if (ast->IsETSScript()) {
366             return result;
367         }
368         ast->Iterate([&](const ir::AstNode *node) {
369             if (ast != node->Parent()) {
370                 ctx.AddInvariantError("EveryChildHasValidParent", "INCORRECT_PARENT_REF", *node);
371                 result = ASTVerifier::CheckResult::FAILED;
372             }
373         });
374         return result;
375     }
376 
377 private:
378 };
379 
380 class VariableHasEnclosingScope {
381 public:
VariableHasEnclosingScope(ArenaAllocator & allocator)382     explicit VariableHasEnclosingScope(ArenaAllocator &allocator) : allocator_ {allocator} {}
383 
operator ()(ASTVerifier::ErrorContext & ctx,const ir::AstNode * ast)384     ASTVerifier::CheckResult operator()(ASTVerifier::ErrorContext &ctx, const ir::AstNode *ast)
385     {
386         const auto maybeVar = VariableHasScope::GetLocalScopeVariable(allocator_, ctx, ast);
387         if (!maybeVar) {
388             return ASTVerifier::CheckResult::SUCCESS;
389         }
390         const auto var = *maybeVar;
391         const auto scope = var->GetScope();
392         const auto name = "VariableHasEnclosingScope";
393         if (scope == nullptr) {
394             // already checked
395             return ASTVerifier::CheckResult::SUCCESS;
396         }
397         const auto encloseScope = scope->EnclosingVariableScope();
398         if (encloseScope == nullptr) {
399             ctx.AddInvariantError(name, "NO_ENCLOSING_VAR_SCOPE", *ast);
400             return ASTVerifier::CheckResult::FAILED;
401         }
402         const auto node = scope->Node();
403         auto result = ASTVerifier::CheckResult::SUCCESS;
404         if (!IsContainedIn(ast, node)) {
405             result = ASTVerifier::CheckResult::FAILED;
406             ctx.AddInvariantError(name, "VARIABLE_NOT_ENCLOSE_SCOPE", *ast);
407         }
408         if (!IsContainedIn<varbinder::Scope>(scope, encloseScope)) {
409             result = ASTVerifier::CheckResult::FAILED;
410             ctx.AddInvariantError(name, "VARIABLE_NOT_ENCLOSE_SCOPE", *ast);
411         }
412         return result;
413     }
414 
415 private:
416     ArenaAllocator &allocator_;
417 };
418 
419 class SequenceExpressionHasLastType {
420 public:
SequenceExpressionHasLastType(ArenaAllocator & allocator)421     explicit SequenceExpressionHasLastType([[maybe_unused]] ArenaAllocator &allocator) {}
422 
operator ()(ASTVerifier::ErrorContext & ctx,const ir::AstNode * ast)423     ASTVerifier::CheckResult operator()(ASTVerifier::ErrorContext &ctx, const ir::AstNode *ast)
424     {
425         if (!ast->IsSequenceExpression()) {
426             return ASTVerifier::CheckResult::SUCCESS;
427         }
428         const auto *expr = ast->AsSequenceExpression();
429         const auto *last = expr->Sequence().back();
430         const auto name = "SequenceExpressionHasLastType";
431         if (expr->TsType() == nullptr) {
432             ctx.AddInvariantError(name, "Sequence expression type is null", *expr);
433             return ASTVerifier::CheckResult::FAILED;
434         }
435         if (last->TsType() == nullptr) {
436             ctx.AddInvariantError(name, "Sequence expression last type is null", *last);
437             return ASTVerifier::CheckResult::FAILED;
438         }
439         if (expr->TsType() != last->TsType()) {
440             ctx.AddInvariantError(name, "Sequence expression type and last expression type are not the same", *expr);
441             return ASTVerifier::CheckResult::FAILED;
442         }
443         return ASTVerifier::CheckResult::SUCCESS;
444     }
445 
446 private:
447 };
448 
449 class ForLoopCorrectlyInitialized {
450 public:
ForLoopCorrectlyInitialized(ArenaAllocator & allocator)451     explicit ForLoopCorrectlyInitialized([[maybe_unused]] ArenaAllocator &allocator) {}
452 
operator ()(ASTVerifier::ErrorContext & ctx,const ir::AstNode * ast)453     ASTVerifier::CheckResult operator()(ASTVerifier::ErrorContext &ctx, const ir::AstNode *ast)
454     {
455         const auto name = "ForLoopCorrectlyInitialized";
456         if (ast->IsForInStatement()) {
457             auto const *left = ast->AsForInStatement()->Left();
458             if (left == nullptr) {
459                 ctx.AddInvariantError(name, "NULL FOR-IN-LEFT", *ast);
460                 return ASTVerifier::CheckResult::FAILED;
461             }
462 
463             if (!left->IsIdentifier() && !left->IsVariableDeclaration()) {
464                 ctx.AddInvariantError(name, "INCORRECT FOR-IN-LEFT", *ast);
465                 return ASTVerifier::CheckResult::FAILED;
466             }
467         }
468 
469         if (ast->IsForOfStatement()) {
470             auto const *left = ast->AsForOfStatement()->Left();
471             if (left == nullptr) {
472                 ctx.AddInvariantError(name, "NULL FOR-OF-LEFT", *ast);
473                 return ASTVerifier::CheckResult::FAILED;
474             }
475 
476             if (!left->IsIdentifier() && !left->IsVariableDeclaration()) {
477                 ctx.AddInvariantError(name, "INCORRECT FOR-OF-LEFT", *ast);
478                 return ASTVerifier::CheckResult::FAILED;
479             }
480         }
481 
482         if (ast->IsForUpdateStatement()) {
483             // The most important part of for-loop is the test.
484             // But it also can be null. Then there must be break;(return) in the body.
485             auto const *test = ast->AsForUpdateStatement()->Test();
486             if (test == nullptr) {
487                 auto const *body = ast->AsForUpdateStatement()->Body();
488                 if (body == nullptr) {
489                     ctx.AddInvariantError(name, "NULL FOR-TEST AND FOR-BODY", *ast);
490                     return ASTVerifier::CheckResult::FAILED;
491                 }
492                 bool hasExit = body->IsBreakStatement() || body->IsReturnStatement();
493                 body->IterateRecursively([&hasExit](ir::AstNode *child) {
494                     hasExit |= child->IsBreakStatement() || child->IsReturnStatement();
495                 });
496                 if (!hasExit) {
497                     // an infinite loop
498                     ctx.AddInvariantError(name, "WARNING: NULL FOR-TEST AND FOR-BODY doesn't exit", *ast);
499                 }
500                 return ASTVerifier::CheckResult::SUCCESS;
501             }
502 
503             if (!test->IsExpression()) {
504                 ctx.AddInvariantError(name, "NULL FOR VAR", *ast);
505                 return ASTVerifier::CheckResult::FAILED;
506             }
507         }
508         return ASTVerifier::CheckResult::SUCCESS;
509     }
510 
511 private:
512 };
513 
514 class ModifierAccessValid {
515 public:
ModifierAccessValid(ArenaAllocator & allocator)516     explicit ModifierAccessValid([[maybe_unused]] ArenaAllocator &allocator) {}
517 
operator ()(ASTVerifier::ErrorContext & ctx,const ir::AstNode * ast)518     ASTVerifier::CheckResult operator()(ASTVerifier::ErrorContext &ctx, const ir::AstNode *ast)
519     {
520         const auto name = "ModifierAccessValid";
521         if (ast->IsMemberExpression()) {
522             const auto *propVar = ast->AsMemberExpression()->PropVar();
523             if (propVar != nullptr && propVar->HasFlag(varbinder::VariableFlags::PROPERTY) &&
524                 !ValidateVariableAccess(propVar, ast->AsMemberExpression())) {
525                 ctx.AddInvariantError(name, "PROPERTY_NOT_VISIBLE_HERE", *ast);
526                 return ASTVerifier::CheckResult::FAILED;
527             }
528         }
529         if (ast->IsCallExpression()) {
530             const auto *callExpr = ast->AsCallExpression();
531             const auto *callee = callExpr->Callee();
532             if (callee != nullptr && callee->IsMemberExpression()) {
533                 const auto *calleeMember = callee->AsMemberExpression();
534                 const auto *propVarCallee = calleeMember->PropVar();
535                 if (propVarCallee != nullptr && propVarCallee->HasFlag(varbinder::VariableFlags::METHOD) &&
536                     !ValidateMethodAccess(calleeMember, ast->AsCallExpression())) {
537                     ctx.AddInvariantError(name, "PROPERTY_NOT_VISIBLE_HERE", *callee);
538                     return ASTVerifier::CheckResult::FAILED;
539                 }
540             }
541         }
542         return ASTVerifier::CheckResult::SUCCESS;
543     }
544 
545 private:
546 };
547 
548 class ImportExportAccessValid {
549 public:
ImportExportAccessValid(ArenaAllocator & allocator)550     explicit ImportExportAccessValid([[maybe_unused]] ArenaAllocator &allocator) {}
551 
operator ()(ASTVerifier::ErrorContext & ctx,const ir::AstNode * ast)552     ASTVerifier::CheckResult operator()(ASTVerifier::ErrorContext &ctx, const ir::AstNode *ast)
553     {
554         ASTVerifier::InvariantSet importedVariables {};
555         if (ast->IsETSImportDeclaration()) {
556             const auto importDecl = ast->AsETSImportDeclaration()->Specifiers();
557             const auto name = [](ir::AstNode *const specifier) {
558                 if (specifier->IsImportNamespaceSpecifier()) {
559                     return specifier->AsImportNamespaceSpecifier()->Local()->Name();
560                 }
561                 if (specifier->IsImportSpecifier()) {
562                     return specifier->AsImportSpecifier()->Local()->Name();
563                 }
564                 return specifier->AsImportDefaultSpecifier()->Local()->Name();
565             };
566             for (const auto import : importDecl) {
567                 importedVariables.emplace(name(import));
568             }
569         }
570         const auto name = "ImportExportAccessValid";
571         if (ast->IsCallExpression()) {
572             const auto *callExpr = ast->AsCallExpression();
573             const auto *callee = callExpr->Callee();
574             if (callee != nullptr && callee->IsIdentifier() &&
575                 !HandleImportExportIdentifier(importedVariables, callee->AsIdentifier(), callExpr)) {
576                 ctx.AddInvariantError(name, "PROPERTY_NOT_VISIBLE_HERE(NOT_EXPORTED)", *callee);
577                 return ASTVerifier::CheckResult::FAILED;
578             }
579         }
580         if (ast->IsIdentifier() && !HandleImportExportIdentifier(importedVariables, ast->AsIdentifier(), nullptr)) {
581             ctx.AddInvariantError(name, "PROPERTY_NOT_VISIBLE_HERE(NOT_EXPORTED)", *ast);
582             return ASTVerifier::CheckResult::FAILED;
583         }
584         return ASTVerifier::CheckResult::SUCCESS;
585     }
586 
587 private:
ValidateExport(const varbinder::Variable * var)588     bool ValidateExport(const varbinder::Variable *var)
589     {
590         const auto *decl = var->Declaration();
591         if (decl == nullptr) {
592             return false;
593         }
594         const auto *node = decl->Node();
595         if (node == nullptr) {
596             return false;
597         }
598         return node->IsExported();
599     }
600 
InvariantImportExportMethod(const ASTVerifier::InvariantSet & importedVariables,const varbinder::Variable * varCallee,const ir::AstNode * callExpr,util::StringView name)601     bool InvariantImportExportMethod(const ASTVerifier::InvariantSet &importedVariables,
602                                      const varbinder::Variable *varCallee, const ir::AstNode *callExpr,
603                                      util::StringView name)
604     {
605         auto *signature = callExpr->AsCallExpression()->Signature();
606         if (signature->Owner() == nullptr) {
607             // NOTE(vpukhov): Add a synthetic owner for dynamic signatures
608             ASSERT(callExpr->AsCallExpression()->Callee()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_DYNAMIC_FLAG));
609             return true;
610         }
611 
612         if (signature != nullptr && varCallee->Declaration() != nullptr &&
613             varCallee->Declaration()->Node() != nullptr &&
614             !IsContainedIn(varCallee->Declaration()->Node(), signature->Owner()->GetDeclNode()) &&
615             varCallee->Declaration()->Node() != signature->Owner()->GetDeclNode()) {
616             if (importedVariables.find(name.Mutf8()) != importedVariables.end() ||
617                 importedVariables.find("") != importedVariables.end()) {
618                 return ValidateExport(varCallee);
619             }
620             return false;
621         }
622         return true;
623     }
624 
InvariantImportExportVariable(const ASTVerifier::InvariantSet & importedVariables,const varbinder::Variable * var,const ir::Identifier * ident,util::StringView name)625     bool InvariantImportExportVariable(const ASTVerifier::InvariantSet &importedVariables,
626                                        const varbinder::Variable *var, const ir::Identifier *ident,
627                                        util::StringView name)
628     {
629         if (!var->HasFlag(varbinder::VariableFlags::LOCAL) && !var->HasFlag(varbinder::VariableFlags::VAR) &&
630             var->HasFlag(varbinder::VariableFlags::INITIALIZED) && var->Declaration() != nullptr &&
631             var->Declaration()->Node() != nullptr && !var->Declaration()->Node()->IsMethodDefinition() &&
632             !var->Declaration()->Node()->IsClassProperty()) {
633             auto varParent = var->Declaration()->Node()->Parent();
634             if (varParent != nullptr && !IsContainedIn(ident->Parent(), varParent) && ident->Parent() != varParent) {
635                 if (var->GetScope() != nullptr && var->GetScope()->Parent() != nullptr &&
636                     var->GetScope()->Parent()->IsGlobalScope() &&
637                     ident->GetTopStatement() == varParent->GetTopStatement()) {
638                     return true;
639                 }
640                 if (importedVariables.find(name.Mutf8()) != importedVariables.end() ||
641                     importedVariables.find("") != importedVariables.end()) {
642                     return ValidateExport(var);
643                 }
644                 return false;
645             }
646         }
647         return true;
648     }
649 
HandleImportExportIdentifier(ASTVerifier::InvariantSet & importedVariables,const ir::Identifier * ident,const ir::AstNode * callExpr)650     bool HandleImportExportIdentifier(ASTVerifier::InvariantSet &importedVariables, const ir::Identifier *ident,
651                                       const ir::AstNode *callExpr)
652     {
653         if (ident->IsReference()) {
654             const auto *var = ident->Variable();
655             if (var != nullptr) {
656                 if (var->HasFlag(varbinder::VariableFlags::METHOD) && callExpr != nullptr) {
657                     return InvariantImportExportMethod(importedVariables, var, callExpr, ident->Name());
658                 }
659                 return InvariantImportExportVariable(importedVariables, var, ident, ident->Name());
660             }
661         }
662         return true;
663     }
664 };
665 
666 class ArithmeticOperationValid {
667 public:
ArithmeticOperationValid(ArenaAllocator & allocator)668     explicit ArithmeticOperationValid([[maybe_unused]] ArenaAllocator &allocator) {}
669 
operator ()(ASTVerifier::ErrorContext & ctx,const ir::AstNode * ast)670     ASTVerifier::CheckResult operator()([[maybe_unused]] ASTVerifier::ErrorContext &ctx, const ir::AstNode *ast)
671     {
672         if (ast->IsBinaryExpression() && ast->AsBinaryExpression()->IsArithmetic()) {
673             if (ast->AsBinaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS &&
674                 IsStringType(ast->AsBinaryExpression()->Left()) && IsStringType(ast->AsBinaryExpression()->Right())) {
675                 return ASTVerifier::CheckResult::SUCCESS;
676             }
677             auto result = ASTVerifier::CheckResult::SUCCESS;
678             ast->Iterate([&result](ir::AstNode *child) {
679                 if (!IsNumericType(child)) {
680                     result = ASTVerifier::CheckResult::FAILED;
681                 }
682             });
683             return result;
684         }
685 
686         return ASTVerifier::CheckResult::SUCCESS;
687     }
688 
689 private:
690 };
691 
692 template <typename Func>
RecursiveInvariant(const Func & func)693 static ASTVerifier::InvariantCheck RecursiveInvariant(const Func &func)
694 {
695     return [func](ASTVerifier::ErrorContext &ctx, const ir::AstNode *ast) -> ASTVerifier::CheckResult {
696         std::function<void(const ir::AstNode *)> aux;
697         auto result = ASTVerifier::CheckResult::SUCCESS;
698         aux = [&ctx, &func, &aux, &result](const ir::AstNode *child) -> void {
699             if (result == ASTVerifier::CheckResult::FAILED) {
700                 return;
701             }
702             const auto newResult = func(ctx, child);
703             if (newResult == ASTVerifier::CheckResult::SKIP_SUBTREE) {
704                 return;
705             }
706             result = newResult;
707             child->Iterate(aux);
708         };
709         aux(ast);
710         return result;
711     };
712 }
713 
AddInvariant(const std::string & name,const InvariantCheck & invariant)714 void ASTVerifier::AddInvariant(const std::string &name, const InvariantCheck &invariant)
715 {
716     invariantsChecks_[name] = invariant;
717     invariantsNames_.insert(name);
718     invariantsChecks_[name + RECURSIVE_SUFFIX] = RecursiveInvariant(invariant);
719     invariantsNames_.insert(name + RECURSIVE_SUFFIX);
720 }
721 
ASTVerifier(ArenaAllocator * allocator)722 ASTVerifier::ASTVerifier(ArenaAllocator *allocator)
723 {
724     AddInvariant("NodeHasParent", *allocator->New<NodeHasParent>(*allocator));
725     AddInvariant("NodeHasType", *allocator->New<NodeHasType>(*allocator));
726     AddInvariant("IdentifierHasVariable", *allocator->New<IdentifierHasVariable>(*allocator));
727     AddInvariant("VariableHasScope", *allocator->New<VariableHasScope>(*allocator));
728     AddInvariant("EveryChildHasValidParent", *allocator->New<EveryChildHasValidParent>(*allocator));
729     AddInvariant("VariableHasEnclosingScope", *allocator->New<VariableHasEnclosingScope>(*allocator));
730     AddInvariant("ForLoopCorrectlyInitialized", *allocator->New<ForLoopCorrectlyInitialized>(*allocator));
731     AddInvariant("ModifierAccessValid", *allocator->New<ModifierAccessValid>(*allocator));
732     AddInvariant("ImportExportAccessValid", *allocator->New<ImportExportAccessValid>(*allocator));
733     AddInvariant("ArithmeticOperationValid", *allocator->New<ArithmeticOperationValid>(*allocator));
734     AddInvariant("SequenceExpressionHasLastType", *allocator->New<SequenceExpressionHasLastType>(*allocator));
735 }
736 
VerifyFull(const std::unordered_set<std::string> & warnings,const std::unordered_set<std::string> & asserts,const ir::AstNode * ast)737 std::tuple<ASTVerifier::Errors, ASTVerifier::Errors> ASTVerifier::VerifyFull(
738     const std::unordered_set<std::string> &warnings, const std::unordered_set<std::string> &asserts,
739     const ir::AstNode *ast)
740 {
741     auto recursiveChecks = InvariantSet {};
742     std::copy_if(invariantsNames_.begin(), invariantsNames_.end(),
743                  std::inserter(recursiveChecks, recursiveChecks.end()),
744                  [](const std::string &s) { return s.find(RECURSIVE_SUFFIX) != s.npos; });
745     return Verify(warnings, asserts, ast, recursiveChecks);
746 }
747 
Verify(const std::unordered_set<std::string> & warnings,const std::unordered_set<std::string> & asserts,const ir::AstNode * ast,const InvariantSet & invariantSet)748 std::tuple<ASTVerifier::Errors, ASTVerifier::Errors> ASTVerifier::Verify(
749     const std::unordered_set<std::string> &warnings, const std::unordered_set<std::string> &asserts,
750     const ir::AstNode *ast, const InvariantSet &invariantSet)
751 {
752     ErrorContext warningCtx {};
753     AssertsContext assertCtx {};
754 
755     const auto containsInvariants =
756         std::includes(invariantsNames_.begin(), invariantsNames_.end(), invariantSet.begin(), invariantSet.end());
757     if (!containsInvariants) {
758         auto invalidInvariants = InvariantSet {};
759         for (const auto &invariant : invariantSet) {
760             if (invariantsNames_.find(invariant) == invariantsNames_.end()) {
761                 invalidInvariants.insert(invariant.data());
762             }
763         }
764         for (const auto &invariant : invalidInvariants) {
765             assertCtx.AddError(std::string {"invariant was not found: "} + invariant);
766         }
767     }
768 
769     for (const auto &invariantName : invariantSet) {
770         if (warnings.count(invariantName) > 0) {
771             invariantsChecks_[invariantName](warningCtx, ast);
772         } else if (asserts.count(invariantName) > 0) {
773             invariantsChecks_[invariantName](assertCtx, ast);
774         }
775     }
776 
777     return std::make_tuple(warningCtx.GetErrors(), assertCtx.GetErrors());
778 }
779 
780 }  // namespace panda::es2panda::compiler
781