Jelly(JavaScript / TypeScript の静的コールグラフ解析ツール)
Aarhus 大学の研究ツール。field-based の points-to 解析でコールグラフを作り、TypeScript は Babel で型を剥がして ECMAScript として扱う。公式にライブラリ API はなく CLI と jelly-server だけで、意図的に unsound と明記。依存ごと解析すると 8 GB 級のヒープが要る。
調査時点: 2026-06(v0.13.0、2026-05-11 リリース)。TypeScript のコールグラフ抽出ツールを自作するか既存ツールを使うかを判断する材料として調べたもの。
概要
- 開発元は Aarhus 大学(Anders Møller ら)。ライセンスは BSD-3-Clause
npm install -g @cs-au-dk/jellyで入る。Node.js 22 以上が必要- 出力は
-j cg.json(JSON)、-m cg.html(可視化 HTML)、--compare-callgraphs(動的コールグラフとの比較) - リリースは 2023-02 の v0.5.0 から 2〜4 か月に 1 回。GitHub の open issue は 3 件で、実用規模のユーザー報告は少ない
- 関連論文: ACG(ICSE 2013)、TAPIR(OOPSLA 2020)、JAM(ISSTA 2021)、Approximate Interpretation(PLDI 2024)、Indirection-Bounding(ECOOP 2024)
セットアップ要件
- README は
NODE_OPTIONS=--max-old-space-size=8192を例として示す。Linux ではvm.max_map_countの引き上げ(sysctl -w vm.max_map_count=524288)も案内されており、依存関係ごと解析するとヒープを大量に消費する - 入力はエントリーファイルかディレクトリ。
tsconfig.jsonは要らない --ignore-dependenciesを付けないと、エントリーから到達できる node_modules 内のファイルも解析する。依存込みで解析するならインストール済みの node_modules が実質必要- 対象拡張子は
.js/.es/.mjs/.cjs/.ts/.tsxと shebang 付きファイル。.d.tsと minify / bundle 済みファイルは除外(node_modules 内の minified は含む) --max-indirections/--timeoutで途中終了し、部分結果(unsound(実際に起こりうる関係を解析結果が取りこぼしうる性質))を得られる- モノレポ / workspace への言及は README にない。
--include-packages/--exclude-packagesでパッケージ単位の絞り込みはできる
出力形式(-j cg.json)
src/typings/callgraph.ts の型定義に基づく構造。
entries : [string] // エントリーファイル(basedir 相対)files : [string] // インデックスがノード IDfunctions : { index → { file, start, end } }calls : { index → { file, start, end } } // コールサイトfun2fun : [[callerFn, calleeFn]]call2fun : [[callSite, calleeFn]]関数レベルの粒度で、コールサイトから関数へのエッジと関数から関数へのエッジを両方持つ。resolved / unresolved の区別や soundness のマーカーは型定義上存在しない。
jelly-server は stdin / stdout で Content-Length 区切りの JSON-RPC を話し、callgraph などのコマンドを受ける。
解析方式と TypeScript の扱い
分析の核は flow-insensitive・context-insensitive な field-based points-to(変数や参照がどのオブジェクトを指しうるかを調べる解析) 解析。TypeScript ファイルは @babel/parser でパースし、型注釈を落として ECMAScript として処理する。TypeScript の型情報(型ガード、条件型、decorator)は解析に使われない。
つまり「型で解決できるディスパッチ」も points-to で処理する。ts-morph のように TypeChecker から解決する方式とは得意領域が異なる。前者はコールバックやイベント経由の暗黙エッジを追う設計で、後者は明示的な呼び出しを型から確実に解決する設計になる(比較は TypeScript コールグラフの静的抽出)。
精度の公表値
| 出典 | 値 |
|---|---|
| ECOOP 2024(Indirection-Bounded) | indirection-bounded 変形で平均 2 倍高速化、recall 5% 減、precision ほぼ同等。25 本の大規模 Node.js + 10 本の Web + 4 本のモバイルで評価 |
PLDI 2024(Approximate Interpretation、--approx 有効時) | completeness 38.8%、soundness 67.4%、exact match 82.2%。--approx なしの baseline 値は本文未確認 |
| EMSE 2025(LLM との比較) | SWARM-JS ベンチマークで 24 の LLM を一貫して上回る |
| arXiv 2405.07206(静的 JS コールグラフ比較) | Jelly は比較対象に含まれていない |
completeness 38.8% という低さは、eval や動的プロパティアクセスなど ECMAScript の動的性質への対応限界を示す。
ライブラリとして組み込めるか
package.json に exports も main もなく、bin に jelly と jelly-server があるだけ。import { analyze } from '@cs-au-dk/jelly' に相当する公開 API は存在しない。
組み込みの選択肢は 2 つ。
jelly -j cg.jsonを子プロセスとして起動し、JSON を読むjelly-serverを起動し、stdin / stdout の JSON-RPC で話す
どちらも利用者に --max-old-space-size 付きの実行環境を求めることになる。nuanced-dev/jellycg(Nuanced 社のフォーク)は npm 公開の自動化と関数位置情報のトラッキング API を足していたが、2025-10 を最終コミットとして 2026-03-05 にアーカイブされた。
公式に認めている限界
- README が “intentionally not fully soundly” と明記。Node.js 標準ライブラリは unknown code として扱う
- 依存込みの解析は時間がかかる。
--max-indirections/--timeoutが回避策 npm run pkgで作るバイナリは NodeProf 連携の動的コールグラフ構築に対応しない- Node.js 20 以下では動かない
使いどころ
- 暗黙エッジ(コールバック・イベント経由)まで含めた recall を重視する解析、研究用途、動的コールグラフとの比較 → Jelly
- 明示的な呼び出しを型から確実に解決し、ライブラリとして自分のツールに組み込む → ts-morph で自作
関連
更新履歴
- 2026-06-20: 初版(ローカル調査ノート)
- 2026-10-17: Wiki へ移植、概念ページとして再構成
出典
- https://github.com/cs-au-dk/jelly
- https://github.com/cs-au-dk/jelly/blob/master/package.json
- https://github.com/cs-au-dk/jelly/blob/master/src/typings/callgraph.ts
- https://drops.dagstuhl.de/entities/document/10.4230/LIPIcs.ECOOP.2024.10
- https://dl.acm.org/doi/10.1145/3656424
- https://arxiv.org/abs/2410.00603
- https://github.com/nuanced-dev/jellycg