feat: Add HTTP status tests and dynamic plugin documentation
- Add e2e_vm_http_status_404/500 tests to verify HTTP status handling - ResultBox properly returns Ok(Response) for HTTP errors, Err for connection failures - Create dynamic-plugin-flow.md documenting MIR→VM→Registry→Plugin flow - Add vm-stats test files for HTTP 404/500 status codes - Update net-plugin.md with HTTP error handling clarification - Create E2E_TESTS.md documenting all E2E test behaviors - Add mir-26-instruction-diet.md for MIR optimization plans - Add vm-stats-cookbook.md for VM statistics usage guide - Update MIR verifier to properly track self-assignment patterns 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -20,6 +20,11 @@
|
||||
- `src/box_factory/*` … Builtin/User/Plugin の各 Factory 実装
|
||||
- `src/runtime/plugin_loader_v2.rs` … BID-FFI v2 ローダ(ExternCall/Plugin 呼び出し)
|
||||
|
||||
関連ドキュメント
|
||||
- 動的プラグインの流れ: [dynamic-plugin-flow.md](./dynamic-plugin-flow.md)
|
||||
- 命令セットダイエット: [mir-26-instruction-diet.md](./mir-26-instruction-diet.md)
|
||||
- MIR→VMマッピング: [mir-to-vm-mapping.md](./mir-to-vm-mapping.md)
|
||||
|
||||
## 実行フロー(概略)
|
||||
1) Nyash コード → Parser → AST → `MirCompiler` で `MirModule` を生成
|
||||
2) `VM::with_runtime(runtime)` で実行(`execute_module`)
|
||||
@ -35,6 +40,7 @@
|
||||
- Builtin: `BuiltinBoxFactory` が直接生成
|
||||
- User-defined: `UserDefinedBoxFactory` → `InstanceBox`
|
||||
- Plugin: プラグイン設定(`nyash.toml`)に従い BID-FFI で `PluginBoxV2`
|
||||
- **動的解決の詳細**: [dynamic-plugin-flow.md](./dynamic-plugin-flow.md) を参照
|
||||
|
||||
## birth/メソッドの関数化(MIR)
|
||||
- Lowering ポリシー: AST の `new` は `NewBox` に続けて `BoxCall("birth")` を自動挿入
|
||||
|
||||
115
docs/reference/architecture/dynamic-plugin-flow.md
Normal file
115
docs/reference/architecture/dynamic-plugin-flow.md
Normal file
@ -0,0 +1,115 @@
|
||||
# Dynamic Plugin Flow (VM × Registry × PluginLoader v2)
|
||||
|
||||
最終更新: 2025-08-23
|
||||
|
||||
目的
|
||||
- Nyash 実行時に、MIR→VM→Registry→Plugin の呼び出しがどう流れるかを図解・手順で把握する。
|
||||
- TLVエンコード、ResultBoxの扱い、Handleのライフサイクル、nyash.tomlとの連携を1枚で理解する。
|
||||
|
||||
## ハイレベル流れ(シーケンス)
|
||||
|
||||
```
|
||||
Nyash Source ──▶ MIR (Builder)
|
||||
│ (BoxCall/NewBox/…)
|
||||
▼
|
||||
VM Executor
|
||||
│ (BoxCall dispatch)
|
||||
├─ InstanceBox → Lowered MIR 関数呼び出し
|
||||
├─ BuiltinBox → VM内ディスパッチ
|
||||
└─ PluginBoxV2 → PluginLoader v2
|
||||
│ (nyash.toml を参照)
|
||||
▼
|
||||
Invoke (TLV)
|
||||
│
|
||||
▼
|
||||
Plugin (lib*.so)
|
||||
│ (戻り値をTLVで返却)
|
||||
▼
|
||||
Loader でデコード
|
||||
│ (returns_result/Handle/型)
|
||||
▼
|
||||
NyashBox (ResultBox/PluginBoxV2/基本型)
|
||||
│
|
||||
▼
|
||||
VM に復帰
|
||||
```
|
||||
|
||||
## 主要構成要素
|
||||
- MIR: `MirInstruction::{BoxCall, NewBox, …}` で外部呼び出し箇所を明示。
|
||||
- VM: `src/backend/vm.rs`
|
||||
- InstanceBoxは `{Class}.{method}/{argc}` のLowered関数へ呼び出し
|
||||
- BuiltinはVM内の簡易ディスパッチ
|
||||
- PluginBoxV2は Loader v2 へ委譲
|
||||
- Registry/Runtime: `NyashRuntime` + `box_registry` + `plugin_loader_v2`
|
||||
- `nyash.toml` の `libraries.*` を読み込み、Box名→ライブラリ名、type_id、method_id等を集約
|
||||
|
||||
## NewBox(生成)
|
||||
1) MIRの `NewBox { box_type, args }`
|
||||
2) VM: `runtime.box_registry` に `box_type` を問い合わせ
|
||||
3) PluginBoxの場合、Loader v2が `birth(method_id=0)` を TLV で呼び出し
|
||||
4) Pluginは `type_id` と新規 `instance_id` を返却 → Loader は `PluginBoxV2` を構築
|
||||
5) VMは `ScopeTracker` に登録(スコープ終了で `fini` を呼ぶ)
|
||||
|
||||
## BoxCall(メソッド呼び出し)
|
||||
- InstanceBox: Lowered関数 `{Class}.{method}/{argc}` を MIR/VM内で実行
|
||||
- Builtin: VM内の `call_box_method` で対応(StringBox.length 等)
|
||||
- PluginBoxV2: Loader v2 の `invoke_instance_method` で TLV を組み立てて呼び出し
|
||||
|
||||
## TLV(Type-Length-Value)
|
||||
- ヘッダ: `u16 ver=1`, `u16 argc`
|
||||
- 各引数: `u8 tag`, `u8 reserved`, `u16 size`, `payload`
|
||||
- 主な tag:
|
||||
- 2 = i32 (size=4)
|
||||
- 6 = string, 7 = bytes
|
||||
- 8 = Handle(BoxRef) → payload = `u32 type_id || u32 instance_id`
|
||||
- 9 = void (size=0)
|
||||
|
||||
## 戻り値のマッピング(重要)
|
||||
- `returns_result=false`
|
||||
- tag=8 → PluginBoxV2(Handle)
|
||||
- tag=2 → IntegerBox、tag=6/7 → StringBox、tag=9 → void
|
||||
- `returns_result=true`(ResultBoxで包む)
|
||||
- tag=8/2 → `Result.Ok(value)`
|
||||
- tag=6/7 → `Result.Err(ErrorBox(message))`(Netプラグインなどがエラー文字列を返却)
|
||||
- tag=9 → `Result.Ok(void)`
|
||||
|
||||
補足
|
||||
- VM内で ResultBox の `isOk/getValue/getError` をディスパッチ済み
|
||||
- `toString()` フォールバックにより任意の Box を安全に文字列化可能
|
||||
|
||||
## Handle(BoxRef)のライフサイクル
|
||||
- Loaderは `(type_id, instance_id)` を `PluginBoxV2` としてラップ
|
||||
- `share_box()` は同一インスタンス共有、`clone_box()` はプラグインの birth を呼ぶ(設計意図による)
|
||||
- `fini` は `ScopeTracker` または Drop で保証(プラグインの `fini_method_id` を参照)
|
||||
|
||||
## 具体例(HttpClientBox.get)
|
||||
1) Nyash: `r = cli.get(url)`
|
||||
2) MIR: `BoxCall`(returns_result=true)
|
||||
3) VM→Loader: TLV(url = tag=6)
|
||||
4) Loader→Plugin: `invoke(type_id=HttpClient, method_id=get)`
|
||||
5) Plugin:
|
||||
- 接続成功: `Handle(HttpResponse)` を返す → Loaderは `Result.Ok(PluginBoxV2)`
|
||||
- 接続失敗: `String("connect failed …")` を返す → Loaderは `Result.Err(ErrorBox)`
|
||||
6) Nyash: `if r.isOk() { resp = r.getValue() … } else { print(r.getError().toString()) }`
|
||||
|
||||
## nyash.toml 連携
|
||||
- 例: `libraries."libnyash_net_plugin.so".HttpClientBox.methods.get = { method_id = 1, args=["url"], returns_result = true }`
|
||||
- Loaderは `method_id` と `returns_result` を参照し、TLVと戻り値のラップ方針を決定
|
||||
- 型宣言(args/kind)により、引数のTLVタグ検証を実施(不一致は InvalidArgs)
|
||||
|
||||
## デバッグTips
|
||||
- `NYASH_DEBUG_PLUGIN=1`: VM→Plugin の TLV ヘッダと先頭64バイトをプレビュー
|
||||
- `NYASH_NET_LOG=1 NYASH_NET_LOG_FILE=net_plugin.log`: Netプラグイン内部ログ
|
||||
- `--dump-mir --mir-verbose`: if/phi/return などのMIRを確認
|
||||
- `--vm-stats --vm-stats-json`: 命令使用のJSONを取得(hot pathの裏取りに)
|
||||
|
||||
## 将来の整合・改善
|
||||
- nyash.toml に ok側の戻り型(例: `ok_returns = "HttpResponseBox"`)を追加 → Loader判定の厳密化
|
||||
- Verifier強化: use-before-def across merge の検出(phi誤用を早期に発見)
|
||||
- BoxCall fast-path の最適化(hot path最優先)
|
||||
|
||||
関連
|
||||
- `docs/reference/plugin-system/net-plugin.md`
|
||||
- `docs/reference/architecture/mir-to-vm-mapping.md`
|
||||
- `docs/reference/architecture/mir-26-instruction-diet.md`
|
||||
|
||||
86
docs/reference/architecture/mir-26-instruction-diet.md
Normal file
86
docs/reference/architecture/mir-26-instruction-diet.md
Normal file
@ -0,0 +1,86 @@
|
||||
# MIR 26-Instruction Diet (Agreed Final Set)
|
||||
|
||||
Goal
|
||||
- Converge on a lean, proven instruction set guided by vm-stats and E2E.
|
||||
- Preserve hot paths, demote meta, fold type ops, reserve room for growth.
|
||||
|
||||
Agreed Final Set (26)
|
||||
1) Const
|
||||
2) Copy
|
||||
3) Load
|
||||
4) Store
|
||||
5) BinOp
|
||||
6) UnaryOp
|
||||
7) Compare
|
||||
8) Jump
|
||||
9) Branch
|
||||
10) Phi
|
||||
11) Return
|
||||
12) Call
|
||||
13) BoxCall
|
||||
14) NewBox
|
||||
15) ArrayGet
|
||||
16) ArraySet
|
||||
17) RefNew
|
||||
18) RefGet
|
||||
19) RefSet
|
||||
20) Await
|
||||
21) Print
|
||||
22) ExternCall (keep minimal; prefer BoxCall)
|
||||
23) TypeOp (unify TypeCheck/Cast)
|
||||
24) WeakRef (unify WeakNew/WeakLoad)
|
||||
25) Barrier (unify BarrierRead/BarrierWrite)
|
||||
26) Reserve (future async/error instr)
|
||||
|
||||
Hot/Core (keep)
|
||||
- Data: Const, Copy, Load, Store
|
||||
- ALU: BinOp, UnaryOp, Compare
|
||||
- Control: Jump, Branch, Phi, Return
|
||||
- Calls: Call, BoxCall
|
||||
- Objects: NewBox
|
||||
- Arrays: ArrayGet, ArraySet
|
||||
|
||||
Likely Keep (usage-dependent)
|
||||
- Refs: RefNew, RefGet, RefSet (seen in language features; keep unless stats prove cold)
|
||||
- Async: Await (FutureNew/Set can be Box/APIs)
|
||||
|
||||
Meta (demote to build-mode)
|
||||
- Debug, Nop, Safepoint
|
||||
|
||||
Type Ops (fold)
|
||||
- TypeCheck, Cast → fold/verify-time or unify as a single TypeOp (optional).
|
||||
|
||||
External (unify)
|
||||
- ExternCall → prefer BoxCall; keep ExternCall only where required.
|
||||
|
||||
Extended/Reserve
|
||||
- Weak*: WeakNew, WeakLoad
|
||||
- Barriers: BarrierRead, BarrierWrite
|
||||
- 2 Reserve IDs for future async/error instrumentation
|
||||
|
||||
Mapping Notes
|
||||
- HTTP E2E shows BoxCall/NewBox dominate (33–42%), then Const/NewBox, with Branch/Jump/Phi only in error flows.
|
||||
- FileBox path similarly heavy on BoxCall/NewBox/Const.
|
||||
- Implication: invest into BoxCall fast path and const/alloc optimization.
|
||||
|
||||
Migration Strategy
|
||||
1) Introduce an experimental feature gate that switches TypeCheck/Cast to TypeOp or folds them.
|
||||
2) Demote Debug/Nop/Safepoint under non-release builds.
|
||||
3) Keep ExternCall available, route new external APIs via BoxCall when possible.
|
||||
4) Track Weak*/Barrier usage; unify under WeakRef/Barrier. Graduate dedicated ops only if vm-stats shows recurring use.
|
||||
|
||||
Mapping from Current → Final
|
||||
- TypeCheck, Cast → TypeOp
|
||||
- WeakNew, WeakLoad → WeakRef
|
||||
- BarrierRead, BarrierWrite → Barrier
|
||||
- (Keep) ExternCall, but prefer BoxCall where possible(ExternCall は最小限に)
|
||||
- (Keep) Debug/Nop/Safepoint as meta under non-release builds(命令セット外のビルドモード制御に降格)
|
||||
|
||||
Verification
|
||||
- Add MIR verifier rule: use-before-def across merges is rejected (guards phi misuse).
|
||||
- Add snapshot tests for classic if-merge returning phi.
|
||||
|
||||
Appendix: Rationale
|
||||
- Smaller ISA simplifies VM fast paths and aids JIT/AOT later.
|
||||
- Data shows hot paths concentrated on calls, const, alloc; control sparse and localized.
|
||||
- Folding type ops reduces interpreter/VM dispatch; verification handles safety.
|
||||
@ -103,6 +103,22 @@
|
||||
- NewBox/BoxCall が上位に入るため、命令セットから外すのは不可(コア扱い)。
|
||||
- Compare/Branch/Jump/Phi は制御フローのコア。26命令の中核として維持が妥当。
|
||||
|
||||
## 実測統計(2025-08-23)
|
||||
出所: vm-stats(正常系HTTP/異常系HTTP/FileBox)
|
||||
|
||||
- 正常系HTTP(40命令)
|
||||
- BoxCall: 17(42.5%)/ Const: 12(30%)/ NewBox: 9(22.5%)/ Return: 1 / Safepoint: 1
|
||||
- 異常系HTTP(21命令 = 正常の52.5%)
|
||||
- BoxCall: 7(33.3%)/ Const: 6(28.6%)/ NewBox: 3(14.3%)
|
||||
- Branch: 1 / Jump: 1 / Phi: 1(エラーハンドリング特有)/ Return: 1 / Safepoint: 1
|
||||
- FileBox(44命令)
|
||||
- BoxCall: 17(38.6%)/ Const: 13(29.5%)/ NewBox: 12(27.3%)/ Return: 1 / Safepoint: 1
|
||||
|
||||
設計含意:
|
||||
- BoxCallが常に最頻出(33〜42%)。呼び出しコスト最適化が最優先。
|
||||
- Const/NewBoxが次点。定数・生成の最適化(定数畳み込み/軽量生成・シェアリング)が効果的。
|
||||
- 異常系は早期収束で命令半減。if/phi修正は実戦で有効(Branch/Jump/Phiが顕在化)。
|
||||
|
||||
## 26命令ダイエット(検討のたたき台)
|
||||
方針: 「命令の意味は保ちつつ集約」。代表案:
|
||||
- 維持: Const / Copy / Load / Store / BinOp / UnaryOp / Compare / Jump / Branch / Phi / Return / Call / BoxCall / NewBox / ArrayGet / ArraySet
|
||||
@ -118,6 +134,19 @@
|
||||
|
||||
---
|
||||
|
||||
## 26命令ダイエットの指針(実測反映)
|
||||
- 維持(ホット・コア): BoxCall / NewBox / Const / BinOp / Compare / Branch / Jump / Phi / Return / Copy / Load / Store / Call
|
||||
- 実装方針: ExternCallは原則BoxCallへ集約(必要なら限定的に残す)。
|
||||
- メタ降格: Debug/Nop/Safepoint(ビルドモードで制御)。
|
||||
- 型系: TypeCheck/Castは折りたたみ or 検証時に処理(1命令に集約も可)。
|
||||
- 参照/弱参照/バリア: 需要ベースで拡張枠へ(vm-statsに登場しない限りコア外)。
|
||||
|
||||
提案(ドラフト):
|
||||
- コア候補(例): Const, Copy, Load, Store, BinOp, UnaryOp, Compare, Jump, Branch, Phi, Return, Call, BoxCall, NewBox, ArrayGet, ArraySet, RefNew, RefGet, RefSet, Await, Print, ExternCall(集約可), TypeOp(=TypeCheck/Cast), 予備2(将来枠)
|
||||
- 予備はWeak*/Barrierや将来の非同期拡張等に割当(実測で常用化したら昇格)。
|
||||
|
||||
---
|
||||
|
||||
## E2E更新(VM経由の実働確認)
|
||||
|
||||
成功ケース(VM):
|
||||
@ -125,6 +154,8 @@
|
||||
- FileBox.copyFrom(handle): Handle引数(tag=8, size=8, type_id+instance_id)で成功
|
||||
- HttpClientBox.get + HttpServerBox: 基本GETの往復(ResultBox経由でResponse取得)
|
||||
- HttpClientBox.post + headers: Status/ヘッダー/ボディをVMで往復確認
|
||||
- HttpClientBox.get unreachable: 接続失敗時はResult.Err(ErrorBox)(ローダーがstring/bytesをErrにマップ)
|
||||
- HTTP 404/500: Result.Ok(Response)(ステータスはResponse上に保持)
|
||||
|
||||
デバッグ小技:
|
||||
- `NYASH_DEBUG_PLUGIN=1` で VM→Plugin 呼び出しTLVの ver/argc/先頭バイトをダンプ
|
||||
|
||||
Reference in New Issue
Block a user