diff --git a/ets2panda/ir/astNode.cpp b/ets2panda/ir/astNode.cpp index 811496115396c1ea9419798713265ad6ff4134b0..a0412788f2b251bf4dfcc4fe4502357b510fef5e 100644 --- a/ets2panda/ir/astNode.cpp +++ b/ets2panda/ir/astNode.cpp @@ -145,6 +145,12 @@ std::string AstNode::DumpEtsSrc() const return dumper.Str(); } +std::string AstNode::DumpEtsSrcWithJsdoc() const +{ + ir::SrcDumper dumper {this, false, true}; + return dumper.Str(); +} + std::string AstNode::DumpDecl() const { ir::SrcDumper dumper {this, true}; diff --git a/ets2panda/ir/astNode.h b/ets2panda/ir/astNode.h index f6bbfea98758a81a1104bd283735a4943a17bcd0..f0ec8b2fce1c6fb17b7874d090ff5a42f164ff26 100644 --- a/ets2panda/ir/astNode.h +++ b/ets2panda/ir/astNode.h @@ -668,6 +668,7 @@ public: std::string DumpJSON() const; std::string DumpEtsSrc() const; + std::string DumpEtsSrcWithJsdoc() const; std::string DumpDecl() const; virtual void Dump(ir::AstDumper *dumper) const = 0; diff --git a/ets2panda/ir/base/classDefinition.cpp b/ets2panda/ir/base/classDefinition.cpp index 0c02f92ba392ebbec02db89591da9beaad8208a4..da0ec57e8dd10572374f03f4cb791dc613f950b8 100644 --- a/ets2panda/ir/base/classDefinition.cpp +++ b/ets2panda/ir/base/classDefinition.cpp @@ -439,6 +439,8 @@ void ClassDefinition::Dump(ir::SrcDumper *dumper) const ES2PANDA_ASSERT(ident_ != nullptr); + dumper->DumpJsdocBeforeTargetNode(this); + if (RegisterUnexportedForDeclGen(dumper)) { return; } diff --git a/ets2panda/ir/base/classProperty.cpp b/ets2panda/ir/base/classProperty.cpp index a292d8c689d95475c44a22be23d5d585f96e795c..5fe3a89ef70e95a8714a16f245298966b14a4845 100644 --- a/ets2panda/ir/base/classProperty.cpp +++ b/ets2panda/ir/base/classProperty.cpp @@ -209,6 +209,7 @@ void ClassProperty::Dump(ir::SrcDumper *dumper) const if (RegisterUnexportedForDeclGen(dumper)) { return; } + dumper->DumpJsdocBeforeTargetNode(this); DumpPrefix(dumper); if (Key() != nullptr) { diff --git a/ets2panda/ir/base/methodDefinition.cpp b/ets2panda/ir/base/methodDefinition.cpp index 276232f512a2929f7c6c0fe1512fd4456e1dee46..5843b4eb59bab5b1b176d2bc4f5d2033a73e1836 100644 --- a/ets2panda/ir/base/methodDefinition.cpp +++ b/ets2panda/ir/base/methodDefinition.cpp @@ -297,6 +297,8 @@ void MethodDefinition::Dump(ir::SrcDumper *dumper) const return; } + dumper->DumpJsdocBeforeTargetNode(this); + if (compiler::HasGlobalClassParent(this) && Id() != nullptr && Id()->Name().Is(compiler::Signatures::INIT_METHOD)) { Function()->Body()->Dump(dumper); return; diff --git a/ets2panda/ir/module/exportNamedDeclaration.cpp b/ets2panda/ir/module/exportNamedDeclaration.cpp index e351e25179d431290a36ab8f13c8b4fe39f05faa..9f1c249cc1942f260a3106ee32b5589cbd586683 100644 --- a/ets2panda/ir/module/exportNamedDeclaration.cpp +++ b/ets2panda/ir/module/exportNamedDeclaration.cpp @@ -71,6 +71,17 @@ void ExportNamedDeclaration::Dump(ir::AstDumper *dumper) const void ExportNamedDeclaration::Dump(ir::SrcDumper *dumper) const { + if (specifiers_.size() == 1 && specifiers_[0]->IsDefaultExported()) { + dumper->Add("export default "); + const auto singleSpec = specifiers_[0]; + if (singleSpec->GetConstantExpression() != nullptr) { + singleSpec->GetConstantExpression()->Dump(dumper); + } else { + singleSpec->Exported()->Dump(dumper); + } + return; + } + dumper->Add("export { "); for (const auto *spec : specifiers_) { spec->Dump(dumper); diff --git a/ets2panda/ir/module/exportSpecifier.cpp b/ets2panda/ir/module/exportSpecifier.cpp index 1c28d57fe53a0f57a6606771288018944388a220..d8495523e437048cb455494d7a3c386ea87d0be5 100644 --- a/ets2panda/ir/module/exportSpecifier.cpp +++ b/ets2panda/ir/module/exportSpecifier.cpp @@ -57,6 +57,10 @@ void ExportSpecifier::Dump(ir::SrcDumper *dumper) const exported_->Dump(dumper); if (local_ != nullptr) { + if (local_->IsIdentifier() && exported_->IsIdentifier() && + local_->AsIdentifier()->Name() == exported_->AsIdentifier()->Name()) { + return; + } dumper->Add(" as "); local_->Dump(dumper); } diff --git a/ets2panda/ir/srcDump.cpp b/ets2panda/ir/srcDump.cpp index d87e3dd148314dc62b95c3f1a29d533f2b6e49cd..217874d7352ab75e316daca086697a316a472917 100644 --- a/ets2panda/ir/srcDump.cpp +++ b/ets2panda/ir/srcDump.cpp @@ -31,8 +31,12 @@ SrcDumper::SrcDumper(const ir::AstNode *node) node->Dump(this); } -SrcDumper::SrcDumper(const ir::AstNode *node, bool isDeclgen) : isDeclgen_(isDeclgen) +SrcDumper::SrcDumper(const ir::AstNode *node, bool isDeclgen, bool needDumpJsdoc) + : isDeclgen_(isDeclgen) { + if (needDumpJsdoc) { + jsdocGetter = std::make_unique(node); + } node->Dump(this); } @@ -150,6 +154,19 @@ void SrcDumper::DumpVariant(NodeVariant &node) node); } +void SrcDumper::DumpJsdocBeforeTargetNode(const ir::AstNode *inputNode) +{ + if (!NeedDumpJsdoc()) { + return; + } + jsdocGetter->InitNode(inputNode); + auto resJsdoc = jsdocGetter->GetJsdocBackward(); + if (!resJsdoc.Empty()) { + ss_ << resJsdoc.Mutf8(); + ss_ << std::endl; + } +} + template void SrcDumper::DumpNodeIfPointer(T *value) { diff --git a/ets2panda/ir/srcDump.h b/ets2panda/ir/srcDump.h index 59bbf15500fd443e843347fe6960dc6f5916a9c5..56165c2f83f80d2055087b0ef50d63428cb05047 100644 --- a/ets2panda/ir/srcDump.h +++ b/ets2panda/ir/srcDump.h @@ -17,8 +17,7 @@ #define ES2PANDA_IR_SRCDUMP_H #include -#include -#include "generated/tokenType.h" +#include "parser/JsdocHelper.h" #include #include @@ -42,7 +41,7 @@ using NodeVariant = class SrcDumper { public: explicit SrcDumper(const ir::AstNode *node); - explicit SrcDumper(const ir::AstNode *node, bool isDeclgen); + explicit SrcDumper(const ir::AstNode *node, bool isDeclgen, bool needDumpJsdoc = false); void Add(const std::string &str); void Add(int8_t i); @@ -109,6 +108,13 @@ public: return isIndirectDepPhase_; } + bool NeedDumpJsdoc() const + { + return jsdocGetter != nullptr; + } + + void DumpJsdocBeforeTargetNode(const ir::AstNode *inputNode); + template void PushTask(T &&task) { @@ -124,6 +130,7 @@ private: bool isIndirectDepPhase_ = false; bool ambientWasDeclared_ = false; std::unordered_map unExportNode_; + std::unique_ptr jsdocGetter {}; std::queue> taskQueue_; }; diff --git a/ets2panda/ir/ts/tsInterfaceDeclaration.cpp b/ets2panda/ir/ts/tsInterfaceDeclaration.cpp index fdc64a468576f0ed1b90c12f58dc2b70f7247ff3..c75d52e4c608373918d312c5a80581f05f4226c6 100644 --- a/ets2panda/ir/ts/tsInterfaceDeclaration.cpp +++ b/ets2panda/ir/ts/tsInterfaceDeclaration.cpp @@ -176,6 +176,8 @@ bool TSInterfaceDeclaration::RegisterUnexportedForDeclGen(ir::SrcDumper *dumper) void TSInterfaceDeclaration::Dump(ir::SrcDumper *dumper) const { ES2PANDA_ASSERT(id_); + dumper->DumpJsdocBeforeTargetNode(this); + if (!id_->Parent()->IsDefaultExported() && !id_->Parent()->IsExported() && dumper->IsDeclgen() && !dumper->IsIndirectDepPhase()) { auto name = id_->Name().Mutf8(); diff --git a/ets2panda/parser/ETSparser.cpp b/ets2panda/parser/ETSparser.cpp index 0e9894d3ff9049608f84ef1a124be46a353a58fc..1cbb24a5fd98d074b4b8e790ece1eee9d93a5627 100644 --- a/ets2panda/parser/ETSparser.cpp +++ b/ets2panda/parser/ETSparser.cpp @@ -1443,6 +1443,9 @@ ir::ExportNamedDeclaration *ETSParser::ParseSingleExport(ir::ModifierFlags modif exports.emplace_back(AllocNode(exported, ParseNamedExport(&token))); exports.back()->SetRange(exported->Range()); + if ((modifiers & ir::ModifierFlags::DEFAULT_EXPORT) != 0) { + exports[0]->AddModifier(ir::ModifierFlags::DEFAULT_EXPORT); + } auto result = AllocNode(Allocator(), static_cast(nullptr), std::move(exports)); result->SetRange(exported->Range()); diff --git a/ets2panda/parser/JsdocHelper.cpp b/ets2panda/parser/JsdocHelper.cpp index f3e75ea9a63636b99be1890c64367b0846e9a36b..ce97fa52d625da01408627387677594b9d96c0c5 100644 --- a/ets2panda/parser/JsdocHelper.cpp +++ b/ets2panda/parser/JsdocHelper.cpp @@ -21,8 +21,6 @@ namespace ark::es2panda::parser { static constexpr std::string_view JSDOC_END = "*/"; -static constexpr std::string_view EMPTY_JSDOC = "Empty Jsdoc"; - static constexpr size_t START_POS = 0; static constexpr size_t COLLECT_CURRENT_POS = 1; @@ -108,6 +106,23 @@ static void HandlePotentialPrefixOrAnnotationUsage(parser::JsdocHelper *jsdocHel } } +void JsdocHelper::InitNode(const ir::AstNode *input) +{ + auto root = input; + while (root->Parent() != nullptr) { + root = root->Parent(); + } + root_ = root; + program_ = root_->AsETSModule()->Program(); + sourceCode_ = program_->SourceCode(); + iter_ = util::StringView::Iterator(sourceCode_); + if (input->IsClassDefinition()) { + node_ = input->Parent(); + } else { + node_ = input; + } +} + bool JsdocHelper::BackWardUntilJsdocStart() { while (true) { @@ -162,7 +177,7 @@ util::StringView JsdocHelper::GetJsdocBackward() } if (backwardPos == jsdocEndPos) { - return EMPTY_JSDOC; + return ""; } return SourceView(backwardPos, jsdocEndPos); } diff --git a/ets2panda/parser/JsdocHelper.h b/ets2panda/parser/JsdocHelper.h index 97418e245eb03e837c15d5171aaec1cdc1aa1571..c7c3e0daa14604d083cfb3ff7e23ce0ad0b67b7b 100644 --- a/ets2panda/parser/JsdocHelper.h +++ b/ets2panda/parser/JsdocHelper.h @@ -19,9 +19,6 @@ #include "parserImpl.h" #include "parser/program/program.h" #include "lexer/token/letters.h" -#include "ir/astNode.h" -#include "ir/base/classDefinition.h" -#include "ir/ets/etsModule.h" namespace ark::es2panda::parser { using UStringView = util::StringView; @@ -29,14 +26,6 @@ class JsdocHelper { public: explicit JsdocHelper(const ir::AstNode *inputNode) { - auto root = inputNode; - while (root->Parent() != nullptr) { - root = root->Parent(); - } - root_ = root; - program_ = root_->AsETSModule()->Program(); - sourceCode_ = program_->SourceCode(); - iter_ = util::StringView::Iterator(sourceCode_); InitNode(inputNode); } @@ -84,16 +73,9 @@ public: return iter_.Peek(); } -private: - void InitNode(const ir::AstNode *input) - { - if (input->IsClassDefinition()) { - node_ = input->Parent(); - } else { - node_ = input; - } - } + void InitNode(const ir::AstNode *input); +private: bool SkipWhiteSpacesBackwardHelper(const char32_t &cp) { if (cp < lexer::LEX_ASCII_MAX_BITS) { diff --git a/ets2panda/test/unit/plugin/CMakeLists.txt b/ets2panda/test/unit/plugin/CMakeLists.txt index 7ba38739e11bf296e60f41d912be2e171d20901b..28a456016d631c8ccd84eedf50f64c46f1fd19d6 100644 --- a/ets2panda/test/unit/plugin/CMakeLists.txt +++ b/ets2panda/test/unit/plugin/CMakeLists.txt @@ -110,6 +110,7 @@ set(PLUGIN_TESTS "plugin_proceed_to_state_log_diagnostic_with_suggestion compile.ets ${COMPILE_MODE} cpp ${EXECUTABLE_PLUGIN}" "use_plugin_to_test_column_number compile.ets ${COMPILE_MODE} cpp ${EXECUTABLE_PLUGIN}" "plugin_proceed_to_state_check_jsdoc compile.ets ${EXPECTED_MODE} cpp ${EXECUTABLE_PLUGIN}" + "plugin_proceed_to_state_dump_src_with_jsdoc_after_parse compile.ets ${COMPILE_MODE} cpp ${EXECUTABLE_PLUGIN}" "plugin_proceed_to_state_parse_and_verifier_position compile.ets ${COMPILE_MODE} cpp ${EXECUTABLE_PLUGIN}" "plugin_proceed_to_state_check_recheck_opaque_type_node compile.ets ${COMPILE_MODE} cpp ${EXECUTABLE_PLUGIN}" "plugin_proceed_to_state_check_recheck_create_opaque_type compile.ets ${COMPILE_MODE} cpp ${EXECUTABLE_PLUGIN}" diff --git a/ets2panda/test/unit/plugin/plugin_proceed_to_state_dump_src_with_jsdoc_after_parse.cpp b/ets2panda/test/unit/plugin/plugin_proceed_to_state_dump_src_with_jsdoc_after_parse.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c838bb60b6bc7987ef4cd6d0ab1db6fd6546dfe1 --- /dev/null +++ b/ets2panda/test/unit/plugin/plugin_proceed_to_state_dump_src_with_jsdoc_after_parse.cpp @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "os/library_loader.h" + +#include "public/es2panda_lib.h" +#include "util.h" + +// NOLINTBEGIN + +static es2panda_Impl *impl = nullptr; + +static std::string g_source = R"( +export default class A {} +class B {} +class C {} +export enum EM { + PROP1 = 1, + PROP2 = 2, +} +//export default A; +export { B, C }; +)"; + +int main(int argc, char **argv) +{ + if (argc < MIN_ARGC) { + return INVALID_ARGC_ERROR_CODE; + } + + impl = GetImpl(); + if (impl == nullptr) { + return NULLPTR_IMPL_ERROR_CODE; + } + + const char **args = const_cast(&(argv[1])); + auto config = impl->CreateConfig(argc - 1, args); + auto context = impl->CreateContextFromString(config, g_source.data(), argv[argc - 1]); + impl->ProceedToState(context, ES2PANDA_STATE_PARSED); + auto *program = impl->ContextProgram(context); + auto *entryAst = impl->ProgramAst(context, program); + std::cout << impl->AstNodeDumpEtsSrcWithJsdocConst(context, entryAst) << std::endl; + return 0; +} + +// NOLINTEND \ No newline at end of file