• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-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 "ETSparser.h"
17 #include "lexer/lexer.h"
18 #include "lexer/keywordsUtil.h"
19 #include "ir/astNode.h"
20 #include "ir/base/methodDefinition.h"
21 #include "ir/ets/etsStructDeclaration.h"
22 #include "ir/statements/classDeclaration.h"
23 #include <ir/base/classProperty.h>
24 #include <ir/expressions/arrowFunctionExpression.h>
25 #include <ir/ets/etsModule.h>
26 #include <ir/statements/functionDeclaration.h>
27 #include <ir/statements/variableDeclaration.h>
28 #include <ir/ts/tsInterfaceDeclaration.h>
29 #include <ir/ts/tsTypeAliasDeclaration.h>
30 
31 namespace ark::es2panda::parser {
ExcludeInvalidStart()32 void ETSParser::ExcludeInvalidStart()
33 {
34     auto cp = Lexer()->Lookahead();
35     while (!lexer::KeywordsUtil::IsIdentifierStart(cp) && cp != util::StringView::Iterator::INVALID_CP &&
36            cp != lexer::LEX_CHAR_LEFT_BRACE && cp != lexer::LEX_CHAR_ASTERISK) {
37         Lexer()->ForwardToken(Lexer()->GetToken().Type());
38         cp = Lexer()->Lookahead();
39     }
40 }
41 
ParseJsDocInfoItemParam()42 std::string ETSParser::ParseJsDocInfoItemParam()
43 {
44     ExcludeInvalidStart();
45     Lexer()->NextToken();
46     auto token = Lexer()->GetToken();
47     std::string jsDocInfoParamStr {};
48     bool needAppendToken = token.Type() != lexer::TokenType::PUNCTUATOR_LEFT_BRACE;
49     bool needBackwardBlank = lexer::KeywordsUtil::IsIdentifierStart(Lexer()->Lookahead());
50     size_t leftBraceCount = 1;
51     while (token.Type() != lexer::TokenType::EOS && token.Type() != lexer::TokenType::JS_DOC_END) {
52         if (needAppendToken) {
53             jsDocInfoParamStr += token.ToString();
54         }
55 
56         if (needBackwardBlank) {
57             jsDocInfoParamStr += " ";
58         }
59 
60         auto cp = Lexer()->Lookahead();
61         if (lexer::KeywordsUtil::IsIdentifierStart(cp) || cp == lexer::LEX_CHAR_ASTERISK) {
62             Lexer()->NextToken();
63             token = Lexer()->GetToken();
64             needAppendToken = lexer::KeywordsUtil::IsIdentifierStart(cp);
65             needBackwardBlank = lexer::KeywordsUtil::IsIdentifierStart(Lexer()->Lookahead());
66             continue;
67         }
68 
69         if (cp == lexer::LEX_CHAR_RIGHT_BRACE) {
70             --leftBraceCount;
71             if (leftBraceCount == 0) {
72                 Lexer()->NextToken();
73                 break;
74             }
75         }
76 
77         if (cp == lexer::LEX_CHAR_LEFT_BRACE) {
78             ++leftBraceCount;
79         }
80 
81         if (cp == util::StringView::Iterator::INVALID_CP) {
82             break;
83         }
84 
85         jsDocInfoParamStr += std::string(1, cp);
86         needAppendToken = false;
87         needBackwardBlank = false;
88         Lexer()->ForwardToken(token.Type());
89     }
90 
91     return jsDocInfoParamStr;
92 }
93 
RegularCommentStr(std::string & str)94 static void RegularCommentStr(std::string &str)
95 {
96     if (str.empty()) {
97         return;
98     }
99 
100     auto backChar = static_cast<char32_t>(static_cast<unsigned char>(str.back()));
101     while (backChar == lexer::LEX_CHAR_CR || backChar == lexer::LEX_CHAR_LF || backChar == lexer::LEX_CHAR_ASTERISK ||
102            backChar == lexer::LEX_CHAR_NBSP || backChar == lexer::LEX_CHAR_SP) {
103         str.pop_back();
104         if (str.empty()) {
105             return;
106         }
107         backChar = static_cast<char32_t>(static_cast<unsigned char>(str.back()));
108     }
109 }
110 
ParseJsDocInfoItemValue()111 std::tuple<util::StringView, util::StringView> ETSParser::ParseJsDocInfoItemValue()
112 {
113     ExcludeInvalidStart();
114     util::UString jsDocInfoItemCommentStr(Allocator());
115     std::string jsDocInfoParamStr {};
116     if (Lexer()->GetToken().Type() == lexer::TokenType::JS_DOC_END) {
117         util::UString jsDocInfoItemParam {jsDocInfoParamStr, Allocator()};
118         return std::make_tuple(jsDocInfoItemParam.View(), jsDocInfoItemCommentStr.View());
119     }
120 
121     const auto startIdx = Lexer()->GetIndex();
122     auto escapeEnd = startIdx;
123     do {
124         const char32_t cp = Lexer()->Lookahead();
125         switch (cp) {
126             case lexer::LEX_CHAR_LEFT_BRACE: {
127                 jsDocInfoParamStr = ParseJsDocInfoItemParam();
128                 continue;
129             }
130             case util::StringView::Iterator::INVALID_CP: {
131                 break;
132             }
133             case lexer::LEX_CHAR_CR:
134             case lexer::LEX_CHAR_LF: {
135                 Lexer()->HandleNewlineHelper(&jsDocInfoItemCommentStr, &escapeEnd);
136                 continue;
137             }
138             case lexer::LEX_CHAR_AT:
139             case lexer::LEX_CHAR_ASTERISK: {
140                 auto saved = Lexer()->Save();
141                 Lexer()->NextToken();
142                 auto nextType = Lexer()->GetToken().Type();
143                 Lexer()->Rewind(saved);
144                 if (nextType == lexer::TokenType::JS_DOC_END || nextType == lexer::TokenType::PUNCTUATOR_AT) {
145                     break;
146                 }
147                 [[fallthrough]];
148             }
149             default: {
150                 Lexer()->SkipCp();
151                 continue;
152             }
153         }
154         Lexer()->FinalizeJsDocInfoHelper(&jsDocInfoItemCommentStr, startIdx, escapeEnd);
155         break;
156     } while (true);
157 
158     std::string commentStr = std::string(jsDocInfoItemCommentStr.View());
159     RegularCommentStr(commentStr);
160     util::UString jsDocInfoItemParam {jsDocInfoParamStr, Allocator()};
161     util::UString jsDocInfoCommentStr {commentStr, Allocator()};
162     return std::make_tuple(jsDocInfoItemParam.View(), jsDocInfoCommentStr.View());
163 }
164 
ParseJsDocInfo()165 ir::JsDocInfo ETSParser::ParseJsDocInfo()
166 {
167     ir::JsDocInfo jsDocInfo(Allocator()->Adapter());
168     auto tokenType = Lexer()->GetToken().Type();
169     while (tokenType != lexer::TokenType::EOS && tokenType != lexer::TokenType::JS_DOC_END) {
170         if (tokenType != lexer::TokenType::PUNCTUATOR_AT) {
171             auto cp = Lexer()->Lookahead();
172             if (cp == lexer::LEX_CHAR_ASTERISK || cp == lexer::LEX_CHAR_AT) {
173                 Lexer()->NextToken();
174                 tokenType = Lexer()->GetToken().Type();
175                 continue;
176             }
177 
178             if (Lexer()->Lookahead() == util::StringView::Iterator::INVALID_CP) {
179                 break;
180             }
181 
182             Lexer()->ForwardToken(tokenType, 1);
183             continue;
184         }
185         Lexer()->NextToken();
186         auto const &token = Lexer()->GetToken();
187         util::UString jsDocInfoItemKey {token.Ident(), Allocator()};
188         auto [jsDocInfoItemParam, jsDocInfoItemComment] = ParseJsDocInfoItemValue();
189         jsDocInfo.emplace(jsDocInfoItemKey.View(),
190                           ir::JsDocRecord(jsDocInfoItemKey.View(), jsDocInfoItemParam, jsDocInfoItemComment));
191         tokenType = Lexer()->GetToken().Type();
192     }
193 
194     Lexer()->NextToken();
195     return jsDocInfo;
196 }
197 
ParseJsDocInfos()198 ArenaVector<ir::JsDocInfo> ETSParser::ParseJsDocInfos()
199 {
200     ArenaVector<ir::JsDocInfo> result(Allocator()->Adapter());
201     bool hasMoreJsDocInfos = true;
202     while (hasMoreJsDocInfos) {
203         auto jsDocInfo = ParseJsDocInfo();
204         if (!jsDocInfo.empty()) {
205             result.emplace_back(jsDocInfo);
206         }
207 
208         if (Lexer()->GetToken().Type() != lexer::TokenType::JS_DOC_START) {
209             hasMoreJsDocInfos = false;
210         }
211     }
212     return result;
213 }
214 
ApplyJsDocInfoToNamespace(ir::ETSModule * ns,ArenaVector<ir::JsDocInfo> & jsDocInformation)215 static bool ApplyJsDocInfoToNamespace(ir::ETSModule *ns, ArenaVector<ir::JsDocInfo> &jsDocInformation)
216 {
217     if (ns->IsNamespaceChainLastNode()) {
218         ns->SetJsDocInformation(std::move(jsDocInformation));
219         return true;
220     }
221 
222     for (auto *node : ns->Statements()) {
223         if (node->IsETSModule()) {
224             if (ApplyJsDocInfoToNamespace(node->AsETSModule(), jsDocInformation)) {
225                 return true;
226             }
227         }
228     }
229     return false;
230 }
231 
232 // CC-OFFNXT(huge_method,huge_cyclomatic_complexity,G.FUN.01-CPP) big switch-case, solid logic
ApplyJsDocInfoToSpecificNodeType(ir::AstNode * node,ArenaVector<ir::JsDocInfo> && jsDocInformation)233 void ETSParser::ApplyJsDocInfoToSpecificNodeType(ir::AstNode *node, ArenaVector<ir::JsDocInfo> &&jsDocInformation)
234 {
235     if (jsDocInformation.empty()) {
236         return;
237     }
238 
239     switch (node->Type()) {
240         case ir::AstNodeType::METHOD_DEFINITION: {
241             auto *func = node->AsMethodDefinition()->Function();
242             ES2PANDA_ASSERT(func != nullptr);
243             func->SetJsDocInformation(std::move(jsDocInformation));
244             break;
245         }
246         case ir::AstNodeType::CLASS_DECLARATION:
247             node->AsClassDeclaration()->Definition()->SetJsDocInformation(std::move(jsDocInformation));
248             break;
249         case ir::AstNodeType::STRUCT_DECLARATION:
250             node->AsETSStructDeclaration()->Definition()->SetJsDocInformation(std::move(jsDocInformation));
251             break;
252         case ir::AstNodeType::FUNCTION_DECLARATION:
253             node->AsFunctionDeclaration()->SetJsDocInformation(std::move(jsDocInformation));
254             break;
255         case ir::AstNodeType::TS_INTERFACE_DECLARATION:
256             node->AsTSInterfaceDeclaration()->SetJsDocInformation(std::move(jsDocInformation));
257             break;
258         case ir::AstNodeType::CLASS_PROPERTY:
259             node->AsClassProperty()->SetJsDocInformation(std::move(jsDocInformation));
260             break;
261         case ir::AstNodeType::VARIABLE_DECLARATION:
262             node->AsVariableDeclaration()->SetJsDocInformation(std::move(jsDocInformation));
263             break;
264         case ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION:
265             node->AsTSTypeAliasDeclaration()->SetJsDocInformation(std::move(jsDocInformation));
266             break;
267         case ir::AstNodeType::ARROW_FUNCTION_EXPRESSION:
268             node->AsArrowFunctionExpression()->SetJsDocInformation(std::move(jsDocInformation));
269             break;
270         case ir::AstNodeType::ETS_MODULE:
271             ApplyJsDocInfoToNamespace(node->AsETSModule(), jsDocInformation);
272             break;
273         default: {
274         }
275     }
276 }
277 }  // namespace ark::es2panda::parser
278