
bytenode の .jsc(V8 コードキャッシュ)を JavaScript に逆変換 — 静的、純粋な Rust、パッチ済み V8/Node 不要。Node 8→26 / V8 5.8–14.6、25k の .jsc をテスト済み、失敗 0。
bytenodeでコンパイルされた.jscファイルをJavaScriptに逆コンパイルします。
静的解析のみ:純粋なRustバイナリ1つ — パッチ済みV8も、Nodeランタイムも、ネットワークも不要。
Node 8.0.0 → 26.10.0(510リリース / 36 V8マイナー)をサポート;約25kの.jscファイルが
テストを0件の失敗で通過(31のNode系列 × 41のフィクスチャ、bytenode 1.7.0のフラグでコンパイル);2つの
最適化レイヤー(レジスタ畳み込み → swcコピー伝播)により、逆コンパイルされたソースが読みやすくなります。
English · 中文
目次: ハイライト · クイックスタート · インストール · 使い方 · CLIリファレンス · 対応バージョン · 仕組み · 検証 · リポジトリ構成 · 制限事項 · コントリビューション
--runtimeは__runtimeスタブを追加するため、結果はnodeで実行できます;
--verifyはnode --checkを通します。JSCD_NO_OPT=1で生の形式を表示します。ReferenceErrorを投げる識別子は決してありません。$ cat hello.js
function greet(name) {
return "hello " + name;
}
console.log(greet("world"));
$ npm i -g bytenode && bytenode -c hello.js # Node 20.20.2 in this transcript
$ xxd -l 48 hello.jsc
00000000: cc05 dec0 0bc2 e400 9200 0000 e521 2eaa .............!..
00000010: f002 0000 0000 0000 011c 5401 2006 a860 ..........T. ..`
00000020: 0000 0000 0600 0000 010c 4c60 0000 0000 ..........L`....
$ jscd hello.jsc
console.log(greet("world"));
function greet(a0) {
return "hello " + a0;
}
$ jscd hello.jsc --runtime > hello.out.js && node hello.out.js
hello world
先頭のバイト列はリトルエンディアンのコードキャッシュマジックです(cc 05 de c0 = 0xc0de05cc)。
jscd info hello.jsc はヘッダー全体をデコードします(サンプルは使用法を参照)。
上記の出力は、両方の最適化レイヤーを経た後に得られるものです。JSCD_NO_OPT=1 jscd hello.jsc
は代わりに生の変換結果を表示します — V8 のレジスタシャッフルを、1命令ずつ示します。
Node 22+(V8 12.4+)では、一部の組み込みプロパティ名が読み取り専用ヒープに存在します。動作マトリクスが対象とする29の V8
マイナーバージョン向けに、jscd は名前テーブル(tables/ro_map_*)を同梱し、
それらを自動的に解決します — macOS 上で、それらのテーブルが抽出された場所です。読み取り専用ヒープのインデックスは
プラットフォーム固有であるため、他のシステムでは jscd ro-map で独自にビルドし、--ro-map を渡してください
(JSCD_NO_RO_MAP=1 は埋め込みテーブルを無効にします)。テーブルがない場合、そのような名前は <ro0_…>
プレースホルダーとして表示されます — 決して誤った名前としては表示されません。
macOS / Linux — Homebrew
brew install ejfkdev/tap/jscd
Windows — Scoop
scoop bucket add ejfkdev https://github.com/ejfkdev/scoop-bucket
scoop install jscd
マニフェストURLから直接、バケットを先に追加せずに:
scoop install https://raw.githubusercontent.com/ejfkdev/scoop-bucket/main/bucket/jscd.json
scoop install ejfkdev/scoop-bucket/jscdは 動作しません: Scoop はbucket/appを すでに追加済みのバケットに対して解決し、owner/repoパスに対しては解決しません。
ビルド済みバイナリ — タグ付けされたすべてのリリースには、そのまま実行可能な 素の実行ファイルが同梱されています (Linux / macOS / Windows × x64 / arm64; Linux amd64 は静的 musl ビルド、Linux と Windows は UPX 圧縮):
リリースページから直接入手してください (アーカイブなし、インストーラーなし — そのファイルが 実行ファイルそのもの です):
curl -fLO https://github.com/ejfkdev/jscd/releases/download/v0.1.0/jscd-v0.1.0-linux-amd64
chmod +x jscd-v0.1.0-linux-amd64
./jscd-v0.1.0-linux-amd64 --version
cargo(Rust 1.96+ が動作する任意のプラットフォーム)
cargo install jscd # build from crates.io
cargo binstall jscd # or fetch the release binary instead of compiling (cargo-binstall)
ソースから
cargo install --git https://github.com/ejfkdev/jscd # latest main
git clone https://github.com/ejfkdev/jscd && cd jscd
cargo build --release # -> target/release/jscd
cargo install --path . # ...or install that build into ~/.cargo/bin
Rust 1.96+ が必要です(swc、JS オプティマイザーは最近の rustc を必要とします)。システム依存関係はありません。
scripts/release.sh vX.Y.Z はリリースゲート(cargo test --release、
clippy --all-targets -- -D warnings、およびエンドツーエンドのスモークテスト scripts/ci_smoke.sh — フィクスチャを実際の .jsc にコンパイルし、デコンパイルし、構文チェックと実行比較を行う)を実行し、その後、メッセージが GitHub Release の説明となる 注釈付き タグをプッシュします。このタグが
.github/workflows/release.yml をトリガーし、上記の 6 つのベアバイナリを再ビルドします。次に
crates.io へ cargo publish し(release.sh はタグと Cargo.toml のバージョンを同期させます)、
python3 scripts/update_readme_help.py で以下の CLI ヘルプを更新します。
Homebrew tap と
Scoop bucket は、毎日の自動更新実行で新しいリリースを取得します。
jscd app.jsc # decompile one file to stdout
jscd app.jsc app.js # ...to a file
jscd dist/ # every .jsc under dist/ → dist-out/ (tree mirrored)
jscd dist/ out/ # ...into out/ instead
jscd info app.jsc # header fields, detected Node/V8 version
jscd disasm --filter main app.jsc
jscd ro-map probe.jsc > m.json # build a read-only-heap name table
jscd --help # bilingual help (-h, `help`, `help <SUBCOMMAND>`)
.jsc ファイル、または *.jsc を再帰的にスキャンするディレクトリです。出力はデフォルトで、ファイルの場合は stdout、ディレクトリの場合は <INPUT>-out/(ツリーをミラーリング)になります — -o / OUTPUT 引数で別の場所に書き出し、- は stdout を意味します。.jsc は、検出されたバージョンとサポート範囲をメッセージに含めて拒否されます(jscd info は supported: yes|no を出力します)— 決してサイレントに、実行時のみのファイルとして扱われることはありません。jscd ro-map でテーブルを構築し、--ro-map で渡してください(JSCD_NO_RO_MAP=1 は埋め込みテーブルを無効にします)。不一致のテーブルは無視されます。<ro0_…> プレースホルダーが得られ、誤った名前になることはありません。JSCD_LANG(次に LC_ALL / LC_MESSAGES / LANGUAGE / LANG / LC_CTYPE)に従います。zh* は中国語、それ以外は英語です。JSCD_LANG=zh|en で強制できます。| フラグ | 適用対象 | 効果 |
|---|---|---|
-o, --output <PATH> | すべての形式 | 出力ファイル / ディレクトリ / -(OUTPUT 引数と同じ) |
--quiet | ディレクトリ入力 | ファイルごとの進捗表示を抑制 |
--json | すべてのサブコマンド | 機械可読な出力 |
--filter <SUBSTR> | disasm | 名前に SUBSTR を含む関数のみを出力 |
--ro-map <PATH> | decompile | jscd ro-map が生成した読み取り専用ヒープ名テーブル |
--verify | decompile | 構文ゲート: 結果を node --check で実行(PATH に node が必要) |
--runtime | decompile | 実行可能なプリアンブル(__runtime スタブ、フラット化された context 変数、名前エイリアス)を保持します。デフォルトではオフ — node で生成物を実行したい場合に追加してください |
-h, --help | すべての形式 | ヘルプを表示(1つのコマンドについては jscd help <SUBCOMMAND>) |
-v, -V, --version | すべての形式 | 名前、バージョン、リポジトリを表示 |
CLI が --help および help <SUBCOMMAND> で出力するすべての内容をそのまま記載します(python3 scripts/update_readme_help.py で再生成)。
jscd --help とすべてのサブコマンドをそのまま記載jscd --help
$ jscd --help
jscd v0.1.0 — V8 code cache → JavaScript
https://github.com/ejfkdev/jscd (MIT license)
Reconstructs JavaScript from bytenode-compiled .jsc files (V8 code caches).
Static parsing — no patched V8, no Node runtime. Directories are scanned
recursively.
Usage: jscd [OPTIONS] <INPUT> [OUTPUT] # decompile (default action)
jscd <SUBCOMMAND> [ARGS...] # stage-by-stage analysis
jscd help [SUBCOMMAND] | version | -h | -v | -V