1 /**
2 * Copyright (c) 2022-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 "options.h"
17
18 #include "utils/pandargs.h"
19
20 #include "arktsconfig.h"
21
22 #include <utility>
23
24 #ifdef PANDA_WITH_BYTECODE_OPTIMIZER
25 #include "bytecode_optimizer/bytecodeopt_options.h"
26 #include "compiler/compiler_options.h"
27 #endif
28
29 namespace panda::es2panda::util {
30 template <class T>
RemoveExtension(T const & filename)31 T RemoveExtension(T const &filename)
32 {
33 typename T::size_type const p(filename.find_last_of('.'));
34 return p > 0 && p != T::npos ? filename.substr(0, p) : filename;
35 }
36
37 // Options
38
Options()39 Options::Options() : argparser_(new panda::PandArgParser()) {}
40
~Options()41 Options::~Options()
42 {
43 delete argparser_;
44 }
45
SplitToStringVector(std::string const & str)46 static std::vector<std::string> SplitToStringVector(std::string const &str)
47 {
48 std::vector<std::string> res;
49 std::string_view currStr {str};
50 auto ix = currStr.find(',');
51 while (ix != std::string::npos) {
52 if (ix != 0) {
53 res.emplace_back(currStr.substr(0, ix));
54 }
55 currStr = currStr.substr(ix + 1);
56 ix = currStr.find(',');
57 }
58
59 if (!currStr.empty()) {
60 res.emplace_back(currStr);
61 }
62 return res;
63 }
64
SplitToStringSet(std::string const & str)65 static std::unordered_set<std::string> SplitToStringSet(std::string const &str)
66 {
67 std::vector<std::string> vec = SplitToStringVector(str);
68 std::unordered_set<std::string> res;
69 for (auto &elem : vec) {
70 res.emplace(elem);
71 }
72 return res;
73 }
74
75 // NOLINTNEXTLINE(modernize-avoid-c-arrays, hicpp-avoid-c-arrays)
SplitArgs(int argc,const char * argv[],std::vector<std::string> & es2pandaArgs,std::vector<std::string> & bcoCompilerArgs,std::vector<std::string> & bytecodeoptArgs)76 static void SplitArgs(int argc, const char *argv[], std::vector<std::string> &es2pandaArgs,
77 std::vector<std::string> &bcoCompilerArgs, std::vector<std::string> &bytecodeoptArgs)
78 {
79 constexpr std::string_view COMPILER_PREFIX = "--bco-compiler";
80 constexpr std::string_view OPTIMIZER_PREFIX = "--bco-optimizer";
81
82 enum class OptState { ES2PANDA, JIT_COMPILER, OPTIMIZER };
83 OptState optState = OptState::ES2PANDA;
84
85 std::unordered_map<OptState, std::vector<std::string> *> argsMap = {{OptState::ES2PANDA, &es2pandaArgs},
86 {OptState::JIT_COMPILER, &bcoCompilerArgs},
87 {OptState::OPTIMIZER, &bytecodeoptArgs}};
88
89 for (int i = 1; i < argc; i++) {
90 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
91 const char *argI = argv[i];
92 if (COMPILER_PREFIX == argI) {
93 optState = OptState::JIT_COMPILER;
94 continue;
95 }
96
97 if (OPTIMIZER_PREFIX == argI) {
98 optState = OptState::OPTIMIZER;
99 continue;
100 }
101
102 argsMap[optState]->emplace_back(argI);
103 optState = OptState::ES2PANDA;
104 }
105 }
106
107 template <class T>
ParseComponentArgs(const std::vector<std::string> & args,T & options)108 static bool ParseComponentArgs(const std::vector<std::string> &args, T &options)
109 {
110 panda::PandArgParser parser;
111 options.AddOptions(&parser);
112 if (!parser.Parse(args)) {
113 std::cerr << parser.GetErrorString();
114 std::cerr << parser.GetHelpString();
115 return false;
116 }
117
118 if (auto optionsErr = options.Validate(); optionsErr) {
119 std::cerr << "Error: " << optionsErr.value().GetMessage() << std::endl;
120 return false;
121 }
122
123 return true;
124 }
125
ParseBCOCompilerOptions(const std::vector<std::string> & compilerArgs,const std::vector<std::string> & bytecodeoptArgs)126 static bool ParseBCOCompilerOptions([[maybe_unused]] const std::vector<std::string> &compilerArgs,
127 [[maybe_unused]] const std::vector<std::string> &bytecodeoptArgs)
128 {
129 #ifdef PANDA_WITH_BYTECODE_OPTIMIZER
130 if (!ParseComponentArgs(compilerArgs, panda::compiler::g_options)) {
131 return false;
132 }
133 if (!ParseComponentArgs(bytecodeoptArgs, panda::bytecodeopt::g_options)) {
134 return false;
135 }
136 #endif
137
138 return true;
139 }
140
141 // NOLINTNEXTLINE(readability-function-size)
Parse(int argc,const char ** argv)142 bool Options::Parse(int argc, const char **argv)
143 {
144 std::vector<std::string> es2pandaArgs;
145 std::vector<std::string> bcoCompilerArgs;
146 std::vector<std::string> bytecodeoptArgs;
147
148 SplitArgs(argc, argv, es2pandaArgs, bcoCompilerArgs, bytecodeoptArgs);
149 if (!ParseBCOCompilerOptions(bcoCompilerArgs, bytecodeoptArgs)) {
150 return false;
151 }
152
153 panda::PandArg<bool> opHelp("help", false, "Print this message and exit");
154
155 // parser
156 panda::PandArg<std::string> inputExtension("extension", "",
157 "Parse the input as the given extension (options: js | ts | as | ets)");
158 panda::PandArg<bool> opModule("module", false, "Parse the input as module (JS only option)");
159 panda::PandArg<bool> opParseOnly("parse-only", false, "Parse the input only");
160 panda::PandArg<bool> opDumpAst("dump-ast", false, "Dump the parsed AST");
161 panda::PandArg<bool> opDumpAstOnlySilent("dump-ast-only-silent", false,
162 "Dump parsed AST with all dumpers available but don't print to stdout");
163 panda::PandArg<bool> opDumpCheckedAst("dump-dynamic-ast", false,
164 "Dump AST with synthetic nodes for dynamic languages");
165 panda::PandArg<bool> opListFiles("list-files", false, "Print names of files that are part of compilation");
166
167 // compiler
168 panda::PandArg<bool> opDumpAssembly("dump-assembly", false, "Dump pandasm");
169 panda::PandArg<bool> opDebugInfo("debug-info", false, "Compile with debug info");
170 panda::PandArg<bool> opDumpDebugInfo("dump-debug-info", false, "Dump debug info");
171 panda::PandArg<int> opOptLevel("opt-level", 0, "Compiler optimization level (options: 0 | 1 | 2)");
172 panda::PandArg<bool> opEtsModule("ets-module", false, "Compile the input as ets-module");
173 panda::PandArg<std::string> opTsDeclOut("gen-ts-decl", "", "For given .ets file, generate .ts interop file");
174
175 auto constexpr DEFAULT_THREAD_COUNT = 0;
176 panda::PandArg<int> opThreadCount("thread", DEFAULT_THREAD_COUNT, "Number of worker threads");
177 panda::PandArg<bool> opSizeStat("dump-size-stat", false, "Dump size statistics");
178 panda::PandArg<std::string> outputFile("output", "", "Compiler binary output (.abc)");
179 panda::PandArg<std::string> logLevel("log-level", "error", "Log-level");
180 panda::PandArg<std::string> stdLib("stdlib", "", "Path to standard library");
181 panda::PandArg<bool> genStdLib("gen-stdlib", false, "Gen standard library");
182 panda::PandArg<std::string> plugins("plugins", "", "Plugins");
183 panda::PandArg<std::string> skipPhases("skip-phases", "", "Phases to skip");
184 panda::PandArg<std::string> verifierWarnings("verifier-warnings", "", "Show warnings form verifier");
185 panda::PandArg<std::string> verifierErrors("verifier-errors", "", "Show warnings form verifier");
186 panda::PandArg<std::string> dumpBeforePhases("dump-before-phases", "",
187 "Generate program dump before running phases in the list");
188 panda::PandArg<std::string> dumpEtsSrcBeforePhases(
189 "dump-ets-src-before-phases", "", "Generate program dump as ets source code before running phases in the list");
190 panda::PandArg<std::string> dumpEtsSrcAfterPhases(
191 "dump-ets-src-after-phases", "", "Generate program dump as ets source code after running phases in the list");
192 panda::PandArg<std::string> dumpAfterPhases("dump-after-phases", "",
193 "Generate program dump after running phases in the list");
194 panda::PandArg<std::string> arktsConfig(
195 "arktsconfig",
196 panda::es2panda::JoinPaths(
197 panda::es2panda::ParentPath(argv[0]), // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
198 "arktsconfig.json"),
199 "Path to arkts configuration file");
200
201 // tail arguments
202 panda::PandArg<std::string> inputFile("input", "", "input file");
203
204 argparser_->Add(&opHelp);
205 argparser_->Add(&opModule);
206 argparser_->Add(&opDumpAst);
207 argparser_->Add(&opDumpAstOnlySilent);
208 argparser_->Add(&opDumpCheckedAst);
209 argparser_->Add(&opParseOnly);
210 argparser_->Add(&opDumpAssembly);
211 argparser_->Add(&opDebugInfo);
212 argparser_->Add(&opDumpDebugInfo);
213
214 argparser_->Add(&opOptLevel);
215 argparser_->Add(&opEtsModule);
216 argparser_->Add(&opThreadCount);
217 argparser_->Add(&opSizeStat);
218 argparser_->Add(&opListFiles);
219
220 argparser_->Add(&inputExtension);
221 argparser_->Add(&outputFile);
222 argparser_->Add(&logLevel);
223 argparser_->Add(&stdLib);
224 argparser_->Add(&genStdLib);
225 argparser_->Add(&plugins);
226 argparser_->Add(&skipPhases);
227 argparser_->Add(&verifierWarnings);
228 argparser_->Add(&verifierErrors);
229 argparser_->Add(&dumpBeforePhases);
230 argparser_->Add(&dumpEtsSrcBeforePhases);
231 argparser_->Add(&dumpAfterPhases);
232 argparser_->Add(&dumpEtsSrcBeforePhases);
233 argparser_->Add(&arktsConfig);
234 argparser_->Add(&opTsDeclOut);
235
236 argparser_->PushBackTail(&inputFile);
237 argparser_->EnableTail();
238 argparser_->EnableRemainder();
239
240 if (!argparser_->Parse(es2pandaArgs) || opHelp.GetValue()) {
241 std::stringstream ss;
242
243 ss << argparser_->GetErrorString() << std::endl;
244 ss << "Usage: "
245 << "es2panda"
246 << " [OPTIONS] [input file] -- [arguments]" << std::endl;
247 ss << std::endl;
248 ss << "optional arguments:" << std::endl;
249 ss << argparser_->GetHelpString() << std::endl;
250
251 ss << std::endl;
252 ss << "--bco-optimizer: Argument directly to bytecode optimizer can be passed after this prefix" << std::endl;
253 ss << "--bco-compiler: Argument directly to jit-compiler inside bytecode optimizer can be passed after this "
254 "prefix"
255 << std::endl;
256
257 errorMsg_ = ss.str();
258 return false;
259 }
260
261 // Determine compilation mode
262 auto compilationMode = genStdLib.GetValue() ? CompilationMode::GEN_STD_LIB
263 : inputFile.GetValue().empty() ? CompilationMode::PROJECT
264 : CompilationMode::SINGLE_FILE;
265
266 sourceFile_ = inputFile.GetValue();
267 std::ifstream inputStream;
268
269 if (compilationMode == CompilationMode::SINGLE_FILE) {
270 inputStream.open(sourceFile_.c_str());
271
272 if (inputStream.fail()) {
273 errorMsg_ = "Failed to open file: ";
274 errorMsg_.append(sourceFile_);
275 return false;
276 }
277
278 std::stringstream ss;
279 ss << inputStream.rdbuf();
280 parserInput_ = ss.str();
281 inputStream.close();
282 }
283
284 if (!outputFile.GetValue().empty()) {
285 if (compilationMode == CompilationMode::PROJECT) {
286 errorMsg_ = "Error: When compiling in project mode --output key is not needed";
287 return false;
288 }
289 compilerOutput_ = outputFile.GetValue();
290 } else {
291 compilerOutput_ = RemoveExtension(BaseName(sourceFile_)).append(".abc");
292 }
293
294 if (const auto logLevelStr = logLevel.GetValue(); !logLevelStr.empty()) {
295 if (logLevelStr == "debug") {
296 logLevel_ = util::LogLevel::DEBUG;
297 } else if (logLevelStr == "info") {
298 logLevel_ = util::LogLevel::INFO;
299 } else if (logLevelStr == "warning") {
300 logLevel_ = util::LogLevel::WARNING;
301 } else if (logLevelStr == "error") {
302 logLevel_ = util::LogLevel::ERROR;
303 } else if (logLevelStr == "fatal") {
304 logLevel_ = util::LogLevel::FATAL;
305 } else {
306 std::cerr << "Invalid log level: '" << logLevelStr
307 << R"('. Possible values: ["debug", "info", "warning", "error", "fatal"])";
308 return false;
309 }
310 }
311
312 std::string extension = inputExtension.GetValue();
313 std::string sourceFileExtension = sourceFile_.substr(sourceFile_.find_last_of('.') + 1);
314
315 if (!extension.empty()) {
316 if (extension == "js") {
317 extension_ = es2panda::ScriptExtension::JS;
318 } else if (extension == "ts") {
319 extension_ = es2panda::ScriptExtension::TS;
320 } else if (extension == "as") {
321 extension_ = es2panda::ScriptExtension::AS;
322 } else if (extension == "ets") {
323 extension_ = es2panda::ScriptExtension::ETS;
324
325 inputStream.open(arktsConfig.GetValue());
326 if (inputStream.fail()) {
327 errorMsg_ = "Failed to open arktsconfig: ";
328 errorMsg_.append(arktsConfig.GetValue());
329 return false;
330 }
331 inputStream.close();
332 } else {
333 errorMsg_ = "Invalid extension (available options: js, ts, as, ets)";
334 return false;
335 }
336
337 if (!sourceFile_.empty() && extension != sourceFileExtension) {
338 std::cerr << "Warning: Not matching extensions! Sourcefile: " << sourceFileExtension
339 << ", Manual(used): " << extension << std::endl;
340 }
341 } else {
342 if (compilationMode == CompilationMode::PROJECT) {
343 extension_ = es2panda::ScriptExtension::ETS;
344 } else if (sourceFileExtension == "js") {
345 extension_ = es2panda::ScriptExtension::JS;
346 } else if (sourceFileExtension == "ts") {
347 extension_ = es2panda::ScriptExtension::TS;
348 } else if (sourceFileExtension == "as") {
349 extension_ = es2panda::ScriptExtension::AS;
350 } else if (sourceFileExtension == "ets") {
351 extension_ = es2panda::ScriptExtension::ETS;
352 } else {
353 errorMsg_ =
354 "Unknown extension of sourcefile, set the extension manually or change the file format (available "
355 "options: js, ts, as, ets)";
356 return false;
357 }
358 }
359
360 #ifndef PANDA_WITH_ECMASCRIPT
361 if (extension_ == es2panda::ScriptExtension::JS) {
362 errorMsg_ = "js extension is not supported within current build";
363 return false;
364 }
365 #endif
366
367 if (extension_ != es2panda::ScriptExtension::JS && opModule.GetValue()) {
368 errorMsg_ = "Error: --module is not supported for this extension.";
369 return false;
370 }
371
372 if (extension_ != es2panda::ScriptExtension::ETS) {
373 if (compilationMode == CompilationMode::PROJECT) {
374 errorMsg_ = "Error: only --extension=ets is supported for project compilation mode.";
375 return false;
376 }
377 if (!opTsDeclOut.GetValue().empty()) {
378 errorMsg_ = "Error: only --extension=ets is supported for --gen-ts-decl option";
379 return false;
380 }
381 }
382
383 optLevel_ = opOptLevel.GetValue();
384 threadCount_ = opThreadCount.GetValue();
385 listFiles_ = opListFiles.GetValue();
386
387 if (opParseOnly.GetValue()) {
388 options_ |= OptionFlags::PARSE_ONLY;
389 }
390
391 if (opModule.GetValue()) {
392 options_ |= OptionFlags::PARSE_MODULE;
393 }
394
395 if (opSizeStat.GetValue()) {
396 options_ |= OptionFlags::SIZE_STAT;
397 }
398
399 compilerOptions_.arktsConfig = std::make_shared<panda::es2panda::ArkTsConfig>(arktsConfig.GetValue());
400 if (extension_ == es2panda::ScriptExtension::ETS) {
401 if (!compilerOptions_.arktsConfig->Parse()) {
402 errorMsg_ = "Invalid ArkTsConfig: ";
403 errorMsg_.append(arktsConfig.GetValue());
404 return false;
405 }
406 }
407
408 if ((dumpEtsSrcAfterPhases.GetValue().size() + dumpEtsSrcAfterPhases.GetValue().size() > 0) &&
409 extension_ != es2panda::ScriptExtension::ETS) {
410 errorMsg_ = "--dump-ets-src-* option is valid only with ETS extension";
411 return false;
412 }
413
414 compilerOptions_.tsDeclOut = opTsDeclOut.GetValue();
415 compilerOptions_.dumpAsm = opDumpAssembly.GetValue();
416 compilerOptions_.dumpAst = opDumpAst.GetValue();
417 compilerOptions_.opDumpAstOnlySilent = opDumpAstOnlySilent.GetValue();
418 compilerOptions_.dumpCheckedAst = opDumpCheckedAst.GetValue();
419 compilerOptions_.dumpDebugInfo = opDumpDebugInfo.GetValue();
420 compilerOptions_.isDebug = opDebugInfo.GetValue();
421 compilerOptions_.parseOnly = opParseOnly.GetValue();
422 compilerOptions_.stdLib = stdLib.GetValue();
423 compilerOptions_.compilationMode = compilationMode;
424 compilerOptions_.isEtsModule = opEtsModule.GetValue();
425 compilerOptions_.plugins = SplitToStringVector(plugins.GetValue());
426 compilerOptions_.skipPhases = SplitToStringSet(skipPhases.GetValue());
427 compilerOptions_.verifierWarnings = SplitToStringSet(verifierWarnings.GetValue());
428 compilerOptions_.verifierErrors = SplitToStringSet(verifierErrors.GetValue());
429 compilerOptions_.dumpBeforePhases = SplitToStringSet(dumpBeforePhases.GetValue());
430 compilerOptions_.dumpEtsSrcBeforePhases = SplitToStringSet(dumpEtsSrcBeforePhases.GetValue());
431 compilerOptions_.dumpAfterPhases = SplitToStringSet(dumpBeforePhases.GetValue());
432 compilerOptions_.dumpEtsSrcAfterPhases = SplitToStringSet(dumpEtsSrcAfterPhases.GetValue());
433
434 return true;
435 }
436 } // namespace panda::es2panda::util
437