• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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