• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "compiler/lowering/ets/topLevelStmts/importExportDecls.h"
17 #include "ir/ets/etsReExportDeclaration.h"
18 #include "parser/program/program.h"
19 #include "util/importPathManager.h"
20 
21 namespace ark::es2panda::compiler {
22 
ProgramFileNameLessThan(const parser::Program * a,const parser::Program * b)23 static bool ProgramFileNameLessThan(const parser::Program *a, const parser::Program *b)
24 {
25     return a->FileName().Mutf8() < b->FileName().Mutf8();
26 }
27 
ParseDefaultSources()28 void ImportExportDecls::ParseDefaultSources()
29 {
30     auto imports = parser_->ParseDefaultSources(DEFAULT_IMPORT_SOURCE_FILE, defaultImportSource_);
31     varbinder_->SetDefaultImports(std::move(imports));
32 }
33 
34 /**
35  * @brief checks if `prog` matches with the path in `stmt` (an ImportDeclaration statement)
36  *
37  * @return true if `prog` is part of a package
38  * @return false otherwise
39  */
MatchResolvedPathWithProgram(std::string_view resolvedPath,parser::Program * prog)40 bool ImportExportDecls::MatchResolvedPathWithProgram(std::string_view resolvedPath, parser::Program *prog)
41 {
42     if (util::Helpers::IsStdLib(prog)) {
43         return false;
44     }
45     if (prog->IsDeclarationModule()) {
46         return false;
47     }
48     if (prog->AbsoluteName().Is(resolvedPath)) {
49         return true;
50     }
51     if (prog->IsPackageModule() && prog->SourceFileFolder().Is(resolvedPath)) {
52         return true;
53     }
54     return false;
55 }
56 
57 /**
58  * @brief Collects imported programs to initialize from the owner classes of the imported programs
59  *
60  * If match is found, `prog` is inserted into a container (`moduleDependencies`) that will be used when
61  * calling the initializers inside the entrypoint's `_$init$_`.
62  */
CollectImportedProgramsFromStmts(ark::es2panda::ir::ETSImportDeclaration * stmt,parser::Program * program,GlobalClassHandler::ModuleDependencies * moduleDependencies)63 void ImportExportDecls::CollectImportedProgramsFromStmts(ark::es2panda::ir::ETSImportDeclaration *stmt,
64                                                          parser::Program *program,
65                                                          GlobalClassHandler::ModuleDependencies *moduleDependencies)
66 {
67     for (auto const &[_, programs] : program->DirectExternalSources()) {
68         (void)_;
69         parser::Program *first = programs.front();
70         if (MatchResolvedPathWithProgram(stmt->ResolvedSource()->Str().Utf8(), first)) {
71             moduleDependencies->insert(first);
72         }
73     }
74 }
75 
HandleGlobalStmts(ArenaVector<parser::Program * > & programs)76 GlobalClassHandler::ModuleDependencies ImportExportDecls::HandleGlobalStmts(ArenaVector<parser::Program *> &programs)
77 {
78     VerifySingleExportDefault(programs);
79     VerifyTypeExports(programs);
80     GlobalClassHandler::ModuleDependencies moduleDependencies {programs.front()->Allocator()->Adapter()};
81     if (!programs.empty()) {
82         std::sort(programs.begin(), programs.end(), ProgramFileNameLessThan);
83     }
84     for (const auto &program : programs) {
85         fieldMap_.clear();
86         exportNameMap_.clear();
87         exportedTypes_.clear();
88         for (auto stmt : program->Ast()->Statements()) {
89             // note (hurton): Current implementation of triggering the imported programs top level statements does
90             // not support type imports and re-exports.
91             if (stmt->IsETSImportDeclaration() && !stmt->AsETSImportDeclaration()->IsTypeKind() &&
92                 !util::Helpers::IsStdLib(program) && !program->IsDeclarationModule()) {
93                 CollectImportedProgramsFromStmts(stmt->AsETSImportDeclaration(), program, &moduleDependencies);
94             }
95             stmt->Accept(this);
96             if (stmt->IsExportNamedDeclaration()) {
97                 PopulateAliasMap(stmt->AsExportNamedDeclaration(), program->SourceFilePath());
98             }
99         }
100         for (auto const &[exportName, startLoc] : exportNameMap_) {
101             const bool isType = exportedTypes_.find(exportName) != exportedTypes_.end();
102             util::StringView originalName = varbinder_->FindNameInAliasMap(program->SourceFilePath(), exportName);
103 
104             ASSERT(!originalName.Empty());
105             auto result = fieldMap_.find(originalName);
106             if (result == fieldMap_.end() && !isType && importedSpecifiersForExportCheck_.count(originalName) == 0) {
107                 util::ErrorHandler::LogSyntaxError(parser_->ErrorLogger(), varbinder_->Program(),
108                                                    "Cannot find name '" + originalName.Mutf8() + "' to export",
109                                                    startLoc);
110             }
111             if (result != fieldMap_.end() && result->second->IsAnnotationDeclaration() && exportName != originalName) {
112                 util::ErrorHandler::LogSyntaxError(parser_->ErrorLogger(), varbinder_->Program(),
113                                                    "Can not rename annotation '" + originalName.Mutf8() +
114                                                        "' in export or import statements.",
115                                                    startLoc);
116             }
117             if (!isType) {
118                 HandleSelectiveExportWithAlias(originalName, exportName, startLoc);
119             }
120         }
121     }
122     return moduleDependencies;
123 }
124 
PopulateAliasMap(const ir::ExportNamedDeclaration * decl,const util::StringView & path)125 void ImportExportDecls::PopulateAliasMap(const ir::ExportNamedDeclaration *decl, const util::StringView &path)
126 {
127     for (auto spec : decl->Specifiers()) {
128         if (!varbinder_->AddSelectiveExportAlias(path, spec->Local()->Name(), spec->Exported()->Name())) {
129             util::ErrorHandler::LogSyntaxError(parser_->ErrorLogger(), varbinder_->Program(),
130                                                "The given name '" + spec->Local()->Name().Mutf8() +
131                                                    "' is already used in another export",
132                                                spec->Start());
133         }
134     }
135 }
136 
HandleSelectiveExportWithAlias(util::StringView originalFieldName,util::StringView exportName,lexer::SourcePosition startLoc)137 void ImportExportDecls::HandleSelectiveExportWithAlias(util::StringView originalFieldName, util::StringView exportName,
138                                                        lexer::SourcePosition startLoc)
139 {
140     auto fieldItem = fieldMap_.find(originalFieldName);
141     if (fieldItem != fieldMap_.end()) {
142         ir::AstNode *field = fieldItem->second;
143         if ((field->Modifiers() & ir::ModifierFlags::EXPORTED) != 0) {
144             // Note (oeotvos) Needs to be discussed, whether we would like to allow exporting the same program
145             // element using its original name and also an alias, like: export {test_func, test_func as foo}.
146             util::ErrorHandler::LogSyntaxError(
147                 parser_->ErrorLogger(), varbinder_->Program(),
148                 "Cannot export '" + originalFieldName.Mutf8() + "', it was already exported", startLoc);
149         }
150         field->AddModifier(ir::ModifierFlags::EXPORT);
151     }
152 
153     if (exportName != originalFieldName) {
154         if (auto declItem = fieldMap_.find(exportName); declItem != fieldMap_.end()) {
155             // Checking for the alias might be unnecessary, because explicit exports cannot
156             // have an alias yet.
157             if (((declItem->second->Modifiers() & ir::ModifierFlags::EXPORTED) != 0) &&
158                 !declItem->second->HasExportAlias()) {
159                 util::ErrorHandler::LogSyntaxError(
160                     parser_->ErrorLogger(), varbinder_->Program(),
161                     "The given name '" + exportName.Mutf8() + "' is already used in another export", startLoc);
162             }
163         }
164 
165         if (fieldItem != fieldMap_.end()) {
166             fieldItem->second->AddAstNodeFlags(ir::AstNodeFlags::HAS_EXPORT_ALIAS);
167         }
168     }
169 }
170 
VisitFunctionDeclaration(ir::FunctionDeclaration * funcDecl)171 void ImportExportDecls::VisitFunctionDeclaration(ir::FunctionDeclaration *funcDecl)
172 {
173     fieldMap_.emplace(funcDecl->Function()->Id()->Name(), funcDecl->Function());
174 }
175 
VisitVariableDeclaration(ir::VariableDeclaration * varDecl)176 void ImportExportDecls::VisitVariableDeclaration(ir::VariableDeclaration *varDecl)
177 {
178     for (const auto &decl : varDecl->Declarators()) {
179         fieldMap_.emplace(decl->Id()->AsIdentifier()->Name(), varDecl);
180     }
181 }
182 
VisitClassDeclaration(ir::ClassDeclaration * classDecl)183 void ImportExportDecls::VisitClassDeclaration(ir::ClassDeclaration *classDecl)
184 {
185     fieldMap_.emplace(classDecl->Definition()->Ident()->Name(), classDecl);
186 }
187 
VisitTSTypeAliasDeclaration(ir::TSTypeAliasDeclaration * typeAliasDecl)188 void ImportExportDecls::VisitTSTypeAliasDeclaration(ir::TSTypeAliasDeclaration *typeAliasDecl)
189 {
190     fieldMap_.emplace(typeAliasDecl->Id()->Name(), typeAliasDecl);
191 }
192 
VisitTSInterfaceDeclaration(ir::TSInterfaceDeclaration * interfaceDecl)193 void ImportExportDecls::VisitTSInterfaceDeclaration(ir::TSInterfaceDeclaration *interfaceDecl)
194 {
195     fieldMap_.emplace(interfaceDecl->Id()->Name(), interfaceDecl);
196 }
197 
VisitAnnotationDeclaration(ir::AnnotationDeclaration * annotationDecl)198 void ImportExportDecls::VisitAnnotationDeclaration(ir::AnnotationDeclaration *annotationDecl)
199 {
200     fieldMap_.emplace(annotationDecl->GetBaseName()->Name(), annotationDecl);
201 }
202 
VisitExportNamedDeclaration(ir::ExportNamedDeclaration * exportDecl)203 void ImportExportDecls::VisitExportNamedDeclaration(ir::ExportNamedDeclaration *exportDecl)
204 {
205     for (auto spec : exportDecl->Specifiers()) {
206         auto local = spec->Local();
207         if (exportDecl->IsExportedType()) {
208             exportedTypes_.insert(local->Name());
209         }
210         if (!exportNameMap_.emplace(local->Name(), local->Start()).second) {
211             util::ErrorHandler::LogSyntaxError(
212                 parser_->ErrorLogger(), varbinder_->Program(),
213                 "The given name '" + local->Name().Mutf8() + "' is already used in another export", local->Start());
214         }
215     }
216 }
217 
VisitETSImportDeclaration(ir::ETSImportDeclaration * importDecl)218 void ImportExportDecls::VisitETSImportDeclaration(ir::ETSImportDeclaration *importDecl)
219 {
220     for (ir::AstNode *spec : importDecl->AsETSImportDeclaration()->Specifiers()) {
221         if (spec->IsImportSpecifier()) {
222             importedSpecifiersForExportCheck_.emplace(spec->AsImportSpecifier()->Imported()->Name());
223         }
224     }
225 }
226 
HandleSimpleType(std::set<util::StringView> & exportedTypes,std::set<util::StringView> & exportedStatements,ir::Statement * stmt,util::StringView name,parser::Program * program,lexer::SourcePosition pos)227 void ImportExportDecls::HandleSimpleType(std::set<util::StringView> &exportedTypes,
228                                          std::set<util::StringView> &exportedStatements, ir::Statement *stmt,
229                                          util::StringView name, parser::Program *program, lexer::SourcePosition pos)
230 {
231     if (stmt->IsExported()) {
232         exportedStatements.insert(name);
233     }
234 
235     if (!stmt->IsExportedType()) {
236         return;
237     }
238 
239     if (exportedStatements.find(name) != exportedStatements.end()) {
240         util::ErrorHandler::LogSyntaxError(
241             parser_->ErrorLogger(), program,
242             "Name '" + name.Mutf8() + "' cannot be exported and type exported at the same time.", pos);
243     }
244 
245     if (exportedTypes.find(name) != exportedTypes.end()) {
246         util::ErrorHandler::LogSyntaxError(parser_->ErrorLogger(), program,
247                                            "Cannot export the same '" + name.Mutf8() + "' type twice.", pos);
248     } else {
249         exportedTypes.insert(name);
250     }
251 }
252 
VerifyTypeExports(const ArenaVector<parser::Program * > & programs)253 void ImportExportDecls::VerifyTypeExports(const ArenaVector<parser::Program *> &programs)
254 {
255     std::set<util::StringView> exportedTypes;
256     std::set<util::StringView> exportedStatements;
257     std::map<util::StringView, ir::AstNode *> typesMap;
258 
259     for (const auto &program : programs) {
260         for (auto stmt : program->Ast()->Statements()) {
261             VerifyType(stmt, program, exportedTypes, exportedStatements, typesMap);
262         }
263     }
264 }
265 
VerifyType(ir::Statement * stmt,parser::Program * program,std::set<util::StringView> & exportedTypes,std::set<util::StringView> & exportedStatements,std::map<util::StringView,ir::AstNode * > & typesMap)266 void ImportExportDecls::VerifyType(ir::Statement *stmt, parser::Program *program,
267                                    std::set<util::StringView> &exportedTypes,
268                                    std::set<util::StringView> &exportedStatements,
269                                    std::map<util::StringView, ir::AstNode *> &typesMap)
270 {
271     if (stmt->IsClassDeclaration()) {
272         typesMap.insert({stmt->AsClassDeclaration()->Definition()->Ident()->Name(), stmt});
273         return HandleSimpleType(exportedTypes, exportedStatements, stmt,
274                                 stmt->AsClassDeclaration()->Definition()->Ident()->Name(), program, stmt->Start());
275     }
276 
277     if (stmt->IsTSInterfaceDeclaration()) {
278         typesMap.insert({stmt->AsTSInterfaceDeclaration()->Id()->Name(), stmt});
279         return HandleSimpleType(exportedTypes, exportedStatements, stmt, stmt->AsTSInterfaceDeclaration()->Id()->Name(),
280                                 program, stmt->Start());
281     }
282 
283     if (stmt->IsTSTypeAliasDeclaration()) {
284         typesMap.insert({stmt->AsTSTypeAliasDeclaration()->Id()->Name(), stmt});
285         return HandleSimpleType(exportedTypes, exportedStatements, stmt, stmt->AsTSTypeAliasDeclaration()->Id()->Name(),
286                                 program, stmt->Start());
287     }
288 
289     if (!stmt->IsExportedType()) {
290         return;
291     }
292 
293     if (!stmt->IsExportNamedDeclaration()) {
294         util::ErrorHandler::LogSyntaxError(parser_->ErrorLogger(), program, "Can only type export class or interface!",
295                                            stmt->Start());
296     }
297 
298     for (auto spec : stmt->AsExportNamedDeclaration()->Specifiers()) {
299         util::StringView name = spec->Local()->Name();
300         util::StringView nameFind = spec->Exported()->Name();
301 
302         auto element = typesMap.find(nameFind);
303         if (element == typesMap.end()) {
304             util::ErrorHandler::LogSyntaxError(parser_->ErrorLogger(), program,
305                                                "Can only type export class or interface!", spec->Local()->Start());
306             continue;
307         }
308         if (!element->second->IsExportedType()) {
309             element->second->AddModifier(ir::ModifierFlags::EXPORT_TYPE);
310         }
311         HandleSimpleType(exportedTypes, exportedStatements, stmt, name, program, spec->Local()->Start());
312         if (!name.Is(nameFind.Mutf8())) {
313             element->second->AddAstNodeFlags(ir::AstNodeFlags::HAS_EXPORT_ALIAS);
314             HandleSimpleType(exportedTypes, exportedStatements, stmt, nameFind, program, spec->Local()->Start());
315         }
316     }
317 }
318 
VerifySingleExportDefault(const ArenaVector<parser::Program * > & programs)319 void ImportExportDecls::VerifySingleExportDefault(const ArenaVector<parser::Program *> &programs)
320 {
321     bool metDefaultExport = false;
322     auto logger = parser_->ErrorLogger();
323     auto verifyDefault = [&metDefaultExport, logger](ir::Statement *stmt, parser::Program *program) {
324         if ((stmt->Modifiers() & ir::ModifierFlags::DEFAULT_EXPORT) == 0) {
325             return;
326         }
327         if (metDefaultExport) {
328             util::ErrorHandler::LogSyntaxError(logger, program, "Only one default export is allowed in a module",
329                                                stmt->Start());
330         }
331         metDefaultExport = true;
332     };
333     for (const auto &program : programs) {
334         for (auto stmt : program->Ast()->Statements()) {
335             verifyDefault(stmt, program);
336         }
337         metDefaultExport = false;
338     }
339 }
340 
341 }  // namespace ark::es2panda::compiler
342