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