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