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