Files
hakorune/docs/archive/consultations/codex/2025-08-21-codex-response-full.md
Moe Charm cc2a820af7 feat(plugin): Fix plugin BoxRef return and Box argument support
- Fixed deadlock in FileBox plugin copyFrom implementation (single lock)
- Added TLV Handle (tag=8) parsing in calls.rs for returned BoxRefs
- Improved plugin loader with config path consistency and detailed logging
- Fixed loader routing for proper Handle type_id/fini_method_id resolution
- Added detailed logging for TLV encoding/decoding in plugin_loader_v2

Test docs/examples/plugin_boxref_return.nyash now works correctly:
- cloneSelf() returns FileBox Handle properly
- copyFrom(Box) accepts plugin Box arguments
- Both FileBox instances close and fini correctly

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-21 00:41:26 +09:00

11 KiB
Raw Blame History

Codex exec 天才的分析回答 (2025-08-21)

モデル: GPT-5
実行時間: 2025-08-20T08:13:13 - 08:14:35
トークン使用: 14,603 (入力: 6,325 + 出力: 8,278)

結論

依存の向きと責務を正し、Interpreter/VMの「実装詳細共有」をやめて「モデル共有・実行時共有」に転換します。鍵は「Everything is Box」をタイプシステムとクラス実装にも一貫させつつ、「AST/宣言モデル」と「Runtimeクラス・インスタンス」を厳密に分離することです。

層構造

  • AST/Model: 言語の純データモデル。依存先なし。
  • Runtime: 型系・クラス登録・インスタンス生成・ライフサイクル・呼出し規約。ASTにのみ依存。
  • Backends: Interpreter と VM。どちらも Runtime にのみ依存ASTは入力として読むが、構造体はRuntimeを介して使う
  • Plugins: BoxClass/Factory を提供。RuntimeのAPIにのみ依存。グローバルへは一切依存しない。

依存関係: AST → Runtime → Interpreter/VMPlugins は Runtime へ)

各質問への回答

1) アーキテクチャ再設計

境界の引き直し:

  • AST/Model: 宣言とシグネチャの「静的情報」だけを保持。
  • Runtime: クラス定義vtable/ディスパッチ、レジストリ、インスタンス化、birth/fini、呼出し規約、セッションFiber/Frame等。
  • Backends: 実行方式AST直解釈 or MIR/Bytecodeだけを差し替える。Boxは常にRuntimeで生成・管理。

セッション単位:

  • NyashRuntime は「不変に近い共有資産」(クラス登録、型定義、グローバルな定数等)。
  • ExecutionSession は「実行ごとの可変状態」(root/global box、スタック、環境変数、I/O handles等)。Interpreter/VMどちらも同じセッション型を使う。

2) BoxDeclaration の適切配置

場所: ast::model(もしくは core::modelに移動。Interpreter固有の型は参照しない。

必要な情報(純データ):

  • name: String
  • type_params: Vec<TypeParam>
  • fields: Vec<FieldDecl { name, ty: TypeRef, attrs }>
  • methods: Vec<MethodDecl { name, sig: FnSig, body: FnBodyRef }>
  • static_methods: Vec<StaticDecl { name, sig: FnSig, body: FnBodyRef }>
  • attrs: AttrSet
  • source_span: Option<Span>(任意)

不要な情報AST層から排除:

  • 実行時ハンドル(Arc<dyn NyashBox>InstanceBox 等)
  • 共有状態やミューテックス
  • インタープリターのクロージャ/関数ポインタ

Body表現:

  • AST直実行であれば FnBodyRef::Ast(AstNodeId)
  • VM/MIR 変換後は FnBodyRef::Mir(MirFuncId) を追加可能(後方互換のため enum

3) SharedState の解体

役割で分割:

ランタイム共通へ移すもの:

  • box_declarationsNyashRuntime.type_space(宣言と型定義)
  • static_box_definitionsNyashRuntime.class_space(宣言→クラスの束縛)
  • static_functionsNyashRuntime.fn_space(宣言・シグネチャ中心。実行体は FnBodyRef
  • included_files → ビルド/ロードフェーズの ModuleLoaderState(実行時からは外す)

実行ごとの状態:

  • global_boxExecutionSession.root_box: SharedBoxInterpreter/VM共通

新コントラクト:

  • NyashRuntime は基本不変。Arcで共有。
  • 変更を伴うロード時は NyashRuntimeBuilder を使い、確定後に NyashRuntime をクローン共有。
  • 実行時は ExecutionSession が唯一の可変中核スタック、環境、root/global box など)。

4) BoxFactory/Registry の統一設計

コアの型

trait NyashBox: Any + Send + Sync {
    // インスタンスの振る舞い
    // get_field/set_field/call_method 等
    // as_any で downcast 可能
}

trait BoxClass: Send + Sync {
    fn name(&self) -> &str;
    fn instantiate(&self, args: &[SharedBox], sess: &mut ExecutionSession) -> Result<SharedBox>;
    fn lookup_method(&self, name: &str) -> Option<MethodHandle>;
    fn lifecycle() -> Option<&dyn BoxLifecycle>;
}

trait BoxFactory: Send + Sync {
    fn can_build(&self, decl: &BoxDeclaration) -> bool;
    fn build_class(&self, decl: &BoxDeclaration, rt: &NyashRuntime) -> Result<Arc<dyn BoxClass>>;
}

レジストリ

  • BoxRegistryRuntime内
    • register_class(class: Arc<dyn BoxClass>)
    • get_class(name: &str) -> Option<Arc<dyn BoxClass>>
    • register_factory(factory: Arc<dyn BoxFactory>)
    • materialize_from_decl(decl: &BoxDeclaration)(必要に応じ lazy materialize

ライフサイクルbirth/fini

  • trait BoxLifecycle { fn on_birth(&self, &mut InstanceCtx) -> Result<()>; fn on_fini(&self, &mut InstanceCtx); }
  • BoxClass::lifecycle() -> Option<&dyn BoxLifecycle>
  • 生成は instantiate の内部で birth を呼ぶ。破棄は Drop フックか Sessionon_scope_exit で fini を呼ぶ。

プラグイン

  • Runtimeに明示登録するのみグローバル関数禁止
    • NyashRuntimeBuilder::with_factory(Arc<dyn BoxFactory>)
  • 動的ロードは別フェーズfeature flagで opt-in。基本は静的リンク/明示登録。

dyn の注意点:

  • VM/Interpreter ともに Arc<dyn BoxFactory> / Arc<dyn BoxClass> / SharedBox = Arc<dyn NyashBox> を使用。Arc<BoxFactory> は使わない。

5) 具体的な実装手順(最小破壊で段階導入)

Step 1: BoxDeclaration の移動

  • interpreter::BoxDeclarationcore::model::BoxDeclaration へ抽出。
  • インタープリターは use core::model::BoxDeclaration as InterpreterBoxDecl の一時別名でコンパイル維持。

Step 2: NyashRuntime の導入(空の骨組み→段階拡張)

  • NyashRuntime { box_registry, type_space, fn_space } を追加。
  • NyashRuntimeBuilder を作り、現行の SharedState から必要データを移送できるようにする。

Step 3: BoxRegistry と Factory の dyn 化

  • BoxFactory/BoxClass を上記インターフェースへ統一dyn前提
  • UserDefinedBoxFactoryArc<dyn BoxFactory> で登録するようにリライト。

Step 4: グローバル登録の排除

  • register_user_defined_factory(...) のようなグローバル関数は削除/非推奨に。
  • 代わりに NyashRuntimeBuilder::with_factory(...) を使用。

Step 5: SharedState の分解

  • global_boxExecutionSession.root_box に移動。
  • 残るマップ類をそれぞれ type_space / fn_space / class_space へ移譲。
  • Interpreter 側は SharedStateShim { runtime: Arc<NyashRuntime>, session: ExecutionSession } を暫定導入して最小変更で通す。

Step 6: Interpreter/VM のコンストラクタ統一

  • NyashInterpreter::new(runtime: Arc<NyashRuntime>)
  • VM::new(runtime: Arc<NyashRuntime>)
  • 双方 ExecutionSession::new(runtime.clone()) から root などを初期化。

Step 7: birth/fini の統一呼出し点

  • すべてのインスタンス生成を BoxClass::instantiate 経由に統一。
  • 破棄時の fini 呼出しを ExecutionSession のスコープ管理に集約。

Step 8: 段階的移行と互換

  • 旧APIの薄いアダプタを用意例えば impl From<SharedState> for NyashRuntimeBuilder)。
  • 段階ごとにテストが通るかを確認し、最後に SharedState を削除。

各論の要点

依存関係の整理

  • VM→Interpreter 依存を完全に撤去。両者は Runtime のみを参照。
  • Interpreter固有の「実行戦略」は Backend に閉じ込め、Box管理・生成は Runtime に一元化。
  • ASTNode は Backend の評価対象だが、Box生成はすべて BoxRegistry を介す。

Everything is Box の貫徹(シンプルに)

  • 値は SharedBox = Arc<dyn NyashBox>
  • 型/クラスは値ではなく「クラスオブジェクト」として Arc<dyn BoxClass>(必要であればメタ階層で ClassBox を実装して値化できるが、初期段階では過剰にしない)。
  • つまり「実行時に扱う実体は必ず Box」、クラスは Box を生み出す工場であり、インスタンスはランタイム一元管理。

テスタビリティと並行実行

  • ランタイムは不変に近い Arc 共有、セッションは疎結合で複数同時に生成可能。
  • グローバル無効化、明示 DIRuntime/Session 引数渡し)。
  • テストでは毎回 NyashRuntimeBuilder から新鮮な Runtime を生成可能。並行テストで衝突なし。

VM実装の容易化

  • VM は Bytecode/MIR のみを意識。Box生成/呼出しは Runtime の BoxClass/Registry に任せる。
  • birth/fini も instantiate/Session に含めるため、VM は命令列の実行に集中できる。

参考スケルトン

ランタイム

pub struct NyashRuntime { 
    box_registry: BoxRegistry, 
    type_space: TypeSpace, 
    fn_space: FnSpace 
}

pub struct ExecutionSession { 
    runtime: Arc<NyashRuntime>, 
    root_box: SharedBox, 
    frames: Vec<Frame>, 
    env: Env 
}

レジストリ

pub struct BoxRegistry { 
    classes: RwLock<HashMap<String, Arc<dyn BoxClass>>>, 
    factories: RwLock<Vec<Arc<dyn BoxFactory>>> 
}

トレイト

pub trait NyashBox { 
    fn get_field(...); 
    fn set_field(...); 
    fn call_method(...); 
    fn as_any(&self) -> &dyn Any; 
}

pub trait BoxClass { 
    fn name(&self) -> &str; 
    fn instantiate(&self, args: &[SharedBox], sess: &mut ExecutionSession) -> Result<SharedBox>; 
    fn lookup_method(&self, name: &str) -> Option<MethodHandle>; 
    fn lifecycle(&self) -> Option<&dyn BoxLifecycle>; 
}

pub trait BoxFactory { 
    fn can_build(&self, decl: &BoxDeclaration) -> bool; 
    fn build_class(&self, decl: &BoxDeclaration, rt: &NyashRuntime) -> Result<Arc<dyn BoxClass>>; 
}

pub trait BoxLifecycle { 
    fn on_birth(&self, ctx: &mut InstanceCtx) -> Result<()>; 
    fn on_fini(&self, ctx: &mut InstanceCtx); 
}

実装上の落とし穴と回避

  • trait object の型: 必ず Arc<dyn Trait>Arc<Trait> は不可。dyn を徹底。
  • InstanceBox の役割: もし「ユーザー定義のレコード/オブジェクト」の具象表現なら、それ自体は NyashBox 実装。VM/Interpreter は InstanceBox のレイアウトを知らなくてよい(NyashBox 経由で操作)。
  • 共有ロックの過剰化: Runtime は基本不変。レジストリも初期化後は読み取り中心。クラス遅延構築だけ RwLock で十分。
  • birth/fini の二重呼び出し防止: instantiate/Drop の責務を一本化。Session スコープまたぎの移動に注意。

最後に

この設計は「モデル(宣言)と実装(クラス/インスタンス」の責務を分け、Interpreter/VM の接合点を Runtime に集約します。グローバル副作用を除去し、Arc<dyn ...> に統一することで、VM と Interpreter の相互依存が解消され、テスト・並行実行が自然になります。段階的移行のステップを踏めば、最小限の変更で最大の整理効果を得られます。

必要なら、上記スケルトンに合わせた最初のパッチ(core::model::BoxDeclarationNyashRuntime/Builder の最小骨組み)まで作成します。