* Copyright (c) 2022-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "options.h"
#include "util/diagnostic.h"
#include "util/diagnosticEngine.h"
#include "util/ustring.h"
#include "libarkbase/os/filesystem.h"
#include "libarkbase/utils/pandargs.h"
#include "arktsconfig.h"
#include "generated/diagnostic.h"
#include <random>
#include <unordered_set>
#include <utility>
#ifdef PANDA_WITH_BYTECODE_OPTIMIZER
#include "bytecode_optimizer/bytecodeopt_options.h"
#include "compiler/compiler_options.h"
#endif
#ifdef ES2PANDA_COMPILE_BY_GN
#include "generated/es2panda_build_info.h"
#endif
namespace ark::es2panda::util {
static std::string Usage(const ark::PandArgParser &argparser)
{
std::stringstream ss;
ss << argparser.GetErrorString() << std::endl;
ss << "Usage: es2panda [OPTIONS] [input file]" << std::endl;
ss << std::endl;
ss << "optional arguments:" << std::endl;
ss << argparser.GetHelpString() << std::endl;
ss << std::endl;
return ss.str();
}
static std::string GetVersion()
{
std::stringstream ss;
ss << std::endl;
ss << " Es2panda Version " << ES2PANDA_VERSION << std::endl;
#ifdef ES2PANDA_DATE
ss << std::endl;
ss << " Build date: ";
ss << ES2PANDA_DATE;
#endif
#ifdef ES2PANDA_HASH
ss << std::endl;
ss << " Last commit hash: ";
ss << ES2PANDA_HASH;
ss << std::endl;
#endif
return ss.str();
}
template <typename T>
bool Options::CallPandArgParser(const std::vector<std::string> &args, T &options,
util::DiagnosticEngine &diagnosticEngine)
{
ark::PandArgParser parser;
options.AddOptions(&parser);
if (!parser.Parse(args)) {
diagnosticEngine.LogFatalError(parser.GetErrorString());
std::cerr << parser.GetHelpString();
return false;
}
if (auto optionsErr = options.Validate(); optionsErr) {
diagnosticEngine.LogFatalError(optionsErr.value().GetMessage());
return false;
}
return true;
}
bool Options::CallPandArgParser(const std::vector<std::string> &args)
{
ark::PandArgParser parser;
AddOptions(&parser);
parser.PushBackTail(&inputFile_);
parser.EnableTail();
parser.EnableRemainder();
if (!parser.Parse(args) || IsHelp()) {
std::cerr << Usage(parser);
return false;
}
if (auto optionsErr = Validate(); optionsErr) {
diagnosticEngine_.LogFatalError(optionsErr.value().GetMessage());
return false;
}
return true;
}
static std::tuple<std::string_view, std::string_view, std::string_view> SplitPath(std::string_view path)
{
std::string_view fileDirectory;
std::string_view fileBaseName = path;
auto lastDelimPos = fileBaseName.find_last_of(ark::os::file::File::GetPathDelim());
if (lastDelimPos != std::string_view::npos) {
++lastDelimPos;
fileDirectory = fileBaseName.substr(0, lastDelimPos);
fileBaseName = fileBaseName.substr(lastDelimPos);
}
std::string_view fileExtensions;
auto fileBaseNamePos = fileBaseName.find_first_of('.');
if (fileBaseNamePos > 0 && fileBaseNamePos != std::string_view::npos) {
fileExtensions = fileBaseName.substr(fileBaseNamePos);
fileBaseName = fileBaseName.substr(0, fileBaseNamePos);
}
return {fileDirectory, fileBaseName, fileExtensions};
}
* @brief Generate evaluated expression wrapping code.
* @param sourceFilePath used for generating a unique package name.
* @param input expression source code file stream.
* @param output stream for generating expression wrapper.
*/
static void GenerateEvaluationWrapper(std::string_view sourceFilePath, std::ifstream &input, std::stringstream &output)
{
static constexpr std::string_view EVAL_PREFIX = "eval_";
static constexpr std::string_view EVAL_SUFFIX = "_eval";
auto splittedPath = SplitPath(sourceFilePath);
auto fileBaseName = std::get<1>(splittedPath);
std::random_device rd;
std::stringstream ss;
ss << EVAL_PREFIX << fileBaseName << '_' << rd() << EVAL_SUFFIX;
auto methodName = ss.str();
output << "package " << methodName << "; class " << methodName << " { private static " << methodName << "() { "
<< input.rdbuf() << " } }";
}
bool Options::ParseInputOutput()
{
auto isDebuggerEvalMode = IsDebuggerEval();
if (isDebuggerEvalMode && compilationMode_ != CompilationMode::SINGLE_FILE) {
diagnosticEngine_.LogDiagnostic(diagnostic::EVAL_MODE_NOT_SINGLE_INPUT, DiagnosticMessageParams {});
return false;
}
if (compilationMode_ == CompilationMode::SINGLE_FILE || GetExtension() != ScriptExtension::ETS) {
std::ifstream inputStream(SourceFileName());
if (inputStream.fail()) {
diagnosticEngine_.LogDiagnostic(diagnostic::OPEN_FAILED,
util::DiagnosticMessageParams {util::StringView(SourceFileName())});
return false;
}
std::stringstream ss;
if (isDebuggerEvalMode) {
GenerateEvaluationWrapper(SourceFileName(), inputStream, ss);
} else {
ss << inputStream.rdbuf();
}
parserInputContents_ = ss.str();
}
if (WasSetOutput()) {
if (compilationMode_ == CompilationMode::PROJECT) {
diagnosticEngine_.LogDiagnostic(diagnostic::PROJ_COMP_WITH_OUTPUT, DiagnosticMessageParams {});
return false;
}
} else if (!SourceFileName().empty()) {
SetOutput(ark::os::RemoveExtension(BaseName(SourceFileName())).append(".abc"));
}
return true;
}
bool Options::Parse(Span<const char *const> args)
{
std::vector<std::string> es2pandaArgs;
auto argc = args.size();
for (size_t i = 1; i < argc; i++) {
es2pandaArgs.emplace_back(args[i]);
}
if (!CallPandArgParser(es2pandaArgs)) {
return false;
}
if (IsVersion()) {
std::cerr << GetVersion();
return false;
}
#ifdef PANDA_WITH_BYTECODE_OPTIMIZER
if ((WasSetBcoCompiler() && !CallPandArgParser(GetBcoCompiler(), ark::compiler::g_options, diagnosticEngine_)) ||
(WasSetBcoOptimizer() &&
!CallPandArgParser(GetBcoOptimizer(), ark::bytecodeopt::g_options, diagnosticEngine_))) {
return false;
}
#endif
DetermineCompilationMode();
if (!DetermineExtension()) {
return false;
}
if (!ParseInputOutput()) {
return false;
}
if (extension_ != ScriptExtension::JS && IsModule()) {
diagnosticEngine_.LogDiagnostic(diagnostic::MODULE_INVALID_EXT, DiagnosticMessageParams {});
return false;
}
if ((WasSetDumpEtsSrcBeforePhases() || WasSetDumpEtsSrcAfterPhases()) && extension_ != ScriptExtension::ETS) {
diagnosticEngine_.LogDiagnostic(diagnostic::DUMP_ETS_INVALID_EXT, DiagnosticMessageParams {});
return false;
}
if (WasSetLogLevel()) {
logLevel_ = Logger::LevelFromString(GetLogLevel());
}
InitCompilerOptions();
return ProcessEtsSpecificOptions();
}
void Options::InitForEval()
{
compilationMode_ = CompilationMode::SINGLE_FILE;
extension_ = ScriptExtension::JS;
}
auto VecToSet(const std::vector<std::string> &v)
{
return std::set<std::string>(v.begin(), v.end());
}
void Options::InitAstVerifierOptions()
{
auto initSeverity = [](std::array<bool, gen::ast_verifier::COUNT> *a, const std::vector<std::string> &v) {
for (const auto &str : v) {
(*a)[gen::ast_verifier::FromString(str)] = true;
}
};
initSeverity(&verifierWarnings_, gen::Options::GetAstVerifierWarnings());
initSeverity(&verifierErrors_, gen::Options::GetAstVerifierErrors());
astVerifierPhases_ = VecToSet(gen::Options::GetAstVerifierPhases());
if (HasVerifierPhase("before")) {
astVerifierBeforePhases_ = true;
}
if (HasVerifierPhase("each")) {
astVerifierEachPhase_ = true;
}
if (HasVerifierPhase("after")) {
astVerifierAfterPhases_ = true;
}
}
void Options::InitCompilerOptions()
{
skipPhases_ = VecToSet(gen::Options::GetSkipPhases());
dumpBeforePhases_ = VecToSet(gen::Options::GetDumpBeforePhases());
dumpEtsSrcBeforePhases_ = VecToSet(gen::Options::GetDumpEtsSrcBeforePhases());
dumpAfterPhases_ = VecToSet(gen::Options::GetDumpAfterPhases());
dumpEtsSrcAfterPhases_ = VecToSet(gen::Options::GetDumpEtsSrcAfterPhases());
InitAstVerifierOptions();
if (IsEtsWarnings()) {
InitializeWarnings();
}
}
void Options::InitializeWarnings()
{
if (WasSetEtsWarningsBasePath()) {
diagnosticEngine_.SetBasePath(GetEtsWarningsBasePath());
}
if (WasSetEtsWarningsDiagnosticFormat() && GetEtsWarningsDiagnosticFormat() == "build-system") {
diagnosticEngine_.SetPrinter(std::make_unique<CustomDiagnosticPrinter>());
}
std::array<bool, ETSWarnings::COUNT> warningSet {};
ES2PANDA_ASSERT(ETSWarnings::LAST < ETSWarnings::COUNT);
const auto processWarningList = [&warningSet](const auto &list, bool v) {
static const std::map<std::string_view, std::pair<size_t, size_t>> WARNING_GROUPS {
{"subset_aware", {ETSWarnings::SUBSET_AWARE_FIRST, ETSWarnings::SUBSET_AWARE_LAST}},
{"subset_unaware", {ETSWarnings::SUBSET_UNAWARE_FIRST, ETSWarnings::SUBSET_UNAWARE_LAST}}};
const auto setWarningRange = [&warningSet, v](size_t first, size_t last) {
ES2PANDA_ASSERT(last < ETSWarnings::COUNT);
for (size_t i = first; i <= last; i++) {
warningSet[i] = v;
}
};
for (const auto &warningOrGroup : list) {
if (WARNING_GROUPS.find(warningOrGroup) != WARNING_GROUPS.end()) {
auto [first, last] = WARNING_GROUPS.at(warningOrGroup);
setWarningRange(first, last);
continue;
}
ES2PANDA_ASSERT(ets_warnings::FromString(warningOrGroup) != ETSWarnings::INVALID);
warningSet[ets_warnings::FromString(warningOrGroup)] = v;
}
};
processWarningList(GetEtsWarningsEnable(), true);
processWarningList(GetEtsWarningsDisable(), false);
for (size_t i = ETSWarnings::FIRST; i <= ETSWarnings::LAST; i++) {
if (warningSet[i]) {
etsWarningCollection_.emplace_back(static_cast<ETSWarnings>(i));
}
}
}
bool Options::DetermineExtension()
{
if (compilationMode_ == CompilationMode::PROJECT) {
if (WasSetExtension() && gen::Options::GetExtension() != "ets") {
diagnosticEngine_.LogDiagnostic(diagnostic::PROJECT_EXT_NOT_ETS, DiagnosticMessageParams {});
return false;
}
extension_ = ScriptExtension::ETS;
return true;
}
std::string sourceFileExtension = SourceFileName().substr(SourceFileName().find_last_of('.') + 1);
if (!SourceFileName().empty() && WasSetExtension() && gen::Options::GetExtension() != sourceFileExtension) {
diagnosticEngine_.LogDiagnostic(diagnostic::EXTENSION_MISMATCH,
DiagnosticMessageParams {std::string_view(sourceFileExtension),
std::string_view(gen::Options::GetExtension())});
}
sourceFileExtension = sourceFileExtension == "ets" ? "ets" : sourceFileExtension;
auto tempExtension = WasSetExtension() ? gen::Options::GetExtension() : sourceFileExtension;
if (gen::extension::FromString(tempExtension) == ScriptExtension::INVALID) {
diagnosticEngine_.LogDiagnostic(diagnostic::UNKNOWN_EXT, DiagnosticMessageParams {});
return false;
}
extension_ = gen::extension::FromString(tempExtension);
switch (extension_) {
#ifndef PANDA_WITH_ECMASCRIPT
case ScriptExtension::JS: {
diagnosticEngine_.LogDiagnostic(diagnostic::JS_UNSUPPORTED, util::DiagnosticMessageParams {});
return false;
}
#endif
case ScriptExtension::ETS: {
std::ifstream inputStream(GetArktsconfig());
if (inputStream.fail()) {
diagnosticEngine_.LogDiagnostic(diagnostic::OPEN_FAILED_ARKTSCONF,
util::DiagnosticMessageParams {GetArktsconfig()});
return false;
}
return true;
}
default:
return true;
}
}
bool Options::ProcessEtsSpecificOptions()
{
if (GetExtension() != ScriptExtension::ETS) {
return true;
}
if (auto config = ParseArktsConfig(); config != std::nullopt) {
arktsConfig_ = std::move(*config);
return true;
}
return false;
}
std::optional<std::unique_ptr<ArkTsConfig>> Options::ParseArktsConfig()
{
auto config = std::make_unique<ArkTsConfig>(GetArktsconfig(), diagnosticEngine_);
if (!config->Parse()) {
diagnosticEngine_.LogDiagnostic(diagnostic::INVALID_ARKTSCONFIG,
util::DiagnosticMessageParams {util::StringView(GetArktsconfig())});
return std::nullopt;
}
if (WasSetStdlib()) {
config->FixupWithStdlibOption(GetStdlib());
}
config->FixupWithoutStdlibOption();
config->ResolveAllDependenciesInArkTsConfig();
config->GenerateSourcePathMap();
return config;
}
}