1 /*
2  * Copyright (c) 2021-2022 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 <fstream>
19 #include <set>
20 #include <sstream>
21 #include <utility>
22 
23 #if defined(PANDA_TARGET_WINDOWS)
24 #include <io.h>
25 #else
26 #include <dirent.h>
27 #endif
28 
29 #include "bytecode_optimizer/bytecodeopt_options.h"
30 #include "compiler_options.h"
31 #include "os/file.h"
32 #include "utils/pandargs.h"
33 #include "utils/timers.h"
34 
35 #include "mergeProgram.h"
36 #include "util/helpers.h"
37 
38 namespace panda::es2panda::aot {
39 constexpr char PROCESS_AS_LIST_MARK = '@';
40 // item list: [filePath; recordName; moduleKind; sourceFile; pkgName; isSharedModule]
41 constexpr size_t ITEM_COUNT_MERGE = 6;
42 // item list: [filePath; recordName; moduleKind; sourceFile; outputfile; isSharedModule]
43 constexpr size_t ITEM_COUNT_NOT_MERGE = 6;
44 const std::string LIST_ITEM_SEPERATOR = ";";
45 const std::set<std::string> VALID_EXTENSIONS = { "js", "ts", "as", "abc" };
46 
47 template <class T>
RemoveExtension(T const &filename)48 T RemoveExtension(T const &filename)
49 {
50     typename T::size_type const P(filename.find_last_of('.'));
51     return P > 0 && P != T::npos ? filename.substr(0, P) : filename;
52 }
53 
GetScriptExtensionFromStr(const std::string &extension)54 static es2panda::ScriptExtension GetScriptExtensionFromStr(const std::string &extension)
55 {
56     if (extension == "js") {
57         return es2panda::ScriptExtension::JS;
58     } else if (extension == "ts") {
59         return es2panda::ScriptExtension::TS;
60     } else if (extension == "as") {
61         return es2panda::ScriptExtension::AS;
62     } else if (extension == "abc") {
63         return es2panda::ScriptExtension::ABC;
64     } else {
65         return es2panda::ScriptExtension::JS;
66     }
67 }
68 
GetScriptExtension(const std::string &filename, const std::string &inputExtension)69 static es2panda::ScriptExtension GetScriptExtension(const std::string &filename, const std::string &inputExtension)
70 {
71     std::string fileExtension = "";
72     std::string::size_type pos(filename.find_last_of('.'));
73     if (pos > 0 && pos != std::string::npos) {
74         fileExtension = filename.substr(pos + 1);
75     }
76 
77     if (VALID_EXTENSIONS.find(fileExtension) != VALID_EXTENSIONS.end()) {
78         return GetScriptExtensionFromStr(fileExtension);
79     }
80 
81     return GetScriptExtensionFromStr(inputExtension);
82 }
83 
GetStringItems(std::string &input, const std::string &delimiter)84 static std::vector<std::string> GetStringItems(std::string &input, const std::string &delimiter)
85 {
86     std::vector<std::string> items;
87     size_t pos = 0;
88     std::string token;
89     while ((pos = input.find(delimiter)) != std::string::npos) {
90         token = input.substr(0, pos);
91         if (!token.empty()) {
92             items.push_back(token);
93         }
94         input.erase(0, pos + delimiter.length());
95     }
96     if (!input.empty()) {
97         items.push_back(input);
98     }
99     return items;
100 }
101 
CollectInputAbcFile(const std::vector<std::string> &itemList, const std::string &inputExtension)102 void Options::CollectInputAbcFile(const std::vector<std::string> &itemList, const std::string &inputExtension)
103 {
104     std::string fileName = itemList[0];
105     // Split the fileInfo string to itemList by the delimiter ';'. If the element is empty, it will not be added to
106     // the itemList. The fileInfo string of the abc file only contains the file path and pkgName, so the index
107     // of pkgName is 1.
108     constexpr uint32_t PKG_NAME_IDX = 1;
109     es2panda::SourceFile src(fileName, "", parser::ScriptKind::SCRIPT, GetScriptExtension(fileName,
110                              inputExtension));
111     src.isSourceMode = false;
112     if (itemList.size() > PKG_NAME_IDX && compilerOptions_.mergeAbc) {
113         src.pkgName = itemList[PKG_NAME_IDX];
114     }
115     sourceFiles_.push_back(src);
116 }
117 
CollectInputSourceFile(const std::vector<std::string> &itemList, const std::string &inputExtension)118 void Options::CollectInputSourceFile(const std::vector<std::string> &itemList, const std::string &inputExtension)
119 {
120     std::string fileName = itemList[0];
121     std::string recordName = compilerOptions_.mergeAbc ? itemList[1] : "";
122     constexpr uint32_t SCRIPT_KIND_IDX = 2;
123     constexpr uint32_t SOURCE_FIEL_IDX = 3;
124     constexpr uint32_t PKG_NAME_IDX = 4;
125     constexpr uint32_t Is_SHARED_MODULE_IDX = 5;
126     parser::ScriptKind scriptKind;
127     if (itemList[SCRIPT_KIND_IDX] == "script") {
128         scriptKind = parser::ScriptKind::SCRIPT;
129     } else if (itemList[SCRIPT_KIND_IDX] == "commonjs") {
130         scriptKind = parser::ScriptKind::COMMONJS;
131     } else {
132         scriptKind = parser::ScriptKind::MODULE;
133     }
134 
135     es2panda::SourceFile src(fileName, recordName, scriptKind, GetScriptExtension(fileName, inputExtension));
136     src.sourcefile = itemList[SOURCE_FIEL_IDX];
137     if (compilerOptions_.mergeAbc) {
138         src.pkgName = itemList[PKG_NAME_IDX];
139     }
140 
141     if (itemList.size() == ITEM_COUNT_MERGE) {
142         src.isSharedModule = itemList[Is_SHARED_MODULE_IDX] == "true";
143     }
144 
145     sourceFiles_.push_back(src);
146     if (!compilerOptions_.mergeAbc) {
147         outputFiles_.insert({fileName, itemList[PKG_NAME_IDX]});
148     }
149 }
150 
CheckFilesValidity(const std::string &input, const std::vector<std::string> &itemList, const std::string &line)151 bool Options::CheckFilesValidity(const std::string &input, const std::vector<std::string> &itemList,
152                                  const std::string &line)
153 {
154     // For compatibility, only throw error when item list's size is bigger than given size.
155     if ((compilerOptions_.mergeAbc && itemList.size() > ITEM_COUNT_MERGE) ||
156         (!compilerOptions_.mergeAbc && itemList.size() > ITEM_COUNT_NOT_MERGE) || itemList.empty()) {
157         std::cerr << "Failed to parse line " << line << " of the input file: '"
158             << input << "'." << std::endl
159             << "Expected " << (compilerOptions_.mergeAbc ? ITEM_COUNT_MERGE : ITEM_COUNT_NOT_MERGE)
160             << " items per line, but found " << itemList.size() << " items." << std::endl
161             << "Please check the file format and content for correctness." << std::endl;
162         return false;
163     }
164     return true;
165 }
166 
IsAbcFile(const std::string &fileName, const std::string &inputExtension)167 bool Options::IsAbcFile(const std::string &fileName, const std::string &inputExtension)
168 {
169     return (GetScriptExtension(fileName, inputExtension) == es2panda::ScriptExtension::ABC);
170 }
171 
172 // Options
CollectInputFilesFromFileList(const std::string &input, const std::string &inputExtension)173 bool Options::CollectInputFilesFromFileList(const std::string &input, const std::string &inputExtension)
174 {
175     std::ifstream ifs;
176     std::string line;
177     ifs.open(panda::os::file::File::GetExtendedFilePath(input));
178     if (!ifs.is_open()) {
179         std::cerr << "Failed to open source list file '" << input << "' during input file collection." << std::endl
180                   << "Please check if the file exists or the path is correct, "
181                   << "and you have the necessary permissions to access it." << std::endl;
182         return false;
183     }
184 
185     while (std::getline(ifs, line)) {
186         std::vector<std::string> itemList = GetStringItems(line, LIST_ITEM_SEPERATOR);
187         if (!CheckFilesValidity(input, itemList, line)) {
188             return false;
189         }
190         if (IsAbcFile(itemList[0], inputExtension)) {
191             CollectInputAbcFile(itemList, inputExtension);
192         } else {
193             CollectInputSourceFile(itemList, inputExtension);
194         }
195     }
196     return true;
197 }
198 
CollectInputFilesFromFileDirectory(const std::string &input, const std::string &extension)199 bool Options::CollectInputFilesFromFileDirectory(const std::string &input, const std::string &extension)
200 {
201     std::vector<std::string> files;
202     if (!proto::MergeProgram::GetProtoFiles(input, extension, files)) {
203         return false;
204     }
205 
206     for (const auto &f : files) {
207         es2panda::SourceFile src(f, RemoveExtension(f.substr(input.length() + 1)),
208                                  scriptKind_, GetScriptExtensionFromStr(extension));
209         sourceFiles_.push_back(src);
210     }
211 
212     return true;
213 }
214 
ParseCacheFileOption(const std::string &cacheInput)215 void Options::ParseCacheFileOption(const std::string &cacheInput)
216 {
217     if (cacheInput[0] != PROCESS_AS_LIST_MARK) {
218         compilerOptions_.cacheFiles.insert({sourceFile_, cacheInput});
219         return;
220     }
221 
222     std::ifstream ifs;
223     std::string line;
224     ifs.open(panda::os::file::File::GetExtendedFilePath(cacheInput.substr(1)));
225     if (!ifs.is_open()) {
226         std::cerr << "Failed to open cache file list from the provided path: '" << cacheInput << "'." << std::endl
227                   << "Please check if the file exists or the path is correct, "
228                   << "and you have the necessary permissions to read the file." << std::endl;
229         return;
230     }
231 
232     constexpr int cacheListItemCount = 2;
233     while (std::getline(ifs, line)) {
234         std::vector<std::string> itemList = GetStringItems(line, LIST_ITEM_SEPERATOR);
235         if (itemList.size() != cacheListItemCount) {
236             continue;
237         }
238         compilerOptions_.cacheFiles.insert({itemList[0], itemList[1]});
239     }
240 }
241 
ParseUpdateVersionInfo(nlohmann::json &compileContextInfoJson)242 void Options::ParseUpdateVersionInfo(nlohmann::json &compileContextInfoJson)
243 {
244     if (compileContextInfoJson.contains("updateVersionInfo") &&
245         compileContextInfoJson["updateVersionInfo"].is_object()) {
246         std::unordered_map<std::string, std::unordered_map<std::string, PkgInfo>> updateVersionInfo {};
247         for (const auto& [abcName, versionInfo] : compileContextInfoJson["updateVersionInfo"].items()) {
248             if (!versionInfo.is_object()) {
249                 std::cerr << "The input file '" << compilerOptions_.compileContextInfoPath
250                           << "' is incomplete format of json" << std::endl;
251             }
252             std::unordered_map<std::string, PkgInfo> pkgContextMap {};
253             for (const auto& [pkgName, version] : versionInfo.items()) {
254                 PkgInfo pkgInfo;
255                 pkgInfo.version = version;
256                 pkgInfo.packageName = pkgName;
257                 pkgContextMap[pkgName] = pkgInfo;
258             }
259             updateVersionInfo[abcName] = pkgContextMap;
260         }
261         compilerOptions_.compileContextInfo.updateVersionInfo = updateVersionInfo;
262     } else if (compileContextInfoJson.contains("pkgContextInfo") &&
263                compileContextInfoJson["pkgContextInfo"].is_object()) {
264         std::unordered_map<std::string, PkgInfo> pkgContextMap {};
265         for (const auto& [pkgName, pkgContextInfo] : compileContextInfoJson["pkgContextInfo"].items()) {
266             PkgInfo pkgInfo;
267             if (pkgContextInfo.contains("version") && pkgContextInfo["version"].is_string()) {
268                 pkgInfo.version = pkgContextInfo["version"];
269             } else {
270                 std::cerr << "Failed to get version from pkgContextInfo."  << std::endl;
271             }
272             if (pkgContextInfo.contains("packageName") && pkgContextInfo["packageName"].is_string()) {
273                 pkgInfo.packageName = pkgContextInfo["packageName"];
274             } else {
275                 std::cerr << "Failed to get package name from pkgContextInfo."  << std::endl;
276             }
277             pkgContextMap[pkgName] = pkgInfo;
278         }
279         compilerOptions_.compileContextInfo.pkgContextInfo = pkgContextMap;
280     } else {
281         UNREACHABLE();
282     }
283 }
284 
ParseCompileContextInfo(const std::string compileContextInfoPath)285 void Options::ParseCompileContextInfo(const std::string compileContextInfoPath)
286 {
287     std::stringstream ss;
288     std::string buffer;
289     if (!util::Helpers::ReadFileToBuffer(compileContextInfoPath, ss)) {
290         return;
291     }
292 
293     buffer = ss.str();
294     if (buffer.empty() || !nlohmann::json::accept(buffer)) {
295         std::cerr << "The input file '" << compileContextInfoPath <<"' is incomplete format of json" << std::endl;
296         return;
297     }
298     // Parser compile context info base on the input json file.
299     nlohmann::json compileContextInfoJson = nlohmann::json::parse(buffer);
300     if (!compileContextInfoJson.contains("compileEntries") || !compileContextInfoJson.contains("hspPkgNames")) {
301         std::cerr << "The input json file '" << compileContextInfoPath << "' content format is incorrect" << std::endl;
302         return;
303     }
304     if (!compileContextInfoJson["compileEntries"].is_array() || !compileContextInfoJson["hspPkgNames"].is_array()) {
305         std::cerr << "The input json file '" << compileContextInfoPath << "' content type is incorrect" << std::endl;
306         return;
307     }
308     std::set<std::string> externalPkgNames;
309     for (const auto& elem : compileContextInfoJson["hspPkgNames"]) {
310         if (elem.is_string()) {
311             externalPkgNames.insert(elem.get<std::string>());
312         }
313     }
314     compilerOptions_.compileContextInfo.externalPkgNames = externalPkgNames;
315     compilerOptions_.compileContextInfo.compileEntries = compileContextInfoJson["compileEntries"];
316     ParseUpdateVersionInfo(compileContextInfoJson);
317 }
318 
319 // Collect dependencies based on the compile entries.
NeedCollectDepsRelation()320 bool Options::NeedCollectDepsRelation()
321 {
322     return compilerOptions_.enableAbcInput && !compilerOptions_.compileContextInfo.compileEntries.empty();
323 }
324 
325 // Remove redundant content from the abc file and remove programs generated from redundant source files.
NeedRemoveRedundantRecord()326 bool Options::NeedRemoveRedundantRecord()
327 {
328     return compilerOptions_.removeRedundantFile && NeedCollectDepsRelation();
329 }
330 
Options()331 Options::Options() : argparser_(new panda::PandArgParser()) {}
332 
~Options()333 Options::~Options()
334 {
335     delete argparser_;
336 }
337 
Parse(int argc, const char **argv)338 bool Options::Parse(int argc, const char **argv)
339 {
340     panda::PandArg<bool> opHelp("help", false, "Print this message and exit");
341 
342     // parser
343     panda::PandArg<std::string> inputExtension("extension", "js",
344                                                "Parse the input as the given extension (options: js | ts | as | abc)");
345     panda::PandArg<bool> opModule("module", false, "Parse the input as module");
346     panda::PandArg<bool> opCommonjs("commonjs", false, "Parse the input as commonjs");
347     panda::PandArg<bool> opParseOnly("parse-only", false, "Parse the input only");
348     panda::PandArg<bool> opEnableTypeCheck("enable-type-check", false, "Check the type in ts after parse");
349     panda::PandArg<bool> opDumpAst("dump-ast", false, "Dump the parsed AST");
350     panda::PandArg<bool> opDumpTransformedAst("dump-transformed-ast", false, "Dump the parsed AST after transform");
351     panda::PandArg<bool> opCheckTransformedAstStructure("check-transformed-ast-structure", false,
352                                                         "Check the AST structure after transform");
353     panda::PandArg<bool> opRecordDebugSource("record-debug-source", false, "Record source code to support "\
354         "multi-platform debugger & detailed backtrace in debug mode");
355 
356     // compiler
357     panda::PandArg<bool> opEnableAbcInput("enable-abc-input", false, "Allow abc file as input");
358     panda::PandArg<bool> opDumpAsmProgram("dump-asm-program", false, "Dump program");
359     std::string descOfDumpNormalizedProg =
360         "Dump program in normalized form to ensure the output of source code compilation is consistent with that of "
361         "abc file compilation.\n"
362         "  The normalized form differs mainly as follows:\n"
363         "  1. all instructions will be labled consecutively and all the labels will be dumped\n"
364         "  2. the content of a literal array, rather than its id, will be dumped when the literal array appears in "
365         "an opcode or is nested in another literal array\n"
366         "  3. labels stored in catch blocks will be unified\n"
367         "  4. strings won't be dumped\n"
368         "  5. invalid opcodes won't be dumped, local variables' start and end offset will skip invalid opcodes";
369     panda::PandArg<bool> opDumpNormalizedAsmProgram("dump-normalized-asm-program", false, descOfDumpNormalizedProg);
370     panda::PandArg<bool> opDumpAssembly("dump-assembly", false, "Dump pandasm");
371     panda::PandArg<bool> opDebugInfo("debug-info", false, "Compile with debug info");
372     panda::PandArg<bool> opDumpDebugInfo("dump-debug-info", false, "Dump debug info");
373     panda::PandArg<int> opOptLevel("opt-level", 2,
374         "Compiler optimization level (options: 0 | 1 | 2). In debug and base64Input mode, optimizer is disabled");
375     panda::PandArg<int> opFunctionThreadCount("function-threads", 0, "Number of worker threads to compile function");
376     panda::PandArg<int> opFileThreadCount("file-threads", 0, "Number of worker threads to compile file");
377     panda::PandArg<int> opAbcClassThreadCount("abc-class-threads", 4,
378         "Number of worker threads to compile classes of abc file");
379     panda::PandArg<bool> opSizeStat("dump-size-stat", false, "Dump size statistics");
380     panda::PandArg<bool> opSizePctStat("dump-file-item-size", false, "Dump the size of each kind of file item "\
381         "of the abc file");
382     panda::PandArg<bool> opDumpLiteralBuffer("dump-literal-buffer", false, "Dump literal buffer");
383     panda::PandArg<std::string> outputFile("output", "", "Compiler binary output (.abc)");
384     panda::PandArg<std::string> recordName("record-name", "", "Specify the record name");
385     panda::PandArg<bool> debuggerEvaluateExpression("debugger-evaluate-expression", false,
386                                                     "evaluate expression in debugger mode");
387     panda::PandArg<std::string> base64Input("base64Input", "", "base64 input of js content");
388     panda::PandArg<bool> base64Output("base64Output", false, "output panda file content as base64 to std out");
389     panda::PandArg<std::string> sourceFile("source-file", "",
390                                            "specify the file path info recorded in generated abc");
391     panda::PandArg<std::string> outputProto("outputProto", "",
392                                             "specify the output name for serializd protobuf file (.protoBin)");
393     panda::PandArg<std::string> opCacheFile("cache-file", "", "cache file for incremental compile");
394     panda::PandArg<std::string> opNpmModuleEntryList("npm-module-entry-list", "", "entry list file for module compile");
395     panda::PandArg<bool> opMergeAbc("merge-abc", false, "Compile as merge abc");
396     panda::PandArg<std::string> opPerfFile("perf-file", "perf.txt", "Specify the file path to dump time consuming data"\
397         " during compilation process, default to 'perf.txt' in the current directory");
398     panda::PandArg<int> opPerfLevel("perf-level", 0, "Specify the performance data output level:"\
399         "  0: Output compilation time data(default)");
400     panda::PandArg<bool> opuseDefineSemantic("use-define-semantic", false, "Compile ts class fields "\
401         "in accordance with ECMAScript2022");
402     panda::PandArg<std::string> moduleRecordFieldName("module-record-field-name", "", "Specify the field name "\
403         "of module record in unmerged abc");
404 
405     // optimizer
406     panda::PandArg<bool> opBranchElimination("branch-elimination", false, "Enable branch elimination optimization");
407     panda::PandArg<bool> opOptTryCatchFunc("opt-try-catch-func", true, "Enable optimizations for functions with "\
408         "try-catch blocks");
409 
410     // patchfix && hotreload
411     panda::PandArg<std::string> opDumpSymbolTable("dump-symbol-table", "", "dump symbol table to file");
412     panda::PandArg<std::string> opInputSymbolTable("input-symbol-table", "", "input symbol table file");
413     panda::PandArg<bool> opGeneratePatch("generate-patch", false, "generate patch abc, default as hotfix mode unless "\
414         "the cold-fix argument is set");
415     panda::PandArg<bool> opHotReload("hot-reload", false, "compile as hot-reload mode");
416     panda::PandArg<bool> opColdReload("cold-reload", false, "compile as cold-reload mode");
417     panda::PandArg<bool> opColdFix("cold-fix", false, "generate patch abc as cold-fix mode");
418 
419     // version
420     panda::PandArg<bool> bcVersion("bc-version", false, "Print ark bytecode version. If both bc-version and"\
421         "bc-min-version are enabled, only bc-version will take effects");
422     panda::PandArg<bool> bcMinVersion("bc-min-version", false, "Print ark bytecode minimum supported version");
423     // todo(huyunhui): change default api verion to 0 after refactoring
424     // The latest api version in isa.yaml is 15
425     panda::PandArg<int> targetApiVersion("target-api-version", 15,
426         "Specify the targeting api version for es2abc to generated the corresponding version of bytecode");
427     panda::PandArg<bool> targetBcVersion("target-bc-version", false, "Print the corresponding ark bytecode version"\
428         "for target api version. If both target-bc-version and bc-version are enabled, only target-bc-version"\
429         "will take effects");
430     panda::PandArg<std::string> targetApiSubVersion("target-api-sub-version",
431         std::string {util::Helpers::DEFAULT_SUB_API_VERSION},
432         "Specify the targeting api sub version for es2abc to generated the corresponding version of bytecode");
433     panda::PandArg<bool> enableAnnotations("enable-annotations", false, "Permits es2abc to compile annotations");
434 
435     // compile entries and pkg context info
436     panda::PandArg<std::string> compileContextInfoPath("compile-context-info", "", "The path to compile context"\
437         "info file");
438     panda::PandArg<bool> opDumpDepsInfo("dump-deps-info", false, "Dump all dependency files and records "\
439         "including source files and bytecode files");
440     panda::PandArg<bool> opRemoveRedundantFile("remove-redundant-file", false, "Remove redundant info"\
441         " from abc file and remove redundant source file, which is effective when the compile-context-info switch"\
442         "  is turned on and there is abc input");
443     panda::PandArg<bool> opDumpString("dump-string", false, "Dump program strings");
444 
445     // aop transform
446     panda::PandArg<std::string> transformLib("transform-lib", "", "aop transform lib file path");
447 
448     // tail arguments
449     panda::PandArg<std::string> inputFile("input", "", "input file");
450 
451     argparser_->Add(&opHelp);
452     argparser_->Add(&opModule);
453     argparser_->Add(&opCommonjs);
454     argparser_->Add(&opDumpAst);
455     argparser_->Add(&opDumpTransformedAst);
456     argparser_->Add(&opCheckTransformedAstStructure);
457     argparser_->Add(&opRecordDebugSource);
458     argparser_->Add(&opParseOnly);
459     argparser_->Add(&opEnableTypeCheck);
460     argparser_->Add(&opEnableAbcInput);
461     argparser_->Add(&opDumpAsmProgram);
462     argparser_->Add(&opDumpNormalizedAsmProgram);
463     argparser_->Add(&opDumpAssembly);
464     argparser_->Add(&opDebugInfo);
465     argparser_->Add(&opDumpDebugInfo);
466     argparser_->Add(&debuggerEvaluateExpression);
467     argparser_->Add(&base64Input);
468     argparser_->Add(&base64Output);
469 
470     argparser_->Add(&opOptLevel);
471     argparser_->Add(&opFunctionThreadCount);
472     argparser_->Add(&opFileThreadCount);
473     argparser_->Add(&opAbcClassThreadCount);
474     argparser_->Add(&opSizeStat);
475     argparser_->Add(&opSizePctStat);
476     argparser_->Add(&opDumpLiteralBuffer);
477 
478     argparser_->Add(&inputExtension);
479     argparser_->Add(&outputFile);
480     argparser_->Add(&sourceFile);
481     argparser_->Add(&recordName);
482     argparser_->Add(&outputProto);
483     argparser_->Add(&opCacheFile);
484     argparser_->Add(&opNpmModuleEntryList);
485     argparser_->Add(&opMergeAbc);
486     argparser_->Add(&opPerfLevel);
487     argparser_->Add(&opPerfFile);
488     argparser_->Add(&opuseDefineSemantic);
489     argparser_->Add(&moduleRecordFieldName);
490     argparser_->Add(&opBranchElimination);
491     argparser_->Add(&opOptTryCatchFunc);
492 
493     argparser_->Add(&opDumpSymbolTable);
494     argparser_->Add(&opInputSymbolTable);
495     argparser_->Add(&opGeneratePatch);
496     argparser_->Add(&opHotReload);
497     argparser_->Add(&opColdReload);
498     argparser_->Add(&opColdFix);
499 
500     argparser_->Add(&bcVersion);
501     argparser_->Add(&bcMinVersion);
502     argparser_->Add(&targetApiVersion);
503     argparser_->Add(&targetBcVersion);
504     argparser_->Add(&targetApiSubVersion);
505     argparser_->Add(&enableAnnotations);
506 
507     argparser_->Add(&compileContextInfoPath);
508     argparser_->Add(&opDumpDepsInfo);
509     argparser_->Add(&opRemoveRedundantFile);
510     argparser_->Add(&opDumpString);
511 
512     argparser_->Add(&transformLib);
513 
514     argparser_->PushBackTail(&inputFile);
515     argparser_->EnableTail();
516     argparser_->EnableRemainder();
517 
518     bool parseStatus = argparser_->Parse(argc, argv);
519 
520     compilerOptions_.targetApiVersion = targetApiVersion.GetValue();
521     compilerOptions_.targetApiSubVersion = targetApiSubVersion.GetValue();
522     if (parseStatus && targetBcVersion.GetValue()) {
523         compilerOptions_.targetBcVersion = targetBcVersion.GetValue();
524         return true;
525     }
526 
527     if (parseStatus && (bcVersion.GetValue() || bcMinVersion.GetValue())) {
528         compilerOptions_.bcVersion = bcVersion.GetValue();
529         compilerOptions_.bcMinVersion = bcMinVersion.GetValue();
530         return true;
531     }
532 
533     if (!parseStatus || opHelp.GetValue() || (inputFile.GetValue().empty() && base64Input.GetValue().empty())) {
534         std::stringstream ss;
535 
536         ss << argparser_->GetErrorString() << std::endl;
537         ss << "Usage: "
538            << "es2panda"
539            << " [OPTIONS] [input file] -- [arguments]" << std::endl;
540         ss << std::endl;
541         ss << "optional arguments:" << std::endl;
542         ss << argparser_->GetHelpString() << std::endl;
543 
544         errorMsg_ = ss.str();
545         return false;
546     }
547 
548     bool inputIsEmpty = inputFile.GetValue().empty();
549     bool base64InputIsEmpty = base64Input.GetValue().empty();
550     bool outputIsEmpty = outputFile.GetValue().empty();
551 
552     if (!inputIsEmpty && !base64InputIsEmpty) {
553         errorMsg_ = "--input and --base64Input can not be used simultaneously";
554         return false;
555     }
556 
557     if (!outputIsEmpty && base64Output.GetValue()) {
558         errorMsg_ = "--output and --base64Output can not be used simultaneously";
559         return false;
560     }
561 
562     if (opModule.GetValue() && opCommonjs.GetValue()) {
563         errorMsg_ = "[--module] and [--commonjs] can not be used simultaneously";
564         return false;
565     }
566 
567     if (opModule.GetValue()) {
568         scriptKind_ = es2panda::parser::ScriptKind::MODULE;
569     } else if (opCommonjs.GetValue()) {
570         scriptKind_ = es2panda::parser::ScriptKind::COMMONJS;
571     } else {
572         scriptKind_ = es2panda::parser::ScriptKind::SCRIPT;
573     }
574 
575     std::string extension = inputExtension.GetValue();
576     if (!extension.empty()) {
577         if (VALID_EXTENSIONS.find(extension) == VALID_EXTENSIONS.end()) {
578             errorMsg_ = "Invalid extension (available options: js, ts, as, abc)";
579             return false;
580         }
581     }
582 
583     bool isInputFileList = false;
584     if (!inputIsEmpty) {
585         std::string rawInput = inputFile.GetValue();
586         isInputFileList = rawInput[0] == PROCESS_AS_LIST_MARK;
587         std::string input = isInputFileList ? rawInput.substr(1) : rawInput;
588         sourceFile_ = input;
589     }
590 
591     if (base64Output.GetValue()) {
592         compilerOutput_ = "";
593     } else if (!outputIsEmpty) {
594         compilerOutput_ = outputFile.GetValue();
595     } else if (outputIsEmpty && !inputIsEmpty) {
596         compilerOutput_ = RemoveExtension(util::Helpers::BaseName(sourceFile_)).append(".abc");
597     }
598 
599     if (opMergeAbc.GetValue()) {
600         recordName_ = recordName.GetValue();
601         if (recordName_.empty()) {
602             recordName_ = compilerOutput_.empty() ? "Base64Output" :
603                 RemoveExtension(util::Helpers::BaseName(compilerOutput_));
604         }
605         compilerOptions_.mergeAbc = opMergeAbc.GetValue();
606     }
607 
608     if (opuseDefineSemantic.GetValue()) {
609         compilerOptions_.useDefineSemantic = opuseDefineSemantic.GetValue();
610     }
611 
612     if (!inputIsEmpty) {
613         // common mode
614         auto inputAbs = panda::os::file::File::GetAbsolutePath(sourceFile_);
615         if (!inputAbs) {
616             std::cerr << "Failed to find file '" << sourceFile_ << "' during input file resolution" << std::endl
617                       << "Please check if the file name is correct, the file exists at the specified path, "
618                       << "and your project has the necessary permissions to access it." << std::endl;
619             return false;
620         }
621 
622         auto fpath = inputAbs.Value();
623         if (isInputFileList) {
624             CollectInputFilesFromFileList(fpath, extension);
625         } else if (panda::os::file::File::IsDirectory(fpath)) {
626             CollectInputFilesFromFileDirectory(fpath, extension);
627         } else {
628             es2panda::SourceFile src(sourceFile_, recordName_, scriptKind_, GetScriptExtension(sourceFile_, extension));
629             src.isSourceMode = !IsAbcFile(sourceFile_, extension);
630             sourceFiles_.push_back(src);
631         }
632     } else if (!base64InputIsEmpty) {
633         // input content is base64 string
634         base64Input_ = ExtractContentFromBase64Input(base64Input.GetValue());
635         if (base64Input_.empty()) {
636             errorMsg_ = "The input string is not a valid base64 data";
637             return false;
638         }
639 
640         es2panda::SourceFile src("", recordName_, es2panda::parser::ScriptKind::SCRIPT,
641                                  GetScriptExtensionFromStr(extension));
642         src.source = base64Input_;
643         sourceFiles_.push_back(src);
644     }
645 
646     if (!outputProto.GetValue().empty()) {
647         compilerProtoOutput_ = outputProto.GetValue();
648     }
649 
650     optLevel_ = opOptLevel.GetValue();
651     functionThreadCount_ = opFunctionThreadCount.GetValue();
652     fileThreadCount_ = opFileThreadCount.GetValue();
653     abcClassThreadCount_ = opAbcClassThreadCount.GetValue();
654     npmModuleEntryList_ = opNpmModuleEntryList.GetValue();
655 
656     if (!opCacheFile.GetValue().empty()) {
657         ParseCacheFileOption(opCacheFile.GetValue());
658     }
659 
660     if (opParseOnly.GetValue()) {
661         options_ |= OptionFlags::PARSE_ONLY;
662     }
663 
664     if (opSizeStat.GetValue()) {
665         options_ |= OptionFlags::SIZE_STAT;
666     }
667 
668     if (opSizePctStat.GetValue()) {
669         options_ |= OptionFlags::SIZE_PCT_STAT;
670     }
671 
672     perfFile_ = "";
673     perfLevel_ = opPerfLevel.GetValue();
674     if (opPerfFile.WasSet() && (perfLevel_ == 0)) {
675         perfFile_ = opPerfFile.GetValue().empty() ? opPerfFile.GetDefaultValue() : opPerfFile.GetValue();
676     }
677     panda::Timer::InitializeTimer(perfFile_);
678 
679     compilerOptions_.recordDebugSource = opRecordDebugSource.GetValue();
680     compilerOptions_.enableAbcInput = opEnableAbcInput.GetValue();
681     compilerOptions_.dumpAsmProgram = opDumpAsmProgram.GetValue();
682     compilerOptions_.dumpNormalizedAsmProgram = opDumpNormalizedAsmProgram.GetValue();
683     compilerOptions_.dumpAsm = opDumpAssembly.GetValue();
684     compilerOptions_.dumpAst = opDumpAst.GetValue();
685     compilerOptions_.dumpTransformedAst = opDumpTransformedAst.GetValue();
686     compilerOptions_.checkTransformedAstStructure = opCheckTransformedAstStructure.GetValue();
687     compilerOptions_.dumpDebugInfo = opDumpDebugInfo.GetValue();
688     compilerOptions_.isDebug = opDebugInfo.GetValue();
689     compilerOptions_.parseOnly = opParseOnly.GetValue();
690     compilerOptions_.enableTypeCheck = opEnableTypeCheck.GetValue();
691     compilerOptions_.dumpLiteralBuffer = opDumpLiteralBuffer.GetValue();
692     compilerOptions_.isDebuggerEvaluateExpressionMode = debuggerEvaluateExpression.GetValue();
693 
694     compilerOptions_.functionThreadCount = functionThreadCount_;
695     compilerOptions_.fileThreadCount = fileThreadCount_;
696     compilerOptions_.abcClassThreadCount = abcClassThreadCount_;
697     compilerOptions_.output = compilerOutput_;
698     compilerOptions_.debugInfoSourceFile = sourceFile.GetValue();
699     compilerOptions_.optLevel = (compilerOptions_.isDebug || !base64Input.GetValue().empty() ||
700         base64Output.GetValue()) ? 0 : opOptLevel.GetValue();
701     compilerOptions_.sourceFiles = sourceFiles_;
702     compilerOptions_.mergeAbc = opMergeAbc.GetValue();
703     compilerOptions_.compileContextInfoPath = compileContextInfoPath.GetValue();
704     if (!compileContextInfoPath.GetValue().empty()) {
705         ParseCompileContextInfo(compileContextInfoPath.GetValue());
706     }
707     compilerOptions_.dumpDepsInfo = opDumpDepsInfo.GetValue();
708     compilerOptions_.updatePkgVersionForAbcInput = compilerOptions_.enableAbcInput &&
709         (!compilerOptions_.compileContextInfo.pkgContextInfo.empty() ||
710         !compilerOptions_.compileContextInfo.updateVersionInfo.empty());
711     compilerOptions_.removeRedundantFile = opRemoveRedundantFile.GetValue();
712     compilerOptions_.dumpString = opDumpString.GetValue();
713     compilerOptions_.moduleRecordFieldName = moduleRecordFieldName.GetValue();
714 
715     compilerOptions_.patchFixOptions.dumpSymbolTable = opDumpSymbolTable.GetValue();
716     compilerOptions_.patchFixOptions.symbolTable = opInputSymbolTable.GetValue();
717 
718     bool generatePatch = opGeneratePatch.GetValue();
719     bool hotReload = opHotReload.GetValue();
720     bool coldReload = opColdReload.GetValue();
721     bool coldFix = opColdFix.GetValue();
722     if (generatePatch && hotReload) {
723         errorMsg_ = "--generate-patch and --hot-reload can not be used simultaneously";
724         return false;
725     }
726     if (coldFix && !generatePatch) {
727         errorMsg_ = "--cold-fix can not be used without --generate-patch";
728         return false;
729     }
730     compilerOptions_.patchFixOptions.generatePatch = generatePatch;
731     compilerOptions_.patchFixOptions.hotReload = hotReload;
732     compilerOptions_.patchFixOptions.coldReload = coldReload;
733     compilerOptions_.patchFixOptions.coldFix = coldFix;
734 
735     compilerOptions_.enableAnnotations = enableAnnotations.GetValue();
736 
737     bool transformLibIsEmpty = transformLib.GetValue().empty();
738     if (!transformLibIsEmpty) {
739         auto libName = transformLib.GetValue();
740         // check file exist or not
741         auto transformLibAbs = panda::os::file::File::GetAbsolutePath(libName);
742         if (!transformLibAbs) {
743             std::cerr << "Failed to find file '" << libName << "' during transformLib file resolution" << std::endl
744                       << "Please check if the file name is correct, the file exists at the specified path, "
745                       << "and your project has the necessary permissions to access it." << std::endl;
746             return false;
747         }
748         compilerOptions_.transformLib = transformLibAbs.Value();
749     }
750 
751     compilerOptions_.branchElimination = opBranchElimination.GetValue();
752     compilerOptions_.requireGlobalOptimization = compilerOptions_.optLevel > 0 &&
753                                                  compilerOptions_.branchElimination &&
754                                                  compilerOptions_.mergeAbc;
755     panda::compiler::options.SetCompilerBranchElimination(compilerOptions_.branchElimination);
756     panda::bytecodeopt::options.SetSkipMethodsWithEh(!opOptTryCatchFunc.GetValue());
757 
758     return true;
759 }
760 
ExtractContentFromBase64Input(const std::string &inputBase64String)761 std::string Options::ExtractContentFromBase64Input(const std::string &inputBase64String)
762 {
763     std::string inputContent = util::Base64Decode(inputBase64String);
764     if (inputContent == "") {
765         return "";
766     }
767     bool validBase64Input = util::Base64Encode(inputContent) == inputBase64String;
768     if (!validBase64Input) {
769         return "";
770     }
771     return inputContent;
772 }
773 }  // namespace panda::es2panda::aot
774