Major changes: - LLVM backend initial implementation (compiler.rs, llvm mode) - Semantics layer integration in interpreter (operators.rs) - Phase 12 plugin architecture revision (3-layer system) - Builtin box removal preparation - MIR instruction set documentation (26→Core-15 migration) - Cross-backend testing infrastructure - Await/nowait syntax support New features: - LLVM AOT compilation support (--backend llvm) - Semantics layer for interpreter→VM flow - Tri-backend smoke tests - Plugin-only registry mode Bug fixes: - Interpreter plugin box arithmetic operations - Branch test returns incorrect values Documentation: - Phase 12 README.md updated with new plugin architecture - Removed obsolete NYIR proposals - Added LLVM test programs documentation Co-Authored-By: Claude <noreply@anthropic.com>
12 KiB
Gemini先生のNyash ABI戦略分析 (2025-09-01)
質問内容
Nyashプラグインシステムの ABI戦略について技術的相談です。
【背景】 Phase 12でNyashスクリプトプラグインシステムを実装中です。現在、プラグインインターフェースの設計で重要な判断が必要です。
【現状】
- 既存: C ABI(シンプル、高速、実績あり)
- 提案: Nyash ABI(3×u64構造体、型情報付き、拡張性高い)
【トレードオフ】 C ABI:
- 利点: ゼロオーバーヘッド、既存資産活用、シンプル
- 欠点: 型情報なし、拡張性限定、エラー処理が原始的
Nyash ABI:
- 利点: 型安全、メタデータ活用、将来性(async等)
- 欠点: pack/unpackのオーバーヘッド、新規学習コスト
【具体的な選択肢】
- Nyash ABIのみ(統一性重視)
- C ABI + Nyash ABI両方(実用性重視)
- その他の革新的アプローチ?
【質問】
- どちらの戦略が長期的に見て賢明でしょうか?
- 両方サポートする場合の複雑性は管理可能でしょうか?
- 他の言語での成功例・失敗例から学べることは?
- VM/JIT最適化の観点から、どちらが有利でしょうか?
Gemini先生の回答
NyashプラグインシステムにおけるABI戦略の技術的考察
Nyashプラグインシステムの長期的なエコシステム発展を見据えたABI(Application 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" ベースのアプローチを基礎としつつ、以下の戦略を段階的に導入することを提案します。
戦略1:C ABIを「公式な安定境界」として厳格に定義・文書化する
これは最も重要です。extern "C" を使っているというだけでは不十分で、それを公式な規約として確立する必要があります。
具体的なアクション:
-
ABI仕様書の作成:
nyash-abi.hのようなCヘッダーファイル形式で、プラグインが実装すべき関数シグネチャと、Nyashが提供するホスト関数(コールバック)をすべて定義します。invoke_fnで使われている*const u8,*mut u8のようなバイトポインタが、具体的にどのようなデータを指すのか(シリアライズ形式、レイアウトなど)を厳密に文書化します。NyashHostVtableのような構造体は、フィールドの順序と型が未来永劫変わらないことを保証しなければなりません。新しいホスト関数を追加する場合は、末尾にのみ追加します。- エラー報告の規約(戻り値の
i32が何を意味するのか)を定義します。
-
FFI(Foreign Function Interface)層の明確化:
- Nyash内部のRustコードと、C ABI境界を明確に分離します。
plugin_loader_v2.rsはこの役割を担っているようですが、この層は「ABI変換」にのみ責任を持つべきです。内部的なリファクタリングがABIに影響を与えないようにします。
- Nyash内部のRustコードと、C 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(クレート)を提供します。
具体的なアクション:
nyash-plugin-sdkクレートの作成:- このSDKは、低レベルなC ABIのやり取りをすべてカプセル化します。
- プラグイン開発者は、
Result<MyData, MyError>のような自然なRustの型を扱うだけで、SDKが自動的にC ABI準拠のデータ(バイト列やエラーコード)との相互変換を行います。 - 定型的なコードを自動生成するマクロ(例:
#[nyash_plugin])を提供し、開発体験を向上させます。
SDK利用者のコード(イメージ):
// プラグイン開発者が書くコード
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内部の実装(イメージ):
// #[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 が最適です。
戦略4:WebAssembly (Wasm) の採用検討
究極の目標として、プラグインをWebAssemblyモジュールとして実行する方式を検討する価値があります。
利点:
- サンドボックス化: Wasmランタイム(Wasmtimeなど)上でプラグインを実行することで、Nyash本体から完全に隔離された安全な環境を提供できます。悪意のあるプラグインやバグのあるプラグインがホストをクラッシュさせるのを防ぎます。
- 真の言語非依存: C ABIよりもさらに多くの言語(コンパイルターゲットにWASIを持つもの)がプラグイン開発に利用可能になります。
- WASIとコンポーネントモデル: WASI(WebAssembly System Interface)やWasm Component Modelの標準化が進むことで、ファイルアクセスやネットワークなどのホスト機能へのアクセスが標準化され、より洗練されたABIを構築できます。
欠点:
- パフォーマンスオーバーヘッド: C ABIのダイナミックライブラリ呼び出しに比べ、Wasmランタイムを介在させることによるオーバーヘッドが存在します。
- 実装の複雑さ: Wasmランタイムの組み込みと、ホスト・ゲスト間のデータ共有(特に複雑なデータ構造)の実装は複雑になります。
まとめと提言
Nyashは extern "C" の採用により、ABI安定化への正しい一歩を踏み出しています。長期的なエコシステムの繁栄のため、以下のロードマップを提案します。
-
短期(すぐ着手すべき):
- C ABIの公式化:
nyash-abi.h相当の仕様を定義し、文書化する。 - データフォーマットの選定:
invoke_fnで交換されるデータのシリアライズ形式としてProtocol Buffers等を導入する。
- C ABIの公式化:
-
中期(エコシステムの成長期):
- Rust向けSDKの提供:
nyash-plugin-sdkクレートを開発し、Rust開発者の体験を向上させる。 - 他言語向けSDKの提供: 需要に応じて、PythonやGo向けのラッパーライブラリを提供する。
- Rust向けSDKの提供:
-
長期(未来のビジョン):
- WebAssemblyへの移行: パフォーマンス要件とセキュリティ要件を天秤にかけ、プラグイン実行環境をWasmに移行することを検討する。これにより、究極の安全性と移植性を獲得できる。
この戦略により、Nyashは安定性、開発者体験、安全性を段階的に向上させ、活発で持続可能なプラグインエコシステムを構築できると確信します。