1 /**
2 * Copyright (c) 2021-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 #ifndef ES2PANDA_UTIL_OPTIONS_H
17 #define ES2PANDA_UTIL_OPTIONS_H
18
19 #include "libpandabase/os/file.h"
20 #include "es2panda.h"
21 #include "util/helpers.h"
22 #include "utils/pandargs.h"
23 #include "arktsconfig.h"
24
25 #include <exception>
26 #include <fstream>
27 #include <iostream>
28 #include <variant>
29
30 namespace ark {
31 class PandArgParser;
32 class PandaArg;
33 } // namespace ark
34
35 namespace ark::es2panda::util {
36 enum class OptionFlags : uint32_t {
37 DEFAULT = 0U,
38 PARSE_ONLY = 1U << 0U,
39 PARSE_MODULE = 1U << 1U,
40 SIZE_STAT = 1U << 2U,
41 };
42
43 constexpr int MAX_OPT_LEVEL = 2;
44
45 inline std::underlying_type_t<OptionFlags> operator&(OptionFlags a, OptionFlags b)
46 {
47 using Utype = std::underlying_type_t<OptionFlags>;
48 /* NOLINTNEXTLINE(hicpp-signed-bitwise) */
49 return static_cast<Utype>(static_cast<Utype>(a) & static_cast<Utype>(b));
50 }
51
52 inline OptionFlags &operator|=(OptionFlags &a, OptionFlags b)
53 {
54 using Utype = std::underlying_type_t<OptionFlags>;
55 /* NOLINTNEXTLINE(hicpp-signed-bitwise) */
56 return a = static_cast<OptionFlags>(static_cast<Utype>(a) | static_cast<Utype>(b));
57 }
58
59 template <class T>
BaseName(T const & path)60 T BaseName(T const &path)
61 {
62 return path.substr(path.find_last_of(ark::os::file::File::GetPathDelim()) + 1);
63 }
64
65 class Options {
66 public:
67 Options();
68 NO_COPY_SEMANTIC(Options);
69 NO_MOVE_SEMANTIC(Options);
70 ~Options();
71
72 bool Parse(int argc, const char **argv);
73
Extension()74 es2panda::ScriptExtension Extension() const
75 {
76 return extension_;
77 }
78
CompilerOptions()79 const es2panda::CompilerOptions &CompilerOptions() const
80 {
81 return compilerOptions_;
82 }
83
SetCompilerOptions(const es2panda::CompilerOptions & options)84 void SetCompilerOptions(const es2panda::CompilerOptions &options)
85 {
86 compilerOptions_ = options;
87 }
88
ParserInput()89 const std::string &ParserInput() const
90 {
91 return parserInput_;
92 }
93
CompilerOutput()94 const std::string &CompilerOutput() const
95 {
96 return compilerOutput_;
97 }
98
SetCompilerOutput(const std::string & compilerOutput)99 void SetCompilerOutput(const std::string &compilerOutput)
100 {
101 compilerOutput_ = compilerOutput;
102 }
103
LogLevel()104 std::string_view LogLevel() const
105 {
106 switch (logLevel_) {
107 case util::LogLevel::DEBUG: {
108 return "debug";
109 }
110 case util::LogLevel::INFO: {
111 return "info";
112 }
113 case util::LogLevel::WARNING: {
114 return "warning";
115 }
116 case util::LogLevel::ERROR: {
117 return "error";
118 }
119 case util::LogLevel::FATAL: {
120 return "fatal";
121 }
122 default: {
123 UNREACHABLE();
124 }
125 }
126 }
127
DetermineLogLevel(const ark::PandArg<std::string> & logLevel)128 void DetermineLogLevel(const ark::PandArg<std::string> &logLevel)
129 {
130 if (const auto logLevelStr = logLevel.GetValue(); !logLevelStr.empty()) {
131 if (logLevelStr == "debug") {
132 logLevel_ = util::LogLevel::DEBUG;
133 } else if (logLevelStr == "info") {
134 logLevel_ = util::LogLevel::INFO;
135 } else if (logLevelStr == "warning") {
136 logLevel_ = util::LogLevel::WARNING;
137 } else if (logLevelStr == "error") {
138 logLevel_ = util::LogLevel::ERROR;
139 } else if (logLevelStr == "fatal") {
140 logLevel_ = util::LogLevel::FATAL;
141 } else {
142 logLevel_ = util::LogLevel::INVALID;
143 std::cerr << "Invalid log level: '" << logLevelStr
144 << R"('. Possible values: ["debug", "info", "warning", "error", "fatal"])";
145 }
146 }
147 }
148
DetermineExtension(const ark::PandArg<std::string> & inputExtension,const ark::PandArg<std::string> & arktsConfig,const es2panda::CompilationMode & compMode)149 void DetermineExtension(const ark::PandArg<std::string> &inputExtension,
150 const ark::PandArg<std::string> &arktsConfig, const es2panda::CompilationMode &compMode)
151 {
152 std::string extension = inputExtension.GetValue();
153 std::string sourceFileExtension = sourceFile_.substr(sourceFile_.find_last_of('.') + 1);
154
155 bool extensionIsEmpty = extension.empty();
156 if (!sourceFile_.empty() && !extensionIsEmpty && extension != sourceFileExtension) {
157 std::cerr << "Warning: Not matching extensions! Sourcefile: " << sourceFileExtension
158 << ", Manual(used): " << extension << std::endl;
159 }
160 auto tempExtension = !extensionIsEmpty ? extension : sourceFileExtension;
161 if (tempExtension == "js") {
162 extension_ = es2panda::ScriptExtension::JS;
163 #ifndef PANDA_WITH_ECMASCRIPT
164 errorMsg_ = "js extension is not supported within current build";
165 extension_ = es2panda::ScriptExtension::INVALID;
166 return;
167 #endif
168 } else if (tempExtension == "ts") {
169 extension_ = es2panda::ScriptExtension::TS;
170 } else if (tempExtension == "as") {
171 extension_ = es2panda::ScriptExtension::AS;
172 } else if (tempExtension == "sts") {
173 extension_ = es2panda::ScriptExtension::ETS;
174
175 std::ifstream inputStream(arktsConfig.GetValue());
176 if (inputStream.fail()) {
177 errorMsg_ = "Failed to open arktsconfig: ";
178 errorMsg_.append(arktsConfig.GetValue());
179 extension_ = es2panda::ScriptExtension::INVALID;
180 return;
181 }
182 } else if (extensionIsEmpty && (compMode == CompilationMode::PROJECT)) {
183 extension_ = es2panda::ScriptExtension::ETS;
184 } else {
185 if (!extensionIsEmpty) {
186 errorMsg_ = "Invalid extension (available options: js, ts, as, sts)";
187 } else {
188 errorMsg_ =
189 "Unknown extension of sourcefile, set the extension manually or change the file format (available "
190 "options: js, ts, as, sts)";
191 }
192 extension_ = es2panda::ScriptExtension::INVALID;
193 return;
194 }
195 }
196
DetermineCompilationMode(const ark::PandArg<bool> & genStdLib,const ark::PandArg<std::string> & inputFile)197 CompilationMode DetermineCompilationMode(const ark::PandArg<bool> &genStdLib,
198 const ark::PandArg<std::string> &inputFile) const
199 {
200 return genStdLib.GetValue() ? CompilationMode::GEN_STD_LIB
201 : inputFile.GetValue().empty() ? CompilationMode::PROJECT
202 : CompilationMode::SINGLE_FILE;
203 }
204
AddOptionFlags(const ark::PandArg<bool> & opParseOnly,const ark::PandArg<bool> & opModule,const ark::PandArg<bool> & opSizeStat)205 void AddOptionFlags(const ark::PandArg<bool> &opParseOnly, const ark::PandArg<bool> &opModule,
206 const ark::PandArg<bool> &opSizeStat)
207 {
208 if (opParseOnly.GetValue()) {
209 options_ |= OptionFlags::PARSE_ONLY;
210 }
211
212 if (opModule.GetValue()) {
213 options_ |= OptionFlags::PARSE_MODULE;
214 }
215
216 if (opSizeStat.GetValue()) {
217 options_ |= OptionFlags::SIZE_STAT;
218 }
219 }
220
ParseArktsConfig(std::string_view path)221 std::optional<ArkTsConfig> ParseArktsConfig(std::string_view path)
222 {
223 auto config = ArkTsConfig {path};
224 if (!config.Parse()) {
225 errorMsg_ = "Invalid ArkTsConfig path: ";
226 errorMsg_.append(path);
227 return std::nullopt;
228 }
229 return std::make_optional(config);
230 }
231
ProcessEtsSpecificOptions(std::string_view arktsConfigPath,const es2panda::CompilationMode & compMode)232 bool ProcessEtsSpecificOptions(std::string_view arktsConfigPath, const es2panda::CompilationMode &compMode)
233 {
234 if (extension_ != es2panda::ScriptExtension::ETS && compMode == CompilationMode::PROJECT) {
235 errorMsg_ = "Error: only --extension=sts is supported for project compilation mode.";
236 return false;
237 }
238
239 if (extension_ != es2panda::ScriptExtension::ETS) {
240 return true;
241 }
242
243 if (auto config = ParseArktsConfig(arktsConfigPath); config != std::nullopt) {
244 compilerOptions_.arktsConfig = std::make_shared<ArkTsConfig>(*config);
245 } else {
246 return false;
247 }
248
249 return true;
250 }
251
SourceFile()252 const std::string &SourceFile() const
253 {
254 return sourceFile_;
255 }
256
ErrorMsg()257 const std::string &ErrorMsg() const
258 {
259 return errorMsg_;
260 }
261
OptLevel()262 int OptLevel() const
263 {
264 return optLevel_;
265 }
266
ThreadCount()267 int ThreadCount() const
268 {
269 return threadCount_;
270 }
271
ParseModule()272 bool ParseModule() const
273 {
274 return (options_ & OptionFlags::PARSE_MODULE) != 0;
275 }
276
ParseOnly()277 bool ParseOnly() const
278 {
279 return (options_ & OptionFlags::PARSE_ONLY) != 0;
280 }
281
SizeStat()282 bool SizeStat() const
283 {
284 return (options_ & OptionFlags::SIZE_STAT) != 0;
285 }
286
IsDynamic()287 bool IsDynamic() const
288 {
289 return extension_ != es2panda::ScriptExtension::ETS;
290 }
291
ListFiles()292 bool ListFiles() const
293 {
294 return listFiles_;
295 }
296
ListPhases()297 bool ListPhases() const
298 {
299 return listPhases_;
300 }
301
302 private:
303 es2panda::ScriptExtension extension_ {es2panda::ScriptExtension::JS};
304 OptionFlags options_ {OptionFlags::DEFAULT};
305 es2panda::CompilerOptions compilerOptions_ {};
306 ark::PandArgParser *argparser_;
307 std::string parserInput_;
308 std::string compilerOutput_;
309 std::string result_;
310 std::string sourceFile_;
311 std::string errorMsg_;
312 int optLevel_ {0};
313 int threadCount_ {0};
314 bool listFiles_ {false};
315 bool listPhases_ {false};
316 util::LogLevel logLevel_ {util::LogLevel::ERROR};
317 };
318 } // namespace ark::es2panda::util
319
320 #endif // UTIL_OPTIONS_H
321