
Reversez un .jsc bytenode (cache de code V8) vers JavaScript — statique, pur Rust, sans V8/Node patché. Node 8→26 / V8 5.8–14.6 ; 25k .jsc testés, 0 échec.
Rétro-ingénierie des fichiers .jsc compilés par bytenode vers JavaScript.
Analyse statique uniquement : un binaire pur Rust — pas de V8 patché, pas de runtime Node, pas de réseau.
Prend en charge Node 8.0.0 → 26.10.0 (510 versions / 36 mineures V8) ; ≈ 25k fichiers .jsc passent les
tests avec 0 échec (31 lignes Node × 41 fixtures, compilés avec les flags de bytenode 1.7.0) ; deux
couches d'optimisation (repliement de registres → propagation de copies swc) rendent le code décompilé lisible.
English · 中文
Sommaire : Points forts · Démarrage rapide · Installation · Utilisation · Référence CLI · Versions prises en charge · Fonctionnement · Vérification · Organisation du dépôt · Limitations · Contribution
--runtime ajoute les stubs __runtime pour que le résultat s'exécute sous node ;
--verify le fait passer par node --check.JSCD_NO_OPT=1 affiche la forme brute.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
Les premiers octets sont la signature du cache de code en little-endian (cc 05 de c0 = 0xc0de05cc) ;
jscd info hello.jsc décode l'intégralité de l'en-tête (exemple dans Usage).
La sortie ci-dessus est ce qui ressort après les deux couches d'optimisation. JSCD_NO_OPT=1 jscd hello.jsc
montre la traduction brute à la place — les réarrangements de registres de V8, une instruction à la fois.
Sur Node 22+ (V8 12.4+), certains noms de propriétés intégrés résident dans le tas en lecture seule. Pour les 29 versions
mineures de V8 couvertes par la matrice de comportement, jscd fournit une table de noms (tables/ro_map_*) et les résout
automatiquement — sur macOS, où ces tables ont été extraites : les indices du tas en lecture seule sont
spécifiques à la plateforme, donc sur d'autres systèmes, construisez la vôtre avec jscd ro-map et passez --ro-map
(JSCD_NO_RO_MAP=1 désactive la table intégrée). Sans table, ces noms apparaissent sous forme de
placeholders <ro0_…> — jamais sous le mauvais nom.
macOS / Linux — Homebrew
brew install ejfkdev/tap/jscd
Windows — Scoop
scoop bucket add ejfkdev https://github.com/ejfkdev/scoop-bucket
scoop install jscd
Directement depuis l'URL du manifeste, sans ajouter d'abord le bucket :
scoop install https://raw.githubusercontent.com/ejfkdev/scoop-bucket/main/bucket/jscd.json
scoop install ejfkdev/scoop-bucket/jscdne fonctionne pas : Scoop résoutbucket/apppar rapport aux buckets que vous avez déjà ajoutés, et non par rapport à un cheminowner/repo.
Binaires précompilés — chaque release taguée fournit des exécutables bruts, prêts à l'emploi (Linux / macOS / Windows × x64 / arm64 ; Linux amd64 est une build musl statique, Linux et Windows sont compressés avec UPX) :
Récupérez-en un directement depuis la page de release (pas d'archive, pas d'installateur — le fichier est l'exécutable) :
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 (toute plateforme avec Rust 1.96+)
cargo install jscd # build from crates.io
cargo binstall jscd # or fetch the release binary instead of compiling (cargo-binstall)
Depuis la source
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
Nécessite Rust 1.96+ (swc, l'optimiseur JS, a besoin d'un rustc récent). Aucune dépendance système.
scripts/release.sh vX.Y.Z exécute les barrières de publication (cargo test --release,
clippy --all-targets -- -D warnings, et le smoke test de bout en bout scripts/ci_smoke.sh — fixtures
compilées en véritables .jsc, décompilées, validées syntaxiquement et comparées à l'exécution), puis pousse un tag annoté
dont le message devient la description de la GitHub Release. Le tag déclenche
.github/workflows/release.yml, qui reconstruit les six binaires nus listés ci-dessus. Ensuite
cargo publish vers crates.io (release.sh garde le tag et la version de Cargo.toml synchronisés), et
python3 scripts/update_readme_help.py pour rafraîchir l'aide CLI ci-dessous ; le
Homebrew tap et le
Scoop bucket récupèrent la nouvelle release lors de leur exécution
de mise à jour automatique quotidienne.
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>`)