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