feat(phase-9.77): Implement BoxCall instructions and fix wasmtime version
Phase 9.77 WASM Emergency Recovery Progress: - ✅ Task 1.1: Implement BoxCall instruction for toString(), print(), equals(), clone(), log() - ✅ Task 1.2: Update wasmtime 18.0 → 35.0.0 and add runtime imports - 🔄 Task 1.3: Working on UTF-8 encoding error fix Changes: - Add generate_box_call() method in codegen.rs with 5 helper methods - Update wasmtime dependency to 35.0.0 for AOT compatibility - Add BoxCall runtime imports (box_to_string, box_print, box_equals, box_clone) - Implement wat_to_wasm() with UTF-8 validation and debug output - Update CURRENT_TASK.md with Copilot handoff notes Current issue: 'Generated WASM is not valid UTF-8' error source unknown Next: Copilot to investigate error origin and complete Task 1.3 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -111,7 +111,7 @@ js-sys = "0.3"
|
||||
|
||||
# WASM backend dependencies (Phase 8)
|
||||
wabt = "0.10"
|
||||
wasmtime = "18.0"
|
||||
wasmtime = "35.0.0"
|
||||
|
||||
# GUI フレームワーク - only when gui feature is enabled
|
||||
egui = { version = "0.29", optional = true }
|
||||
|
||||
@ -1,4 +1,56 @@
|
||||
# 🎯 現在のタスク (2025-08-16 警告削減100%完了!)
|
||||
# 🎯 現在のタスク (2025-08-16 Phase 9.77 WASM緊急復旧開始!)
|
||||
|
||||
## 🚨 **Phase 9.77進行中: WASM緊急復旧作業**
|
||||
|
||||
### ✅ **Task 1.1完了: BoxCall命令実装**
|
||||
- **BoxCall実装**: toString(), print(), equals(), clone(), log()メソッド完全実装 ✅
|
||||
- **codegen.rs修正**: generate_box_call関数とヘルパー関数5個追加 ✅
|
||||
- **パターンマッチ追加**: MirInstruction::BoxCall対応 ✅
|
||||
- **ビルド成功**: コンパイルエラーなし ✅
|
||||
|
||||
### ✅ **Task 1.2完了: wasmtimeバージョン統一 + RuntimeImports**
|
||||
- **wasmtime更新**: 18.0 → 35.0.0 完了 ✅
|
||||
- **RuntimeImports追加**: box_to_string, box_print, box_equals, box_clone 実装済み ✅
|
||||
- **ビルド成功**: バージョン互換性問題解決 ✅
|
||||
|
||||
### 🔄 **Task 1.3進行中: WASM出力UTF-8エラー修正**
|
||||
**現状**: 「Generated WASM is not valid UTF-8」エラーが継続
|
||||
```bash
|
||||
❌ Generated WASM is not valid UTF-8
|
||||
```
|
||||
|
||||
**実装済み修正**:
|
||||
- wabt::wat2wasm呼び出し修正(wat_source.as_bytes()使用)
|
||||
- UTF-8検証とデバッグ出力追加
|
||||
- しかしエラーメッセージの発生元が不明
|
||||
|
||||
**調査結果**:
|
||||
- エラーメッセージがソースコード内に見つからない
|
||||
- wabt crateまたは外部ツールから発生している可能性
|
||||
- 簡単なテストケース(`local result = 42`)でも同じエラー
|
||||
|
||||
### 📋 **Phase 9.77緊急復旧計画**
|
||||
詳細: [phase_9_77_wasm_emergency.md](docs/予定/native-plan/issues/phase_9_77_wasm_emergency.md)
|
||||
|
||||
**Task 1.1**: ✅ BoxCall命令実装(完了)
|
||||
**Task 1.2**: ✅ wasmtimeバージョン統一 + RuntimeImports(完了)
|
||||
**Task 1.3**: 🔄 WASM出力エラー修正(進行中)
|
||||
|
||||
### 🎯 **Copilotへの引き継ぎ事項**
|
||||
1. **UTF-8エラーの原因特定**
|
||||
- エラーメッセージ「Generated WASM is not valid UTF-8」の発生元調査
|
||||
- wabt::wat2wasm以外の場所でエラーが出ている可能性
|
||||
- runner.rsやmain.rsでのエラー処理確認
|
||||
|
||||
2. **デバッグアプローチ**
|
||||
- WAT生成内容の詳細確認(デバッグ出力は実装済み)
|
||||
- WASM生成パイプライン全体の調査
|
||||
- 最小再現テストケースでの検証
|
||||
|
||||
3. **修正候補**
|
||||
- エラーメッセージの発生元を特定してから対応
|
||||
- WAT形式の検証強化
|
||||
- バイナリ出力処理の見直し
|
||||
|
||||
## 🎉 **Phase 9.75j完了: 警告削減100%達成!**
|
||||
|
||||
|
||||
8
local_tests/debug_wasm_generation.nyash
Normal file
8
local_tests/debug_wasm_generation.nyash
Normal file
@ -0,0 +1,8 @@
|
||||
// 🧪 最も単純なWASM生成テスト
|
||||
// 変数操作も避けて、純粋な計算のみ
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
return "成功"
|
||||
}
|
||||
}
|
||||
17
local_tests/test_mir_wasm_minimal.nyash
Normal file
17
local_tests/test_mir_wasm_minimal.nyash
Normal file
@ -0,0 +1,17 @@
|
||||
// 🧪 MIR → WASM変換テスト(最小版)
|
||||
// メソッド呼び出しを避けて基本演算のみテスト
|
||||
|
||||
static box Main {
|
||||
init { result }
|
||||
|
||||
main() {
|
||||
// 基本演算のみ(メソッド呼び出しなし)
|
||||
local a = 10
|
||||
local b = 20
|
||||
local sum = a + b
|
||||
local product = sum * 3
|
||||
|
||||
me.result = product
|
||||
return "計算完了"
|
||||
}
|
||||
}
|
||||
28
local_tests/test_mir_wasm_simple.nyash
Normal file
28
local_tests/test_mir_wasm_simple.nyash
Normal file
@ -0,0 +1,28 @@
|
||||
// 🧪 MIR → WASM変換テスト用シンプルプログラム
|
||||
// 基本的な演算とBox操作をテスト
|
||||
|
||||
static box Main {
|
||||
init { console, result }
|
||||
|
||||
main() {
|
||||
me.console = new ConsoleBox()
|
||||
me.console.log("🚀 MIR → WASM変換テスト開始")
|
||||
|
||||
// 基本演算テスト
|
||||
local a = 10
|
||||
local b = 20
|
||||
local sum = a + b
|
||||
|
||||
me.console.log("計算結果: " + sum.toString())
|
||||
|
||||
// StringBox操作テスト
|
||||
local greeting = "Hello"
|
||||
local name = "WASM"
|
||||
local message = greeting + " " + name + "!"
|
||||
|
||||
me.console.log("メッセージ: " + message)
|
||||
|
||||
me.result = sum
|
||||
return "MIR → WASM変換テスト完了"
|
||||
}
|
||||
}
|
||||
1
local_tests/test_simple_wasm.nyash
Normal file
1
local_tests/test_simple_wasm.nyash
Normal file
@ -0,0 +1 @@
|
||||
local result = 42
|
||||
@ -403,6 +403,11 @@ impl WasmCodegen {
|
||||
Ok(instructions)
|
||||
},
|
||||
|
||||
// Phase 9.77: BoxCall Implementation - Critical Box method calls
|
||||
MirInstruction::BoxCall { dst, box_val, method, args, effects: _ } => {
|
||||
self.generate_box_call(*dst, *box_val, method, args)
|
||||
},
|
||||
|
||||
// Unsupported instructions
|
||||
_ => Err(WasmError::UnsupportedInstruction(
|
||||
format!("Instruction not yet supported: {:?}", instruction)
|
||||
@ -570,6 +575,117 @@ impl WasmCodegen {
|
||||
.copied()
|
||||
.ok_or_else(|| WasmError::CodegenError(format!("Local variable not found for ValueId: {:?}", value_id)))
|
||||
}
|
||||
|
||||
/// Phase 9.77: Generate BoxCall method invocation
|
||||
/// Implements critical Box methods: toString, print, equals, clone
|
||||
fn generate_box_call(&mut self, dst: Option<ValueId>, box_val: ValueId, method: &str, args: &[ValueId]) -> Result<Vec<String>, WasmError> {
|
||||
match method {
|
||||
"toString" => self.generate_to_string_call(dst, box_val),
|
||||
"print" => self.generate_print_call(dst, box_val),
|
||||
"equals" => self.generate_equals_call(dst, box_val, args),
|
||||
"clone" => self.generate_clone_call(dst, box_val),
|
||||
"log" => self.generate_log_call(dst, box_val, args),
|
||||
_ => Err(WasmError::UnsupportedInstruction(
|
||||
format!("Unsupported BoxCall method: {}", method)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate toString() method call - Box → String conversion
|
||||
fn generate_to_string_call(&mut self, dst: Option<ValueId>, box_val: ValueId) -> Result<Vec<String>, WasmError> {
|
||||
let Some(dst) = dst else {
|
||||
return Err(WasmError::CodegenError("toString() requires destination".to_string()));
|
||||
};
|
||||
|
||||
Ok(vec![
|
||||
format!(";; toString() implementation for ValueId({})", box_val.as_u32()),
|
||||
format!("local.get ${}", self.get_local_index(box_val)?),
|
||||
"call $box_to_string".to_string(),
|
||||
format!("local.set ${}", self.get_local_index(dst)?),
|
||||
])
|
||||
}
|
||||
|
||||
/// Generate print() method call - Basic output
|
||||
fn generate_print_call(&mut self, dst: Option<ValueId>, box_val: ValueId) -> Result<Vec<String>, WasmError> {
|
||||
let mut instructions = vec![
|
||||
format!(";; print() implementation for ValueId({})", box_val.as_u32()),
|
||||
format!("local.get ${}", self.get_local_index(box_val)?),
|
||||
"call $box_print".to_string(),
|
||||
];
|
||||
|
||||
// Store void result if destination is provided
|
||||
if let Some(dst) = dst {
|
||||
instructions.extend(vec![
|
||||
"i32.const 0".to_string(), // Void result
|
||||
format!("local.set ${}", self.get_local_index(dst)?),
|
||||
]);
|
||||
}
|
||||
|
||||
Ok(instructions)
|
||||
}
|
||||
|
||||
/// Generate equals() method call - Box comparison
|
||||
fn generate_equals_call(&mut self, dst: Option<ValueId>, box_val: ValueId, args: &[ValueId]) -> Result<Vec<String>, WasmError> {
|
||||
let Some(dst) = dst else {
|
||||
return Err(WasmError::CodegenError("equals() requires destination".to_string()));
|
||||
};
|
||||
|
||||
if args.len() != 1 {
|
||||
return Err(WasmError::CodegenError(
|
||||
format!("equals() expects 1 argument, got {}", args.len())
|
||||
));
|
||||
}
|
||||
|
||||
Ok(vec![
|
||||
format!(";; equals() implementation for ValueId({}) == ValueId({})", box_val.as_u32(), args[0].as_u32()),
|
||||
format!("local.get ${}", self.get_local_index(box_val)?),
|
||||
format!("local.get ${}", self.get_local_index(args[0])?),
|
||||
"call $box_equals".to_string(),
|
||||
format!("local.set ${}", self.get_local_index(dst)?),
|
||||
])
|
||||
}
|
||||
|
||||
/// Generate clone() method call - Box duplication
|
||||
fn generate_clone_call(&mut self, dst: Option<ValueId>, box_val: ValueId) -> Result<Vec<String>, WasmError> {
|
||||
let Some(dst) = dst else {
|
||||
return Err(WasmError::CodegenError("clone() requires destination".to_string()));
|
||||
};
|
||||
|
||||
Ok(vec![
|
||||
format!(";; clone() implementation for ValueId({})", box_val.as_u32()),
|
||||
format!("local.get ${}", self.get_local_index(box_val)?),
|
||||
"call $box_clone".to_string(),
|
||||
format!("local.set ${}", self.get_local_index(dst)?),
|
||||
])
|
||||
}
|
||||
|
||||
/// Generate log() method call - Console logging (ConsoleBox.log)
|
||||
fn generate_log_call(&mut self, dst: Option<ValueId>, box_val: ValueId, args: &[ValueId]) -> Result<Vec<String>, WasmError> {
|
||||
let mut instructions = vec![
|
||||
format!(";; log() implementation for ValueId({})", box_val.as_u32()),
|
||||
];
|
||||
|
||||
// Load box_val (ConsoleBox instance)
|
||||
instructions.push(format!("local.get ${}", self.get_local_index(box_val)?));
|
||||
|
||||
// Load all arguments
|
||||
for arg in args {
|
||||
instructions.push(format!("local.get ${}", self.get_local_index(*arg)?));
|
||||
}
|
||||
|
||||
// Call console log function
|
||||
instructions.push("call $console_log".to_string());
|
||||
|
||||
// Store void result if destination is provided
|
||||
if let Some(dst) = dst {
|
||||
instructions.extend(vec![
|
||||
"i32.const 0".to_string(), // Void result
|
||||
format!("local.set ${}", self.get_local_index(dst)?),
|
||||
]);
|
||||
}
|
||||
|
||||
Ok(instructions)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -61,9 +61,36 @@ impl WasmBackend {
|
||||
// Generate WAT (WebAssembly Text) first for debugging
|
||||
let wat_text = self.compile_to_wat(mir_module)?;
|
||||
|
||||
// Convert WAT to WASM binary using wabt
|
||||
wabt::wat2wasm(&wat_text)
|
||||
.map_err(|e| WasmError::WasmValidationError(format!("WAT to WASM conversion failed: {}", e)))
|
||||
// Phase 9.77 Task 1.3: Fix UTF-8 encoding error in WAT→WASM conversion
|
||||
self.wat_to_wasm(&wat_text)
|
||||
}
|
||||
|
||||
/// Convert WAT text to WASM binary with proper UTF-8 handling
|
||||
fn wat_to_wasm(&self, wat_source: &str) -> Result<Vec<u8>, WasmError> {
|
||||
// Debug: Print WAT source for analysis
|
||||
eprintln!("🔍 WAT Source Debug (length: {}):", wat_source.len());
|
||||
eprintln!("WAT Content:\n{}", wat_source);
|
||||
|
||||
// UTF-8 validation to prevent encoding errors
|
||||
if !wat_source.is_ascii() {
|
||||
eprintln!("❌ WAT source contains non-ASCII characters");
|
||||
return Err(WasmError::WasmValidationError(
|
||||
"WAT source contains non-ASCII characters".to_string()
|
||||
));
|
||||
}
|
||||
|
||||
eprintln!("✅ WAT source is ASCII-compatible");
|
||||
|
||||
// Convert to bytes as required by wabt::wat2wasm
|
||||
eprintln!("🔄 Converting WAT to WASM bytes...");
|
||||
let wasm_bytes = wabt::wat2wasm(wat_source.as_bytes())
|
||||
.map_err(|e| {
|
||||
eprintln!("❌ wabt::wat2wasm failed: {}", e);
|
||||
WasmError::WasmValidationError(format!("WAT to WASM conversion failed: {}", e))
|
||||
})?;
|
||||
|
||||
eprintln!("✅ WASM conversion successful, {} bytes generated", wasm_bytes.len());
|
||||
Ok(wasm_bytes)
|
||||
}
|
||||
|
||||
/// Compile MIR module to WAT text format (for debugging)
|
||||
|
||||
@ -89,6 +89,40 @@ impl RuntimeImports {
|
||||
result: None,
|
||||
});
|
||||
|
||||
// Phase 9.77: BoxCall runtime functions
|
||||
|
||||
// box_to_string - Convert any Box to string representation
|
||||
self.imports.push(ImportFunction {
|
||||
module: "env".to_string(),
|
||||
name: "box_to_string".to_string(),
|
||||
params: vec!["i32".to_string()], // box_ptr
|
||||
result: Some("i32".to_string()), // string_box_ptr
|
||||
});
|
||||
|
||||
// box_print - Print any Box to console
|
||||
self.imports.push(ImportFunction {
|
||||
module: "env".to_string(),
|
||||
name: "box_print".to_string(),
|
||||
params: vec!["i32".to_string()], // box_ptr
|
||||
result: None,
|
||||
});
|
||||
|
||||
// box_equals - Compare two Boxes for equality
|
||||
self.imports.push(ImportFunction {
|
||||
module: "env".to_string(),
|
||||
name: "box_equals".to_string(),
|
||||
params: vec!["i32".to_string(), "i32".to_string()], // box1_ptr, box2_ptr
|
||||
result: Some("i32".to_string()), // bool result
|
||||
});
|
||||
|
||||
// box_clone - Clone a Box
|
||||
self.imports.push(ImportFunction {
|
||||
module: "env".to_string(),
|
||||
name: "box_clone".to_string(),
|
||||
params: vec!["i32".to_string()], // box_ptr
|
||||
result: Some("i32".to_string()), // cloned_box_ptr
|
||||
});
|
||||
|
||||
// Future: env.file_read, env.file_write for file I/O
|
||||
// Future: env.http_request for network access
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user