• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024-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 "importExportAccessValid.h"
17 #include "ast_verifier/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 ()(const ir::AstNode * ast)29 [[nodiscard]] CheckResult ImportExportAccessValid::operator()(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             AddCheckMessage("PROPERTY_NOT_VISIBLE_HERE(NOT_EXPORTED)", *callee);
53             return {CheckDecision::INCORRECT, CheckAction::CONTINUE};
54         }
55     }
56     if (ast->IsIdentifier() && !HandleImportExportIdentifier(importedVariables, ast->AsIdentifier(), nullptr)) {
57         AddCheckMessage("PROPERTY_NOT_VISIBLE_HERE(NOT_EXPORTED)", *ast);
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() || node->HasExportAlias();
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         ES2PANDA_ASSERT(
84             callExpr->AsCallExpression()->Callee()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_DYNAMIC_FLAG));
85         return true;
86     }
87 
88     if (signature == nullptr || signature->Owner() == nullptr) {
89         // NOTE(vpukhov): Add a synthetic owner for dynamic signatures
90         ES2PANDA_ASSERT(
91             callExpr->AsCallExpression()->Callee()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_DYNAMIC_FLAG));
92         return true;
93     }
94 
95     if (signature != nullptr && varCallee->Declaration() != nullptr && varCallee->Declaration()->Node() != nullptr &&
96         !IsContainedIn(varCallee->Declaration()->Node(), signature->Owner()->GetDeclNode()) &&
97         varCallee->Declaration()->Node() != signature->Owner()->GetDeclNode()) {
98         if (importedVariables.find(name.Mutf8()) != importedVariables.end() ||
99             importedVariables.find("") != importedVariables.end()) {
100             return ValidateExport(varCallee);
101         }
102         return false;
103     }
104     return true;
105 }
106 
InvariantImportExportVariable(const std::unordered_set<std::string> & importedVariables,const varbinder::Variable * var,const ir::Identifier * ident,util::StringView name)107 bool ImportExportAccessValid::InvariantImportExportVariable(const std::unordered_set<std::string> &importedVariables,
108                                                             const varbinder::Variable *var, const ir::Identifier *ident,
109                                                             util::StringView name)
110 {
111     if (!var->HasFlag(varbinder::VariableFlags::LOCAL) && !var->HasFlag(varbinder::VariableFlags::VAR) &&
112         var->HasFlag(varbinder::VariableFlags::INITIALIZED) && var->Declaration() != nullptr &&
113         var->Declaration()->Node() != nullptr && !var->Declaration()->Node()->IsMethodDefinition() &&
114         !var->Declaration()->Node()->IsClassProperty()) {
115         auto varParent = var->Declaration()->Node()->Parent();
116         if (varParent != nullptr && !IsContainedIn(ident->Parent(), varParent) && ident->Parent() != varParent) {
117             if (var->GetScope() != nullptr && var->GetScope()->Parent() != nullptr &&
118                 var->GetScope()->Parent()->IsGlobalScope() &&
119                 ident->GetTopStatement() == varParent->GetTopStatement()) {
120                 return true;
121             }
122             if (importedVariables.find(name.Mutf8()) != importedVariables.end() ||
123                 importedVariables.find("") != importedVariables.end()) {
124                 return ValidateExport(var);
125             }
126             return false;
127         }
128     }
129     return true;
130 }
131 
HandleImportExportIdentifier(std::unordered_set<std::string> & importedVariables,const ir::Identifier * ident,const ir::AstNode * callExpr)132 bool ImportExportAccessValid::HandleImportExportIdentifier(std::unordered_set<std::string> &importedVariables,
133                                                            const ir::Identifier *ident, const ir::AstNode *callExpr)
134 {
135     // We are running AST verifier only for ETS files so it is correct to pass ETS extension here
136     if (ident->IsReference(ScriptExtension::ETS)) {
137         const auto *var = ident->Variable();
138         if (var != nullptr) {
139             if (var->HasFlag(varbinder::VariableFlags::METHOD) && callExpr != nullptr) {
140                 return InvariantImportExportMethod(importedVariables, var, callExpr, ident->Name());
141             }
142             return InvariantImportExportVariable(importedVariables, var, ident, ident->Name());
143         }
144     }
145     return true;
146 }
147 
148 }  // namespace ark::es2panda::compiler::ast_verifier
149