1 /*
2  * Copyright (c) 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 "importExportAccessValid.h"
17 #include "helpers.h"
18 #include "ir/expressions/callExpression.h"
19 #include "checker/types/ets/etsObjectType.h"
20 #include "ir/module/importSpecifier.h"
21 #include "ir/module/importNamespaceSpecifier.h"
22 #include "ir/module/importDefaultSpecifier.h"
23 #include "ir/expressions/identifier.h"
24 #include "ir/ets/etsImportDeclaration.h"
25 #include "checker/types/signature.h"
26 
27 namespace ark::es2panda::compiler::ast_verifier {
28 
operator ()(CheckContext &ctx, const ir::AstNode *ast)29 [[nodiscard]] CheckResult ImportExportAccessValid::operator()(CheckContext &ctx, const ir::AstNode *ast)
30 {
31     std::unordered_set<std::string> importedVariables {};
32     if (ast->IsETSImportDeclaration()) {
33         const auto importDecl = ast->AsETSImportDeclaration()->Specifiers();
34         const auto name = [](ir::AstNode *const specifier) {
35             if (specifier->IsImportNamespaceSpecifier()) {
36                 return specifier->AsImportNamespaceSpecifier()->Local()->Name();
37             }
38             if (specifier->IsImportSpecifier()) {
39                 return specifier->AsImportSpecifier()->Local()->Name();
40             }
41             return specifier->AsImportDefaultSpecifier()->Local()->Name();
42         };
43         for (const auto import : importDecl) {
44             importedVariables.emplace(name(import));
45         }
46     }
47     if (ast->IsCallExpression()) {
48         const auto *callExpr = ast->AsCallExpression();
49         const auto *callee = callExpr->Callee();
50         if (callee != nullptr && callee->IsIdentifier() &&
51             !HandleImportExportIdentifier(importedVariables, callee->AsIdentifier(), callExpr)) {
52             ctx.AddCheckMessage("PROPERTY_NOT_VISIBLE_HERE(NOT_EXPORTED)", *callee, callee->Start());
53             return {CheckDecision::INCORRECT, CheckAction::CONTINUE};
54         }
55     }
56     if (ast->IsIdentifier() && !HandleImportExportIdentifier(importedVariables, ast->AsIdentifier(), nullptr)) {
57         ctx.AddCheckMessage("PROPERTY_NOT_VISIBLE_HERE(NOT_EXPORTED)", *ast, ast->Start());
58         return {CheckDecision::INCORRECT, CheckAction::CONTINUE};
59     }
60     return {CheckDecision::CORRECT, CheckAction::CONTINUE};
61 }
62 
ValidateExport(const varbinder::Variable *var)63 bool ImportExportAccessValid::ValidateExport(const varbinder::Variable *var)
64 {
65     const auto *decl = var->Declaration();
66     if (decl == nullptr) {
67         return false;
68     }
69     const auto *node = decl->Node();
70     if (node == nullptr) {
71         return false;
72     }
73     return node->IsExported() || node->IsExportedType();
74 }
75 
InvariantImportExportMethod(const std::unordered_set<std::string> &importedVariables, const varbinder::Variable *varCallee, const ir::AstNode *callExpr, util::StringView name)76 bool ImportExportAccessValid::InvariantImportExportMethod(const std::unordered_set<std::string> &importedVariables,
77                                                           const varbinder::Variable *varCallee,
78                                                           const ir::AstNode *callExpr, util::StringView name)
79 {
80     auto *signature = callExpr->AsCallExpression()->Signature();
81     if (signature == nullptr || signature->Owner() == nullptr) {
82         // NOTE(vpukhov): Add a synthetic owner for dynamic signatures
83         ASSERT(callExpr->AsCallExpression()->Callee()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_DYNAMIC_FLAG));
84         return true;
85     }
86 
87     if (signature != nullptr && varCallee->Declaration() != nullptr && varCallee->Declaration()->Node() != nullptr &&
88         !IsContainedIn(varCallee->Declaration()->Node(), signature->Owner()->GetDeclNode()) &&
89         varCallee->Declaration()->Node() != signature->Owner()->GetDeclNode()) {
90         if (importedVariables.find(name.Mutf8()) != importedVariables.end() ||
91             importedVariables.find("") != importedVariables.end()) {
92             return ValidateExport(varCallee);
93         }
94         return false;
95     }
96     return true;
97 }
98 
InvariantImportExportVariable(const std::unordered_set<std::string> &importedVariables, const varbinder::Variable *var, const ir::Identifier *ident, util::StringView name)99 bool ImportExportAccessValid::InvariantImportExportVariable(const std::unordered_set<std::string> &importedVariables,
100                                                             const varbinder::Variable *var, const ir::Identifier *ident,
101                                                             util::StringView name)
102 {
103     if (!var->HasFlag(varbinder::VariableFlags::LOCAL) && !var->HasFlag(varbinder::VariableFlags::VAR) &&
104         var->HasFlag(varbinder::VariableFlags::INITIALIZED) && var->Declaration() != nullptr &&
105         var->Declaration()->Node() != nullptr && !var->Declaration()->Node()->IsMethodDefinition() &&
106         !var->Declaration()->Node()->IsClassProperty()) {
107         auto varParent = var->Declaration()->Node()->Parent();
108         if (varParent != nullptr && !IsContainedIn(ident->Parent(), varParent) && ident->Parent() != varParent) {
109             if (var->GetScope() != nullptr && var->GetScope()->Parent() != nullptr &&
110                 var->GetScope()->Parent()->IsGlobalScope() &&
111                 ident->GetTopStatement() == varParent->GetTopStatement()) {
112                 return true;
113             }
114             if (importedVariables.find(name.Mutf8()) != importedVariables.end() ||
115                 importedVariables.find("") != importedVariables.end()) {
116                 return ValidateExport(var);
117             }
118             return false;
119         }
120     }
121     return true;
122 }
123 
HandleImportExportIdentifier(std::unordered_set<std::string> &importedVariables, const ir::Identifier *ident, const ir::AstNode *callExpr)124 bool ImportExportAccessValid::HandleImportExportIdentifier(std::unordered_set<std::string> &importedVariables,
125                                                            const ir::Identifier *ident, const ir::AstNode *callExpr)
126 {
127     if (ident->IsReference()) {
128         const auto *var = ident->Variable();
129         if (var != nullptr) {
130             if (var->HasFlag(varbinder::VariableFlags::METHOD) && callExpr != nullptr) {
131                 return InvariantImportExportMethod(importedVariables, var, callExpr, ident->Name());
132             }
133             return InvariantImportExportVariable(importedVariables, var, ident, ident->Name());
134         }
135     }
136     return true;
137 }
138 
139 }  // namespace ark::es2panda::compiler::ast_verifier
140