1 /*
2 * Copyright (c) 2023-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 "compiler/lowering/ets/topLevelStmts/importExportDecls.h"
17 #include "generated/diagnostic.h"
18
19 namespace ark::es2panda::compiler {
20
ProgramFileNameLessThan(const parser::Program * a,const parser::Program * b)21 static bool ProgramFileNameLessThan(const parser::Program *a, const parser::Program *b)
22 {
23 return a->FileName().Mutf8() < b->FileName().Mutf8();
24 }
25
ParseDefaultSources()26 void ImportExportDecls::ParseDefaultSources()
27 {
28 auto imports = parser_->ParseDefaultSources(DEFAULT_IMPORT_SOURCE_FILE, defaultImportSource_);
29 varbinder_->SetDefaultImports(std::move(imports));
30 }
31
ProcessProgramStatements(parser::Program * program,const ArenaVector<ir::Statement * > & statements,GlobalClassHandler::ModuleDependencies & moduleDependencies)32 void ImportExportDecls::ProcessProgramStatements(parser::Program *program,
33 const ArenaVector<ir::Statement *> &statements,
34 GlobalClassHandler::ModuleDependencies &moduleDependencies)
35 {
36 for (auto stmt : statements) {
37 if (stmt->IsETSModule()) {
38 SavedImportExportDeclsContext savedContext(this, program);
39 ProcessProgramStatements(program, stmt->AsETSModule()->Statements(), moduleDependencies);
40 VerifyCollectedExportName(program);
41 savedContext.RecoverExportAliasMultimap();
42 }
43 stmt->Accept(this);
44 if (stmt->IsExportNamedDeclaration()) {
45 PopulateAliasMap(stmt->AsExportNamedDeclaration(), program->SourceFilePath());
46 }
47 if (stmt->IsTSTypeAliasDeclaration() && (stmt->IsExported() || stmt->IsDefaultExported())) {
48 PopulateAliasMap(stmt->AsTSTypeAliasDeclaration(), program->SourceFilePath());
49 }
50 }
51 }
52
HandleGlobalStmts(ArenaVector<parser::Program * > & programs)53 GlobalClassHandler::ModuleDependencies ImportExportDecls::HandleGlobalStmts(ArenaVector<parser::Program *> &programs)
54 {
55 VerifySingleExportDefault(programs);
56 VerifyTypeExports(programs);
57 GlobalClassHandler::ModuleDependencies moduleDependencies {programs.front()->Allocator()->Adapter()};
58 if (!programs.empty()) {
59 std::sort(programs.begin(), programs.end(), ProgramFileNameLessThan);
60 }
61 for (const auto &program : programs) {
62 PreMergeNamespaces(program);
63 SavedImportExportDeclsContext savedContext(this, program);
64 ProcessProgramStatements(program, program->Ast()->Statements(), moduleDependencies);
65 VerifyCollectedExportName(program);
66 }
67 return moduleDependencies;
68 }
69
PopulateAliasMap(const ir::ExportNamedDeclaration * decl,const util::StringView & path)70 void ImportExportDecls::PopulateAliasMap(const ir::ExportNamedDeclaration *decl, const util::StringView &path)
71 {
72 for (auto spec : decl->Specifiers()) {
73 if (!varbinder_->AddSelectiveExportAlias(parser_, path, spec->Local()->Name(), spec->Exported()->Name(),
74 decl)) {
75 parser_->LogError(diagnostic::AMBIGUOUS_EXPORT, {spec->Local()->Name().Mutf8()}, spec->Exported()->Start());
76 lastExportErrorPos_ = lexer::SourcePosition();
77 }
78 }
79 }
80
AddExportFlags(ir::AstNode * node,util::StringView originalFieldName,bool exportedWithAlias)81 void ImportExportDecls::AddExportFlags(ir::AstNode *node, util::StringView originalFieldName, bool exportedWithAlias)
82 {
83 if (exportedWithAlias) {
84 node->AddAstNodeFlags(ir::AstNodeFlags::HAS_EXPORT_ALIAS);
85 } else if (originalFieldName == exportDefaultName_) {
86 node->AddModifier(ir::ModifierFlags::DEFAULT_EXPORT);
87 } else {
88 node->AddModifier(ir::ModifierFlags::EXPORT);
89 }
90 }
PopulateAliasMap(const ir::TSTypeAliasDeclaration * decl,const util::StringView & path)91 void ImportExportDecls::PopulateAliasMap(const ir::TSTypeAliasDeclaration *decl, const util::StringView &path)
92 {
93 if (!varbinder_->AddSelectiveExportAlias(parser_, path, decl->Id()->AsIdentifier()->Name(),
94 decl->Id()->AsIdentifier()->Name(), decl)) {
95 parser_->LogError(diagnostic::AMBIGUOUS_EXPORT, {decl->Id()->AsIdentifier()->Name().Mutf8()},
96 lastExportErrorPos_);
97 lastExportErrorPos_ = lexer::SourcePosition();
98 }
99 }
100
HandleSelectiveExportWithAlias(util::StringView originalFieldName,util::StringView exportName,lexer::SourcePosition startLoc)101 void ImportExportDecls::HandleSelectiveExportWithAlias(util::StringView originalFieldName, util::StringView exportName,
102 lexer::SourcePosition startLoc)
103 {
104 bool exportedWithAlias = exportName != originalFieldName;
105
106 auto fieldItem = fieldMap_.find(originalFieldName);
107 ir::VariableDeclarator *variableDeclarator = nullptr;
108 if (fieldItem != fieldMap_.end()) {
109 ir::AstNode *field = fieldItem->second;
110 if (field->IsVariableDeclaration()) {
111 variableDeclarator = field->AsVariableDeclaration()->GetDeclaratorByName(originalFieldName);
112 ES2PANDA_ASSERT(variableDeclarator != nullptr);
113 }
114
115 if (variableDeclarator != nullptr) {
116 AddExportFlags(variableDeclarator, originalFieldName, exportedWithAlias);
117 } else {
118 AddExportFlags(field, originalFieldName, exportedWithAlias);
119 }
120 }
121
122 if (exportedWithAlias) {
123 if (auto declItem = fieldMap_.find(exportName); declItem != fieldMap_.end()) {
124 // Checking for the alias might be unnecessary, because explicit exports cannot
125 // have an alias yet.
126 bool alreadyExported = ((declItem->second->Modifiers() & ir::ModifierFlags::EXPORTED) != 0) &&
127 !declItem->second->HasExportAlias();
128 if (!alreadyExported && declItem->second->IsVariableDeclaration()) {
129 auto declarator = declItem->second->AsVariableDeclaration()->GetDeclaratorByName(exportName);
130 ES2PANDA_ASSERT(declarator != nullptr);
131 alreadyExported |=
132 ((declarator->Modifiers() & ir::ModifierFlags::EXPORTED) != 0) && !declarator->HasExportAlias();
133 }
134 if (alreadyExported) {
135 parser_->LogError(diagnostic::DUPLICATE_EXPORT_NAME, {exportName.Mutf8()}, startLoc);
136 }
137 }
138 }
139 }
140
VisitFunctionDeclaration(ir::FunctionDeclaration * funcDecl)141 void ImportExportDecls::VisitFunctionDeclaration(ir::FunctionDeclaration *funcDecl)
142 {
143 fieldMap_.emplace(funcDecl->Function()->Id()->Name(), funcDecl->Function());
144 }
145
VisitVariableDeclaration(ir::VariableDeclaration * varDecl)146 void ImportExportDecls::VisitVariableDeclaration(ir::VariableDeclaration *varDecl)
147 {
148 for (const auto &decl : varDecl->Declarators()) {
149 fieldMap_.emplace(decl->Id()->AsIdentifier()->Name(), varDecl);
150 }
151 }
152
VisitClassDeclaration(ir::ClassDeclaration * classDecl)153 void ImportExportDecls::VisitClassDeclaration(ir::ClassDeclaration *classDecl)
154 {
155 fieldMap_.emplace(classDecl->Definition()->Ident()->Name(), classDecl);
156 }
157
VisitTSEnumDeclaration(ir::TSEnumDeclaration * enumDecl)158 void ImportExportDecls::VisitTSEnumDeclaration(ir::TSEnumDeclaration *enumDecl)
159 {
160 fieldMap_.emplace(enumDecl->Key()->Name(), enumDecl);
161 }
162
VisitTSTypeAliasDeclaration(ir::TSTypeAliasDeclaration * typeAliasDecl)163 void ImportExportDecls::VisitTSTypeAliasDeclaration(ir::TSTypeAliasDeclaration *typeAliasDecl)
164 {
165 fieldMap_.emplace(typeAliasDecl->Id()->Name(), typeAliasDecl);
166 }
167
VisitTSInterfaceDeclaration(ir::TSInterfaceDeclaration * interfaceDecl)168 void ImportExportDecls::VisitTSInterfaceDeclaration(ir::TSInterfaceDeclaration *interfaceDecl)
169 {
170 fieldMap_.emplace(interfaceDecl->Id()->Name(), interfaceDecl);
171 }
172
VisitAnnotationDeclaration(ir::AnnotationDeclaration * annotationDecl)173 void ImportExportDecls::VisitAnnotationDeclaration(ir::AnnotationDeclaration *annotationDecl)
174 {
175 fieldMap_.emplace(annotationDecl->GetBaseName()->Name(), annotationDecl);
176 }
177
VisitETSModule(ir::ETSModule * etsModule)178 void ImportExportDecls::VisitETSModule(ir::ETSModule *etsModule)
179 {
180 if (etsModule->IsETSScript()) {
181 return;
182 }
183 fieldMap_.emplace(etsModule->Ident()->Name(), etsModule);
184 }
185
VisitExportNamedDeclaration(ir::ExportNamedDeclaration * exportDecl)186 void ImportExportDecls::VisitExportNamedDeclaration(ir::ExportNamedDeclaration *exportDecl)
187 {
188 for (auto spec : exportDecl->Specifiers()) {
189 auto local = spec->Local();
190 // If this was enterred more than once, CTE must has been logged in parser.
191 if ((exportDecl->Modifiers() & ir::ModifierFlags::DEFAULT_EXPORT) != 0) {
192 if (exportDefaultName_ != nullptr) {
193 parser_->LogError(diagnostic::EXPORT_DEFAULT_WITH_MUPLTIPLE_SPECIFIER, {}, local->Start());
194 continue;
195 }
196 exportDefaultName_ = local->Name();
197 }
198 exportNameMap_.emplace(local->Name(), local->Start());
199 }
200 }
201
VisitETSImportDeclaration(ir::ETSImportDeclaration * importDecl)202 void ImportExportDecls::VisitETSImportDeclaration(ir::ETSImportDeclaration *importDecl)
203 {
204 for (ir::AstNode *spec : importDecl->AsETSImportDeclaration()->Specifiers()) {
205 if (spec->IsImportSpecifier()) {
206 importedSpecifiersForExportCheck_.emplace(spec->AsImportSpecifier()->Local()->Name(),
207 spec->AsImportSpecifier()->Imported()->Name());
208 }
209 if (spec->IsImportDefaultSpecifier()) {
210 importedSpecifiersForExportCheck_.emplace(spec->AsImportDefaultSpecifier()->Local()->Name(),
211 spec->AsImportDefaultSpecifier()->Local()->Name());
212 }
213 }
214 }
215
HandleSimpleType(std::set<util::StringView> & exportedStatements,ir::Statement * stmt,util::StringView name)216 void ImportExportDecls::HandleSimpleType(std::set<util::StringView> &exportedStatements, ir::Statement *stmt,
217 util::StringView name)
218 {
219 if (stmt->IsExported()) {
220 exportedStatements.insert(name);
221 }
222 }
223
VerifyTypeExports(const ArenaVector<parser::Program * > & programs)224 void ImportExportDecls::VerifyTypeExports(const ArenaVector<parser::Program *> &programs)
225 {
226 std::set<util::StringView> exportedStatements;
227 std::map<util::StringView, ir::AstNode *> typesMap;
228
229 for (const auto &program : programs) {
230 for (auto stmt : program->Ast()->Statements()) {
231 VerifyType(stmt, exportedStatements, typesMap);
232 }
233 }
234 }
235
VerifyType(ir::Statement * stmt,std::set<util::StringView> & exportedStatements,std::map<util::StringView,ir::AstNode * > & typesMap)236 void ImportExportDecls::VerifyType(ir::Statement *stmt, std::set<util::StringView> &exportedStatements,
237 std::map<util::StringView, ir::AstNode *> &typesMap)
238 {
239 if (stmt->IsClassDeclaration()) {
240 if (!stmt->IsDeclare() && stmt->AsClassDeclaration()->Definition()->Language().IsDynamic()) {
241 parser_->LogError(diagnostic::EXPORT_WITHOUT_DECLARE_IN_DECL_MODULE, {}, stmt->Start());
242 }
243 typesMap.insert({stmt->AsClassDeclaration()->Definition()->Ident()->Name(), stmt});
244 return HandleSimpleType(exportedStatements, stmt, stmt->AsClassDeclaration()->Definition()->Ident()->Name());
245 }
246
247 if (stmt->IsTSInterfaceDeclaration()) {
248 if (!stmt->IsDeclare() && stmt->AsTSInterfaceDeclaration()->Language().IsDynamic()) {
249 parser_->LogError(diagnostic::EXPORT_WITHOUT_DECLARE_IN_DECL_MODULE, {}, stmt->Start());
250 }
251 typesMap.insert({stmt->AsTSInterfaceDeclaration()->Id()->Name(), stmt});
252 return HandleSimpleType(exportedStatements, stmt, stmt->AsTSInterfaceDeclaration()->Id()->Name());
253 }
254
255 if (stmt->IsTSTypeAliasDeclaration()) {
256 typesMap.insert({stmt->AsTSTypeAliasDeclaration()->Id()->Name(), stmt});
257 return HandleSimpleType(exportedStatements, stmt, stmt->AsTSTypeAliasDeclaration()->Id()->Name());
258 }
259 }
260
VerifySingleExportDefault(const ArenaVector<parser::Program * > & programs)261 void ImportExportDecls::VerifySingleExportDefault(const ArenaVector<parser::Program *> &programs)
262 {
263 bool metDefaultExport = false;
264 auto &logger = parser_->DiagnosticEngine();
265 auto verifyDefault = [&metDefaultExport, &logger](ir::Statement *stmt) {
266 if ((stmt->Modifiers() & ir::ModifierFlags::DEFAULT_EXPORT) == 0) {
267 return;
268 }
269 if (metDefaultExport) {
270 logger.LogDiagnostic(diagnostic::MULTIPLE_DEFAULT_EXPORTS, util::DiagnosticMessageParams {}, stmt->Start());
271 }
272 metDefaultExport = true;
273 };
274 for (const auto &program : programs) {
275 for (auto stmt : program->Ast()->Statements()) {
276 verifyDefault(stmt);
277 }
278 metDefaultExport = false;
279 }
280 }
281
VerifyCollectedExportName(const parser::Program * program)282 void ImportExportDecls::VerifyCollectedExportName(const parser::Program *program)
283 {
284 for (auto const &[exportName, startLoc] : exportNameMap_) {
285 const bool isType = exportedTypes_.find(exportName) != exportedTypes_.end();
286 util::StringView middleName = varbinder_->FindNameInAliasMap(program->SourceFilePath(), exportName);
287 ES2PANDA_ASSERT(!middleName.Empty());
288 auto originNameIt = importedSpecifiersForExportCheck_.find(middleName);
289 auto originalName = originNameIt != importedSpecifiersForExportCheck_.end() ? originNameIt->second : middleName;
290 auto result = fieldMap_.find(originalName);
291 if (result == fieldMap_.end() && !isType && originNameIt == importedSpecifiersForExportCheck_.end()) {
292 parser_->LogError(diagnostic::CAN_NOT_FIND_NAME_TO_EXPORT, {originalName}, startLoc);
293 }
294 if (result != fieldMap_.end() && result->second->IsAnnotationDeclaration() && exportName != originalName) {
295 parser_->LogError(diagnostic::CAN_NOT_RENAME_ANNOTATION, {originalName}, startLoc);
296 }
297 if (!isType) {
298 HandleSelectiveExportWithAlias(originalName, exportName, startLoc);
299 }
300 }
301 }
302
PreMergeNamespaces(parser::Program * program)303 void ImportExportDecls::PreMergeNamespaces(parser::Program *program)
304 {
305 bool isChanged = false;
306 auto mergeNameSpace = [&program, &isChanged](ir::AstNode *ast) {
307 if (!ast->IsETSModule()) {
308 return;
309 }
310
311 ArenaVector<ir::ETSModule *> namespaces(program->Allocator()->Adapter());
312 auto &body = ast->AsETSModule()->Statements();
313 auto originalSize = body.size();
314 auto end = std::remove_if(body.begin(), body.end(), [&namespaces](ir::AstNode *node) {
315 if (node->IsETSModule() && node->AsETSModule()->IsNamespace()) {
316 namespaces.emplace_back(node->AsETSModule());
317 return true;
318 }
319 return false;
320 });
321 body.erase(end, body.end());
322
323 GlobalClassHandler::MergeNamespace(namespaces, program);
324
325 for (auto ns : namespaces) {
326 body.emplace_back(ns);
327 }
328
329 isChanged |= (originalSize != body.size());
330 };
331
332 do {
333 isChanged = false;
334 mergeNameSpace(program->Ast());
335 program->Ast()->IterateRecursivelyPreorder(mergeNameSpace);
336 } while (isChanged);
337 }
338 } // namespace ark::es2panda::compiler
339