Files
hakorune/docs/development/current/main/phase131-2-consolebox-investigation.md
nyash-codex 05c5021147 docs(phase131): LLVM SSOT強化 + ConsoleBox調査完了 + Phase86-90要約
Phase 131-1 完了: LLVM exe line SSOT 強化
- phase87-selfhost-llvm-exe-line.md に 4セクション追加(+300行)
  - 環境変数リファレンス(14変数)
  - 成功/失敗基準(exit code 0/1/2/3)
  - コンパイラモード説明(harness vs crate)
  - デバッグセクション拡張
- "1コマンドで再現" 可能な状態を確立

Phase 131-2 完了: ConsoleBox 問題調査
- VM の 3重登録経路を特定(BoxFactoryRegistry/UnifiedRegistry/Builtin)
- LLVM backend は Phase 133 で解決済み
- 3つのドキュメント作成:
  - phase131-2-consolebox-investigation.md(詳細調査)
  - phase131-2-summary.md(エグゼクティブサマリ)
  - phase131-2-box-resolution-map.md(Box 解決マップ)

Phase 86-90 完了: Loop frontends 要約
- phase86-90-loop-frontends-summary.md 追加
- Pattern4/ContinueReturn/ParseStringComposite の経緯を1枚に集約
- INDEX から導線追加

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 05:24:31 +09:00

571 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Phase 131-2: ConsoleBox 問題根治調査レポート
## 🎯 調査目的
実アプリの E2E で最初に詰まる「Box 登録/認識」問題を特定し、VM/LLVM 共通の土台を確立する。
## 📊 調査結果サマリ
### 問題の本質
**ConsoleBox は既に両 backend で完全実装済み** - 問題は **登録メカニズムの分岐** にある:
1. **VM backend**: Box 解決が「UnifiedBoxRegistry入口 + BoxFactoryRegistryplugin provider mapping + VM fast path」の合成に見え、全体像が追いにくい
2. **LLVM backend**: 別の経路TypeRegistry/FFIを使用Phase 133 の事例あり)
3. **両者の規約不一致**: “どれをSSOTとして読むべきか” が docs/コードで分散している
### 重要な発見
#### ✅ LLVM 側は統合事例があるPhase 133 / archive
LLVM backend の ConsoleBox 問題は **Phase 133 で完全解決済み**:
- ConsoleLlvmBridge 箱化モジュール実装済み
- TypeRegistry との ABI 完全一致
- 7/7 テスト全て PASS
**結論**: LLVM 側は “参考モデル” として参照し、当面の焦点は VM 側の経路可視化と SSOT 明文化。
#### ⚠️ VM backend の Box 解決が「2層 + 特例」に見える(問題の根源)
VM backend では “入口” と “plugin provider mapping” が分かれており、加えて入口に特例がある:
```
1. BoxFactoryRegistry (src/runtime/box_registry.rs)
- Plugin-First アーキテクチャ
- プラグイン設定で上書き可能
- グローバルレジストリ
2. UnifiedBoxRegistry + global accessor
- global accessor: `src/runtime/unified_registry.rs`
- registry 本体: `src/box_factory/mod.rs`
- handle_new_box()VM NewBoxが使用
3. VM fast path特例
- `NYASH_VM_FAST=1` のとき `StringBox` をレジストリ経由せず生成bench/profile-only
```
## 🔍 詳細分析
### 1. VM Backend の Box 解決フロー
#### NewBox 命令処理src/backend/mir_interpreter/handlers/boxes.rs
```rust
pub(super) fn handle_new_box(
&mut self,
dst: ValueId,
box_type: &str,
args: &[ValueId],
) -> Result<(), VMError> {
// ① Provider Lock guard既定は挙動不変
provider_lock::guard_before_new_box(box_type)?;
// ② Fast path (StringBox のみ、NYASH_VM_FAST=1 時)
if box_type == "StringBox" { /* ... */ }
// ③ 統一レジストリ経由で生成 ← ここが主経路
let reg = unified_registry::get_global_unified_registry();
let created = reg.lock().unwrap().create_box(box_type, &converted)?;
// ④ 生成結果を VMValue に変換して格納
self.regs.insert(dst, VMValue::from_nyash_box(created));
Ok(())
}
```
**問題点**:
- `unified_registry``src/runtime/unified_registry.rs` にあり、`UnifiedBoxRegistry``src/box_factory/mod.rs`
- `UnifiedBoxRegistry``PluginBoxFactory``src/box_factory/plugin.rs`)を通じて `BoxFactoryRegistry` を参照するため、間接的に両者が接続されている
- Provider Lock の役割が曖昧(「既定は挙動不変」)
#### BoxFactoryRegistry の設計src/runtime/box_registry.rs
```rust
pub struct BoxFactoryRegistry {
providers: RwLock<HashMap<String, BoxProvider>>,
}
pub enum BoxProvider {
Builtin(BoxConstructor), // 互換用(テスト専用)
Plugin(String), // プラグイン実装
}
```
**特徴**:
- Plugin-First 設計(プラグインがビルトインを上書き可能)
- `apply_plugin_config()` で nyash.toml から動的登録
- `create_plugin_box()` → PluginHost 経由で実際の生成
**疑問**:
- このレジストリは `PluginBoxFactory` の provider mapping として使用されるVM NewBox → UnifiedBoxRegistry → PluginBoxFactory → BoxFactoryRegistry
- provider の populate は `src/runtime/plugin_loader_unified.rs` が行う(`apply_plugin_config`
#### Builtin Fallbacksrc/box_factory/builtin_impls/console_box.rs
```rust
/// Create builtin ConsoleBox instance
///
/// Primary: nyash-console-plugin
/// Fallback: This builtin implementation (selfhost support)
pub fn create(_args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, RuntimeError> {
// Phase 151: Quiet fallback (no deprecation warning)
Ok(Box::new(ConsoleBox::new()))
}
```
**用途**:
- セルフホスト Stage-3 パイプライン用
- プラグイン初期化失敗時のバックアップ
- Phase 151 で追加Phase 150 のエラー対処)
### 2. LLVM Backend の Box 解決フローPhase 133 完了済み)
#### ConsoleLlvmBridgesrc/llvm_py/console_bridge.py
```python
CONSOLE_METHODS = {
"log": "nyash.console.log",
"println": "nyash.console.log", # Phase 122: log のエイリアス
"warn": "nyash.console.warn",
"error": "nyash.console.error",
"clear": "nyash.console.clear",
}
def emit_console_call(builder, module, method_name, args, ...):
"""ConsoleBox method call to LLVM IR"""
if method_name not in CONSOLE_METHODS:
return False
runtime_fn_name = CONSOLE_METHODS[method_name]
# LLVM IR generation...
return True
```
**特徴**:
- Phase 133 で箱化モジュール化済み
- TypeRegistry の slot 400-403 と完全一致
- Phase 122 の println/log エイリアス統一を継承
#### TypeRegistry との連携src/runtime/type_registry.rs
```rust
// ConsoleBox 用 slot 定義
const CONSOLE_METHODS: &[MethodEntry] = &[
MethodEntry { name: "log", arity: 1, slot: 400 },
MethodEntry { name: "println", arity: 1, slot: 400 }, // エイリアス
MethodEntry { name: "warn", arity: 1, slot: 401 },
MethodEntry { name: "error", arity: 1, slot: 402 },
MethodEntry { name: "clear", arity: 0, slot: 403 },
];
```
**SSOT 化の成果**:
- ✅ メソッド名 → slot 番号のマッピングが一元管理
- ✅ VM/LLVM 両方で共通の型情報を参照
- ✅ Phase 122 のエイリアス統一が完全適用
### 3. Plugin System との連携
#### TypeBox v2 FFIplugins/nyash-console-plugin/src/lib.rs
```rust
#[no_mangle]
pub static nyash_typebox_ConsoleBox: NyashTypeBoxFfi = NyashTypeBoxFfi {
abi_tag: 0x54594258, // 'TYBX'
version: 1,
name: b"ConsoleBox\0".as_ptr() as *const c_char,
resolve: Some(console_resolve),
invoke_id: Some(console_invoke_id),
capabilities: 0,
};
```
**特徴**:
- TypeBox v2 FFI を実装
- `resolve()` でメソッド名 → ID 変換
- `invoke_id()` で実際のメソッド呼び出し
**問題点**:
- VM backend の `unified_registry` がこの FFI を使っているか不明
- プラグインローダーとの接続点が見えない
### 4. CoreBoxId システムPhase 87
#### CoreBoxId 定義src/runtime/core_box_ids.rs
```rust
pub enum CoreBoxId {
String, Integer, Bool, Array, Map, Console, // core_required
Float, Null, File, Path, Regex, Math, Time, Json, Toml, // core_optional
Function, Result, Method, Missing, // 特殊型
}
impl CoreBoxId {
pub fn is_core_required(&self) -> bool {
matches!(self, String | Integer | Bool | Array | Map | Console | File)
}
pub fn is_required_in(&self, profile: &RuntimeProfile) -> bool {
match profile {
RuntimeProfile::Default => self.is_core_required(),
RuntimeProfile::NoFs => self.is_core_required() && *self != Self::File,
}
}
}
```
**設計思想**:
- Core Box を型安全な enum で管理
- Phase 85 の core_required/core_optional 分類を実装
- Runtime profile による動的要件変更
**問題点**:
- この enum が **Box 登録の強制力** を持っているか不明
- `is_core_required()` の結果が実際の登録処理に反映されているか要確認
### 5. CoreServicesPhase 91
#### CoreServices 定義src/runtime/core_services.rs
```rust
pub trait ConsoleService: Send + Sync {
fn println(&self, msg: &str);
fn print(&self, msg: &str);
}
pub struct CoreServices {
pub string: Option<Arc<dyn StringService>>,
pub integer: Option<Arc<dyn IntegerService>>,
pub bool: Option<Arc<dyn BoolService>>,
pub array: Option<Arc<dyn ArrayService>>,
pub map: Option<Arc<dyn MapService>>,
pub console: Option<Arc<dyn ConsoleService>>,
}
```
**特徴**:
- Ring1-Core の Service trait 群
- Phase 87 CoreBoxId の core_required をカバー
- Optional 化により Graceful Degradation 対応
**疑問**:
- この Service trait は **誰が実装しているのか?**
- VM の Box 呼び出しがこの trait を経由するのか?
## 🚨 発見した問題点
### 1. **SSOT の欠如(最重要)**
Box 登録規約が複数箇所に分散:
```
┌─ BoxFactoryRegistry (グローバル)
├─ UnifiedBoxRegistry (NewBox の入口)
├─ BuiltinBoxFactory (UnifiedBoxRegistry 内の factory)
├─ CoreBoxId enum (型定義)
├─ CoreServices (Service trait)
└─ TypeRegistry (メソッド情報)
```
**問題**:
- どのレジストリが「正」なのか不明
- 登録順序・優先度の規約がない
- 初期化タイミングが散在
### 2. **VM と LLVM の分岐**
| 項目 | VM Backend | LLVM Backend |
|------|-----------|--------------|
| Box 登録 | UnifiedBoxRegistry + BoxFactoryRegistry | TypeRegistry + Plugin FFI |
| メソッド解決 | BoxCall handler | ConsoleLlvmBridge |
| ABI | NyashBox trait | i8* + i64 |
| SSOT 化 | ❌ 不明 | ✅ Phase 133 完了 |
### 3. **失敗パターンの特定**
Phase 150/151 で報告されたエラー:
```
[ERROR] ❌ [rust-vm] VM error: Invalid instruction: NewBox ConsoleBox:
invalid operation: Unknown Box type: ConsoleBox. Available: Main
```
**原因推測**:
1. セルフホスト経由時にプラグイン初期化が失敗
2. plugin provider mappingBoxFactoryRegistryが未設定で PluginBoxFactory が失敗
3. BuiltinBoxFactory が無効(`plugins-only`)または該当 Box が builtin factory に登録されていない
**Phase 151 の対処**:
- `builtin_impls/console_box.rs` を builtin 実装として追加/整備UnifiedBoxRegistry 内の BuiltinBoxFactory 経由)
- "Available: Main" → ConsoleBox が登録されるようになった
## 💡 SSOT 化提案
### 設計原則
1. **Single Source of Truth**: Box 登録規約を一箇所に集約
2. **Explicit Dependencies**: 依存関係を明示化
3. **Fail-Fast**: エラーは早期に明示的に失敗
4. **Box-First**: Phase 33 の箱理論に基づく設計
### 提案 A: UnifiedBoxRegistry を入口SSOTとして強化保守的アプローチ
```rust
// src/runtime/unified_registry.rs / src/box_factory/mod.rs を “入口SSOT” として扱い、
// provider mapping や core_required 検証の責務をどこまで寄せるかを整理する。
pub struct UnifiedBoxRegistry {
// SSOT: すべての Box 登録情報
factories: RwLock<BTreeMap<String, BoxProvider>>,
// CoreBoxId による必須 Box 検証
core_validator: CoreBoxValidator,
// プラグインローダーとの接続
plugin_loader: Arc<PluginHost>,
}
impl UnifiedBoxRegistry {
/// 初期化時に core_required Box を強制登録
pub fn new(profile: RuntimeProfile) -> Self {
let mut reg = Self { /* ... */ };
// CoreBoxId に基づく必須 Box 登録
for box_id in CoreBoxId::iter() {
if box_id.is_required_in(&profile) {
reg.register_core_box(box_id)?;
}
}
reg
}
/// Box 生成(優先順位: Plugin > Builtin > Error
pub fn create_box(&self, name: &str, args: &[Box<dyn NyashBox>])
-> Result<Box<dyn NyashBox>, RuntimeError>
{
// 1. プラグイン検索
if let Some(provider) = self.factories.read().unwrap().get(name) {
match provider {
BoxProvider::Plugin(plugin_name) => {
return self.plugin_loader.create_box(name, args);
}
BoxProvider::Builtin(constructor) => {
return constructor(args);
}
}
}
// 2. エラーSSOT なので見つからない = 存在しない)
Err(RuntimeError::InvalidOperation {
message: format!("Unknown Box type: {}", name)
})
}
/// プラグイン設定の適用nyash.toml から)
pub fn apply_plugin_config(&mut self, config: &PluginConfig) {
for (box_name, plugin_name) in &config.plugins {
self.factories.write().unwrap()
.insert(box_name.clone(), BoxProvider::Plugin(plugin_name.clone()));
}
}
}
```
**利点**:
- 既存コードへの影響最小
- CoreBoxId による型安全性
- プラグイン優先順位の明確化
### 提案 B: CoreBoxRegistry 新設(理想的アプローチ)
```rust
// src/runtime/core_box_registry.rs (新規)
/// Phase 131-2: Core Box 登録の SSOT
pub struct CoreBoxRegistry {
// CoreBoxId → 登録情報のマッピング
core_boxes: RwLock<HashMap<CoreBoxId, CoreBoxEntry>>,
// ユーザー定義 BoxCoreBoxId 以外)
user_boxes: RwLock<HashMap<String, UserBoxEntry>>,
}
struct CoreBoxEntry {
box_id: CoreBoxId,
provider: CoreBoxProvider,
metadata: CoreBoxMetadata,
}
enum CoreBoxProvider {
Plugin { plugin_name: String, type_id: u32 },
Builtin { constructor: fn(&[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, RuntimeError> },
}
impl CoreBoxRegistry {
/// 初期化時の必須 Box 検証
pub fn validate_core_boxes(&self, profile: &RuntimeProfile) -> Result<(), String> {
let missing: Vec<_> = CoreBoxId::iter()
.filter(|id| id.is_required_in(profile))
.filter(|id| !self.has_core_box(*id))
.collect();
if !missing.is_empty() {
return Err(format!("Missing core_required boxes: {:?}", missing));
}
Ok(())
}
/// Box 生成(型安全)
pub fn create_core_box(&self, box_id: CoreBoxId, args: &[Box<dyn NyashBox>])
-> Result<Box<dyn NyashBox>, RuntimeError>
{
let entry = self.core_boxes.read().unwrap()
.get(&box_id)
.ok_or_else(|| RuntimeError::InvalidOperation {
message: format!("Core box not registered: {:?}", box_id)
})?
.clone();
match entry.provider {
CoreBoxProvider::Plugin { plugin_name, type_id } => {
self.create_plugin_box(&plugin_name, type_id, args)
}
CoreBoxProvider::Builtin { constructor } => {
constructor(args)
}
}
}
}
```
**利点**:
- CoreBoxId による完全な型安全性
- 必須 Box の起動時検証
- プラグイン vs ビルトインの明確な分離
## 🎯 次のステップ
### Phase 131-3: SSOT 実装(推奨)
1. **入口SSOTの確定VM NewBox**
- 入口は `src/backend/mir_interpreter/handlers/boxes.rs::handle_new_box`
`src/runtime/unified_registry.rs` / `src/box_factory/mod.rs`UnifiedBoxRegistryで固定する
- BoxFactoryRegistry は “plugin provider mapping” として位置付け、関係図を SSOT 化する
2. **CoreBoxId との統合**
- `CoreBoxValidator` 実装
- 起動時の必須 Box 検証
3. **プラグインローダーとの接続明確化**
- plugin_loader_unified が BoxFactoryRegistry を populate し、
PluginBoxFactoryUnifiedBoxRegistry 内)が BoxFactoryRegistry を参照する流れを明文化
- TypeBox v2 FFI との整合method/type id の参照箇所)を整理
4. **既存 BoxFactoryRegistry の整理**
- provider mapping を BoxFactoryRegistry に閉じるか、UnifiedBoxRegistry に吸収するかを決める
- 移行ガイドの作成
### Phase 131-4: テストケース追加
```rust
#[test]
fn test_core_box_registration() {
let reg = UnifiedBoxRegistry::new(RuntimeProfile::Default);
// core_required が全て登録されていること
for box_id in CoreBoxId::iter() {
if box_id.is_core_required() {
assert!(reg.has_box(box_id.name()));
}
}
// ConsoleBox が生成できること
let console = reg.create_box("ConsoleBox", &[]).unwrap();
assert_eq!(console.type_name(), "ConsoleBox");
}
#[test]
fn test_plugin_priority() {
let mut reg = UnifiedBoxRegistry::new(RuntimeProfile::Default);
// ビルトイン ConsoleBox 登録済み
assert!(reg.has_box("ConsoleBox"));
// プラグイン設定で上書き
let mut config = PluginConfig::default();
config.plugins.insert("ConsoleBox".to_string(), "nyash-console".to_string());
reg.apply_plugin_config(&config);
// プラグイン版が優先されること
let console = reg.create_box("ConsoleBox", &[]).unwrap();
// プラグイン判定ロジック(実装依存)
}
```
## 📋 関連ファイル一覧
### VM Backend
- `src/runtime/box_registry.rs` - BoxFactoryRegistry
- `src/runtime/unified_registry.rs` - global UnifiedBoxRegistry accessor
- `src/box_factory/mod.rs` - UnifiedBoxRegistryFactoryPolicy / create_box
- `src/box_factory/plugin.rs` - PluginBoxFactoryBoxFactoryRegistry を参照)
- `src/runtime/core_box_ids.rs` - CoreBoxId enum
- `src/runtime/core_services.rs` - CoreServices trait
- `src/runtime/type_registry.rs` - TypeRegistry (メソッド情報)
- `src/backend/mir_interpreter/handlers/boxes.rs` - handle_new_box()
- `src/box_factory/builtin_impls/console_box.rs` - Builtin 実装BuiltinBoxFactory 経由)
### LLVM Backend
- `src/llvm_py/console_bridge.py` - ConsoleLlvmBridgePhase 133
- `src/llvm_py/instructions/boxcall.py` - BoxCall lowering
### Plugin System
- `plugins/nyash-console-plugin/src/lib.rs` - TypeBox v2 FFI
- `src/runtime/plugin_host.rs` - PluginHost
### Core Infrastructure
- `src/boxes/console_box.rs` - ConsoleBox 実装
- `crates/nyash_kernel/src/lib.rs` - NyRT ランタイム
## 🔍 未解決の疑問
1. **UnifiedBoxRegistry ↔ BoxFactoryRegistry の責務境界**
- provider mapping の SSOT をどこに置くかBoxFactoryRegistry を残す/吸収する)
- `UnifiedBoxRegistry::FactoryPolicy` と plugin_config の関係をどう可視化するか
2. **Provider Lock の役割**
- `provider_lock::guard_before_new_box()` の実装と目的
- 「既定は挙動不変」の意味
3. **CoreServices の実装者**
- ConsoleService trait を実装しているのは誰か
- VM の Box 呼び出しがこの trait を経由するのか
4. **初期化順序**
- プラグインローダー、レジストリ、サービスの初期化タイミング
- 循環依存の有無
## ✅ 成果
### 明確になった事実
1. **LLVM backend は問題なし** - Phase 133 で完全解決済み
2. **VM backend の Box 解決が分散して見える** - UnifiedBoxRegistry入口と BoxFactoryRegistryprovider mappingの関係を SSOT 化する必要がある
3. **ConsoleBox 自体は完全実装** - 登録メカニズムの問題
4. **CoreBoxId システム存在** - 型安全性の基盤あり
### 提案した解決策
1. **UnifiedBoxRegistry 強化** - 保守的アプローチ
2. **CoreBoxRegistry 新設** - 理想的アプローチ
3. **起動時検証** - Fail-Fast 原則の実現
---
**Status**: Investigation Complete - Ready for Implementation
**Next Phase**: 131-3 (SSOT Implementation)
**Estimated Time**: 4-6 hours