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