Phase 12: 統一TypeBox ABI実装開始 - ChatGPT5による極小コアABI基盤構築

- TypeBox ABI雛形: メソッドスロット管理システム追加
- Type Registry: Array/Map/StringBoxの基本メソッド定義
- Host API: C ABI逆呼び出しシステム実装
- Phase 12ドキュメント整理: 設計文書統合・アーカイブ化
- MIR Builder: クリーンアップと分離実装完了

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-09-03 05:04:56 +09:00
parent e2e25f6615
commit 53d88157aa
84 changed files with 4739 additions and 2750 deletions

View File

@ -0,0 +1,87 @@
# Nyash ABI戦略議論まとめ (2025-09-01)
## 📋 概要
Phase 12のプラグインシステム実装において、C ABIとNyash ABIの選択について、AI先生方と深い技術議論を行いました。
## 🗂️ ドキュメント一覧
1. **[gemini-abi-analysis.md](gemini-abi-analysis.md)**
- Gemini先生の長期エコシステム視点
- ABI安定性の重要性
- 段階的進化戦略C ABI → SDK → WASM
2. **[codex-abi-implementation.md](codex-abi-implementation.md)**
- Codex先生の実装最適化視点
- C呼出規約×Nyash値表現の提案
- VM/JIT最適化の具体策
3. **[deep-analysis-synthesis.md](deep-analysis-synthesis.md)**
- なぜ正解が難しいのかの深い分析
- 時間軸のジレンマ
- 「正解がない」ことが答え
## 🎯 結論BoxCall拡張による統合案
### 最終的な実装方針
```rust
// MIRレベルBoxCallをちょっと拡張
MirInstruction::BoxCall {
receiver: Value,
method: String,
args: Vec<Value>,
abi_hint: Option<AbiType>, // ← これだけ追加
}
```
### 利点
1. **MIR命令数は15個のまま**(美しさ維持)
2. **既存コードは変更不要**(後方互換)
3. **プラグインごとにABI選択可能**(段階的移行)
4. **Everything is Box哲学の体現**
### 実装計画
```yaml
Week 1: 基盤整備
- PluginABI enum定義
- nyash.tomlのabi field追加
- NyashValue構造体作成
Week 2: VM統合
- プラグインローダー拡張
- VM実行時のABI分岐
- pack/unpack実装
Week 3: 検証
- 比較ベンチマーク
- ドキュメント作成
- 方向性判断
```
## 🔑 重要な洞察
1. **両ABI共存が現実的**
- C ABI既存資産・高速・安定
- Nyash ABI型安全・拡張性・将来性
2. **適応的戦略の採用**
- 3ヶ月ごとに測定・評価
- データに基づく進化
3. **箱理論による差し替え可能性**
- 実装を箱に切り出す
- いつでも戻せる安心感
## 📊 次のステップ
1. このREADMEを起点に実装開始
2. 最小限のプロトタイプ作成
3. 性能・開発体験の比較データ収集
4. Phase 12本実装への反映
---
*「間に挟むだけ」が最も難しい設計判断だった。しかし、BoxCall拡張という自然な解決策にたどり着いた。*

View File

@ -0,0 +1,124 @@
# Codex先生のNyash ABI実装戦略 (2025-09-01)
## 質問内容
Nyashプラグインシステムの ABI戦略について技術的相談です。
【背景】
Phase 12でNyashスクリプトプラグインシステムを実装中です。現在、プラグインインターフェースの設計で重要な判断が必要です。
【現状】
- 既存: C ABIシンプル、高速、実績あり
- 提案: Nyash ABI3×u64構造体、型情報付き、拡張性高い
【トレードオフ】
C ABI:
- 利点: ゼロオーバーヘッド、既存資産活用、シンプル
- 欠点: 型情報なし、拡張性限定、エラー処理が原始的
Nyash ABI:
- 利点: 型安全、メタデータ活用、将来性async等
- 欠点: pack/unpackのオーバーヘッド、新規学習コスト
【具体的な選択肢】
1. Nyash ABIのみ統一性重視
2. C ABI + Nyash ABI両方実用性重視
3. その他の革新的アプローチ?
【質問】
1. どちらの戦略が長期的に見て賢明でしょうか?
2. 両方サポートする場合の複雑性は管理可能でしょうか?
3. 他の言語での成功例・失敗例から学べることは?
4. VM/JIT最適化の観点から、どちらが有利でしょうか
実装の実践的観点から、C ABIとNyash ABIの選択について深く分析してください。特にVM/JIT最適化や実装の複雑性を考慮して。
## Codex先生の回答
**結論**
- 長期的には「C呼出規約 × Nyash値表現3×u64」を中核に、C ABIとNyash ABIの両方をサポートする二層戦略が最も堅実。
- まずは二者併存で運用し、JIT/VMでの最適化余地と実用性を確保しながら、将来的にNyash ABIを第一級に育てるのが現実的。
**推奨戦略**
- 両対応2を採用。ただし"呼出規約はC、値表現はNyash"を共通土台にする。
- 共通の値ABI: `#[repr(C)] struct NyValue { u64 tag; u64 payload; u64 aux; }`
- C ABI関数でもNyValueを受け渡しできるため、型安全・メタデータ拡張の道を確保しつつ、Cの安定性・高速性を得る。
- プラグイン初期化は「バージョン付き関数テーブルvtable機能フラグ」の交渉型にする。
- 例: `NyPluginInitV1(NyRtFnsV1* rt, NyPluginV1* out)``NyPluginV1 { u32 abi_mask; u32 size; NyFnDesc* fns; ... }`
- `abi_mask``NY_ABI_C``NY_ABI_NYV1` の両方可否を示す。将来の `NY_ABI_NYV2` も追加可能。
- エラーモデルは二段階:
- 速経路: `NyStatus`enum, small intを戻す。失敗時は`NyValue`でエラーオブジェクトをout-param返却。
- 詳細情報は`NyError`ヘルパ(`NyValue`として表現に格納。FFI境界でpanic禁止・例外横断なし。
- メモリ/所有権はハンドル/関数テーブル経由で統一NyRTのalloc/free、string/slice作成API。内部構造体への直接依存は不可。
**複雑性と管理**
- 追加コストは主にローダとアダプタ(トランポリン)実装+テスト行列の増加。ただし次の工夫で管理可能:
- 機能交渉を一本化init関数とマニフェスト/nyash.tomlで表明。ロード時に一度だけ決定。
- 呼出アダプタをテンプレート化JIT時に生成・キャッシュ型配列→シグネチャ→thunkアドレス
- 型表(`NyFnDesc`に引数/戻りの型ID配列、純関数/readonly/aliasing属性を持ち、最適化と検証を兼用。
- 見積りローダvtableアダプタの骨格は数百行〜千行弱で収まり、以降は関数種別追加が足し算。
**他言語の事例からの学び**
- 成功例
- Node N-API: バージョン化された関数テーブルでABIを安定化。実装側の進化と互換の両立に有効。
- Lua C API: C呼出規約独自値表現で長期安定。ただしstackベースは学習コストあり。
- 反省点
- CPython C API: 内部構造体露出で進化の足枷に。HPyはハンドル化で再設計。
- JNI: 汎用だが冗長・低速傾向。最適化しづらい。
- 教訓
- 内部レイアウト非公開(ハンドル/関数経由・バージョン化vtable・拡張フラグが長期安定の鍵。
- 値ABIは固定サイズ・レジスタ渡し最適化可能な形が吉3×u64は合理的
**VM/JIT最適化の観点**
- 呼出オーバーヘッド
- 3×u64はSysV/Win64いずれもレジスタ渡しが効きやすく、C ABIと同等のコールコストに収束。
- 本質的ボトルネックは間接呼出ミス予測と型検査。JITで型が既知ならチェックを埋め込み/削減可能。
- 直呼 vs トランポリン
- 署名が一致する場合は直接呼出JITに外部シンボルとして登録
- 型昇降/借用が必要な時のみthunkを噛ませる。JITでモモーフィック化・デッドパス除去。
- 最適化ヒントCranelift/LLVM
- 関数属性: `nounwind`, `readonly`/`readnone`, `nocapture`, `nonnull`, `cold`(エラーパス)
- 戻り値はout-paramにしてtailcall最適化`musttail`適用可否は要検証)。
- シンボルはAOT時`dso_local`、JITは`CallConv::SystemV/WindowsFast`で3×i64に整合。
- ホットループ対策
- スカラー跨ぎは避け、スライス/バルクAPIを定義例: vector add。boundary跨ぎ回数を削減。
- 将来は静的リンクモードfirst-party pluginsでLTO許可→AOTでインライン・DCE可。
**Nyash ABIのみ vs 併存の比較**
- Nyash ABIのみ1
- 一貫性と将来性は高いが、既存C資産/ツール連携コストと立上げ摩擦が大きい。
- 併存2, 推奨)
- 早期エコシステム拡大と最適化の両取り。複雑性はvtableアダプタの設計で抑え込み可能。
- その他3
- N-API風ハンドル専一は安定するが、オーバーヘッド/冗長性が増える。Nyashのパフォーマンス志向には過剰。
**実装メモ(具体)**
- 値ABI
- `tag`: 上位ビットに種別・フラグown/borrow、small/heap、nullable等
- `payload`: ポインタor即値、`aux`: len/stride/arena_id等
- `NySlice`: `tag=SLICE``payload=ptr`, `aux=len`。borrow時は生存期間制約、必要なら`nyrt_clone_slice`で所有権取得。
- 初期化/交渉
- `NyPluginInitV1(NyRtFnsV1*, NyPluginV1*) -> NyStatus`
- `NyRtFnsV1`: alloc/free、string/slice/future、log、error造作API。
- `NyPluginV1`: `abi_mask`, `size`, `features`, `fn_count`, `NyFnDesc*`.
- エラー/非同期
- `NyStatus` + `NyValue err_out`。非同期は `NyFuture*`を返却、`NyRtFnsV1``poll/wake`を橋渡し。
- VMのスケジューラとWakerのブリッジはvtableで隠蔽ABI非依存
- 安定性
- すべての公開構造体に`size`を先頭フィールドに持たせ将来拡張。未理解フィールドは無視。
- ABIバージョンは数値機能フラグで条件分岐ビルド時にも`#[cfg(feature)]`で絞る)。
**質問への回答**
- 1) 長期的に賢明か: 併存共通値ABIが最善。Cの互換性とNyashの型安全/拡張性を両立。
- 2) 複雑性は管理可能か: はい。vtableの一本化、JIT生成thunk、機能交渉で抑制できる。
- 3) 学び: 内部構造体を外部に晒さない・ハンドル経由・バージョン化テーブル・豊富な属性メタデータ。CPythonの反省とN-APIの成功を取り込む。
- 4) VM/JIT最適化: 3×u64のNyash値はレジスタ渡しでC ABIと同等に速く、型既知箇所はJITで直呼/チェック省略可能。最適化の自由度はNyash ABIの方が高い。
**次のアクション**
- 値ABIと関数テーブル仕様を`crates/nyrt/include/nyrt.h`に最小ドラフト化(`NyValue`, `NyStatus`, `NyRtFnsV1`, `NyPluginV1`, `NyFnDesc`)。
- ローダにinit/交渉実装(`nyash.toml`のabi種別・型情報読み込み→関数登録
- JIT/VMに外部関数署名登録とthunk生成を追加Cranelift/LLVM兼用
- バルク演算プラグインのスモークを作成C ABI版とNyash ABI版を比較ベンチ
- ドキュメント化(`docs/plugins/abi.md`)とサンプル(`plugins/``apps/`)追加。
必要なら、ドラフトの`NyValue``NyPluginInitV1`の最小Cヘッダ案もすぐ出します。

View File

@ -0,0 +1,188 @@
# Codex先生のBoxCall拡張分析 (2025-09-01)
## 質問内容
Nyash ABI実装の具体的アプローチについて技術相談です。
【背景】
前回の議論で、C ABIとNyash ABIの両方をサポートする方向性が見えました。実装方法について新しいアイデアがあります。
【BoxCall拡張による統合案】
MIR命令を増やさず、既存のBoxCallを自然に拡張する案です
```rust
// 現在のBoxCall
MirInstruction::BoxCall {
receiver: Value,
method: String,
args: Vec<Value>,
}
// 提案:最小限の拡張
MirInstruction::BoxCall {
receiver: Value,
method: String,
args: Vec<Value>,
abi_hint: Option<AbiType>, // 追加
}
```
【実装の流れ】
1. MIRビルダープラグインBoxならABIヒント付与
2. VM実行時receiver.type_idから適切なABI選択
3. プラグインローダーnyash.tomlでABI指定可能
【この案の利点】
- MIR命令数は15個維持Everything is Box哲学
- 既存コード無変更(後方互換)
- プラグインごとに段階的移行可能
- JITでABI別最適化も可能
【質問】
1. このBoxCall拡張案は設計として健全でしょうか
2. abi_hintは本当に必要VM実行時の型判定で十分
3. 将来的な拡張WASM等にも対応できる設計か
4. パフォーマンス影響をどう評価すべきか?
実装の観点から、MIR層でのabi_hint追加とVM層でのABI判定について深く分析してください。特に既存実装への影響を最小化する方法を考慮して。
## Codex先生の回答
### コードベース分析
まず、現在のMIR/VM実装を確認
```rust
// 現在のMirInstruction::BoxCall定義
BoxCall {
dst: Option<ValueId>,
box_val: ValueId,
method: String,
method_id: Option<u16>, // Universal method用
args: Vec<ValueId>,
effects: EffectMask,
}
```
プラグインローダーはすでにnyash.tomlから型情報を読み込んでおり、VM実行時に利用可能。
### 核心的な洞察
**abi_hintは実は不要かもしれない**
理由:
1. VM実行時にreceiver.type_idから型情報を取得可能
2. プラグインローダーが既にABI情報を保持している
3. MIR層を変更せずに済む15命令維持
### 実装提案
#### Option A: 最小限実装MIR無変更
```rust
// VM層での実装
fn execute_boxcall(&mut self, dst: Option<ValueId>, box_val: ValueId, method: &str, args: &[ValueId]) -> Result<ControlFlow, VMError> {
let receiver = self.get_value(box_val)?;
let type_id = receiver.get_type_id();
// プラグインローダーから型情報取得
if let Some(plugin_info) = self.runtime.get_plugin_info(type_id) {
match plugin_info.abi.as_str() {
"c" => self.call_c_abi_plugin(plugin_info, method, args),
"nyash" => self.call_nyash_abi_plugin(plugin_info, method, args),
_ => self.call_c_abi_plugin(plugin_info, method, args), // fallback
}
} else {
// ビルトインBox処理既存のまま
self.execute_builtin_method(receiver, method, args)
}
}
```
#### Option B: 将来の拡張性を考慮
```rust
// MIRに汎用メタデータ追加ABIに限定しない
MirInstruction::BoxCall {
receiver: Value,
method: String,
args: Vec<Value>,
metadata: Option<u32>, // 汎用的な最適化ヒント
}
```
### JIT/AOT最適化の観点
```rust
// JIT時の特化
match type_info.abi {
Some("c") => {
// 直接call命令生成
builder.emit_direct_call(symbol, args);
}
Some("nyash") => {
// NyashValue pack/unpackを含めてインライン化
let packed = builder.emit_pack_nyash_values(args);
builder.emit_call_with_abi(symbol, packed);
}
_ => {
// 汎用パスVM呼び出し
builder.emit_vm_boxcall(receiver, method, args);
}
}
```
### パフォーマンス評価方法
```rust
// ベンチマーク設計
fn benchmark_abi_comparison() {
// 1. 高頻度呼び出しmath operations
measure_time("C ABI", || {
for _ in 0..1_000_000 {
math_add_c_abi(1.0, 2.0);
}
});
measure_time("Nyash ABI", || {
for _ in 0..1_000_000 {
math_add_nyash_abi(pack(1.0), pack(2.0));
}
});
// 2. 複雑な型string operations
// 3. プラグイン初期化コスト
}
```
### 結論と推奨事項
1. **abi_hintは不要** - VM実行時の型判定で十分
2. **MIR層は無変更** - 15命令の美しさ維持
3. **将来拡張は型システム経由** - WASMもプラグイン型として追加
4. **段階的実装**
- Week 1: nyash.toml拡張 + VM分岐
- Week 2: ベンチマーク + 最適化
- Week 3: JIT特化必要なら
### 実装の具体的ステップ
```yaml
Step 1: nyash.toml拡張
- LibraryDefinition に abi: Option<String> 追加
- デフォルトは "c"
Step 2: プラグインローダー
- PluginInfo 構造体に abi フィールド追加
- ロード時に保存
Step 3: VM execute_boxcall
- 型判定でABI分岐
- call_c_abi / call_nyash_abi メソッド追加
Step 4: テスト
- 同一機能の C/Nyash 両実装
- 性能比較測定
```
この最小限アプローチなら、既存コードへの影響を最小化しつつ、将来の拡張性も確保できます。

View File

@ -0,0 +1,164 @@
# Nyash ABI戦略の深い分析 - なぜ正解が難しいのか (2025-09-01)
## 🤔 両先生の回答から見えた根本的な難しさ
### 1. 時間軸のジレンマ
**Gemini先生の視点**(長期エコシステム):
- 10年後も動くプラグインを作りたい
- 破壊的変更は絶対避けたい
- 開発者の信頼が最重要
**Codex先生の視点**(現実的最適化):
- 今すぐ高速に動かしたい
- JIT/VMの最適化余地を残したい
- 実装の複雑性を抑えたい
**この2つは本質的に矛盾する**
### 2. 抽象化レベルの選択
```
高レベル抽象化Gemini案
SDK層
C ABI安定境界
vs
低レベル統合Codex案
共通値表現3×u64
C呼出規約で統一
```
どちらが正解? **状況による!**
### 3. 隠れた複雑性の罠
**表面的には単純に見える選択**
- C ABI only → シンプル!
- Nyash ABI only → 統一的!
- 両方サポート → 柔軟!
**実際の複雑さ**
- C ABI only → 型情報なし、拡張困難
- Nyash ABI only → 既存資産切り捨て
- 両方サポート → **複雑性が2倍...ではなく4倍**
### 4. なぜ複雑性が爆発するのか
```
組み合わせ爆発:
- 2つのABI ×
- 3つのバックエンドInterpreter/VM/JIT ×
- N個のプラグイン型 ×
- M個の最適化レベル
= 指数関数的複雑性
```
## 🎯 深い洞察:本当の問題は何か
### 技術的正解 vs ビジネス的正解
**技術的に美しい解**
- Nyash ABI一本化
- 型安全、拡張可能、統一的
**ビジネス的に賢い解**
- C ABI + 段階的移行
- 既存資産活用、リスク分散
**どちらも「正解」であり「不正解」**
### 隠れた第3の選択肢
両先生が暗黙的に示唆している:
```
時期による使い分け:
Phase 1: C ABIで素早くエコシステム立ち上げ
Phase 2: SDK層で開発体験向上
Phase 3: Nyash ABIで技術的優位性確立
Phase 4: 統合または選択的廃止
```
## 💡 究極の洞察:「正解がない」ことが答え
### なぜ正解が難しいのか
1. **未来は予測不可能**
- NyashがRustを超えるか
- WASMが世界標準になるか
- 新しいABI標準が生まれるか
2. **トレードオフは価値観次第**
- 速度 vs 安全性
- シンプル vs 機能性
- 互換性 vs 革新性
3. **成功の定義が人による**
- 多くのプラグイン?
- 高速な実行?
- 美しいコード?
## 🚀 実践的な答え:適応的戦略
### 推奨アプローチ
```rust
// 初期実装:両対応だが内部統一
enum PluginABI {
C(CPlugin), // 既存資産
Nyash(NyashPlugin), // 新規開発
}
// 共通インターフェース
trait Plugin {
fn invoke(&self, args: &[Value]) -> Result<Value>;
}
// 将来の拡張ポイント
impl PluginABI {
fn optimize_for_jit(&self) -> Option<DirectCall> {
// JIT時に最適化可能なら直接呼び出しに変換
}
}
```
### 段階的進化の道筋
1. **観察期間**6ヶ月
- 両ABI並行運用
- 使用パターン分析
- パフォーマンス測定
2. **最適化期間**次の6ヶ月
- 頻出パターンの高速化
- SDK層の洗練
- ドキュメント充実
3. **判断期間**1年後
- データに基づく選択
- 片方を非推奨に?
- それとも永続的共存?
## 🌟 結論:「正解がない」を受け入れる勇気
**Nyashの哲学**
- Everything is Box → Everything has Trade-offs
- 完璧より進捗80/20ルール
- 箱理論で差し替え可能に
**最終提案**
1. 両方実装するが、**内部アーキテクチャは統一**
2. **測定可能な成功指標**を先に定義
3. **3ヶ月ごとに振り返り**、方向修正
正解がないからこそ、**適応し続けることが正解**。
---
*「間に挟むだけ」のABI層が、実は最も難しい設計判断の一つだった。*

View File

@ -0,0 +1,121 @@
# BoxCall拡張によるABI戦略 - 最終実装決定 (2025-09-01)
## 🎯 両先生の回答から得た重要な洞察
### Gemini先生の誤解が示す重要な点
- 先生は「BoxCall = Box<dyn Trait>を渡す」と誤解
- しかし、この誤解が**abi_stable**の重要性を教えてくれた
- 将来的にRustトレイトオブジェクトを扱う際の指針に
### Codex先生の実践的分析
1. **abi_hintは実は不要かもしれない**
- VM実行時にtype_idから判定可能
- プラグインローダーが型情報を保持
- MIR層を汚さない
2. **既存実装への影響最小化**
- TypeMetaにABI情報を含める
- nyash.tomlから読み込み済み
- VM側で判定するだけ
## 🚀 最終実装方針
### Option A: 最小限実装(推奨)
```rust
// MIR層変更なし
MirInstruction::BoxCall {
receiver: Value,
method: String,
args: Vec<Value>,
// abi_hint不要
}
// VM層型判定でABI選択
fn execute_boxcall(...) {
let type_id = receiver.get_type_id();
// プラグインローダーから型情報取得
if let Some(plugin_info) = get_plugin_info(type_id) {
match plugin_info.abi {
"c" => call_c_abi(...),
"nyash" => call_nyash_abi(...),
_ => fallback_to_c_abi(...),
}
}
}
```
### Option B: 明示的ヒント(将来の拡張性)
```rust
// MIR層最小限の拡張
MirInstruction::BoxCall {
receiver: Value,
method: String,
args: Vec<Value],
metadata: Option<u32>, // 汎用メタデータABIヒント含む
}
```
## 📊 比較表
| 観点 | Option A最小限 | Option Bヒント付き |
|------|------------------|---------------------|
| MIR変更 | なし ✅ | 最小限 |
| 実装工数 | 1週間 | 2週間 |
| JIT最適化 | VM時判定 | ビルド時判定可能 |
| 将来拡張 | 型情報経由 | メタデータ活用 |
## 🎯 結論Option Aで始める
### 理由
1. **80/20ルール**: まず動くものを作る
2. **MIR無変更**: 15命令の美しさ維持 → さらに14命令へPluginInvoke統合
3. **段階的進化**: 必要になったらOption Bへ
### Phase 12での追加統合
- **PluginInvoke → BoxCall 完全統合**
- ビルトインBox廃止により区別が不要
- VM層でのABI判定で十分
- Core-15 → Core-14 への削減
### 実装ステップ1週間
```yaml
Day 1-2: nyash.toml拡張
- abi: "c" | "nyash" field追加
- プラグインローダー対応
Day 3-4: VM実行時分岐
- execute_boxcall内でABI判定
- NyashValue pack/unpack実装
Day 5-6: テスト・ベンチマーク
- 同一機能の2種類プラグイン
- 性能比較測定
Day 7: 判断
- データに基づく方向性決定
- Option B移行の必要性評価
```
## 💡 深い洞察
**「abi_hintは不要」という発見が示すもの**
- 型システムが既に十分な情報を持っている
- MIRは純粋な意図の表現で良い
- 実行時の賢い判断に任せる
これこそ「Everything is Box」の真髄 - Boxが自分の実装方法を知っている
## 🚀 次のアクション
1. nyash.tomlパーサーに`abi` field追加
2. プラグインローダーでABI情報保持
3. VM execute_boxcallで分岐実装
4. 最小限のテストプラグイン作成
---
*最小限から始めて、データで判断する。これがNyashの道。*

View File

@ -0,0 +1,179 @@
# Gemini先生のNyash ABI戦略分析 (2025-09-01)
## 質問内容
Nyashプラグインシステムの ABI戦略について技術的相談です。
【背景】
Phase 12でNyashスクリプトプラグインシステムを実装中です。現在、プラグインインターフェースの設計で重要な判断が必要です。
【現状】
- 既存: C ABIシンプル、高速、実績あり
- 提案: Nyash ABI3×u64構造体、型情報付き、拡張性高い
【トレードオフ】
C ABI:
- 利点: ゼロオーバーヘッド、既存資産活用、シンプル
- 欠点: 型情報なし、拡張性限定、エラー処理が原始的
Nyash ABI:
- 利点: 型安全、メタデータ活用、将来性async等
- 欠点: pack/unpackのオーバーヘッド、新規学習コスト
【具体的な選択肢】
1. Nyash ABIのみ統一性重視
2. C ABI + Nyash ABI両方実用性重視
3. その他の革新的アプローチ?
【質問】
1. どちらの戦略が長期的に見て賢明でしょうか?
2. 両方サポートする場合の複雑性は管理可能でしょうか?
3. 他の言語での成功例・失敗例から学べることは?
4. VM/JIT最適化の観点から、どちらが有利でしょうか
## Gemini先生の回答
### NyashプラグインシステムにおけるABI戦略の技術的考察
Nyashプラグインシステムの長期的なエコシステム発展を見据えたABIApplication Binary Interface戦略について、深く技術的に考察します。
### 1. ABI安定性の重要性エコシステム成功の礎
プラグインシステムの成功は、**ABIの鉄壁の安定性**にかかっています。ホストアプリケーションNyashとプラグインは、別々にコンパイル・配布されるバイナリです。両者が正しく協調動作できるのは、関数呼び出し規約、データ型表現、メモリアロケーションといった低レベルな規約、すなわちABIが厳密に一致しているからです。
もしNyashのバージョンアップでABIに破壊的変更が生じれば、既存の全プラグインは動作しなくなり、再コンパイルが必要になります。これはエコシステムにとって致命的です。開発者は追従を諦め、ユーザーはプラグインが使えなくなることを恐れてNyashのアップデートをためらうでしょう。
したがって、ABI戦略の最優先目標は**「前方互換性新しいNyashが古いプラグインを動かせると、可能な限りの後方互換性古いNyashが新しいプラグインの機能を限定的にでも利用できる」**を保証することです。
### 2. 現状の分析と潜在的リスク
現在のNyashのコードベースを調査し、プラグイン関連の実装を特定する必要があります。`Cargo.toml`やソースコードから、おそらくRustのダイナミックライブラリ`.so`, `.dll`, `.dylib`)を動的に読み込む形式が採用されていると推測されます。
ここで最も警戒すべきは、**Rust固有のABIに依存することのリスク**です。Rustコンパイラは、パフォーマンス最適化のために、構造体のメモリレイアウトや関数呼び出し規約をバージョン間で予告なく変更します。したがって、`extern "Rust"`(デフォルト)の関数をプラグイン境界として公開するのは、エコシステムの観点からは**絶対に避けなければなりません**。
素晴らしい検索結果から、Nyashはすでに `extern "C"` を広範囲に利用していることが明確にわかります。これは非常に良い出発点です。`plugin_loader_v2.rs``bid/plugin_api.rs` などを見ると、プラグインの初期化(`nyash_plugin_init`)や関数呼び出し(`invoke_fn`のシグネチャがC ABIで定義されています。
これは、ABI安定性に対する基本的な理解がプロジェクトに既に存在することを示しています。しかし、長期的なエコシステムを築くには、これをさらに推し進め、より洗練された戦略を立てる必要があります。
### 3. 長期的な発展のためのABI戦略提案
現在の `extern "C"` ベースのアプローチを基礎としつつ、以下の戦略を段階的に導入することを提案します。
#### 戦略1C ABIを「公式な安定境界」として厳格に定義・文書化する
これは最も重要です。`extern "C"` を使っているというだけでは不十分で、それを**公式な規約**として確立する必要があります。
**具体的なアクション:**
1. **ABI仕様書の作成:**
- `nyash-abi.h`のようなCヘッダーファイル形式で、プラグインが実装すべき関数シグネチャと、Nyashが提供するホスト関数コールバックをすべて定義します。
- `invoke_fn` で使われている `*const u8`, `*mut u8` のようなバイトポインタが、具体的にどのようなデータを指すのか(シリアライズ形式、レイアウトなど)を厳密に文書化します。
- `NyashHostVtable` のような構造体は、フィールドの順序と型が未来永劫変わらないことを保証しなければなりません。新しいホスト関数を追加する場合は、末尾にのみ追加します。
- エラー報告の規約(戻り値の `i32` が何を意味するのか)を定義します。
2. **FFIForeign Function Interface層の明確化:**
- Nyash内部のRustコードと、C ABI境界を明確に分離します。`plugin_loader_v2.rs` はこの役割を担っているようですが、この層は「ABI変換」にのみ責任を持つべきです。内部的なリファクタリングがABIに影響を与えないようにします。
**利点:**
- **言語非依存:** C ABIはあらゆる主要言語C++, Zig, Go, Python, etc.)から利用可能で、プラグイン開発者の裾野を広げます。
- **究極の安定性:** C ABIは数十年にわたり安定しています。
**欠点:**
- **表現力の低さ:** Rustの`Result``Option`、ライフタイムといった豊かな型情報を直接渡せず、`i32`のエラーコードやポインタのNULLチェックといったプリミティブな方法に頼らざるを得ません。
- **手作業と`unsafe`:** データのシリアライズ・デシリアライズやポインタ操作が必須となり、`unsafe`コードが増え、バグの温床になりやすいです。
#### 戦略2高レベルな「Rust向けラッパーSDK」を提供する
C ABIの欠点を補うため、プラグイン開発者が直接C ABIを触らなくても済むように、公式のRust製SDKクレートを提供します。
**具体的なアクション:**
1. **`nyash-plugin-sdk` クレートの作成:**
- このSDKは、低レベルなC ABIのやり取りをすべてカプセル化します。
- プラグイン開発者は、`Result<MyData, MyError>` のような自然なRustの型を扱うだけで、SDKが自動的にC ABI準拠のデータバイト列やエラーコードとの相互変換を行います。
- 定型的なコードを自動生成するマクロ(例: `#[nyash_plugin]`)を提供し、開発体験を向上させます。
**SDK利用者のコードイメージ:**
```rust
// プラグイン開発者が書くコード
use nyash_plugin_sdk::{plugin, NyashResult, NyashError};
struct MyPlugin;
#[plugin]
impl Plugin for MyPlugin {
fn handle_request(&self, input: &str) -> NyashResult<String> {
if input == "hello" {
Ok("world".to_string())
} else {
Err(NyashError::new("Invalid input"))
}
}
}
```
**SDK内部の実装イメージ:**
```rust
// #[plugin] マクロが生成するコード
#[no_mangle]
pub unsafe extern "C" fn nyash_plugin_invoke(
/* C ABI引数 */
) -> i32 {
// 1. C ABIのバイト列から &str へデシリアライズ
// 2. MyPlugin::handle_request を呼び出し
// 3. 結果の Result<String, NyashError> をC ABIのエラーコードとバイト列にシリアライズ
// 4. 結果を返す
// ... この部分はSDKが隠蔽する ...
}
```
**利点:**
- **高い開発者体験:** Rust開発者は、安全で慣用的なRustコードを書くことに集中できます。
- **安全性の向上:** `unsafe`コードをSDKに集約させることで、プラグイン開発者がメモリ安全性を破壊するリスクを大幅に低減できます。
#### 戦略3データ交換フォーマットの標準化
`invoke_fn` でやり取りされる `*const u8` の中身を標準化します。これにより、堅牢性と前方互換性が向上します。
**候補:**
- **Protocol Buffers / FlatBuffers:**
- **利点:** スキーマ定義が明確で、前方・後方互換性を考慮した設計が容易。多言語対応のコード生成ツールが豊富。パフォーマンスも高い。
- **欠点:** ビルドプロセスに一手間加わる。
- **JSON / MessagePack:**
- **利点:** 動的で柔軟。デバッグが容易。
- **欠点:** スキーマがないため、実行時エラーになりやすい。シリアライズ・デシリアライズのオーバーヘッドが比較的多きい。
**推奨:**
長期的なエコシステムの安定性を考えると、スキーマベースの **Protocol Buffers** または **FlatBuffers** が最適です。
#### 戦略4WebAssembly (Wasm) の採用検討
究極の目標として、プラグインをWebAssemblyモジュールとして実行する方式を検討する価値があります。
**利点:**
- **サンドボックス化:** WasmランタイムWasmtimeなど上でプラグインを実行することで、Nyash本体から完全に隔離された安全な環境を提供できます。悪意のあるプラグインやバグのあるプラグインがホストをクラッシュさせるのを防ぎます。
- **真の言語非依存:** C ABIよりもさらに多くの言語コンパイルターゲットにWASIを持つものがプラグイン開発に利用可能になります。
- **WASIとコンポーネントモデル:** WASIWebAssembly System InterfaceやWasm Component Modelの標準化が進むことで、ファイルアクセスやネットワークなどのホスト機能へのアクセスが標準化され、より洗練されたABIを構築できます。
**欠点:**
- **パフォーマンスオーバーヘッド:** C ABIのダイナミックライブラリ呼び出しに比べ、Wasmランタイムを介在させることによるオーバーヘッドが存在します。
- **実装の複雑さ:** Wasmランタイムの組み込みと、ホスト・ゲスト間のデータ共有特に複雑なデータ構造の実装は複雑になります。
### まとめと提言
Nyashは `extern "C"` の採用により、ABI安定化への正しい一歩を踏み出しています。長期的なエコシステムの繁栄のため、以下のロードマップを提案します。
1. **短期(すぐ着手すべき):**
- **C ABIの公式化:** `nyash-abi.h`相当の仕様を定義し、文書化する。
- **データフォーマットの選定:** `invoke_fn` で交換されるデータのシリアライズ形式としてProtocol Buffers等を導入する。
2. **中期(エコシステムの成長期):**
- **Rust向けSDKの提供:** `nyash-plugin-sdk` クレートを開発し、Rust開発者の体験を向上させる。
- **他言語向けSDKの提供:** 需要に応じて、PythonやGo向けのラッパーライブラリを提供する。
3. **長期(未来のビジョン):**
- **WebAssemblyへの移行:** パフォーマンス要件とセキュリティ要件を天秤にかけ、プラグイン実行環境をWasmに移行することを検討する。これにより、究極の安全性と移植性を獲得できる。
この戦略により、Nyashは安定性、開発者体験、安全性を段階的に向上させ、活発で持続可能なプラグインエコシステムを構築できると確信します。