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 "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
404 // optimizer
405 panda::PandArg<bool> opBranchElimination("branch-elimination", false, "Enable branch elimination optimization");
406 panda::PandArg<bool> opOptTryCatchFunc("opt-try-catch-func", true, "Enable optimizations for functions with "\
407 "try-catch blocks");
408
409 // patchfix && hotreload
410 panda::PandArg<std::string> opDumpSymbolTable("dump-symbol-table", "", "dump symbol table to file");
411 panda::PandArg<std::string> opInputSymbolTable("input-symbol-table", "", "input symbol table file");
412 panda::PandArg<bool> opGeneratePatch("generate-patch", false, "generate patch abc, default as hotfix mode unless "\
413 "the cold-fix argument is set");
414 panda::PandArg<bool> opHotReload("hot-reload", false, "compile as hot-reload mode");
415 panda::PandArg<bool> opColdReload("cold-reload", false, "compile as cold-reload mode");
416 panda::PandArg<bool> opColdFix("cold-fix", false, "generate patch abc as cold-fix mode");
417
418 // version
419 panda::PandArg<bool> bcVersion("bc-version", false, "Print ark bytecode version. If both bc-version and"\
420 "bc-min-version are enabled, only bc-version will take effects");
421 panda::PandArg<bool> bcMinVersion("bc-min-version", false, "Print ark bytecode minimum supported version");
422 // todo(huyunhui): change default api verion to 0 after refactoring
423 // Current api version is 18
424 panda::PandArg<int> targetApiVersion("target-api-version", 18,
425 "Specify the targeting api version for es2abc to generated the corresponding version of bytecode");
426 panda::PandArg<bool> targetBcVersion("target-bc-version", false, "Print the corresponding ark bytecode version"\
427 "for target api version. If both target-bc-version and bc-version are enabled, only target-bc-version"\
428 "will take effects");
429 panda::PandArg<std::string> targetApiSubVersion("target-api-sub-version",
430 std::string {util::Helpers::DEFAULT_SUB_API_VERSION},
431 "Specify the targeting api sub version for es2abc to generated the corresponding version of bytecode");
432 panda::PandArg<bool> enableAnnotations("enable-annotations", false, "Permits es2abc to compile annotations");
433
434 // compile entries and pkg context info
435 panda::PandArg<std::string> compileContextInfoPath("compile-context-info", "", "The path to compile context"\
436 "info file");
437 panda::PandArg<bool> opDumpDepsInfo("dump-deps-info", false, "Dump all dependency files and records "\
438 "including source files and bytecode files");
439 panda::PandArg<bool> opRemoveRedundantFile("remove-redundant-file", false, "Remove redundant info"\
440 " from abc file and remove redundant source file, which is effective when the compile-context-info switch"\
441 " is turned on and there is abc input");
442 panda::PandArg<bool> opDumpString("dump-string", false, "Dump program strings");
443 panda::PandArg<std::string> srcPkgName("src-package-name", "", "This is for modify pacakge name in input abc"\
444 " file and should aways be used with dstPkgName. srcPkgName is for finding the targeting package name to be"\
445 " modified.");
446 panda::PandArg<std::string> dstPkgName("dst-package-name", "", "This is for modify pacakge name in input abc"\
447 " file, and should always be used with srcPkgName. dstPkgName what targeting package name will be"\
448 " modified to.");
449
450 // aop transform
451 panda::PandArg<std::string> transformLib("transform-lib", "", "aop transform lib file path");
452
453 // tail arguments
454 panda::PandArg<std::string> inputFile("input", "", "input file");
455
456 argparser_->Add(&opHelp);
457 argparser_->Add(&opModule);
458 argparser_->Add(&opCommonjs);
459 argparser_->Add(&opDumpAst);
460 argparser_->Add(&opDumpTransformedAst);
461 argparser_->Add(&opCheckTransformedAstStructure);
462 argparser_->Add(&opRecordDebugSource);
463 argparser_->Add(&opParseOnly);
464 argparser_->Add(&opEnableTypeCheck);
465 argparser_->Add(&opEnableAbcInput);
466 argparser_->Add(&opDumpAsmProgram);
467 argparser_->Add(&opDumpNormalizedAsmProgram);
468 argparser_->Add(&opDumpAssembly);
469 argparser_->Add(&opDebugInfo);
470 argparser_->Add(&opDumpDebugInfo);
471 argparser_->Add(&debuggerEvaluateExpression);
472 argparser_->Add(&base64Input);
473 argparser_->Add(&base64Output);
474
475 argparser_->Add(&opOptLevel);
476 argparser_->Add(&opFunctionThreadCount);
477 argparser_->Add(&opFileThreadCount);
478 argparser_->Add(&opAbcClassThreadCount);
479 argparser_->Add(&opSizeStat);
480 argparser_->Add(&opSizePctStat);
481 argparser_->Add(&opDumpLiteralBuffer);
482
483 argparser_->Add(&inputExtension);
484 argparser_->Add(&outputFile);
485 argparser_->Add(&sourceFile);
486 argparser_->Add(&recordName);
487 argparser_->Add(&outputProto);
488 argparser_->Add(&opCacheFile);
489 argparser_->Add(&opNpmModuleEntryList);
490 argparser_->Add(&opMergeAbc);
491 argparser_->Add(&opPerfLevel);
492 argparser_->Add(&opPerfFile);
493 argparser_->Add(&opuseDefineSemantic);
494 argparser_->Add(&moduleRecordFieldName);
495 argparser_->Add(&opBranchElimination);
496 argparser_->Add(&opOptTryCatchFunc);
497
498 argparser_->Add(&opDumpSymbolTable);
499 argparser_->Add(&opInputSymbolTable);
500 argparser_->Add(&opGeneratePatch);
501 argparser_->Add(&opHotReload);
502 argparser_->Add(&opColdReload);
503 argparser_->Add(&opColdFix);
504
505 argparser_->Add(&bcVersion);
506 argparser_->Add(&bcMinVersion);
507 argparser_->Add(&targetApiVersion);
508 argparser_->Add(&targetBcVersion);
509 argparser_->Add(&targetApiSubVersion);
510 argparser_->Add(&enableAnnotations);
511
512 argparser_->Add(&compileContextInfoPath);
513 argparser_->Add(&opDumpDepsInfo);
514 argparser_->Add(&opRemoveRedundantFile);
515 argparser_->Add(&opDumpString);
516
517 argparser_->Add(&transformLib);
518
519 argparser_->Add(&srcPkgName);
520 argparser_->Add(&dstPkgName);
521
522 argparser_->PushBackTail(&inputFile);
523 argparser_->EnableTail();
524 argparser_->EnableRemainder();
525
526 bool parseStatus = argparser_->Parse(argc, argv);
527
528 compilerOptions_.targetApiVersion = targetApiVersion.GetValue();
529 compilerOptions_.targetApiSubVersion = targetApiSubVersion.GetValue();
530 if (parseStatus && targetBcVersion.GetValue()) {
531 compilerOptions_.targetBcVersion = targetBcVersion.GetValue();
532 return true;
533 }
534
535 if (parseStatus && (bcVersion.GetValue() || bcMinVersion.GetValue())) {
536 compilerOptions_.bcVersion = bcVersion.GetValue();
537 compilerOptions_.bcMinVersion = bcMinVersion.GetValue();
538 return true;
539 }
540
541 if (!parseStatus || opHelp.GetValue() || (inputFile.GetValue().empty() && base64Input.GetValue().empty())) {
542 std::stringstream ss;
543
544 ss << argparser_->GetErrorString() << std::endl;
545 ss << "Usage: "
546 << "es2panda"
547 << " [OPTIONS] [input file] -- [arguments]" << std::endl;
548 ss << std::endl;
549 ss << "optional arguments:" << std::endl;
550 ss << argparser_->GetHelpString() << std::endl;
551
552 errorMsg_ = ss.str();
553 return false;
554 }
555
556 bool inputIsEmpty = inputFile.GetValue().empty();
557 bool base64InputIsEmpty = base64Input.GetValue().empty();
558 bool outputIsEmpty = outputFile.GetValue().empty();
559
560 if (!inputIsEmpty && !base64InputIsEmpty) {
561 errorMsg_ = "--input and --base64Input can not be used simultaneously";
562 return false;
563 }
564
565 if (!outputIsEmpty && base64Output.GetValue()) {
566 errorMsg_ = "--output and --base64Output can not be used simultaneously";
567 return false;
568 }
569
570 if (opModule.GetValue() && opCommonjs.GetValue()) {
571 errorMsg_ = "[--module] and [--commonjs] can not be used simultaneously";
572 return false;
573 }
574
575 if (opModule.GetValue()) {
576 scriptKind_ = es2panda::parser::ScriptKind::MODULE;
577 } else if (opCommonjs.GetValue()) {
578 scriptKind_ = es2panda::parser::ScriptKind::COMMONJS;
579 } else {
580 scriptKind_ = es2panda::parser::ScriptKind::SCRIPT;
581 }
582
583 std::string extension = inputExtension.GetValue();
584 if (!extension.empty()) {
585 if (VALID_EXTENSIONS.find(extension) == VALID_EXTENSIONS.end()) {
586 errorMsg_ = "Invalid extension (available options: js, ts, as, abc)";
587 return false;
588 }
589 }
590
591 bool isInputFileList = false;
592 if (!inputIsEmpty) {
593 std::string rawInput = inputFile.GetValue();
594 isInputFileList = rawInput[0] == PROCESS_AS_LIST_MARK;
595 std::string input = isInputFileList ? rawInput.substr(1) : rawInput;
596 sourceFile_ = input;
597 }
598
599 if (base64Output.GetValue()) {
600 compilerOutput_ = "";
601 } else if (!outputIsEmpty) {
602 compilerOutput_ = outputFile.GetValue();
603 } else if (outputIsEmpty && !inputIsEmpty) {
604 compilerOutput_ = RemoveExtension(util::Helpers::BaseName(sourceFile_)).append(".abc");
605 }
606
607 if (opMergeAbc.GetValue()) {
608 recordName_ = recordName.GetValue();
609 if (recordName_.empty()) {
610 recordName_ = compilerOutput_.empty() ? "Base64Output" :
611 RemoveExtension(util::Helpers::BaseName(compilerOutput_));
612 }
613 compilerOptions_.mergeAbc = opMergeAbc.GetValue();
614 }
615
616 if (opuseDefineSemantic.GetValue()) {
617 compilerOptions_.useDefineSemantic = opuseDefineSemantic.GetValue();
618 }
619
620 if (!inputIsEmpty) {
621 // common mode
622 auto inputAbs = panda::os::file::File::GetAbsolutePath(sourceFile_);
623 if (!inputAbs) {
624 std::cerr << "Failed to find file '" << sourceFile_ << "' during input file resolution" << std::endl
625 << "Please check if the file name is correct, the file exists at the specified path, "
626 << "and your project has the necessary permissions to access it." << std::endl;
627 return false;
628 }
629
630 auto fpath = inputAbs.Value();
631 if (isInputFileList) {
632 CollectInputFilesFromFileList(fpath, extension);
633 } else if (panda::os::file::File::IsDirectory(fpath)) {
634 CollectInputFilesFromFileDirectory(fpath, extension);
635 } else {
636 es2panda::SourceFile src(sourceFile_, recordName_, scriptKind_, GetScriptExtension(sourceFile_, extension));
637 src.isSourceMode = !IsAbcFile(sourceFile_, extension);
638 sourceFiles_.push_back(src);
639 }
640 } else if (!base64InputIsEmpty) {
641 // input content is base64 string
642 base64Input_ = ExtractContentFromBase64Input(base64Input.GetValue());
643 if (base64Input_.empty()) {
644 errorMsg_ = "The input string is not a valid base64 data";
645 return false;
646 }
647
648 es2panda::SourceFile src("", recordName_, es2panda::parser::ScriptKind::SCRIPT,
649 GetScriptExtensionFromStr(extension));
650 src.source = base64Input_;
651 sourceFiles_.push_back(src);
652 }
653
654 if (!outputProto.GetValue().empty()) {
655 compilerProtoOutput_ = outputProto.GetValue();
656 }
657
658 optLevel_ = opOptLevel.GetValue();
659 functionThreadCount_ = opFunctionThreadCount.GetValue();
660 fileThreadCount_ = opFileThreadCount.GetValue();
661 abcClassThreadCount_ = opAbcClassThreadCount.GetValue();
662 npmModuleEntryList_ = opNpmModuleEntryList.GetValue();
663
664 if (!opCacheFile.GetValue().empty()) {
665 ParseCacheFileOption(opCacheFile.GetValue());
666 }
667
668 if (opParseOnly.GetValue()) {
669 options_ |= OptionFlags::PARSE_ONLY;
670 }
671
672 if (opSizeStat.GetValue()) {
673 options_ |= OptionFlags::SIZE_STAT;
674 }
675
676 if (opSizePctStat.GetValue()) {
677 options_ |= OptionFlags::SIZE_PCT_STAT;
678 }
679
680 perfFile_ = "";
681 perfLevel_ = opPerfLevel.GetValue();
682 if (opPerfFile.WasSet() && (perfLevel_ == 0)) {
683 perfFile_ = opPerfFile.GetValue().empty() ? opPerfFile.GetDefaultValue() : opPerfFile.GetValue();
684 }
685 panda::Timer::InitializeTimer(perfFile_);
686
687 compilerOptions_.recordDebugSource = opRecordDebugSource.GetValue();
688 compilerOptions_.enableAbcInput = opEnableAbcInput.GetValue();
689 compilerOptions_.dumpAsmProgram = opDumpAsmProgram.GetValue();
690 compilerOptions_.dumpNormalizedAsmProgram = opDumpNormalizedAsmProgram.GetValue();
691 compilerOptions_.dumpAsm = opDumpAssembly.GetValue();
692 compilerOptions_.dumpAst = opDumpAst.GetValue();
693 compilerOptions_.dumpTransformedAst = opDumpTransformedAst.GetValue();
694 compilerOptions_.checkTransformedAstStructure = opCheckTransformedAstStructure.GetValue();
695 compilerOptions_.dumpDebugInfo = opDumpDebugInfo.GetValue();
696 compilerOptions_.isDebug = opDebugInfo.GetValue();
697 compilerOptions_.parseOnly = opParseOnly.GetValue();
698 compilerOptions_.enableTypeCheck = opEnableTypeCheck.GetValue();
699 compilerOptions_.dumpLiteralBuffer = opDumpLiteralBuffer.GetValue();
700 compilerOptions_.isDebuggerEvaluateExpressionMode = debuggerEvaluateExpression.GetValue();
701
702 compilerOptions_.functionThreadCount = functionThreadCount_;
703 compilerOptions_.fileThreadCount = fileThreadCount_;
704 compilerOptions_.abcClassThreadCount = abcClassThreadCount_;
705 compilerOptions_.output = compilerOutput_;
706 compilerOptions_.debugInfoSourceFile = sourceFile.GetValue();
707 compilerOptions_.optLevel = (compilerOptions_.isDebug || !base64Input.GetValue().empty() ||
708 base64Output.GetValue()) ? 0 : opOptLevel.GetValue();
709 compilerOptions_.sourceFiles = sourceFiles_;
710 compilerOptions_.mergeAbc = opMergeAbc.GetValue();
711 compilerOptions_.compileContextInfoPath = compileContextInfoPath.GetValue();
712 if (!compileContextInfoPath.GetValue().empty()) {
713 ParseCompileContextInfo(compileContextInfoPath.GetValue());
714 }
715 compilerOptions_.dumpDepsInfo = opDumpDepsInfo.GetValue();
716 compilerOptions_.updatePkgVersionForAbcInput = compilerOptions_.enableAbcInput &&
717 (!compilerOptions_.compileContextInfo.pkgContextInfo.empty() ||
718 !compilerOptions_.compileContextInfo.updateVersionInfo.empty());
719 compilerOptions_.removeRedundantFile = opRemoveRedundantFile.GetValue();
720 compilerOptions_.dumpString = opDumpString.GetValue();
721 compilerOptions_.moduleRecordFieldName = moduleRecordFieldName.GetValue();
722
723 compilerOptions_.patchFixOptions.dumpSymbolTable = opDumpSymbolTable.GetValue();
724 compilerOptions_.patchFixOptions.symbolTable = opInputSymbolTable.GetValue();
725
726 if (!srcPkgName.GetValue().empty() && !dstPkgName.GetValue().empty()) {
727 compilerOptions_.modifiedPkgName = srcPkgName.GetValue() + util::COLON_SEPARATOR + dstPkgName.GetValue();
728 }
729
730 bool generatePatch = opGeneratePatch.GetValue();
731 bool hotReload = opHotReload.GetValue();
732 bool coldReload = opColdReload.GetValue();
733 bool coldFix = opColdFix.GetValue();
734 if (generatePatch && hotReload) {
735 errorMsg_ = "--generate-patch and --hot-reload can not be used simultaneously";
736 return false;
737 }
738 if (coldFix && !generatePatch) {
739 errorMsg_ = "--cold-fix can not be used without --generate-patch";
740 return false;
741 }
742 compilerOptions_.patchFixOptions.generatePatch = generatePatch;
743 compilerOptions_.patchFixOptions.hotReload = hotReload;
744 compilerOptions_.patchFixOptions.coldReload = coldReload;
745 compilerOptions_.patchFixOptions.coldFix = coldFix;
746
747 compilerOptions_.enableAnnotations = enableAnnotations.GetValue();
748
749 bool transformLibIsEmpty = transformLib.GetValue().empty();
750 if (!transformLibIsEmpty) {
751 auto libName = transformLib.GetValue();
752 // check file exist or not
753 auto transformLibAbs = panda::os::file::File::GetAbsolutePath(libName);
754 if (!transformLibAbs) {
755 std::cerr << "Failed to find file '" << libName << "' during transformLib file resolution" << std::endl
756 << "Please check if the file name is correct, the file exists at the specified path, "
757 << "and your project has the necessary permissions to access it." << std::endl;
758 return false;
759 }
760 compilerOptions_.transformLib = transformLibAbs.Value();
761 }
762
763 compilerOptions_.branchElimination = opBranchElimination.GetValue();
764 compilerOptions_.requireGlobalOptimization = compilerOptions_.optLevel > 0 &&
765 compilerOptions_.branchElimination &&
766 compilerOptions_.mergeAbc;
767 panda::compiler::options.SetCompilerBranchElimination(compilerOptions_.branchElimination);
768 panda::bytecodeopt::options.SetSkipMethodsWithEh(!opOptTryCatchFunc.GetValue());
769
770 return true;
771 }
772
ExtractContentFromBase64Input(const std::string & inputBase64String)773 std::string Options::ExtractContentFromBase64Input(const std::string &inputBase64String)
774 {
775 std::string inputContent = util::Base64Decode(inputBase64String);
776 if (inputContent == "") {
777 return "";
778 }
779 bool validBase64Input = util::Base64Encode(inputContent) == inputBase64String;
780 if (!validBase64Input) {
781 return "";
782 }
783 return inputContent;
784 }
785 } // namespace panda::es2panda::aot
786