use clap::{Args, Parser, Subcommand};
use std::path::PathBuf;
pub const SUBCOMMANDS: &[&str] = &[
"info",
"strings",
"functions",
"disasm",
"decompile",
"ro-map",
"help",
"version",
"debug-parse",
];
pub const SUBCOMMAND_HELP: &[(&str, &str, &str)] = &[
(
"decompile",
"reconstruct JavaScript (default action: `jscd <INPUT>`)",
"重建 JavaScript(默认动作,等价于直接 `jscd <输入>`)",
),
(
"info",
"header fields and the detected Node/V8 version",
"头部字段与识别出的 Node/V8 版本",
),
(
"strings",
"string and symbol constants from the constant pools",
"常量池里的字符串与符号",
),
(
"functions",
"SharedFunctionInfo tree (names, params, bytecode sizes)",
"SharedFunctionInfo 树(名字、形参、字节码长度)",
),
(
"disasm",
"bytecode disassembly (View8-compatible text)",
"字节码反汇编(View8 兼容文本)",
),
(
"ro-map",
"build a read-only-heap name table from a probe cache",
"从探针缓存生成只读堆名表",
),
(
"help",
"print help (`jscd help <SUBCOMMAND>`)",
"打印帮助(可跟子命令名)",
),
(
"version",
"print name, version and repository",
"打印名字、版本与仓库地址",
),
];
#[derive(Args, Debug)]
pub struct Common {
#[arg(short, long)]
pub output: Option<PathBuf>,
#[arg(long)]
pub quiet: bool,
}
#[derive(Parser, Debug)]
#[command(
name = "jscd",
disable_help_flag = true,
disable_version_flag = true,
about = "Reverse bytenode-compiled .jsc (V8 code cache) back to JavaScript"
)]
pub struct Cli {
#[arg(long, global = true)]
pub json: bool,
#[command(subcommand)]
pub cmd: Cmd,
}
#[derive(Subcommand, Debug)]
pub enum Cmd {
Info {
file: PathBuf,
#[command(flatten)]
common: Common,
},
Strings {
file: PathBuf,
#[command(flatten)]
common: Common,
},
Functions {
file: PathBuf,
#[command(flatten)]
common: Common,
},
Disasm {
file: PathBuf,
#[command(flatten)]
common: Common,
#[arg(long)]
filter: Option<String>,
},
Decompile {
file: PathBuf,
output: Option<PathBuf>,
#[command(flatten)]
common: Common,
#[arg(long = "ro-map")]
ro_map: Option<PathBuf>,
#[arg(long)]
verify: bool,
#[arg(long)]
runtime: bool,
},
RoMap {
file: PathBuf,
#[command(flatten)]
common: Common,
#[arg(long)]
names: Option<PathBuf>,
},
#[command(hide = true)]
DebugParse {
file: PathBuf,
},
}
#[derive(Parser, Debug)]
#[command(
name = "jscd",
disable_help_flag = true,
disable_version_flag = true,
about = "Reverse bytenode-compiled .jsc (V8 code cache) back to JavaScript"
)]
pub struct Direct {
#[arg(long)]
pub json: bool,
#[arg(short, long)]
pub output: Option<PathBuf>,
#[arg(long = "ro-map")]
pub ro_map: Option<PathBuf>,
#[arg(long)]
pub verify: bool,
#[arg(long)]
pub runtime: bool,
#[arg(long)]
pub quiet: bool,
pub input: PathBuf,
pub dest: Option<PathBuf>,
}
impl Direct {
pub fn resolved_output(&self) -> Result<Option<PathBuf>, String> {
match (&self.output, &self.dest) {
(Some(a), Some(b)) if a != b => Err(crate::bif!(
"-o {0} conflicts with the OUTPUT argument {1}",
"-o {0} 与位置参数 OUTPUT {1} 冲突";
a.display(),
b.display()
)),
(Some(a), _) => Ok(Some(a.clone())),
(None, Some(b)) => Ok(Some(b.clone())),
(None, None) => Ok(None),
}
}
}
pub fn localize_clap_error(err: &clap::Error) -> String {
use clap::error::ErrorKind as K;
let raw = err.to_string();
let token = raw.split('\'').nth(1).unwrap_or("").to_string();
let hint = crate::bi!("see `jscd --help`", "用法见 `jscd --help`");
match err.kind() {
K::UnknownArgument => crate::bif!(
"unknown option '{0}' — {1}",
"未知选项 '{0}' —— {1}";
token,
hint
),
K::InvalidSubcommand => crate::bif!(
"unknown subcommand '{0}' — {1}",
"未知子命令 '{0}' —— {1}";
token,
hint
),
K::MissingRequiredArgument => {
crate::bif!("missing argument: {0} — {1}", "缺少参数:{0} —— {1}"; raw.trim(), hint)
}
K::InvalidValue | K::ValueValidation => crate::bif!(
"invalid value for '{0}' — {1}",
"'{0}' 的取值不合法 —— {1}";
token,
hint
),
K::TooManyValues | K::WrongNumberOfValues => crate::bif!(
"too many arguments: {0} — {1}",
"参数过多:{0} —— {1}";
raw.trim(),
hint
),
_ => raw.trim().to_string(),
}
}