Files
hakorune/docs/archive/cleanup/PHASE1_IMPLEMENTATION_GUIDE.md
nyash-codex 2dfe363365 refactor(joinir): Phase 242-EX-A - Delete legacy Pattern3 lowerer
Remove hardcoded Pattern3 PoC implementation (loop_with_if_phi_minimal.rs)
and enhance if-sum mode to handle complex conditions like `i % 2 == 1`.

Key changes:
- condition_pattern.rs: Accept BinaryOp in comparison operands (+58 lines)
- loop_with_if_phi_if_sum.rs: Dynamic complex condition lowering (+147 lines)
- pattern3_with_if_phi.rs: Remove lower_pattern3_legacy() (-130 lines)
- loop_with_if_phi_minimal.rs: Delete entire file (-437 lines)
- loop_patterns/with_if_phi.rs: Update stub (-45 lines)
- mod.rs: Remove module reference (-4 lines)

Net reduction: -664 lines of hardcoded PoC code

Test results: 909/909 PASS (legacy mode completely removed)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 01:27:08 +09:00

519 lines
14 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.

Status: Historical
# Phase 1実装ガイド - 低リスク・高効果ヘルパー関数
## 概要
Phase 1では以下3つのヘルパー関数を実装し、約270-380行のコード削減を目指します。
1. Destination書き込みヘルパー150-200行削減
2. 引数検証ヘルパー100-150行削減
3. Receiver変換ヘルパー20-30行削減
**実装時間見込み**: 5-8時間
**リスク**: 低
**優先度**: 最高
---
## 実装手順
### Step 1: ユーティリティモジュール作成
```bash
# ディレクトリ構造作成
mkdir -p src/backend/mir_interpreter/utils
touch src/backend/mir_interpreter/utils/mod.rs
touch src/backend/mir_interpreter/utils/register_ops.rs
touch src/backend/mir_interpreter/utils/validation.rs
touch src/backend/mir_interpreter/utils/conversions.rs
```
---
### Step 2: register_ops.rs 実装
**ファイル**: `src/backend/mir_interpreter/utils/register_ops.rs`
```rust
use crate::backend::mir_interpreter::MirInterpreter;
use crate::box_trait::NyashBox;
use crate::mir::ValueId;
use crate::backend::mir_interpreter::VMValue;
impl MirInterpreter {
/// Write a VMValue result to destination register if present.
///
/// # Example
/// ```rust
/// self.write_result(dst, VMValue::Integer(42));
/// ```
pub(crate) fn write_result(&mut self, dst: Option<ValueId>, value: VMValue) {
if let Some(d) = dst {
self.regs.insert(d, value);
}
}
/// Write a NyashBox result to destination register (converted to VMValue::BoxRef).
///
/// # Example
/// ```rust
/// let result_box = string_box.trim();
/// self.write_box_result(dst, result_box);
/// ```
pub(crate) fn write_box_result(&mut self, dst: Option<ValueId>, boxed: Box<dyn NyashBox>) {
self.write_result(dst, VMValue::from_nyash_box(boxed));
}
/// Write VMValue::Void to destination register.
///
/// # Example
/// ```rust
/// arr.push(item);
/// self.write_void(dst); // push returns void
/// ```
pub(crate) fn write_void(&mut self, dst: Option<ValueId>) {
self.write_result(dst, VMValue::Void);
}
/// Write integer value to destination register.
pub(crate) fn write_integer(&mut self, dst: Option<ValueId>, value: i64) {
self.write_result(dst, VMValue::Integer(value));
}
/// Write boolean value to destination register.
pub(crate) fn write_bool(&mut self, dst: Option<ValueId>, value: bool) {
self.write_result(dst, VMValue::Bool(value));
}
/// Write string value to destination register.
pub(crate) fn write_string(&mut self, dst: Option<ValueId>, value: String) {
self.write_result(dst, VMValue::String(value));
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
#[test]
fn test_write_result_with_dst() {
let mut interp = MirInterpreter {
regs: HashMap::new(),
// ... other fields
};
let dst = Some(ValueId(42));
interp.write_result(dst, VMValue::Integer(100));
assert_eq!(interp.regs.get(&ValueId(42)), Some(&VMValue::Integer(100)));
}
#[test]
fn test_write_result_without_dst() {
let mut interp = MirInterpreter {
regs: HashMap::new(),
// ... other fields
};
interp.write_result(None, VMValue::Integer(100));
assert!(interp.regs.is_empty());
}
#[test]
fn test_write_void() {
let mut interp = MirInterpreter {
regs: HashMap::new(),
// ... other fields
};
let dst = Some(ValueId(10));
interp.write_void(dst);
assert_eq!(interp.regs.get(&ValueId(10)), Some(&VMValue::Void));
}
}
```
---
### Step 3: validation.rs 実装
**ファイル**: `src/backend/mir_interpreter/utils/validation.rs`
```rust
use crate::backend::mir_interpreter::{MirInterpreter, VMError};
use crate::mir::ValueId;
impl MirInterpreter {
/// Validate that the method call has exactly the expected number of arguments.
///
/// # Example
/// ```rust
/// self.validate_args_exact("push", args, 1)?;
/// let val = self.reg_load(args[0])?;
/// ```
///
/// # Errors
/// Returns `VMError::InvalidInstruction` if argument count doesn't match.
pub(crate) fn validate_args_exact(
&self,
method: &str,
args: &[ValueId],
expected: usize,
) -> Result<(), VMError> {
let actual = args.len();
if actual != expected {
return Err(VMError::InvalidInstruction(format!(
"{} expects exactly {} argument(s), but got {}",
method, expected, actual
)));
}
Ok(())
}
/// Validate that the method call has arguments within the expected range (inclusive).
///
/// # Example
/// ```rust
/// // substring accepts 1 or 2 arguments
/// self.validate_args_range("substring", args, 1, 2)?;
/// ```
///
/// # Errors
/// Returns `VMError::InvalidInstruction` if argument count is out of range.
pub(crate) fn validate_args_range(
&self,
method: &str,
args: &[ValueId],
min: usize,
max: usize,
) -> Result<(), VMError> {
let actual = args.len();
if actual < min || actual > max {
return Err(VMError::InvalidInstruction(format!(
"{} expects {}-{} argument(s), but got {}",
method, min, max, actual
)));
}
Ok(())
}
/// Validate that the method call has at least the minimum number of arguments.
///
/// # Example
/// ```rust
/// self.validate_args_min("varargs_method", args, 2)?;
/// ```
pub(crate) fn validate_args_min(
&self,
method: &str,
args: &[ValueId],
min: usize,
) -> Result<(), VMError> {
let actual = args.len();
if actual < min {
return Err(VMError::InvalidInstruction(format!(
"{} expects at least {} argument(s), but got {}",
method, min, actual
)));
}
Ok(())
}
/// Validate that the method call has no arguments.
///
/// # Example
/// ```rust
/// self.validate_args_empty("pop", args)?;
/// ```
pub(crate) fn validate_args_empty(&self, method: &str, args: &[ValueId]) -> Result<(), VMError> {
self.validate_args_exact(method, args, 0)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn dummy_interp() -> MirInterpreter {
// Create minimal interpreter for testing
MirInterpreter {
// ... minimal fields
}
}
#[test]
fn test_validate_args_exact_ok() {
let interp = dummy_interp();
let args = vec![ValueId(1)];
assert!(interp.validate_args_exact("push", &args, 1).is_ok());
}
#[test]
fn test_validate_args_exact_too_many() {
let interp = dummy_interp();
let args = vec![ValueId(1), ValueId(2)];
let result = interp.validate_args_exact("push", &args, 1);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("expects exactly 1"));
}
#[test]
fn test_validate_args_range_ok() {
let interp = dummy_interp();
let args = vec![ValueId(1), ValueId(2)];
assert!(interp.validate_args_range("substring", &args, 1, 2).is_ok());
}
#[test]
fn test_validate_args_range_too_few() {
let interp = dummy_interp();
let args = vec![];
let result = interp.validate_args_range("substring", &args, 1, 2);
assert!(result.is_err());
}
}
```
---
### Step 4: conversions.rs 実装
**ファイル**: `src/backend/mir_interpreter/utils/conversions.rs`
```rust
use crate::backend::mir_interpreter::{MirInterpreter, VMValue};
use crate::box_trait::NyashBox;
impl MirInterpreter {
/// Convert a VMValue to Box<dyn NyashBox>, handling both BoxRef and primitive types.
///
/// # Example
/// ```rust
/// let recv = self.reg_load(box_val)?;
/// let recv_box = self.convert_to_box(&recv);
/// ```
pub(crate) fn convert_to_box(&self, value: &VMValue) -> Box<dyn NyashBox> {
match value.clone() {
VMValue::BoxRef(b) => b.share_box(),
other => other.to_nyash_box(),
}
}
/// Convert and downcast to a specific box type.
///
/// # Example
/// ```rust
/// if let Some(arr) = self.downcast_box::<ArrayBox>(&recv)? {
/// // Handle ArrayBox methods
/// }
/// ```
pub(crate) fn downcast_box<T: NyashBox + 'static>(
&self,
value: &VMValue,
) -> Option<&T> {
let boxed = self.convert_to_box(value);
boxed.as_any().downcast_ref::<T>()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::boxes::array::ArrayBox;
#[test]
fn test_convert_to_box_from_primitive() {
let interp = dummy_interp();
let value = VMValue::Integer(42);
let boxed = interp.convert_to_box(&value);
// Should convert to IntegerBox
assert!(boxed.as_any().is::<crate::boxes::integer::IntegerBox>());
}
#[test]
fn test_convert_to_box_from_boxref() {
let interp = dummy_interp();
let arr = Box::new(ArrayBox::new());
let value = VMValue::BoxRef(arr.into());
let boxed = interp.convert_to_box(&value);
assert!(boxed.as_any().is::<ArrayBox>());
}
}
```
---
### Step 5: mod.rs 設定
**ファイル**: `src/backend/mir_interpreter/utils/mod.rs`
```rust
//! Utility functions for MIR interpreter handlers.
//!
//! This module provides common patterns used across handler implementations:
//! - Register operations (writing results)
//! - Argument validation
//! - Type conversions
mod conversions;
mod register_ops;
mod validation;
// Re-export for convenience (optional)
pub(crate) use conversions::*;
pub(crate) use register_ops::*;
pub(crate) use validation::*;
```
---
### Step 6: ハンドラー更新 - 段階的移行
#### 例: boxes_array.rs の更新
**Before**:
```rust
"push" => {
if args.len() != 1 {
return Err(VMError::InvalidInstruction("push expects 1 arg".into()));
}
let val = this.reg_load(args[0])?.to_nyash_box();
let _ = ab.push(val);
if let Some(d) = dst {
this.regs.insert(d, VMValue::Void);
}
return Ok(true);
}
```
**After**:
```rust
"push" => {
this.validate_args_exact("push", args, 1)?;
let val = this.reg_load(args[0])?.to_nyash_box();
let _ = ab.push(val);
this.write_void(dst);
return Ok(true);
}
```
**削減**: 6行 → 4行2行削減
---
## 移行チェックリスト
### Phase 1-A: インフラ構築1-2時間
- [ ] `utils/` ディレクトリ作成
- [ ] `mod.rs`, `register_ops.rs`, `validation.rs`, `conversions.rs` 実装
- [ ] ユニットテスト追加
- [ ] 全テストが通ることを確認
### Phase 1-B: Handler更新3-5時間
ファイル単位で更新し、都度テストを実行:
1. [ ] `boxes_array.rs` 更新(最も小さいファイルから開始)
- [ ] 引数検証を`validate_args_*`に置き換え
- [ ] Destination書き込みを`write_*`に置き換え
- [ ] Receiver変換を`convert_to_box`に置き換え
- [ ] テスト実行
2. [ ] `boxes_map.rs` 更新
3. [ ] `boxes_string.rs` 更新
4. [ ] `boxes_plugin.rs` 更新
5. [ ] `boxes_instance.rs` 更新
6. [ ] `boxes_object_fields.rs` 更新
7. [ ] `boxes.rs` 更新
8. [ ] `calls.rs` 更新(大きいファイルは最後に)
### Phase 1-C: 検証1時間
- [ ] 全ハンドラーファイルでパターン検索
```bash
# 残存する古いパターンを確認
grep -rn "if let Some(d) = dst { this.regs.insert" src/backend/mir_interpreter/handlers/
grep -rn "args.len() !=" src/backend/mir_interpreter/handlers/
grep -rn "match recv.clone()" src/backend/mir_interpreter/handlers/
```
- [ ] スモークテスト実行
```bash
./tools/jit_smoke.sh
```
- [ ] ドキュメント更新
---
## テスト戦略
### 単体テスト
各ユーティリティ関数に対して:
- 正常系(期待通りの動作)
- 異常系(エラーケース)
- 境界値0引数、大量引数など
### 回帰テスト
既存のスモークテストを活用:
```bash
# 変更前にベースライン取得
./tools/jit_smoke.sh > /tmp/before.log 2>&1
# 変更適用
# 変更後に結果比較
./tools/jit_smoke.sh > /tmp/after.log 2>&1
diff /tmp/before.log /tmp/after.log
```
---
## トラブルシューティング
### 問題: コンパイルエラー「method not found」
**原因**: `utils/mod.rs` のインポートが不足
**解決**: `src/backend/mir_interpreter/mod.rs` に以下を追加
```rust
mod utils;
```
### 問題: テスト実行時のパニック
**原因**: `MirInterpreter` の最小構成が不完全
**解決**: テスト用のビルダー関数を用意
```rust
#[cfg(test)]
fn test_interpreter() -> MirInterpreter {
MirInterpreter::new_for_test()
}
```
### 問題: 既存テストが失敗
**原因**: エラーメッセージの形式変更
**解決**: テストの期待値を更新(より良いメッセージに改善された)
---
## 期待される結果
### コード削減
- **boxes_array.rs**: 63行 → 約50行13行削減、21%減)
- **boxes_map.rs**: 134行 → 約110行24行削減、18%減)
- **boxes_string.rs**: 208行 → 約170行38行削減、18%減)
- **全体**: 約270-380行削減8-11%減)
### 品質向上
- エラーメッセージの統一
- テストカバレッジの向上
- 新機能追加の容易性
---
## 次のステップPhase 2
Phase 1完了後、以下を実施
1. Phase 2計画エラー生成、PHIヘルパー
2. 追加の共通化機会の調査
3. Phase 3Box Handler統合の詳細設計
---
**実装者**: (名前)
**開始日**: (日付)
**完了予定**: (日付)
**実績**: (完了日)